Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update base model methods #88

Merged
17 changes: 9 additions & 8 deletions apps/sage300/exports/accounting_export.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from django.db import transaction

from apps.accounting_exports.models import AccountingExport
from apps.workspaces.models import ExportSetting
from apps.workspaces.models import AdvancedSetting


class AccountingDataExporter:
Expand All @@ -11,10 +11,11 @@ class AccountingDataExporter:
Subclasses should implement the 'post' method for posting data.
"""

body_model = None
lineitem_model = None
def __init__(self):
self.body_model = None
self.lineitem_model = None

def post(self, body, lineitems):
def post(self, workspace_id, body, lineitems):
"""
Implement this method to post data to the external accounting system.
"""
Expand All @@ -31,8 +32,8 @@ def create_sage300_object(self, accounting_export: AccountingExport):
NotImplementedError: If the method is not implemented in the subclass.
"""

# Retrieve export settings for the current workspace
export_settings = ExportSetting.objects.filter(workspace_id=accounting_export.workspace_id)
# Retrieve advance settings for the current workspace
advance_settings = AdvancedSetting.objects.filter(workspace_id=accounting_export.workspace_id).first()

# Check and update the status of the accounting export
if accounting_export.status not in ['IN_PROGRESS', 'COMPLETE']:
Expand All @@ -49,11 +50,11 @@ def create_sage300_object(self, accounting_export: AccountingExport):

# Create or update line items for the accounting object
lineitems_model_objects = self.lineitem_model.create_or_update_object(
accounting_export, export_settings
accounting_export, advance_settings
)

# Post the data to the external accounting system
created_object = self.post(body_model_object, lineitems_model_objects)
created_object = self.post(accounting_export.workspace_id, body_model_object, lineitems_model_objects)

# Update the accounting export details
accounting_export.detail = created_object
Expand Down
254 changes: 242 additions & 12 deletions apps/sage300/exports/base_model.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,27 @@
from typing import Optional
from datetime import datetime
from django.db import models
from django.db.models import Sum

from fyle_accounting_mappings.models import MappingSetting, ExpenseAttribute, Mapping

from apps.accounting_exports.models import AccountingExport
from apps.fyle.models import Expense
from apps.fyle.models import Expense, DependentFieldSetting
from apps.workspaces.models import Workspace, FyleCredential, AdvancedSetting

from apps.sage300.exports.helpers import get_filtered_mapping


class BaseExportModel(models.Model):
"""
Base Model for Sage300 Export
"""
created_at = models.DateTimeField(auto_now_add=True, help_text='Created at')
updated_at = models.DateTimeField(auto_now=True, help_text='Updated at')
workspace = models.ForeignKey(Workspace, on_delete=models.PROTECT, help_text='Reference to Workspace model')

class Meta:
abstract = True

