-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
validate accounting export group before the export (#94)
- Loading branch information
1 parent
4d79ba2
commit 1e9469e
Showing
18 changed files
with
382 additions
and
119 deletions.
There are no files selected for viewing
24 changes: 24 additions & 0 deletions
24
...xports/migrations/0002_rename_sage_300_errors_accountingexport_sage300_errors_and_more.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
# Generated by Django 4.1.2 on 2023-11-28 09:49 | ||
|
||
from django.db import migrations | ||
import sage_desktop_api.models.fields | ||
|
||
|
||
class Migration(migrations.Migration): | ||
|
||
dependencies = [ | ||
('accounting_exports', '0001_initial'), | ||
] | ||
|
||
operations = [ | ||
migrations.RenameField( | ||
model_name='accountingexport', | ||
old_name='sage_300_errors', | ||
new_name='sage300_errors', | ||
), | ||
migrations.AddField( | ||
model_name='accountingexport', | ||
name='export_id', | ||
field=sage_desktop_api.models.fields.StringNullField(help_text='id of the exported expense', max_length=255, null=True), | ||
), | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
from django.db import models | ||
|
||
from fyle_accounting_mappings.models import CategoryMapping | ||
|
||
from apps.sage300.exports.base_model import BaseExportModel | ||
from apps.accounting_exports.models import AccountingExport | ||
from apps.fyle.models import Expense, DependentFieldSetting | ||
from apps.workspaces.models import AdvancedSetting | ||
|
||
from sage_desktop_api.models.fields import ( | ||
CustomDateTimeField, | ||
FloatNullField, | ||
StringNullField, | ||
TextNotNullField | ||
) | ||
|
||
|
||
class DirectCost(BaseExportModel): | ||
""" | ||
Direct Cost Model | ||
""" | ||
|
||
accounting_export = models.OneToOneField(AccountingExport, on_delete=models.PROTECT, help_text='Accounting Export reference') | ||
accounting_date = CustomDateTimeField(help_text='accounting date of direct cost') | ||
code = StringNullField(max_length=10, help_text='unique code for invoice') | ||
expense = models.OneToOneField(Expense, on_delete=models.PROTECT, help_text='Reference to Expense') | ||
amount = FloatNullField(help_text='Amount of the invoice') | ||
category_id = StringNullField(help_text='destination id of category') | ||
commitment_id = StringNullField(help_text='destination id of commitment') | ||
cost_code_id = StringNullField(help_text='destination id of cost code') | ||
credit_card_account_id = StringNullField(help_text='destination id of credit card account') | ||
debit_card_account_id = StringNullField(help_text='destination id of debit card account') | ||
description = TextNotNullField(help_text='description for the invoice') | ||
job_id = StringNullField(help_text='destination id of job') | ||
standard_category_id = StringNullField(help_text='destination id of standard category') | ||
standard_cost_code_id = StringNullField(help_text='destination id of standard cost code') | ||
|
||
class Meta: | ||
db_table = 'direct_costs' | ||
|
||
@classmethod | ||
def create_or_update_object(self, accounting_export: AccountingExport, advance_setting: AdvancedSetting): | ||
""" | ||
Create Direct Cost | ||
:param accounting_export: expense group | ||
:return: Direct cost object | ||
""" | ||
expenses = accounting_export.expenses.all() | ||
dependent_field_setting = DependentFieldSetting.objects.filter(workspace_id=accounting_export.workspace_id).first() | ||
|
||
cost_category_id = None | ||
cost_code_id = None | ||
|
||
direct_cost_objects = [] | ||
|
||
for lineitem in expenses: | ||
account = CategoryMapping.objects.filter( | ||
source_category__value=lineitem.category, | ||
workspace_id=accounting_export.workspace_id | ||
).first() | ||
|
||
job_id = self.get_job_id(accounting_export, lineitem) | ||
commitment_id = self.get_commitment_id(accounting_export, lineitem) | ||
standard_category_id = self.get_standard_category_id(accounting_export, lineitem) | ||
standard_cost_code_id = self.get_standard_cost_code_id(accounting_export, lineitem) | ||
description = self.get_expense_purpose(accounting_export.workspace_id, lineitem, lineitem.category, advance_setting) | ||
|
||
if dependent_field_setting: | ||
cost_category_id = self.get_cost_category_id(accounting_export, lineitem, dependent_field_setting, job_id) | ||
cost_code_id = self.get_cost_code_id(accounting_export, lineitem, dependent_field_setting, job_id, cost_category_id) | ||
|
||
direct_cost_object, _ = DirectCost.objects.update_or_create( | ||
expense_id=lineitem.id, | ||
accounting_export=accounting_export, | ||
defaults={ | ||
'amount': lineitem.amount, | ||
'credit_card_account_id': account.destination_account.destination_id, | ||
'job_id': job_id, | ||
'commitment_id': commitment_id, | ||
'standard_category_id': standard_category_id, | ||
'standard_cost_code_id': standard_cost_code_id, | ||
'category_id': cost_category_id, | ||
'cost_code_id': cost_code_id, | ||
'description': description, | ||
'workspace_id': accounting_export.workspace_id | ||
} | ||
) | ||
direct_cost_objects.append(direct_cost_object) | ||
|
||
return direct_cost_objects |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
from typing import List | ||
from django.db.models import Q | ||
from django_q.tasks import Chain | ||
|
||
from apps.accounting_exports.models import AccountingExport | ||
from apps.workspaces.models import FyleCredential | ||
|
||
|
||
def check_accounting_export_and_start_import(workspace_id: int, accounting_export_ids: List[str]): | ||
""" | ||
Check accounting export group and start export | ||
""" | ||
|
||
fyle_credentials = FyleCredential.objects.filter(workspace_id=workspace_id).first() | ||
|
||
accounting_exports = AccountingExport.objects.filter( | ||
~Q(status__in=['IN_PROGRESS', 'COMPLETE']), | ||
workspace_id=workspace_id, id__in=accounting_export_ids, directcost__id__isnull=True, | ||
exported_at__isnull=True | ||
).all() | ||
|
||
chain = Chain() | ||
chain.append('apps.fyle.helpers.sync_dimensions', fyle_credentials) | ||
|
||
for index, accounting_export_group in enumerate(accounting_exports): | ||
accounting_export, _ = AccountingExport.objects.update_or_create( | ||
workspace_id=accounting_export_group.workspace_id, | ||
id=accounting_export_group.id, | ||
defaults={ | ||
'status': 'ENQUEUED', | ||
'type': 'PURCHASE_INVOICE' | ||
} | ||
) | ||
|
||
if accounting_export.status not in ['IN_PROGRESS', 'ENQUEUED']: | ||
accounting_export.status = 'ENQUEUED' | ||
accounting_export.save() | ||
|
||
""" | ||
Todo: Add last export details | ||
""" | ||
|
||
chain.append('apps.sage300.exports.direct_cost.tasks.create_direct_cost', accounting_export) | ||
|
||
if chain.length() > 1: | ||
chain.run() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
from typing import Dict | ||
|
||
from apps.sage300.exports.accounting_export import AccountingDataExporter | ||
from apps.accounting_exports.models import AccountingExport | ||
from apps.workspaces.models import Sage300Credential | ||
from apps.sage300.utils import SageDesktopConnector | ||
from apps.sage300.exports.direct_cost.queues import check_accounting_export_and_start_import | ||
from apps.sage300.exceptions import handle_sage300_exceptions | ||
from apps.sage300.exports.direct_cost.models import DirectCost | ||
|
||
|
||
class ExportDirectCost(AccountingDataExporter): | ||
""" | ||
Class for handling the export of Direct Cost to Sage 300. | ||
Extends the base AccountingDataExporter class. | ||
""" | ||
|
||
def __init__(self): | ||
super().__init__() # Call the constructor of the parent class | ||
self.body_model = DirectCost | ||
|
||
def trigger_export(self, workspace_id, accounting_export_ids): | ||
""" | ||
Trigger the import process for the Project module. | ||
""" | ||
check_accounting_export_and_start_import(workspace_id, accounting_export_ids) | ||
|
||
def __construct_direct_cost(self, body: DirectCost) -> Dict: | ||
""" | ||
Construct the payload for the direct invoice. | ||
:param expense_report: ExpenseReport object extracted from database | ||
:param expense_report_lineitems: ExpenseReportLineitem objects extracted from database | ||
:return: constructed expense_report | ||
""" | ||
|
||
transaction_date = '2023-08-17' | ||
direct_cost_payload = { | ||
"AccountingDate": transaction_date, | ||
"Amount": body.amount, | ||
"Code": 234234, | ||
"CategoryId": body.category_id, | ||
"CostCodeId": body.cost_code_id, | ||
"CreditAccountId": body.credit_card_account_id, | ||
"DebitAccountId": body.debit_card_account_id, | ||
"Description": "Fyle - Line 1 Wow", | ||
"JobId": body.job_id, | ||
"TransactionDate": transaction_date, | ||
"StandardCategoryId": body.standard_category_id, | ||
"StandardCostCodeId": body.standard_cost_code_id, | ||
"TransactionType": 1 | ||
} | ||
|
||
return direct_cost_payload | ||
|
||
def post(self, accounting_export, item, lineitem = None): | ||
""" | ||
Export the direct cost to Sage 300. | ||
""" | ||
|
||
direct_cost_payload = self.__construct_direct_cost(item) | ||
sage300_credentials = Sage300Credential.objects.filter(workspace_id=accounting_export.workspace_id).first() | ||
# Establish a connection to Sage 300 | ||
sage300_connection = SageDesktopConnector(sage300_credentials, accounting_export.workspace_id) | ||
|
||
# Post the direct cost to Sage 300 | ||
created_direct_cost_export_id = sage300_connection.connection.direct_costs.post_direct_cost(direct_cost_payload) | ||
|
||
accounting_export.export_id = created_direct_cost_export_id | ||
accounting_export.save() | ||
|
||
exported_direct_cost_id = sage300_connection.connection.direct_costs.export_direct_cost(created_direct_cost_export_id) | ||
|
||
return exported_direct_cost_id | ||
|
||
|
||
@handle_sage300_exceptions() | ||
def create_direct_cost(accounting_export: AccountingExport): | ||
""" | ||
Helper function to create and export a direct cost. | ||
""" | ||
export_direct_cost_instance = ExportDirectCost() | ||
|
||
# Create and export the direct cost using the base class method | ||
exported_direct_cost = export_direct_cost_instance.create_sage300_object(accounting_export=accounting_export) | ||
|
||
return exported_direct_cost |
Oops, something went wrong.