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

Disable sage fields #183

Merged
merged 20 commits into from
Jun 10, 2024
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion apps/accounting_exports/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ def _group_expenses(expenses: List[Expense], export_setting: ExportSetting, fund
reimbursable_expense_date = export_setting.reimbursable_expense_date

default_fields = ['employee_email', 'fund_source']
report_grouping_fields = ['report_id', 'claim_number']
report_grouping_fields = ['report_id', 'claim_number', 'corporate_card_id']
expense_grouping_fields = ['expense_id', 'expense_number']

# Define a mapping for fund sources and their associated group fields
Expand Down
5 changes: 2 additions & 3 deletions apps/sage300/dependent_fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,7 @@ def create_dependent_custom_field_in_fyle(workspace_id: int, fyle_attribute_type
return platform.expense_custom_fields.post(expense_custom_field_payload)


def post_dependent_cost_code(dependent_field_setting: DependentFieldSetting, platform: PlatformConnector, filters: Dict) -> List[str]:

def post_dependent_cost_code(dependent_field_setting: DependentFieldSetting, platform: PlatformConnector, filters: Dict, is_enabled: bool = None) -> List[str]:
projects = CostCategory.objects.filter(**filters).values('job_name').annotate(cost_codes=ArrayAgg('cost_code_name', distinct=True))
projects_from_categories = [project['job_name'] for project in projects]
posted_cost_codes = []
Expand All @@ -91,7 +90,7 @@ def post_dependent_cost_code(dependent_field_setting: DependentFieldSetting, pla
'parent_expense_field_value': project['job_name'],
'expense_field_id': dependent_field_setting.cost_code_field_id,
'expense_field_value': cost_code,
'is_enabled': True
'is_enabled': is_enabled if is_enabled is not None else True
})
cost_code_names.append(cost_code)

Expand Down
29 changes: 14 additions & 15 deletions apps/sage300/exports/base_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

from django.db import models
from django.db.models import Sum
from fyle_accounting_mappings.models import ExpenseAttribute, Mapping, MappingSetting, EmployeeMapping, DestinationAttribute
from fyle_accounting_mappings.models import ExpenseAttribute, Mapping, MappingSetting, EmployeeMapping

from apps.accounting_exports.models import AccountingExport
from apps.fyle.models import DependentFieldSetting, Expense
Expand Down Expand Up @@ -60,7 +60,6 @@ def get_expense_purpose(workspace_id, lineitem: Expense, category: str, advance_
def get_vendor_id(accounting_export: AccountingExport):
# Retrieve export settings for the given workspace
export_settings = ExportSetting.objects.get(workspace_id=accounting_export.workspace_id)

# Extract the description from the accounting export
description = accounting_export.description

Expand All @@ -81,22 +80,22 @@ def get_vendor_id(accounting_export: AccountingExport):
# Check if the fund source is 'CCC'
elif accounting_export.fund_source == 'CCC':
# Retrieve the vendor from the first expense
expense_vendor = accounting_export.expenses.first().vendor
vendor_id = None
corporate_card_id = accounting_export.expenses.first().corporate_card_id

# Query DestinationAttribute for the vendor with case-insensitive search
if expense_vendor:
vendor = DestinationAttribute.objects.filter(
if corporate_card_id:
vendor_mapping = Mapping.objects.filter(
workspace_id=accounting_export.workspace_id,
value__icontains=expense_vendor,
attribute_type='VENDOR'
).values_list('destination_id', flat=True).first()
if not vendor:
vendor = export_settings.default_vendor_id
else:
vendor = export_settings.default_vendor_id
source_type='CORPORATE_CARD',
destination_type='VENDOR',
source__source_id=corporate_card_id
).first()

# Update vendor_id with the retrieved vendor or default to export settings
vendor_id = vendor
if vendor_mapping:
vendor_id = vendor_mapping.destination.destination_id

if not vendor_id:
vendor_id = export_settings.default_vendor_id

# Return the determined vendor_id
return vendor_id
Expand Down
82 changes: 80 additions & 2 deletions apps/sage300/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,16 @@
from datetime import datetime, timezone
import logging

from typing import Dict
from django.utils.module_loading import import_string

from apps.workspaces.models import Workspace, Sage300Credential
from apps.workspaces.models import Workspace, Sage300Credential, FyleCredential
from apps.mappings.models import Version

from fyle_accounting_mappings.models import ExpenseAttribute
from fyle_integrations_platform_connector import PlatformConnector
from apps.sage300.models import CostCategory
from apps.fyle.models import DependentFieldSetting
from apps.sage300.dependent_fields import post_dependent_cost_code

logger = logging.getLogger(__name__)
logger.level = logging.INFO
Expand Down Expand Up @@ -62,3 +67,76 @@ def sync_dimensions(sage300_credential: Sage300Credential, workspace_id: int) ->
except Exception as exception:
# Log any exceptions that occur during synchronization
logger.info(exception)


def disable_projects(workspace_id: int, projects_to_disable: Dict):
Hrishabh17 marked this conversation as resolved.
Show resolved Hide resolved
"""
Disable projects in Fyle when the projects are updated in Sage 300.
This is a callback function that is triggered from accounting_mappings.
"""
filters = {
'workspace_id': workspace_id,
'attribute_type': 'PROJECT',
'value__in': [projects_map['value'] for projects_map in projects_to_disable.values()]
}

value_id_map = {v['value']: k for k, v in projects_to_disable.items()}

expense_attributes = ExpenseAttribute.objects.filter(**filters)

bulk_payload = []
for expense_attribute in expense_attributes:
code = value_id_map.get(expense_attribute.value, None)
Hrishabh17 marked this conversation as resolved.
Show resolved Hide resolved
if code:
payload = {
'name': expense_attribute.value,
'code': code,
'description': 'Sage 300 Project - {0}, Id - {1}'.format(
expense_attribute.value,
code
),
'is_enabled': False,
'id': expense_attribute.source_id
}

bulk_payload.append(payload)

sync_after = datetime.now(timezone.utc)

fyle_credentials = FyleCredential.objects.get(workspace_id=workspace_id)
platform = PlatformConnector(fyle_credentials=fyle_credentials)

if bulk_payload:
platform.projects.post_bulk(bulk_payload)
platform.projects.sync(sync_after=sync_after)

update_and_disable_cost_code(workspace_id, projects_to_disable, platform)
Hrishabh17 marked this conversation as resolved.
Show resolved Hide resolved


def update_and_disable_cost_code(workspace_id: int, cost_codes_to_disable: Dict, platform: PlatformConnector):
"""
Update the job_name in CostCategory and disable the old cost code in Fyle
"""
dependent_field_setting = DependentFieldSetting.objects.filter(workspace_id=workspace_id).first()
Hrishabh17 marked this conversation as resolved.
Show resolved Hide resolved

filters = {
'job_id__in':list(cost_codes_to_disable.keys()),
'workspace_id': workspace_id
}

post_dependent_cost_code(dependent_field_setting, platform, filters, is_enabled=False)
Hrishabh17 marked this conversation as resolved.
Show resolved Hide resolved
Hrishabh17 marked this conversation as resolved.
Show resolved Hide resolved

bulk_update_payload = []
for destination_id, value in cost_codes_to_disable.items():
cost_categories = CostCategory.objects.filter(
workspace_id=workspace_id,
job_id=destination_id
).exclude(job_name=value['updated_value'])

for cost_category in cost_categories:
cost_category.job_name = value['updated_value']
cost_category.updated_at = datetime.now(timezone.utc)
bulk_update_payload.append(cost_category)

if bulk_update_payload:
CostCategory.objects.bulk_update(bulk_update_payload, ['job_name', 'updated_at'], batch_size=50)
14 changes: 12 additions & 2 deletions apps/sage300/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,8 +141,18 @@ def _sync_data(self, data_gen, attribute_type, display_name, workspace_id, field
if destination_attr:
destination_attributes.append(destination_attr)

DestinationAttribute.bulk_create_or_update_destination_attributes(
destination_attributes, attribute_type, workspace_id, True)
if attribute_type == 'JOB':
project_disable_callback_path = 'apps.sage300.helpers.disable_projects'
DestinationAttribute.bulk_create_or_update_destination_attributes(
destination_attributes,
attribute_type,
workspace_id,
True,
project_disable_callback_path=project_disable_callback_path
)
else:
DestinationAttribute.bulk_create_or_update_destination_attributes(
destination_attributes, attribute_type, workspace_id, True)
else:
destination_attributes = []
for item in data_gen:
Expand Down
11 changes: 11 additions & 0 deletions apps/workspaces/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,17 @@ def create(self, validated_data):
# Update workspace onboarding state
workspace = export_settings.workspace

if export_settings.credit_card_expense_export_type == 'PURCHASE_INVOICE':
MappingSetting.objects.update_or_create(
workspace_id = workspace_id,
defaults={
'source_field':'CORPORATE_CARD',
'destination_field':'VENDOR',
'import_to_fyle': False,
'is_custom': False
}
)

if workspace.onboarding_state == 'EXPORT_SETTINGS':
workspace.onboarding_state = 'IMPORT_SETTINGS'
workspace.save()
Expand Down
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ fyle==0.36.1

# Reusable Fyle Packages
fyle-rest-auth==1.7.2
fyle-accounting-mappings==1.32.3
fyle-accounting-mappings==1.33.0
fyle-integrations-platform-connector==1.36.3

# Postgres Dependincies
Expand Down
20 changes: 20 additions & 0 deletions scripts/002_add_vendor_fyle_card_mapping.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
rollback;
begin;

insert into mapping_settings (source_field, destination_field, import_to_fyle, is_custom, workspace_id, created_at, updated_at)
select 'CORPORATE_CARD', 'VENDOR', 'f', 'f', ws.id, now(), now()
from workspaces ws
where not exists (
select 1
from mapping_settings ms
inner join export_settings es
on es.workspace_id = ms.workspace_id
where ms.source_field = 'CORPORATE_CARD'
and ms.destination_field = 'VENDOR'
and ms.workspace_id = ws.id
and es.credit_card_expense_export_type = 'PURCHASE_INVOICE'
);

select COUNT(*) from export_settings where credit_card_expense_export_type = 'PURCHASE_INVOICE';

select COUNT(distinct(workspace_id)) from mapping_settings where workspace_id not in (select distinct(workspace_id) from mapping_settings where source_field = 'CORPORATE_CARD' and destination_field = 'VENDOR');
Loading
Loading