Skip to content

Commit

Permalink
Add check for syncing jobs, deps and add test cases
Browse files Browse the repository at this point in the history
  • Loading branch information
Hrishabh17 committed Jul 17, 2024
1 parent ab91b34 commit 2a6c29a
Show file tree
Hide file tree
Showing 8 changed files with 213 additions and 9 deletions.
20 changes: 20 additions & 0 deletions apps/mappings/helpers.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
from datetime import datetime, timedelta, timezone
from apps.mappings.models import ImportLog


def format_attribute_name(use_code_in_naming: bool, attribute_name: str, attribute_code: str = None) -> str:
"""
Expand All @@ -6,3 +9,20 @@ def format_attribute_name(use_code_in_naming: bool, attribute_name: str, attribu
if use_code_in_naming and attribute_code:
return "{} {}".format(attribute_code, attribute_name)
return attribute_name


def allow_job_sync(import_log: ImportLog = None) -> bool:
"""
Check if job sync is allowed
"""
time_difference = datetime.now(timezone.utc) - timedelta(minutes=30)

if (
not import_log
or import_log.status != 'COMPLETE'
or import_log.last_successful_run_at is None
or import_log.last_successful_run_at < time_difference
):
return True

return False
10 changes: 8 additions & 2 deletions apps/mappings/imports/queues.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from apps.workspaces.models import ImportSetting
from apps.fyle.models import DependentFieldSetting
from apps.mappings.models import ImportLog
from apps.mappings.helpers import allow_job_sync


def chain_import_fields_to_fyle(workspace_id):
Expand All @@ -17,10 +18,15 @@ def chain_import_fields_to_fyle(workspace_id):
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
project_import_log = ImportLog.objects.filter(workspace_id=workspace_id, attribute_type='PROJECT').first()

# We'll only sync job when the time_difference > 30 minutes to avoid
# any dependent field import issue due to timestamp on job name update
is_sync_allowed = allow_job_sync(project_import_log)

chain = Chain()

if project_mapping and dependent_field_settings:
if project_mapping and dependent_field_settings and is_sync_allowed:
cost_code_import_log = ImportLog.create('COST_CODE', workspace_id)
cost_category_import_log = ImportLog.create('COST_CATEGORY', workspace_id)
chain.append('apps.mappings.tasks.sync_sage300_attributes', 'JOB', workspace_id)
Expand Down Expand Up @@ -64,7 +70,7 @@ def chain_import_fields_to_fyle(workspace_id):
True if custom_fields_mapping_setting.destination_field in import_code_fields else False
)

if project_mapping and dependent_field_settings:
if project_mapping and dependent_field_settings and is_sync_allowed:
chain.append('apps.sage300.dependent_fields.import_dependent_fields_to_fyle', workspace_id)

if chain.length() > 0:
Expand Down
4 changes: 1 addition & 3 deletions apps/sage300/dependent_fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,6 @@ def post_dependent_cost_code(import_log: ImportLog, dependent_field_setting: Dep
for project in projects:
project_name = format_attribute_name(use_code_in_naming=use_job_code_in_naming, attribute_name=project['job_name'], attribute_code=project['job_code'])
projects_from_categories.append(project_name)
print(projects_from_categories)

existing_projects_in_fyle = ExpenseAttribute.objects.filter(
workspace_id=dependent_field_setting.workspace_id,
Expand All @@ -118,7 +117,6 @@ def post_dependent_cost_code(import_log: ImportLog, dependent_field_setting: Dep
active=True
).values_list('value', flat=True)

print(existing_projects_in_fyle)
import_log.total_batches_count = len(existing_projects_in_fyle)
import_log.save()

Expand Down Expand Up @@ -253,7 +251,7 @@ def post_dependent_expense_field_values(workspace_id: int, dependent_field_setti
return
else:
is_cost_type_errored = post_dependent_cost_type(cost_category_import_log, dependent_field_setting, platform, filters, posted_cost_codes)
if not is_cost_type_errored and not is_cost_code_errored:
if not is_cost_type_errored and not is_cost_code_errored and cost_category_import_log.processed_batches_count > 0:
DependentFieldSetting.objects.filter(workspace_id=workspace_id).update(last_successful_import_at=datetime.now())


Expand Down
1 change: 1 addition & 0 deletions apps/sage300/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ def disable_projects(workspace_id: int, projects_to_disable: Dict):

update_and_disable_cost_code(workspace_id, projects_to_disable, platform, use_code_in_naming)
platform.projects.sync()
return bulk_payload


def update_and_disable_cost_code(workspace_id: int, cost_codes_to_disable: Dict, platform: PlatformConnector, use_code_in_naming: bool):
Expand Down
60 changes: 60 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,26 @@ def add_project_mappings():
detail='Sage 300 Project - Platform APIs, Id - 10081',
active=True
)
DestinationAttribute.objects.create(
workspace_id=workspace_id,
attribute_type='JOB',
display_name='CRE Platform',
value='CRE Platform',
destination_id='10064',
detail='Sage 300 Project - CRE Platform, Id - 10064',
active=True,
code='123'
)
DestinationAttribute.objects.create(
workspace_id=workspace_id,
attribute_type='JOB',
display_name='Integrations CRE',
value='Integrations CRE',
destination_id='10081',
detail='Sage 300 Project - Integrations CRE, Id - 10081',
active=True,
code='123'
)
ExpenseAttribute.objects.create(
workspace_id=workspace_id,
attribute_type='PROJECT',
Expand All @@ -357,6 +377,24 @@ def add_project_mappings():
detail='Sage 300 Project - Platform APIs, Id - 10081',
active=True
)
ExpenseAttribute.objects.create(
workspace_id=workspace_id,
attribute_type='PROJECT',
display_name='CRE Platform',
value='123 CRE Platform',
source_id='10065',
detail='Sage 300 Project - 123 CRE Platform, Id - 10065',
active=True
)
ExpenseAttribute.objects.create(
workspace_id=workspace_id,
attribute_type='PROJECT',
display_name='Integrations CRE',
value='123 Integrations CRE',
source_id='10082',
detail='Sage 300 Project - 123 Integrations CRE, Id - 10082',
active=True
)


@pytest.fixture()
Expand Down Expand Up @@ -616,6 +654,28 @@ def add_cost_category(create_temp_workspace):
workspace = Workspace.objects.get(id=workspace_id),
is_imported = False
)
CostCategory.objects.create(
job_id='10065',
job_name='Integrations CRE',
cost_code_id='cost_code_id_123',
cost_code_name='Integrations CRE',
name='Integrations',
cost_category_id='cost_category_id_456',
status=True,
workspace = Workspace.objects.get(id=workspace_id),
is_imported = False
)
CostCategory.objects.create(
job_id='10082',
job_name='CRE Platform',
cost_code_id='cost_code_id_545',
cost_code_name='CRE Platform',
name='CRE',
cost_category_id='cost_category_id_583',
status=True,
workspace = Workspace.objects.get(id=workspace_id),
is_imported = False
)


@pytest.fixture()
Expand Down
36 changes: 35 additions & 1 deletion tests/test_mappings/test_helpers.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
from apps.mappings.helpers import format_attribute_name
from datetime import datetime, timedelta, timezone
from apps.mappings.helpers import format_attribute_name, allow_job_sync
from apps.mappings.models import ImportLog


def test_format_attribute_name():
Expand All @@ -17,3 +19,35 @@ def test_format_attribute_name():
# Test case 4: use_code_in_naming is False and attribute_code is None
result = format_attribute_name(False, "attribute_name", None)
assert result == "attribute_name"


def test_allow_job_sync(db, create_temp_workspace):
import_log = ImportLog.create('PROJECT', 1)

# Test case 1: import_log is None
result = allow_job_sync(None)
assert result is True

# Test case 2: import_log is not None and last_successful_run_at is None
import_log.last_successful_run_at = None
import_log.status = 'COMPLETE'
result = allow_job_sync(import_log)
assert result is True

# Test case 3: import_log is not None and status is not 'COMPLETE'
import_log.last_successful_run_at = '2021-01-01T00:00:00Z'
import_log.status = 'FATAL'
result = allow_job_sync(import_log)
assert result is True

# Test case 4: import_log is not None and last_successful_run_at is less than 30 minutes
import_log.last_successful_run_at = datetime.now(timezone.utc) - timedelta(minutes=29)
import_log.status = 'COMPLETE'
result = allow_job_sync(import_log)
assert result is False

# Test case 5: import_log is not None and last_successful_run_at is greater than 30 minutes
import_log.last_successful_run_at = datetime.now(timezone.utc) - timedelta(minutes=31)
import_log.status = 'COMPLETE'
result = allow_job_sync(import_log)
assert result is True
30 changes: 30 additions & 0 deletions tests/test_sage300/test_dependent_fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
)
from apps.fyle.models import DependentFieldSetting
from apps.mappings.models import ImportLog
from apps.sage300.models import CostCategory
from apps.workspaces.models import ImportSetting


def test_construct_custom_field_placeholder():
Expand Down Expand Up @@ -86,6 +88,19 @@ def test_post_dependent_cost_code(
)
assert cost_code_import_log.status == 'FATAL'

# Code pre-prepend case
ImportSetting.objects.filter(workspace_id=workspace_id).update(import_code_fields=['JOB', 'COST_CODE', 'COST_CATEGORY'])
CostCategory.objects.filter(workspace_id=workspace_id).update(job_code='123', cost_code_code='456', cost_category_code='789')

result, is_errored = post_dependent_cost_code(
cost_code_import_log,
dependent_field_setting=dependent_field_settings,
platform=platform.return_value,
filters=filters
)
assert result == ['CRE Platform', 'Integrations CRE']
assert is_errored == False


def test_post_dependent_cost_type(
db,
Expand Down Expand Up @@ -131,6 +146,21 @@ def test_post_dependent_cost_type(
)
assert cost_category_import_log.status == 'FATAL'

# Code pre-prepend case
ImportSetting.objects.filter(workspace_id=workspace_id).update(import_code_fields=['JOB', 'COST_CODE', 'COST_CATEGORY'])
CostCategory.objects.filter(workspace_id=workspace_id).update(job_code='123', cost_code_code='456', cost_category_code='789')

post_dependent_cost_type(
cost_category_import_log,
dependent_field_setting=dependent_field_settings,
platform=platform.return_value,
filters=filters,
posted_cost_codes=['CRE Platform', 'Integrations CRE']
)

assert platform.return_value.dependent_fields.bulk_post_dependent_expense_field_values.call_count == 4
assert cost_category_import_log.status == 'COMPLETE'


def test_post_dependent_expense_field_values(
db,
Expand Down
61 changes: 58 additions & 3 deletions tests/test_sage300/test_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,9 @@ def test_disable_projects(
db,
mocker,
create_temp_workspace,
add_fyle_credentials
add_fyle_credentials,
add_project_mappings,
add_import_settings
):
workspace_id = 1

Expand Down Expand Up @@ -142,6 +144,42 @@ def test_disable_projects(
assert sync_call.call_count == 4
disable_cost_code_call.call_count == 2

# Test disable projects with code in naming
import_settings = ImportSetting.objects.get(workspace_id=workspace_id)
import_settings.import_code_fields = ['JOB']
import_settings.save()

ExpenseAttribute.objects.create(
workspace_id=workspace_id,
attribute_type='PROJECT',
display_name='Project',
value='old_project_code old_project',
source_id='source_id_123',
active=True
)

projects_to_disable = {
'destination_id': {
'value': 'old_project',
'updated_value': 'new_project',
'code': 'old_project_code',
'updated_code': 'old_project_code'
}
}

payload = [{
'name': 'old_project_code old_project',
'code': 'destination_id',
'description': 'Sage 300 Project - {0}, Id - {1}'.format(
'old_project_code old_project',
'destination_id'
),
'is_enabled': False,
'id': 'source_id_123'
}]

assert disable_projects(workspace_id, projects_to_disable) == payload


def test_update_and_disable_cost_code(
db,
Expand All @@ -158,8 +196,8 @@ def test_update_and_disable_cost_code(
'destination_id': {
'value': 'old_project',
'updated_value': 'new_project',
'code': 'old_project_code',
'updated_code': 'old_project_code'
'code': 'new_project_code',
'updated_code': 'new_project_code'
}
}

Expand Down Expand Up @@ -192,6 +230,23 @@ def test_update_and_disable_cost_code(
updated_cost_category = CostCategory.objects.filter(workspace_id=workspace_id, job_id='destination_id').first()
assert updated_cost_category.job_name == 'new_project'

# Test with code in naming
use_code_in_naming = True

ExpenseAttribute.objects.create(
workspace_id=workspace_id,
attribute_type='PROJECT',
display_name='Project',
value='old_project_code old_project',
source_id='source_id_123',
active=True
)

update_and_disable_cost_code(workspace_id, projects_to_disable, mock_platform, use_code_in_naming)
assert updated_cost_category.job_name == 'new_project'
assert updated_cost_category.job_code == 'new_project_code'

# Delete dependent field setting
updated_cost_category.job_name = 'old_project'
updated_cost_category.save()

Expand Down

0 comments on commit 2a6c29a

Please sign in to comment.