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

Job, Dep Field code-prepending support #206

Merged
merged 13 commits into from
Jul 23, 2024
11 changes: 9 additions & 2 deletions apps/mappings/imports/modules/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,14 @@ def __init__(
destination_field: str,
platform_class_name: str,
sync_after:datetime,
use_code_in_naming: bool = False
):
self.workspace_id = workspace_id
self.source_field = source_field
self.destination_field = destination_field
self.platform_class_name = platform_class_name
self.sync_after = sync_after
self.use_code_in_naming = use_code_in_naming

def get_platform_class(self, platform: PlatformConnector):
"""
Expand Down Expand Up @@ -92,9 +94,14 @@ def remove_duplicate_attributes(self, destination_attributes: List[DestinationAt
attribute_values = []

for destination_attribute in destination_attributes:
if destination_attribute.value.lower() not in attribute_values:
attribute_value = destination_attribute.value
if self.use_code_in_naming:
Hrishabh17 marked this conversation as resolved.
Show resolved Hide resolved
attribute_value = '{} {}'.format(destination_attribute.code, destination_attribute.value)

if attribute_value.lower() not in attribute_values:
destination_attribute.value = attribute_value
unique_attributes.append(destination_attribute)
attribute_values.append(destination_attribute.value.lower())
Hrishabh17 marked this conversation as resolved.
Show resolved Hide resolved
attribute_values.append(attribute_value.lower())

return unique_attributes

Expand Down
3 changes: 2 additions & 1 deletion apps/mappings/imports/modules/categories.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,14 @@ class Category(Base):
Class for Category module
"""

def __init__(self, workspace_id: int, destination_field: str, sync_after: datetime):
def __init__(self, workspace_id: int, destination_field: str, sync_after: datetime, use_code_in_naming: bool = False):
super().__init__(
workspace_id=workspace_id,
source_field="CATEGORY",
destination_field=destination_field,
platform_class_name="categories",
sync_after=sync_after,
use_code_in_naming=use_code_in_naming
)

def trigger_import(self):
Expand Down
3 changes: 2 additions & 1 deletion apps/mappings/imports/modules/cost_centers.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,14 @@ class CostCenter(Base):
Class for Cost Center module
"""

def __init__(self, workspace_id: int, destination_field: str, sync_after: datetime):
def __init__(self, workspace_id: int, destination_field: str, sync_after: datetime, use_code_in_naming: bool = False):
super().__init__(
workspace_id=workspace_id,
source_field="COST_CENTER",
destination_field=destination_field,
platform_class_name="cost_centers",
sync_after=sync_after,
use_code_in_naming=use_code_in_naming
)

def trigger_import(self):
Expand Down
5 changes: 3 additions & 2 deletions apps/mappings/imports/modules/expense_custom_fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,14 @@ class ExpenseCustomField(Base):
"""
Class for ExepenseCustomField module
"""
def __init__(self, workspace_id: int, source_field: str, destination_field: str, sync_after: datetime):
def __init__(self, workspace_id: int, source_field: str, destination_field: str, sync_after: datetime, use_code_in_naming: bool = False):
super().__init__(
workspace_id=workspace_id,
source_field=source_field,
destination_field=destination_field,
platform_class_name='expense_custom_fields',
sync_after=sync_after
sync_after=sync_after,
use_code_in_naming=use_code_in_naming
)

def trigger_import(self):
Expand Down
5 changes: 3 additions & 2 deletions apps/mappings/imports/modules/merchants.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,14 @@ class Merchant(Base):
"""
Class for Merchant module
"""
def __init__(self, workspace_id: int, destination_field: str, sync_after: datetime):
def __init__(self, workspace_id: int, destination_field: str, sync_after: datetime, use_code_in_naming: bool = False):
super().__init__(
workspace_id=workspace_id,
source_field='MERCHANT',
destination_field=destination_field,
platform_class_name='merchants',
sync_after=sync_after
sync_after=sync_after,
use_code_in_naming=use_code_in_naming
)

def trigger_import(self):
Expand Down
3 changes: 2 additions & 1 deletion apps/mappings/imports/modules/projects.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,14 @@ class Project(Base):
Class for Project module
"""

def __init__(self, workspace_id: int, destination_field: str, sync_after: datetime):
def __init__(self, workspace_id: int, destination_field: str, sync_after: datetime, use_code_in_naming: bool = False):
super().__init__(
workspace_id=workspace_id,
source_field="PROJECT",
destination_field=destination_field,
platform_class_name="projects",
sync_after=sync_after,
use_code_in_naming=use_code_in_naming
)

