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

add support for code prepending in CATEGORY #208

Merged
merged 5 commits into from
Jul 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
78 changes: 76 additions & 2 deletions apps/mappings/imports/modules/categories.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
import logging
from datetime import datetime
from typing import List
from typing import List, Dict
from apps.workspaces.models import ImportSetting, FyleCredential
from apps.mappings.imports.modules.base import Base
from fyle_accounting_mappings.models import DestinationAttribute, CategoryMapping
from apps.mappings.helpers import prepend_code_to_name
from fyle_integrations_platform_connector import PlatformConnector
from fyle_accounting_mappings.models import DestinationAttribute, ExpenseAttribute, CategoryMapping

logger = logging.getLogger(__name__)
logger.level = logging.INFO


class Category(Base):
Expand Down Expand Up @@ -82,3 +89,70 @@ def create_mappings(self):
self.destination_field,
self.workspace_id,
)


def disable_categories(workspace_id: int, categories_to_disable: Dict, *args, **kwargs):
"""
categories_to_disable object format:
{
'destination_id': {
'value': 'old_category_name',
'updated_value': 'new_category_name',
'code': 'old_code',
'update_code': 'new_code' ---- if the code is updated else same as code
}
}
"""
fyle_credentials = FyleCredential.objects.get(workspace_id=workspace_id)
platform = PlatformConnector(fyle_credentials=fyle_credentials)

use_code_in_naming = ImportSetting.objects.filter(workspace_id=workspace_id, import_code_fields__contains=['ACCOUNT']).first()
category_account_mapping = CategoryMapping.objects.filter(
workspace_id=workspace_id,
destination_account__destination_id__in=categories_to_disable.keys()
)

logger.info(f"Deleting Category-Account Mappings | WORKSPACE_ID: {workspace_id} | COUNT: {category_account_mapping.count()}")
category_account_mapping.delete()

category_values = []
for category_map in categories_to_disable.values():
category_name = prepend_code_to_name(prepend_code_in_name=use_code_in_naming, value=category_map['value'], code=category_map['code'])
category_values.append(category_name)

filters = {
'workspace_id': workspace_id,
'attribute_type': 'CATEGORY',
'value__in': category_values,
'active': True
}

# Expense attribute value map is as follows: {old_category_name: destination_id}
expense_attribute_value_map = {}
for k, v in categories_to_disable.items():
category_name = prepend_code_to_name(prepend_code_in_name=use_code_in_naming, value=v['value'], code=v['code'])
expense_attribute_value_map[category_name] = k

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

bulk_payload = []
for expense_attribute in expense_attributes:
code = expense_attribute_value_map.get(expense_attribute.value, None)
if code:
payload = {
'name': expense_attribute.value,
'code': code,
'is_enabled': False,
'id': expense_attribute.source_id
}
bulk_payload.append(payload)
else:
logger.error(f"Category not found in categories_to_disable: {expense_attribute.value}")

if bulk_payload:
logger.info(f"Disabling Category in Fyle | WORKSPACE_ID: {workspace_id} | COUNT: {len(bulk_payload)}")
platform.categories.post_bulk(bulk_payload)
else:
logger.info(f"No Categories to Disable in Fyle | WORKSPACE_ID: {workspace_id}")

return bulk_payload
85 changes: 83 additions & 2 deletions apps/mappings/imports/modules/cost_centers.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
import logging
from datetime import datetime
from typing import List
from typing import List, Dict
from apps.mappings.imports.modules.base import Base
from fyle_accounting_mappings.models import DestinationAttribute
from fyle_accounting_mappings.models import DestinationAttribute, ExpenseAttribute, MappingSetting, Mapping
from apps.workspaces.models import FyleCredential, ImportSetting
from fyle_integrations_platform_connector import PlatformConnector
from apps.mappings.helpers import prepend_code_to_name

logger = logging.getLogger(__name__)
logger.level = logging.INFO


