Skip to content

Commit

Permalink
Split Expenses: QBO (#619)
Browse files Browse the repository at this point in the history
* Tests for Split Expenses

* coderabbit suggestion

* updated tests

* flake8

* replacing workspace

* bank_transaction_id

* bank_transaction_id fixtures

* fixing fixtures

* Revert fixtures

* updated fixtures

* updated tests

* revert for original test

* updated fixtures

* flake8

* added bank_transaction_id to fixture

* updated keys

* updated tests

* mock code removed

* added support for split_expense_grouping based on line item selection

* pr comments

* updated tests with fixtures

* PR Comments

* Model changes for Split Expenses (#632)

* Model changes for Split Expenses

* split expense grouping key

* Updated Model and SQL Fixtures

* Fixed tests for expense_groups_settings

* pr comments

* pr comment

* revert reset db sql

* new migration with sql fixtures

* Split Expense: Grouping function (#638)

* Initial Commit

* grouping function

* model changes final

* extend the filtered_corporate_credit_card_expense_groups

* flake8

* flake8

* flake8

* removed trailing spaces

* pr comment

* Split Expense: purchase number generation (#639)

* Initial Commit

* grouping function

* model changes final

* extend the filtered_corporate_credit_card_expense_groups

* flake8

* flake8

* flake8

* removed trailing spaces

* Purchase number generation

* flake8 whitespace

* sql fixtures and script

* pr comment

* pr comment

* revert reset db

* reset db

* Split Expense: serializers (#641)

* Split Expense: serializers

* minor fixes

* pr comments

* fix tests and fixtures

* updated fixtures

* Updated fixtures

* updated broken tests fixture
  • Loading branch information
anishfyle authored Jul 3, 2024
1 parent 42134f5 commit 3972482
Show file tree
Hide file tree
Showing 12 changed files with 667 additions and 81 deletions.
24 changes: 24 additions & 0 deletions apps/fyle/migrations/0037_auto_20240625_1035.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Generated by Django 3.2.14 on 2024-06-25 10:35

import apps.fyle.models
from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('fyle', '0036_expense_paid_on_fyle'),
]

operations = [
migrations.AddField(
model_name='expense',
name='bank_transaction_id',
field=models.CharField(blank=True, help_text='Bank Transaction ID', max_length=255, null=True),
),
migrations.AddField(
model_name='expensegroupsettings',
name='split_expense_grouping',
field=models.CharField(choices=[('SINGLE_LINE_ITEM', 'SINGLE_LINE_ITEM'), ('MULTIPLE_LINE_ITEM', 'MULTIPLE_LINE_ITEM')], default=apps.fyle.models.get_default_split_expense_grouping, help_text='specify line items for split expenses grouping', max_length=100),
),
]
53 changes: 48 additions & 5 deletions apps/fyle/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@

CCC_EXPENSE_STATE = (('APPROVED', 'APPROVED'), ('PAID', 'PAID'), ('PAYMENT_PROCESSING', 'PAYMENT_PROCESSING'))

SPLIT_EXPENSE_GROUPING = (('SINGLE_LINE_ITEM', 'SINGLE_LINE_ITEM'), ('MULTIPLE_LINE_ITEM', 'MULTIPLE_LINE_ITEM'))

EXPENSE_FILTER_RANK = ((1, 1), (2, 2))

EXPENSE_FILTER_JOIN_BY = (('AND', 'AND'), ('OR', 'OR'))
Expand All @@ -38,6 +40,10 @@ def get_default_ccc_expense_state():
return 'PAID'


def get_default_split_expense_grouping():
return 'MULTIPLE_LINE_ITEM'


def _format_date(date_string) -> datetime:
"""
Format date.
Expand Down Expand Up @@ -90,6 +96,7 @@ class Expense(models.Model):
report_id = models.CharField(max_length=255, help_text='Report ID')
report_title = models.TextField(null=True, blank=True, help_text='Report title')
corporate_card_id = models.CharField(max_length=255, null=True, blank=True, help_text='Corporate Card ID')
bank_transaction_id = models.CharField(max_length=255, null=True, blank=True, help_text='Bank Transaction ID')
file_ids = ArrayField(base_field=models.CharField(max_length=255), null=True, help_text='File IDs')
spent_at = models.DateTimeField(null=True, help_text='Expense spent at')
approved_at = models.DateTimeField(null=True, help_text='Expense approved at')
Expand Down Expand Up @@ -151,6 +158,7 @@ def create_expense_objects(expenses: List[Dict], workspace_id: int):
'report_id': expense['report_id'],
'report_title': expense['report_title'],
'corporate_card_id': expense['corporate_card_id'],
'bank_transaction_id': expense['bank_transaction_id'],
'file_ids': expense['file_ids'],
'spent_at': expense['spent_at'],
'approved_at': expense['approved_at'],
Expand Down Expand Up @@ -194,6 +202,7 @@ class ExpenseGroupSettings(models.Model):
reimbursable_export_date_type = models.CharField(max_length=100, default='current_date', help_text='Export Date')
ccc_export_date_type = models.CharField(max_length=100, default='current_date', help_text='CCC Export Date')
import_card_credits = models.BooleanField(help_text='Import Card Credits', default=False)
split_expense_grouping = models.CharField(max_length=100, default=get_default_split_expense_grouping, choices=SPLIT_EXPENSE_GROUPING, help_text='specify line items for split expenses grouping')
workspace = models.OneToOneField(Workspace, on_delete=models.PROTECT, help_text='To which workspace this expense group setting belongs to', related_name='expense_group_settings')
created_at = models.DateTimeField(auto_now_add=True, help_text='Created at')
updated_at = models.DateTimeField(auto_now=True, help_text='Updated at')
Expand Down Expand Up @@ -276,6 +285,7 @@ def update_expense_group_settings(expense_group_settings: Dict, workspace_id: in
'reimbursable_export_date_type': expense_group_settings['reimbursable_export_date_type'],
'ccc_export_date_type': expense_group_settings['ccc_export_date_type'],
'import_card_credits': import_card_credits,
'split_expense_grouping': expense_group_settings['split_expense_grouping']
},
)

Expand Down Expand Up @@ -419,11 +429,44 @@ def create_expense_groups_by_report_id_fund_source(
filter(lambda expense: expense.fund_source == "CCC", expense_objects)
)

filtered_corporate_credit_card_expense_groups = _group_expenses(
corporate_credit_card_expenses,
corporate_credit_card_expense_group_field,
workspace_id,
)
if (
general_settings.corporate_credit_card_expenses_object == 'CREDIT CARD PURCHASE' and
expense_group_settings.split_expense_grouping == 'MULTIPLE_LINE_ITEM'
):
ccc_expenses_without_bank_transaction = [
expense for expense in expense_objects
if not expense.bank_transaction_id
]

ccc_expenses_with_bank_transaction = [
expense for expense in expense_objects
if expense.bank_transaction_id
]

filtered_corporate_credit_card_expense_groups = _group_expenses(
ccc_expenses_without_bank_transaction,
corporate_credit_card_expense_group_field,
workspace_id,
)

corporate_credit_card_expense_group_field = [
field for field in corporate_credit_card_expense_group_field
if field not in {'expense_number', 'expense_id'}
]
corporate_credit_card_expense_group_field.append('bank_transaction_id')
filtered_corporate_credit_card_expense_groups.extend(
_group_expenses(
ccc_expenses_with_bank_transaction,
corporate_credit_card_expense_group_field,
workspace_id,
)
)
else:
filtered_corporate_credit_card_expense_groups = _group_expenses(
corporate_credit_card_expenses,
corporate_credit_card_expense_group_field,
workspace_id,
)

if (
general_settings.corporate_credit_card_expenses_object == "BILL"
Expand Down
17 changes: 15 additions & 2 deletions apps/quickbooks_online/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from django.db import models
from fyle_accounting_mappings.models import DestinationAttribute, EmployeeMapping, ExpenseAttribute, Mapping, MappingSetting

from apps.fyle.models import Expense, ExpenseGroup
from apps.fyle.models import Expense, ExpenseGroup, ExpenseGroupSettings
from apps.mappings.models import GeneralMapping
from apps.workspaces.models import Workspace, WorkspaceGeneralSettings

Expand Down Expand Up @@ -172,6 +172,13 @@ def get_category_mapping_and_detail_type(workspace_general_settings: WorkspaceGe
return qbo_account, 'AccountBasedExpenseLineDetail'


def get_credit_card_purchase_number(expense_group, expense, expense_group_settings, map_merchant_to_vendor):
if expense_group_settings.split_expense_grouping == 'MULTIPLE_LINE_ITEM' and 'bank_transaction_id' in expense_group.description:
return expense_group.description['bank_transaction_id']
else:
return expense.expense_number


class Bill(models.Model):
"""
QBO Bill
Expand Down Expand Up @@ -586,6 +593,7 @@ def create_credit_card_purchase(expense_group: ExpenseGroup, map_merchant_to_ven
expense = expense_group.expenses.first()
general_mappings = GeneralMapping.objects.get(workspace_id=expense_group.workspace_id)
workspace_general_settings = WorkspaceGeneralSettings.objects.get(workspace_id=expense_group.workspace_id)
expense_group_settings = ExpenseGroupSettings.objects.get(workspace_id=expense_group.workspace_id)
employee_field_mapping = workspace_general_settings.employee_field_mapping
entity_id = None

Expand Down Expand Up @@ -618,7 +626,12 @@ def create_credit_card_purchase(expense_group: ExpenseGroup, map_merchant_to_ven
'transaction_date': get_transaction_date(expense_group),
'private_note': private_note,
'currency': expense.currency,
'credit_card_purchase_number': expense.expense_number if map_merchant_to_vendor else '',
'credit_card_purchase_number': get_credit_card_purchase_number(
expense_group,
expense,
expense_group_settings,
map_merchant_to_vendor
),
},
)
return credit_card_purchase_object
Expand Down
3 changes: 2 additions & 1 deletion apps/workspaces/apis/export_settings/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,11 @@ class ExpenseGroupSettingsSerializer(serializers.ModelSerializer):
ccc_export_date_type = serializers.CharField(allow_null=True, allow_blank=True, required=False)
ccc_expense_state = serializers.CharField(allow_null=True, allow_blank=True, required=False)
expense_state = serializers.CharField(allow_null=True, allow_blank=True, required=False)
split_expense_grouping = serializers.CharField(allow_null=False, allow_blank=False, required=True)

class Meta:
model = ExpenseGroupSettings
fields = ['reimbursable_expense_group_fields', 'corporate_credit_card_expense_group_fields', 'expense_state', 'ccc_expense_state', 'reimbursable_export_date_type', 'ccc_export_date_type']
fields = ['reimbursable_expense_group_fields', 'corporate_credit_card_expense_group_fields', 'expense_state', 'ccc_expense_state', 'reimbursable_export_date_type', 'ccc_export_date_type', 'split_expense_grouping']


class GeneralMappingsSerializer(serializers.ModelSerializer):
Expand Down
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ enum34==1.1.10
future==0.18.2
fyle==0.37.0
fyle-accounting-mappings==1.32.1
fyle-integrations-platform-connector==1.38.1
fyle-integrations-platform-connector==1.38.3
fyle-rest-auth==1.7.2
flake8==4.0.1
gevent==23.9.1
Expand Down
5 changes: 5 additions & 0 deletions sql/scripts/026-mark-split-expense-grouping.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
rollback;
begin;

UPDATE expense_group_settings
SET split_expense_grouping = 'SINGLE_LINE_ITEM';
Loading

0 comments on commit 3972482

Please sign in to comment.