def trigger_import(self):
Expand Down
9 changes: 7 additions & 2 deletions apps/mappings/imports/queues.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ def chain_import_fields_to_fyle(workspace_id):
dependent_field_settings = DependentFieldSetting.objects.filter(workspace_id=workspace_id, is_import_enabled=True).first()
project_mapping = MappingSetting.objects.filter(workspace_id=workspace_id, source_field='PROJECT', import_to_fyle=True).first()

import_code_fields = import_settings.import_code_fields

chain = Chain()

if project_mapping and dependent_field_settings:
Expand Down Expand Up @@ -47,7 +49,9 @@ def chain_import_fields_to_fyle(workspace_id):
'apps.mappings.imports.tasks.trigger_import_via_schedule',
workspace_id,
mapping_setting.destination_field,
mapping_setting.source_field
mapping_setting.source_field,
False,
True if mapping_setting.destination_field in import_code_fields else False
)

for custom_fields_mapping_setting in custom_field_mapping_settings:
Expand All @@ -56,7 +60,8 @@ def chain_import_fields_to_fyle(workspace_id):
workspace_id,
custom_fields_mapping_setting.destination_field,
custom_fields_mapping_setting.source_field,
True
True,
True if custom_fields_mapping_setting.destination_field in import_code_fields else False
)

if project_mapping and dependent_field_settings:
Expand Down
12 changes: 9 additions & 3 deletions apps/mappings/imports/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,13 @@
}