class CostCenter(Base):
Expand Down Expand Up @@ -56,3 +63,77 @@ def construct_fyle_payload(
payload.append(cost_center)

return payload


def disable_cost_centers(workspace_id: int, cost_centers_to_disable: Dict, *args, **kwargs):
"""
cost_centers_to_disable object format:
{
'destination_id': {
'value': 'old_cost_center_name',
'updated_value': 'new_cost_center_name',
'code': 'old_code',
'update_code': 'new_code' ---- if the code is updated else same as code
}
}
"""
destination_type = MappingSetting.objects.get(workspace_id=workspace_id, source_field='COST_CENTER').destination_field
use_code_in_naming = ImportSetting.objects.filter(workspace_id=workspace_id, import_code_fields__contains=[destination_type]).first()

cost_center_mappings = Mapping.objects.filter(
workspace_id=workspace_id,
source_type='COST_CENTER',
destination_type=destination_type,
destination_id__destination_id__in=cost_centers_to_disable.keys()
)

logger.info(f"Deleting Cost Center Mappings | WORKSPACE_ID: {workspace_id} | COUNT: {cost_center_mappings.count()}")
cost_center_mappings.delete()

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

cost_center_values = []
for cost_center_map in cost_centers_to_disable.values():
cost_center_name = prepend_code_to_name(prepend_code_in_name=use_code_in_naming, value=cost_center_map['value'], code=cost_center_map['code'])
cost_center_values.append(cost_center_name)

filters = {
'workspace_id': workspace_id,
'attribute_type': 'COST_CENTER',
'value__in': cost_center_values,
'active': True
}

expense_attribute_value_map = {}
for k, v in cost_centers_to_disable.items():
cost_center_name = prepend_code_to_name(prepend_code_in_name=use_code_in_naming, value=v['value'], code=v['code'])
expense_attribute_value_map[cost_center_name] = k

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

bulk_payload = []
for expense_attribute in expense_attributes:
code = expense_attribute_value_map.get(expense_attribute.value, None)
if code:
payload = {
'name': expense_attribute.value,
'code': code,
'is_enabled': False,
'id': expense_attribute.source_id,
'description': 'Cost Center - {0}, Id - {1}'.format(
expense_attribute.value,
code
)
}
bulk_payload.append(payload)
else:
logger.error(f"Cost Center with value {expense_attribute.value} not found | WORKSPACE_ID: {workspace_id}")

if bulk_payload:
logger.info(f"Disabling Cost Center in Fyle | WORKSPACE_ID: {workspace_id} | COUNT: {len(bulk_payload)}")
platform.cost_centers.post_bulk(bulk_payload)
else:
logger.info(f"No Cost Center to Disable in Fyle | WORKSPACE_ID: {workspace_id}")

return bulk_payload
32 changes: 31 additions & 1 deletion apps/mappings/imports/modules/expense_custom_fields.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,21 @@
import logging
from datetime import datetime
from typing import List, Dict
from apps.mappings.imports.modules.base import Base
from fyle_accounting_mappings.models import (
DestinationAttribute,
ExpenseAttribute
ExpenseAttribute,
Mapping
)
from apps.mappings.exceptions import handle_import_exceptions
from apps.mappings.models import ImportLog
from fyle_integrations_platform_connector import PlatformConnector
from apps.workspaces.models import FyleCredential
from apps.mappings.constants import FYLE_EXPENSE_SYSTEM_FIELDS

logger = logging.getLogger(__name__)
logger.level = logging.INFO


class ExpenseCustomField(Base):
"""
Expand Down Expand Up @@ -176,3 +181,28 @@ def import_destination_attribute_to_fyle(self, import_log: ImportLog):
self.sync_expense_attributes(platform)

self.create_mappings()


def disable_custom_attributes(workspace_id: int, custom_fields_to_disable: Dict, *args, **kwargs):
"""
custom_fields_to_disable object format:
{
'destination_id': {
'value': 'old_custom_field_name',
'updated_value': 'new_custom_field_name',
'code': 'old_code',
'update_code': 'new_code' ---- if the code is updated else same as code
}
}

Currently JOB is only field that can be imported as Custom Field, may need to
update this function if more fields are added in future
"""
custom_field_mappings = Mapping.objects.filter(
workspace_id=workspace_id,
destination_type='JOB',
destination_id__destination_id__in=custom_fields_to_disable.keys()
)

logger.info(f"Deleting Custom Field Mappings | WORKSPACE_ID: {workspace_id} | COUNT: {custom_field_mappings.count()}")
custom_field_mappings.delete()
50 changes: 47 additions & 3 deletions apps/mappings/imports/modules/merchants.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
import logging
from datetime import datetime
from typing import List
from typing import List, Dict
from apps.mappings.imports.modules.base import Base
from fyle_accounting_mappings.models import DestinationAttribute
from fyle_accounting_mappings.models import DestinationAttribute, ExpenseAttribute
from apps.mappings.models import ImportLog
from apps.mappings.exceptions import handle_import_exceptions
from apps.workspaces.models import FyleCredential
from apps.workspaces.models import FyleCredential, ImportSetting
from fyle_integrations_platform_connector import PlatformConnector
from apps.mappings.helpers import prepend_code_to_name

logger = logging.getLogger(__name__)
logger.level = logging.INFO


class Merchant(Base):
Expand Down Expand Up @@ -68,3 +73,42 @@ def import_destination_attribute_to_fyle(self, import_log: ImportLog):
self.construct_payload_and_import_to_fyle(platform, import_log)

self.sync_expense_attributes(platform)


def disable_merchants(workspace_id: int, merchants_to_disable: Dict, *args, **kwargs):
"""
merchants_to_disable object format:
{
'destination_id': {
'value': 'old_merchant_name',
'updated_value': 'new_merchant_name',
'code': 'old_code',
'update_code': 'new_code' ---- if the code is updated else same as code
}
}
"""
fyle_credentials = FyleCredential.objects.get(workspace_id=workspace_id)
platform = PlatformConnector(fyle_credentials=fyle_credentials)
use_code_in_naming = ImportSetting.objects.filter(workspace_id = workspace_id, import_code_fields__contains=['VENDOR']).first()

merchant_values = []
for merchant_map in merchants_to_disable.values():
merchant_name = prepend_code_to_name(prepend_code_in_name=use_code_in_naming, value=merchant_map['value'], code=merchant_map['code'])
merchant_values.append(merchant_name)

filters = {
'workspace_id': workspace_id,
'attribute_type': 'MERCHANT',
'value__in': merchant_values,
'active': True
}

bulk_payload = ExpenseAttribute.objects.filter(**filters).values_list('value', flat=True)

if bulk_payload:
logger.info(f"Disabling Merchants in Fyle | WORKSPACE_ID: {workspace_id} | COUNT: {len(bulk_payload)}")
platform.merchants.post(bulk_payload, delete_merchants=True)
else:
logger.info(f"No Merchants to Disable in Fyle | WORKSPACE_ID: {workspace_id}")

return bulk_payload
8 changes: 6 additions & 2 deletions apps/mappings/imports/queues.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,15 +45,19 @@ def chain_import_fields_to_fyle(workspace_id):
'apps.mappings.imports.tasks.trigger_import_via_schedule',
workspace_id,
'ACCOUNT',
'CATEGORY'
'CATEGORY',
False,
True if 'ACCOUNT' in import_code_fields else False
)

if import_settings.import_vendors_as_merchants:
chain.append(
'apps.mappings.imports.tasks.trigger_import_via_schedule',
workspace_id,
'VENDOR',
'MERCHANT'
'MERCHANT',
False,
True if 'VENDOR' in import_code_fields else False
)

for mapping_setting in mapping_settings:
Expand Down
3 changes: 1 addition & 2 deletions apps/sage300/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,11 +135,10 @@ def disable_projects(workspace_id: int, projects_to_disable: Dict, *args, **kwar
'is_enabled': False,
'id': expense_attribute.source_id
}
bulk_payload.append(payload)
else:
logger.error(f"Project with value {expense_attribute.value} not found | WORKSPACE_ID: {workspace_id}")

bulk_payload.append(payload)

if bulk_payload:
logger.info(f"Disabling Projects in Fyle | WORKSPACE_ID: {workspace_id} | COUNT: {len(bulk_payload)}")
platform.projects.post_bulk(bulk_payload)
Expand Down
25 changes: 21 additions & 4 deletions apps/sage300/utils.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import logging
from django.utils.module_loading import import_string
from fyle_accounting_mappings.models import DestinationAttribute
from fyle_accounting_mappings.models import DestinationAttribute, MappingSetting
from apps.workspaces.models import Sage300Credential
from sage_desktop_sdk.sage_desktop_sdk import SageDesktopSDK
from apps.sage300.models import CostCategory
Expand All @@ -11,6 +11,15 @@
logger.level = logging.INFO


ATTRIBUTE_CALLBACK_MAP = {
'PROJECT': 'apps.sage300.helpers.disable_projects',
'CATEGORY': 'apps.mappings.imports.modules.categories.disable_categories',
'MERCHANT': 'apps.mappings.imports.modules.merchants.disable_merchants',
'COST_CENTER': 'apps.mappings.imports.modules.cost_centers.disable_cost_centers',
'CUSTOM': 'apps.mappings.imports.modules.expense_custom_fields.disable_custom_attributes'
}


class SageDesktopConnector:
"""
Sage300 utility functions for syncing data from Sage Desktop SDK to your application
Expand Down Expand Up @@ -132,6 +141,15 @@ def _sync_data(self, data_gen, attribute_type, display_name, workspace_id, field
:param workspace_id: ID of the workspace
:param field_names: Names of fields to include in detail
"""
source_type = None
mapping_setting = MappingSetting.objects.filter(workspace_id=workspace_id, destination_field=attribute_type).first()
if mapping_setting:
if attribute_type == 'VENDOR':
source_type = 'MERCHANT'
elif mapping_setting.is_custom:
source_type = 'CUSTOM'
else:
source_type = mapping_setting.source_field

if is_generator:
for data in data_gen:
Expand All @@ -144,14 +162,13 @@ def _sync_data(self, data_gen, attribute_type, display_name, workspace_id, field
if destination_attr:
destination_attributes.append(destination_attr)

if attribute_type == 'JOB':
project_disable_callback_path = 'apps.sage300.helpers.disable_projects'
if source_type in ATTRIBUTE_CALLBACK_MAP.keys():
DestinationAttribute.bulk_create_or_update_destination_attributes(
destination_attributes,
attribute_type,
workspace_id,
True,
attribute_disable_callback_path=project_disable_callback_path
attribute_disable_callback_path=ATTRIBUTE_CALLBACK_MAP[source_type]
)
else:
DestinationAttribute.bulk_create_or_update_destination_attributes(
Expand Down
Loading
Loading