def get_expense_purpose(workspace_id, lineitem: Expense, category: str, advance_setting: AdvancedSetting) -> str:
workspace = Workspace.objects.get(id=workspace_id)
Expand Down Expand Up @@ -49,25 +59,245 @@ def get_expense_purpose(workspace_id, lineitem: Expense, category: str, advance_
return purpose

def get_vendor_id(accounting_export: AccountingExport):
return '124'
return '3a3485d9-5cc7-4668-9557-b06100a3e8c9'

def get_total_amount(accounting_export: AccountingExport):
return '123123'
"""
Calculate the total amount of expenses associated with a given AccountingExport

Parameters:
- accounting_export (AccountingExport): The AccountingExport instance for which to calculate the total amount.

Returns:
- float: The total amount of expenses associated with the provided AccountingExport.
"""

def get_invoice_date(accounting_export: AccountingExport):
return datetime.now()
# Using the related name 'expenses' to access the expenses associated with the given AccountingExport
total_amount = accounting_export.expenses.aggregate(Sum('amount'))['amount__sum']

# If there are no expenses for the given AccountingExport, 'total_amount' will be None
# Handle this case by returning 0 or handling it as appropriate for your application
return total_amount or 0.0

def get_invoice_date(accounting_export: AccountingExport) -> str:
"""
Get the invoice date from the provided AccountingExport.

Parameters:
- accounting_export (AccountingExport): The AccountingExport instance containing the description field.

Returns:
- str: The invoice date as a string in the format '%Y-%m-%dT%H:%M:%S'.
"""
# Check for specific keys in the 'description' field and return the corresponding value
if 'spent_at' in accounting_export.description and accounting_export.description['spent_at']:
return accounting_export.description['spent_at']
elif 'approved_at' in accounting_export.description and accounting_export.description['approved_at']:
return accounting_export.description['approved_at']
elif 'verified_at' in accounting_export.description and accounting_export.description['verified_at']:
return accounting_export.description['verified_at']
elif 'last_spent_at' in accounting_export.description and accounting_export.description['last_spent_at']:
return accounting_export.description['last_spent_at']
elif 'posted_at' in accounting_export.description and accounting_export.description['posted_at']:
return accounting_export.description['posted_at']

# If none of the expected keys are present or if the values are empty, return the current date and time
return datetime.now().strftime('%Y-%m-%dT%H:%M:%S')

def get_job_id(accounting_export: AccountingExport, expense: Expense):
return '2312'
"""
Get the job ID based on the provided AccountingExport and Expense.

Parameters:
- accounting_export (AccountingExport): The AccountingExport instance containing workspace information.
- expense (Expense): The Expense instance containing information for job ID retrieval.

Returns:
- Optional[str]: The job ID as a string if found, otherwise None.
"""

job_id = None

# Retrieve mapping settings for job
job_settings: MappingSetting = MappingSetting.objects.filter(
workspace_id=accounting_export.workspace_id,
destination_field='JOB'
).first()

if job_settings:
# Determine the source value based on the configured source field
if job_settings.source_field == 'PROJECT':
source_value = expense.project
elif job_settings.source_field == 'COST_CENTER':
source_value = expense.cost_center
else:
attribute = ExpenseAttribute.objects.filter(attribute_type=job_settings.source_field).first()
source_value = expense.custom_properties.get(attribute.display_name, None)

# Check for a mapping based on the source value
mapping: Mapping = Mapping.objects.filter(
source_type=job_settings.source_field,
destination_type='JOB',
source__value=source_value,
workspace_id=accounting_export.workspace_id
).first()

# If a mapping is found, retrieve the destination job ID
if mapping:
job_id = mapping.destination.destination_id

return job_id

def get_commitment_id(accounting_export: AccountingExport, expense: Expense):
return '12312'
"""
Get the commitment ID based on the provided AccountingExport and Expense.

Parameters:
- accounting_export (AccountingExport): The AccountingExport instance containing workspace information.
- expense (Expense): The Expense instance containing information for job ID retrieval.

Returns:
- Optional[str]: The commitment ID as a string if found, otherwise None.
"""

commitment_setting: MappingSetting = MappingSetting.objects.filter(
workspace_id=accounting_export.workspace_id,
destination_field='COMMITMENT'
).first()

commitment_id = None
source_id = None

if accounting_export and commitment_setting:
if expense:
if commitment_setting.source_field == 'PROJECT':
source_id = expense.project_id
source_value = expense.project
elif commitment_setting.source_field == 'COST_CENTER':
source_value = expense.cost_center
else:
attribute = ExpenseAttribute.objects.filter(attribute_type=expense.source_field).first()
source_value = expense.custom_properties.get(attribute.display_name, None)
else:
source_value = accounting_export.description[accounting_export.source_field.lower()]

mapping: Mapping = get_filtered_mapping(
commitment_setting.source_field, 'COMMITMENT', accounting_export.workspace_id, source_value, source_id
)

def get_standard_category_id(accounting_export: AccountingExport, expense: Expense):
return '123123'
if mapping:
commitment_id = mapping.destination.destination_id
return commitment_id

def get_cost_code_id(accounting_export: AccountingExport, lineitem: Expense, dependent_field_setting: DependentFieldSetting, job_id: str):
from apps.sage300.models import CostCategory
cost_code_id = None

selected_cost_code = lineitem.custom_properties.get(dependent_field_setting.cost_code_field_name, None)
cost_code = CostCategory.objects.filter(
workspace_id=accounting_export.workspace_id,
cost_code_name=selected_cost_code,
project_id=job_id
).first()

if cost_code:
cost_code_id = cost_code.cost_code_id

return cost_code_id

def get_cost_category_id(expense_group: AccountingExport, lineitem: Expense, dependent_field_setting: DependentFieldSetting, project_id: str, cost_code_id: str):
from apps.sage300.models import CostCategory
cost_category_id = None

selected_cost_category = lineitem.custom_properties.get(dependent_field_setting.cost_type_field_name, None)
cost_category = CostCategory.objects.filter(
workspace_id=expense_group.workspace_id,
cost_code_id=cost_code_id,
project_id=project_id,
name=selected_cost_category
).first()

if cost_category:
cost_category_id = cost_category.cost_category_id

return cost_category_id

def get_standard_category_id(accounting_export: AccountingExport, expense: Expense) -> Optional[str]:
"""
Get the standard category ID based on the provided AccountingExport and Expense.

Parameters:
- accounting_export (AccountingExport): The AccountingExport instance containing workspace information.
- expense (Expense): The Expense instance containing information for standard category ID retrieval.

Returns:
- Optional[str]: The standard category ID as a string if found, otherwise None.
"""
standard_category_id = None

# Retrieve mapping settings for standard category
standard_category_setting: MappingSetting = MappingSetting.objects.filter(
workspace_id=accounting_export.workspace_id,
destination_field='STANDARD_CATEGORY'
).first()

if standard_category_setting:
# Retrieve the attribute corresponding to the source field
attribute = ExpenseAttribute.objects.filter(attribute_type=standard_category_setting.source_field).first()

# Determine the source value based on the configured source field
source_value = expense.custom_properties.get(attribute.display_name, None)

# Check for a mapping based on the source value
mapping: Mapping = Mapping.objects.filter(
source_type=standard_category_setting.source_field,
destination_type='STANDARD_CATEGORY',
source__value=source_value,
workspace_id=accounting_export.workspace_id
).first()

# If a mapping is found, retrieve the destination standard category ID
if mapping:
standard_category_id = mapping.destination.destination_id

return standard_category_id

def get_standard_cost_code_id(accounting_export: AccountingExport, expense: Expense):
return '123123'
"""
Get the standard cost code ID based on the provided AccountingExport and Expense.

class Meta:
abstract = True
Parameters:
- accounting_export (AccountingExport): The AccountingExport instance containing workspace information.
- expense (Expense): The Expense instance containing information for standard category ID retrieval.

Returns:
- Optional[str]: The standard cost code ID as a string if found, otherwise None.
"""
standard_cost_code_id = None

# Retrieve mapping settings for standard cost code
standard_cost_code_setting: MappingSetting = MappingSetting.objects.filter(
workspace_id=accounting_export.workspace_id,
destination_field='STANDARD_COST_CODE'
).first()

if standard_cost_code_setting:
# Retrieve the attribute corresponding to the source field
attribute = ExpenseAttribute.objects.filter(attribute_type=standard_cost_code_setting.source_field).first()

# Determine the source value based on the configured source field
source_value = expense.custom_properties.get(attribute.display_name, None)

# Check for a mapping based on the source value
mapping: Mapping = Mapping.objects.filter(
source_type=standard_cost_code_setting.source_field,
destination_type='STANDARD_COST_CODE',
source__value=source_value,
workspace_id=accounting_export.workspace_id
).first()

# If a mapping is found, retrieve the destination standard cost code ID
if mapping:
standard_cost_code_id = mapping.destination.destination_id

return standard_cost_code_id
17 changes: 17 additions & 0 deletions apps/sage300/exports/helpers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from fyle_accounting_mappings.models import Mapping


def get_filtered_mapping(
source_field: str, destination_type: str, workspace_id: int, source_value: str, source_id: str) -> Mapping:
filters = {
'source_type': source_field,
'destination_type': destination_type,
'workspace_id': workspace_id
}

if source_id:
filters['source__source_id'] = source_id
else:
filters['source__value'] = source_value

return Mapping.objects.filter(**filters).first()
Loading
Loading