def trigger_import_via_schedule(workspace_id: int, destination_field: str, source_field: str, is_custom: bool = False):
def trigger_import_via_schedule(
workspace_id: int,
destination_field: str,
source_field: str,
is_custom: bool = False,
use_code_in_naming: bool = False
):
"""
Trigger import via schedule
:param workspace_id: Workspace id
Expand All @@ -30,9 +36,9 @@ def trigger_import_via_schedule(workspace_id: int, destination_field: str, sourc
sync_after = import_log.last_successful_run_at if import_log else None

if is_custom:
item = ExpenseCustomField(workspace_id, source_field, destination_field, sync_after)
item = ExpenseCustomField(workspace_id, source_field, destination_field, sync_after, use_code_in_naming)
item.trigger_import()
else:
module_class = SOURCE_FIELD_CLASS_MAP[source_field]
item = module_class(workspace_id, destination_field, sync_after)
item = module_class(workspace_id, destination_field, sync_after, use_code_in_naming)
item.trigger_import()
90 changes: 76 additions & 14 deletions apps/sage300/dependent_fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
from datetime import datetime
from typing import Dict, List
from time import sleep
from django.contrib.postgres.aggregates import ArrayAgg
from django.contrib.postgres.aggregates import JSONBAgg
from django.contrib.postgres.fields import JSONField
from django.db.models import F, Func, Value

from fyle_accounting_mappings.models import ExpenseAttribute
from fyle_integrations_platform_connector import PlatformConnector
Expand All @@ -13,6 +15,7 @@
from apps.fyle.helpers import connect_to_platform
from apps.mappings.models import ImportLog
from apps.mappings.exceptions import handle_import_exceptions
from apps.workspaces.models import ImportSetting

logger = logging.getLogger(__name__)
logger.level = logging.INFO
Expand Down Expand Up @@ -72,12 +75,40 @@ def create_dependent_custom_field_in_fyle(workspace_id: int, fyle_attribute_type

@handle_import_exceptions
def post_dependent_cost_code(import_log: ImportLog, dependent_field_setting: DependentFieldSetting, platform: PlatformConnector, filters: Dict, is_enabled: bool = True) -> 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]
import_settings = ImportSetting.objects.filter(workspace_id=import_log.workspace.id).first()
use_job_code_in_naming = False
use_cost_code_in_naming = False

if 'JOB' in import_settings.import_code_fields:
use_job_code_in_naming = True
if 'COST_CODE' in import_settings.import_code_fields:
use_cost_code_in_naming = True
Hrishabh17 marked this conversation as resolved.
Show resolved Hide resolved

projects = (
CostCategory.objects.filter(**filters)
.values('job_name', 'job_code')
.annotate(
cost_codes=JSONBAgg(
Func(
Value('cost_code_name'), F('cost_code_name'),
Value('cost_code_code'), F('cost_code_code'),
function='jsonb_build_object'
),
output_field=JSONField(),
distinct=True
)
)
)

projects_from_categories = []
posted_cost_codes = []
processed_batches = 0
is_errored = False

for project in projects:
project_name = "{} {}".format(project['job_code'], project['job_name']) if use_job_code_in_naming else project['job_name']
Hrishabh17 marked this conversation as resolved.
Show resolved Hide resolved
projects_from_categories.append(project_name)

existing_projects_in_fyle = ExpenseAttribute.objects.filter(
workspace_id=dependent_field_setting.workspace_id,
attribute_type='PROJECT',
Expand All @@ -91,17 +122,19 @@ def post_dependent_cost_code(import_log: ImportLog, dependent_field_setting: Dep
for project in projects:
payload = []
cost_code_names = []
project_name = "{} {}".format(project['job_code'], project['job_name']) if use_job_code_in_naming else project['job_name']
Hrishabh17 marked this conversation as resolved.
Show resolved Hide resolved

for cost_code in project['cost_codes']:
if project['job_name'] in existing_projects_in_fyle:
if project_name in existing_projects_in_fyle:
cost_code_name = "{} {}".format(cost_code['cost_code_code'], cost_code['cost_code_name']) if use_cost_code_in_naming else cost_code['cost_code_name']
Hrishabh17 marked this conversation as resolved.
Show resolved Hide resolved
payload.append({
'parent_expense_field_id': dependent_field_setting.project_field_id,
'parent_expense_field_value': project['job_name'],
'parent_expense_field_value': project_name,
'expense_field_id': dependent_field_setting.cost_code_field_id,
'expense_field_value': cost_code,
'expense_field_value': cost_code_name,
'is_enabled': is_enabled
})
cost_code_names.append(cost_code)
cost_code_names.append(cost_code['cost_code_name'])

if payload:
sleep(0.2)
Expand All @@ -116,14 +149,39 @@ def post_dependent_cost_code(import_log: ImportLog, dependent_field_setting: Dep
import_log.status = 'PARTIALLY_FAILED' if is_errored else 'COMPLETE'
import_log.error_log = []
import_log.processed_batches_count = processed_batches
import_log.last_successful_run_at = datetime.now() if not is_errored else import_log.last_successful_run_at
Hrishabh17 marked this conversation as resolved.
Show resolved Hide resolved
import_log.save()

return posted_cost_codes, is_errored


@handle_import_exceptions
def post_dependent_cost_type(import_log: ImportLog, dependent_field_setting: DependentFieldSetting, platform: PlatformConnector, filters: Dict, posted_cost_codes: List = []):
cost_categories = CostCategory.objects.filter(is_imported=False, **filters).values('cost_code_name').annotate(cost_categories=ArrayAgg('name', distinct=True))
import_settings = ImportSetting.objects.filter(workspace_id=import_log.workspace.id).first()
use_cost_code_in_naming = False
use_category_code_in_naming = False

if 'COST_CODE' in import_settings.import_code_fields:
use_cost_code_in_naming = True
if 'COST_CATEGORY' in import_settings.import_code_fields:
use_category_code_in_naming = True
Hrishabh17 marked this conversation as resolved.
Show resolved Hide resolved

cost_categories = (
CostCategory.objects.filter(is_imported=False, **filters)
.values('cost_code_name', 'cost_code_code')
.annotate(
cost_categories=JSONBAgg(
Func(
Value('cost_category_name'), F('name'),
Value('cost_category_code'), F('cost_category_code'),
function='jsonb_build_object'
),
output_field=JSONField(),
distinct=True
)
)
)

is_errored = False
processed_batches = 0

Expand All @@ -132,15 +190,18 @@ def post_dependent_cost_type(import_log: ImportLog, dependent_field_setting: Dep

for category in cost_categories:
if category['cost_code_name'] in posted_cost_codes:
payload = [
{
cost_code_name = "{} {}".format(category['cost_code_code'], category['cost_code_name']) if use_cost_code_in_naming else category['cost_code_name']
Hrishabh17 marked this conversation as resolved.
Show resolved Hide resolved
payload = []

for cost_type in category['cost_categories']:
cost_type_name = "{} {}".format(cost_type['cost_category_code'], cost_type['cost_category_name']) if use_category_code_in_naming else cost_type['cost_category_name']
payload.append({
'parent_expense_field_id': dependent_field_setting.cost_code_field_id,
'parent_expense_field_value': category['cost_code_name'],
'parent_expense_field_value': cost_code_name,
'expense_field_id': dependent_field_setting.cost_category_field_id,
'expense_field_value': cost_type,
'expense_field_value': cost_type_name,
'is_enabled': True
} for cost_type in category['cost_categories']
]
})

if payload:
sleep(0.2)
Expand All @@ -155,6 +216,7 @@ def post_dependent_cost_type(import_log: ImportLog, dependent_field_setting: Dep
import_log.status = 'PARTIALLY_FAILED' if is_errored else 'COMPLETE'
import_log.error_log = []
import_log.processed_batches_count = processed_batches
import_log.last_successful_run_at = datetime.now() if not is_errored else import_log.last_successful_run_at
Hrishabh17 marked this conversation as resolved.
Show resolved Hide resolved
import_log.save()

return is_errored
Expand Down
Loading
Loading