Skip to content

Commit

Permalink
Max retry payment Limit with test (#548)
Browse files Browse the repository at this point in the history
* Max retry payment Limit with test

* updated test

* Creating reimbursement max retry limit

* comment resolved
  • Loading branch information
Ashutosh619-sudo authored Sep 24, 2024
1 parent 65af7e3 commit 6f6708c
Show file tree
Hide file tree
Showing 5 changed files with 181 additions and 8 deletions.
23 changes: 23 additions & 0 deletions apps/sage_intacct/migrations/0029_auto_20240902_1511.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Generated by Django 3.2.14 on 2024-09-02 15:11

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('sage_intacct', '0028_add_billable_field_to_cct'),
]

operations = [
migrations.AddField(
model_name='bill',
name='is_retired',
field=models.BooleanField(default=False, help_text='Is Payment sync retried'),
),
migrations.AddField(
model_name='expensereport',
name='is_retired',
field=models.BooleanField(default=False, help_text='Is Payment sync retried'),
),
]
2 changes: 2 additions & 0 deletions apps/sage_intacct/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -565,6 +565,7 @@ class Bill(models.Model):
transaction_date = models.DateTimeField(help_text='Bill transaction date', null=True)
payment_synced = models.BooleanField(help_text='Payment synced status', default=False)
paid_on_sage_intacct = models.BooleanField(help_text='Payment status in Sage Intacct', default=False)
is_retired = models.BooleanField(help_text='Is Payment sync retried', default=False)
created_at = models.DateTimeField(auto_now_add=True, help_text='Created at')
updated_at = models.DateTimeField(auto_now=True, help_text='Updated at')

Expand Down Expand Up @@ -757,6 +758,7 @@ class ExpenseReport(models.Model):
transaction_date = models.DateTimeField(help_text='Expense Report transaction date', null=True)
payment_synced = models.BooleanField(help_text='Payment synced status', default=False)
paid_on_sage_intacct = models.BooleanField(help_text='Payment status in Sage Intacct', default=False)
is_retired = models.BooleanField(help_text='Is Payment sync retried', default=False)
created_at = models.DateTimeField(auto_now_add=True, help_text='Created at')
updated_at = models.DateTimeField(auto_now=True, help_text='Updated at')

Expand Down
41 changes: 38 additions & 3 deletions apps/sage_intacct/tasks.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import logging
import traceback
from typing import List
from datetime import datetime, timezone
from datetime import datetime
from dateutil.relativedelta import relativedelta
from django.utils import timezone

