Skip to content

Commit

Permalink
Support Items Module: Credit card purchase line items (#98)
Browse files Browse the repository at this point in the history
* Support Items Module: Credit card purchase line items

* get item and account, for CCP lineitem

* lint fix

* pr comment

* revert fixtures

* updated sync and updated UTs

* fixed few bugs

* lint fix

* lint fix
  • Loading branch information
anishfyle authored Jul 29, 2024
1 parent eb95b42 commit 3dc3d9a
Show file tree
Hide file tree
Showing 6 changed files with 195 additions and 266 deletions.
19 changes: 10 additions & 9 deletions apps/fyle/actions.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,15 @@ def sync_fyle_dimensions(workspace_id: int):
qbd_connection = PlatformConnector(workspace_id=workspace_id)
qbd_connection.sync_corporate_card()
field_mapping = FieldMapping.objects.filter(workspace_id=workspace_id).first()

if field_mapping.item_type == 'PROJECT':
qbd_connection.sync_projects(field_mapping.item_type)

if field_mapping.item_type == 'COST_CENTER':
qbd_connection.sync_cost_center(field_mapping.item_type)

sync_custom_field_options = False

if field_mapping.item_type not in ['PROJECT', 'COST_CENTER']:
sync_custom_field_options = True
if field_mapping:
if field_mapping.item_type == 'PROJECT':
qbd_connection.sync_projects(field_mapping.item_type)
elif field_mapping.item_type == 'COST_CENTER':
qbd_connection.sync_cost_center(field_mapping.item_type)
else:
sync_custom_field_options = True

qbd_connection.sync_custom_field(field_mapping.item_type, field_mapping, sync_custom_field_options)
qbd_connection.sync_custom_field(field_mapping.item_type, field_mapping, sync_custom_field_options)
200 changes: 100 additions & 100 deletions apps/mappings/connector.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,104 +53,104 @@ def sync_corporate_card(self):
if len(card_attributes) > 0:
QBDMapping.update_or_create_mapping_objects(card_attributes, self.workspace_id)

def sync_custom_field(self, source_type: str, field_mapping: FieldMapping, sync_custom_field_options: bool = False):
"""
Sync custom fields that are mapped to the Item in the FieldMapping
:source_type: The Custom Field Items is mapped to
:field_mapping: FieldMapping instance
:sync_custom_field_options: bool, when set to true, we create the QBDMapping
else only update the values of custom_fields in field_mapping table
"""

query = {
'order': 'updated_at.desc',
'is_custom': 'eq.true',
'type': 'eq.SELECT',
'is_enabled': 'eq.true'
}
workspace_id = self.context['request'].parser_context.get('kwargs').get('workspace_id')
custom_fields = self.platform.v1beta.admin.expense_custom_fields.list_all(query)
query = QBDMapping.objects.filter(workspace_id=workspace_id, attribute_type=source_type)
existing_source_attributes = query.values_list('value', flat=True)

distinct_custom_fields = []
source_values = []

for custom_field in custom_fields:
distinct_custom_fields.append(custom_field['field_name'])
if source_type == custom_field['field_name']:
source_values.extend(custom_field['options'])

if distinct_custom_fields:
field_mapping.custom_fields = distinct_custom_fields
field_mapping.save()

if sync_custom_field_options:
source_attributes = []
for source_value in source_values:
if source_value not in existing_source_attributes:
source_attributes.append({
'attribute_type': source_type,
'source_value': source_value,
'source_id': source_value
})
if source_attributes:
QBDMapping.update_or_create_mapping_objects(source_attributes, self.workspace_id)


def sync_projects(self, source_type: str):
"""
Sync PROJECT as Item
"""

query = {
'order': 'updated_at.desc'
}
workspace_id = self.context['request'].parser_context.get('kwargs').get('workspace_id')
projects_generator = self.platform.v1beta.admin.projects.list_all(query)
existing_projects = QBDMapping.objects.filter(workspace_id=workspace_id, attribute_type=source_type)

source_attributes = []

for projects in projects_generator:
for project in projects.get('data'):
if project['sub_project']:
project['name'] = '{0} / {1}'.format(project['name'], project['sub_project'])
if project['name'] not in existing_projects:
source_attributes.append({
def sync_custom_field(self, source_type: str, field_mapping: FieldMapping, sync_custom_field_options: bool = False):
"""
Sync custom fields that are mapped to the Item in the FieldMapping
:source_type: The Custom Field Items is mapped to
:field_mapping: FieldMapping instance
:sync_custom_field_options: bool, when set to true, we create the QBDMapping
else only update the values of custom_fields in field_mapping table
"""

query = {
'order': 'updated_at.desc',
'is_custom': 'eq.true',
'type': 'eq.SELECT',
'is_enabled': 'eq.true'
}
workspace_id = self.context['request'].parser_context.get('kwargs').get('workspace_id')
custom_fields = self.platform.v1beta.admin.expense_custom_fields.list_all(query)
query = QBDMapping.objects.filter(workspace_id=workspace_id, attribute_type=source_type)
existing_source_attributes = query.values_list('value', flat=True)

distinct_custom_fields = []
source_values = []

for custom_field in custom_fields:
distinct_custom_fields.append(custom_field['field_name'])
if source_type == custom_field['field_name']:
source_values.extend(custom_field['options'])

if distinct_custom_fields:
field_mapping.custom_fields = distinct_custom_fields
field_mapping.save()

if sync_custom_field_options:
source_attributes = []
for source_value in source_values:
if source_value not in existing_source_attributes:
source_attributes.append({
'attribute_type': source_type,
'source_value': project['name'],
'source_id': project['id']
})


if source_attributes:
QBDMapping.update_or_create_mapping_objects(source_attributes, self.workspace_id)


def sync_cost_center(self, source_type: str):
"""
Sync PROJECT as Item
"""

query = {
'order': 'updated_at.desc'
}
workspace_id = self.context['request'].parser_context.get('kwargs').get('workspace_id')
cost_center_generator = self.platform.v1beta.admin.cost_centers.list_all(query)
existing_cost_centers = QBDMapping.objects.filter(workspace_id=workspace_id, attribute_type=source_type)

source_attributes = []

for cost_centers in cost_center_generator:
for cost_center in cost_centers.get('data'):
if cost_center['name'] not in existing_cost_centers:
source_attributes.append({
'attribute_type': source_type,
'source_value': cost_center['name'],
'source_id': cost_center['id']
})


if source_attributes:
QBDMapping.update_or_create_mapping_objects(source_attributes, self.workspace_id)
'source_value': source_value,
'source_id': source_value
})
if source_attributes:
QBDMapping.update_or_create_mapping_objects(source_attributes, self.workspace_id)


def sync_projects(self, source_type: str):
"""
Sync PROJECT as Item
"""

query = {
'order': 'updated_at.desc'
}
workspace_id = self.context['request'].parser_context.get('kwargs').get('workspace_id')
projects_generator = self.platform.v1beta.admin.projects.list_all(query)
existing_projects = QBDMapping.objects.filter(workspace_id=workspace_id, attribute_type=source_type)

source_attributes = []

for projects in projects_generator:
for project in projects.get('data'):
if project['sub_project']:
project['name'] = '{0} / {1}'.format(project['name'], project['sub_project'])
if project['name'] not in existing_projects:
source_attributes.append({
'attribute_type': source_type,
'source_value': project['name'],
'source_id': project['id']
})


if source_attributes:
QBDMapping.update_or_create_mapping_objects(source_attributes, self.workspace_id)


def sync_cost_center(self, source_type: str):
"""
Sync PROJECT as Item
"""

query = {
'order': 'updated_at.desc'
}
workspace_id = self.context['request'].parser_context.get('kwargs').get('workspace_id')
cost_center_generator = self.platform.v1beta.admin.cost_centers.list_all(query)
existing_cost_centers = QBDMapping.objects.filter(workspace_id=workspace_id, attribute_type=source_type)

source_attributes = []

for cost_centers in cost_center_generator:
for cost_center in cost_centers.get('data'):
if cost_center['name'] not in existing_cost_centers:
source_attributes.append({
'attribute_type': source_type,
'source_value': cost_center['name'],
'source_id': cost_center['id']
})


if source_attributes:
QBDMapping.update_or_create_mapping_objects(source_attributes, self.workspace_id)
30 changes: 28 additions & 2 deletions apps/qbd/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,27 @@
from apps.mappings.models import QBDMapping


def get_item_and_account_name(field_mapping: FieldMapping, expense: Expense):
item_type = field_mapping.item_type
expense_item = None
expense_category = expense.category

if item_type.upper() in ['PROJECT', 'COST_CENTER']:
expense_item = getattr(field_mapping, item_type.lower())
else:
# Modify item_type to match the format in custom_properties
modified_item_type = item_type.replace('_', ' ').title()
expense_item = getattr(expense, 'custom_properties', {}).get(modified_item_type)

if item_type and expense_item and expense_category:
item_mapped_account = QBDMapping.objects.filter(attribute_type=modified_item_type, source_value=expense_item).first()

if item_mapped_account:
return expense_item, item_mapped_account

return '', expense_category


def get_class_and_project_name(field_mappings: FieldMapping, expense: Expense):
"""
According to class_type, project_type return the value of project, class field
Expand Down Expand Up @@ -515,18 +536,23 @@ def create_credit_card_purchase_lineitems(
field_mappings, expense
)

inv_item, account = get_item_and_account_name(
field_mappings, expense
)

lineitem = CreditCardPurchaseLineitem.objects.create(
transaction_type='CREDIT CARD' if expense.amount > 0 else 'CCARD REFUND',
date=get_transaction_date(expenses, date_preference=export_settings.credit_card_expense_date),
account=expense.category,
account=account,
name=project_name,
class_name=class_name,
amount=expense.amount,
memo=get_expense_purpose(workspace_id, expense),
reimbursable_expense='No',
credit_card_purchase=credit_card_purchase,
expense=expense,
workspace_id=workspace_id
workspace_id=workspace_id,
inventory_item=inv_item
)

lineitems.append(lineitem)
Expand Down
8 changes: 8 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,14 @@ def add_field_mappings():
workspace_id=workspace_id,
class_type='COST_CENTER' if workspace_id in [1, 2] else 'PROJECT',
project_type='PROJECT' if workspace_id in [1, 2] else 'COST_CENTER',
item_type='ITEM_TYPE_1' if workspace_id == 1 else (
'ITEM_TYPE_2' if workspace_id == 2 else 'ITEM_TYPE_3'
),
custom_fields=(
['custom_field_1', 'custom_field_2']
if workspace_id in [1, 2]
else ['custom_field_3', 'custom_field_4']
),
)


Expand Down
Loading

0 comments on commit 3dc3d9a

Please sign in to comment.