Skip to content

Commit

Permalink
Merge branch master into journal-support-allocation
Browse files Browse the repository at this point in the history
  • Loading branch information
Ashutosh619-sudo committed Jul 30, 2024
2 parents 4a30af8 + ef6e88e commit 39921c5
Show file tree
Hide file tree
Showing 17 changed files with 287 additions and 117 deletions.
1 change: 0 additions & 1 deletion apps/fyle/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
import logging
from typing import List, Union

from django.utils.module_loading import import_string
from django.conf import settings
from django.db.models import Q
from fyle_accounting_mappings.models import ExpenseAttribute
Expand Down
74 changes: 38 additions & 36 deletions apps/fyle/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
ALLOWED_FIELDS = [
'employee_email', 'report_id', 'claim_number', 'settlement_id',
'fund_source', 'vendor', 'category', 'project', 'cost_center',
'verified_at', 'approved_at', 'spent_at', 'expense_id', 'expense_number', 'payment_number', 'posted_at'
'verified_at', 'approved_at', 'spent_at', 'expense_id', 'expense_number', 'payment_number', 'posted_at', 'bank_transaction_id'
]

ALLOWED_FORM_INPUT = {
Expand Down Expand Up @@ -425,6 +425,7 @@ def create_expense_groups_by_report_id_fund_source(expense_objects: List[Expense
Group expense by and fund_source
"""
expense_groups = []
filtered_corporate_credit_card_expense_groups = []
expense_group_settings = ExpenseGroupSettings.objects.get(workspace_id=workspace_id)

reimbursable_expense_group_fields = expense_group_settings.reimbursable_expense_group_fields
Expand All @@ -440,45 +441,46 @@ def create_expense_groups_by_report_id_fund_source(expense_objects: List[Expense

corporate_credit_card_expense_group_field = expense_group_settings.corporate_credit_card_expense_group_fields
corporate_credit_card_expenses = list(filter(lambda expense: expense.fund_source == 'CCC', expense_objects))

if (
configuration.corporate_credit_card_expenses_object == 'CHARGE_CARD_TRANSACTION' 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,
if corporate_credit_card_expenses:
if (
configuration.corporate_credit_card_expenses_object == 'CHARGE_CARD_TRANSACTION' and
expense_group_settings.split_expense_grouping == 'MULTIPLE_LINE_ITEM'
):
ccc_expenses_without_bank_transaction = [
expense for expense in corporate_credit_card_expenses
if not expense.bank_transaction_id
]

ccc_expenses_with_bank_transaction = [
expense for expense in corporate_credit_card_expenses
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,
)
)
else:
filtered_corporate_credit_card_expense_groups = _group_expenses(
corporate_credit_card_expenses,
corporate_credit_card_expense_group_field,
workspace_id,
)

filtered_corporate_credit_card_expense_groups = filter_expense_groups(
filtered_corporate_credit_card_expense_groups, corporate_credit_card_expenses, configuration.corporate_credit_card_expenses_object, corporate_credit_card_expense_group_field
Expand Down
13 changes: 7 additions & 6 deletions apps/sage_intacct/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@
from django.conf import settings
from django.db.models import Q,JSONField
from django.db import models

from django.utils.module_loading import import_string

from fyle_accounting_mappings.models import Mapping, MappingSetting, DestinationAttribute, CategoryMapping, \
EmployeeMapping

from apps.fyle.models import ExpenseGroup, Expense, ExpenseAttribute, Reimbursement, ExpenseGroupSettings, DependentFieldSetting
from apps.mappings.models import GeneralMapping

from apps.workspaces.models import Configuration, Workspace, FyleCredential
from apps.workspaces.models import Configuration, Workspace, FyleCredential, SageIntacctCredential
from typing import Dict, List, Union


Expand Down Expand Up @@ -937,9 +937,9 @@ class JournalEntryLineitem(models.Model):

class Meta:
db_table = 'journal_entry_lineitems'

@staticmethod
def create_journal_entry_lineitems(expense_group: ExpenseGroup, configuration: Configuration):
def create_journal_entry_lineitems(expense_group: ExpenseGroup, configuration: Configuration, sage_intacct_connection):
"""
Create journal entry lineitems
:param expense_group: expense group
Expand Down Expand Up @@ -998,8 +998,9 @@ def create_journal_entry_lineitems(expense_group: ExpenseGroup, configuration: C

vendor_id = entity.destination_vendor.destination_id if employee_mapping_setting == 'VENDOR' else None

if lineitem.fund_source == 'CCC' and configuration.use_merchant_in_journal_line and lineitem.vendor:
vendor = DestinationAttribute.objects.filter(attribute_type='VENDOR', value__iexact=lineitem.vendor, workspace_id=expense_group.workspace_id).order_by('-updated_at').first()
if lineitem.fund_source == 'CCC' and configuration.use_merchant_in_journal_line:
# here it would create a Credit Card Vendor if the expene vendor is not present
vendor = import_string('apps.sage_intacct.tasks.get_or_create_credit_card_vendor')(expense_group.workspace_id, configuration, lineitem.vendor, sage_intacct_connection)
if vendor:
vendor_id = vendor.destination_id

Expand Down
54 changes: 34 additions & 20 deletions apps/sage_intacct/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,29 +19,29 @@
)

from fyle_intacct_api.exceptions import BulkError
from apps.fyle.models import ExpenseGroup, ExpenseGroupSettings, Reimbursement, Expense
from apps.fyle.models import ExpenseGroup, Expense
from apps.tasks.models import TaskLog, Error
from apps.mappings.models import GeneralMapping
from apps.fyle.actions import update_expenses_in_progress, update_failed_expenses, update_complete_expenses
from apps.fyle.tasks import post_accounting_export_summary
from apps.workspaces.models import (
SageIntacctCredential,
FyleCredential,
Configuration,
SageIntacctCredential,
FyleCredential,
Configuration,
LastExportDetail,
Workspace
)
from apps.sage_intacct.models import (
ExpenseReport,
ExpenseReportLineitem,
ExpenseReport,
ExpenseReportLineitem,
Bill,
BillLineitem,
BillLineitem,
ChargeCardTransaction,
ChargeCardTransactionLineitem,
APPayment,
APPaymentLineitem,
JournalEntry,
JournalEntryLineitem,
ChargeCardTransactionLineitem,
APPayment,
APPaymentLineitem,
JournalEntry,
JournalEntryLineitem,
SageIntacctReimbursement,
SageIntacctReimbursementLineitem
)
Expand Down Expand Up @@ -224,20 +224,34 @@ def create_or_update_employee_mapping(expense_group: ExpenseGroup, sage_intacct_
)


def get_or_create_credit_card_vendor(merchant: str, workspace_id: int):
def get_or_create_credit_card_vendor(workspace_id: int, configuration: Configuration, merchant: str = None, sage_intacct_connection: SageIntacctConnector = None):
"""
Get or create default vendor
:param merchant: Fyle Expense Merchant
:param workspace_id: Workspace Id
:return:
"""
sage_intacct_credentials = SageIntacctCredential.objects.get(workspace_id=workspace_id)
sage_intacct_connection = SageIntacctConnector(sage_intacct_credentials, workspace_id)
if not sage_intacct_connection:
sage_intacct_credentials = SageIntacctCredential.objects.get(workspace_id=workspace_id)
sage_intacct_connection = SageIntacctConnector(sage_intacct_credentials, workspace_id)

vendor = None

if merchant:
if (
merchant
and not configuration.import_vendors_as_merchants
and configuration.corporate_credit_card_expenses_object
and configuration.auto_create_merchants_as_vendors
and (
configuration.corporate_credit_card_expenses_object == 'CHARGE_CARD_TRANSACTION'
or (
configuration.corporate_credit_card_expenses_object == 'JOURNAL_ENTRY'
and configuration.use_merchant_in_journal_line
)
)
):
try:
vendor = sage_intacct_connection.get_or_create_vendor(merchant, create=False)
vendor = sage_intacct_connection.get_or_create_vendor(merchant, create=True)
except WrongParamsError as bad_request:
logger.info(bad_request.response)

Expand Down Expand Up @@ -553,7 +567,7 @@ def create_journal_entry(expense_group: ExpenseGroup, task_log_id: int, last_exp
)
else:
merchant = expense_group.expenses.first().vendor
get_or_create_credit_card_vendor(merchant, expense_group.workspace_id)
get_or_create_credit_card_vendor(expense_group.workspace_id, configuration, merchant, sage_intacct_connection)

__validate_employee_mapping(expense_group, configuration)
logger.info('Validated Employee mapping %s successfully', expense_group.id)
Expand All @@ -568,7 +582,7 @@ def create_journal_entry(expense_group: ExpenseGroup, task_log_id: int, last_exp

journal_entry_object = JournalEntry.create_journal_entry(expense_group, task_log.supdoc_id)

journal_entry_lineitem_object = JournalEntryLineitem.create_journal_entry_lineitems(expense_group, configuration)
journal_entry_lineitem_object = JournalEntryLineitem.create_journal_entry_lineitems(expense_group, configuration, sage_intacct_connection)

created_journal_entry = sage_intacct_connection.post_journal_entry(journal_entry_object, journal_entry_lineitem_object)
logger.info('Created Journal Entry with Expense Group %s successfully', expense_group.id)
Expand Down Expand Up @@ -950,7 +964,7 @@ def create_charge_card_transaction(expense_group: ExpenseGroup, task_log_id: int
sage_intacct_connection = SageIntacctConnector(sage_intacct_credentials, expense_group.workspace_id)

merchant = expense_group.expenses.first().vendor
vendor = get_or_create_credit_card_vendor(merchant, expense_group.workspace_id)
vendor = get_or_create_credit_card_vendor(expense_group.workspace_id, configuration, merchant, sage_intacct_connection)

vendor_id = vendor.destination_id if vendor else None
__validate_employee_mapping(expense_group, configuration)
Expand Down
23 changes: 20 additions & 3 deletions apps/sage_intacct/utils.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import re
import logging
import base64
from typing import List, Dict
from datetime import datetime, timedelta
import unidecode
import time
from django.conf import settings

from cryptography.fernet import Fernet
Expand Down Expand Up @@ -686,6 +685,7 @@ def get_or_create_vendor(self, vendor_name: str, email: str = None, create: bool
:param create: False to just Get and True to Get or Create if not exists
:return: Vendor
"""
vendor_name = self.sanitize_vendor_name(vendor_name)
vendor_from_db = DestinationAttribute.objects.filter(workspace_id=self.workspace_id, attribute_type='VENDOR', value=vendor_name, active=True).first()

if vendor_from_db:
Expand All @@ -701,7 +701,7 @@ def get_or_create_vendor(self, vendor_name: str, email: str = None, create: bool
vendor = sorted_vendor_data[0]
else:
vendor = vendor['VENDOR'][0]

vendor = vendor if vendor['STATUS'] == 'active' else None
else:
vendor = None
Expand Down Expand Up @@ -1618,3 +1618,20 @@ def post_sage_intacct_reimbursement(self, reimbursement: SageIntacctReimbursemen
reimbursement_payload = self.__construct_sage_intacct_reimbursement(reimbursement, reimbursement_lineitems)
created_reimbursement = self.connection.reimbursements.post(reimbursement_payload)
return created_reimbursement

def sanitize_vendor_name(self, vendor_name: str = None) -> str:
"""
Remove special characters from Vendor Name
:param vendor_name: Vendor Name
:return: Sanitized Vendor Name
"""
sanitized_name = None
if vendor_name:
pattern = r'[!@#$%^&*()\-_=\+\[\]{}|\\:;"\'<>,.?/~`]'
sanitized_name = re.sub(pattern, '', vendor_name)
sanitized_name = re.sub(r'\s+', ' ', sanitized_name).strip()

if sanitized_name:
return sanitized_name

return None
9 changes: 4 additions & 5 deletions apps/workspaces/apis/advanced_settings/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ class Meta:
'sync_fyle_to_sage_intacct_payments',
'sync_sage_intacct_to_fyle_payments',
'auto_create_destination_entity',
'memo_structure'
'memo_structure',
'auto_create_merchants_as_vendors'
]


Expand All @@ -57,7 +58,6 @@ class Meta:
'use_intacct_employee_locations'
]


def get_default_location(self, instance):
return {
'name': instance.default_location_name,
Expand Down Expand Up @@ -127,11 +127,9 @@ class Meta:
]
read_only_fields = ['workspace_id']


def get_workspace_id(self, instance):
return instance.id


def update(self, instance, validated):
configurations = validated.pop('configurations')
general_mappings = validated.pop('general_mappings')
Expand All @@ -144,7 +142,8 @@ def update(self, instance, validated):
'sync_sage_intacct_to_fyle_payments': configurations.get('sync_sage_intacct_to_fyle_payments'),
'auto_create_destination_entity': configurations.get('auto_create_destination_entity'),
'change_accounting_period': configurations.get('change_accounting_period'),
'memo_structure': configurations.get('memo_structure')
'memo_structure': configurations.get('memo_structure'),
'auto_create_merchants_as_vendors': configurations.get('auto_create_merchants_as_vendors')
}
)

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 3.2.14 on 2024-07-26 17:19

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('workspaces', '0034_configuration_is_journal_credit_billable'),
]

operations = [
migrations.AddField(
model_name='configuration',
name='auto_create_merchants_as_vendors',
field=models.BooleanField(default=False, help_text='Auto create merchants as vendors in sage intacct'),
),
]
3 changes: 2 additions & 1 deletion apps/workspaces/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -134,8 +134,9 @@ class Configuration(models.Model):
change_accounting_period = models.BooleanField(default=False, help_text='Change the accounting period')
import_vendors_as_merchants = models.BooleanField(default=False, help_text='Auto import vendors from sage intacct '
'as merchants to Fyle')

use_merchant_in_journal_line = models.BooleanField(default=False, help_text='Export merchant as vendor in journal entry line item')
auto_create_merchants_as_vendors = models.BooleanField(default=False, help_text='Auto create merchants as vendors in sage intacct')
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
Loading

0 comments on commit 39921c5

Please sign in to comment.