-
Notifications
You must be signed in to change notification settings - Fork 3
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
feat: Max retry limit for bill payment #664
Changes from all commits
79ea2a2
ed14996
4684042
a7cb626
43699ea
0fba1f4
2efea93
0907fee
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
# Generated by Django 3.2.14 on 2024-09-03 15:02 | ||
|
||
from django.db import migrations, models | ||
|
||
|
||
class Migration(migrations.Migration): | ||
|
||
dependencies = [ | ||
('quickbooks_online', '0015_add_bill_number'), | ||
] | ||
|
||
operations = [ | ||
migrations.AddField( | ||
model_name='bill', | ||
name='is_retired', | ||
field=models.BooleanField(default=False, help_text='Is Payment sync retried'), | ||
), | ||
] |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -213,6 +213,7 @@ class Bill(models.Model): | |||||
private_note = models.TextField(help_text='Bill Description') | ||||||
payment_synced = models.BooleanField(help_text='Payment synced status', default=False) | ||||||
paid_on_qbo = models.BooleanField(help_text='Payment status in QBO', default=False) | ||||||
is_retired = models.BooleanField(help_text='Is Payment sync retried', default=False) | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fix typo in field name 'is_retired' to 'is_retried' The field Apply this diff to correct the field name: - is_retired = models.BooleanField(help_text='Is Payment sync retried', default=False)
+ is_retried = models.BooleanField(help_text='Is Payment sync retried', default=False) 📝 Committable suggestion
Suggested change
|
||||||
exchange_rate = models.FloatField(help_text='Exchange rate', null=True) | ||||||
created_at = models.DateTimeField(auto_now_add=True, help_text='Created at') | ||||||
updated_at = models.DateTimeField(auto_now=True, help_text='Updated at') | ||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,6 +4,9 @@ | |
from datetime import datetime, timezone | ||
from typing import List | ||
|
||
from dateutil.relativedelta import relativedelta | ||
from django.utils import timezone as django_timezone | ||
|
||
from django.db import transaction | ||
from fyle_accounting_mappings.models import DestinationAttribute, EmployeeMapping, ExpenseAttribute, Mapping | ||
from fyle_integrations_platform_connector import PlatformConnector | ||
|
@@ -643,6 +646,30 @@ def process_bill_payments(bill: Bill, workspace_id: int, task_log: TaskLog): | |
task_log.save() | ||
|
||
|
||
def validate_for_skipping_payment(bill: Bill, workspace_id: int): | ||
task_log = TaskLog.objects.filter(task_id='PAYMENT_{}'.format(bill.expense_group.id), workspace_id=workspace_id, type='CREATING_BILL_PAYMENT').first() | ||
if task_log: | ||
now = django_timezone.now() | ||
|
||
if now - relativedelta(months=2) > task_log.created_at: | ||
bill.is_retired = True | ||
bill.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 | ||
|
||
Comment on lines
+649
to
+671
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Simplify and Correct the Logic in The
Consider refactoring the function to improve logic accuracy and readability. Here's a suggested revision: def validate_for_skipping_payment(bill: Bill, workspace_id: int):
task_log = TaskLog.objects.filter(
task_id='PAYMENT_{}'.format(bill.expense_group.id),
workspace_id=workspace_id,
type='CREATING_BILL_PAYMENT'
).first()
if task_log:
now = django_timezone.now()
created_diff = now - task_log.created_at
updated_diff = now - task_log.updated_at
if created_diff > relativedelta(months=2):
bill.is_retired = True
bill.save()
return True
elif relativedelta(months=1) < created_diff <= relativedelta(months=2):
# Skip if updated within the last month
if updated_diff <= relativedelta(months=1):
return True
elif created_diff <= relativedelta(months=1):
# Skip if updated within the last week
if updated_diff <= relativedelta(weeks=1):
return True
return False This refactor calculates the time differences once and clarifies the conditions, reducing the chance of logical errors. |
||
|
||
def create_bill_payment(workspace_id): | ||
fyle_credentials = FyleCredential.objects.get(workspace_id=workspace_id) | ||
|
||
|
@@ -656,6 +683,9 @@ def create_bill_payment(workspace_id): | |
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(bill=bill, workspace_id=workspace_id) | ||
if skip_payment: | ||
continue | ||
task_log, _ = TaskLog.objects.update_or_create(workspace_id=workspace_id, task_id='PAYMENT_{}'.format(bill.expense_group.id), defaults={'status': 'IN_PROGRESS', 'type': 'CREATING_BILL_PAYMENT'}) | ||
process_bill_payments(bill, workspace_id, task_log) | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,7 @@ | ||
import json | ||
import logging | ||
import random | ||
from datetime import datetime | ||
from datetime import datetime, timedelta, timezone | ||
from unittest import mock | ||
|
||
from django_q.models import Schedule | ||
|
@@ -1286,3 +1286,71 @@ def test_skipping_cheque_creation(db, mocker): | |
|
||
task_log = TaskLog.objects.filter(expense_group_id=expense_group.id).first() | ||
assert task_log.type == 'CREATING_CHECK' | ||
|
||
|
||
def test_skipping_bill_payment(mocker, db): | ||
mocker.patch('apps.quickbooks_online.tasks.load_attachments', return_value=[]) | ||
mocker.patch('fyle_integrations_platform_connector.apis.Reimbursements.sync', return_value=None) | ||
mocker.patch('fyle_integrations_platform_connector.apis.Expenses.get', return_value=[]) | ||
mocker.patch('qbosdk.apis.Bills.post', return_value=data['post_bill']) | ||
mocker.patch('qbosdk.apis.BillPayments.post', return_value=data['post_bill']) | ||
mocker.patch('qbosdk.apis.Attachments.post', return_value=None) | ||
workspace_id = 3 | ||
task_log = TaskLog.objects.filter(workspace_id=workspace_id).first() | ||
task_log.status = 'READY' | ||
task_log.save() | ||
|
||
expense_group = ExpenseGroup.objects.get(id=14) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Avoid hardcoding object IDs in tests Using hardcoded IDs like |
||
expenses = expense_group.expenses.all() | ||
|
||
expense_group.id = random.randint(100, 1500000) | ||
expense_group.save() | ||
|
||
for expense in expenses: | ||
expense.expense_group_id = expense_group.id | ||
expense.save() | ||
|
||
expense_group.expenses.set(expenses) | ||
expense_group.save() | ||
|
||
create_bill(expense_group, task_log.id, False) | ||
|
||
bill = Bill.objects.last() | ||
task_log = TaskLog.objects.get(id=task_log.id) | ||
task_log.expense_group = bill.expense_group | ||
task_log.save() | ||
|
||
reimbursements = data['reimbursements'] | ||
|
||
Reimbursement.create_or_update_reimbursement_objects(reimbursements=reimbursements, workspace_id=workspace_id) | ||
|
||
task_log = TaskLog.objects.create(workspace_id=workspace_id, type='CREATING_BILL_PAYMENT', task_id='PAYMENT_{}'.format(bill.expense_group.id), status='FAILED') | ||
updated_at = task_log.updated_at | ||
create_bill_payment(workspace_id) | ||
|
||
task_log = TaskLog.objects.get(workspace_id=workspace_id, type='CREATING_BILL_PAYMENT', task_id='PAYMENT_{}'.format(bill.expense_group.id)) | ||
assert task_log.updated_at == updated_at | ||
|
||
now = datetime.now().replace(tzinfo=timezone.utc) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Use Using Apply this diff to make the change: -now = datetime.now().replace(tzinfo=timezone.utc)
+from django.utils import timezone
+now = timezone.now()
|
||
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_bill_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_bill_payment(workspace_id) | ||
task_log.refresh_from_db() | ||
assert task_log.updated_at == updated_at |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Codebase verification
Potential Unjustified Lowering of Coverage Threshold
The coverage threshold in the pytest command has been reduced from 94% to 93%. However, no recent changes in test files or pending TODOs/FIXMEs were found to justify this modification. This reduction might inadvertently lower the code quality assurance standards.
.github/workflows/pytest.yml
🔗 Analysis chain
Consider the implications of lowering the coverage threshold.
The coverage threshold in the pytest command has been reduced from 94% to 93%. While this change allows for a slightly lower test coverage, it's important to consider the following:
To ensure this change aligns with the PR objectives and doesn't introduce unintended consequences:
Consider implementing a strategy to gradually increase test coverage over time, such as:
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
Length of output: 342
Script:
Length of output: 207
Script:
Length of output: 184