from django.db import transaction
from django.db.models import Q, F
Expand Down Expand Up @@ -1081,21 +1083,49 @@ def check_expenses_reimbursement_status(expenses, workspace_id, platform, filter
return is_paid


def validate_for_skipping_payment(export_module: Bill | ExpenseReport, workspace_id: int, type: str):
task_log = TaskLog.objects.filter(task_id='PAYMENT_{}'.format(export_module.expense_group.id), workspace_id=workspace_id, type=type).first()
if task_log:
now = timezone.now()

if now - relativedelta(months=2) > task_log.created_at:
export_module.is_retired = True
export_module.save()
return True

elif now - relativedelta(months=1) > task_log.created_at and now - relativedelta(months=2) < task_log.created_at:
# if updated_at is within 1 months will be skipped
if task_log.updated_at > now - relativedelta(months=1):
return True

# If created is within 1 month
elif now - relativedelta(months=1) < task_log.created_at:
# Skip if updated within the last week
if task_log.updated_at > now - relativedelta(weeks=1):
return True

return False

def create_ap_payment(workspace_id):
fyle_credentials = FyleCredential.objects.get(workspace_id=workspace_id)
platform = PlatformConnector(fyle_credentials)
filter_credit_expenses = False

bills: List[Bill] = Bill.objects.filter(
payment_synced=False, expense_group__workspace_id=workspace_id,
expense_group__fund_source='PERSONAL'
expense_group__fund_source='PERSONAL', is_retired=False
).all()

if bills:
for bill in bills:
expense_group_reimbursement_status = check_expenses_reimbursement_status(
bill.expense_group.expenses.all(), workspace_id=workspace_id, platform=platform, filter_credit_expenses=filter_credit_expenses)
if expense_group_reimbursement_status:

skip_payment = validate_for_skipping_payment(export_module=bill, workspace_id=workspace_id, type='CREATING_AP_PAYMENT')
if skip_payment:
continue

task_log, _ = TaskLog.objects.update_or_create(
workspace_id=workspace_id,
task_id='PAYMENT_{}'.format(bill.expense_group.id),
Expand Down Expand Up @@ -1212,13 +1242,18 @@ def create_sage_intacct_reimbursement(workspace_id):

expense_reports: List[ExpenseReport] = ExpenseReport.objects.filter(
payment_synced=False, expense_group__workspace_id=workspace_id,
expense_group__fund_source='PERSONAL'
expense_group__fund_source='PERSONAL', is_retired=False
).all()

for expense_report in expense_reports:
expense_group_reimbursement_status = check_expenses_reimbursement_status(
expense_report.expense_group.expenses.all(), workspace_id=workspace_id, platform=platform, filter_credit_expenses=filter_credit_expenses)
if expense_group_reimbursement_status:

skip_reimbursement = validate_for_skipping_payment(export_module=expense_report, workspace_id=workspace_id, type='CREATING_REIMBURSEMENT')
if skip_reimbursement:
continue

task_log, _ = TaskLog.objects.update_or_create(
workspace_id=workspace_id,
task_id='PAYMENT_{}'.format(expense_report.expense_group.id),
Expand Down
13 changes: 8 additions & 5 deletions tests/sql_fixtures/reset_db_fixtures/reset_db.sql
Original file line number Diff line number Diff line change
Expand Up @@ -277,7 +277,8 @@ CREATE TABLE public.bills (
transaction_date timestamp with time zone,
paid_on_sage_intacct boolean NOT NULL,
payment_synced boolean NOT NULL,
currency character varying(5) NOT NULL
currency character varying(5) NOT NULL,
is_retired boolean NOT NULL
);


Expand Down Expand Up @@ -1107,7 +1108,8 @@ CREATE TABLE public.expense_reports (
transaction_date timestamp with time zone,
paid_on_sage_intacct boolean NOT NULL,
payment_synced boolean NOT NULL,
currency character varying(5) NOT NULL
currency character varying(5) NOT NULL,
is_retired boolean NOT NULL
);


Expand Down Expand Up @@ -2847,7 +2849,7 @@ COPY public.bill_lineitems (id, expense_type_id, gl_account_number, project_id,
-- Data for Name: bills; Type: TABLE DATA; Schema: public; Owner: postgres
--

COPY public.bills (id, vendor_id, description, supdoc_id, created_at, updated_at, expense_group_id, memo, transaction_date, paid_on_sage_intacct, payment_synced, currency) FROM stdin;
COPY public.bills (id, vendor_id, description, supdoc_id, created_at, updated_at, expense_group_id, memo, transaction_date, paid_on_sage_intacct, payment_synced, currency, is_retired) FROM stdin;
\.


Expand Down Expand Up @@ -4127,6 +4129,7 @@ COPY public.django_migrations (id, app, name, applied) FROM stdin;
192 workspaces 0037_configuration_import_code_fields 2024-08-12 10:35:18.352466+00
193 fyle_accounting_mappings 0026_destinationattribute_code 2024-08-12 10:38:36.247956+00
194 workspaces 0038_alter_configuration_import_code_fields 2024-09-04 11:00:27.611651+00
195 sage_intacct 0029_auto_20240902_1511 2024-09-02 15:12:18.555335+00
\.


Expand Down Expand Up @@ -7555,7 +7558,7 @@ COPY public.expense_report_lineitems (id, expense_type_id, gl_account_number, pr
-- Data for Name: expense_reports; Type: TABLE DATA; Schema: public; Owner: postgres
--

COPY public.expense_reports (id, employee_id, description, supdoc_id, created_at, updated_at, expense_group_id, memo, transaction_date, paid_on_sage_intacct, payment_synced, currency) FROM stdin;
COPY public.expense_reports (id, employee_id, description, supdoc_id, created_at, updated_at, expense_group_id, memo, transaction_date, paid_on_sage_intacct, payment_synced, currency, is_retired) FROM stdin;
\.


Expand Down Expand Up @@ -8134,7 +8137,7 @@ SELECT pg_catalog.setval('public.django_content_type_id_seq', 50, true);
-- Name: django_migrations_id_seq; Type: SEQUENCE SET; Schema: public; Owner: postgres
--

SELECT pg_catalog.setval('public.django_migrations_id_seq', 194, true);
SELECT pg_catalog.setval('public.django_migrations_id_seq', 195, true);


--
Expand Down
110 changes: 110 additions & 0 deletions tests/test_sageintacct/test_tasks.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
from datetime import datetime
from dateutil.relativedelta import relativedelta

import logging
import random
from unittest import mock
Expand Down Expand Up @@ -451,7 +454,12 @@ def test_post_sage_intacct_reimbursements_exceptions(mocker, db, create_expense_
task_log = TaskLog.objects.get(task_id='PAYMENT_{}'.format(expense_report.expense_group.id))
assert task_log.status == 'FAILED'


now = datetime.now().replace(tzinfo=timezone.utc)
updated_at = now - timedelta(days=10)

mock_call.side_effect = Exception()
TaskLog.objects.filter(task_id='PAYMENT_{}'.format(expense_report.expense_group.id)).update(updated_at=updated_at)
create_sage_intacct_reimbursement(workspace_id)

task_log = TaskLog.objects.get(task_id='PAYMENT_{}'.format(expense_report.expense_group.id))
Expand All @@ -464,6 +472,7 @@ def test_post_sage_intacct_reimbursements_exceptions(mocker, db, create_expense_
'error': [{'code': 400, 'Message': 'Invalid parametrs', 'Detail': 'Invalid parametrs', 'description': '', 'description2': '', 'correction': ''}],
'type': 'Invalid_params'
})
TaskLog.objects.filter(task_id='PAYMENT_{}'.format(expense_report.expense_group.id)).update(updated_at=updated_at)
create_sage_intacct_reimbursement(workspace_id)

task_log = TaskLog.objects.get(task_id='PAYMENT_{}'.format(expense_report.expense_group.id))
Expand All @@ -476,11 +485,13 @@ def test_post_sage_intacct_reimbursements_exceptions(mocker, db, create_expense_
'error': [{'code': 400, 'Message': 'Invalid parametrs', 'Detail': 'Invalid parametrs', 'description': '', 'description2': '', 'correction': ''}],
'type': 'Invalid_params'
})
TaskLog.objects.filter(task_id='PAYMENT_{}'.format(expense_report.expense_group.id)).update(updated_at=updated_at)
create_sage_intacct_reimbursement(workspace_id)

task_log = TaskLog.objects.get(task_id='PAYMENT_{}'.format(expense_report.expense_group.id))
assert task_log.status == 'FAILED'

TaskLog.objects.filter(task_id='PAYMENT_{}'.format(expense_report.expense_group.id)).update(updated_at=updated_at)
mock_call.side_effect = SageIntacctCredential.DoesNotExist()
create_sage_intacct_reimbursement(workspace_id)

Expand All @@ -501,6 +512,10 @@ def test_post_sage_intacct_reimbursements_exceptions(mocker, db, create_expense_
'error': [{'code': 400, 'Message': 'Invalid parametrs', 'Detail': 'Invalid parametrs', 'description': '', 'description2': "exceeds total amount due ()", 'correction': ''}],
'type': 'Invalid_params'
})

now = datetime.now().replace(tzinfo=timezone.utc)
updated_at = now - timedelta(days=10)
TaskLog.objects.filter(task_id='PAYMENT_{}'.format(expense_report.expense_group.id)).update(updated_at=updated_at)
create_sage_intacct_reimbursement(workspace_id)
expense_report = ExpenseReport.objects.get(id=expense_report.id)

Expand Down Expand Up @@ -1422,3 +1437,98 @@ def test__validate_employee_mapping(mocker, db):
__validate_employee_mapping(expense_group, configuration)
except BulkError as exception:
logger.info(exception.response)


def test_skipping_create_ap_payment(mocker, db):
mocker.patch(
'sageintacctsdk.apis.Bills.post',
return_value=data['bill_response']
)
mocker.patch(
'sageintacctsdk.apis.Bills.get',
return_value=data['bill_response']['data']
)
mocker.patch(
'sageintacctsdk.apis.APPayments.post',
return_value=data['reimbursements']
)
mocker.patch(
'apps.sage_intacct.tasks.load_attachments',
return_value=['sdfgh']
)
mocker.patch(
'sageintacctsdk.apis.Reimbursements.post',
return_value=data['reimbursements']
)
mocker.patch(
'fyle_integrations_platform_connector.apis.Reimbursements.sync',
return_value=None
)
mocker.patch(
'sageintacctsdk.apis.Bills.update_attachment',
return_value=data['bill_response']
)

mocker.patch('fyle_integrations_platform_connector.apis.Expenses.get', return_value=[])

workspace_id = 1
task_log = TaskLog.objects.filter(expense_group__workspace_id=workspace_id).first()
task_log.status = 'READY'
task_log.save()

expense_group = ExpenseGroup.objects.get(id=1)
expenses = expense_group.expenses.all()

for expense in expenses:
reimbursement = Reimbursement.objects.filter(settlement_id=expense.settlement_id).first()
reimbursement.state = 'COMPLETE'
reimbursement.save()

create_bill(expense_group, task_log.id, True)

bill = Bill.objects.get(expense_group__workspace_id=workspace_id)

task_log = TaskLog.objects.get(expense_group=bill.expense_group)
task_log.detail = data['bill_response']
task_log.save()

bill.expense_group.expenses.all().update(paid_on_fyle=True)

with mock.patch('apps.sage_intacct.models.APPayment.create_ap_payment') as mock_call:
mock_call.side_effect = BulkError(msg='employess not found', response='mapping error')
create_ap_payment(workspace_id)

now = datetime.now().replace(tzinfo=timezone.utc)
updated_at = now - timedelta(days=25)
# Update created_at to more than 2 months ago (more than 60 days)
TaskLog.objects.filter(task_id='PAYMENT_{}'.format(bill.expense_group.id)).update(
created_at=now - timedelta(days=61), # More than 2 months ago
updated_at=updated_at # Updated within the last 1 month
)

task_log = TaskLog.objects.get(task_id='PAYMENT_{}'.format(bill.expense_group.id))

create_ap_payment(workspace_id)
task_log.refresh_from_db()
assert task_log.updated_at == updated_at


updated_at = now - timedelta(days=25)
# Update created_at to between 1 and 2 months ago (between 30 and 60 days)
TaskLog.objects.filter(task_id='PAYMENT_{}'.format(bill.expense_group.id)).update(
created_at=now - timedelta(days=45), # Between 1 and 2 months ago
updated_at=updated_at # Updated within the last 1 month
)
create_ap_payment(workspace_id)
task_log.refresh_from_db()
assert task_log.updated_at == updated_at

updated_at = now - timedelta(days=5)
# Update created_at to within the last 1 month (less than 30 days)
TaskLog.objects.filter(task_id='PAYMENT_{}'.format(bill.expense_group.id)).update(
created_at=now - timedelta(days=25), # Within the last 1 month
updated_at=updated_at # Updated within the last week
)
create_ap_payment(workspace_id)
task_log.refresh_from_db()
assert task_log.updated_at == updated_at

0 comments on commit 6f6708c

Please sign in to comment.