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

feat: Max retry limit for bill payment #664

Merged
merged 8 commits into from
Sep 27, 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
2 changes: 1 addition & 1 deletion .github/workflows/pytest.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:
run: |
docker compose -f docker-compose-pipeline.yml build
docker compose -f docker-compose-pipeline.yml up -d
docker compose -f docker-compose-pipeline.yml exec -T api pytest tests/ --cov --cov-report=xml --cov-fail-under=94 --junit-xml=test-reports/report.xml
docker compose -f docker-compose-pipeline.yml exec -T api pytest tests/ --cov --cov-report=xml --cov-fail-under=93 --junit-xml=test-reports/report.xml
Copy link

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.

  • File: .github/workflows/pytest.yml
  • Line: 19
🔗 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:

  1. This modification might be a temporary measure to accommodate the new changes in the codebase.
  2. However, lowering the coverage threshold could potentially lead to decreased code quality in the long run.

To ensure this change aligns with the PR objectives and doesn't introduce unintended consequences:

  1. Verify if this change is necessary for the implementation of the max retry limit for bill payment.
  2. Check if there are plans to increase the coverage back to the previous level in future PRs.

Consider implementing a strategy to gradually increase test coverage over time, such as:

  1. Setting up a separate CI job that runs with the higher coverage threshold but doesn't fail the build.
  2. Creating issues to track areas of the codebase that need improved test coverage.
  3. Implementing a code review policy that requires new features to meet the higher coverage threshold.
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for recent changes in test files that might justify the coverage change

# Test: Look for recent changes in test files
echo "Recent changes in test files:"
git diff --name-only HEAD~5..HEAD | grep -E 'test_.*\.py$'

# Test: Check for TODOs or FIXMEs in test files that might indicate pending test implementations
echo "TODOs or FIXMEs in test files:"
rg -i "TODO|FIXME" --type py "test_.*\.py$"

Length of output: 342


Script:

#!/bin/bash
# Description: Re-check for TODOs or FIXMEs in all test files using correct rg syntax

# Test: Search for TODOs or FIXMEs in all Python test files
echo "Searching for TODOs or FIXMEs in test files:"
rg -i "TODO|FIXME" --type py 'tests/test_.*\.py'

Length of output: 207


Script:

#!/bin/bash
# Description: Search for TODOs or FIXMEs within all Python test files using fd and rg

echo "Searching for TODOs or FIXMEs in Python test files:"
fd -e py -g 'tests/test_*.py' | xargs -I {} rg -i "TODO|FIXME" {}

Length of output: 184

echo "STATUS=$(cat pytest-coverage.txt | grep 'Required test' | awk '{ print $1 }')" >> $GITHUB_ENV
echo "FAILED=$(cat test-reports/report.xml | awk -F'=' '{print $5}' | awk -F' ' '{gsub(/"/, "", $1); print $1}')" >> $GITHUB_ENV
env:
Expand Down
18 changes: 18 additions & 0 deletions apps/quickbooks_online/migrations/0016_bill_is_retired.py
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'),
),
]
1 change: 1 addition & 0 deletions apps/quickbooks_online/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix typo in field name 'is_retired' to 'is_retried'

The field is_retired seems to have a typo. Given the help text 'Is Payment sync retried', the intended field name might be is_retried.

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

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
is_retired = models.BooleanField(help_text='Is Payment sync retried', default=False)
is_retried = models.BooleanField(help_text='Is Payment sync retried', default=False)

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')
Expand Down
30 changes: 30 additions & 0 deletions apps/quickbooks_online/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Simplify and Correct the Logic in validate_for_skipping_payment Function

The validate_for_skipping_payment function is intended to determine whether a bill payment should be skipped based on the task_log timestamps. However, there are some issues to address:

  1. Logical Errors and Overlapping Conditions: The current conditions may lead to unintended overlaps or gaps, potentially causing incorrect evaluations. Specifically, the elif statements might not cover all date ranges accurately.

  2. Code Readability and Maintainability: The nested if-elif structure with multiple date calculations can be simplified for better clarity.

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)

Expand All @@ -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)

Expand Down
2 changes: 1 addition & 1 deletion tests/sql_fixtures/migration_fixtures/create_migration.sh
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ bash tests/sql_fixtures/reset_db_fixtures/reset_db.sh
export DATABASE_URL=postgres://postgres:postgres@db:5432/test_qbo_db

# # Running migrations on the fixture database
python manage.py migrate
# python manage.py migrate

read -p "Add SQL script paths separated by spaces if any, else press enter to continue? " scripts

Expand Down
19 changes: 11 additions & 8 deletions tests/sql_fixtures/reset_db_fixtures/reset_db.sql
Original file line number Diff line number Diff line change
Expand Up @@ -264,7 +264,8 @@ CREATE TABLE public.bills (
paid_on_qbo boolean NOT NULL,
payment_synced boolean NOT NULL,
exchange_rate double precision,
bill_number character varying(255)
bill_number character varying(255),
is_retired boolean NOT NULL
);


Expand Down Expand Up @@ -2638,7 +2639,7 @@ COPY public.bill_payments (id, private_note, vendor_id, amount, currency, paymen
-- Data for Name: bills; Type: TABLE DATA; Schema: public; Owner: postgres
--

COPY public.bills (id, accounts_payable_id, vendor_id, department_id, transaction_date, currency, private_note, created_at, updated_at, expense_group_id, paid_on_qbo, payment_synced, exchange_rate, bill_number) FROM stdin;
COPY public.bills (id, accounts_payable_id, vendor_id, department_id, transaction_date, currency, private_note, created_at, updated_at, expense_group_id, paid_on_qbo, payment_synced, exchange_rate, bill_number, is_retired) FROM stdin;
\.


Expand Down Expand Up @@ -2916,6 +2917,7 @@ COPY public.destination_attributes (id, attribute_type, display_name, value, des
225 ACCOUNTS_PAYABLE Accounts Payable Sales of Product Income 79 2022-05-23 04:13:50.086029+00 2022-05-23 04:13:50.086076+00 2 t {"account_type": "Income", "fully_qualified_name": "Sales of Product Income"} f \N
226 ACCOUNTS_PAYABLE Accounts Payable Services 1 2022-05-23 04:13:50.086195+00 2022-05-23 04:13:50.086238+00 2 t {"account_type": "Income", "fully_qualified_name": "Services"} f \N
227 ACCOUNTS_PAYABLE Accounts Payable Stationery & Printing 19 2022-05-23 04:13:50.086351+00 2022-05-23 04:13:50.086392+00 2 t {"account_type": "Expense", "fully_qualified_name": "Stationery & Printing"} f \N
830 CLASS class cc2 5000000000000142240 2022-05-23 11:33:57.280177+00 2022-05-23 11:33:57.280207+00 4 t \N f \N
228 ACCOUNTS_PAYABLE Accounts Payable Supplies 20 2022-05-23 04:13:50.08669+00 2022-05-23 04:13:50.08674+00 2 t {"account_type": "Expense", "fully_qualified_name": "Supplies"} f \N
229 ACCOUNTS_PAYABLE Accounts Payable Taxes & Licenses 21 2022-05-23 04:13:50.086858+00 2022-05-23 04:13:50.086901+00 2 t {"account_type": "Expense", "fully_qualified_name": "Taxes & Licenses"} f \N
230 ACCOUNTS_PAYABLE Accounts Payable Travel 22 2022-05-23 04:13:50.087009+00 2022-05-23 04:13:50.087126+00 2 t {"account_type": "Expense", "fully_qualified_name": "Travel"} f \N
Expand Down Expand Up @@ -3284,6 +3286,7 @@ COPY public.destination_attributes (id, attribute_type, display_name, value, des
593 ACCOUNT Account Cost of Goods Sold 80 2022-05-23 11:21:16.959749+00 2022-05-23 11:21:16.959775+00 3 t {"account_type": "Cost of Goods Sold", "fully_qualified_name": "Cost of Goods Sold"} f \N
594 ACCOUNT Account Depreciation 40 2022-05-23 11:21:16.959937+00 2022-05-23 11:21:16.959965+00 3 t {"account_type": "Other Expense", "fully_qualified_name": "Depreciation"} f \N
595 ACCOUNT Account Disposal Fees 28 2022-05-23 11:21:16.960029+00 2022-05-23 11:21:16.960057+00 3 t {"account_type": "Expense", "fully_qualified_name": "Disposal Fees"} f \N
755 VENDOR vendor Brosnahan Insurance Agency 31 2022-05-23 11:33:51.369394+00 2022-05-23 11:33:51.369422+00 4 t {"email": null} f \N
596 ACCOUNT Account Dues & Subscriptions 10 2022-05-23 11:21:16.96012+00 2022-05-23 11:21:16.960148+00 3 t {"account_type": "Expense", "fully_qualified_name": "Dues & Subscriptions"} f \N
597 ACCOUNT Account Equipment Rental 29 2022-05-23 11:21:16.960211+00 2022-05-23 11:21:16.960239+00 3 t {"account_type": "Expense", "fully_qualified_name": "Equipment Rental"} f \N
598 ACCOUNT Account Insurance 11 2022-05-23 11:21:16.960303+00 2022-05-23 11:21:16.960331+00 3 t {"account_type": "Expense", "fully_qualified_name": "Insurance"} f \N
Expand Down Expand Up @@ -3364,6 +3367,7 @@ COPY public.destination_attributes (id, attribute_type, display_name, value, des
665 ACCOUNTS_PAYABLE Accounts Payable Board of Equalization Payable 90 2022-05-23 11:33:46.953486+00 2022-05-23 11:33:46.953514+00 4 t {"account_type": "Other Current Liability", "fully_qualified_name": "Board of Equalization Payable"} f \N
666 ACCOUNTS_PAYABLE Accounts Payable Commissions & fees 9 2022-05-23 11:33:46.953577+00 2022-05-23 11:33:46.953727+00 4 t {"account_type": "Expense", "fully_qualified_name": "Commissions & fees"} f \N
667 ACCOUNTS_PAYABLE Accounts Payable Cost of Goods Sold 80 2022-05-23 11:33:46.953807+00 2022-05-23 11:33:46.953835+00 4 t {"account_type": "Cost of Goods Sold", "fully_qualified_name": "Cost of Goods Sold"} f \N
756 VENDOR vendor Cal Telephone 32 2022-05-23 11:33:51.369484+00 2022-05-23 11:33:51.369512+00 4 t {"email": null} f \N
668 ACCOUNTS_PAYABLE Accounts Payable Depreciation 40 2022-05-23 11:33:46.953898+00 2022-05-23 11:33:46.953926+00 4 t {"account_type": "Other Expense", "fully_qualified_name": "Depreciation"} f \N
669 ACCOUNTS_PAYABLE Accounts Payable Design income 82 2022-05-23 11:33:46.953989+00 2022-05-23 11:33:46.954017+00 4 t {"account_type": "Income", "fully_qualified_name": "Design income"} f \N
670 ACCOUNTS_PAYABLE Accounts Payable Discounts given 86 2022-05-23 11:33:46.95408+00 2022-05-23 11:33:46.954107+00 4 t {"account_type": "Income", "fully_qualified_name": "Discounts given"} f \N
Expand Down Expand Up @@ -3401,6 +3405,7 @@ COPY public.destination_attributes (id, attribute_type, display_name, value, des
707 ACCOUNTS_PAYABLE Accounts Payable Maintenance and Repair:Equipment Repairs 75 2022-05-23 11:33:46.965624+00 2022-05-23 11:33:46.965846+00 4 t {"account_type": "Expense", "fully_qualified_name": "Maintenance and Repair:Equipment Repairs"} f \N
708 ACCOUNTS_PAYABLE Accounts Payable Meals and Entertainment 13 2022-05-23 11:33:46.965977+00 2022-05-23 11:33:46.966009+00 4 t {"account_type": "Expense", "fully_qualified_name": "Meals and Entertainment"} f \N
709 ACCOUNTS_PAYABLE Accounts Payable Miscellaneous 14 2022-05-23 11:33:46.966294+00 2022-05-23 11:33:46.96634+00 4 t {"account_type": "Other Expense", "fully_qualified_name": "Miscellaneous"} f \N
757 VENDOR vendor Chin's Gas and Oil 33 2022-05-23 11:33:51.369573+00 2022-05-23 11:33:51.369601+00 4 t {"email": null} f \N
710 ACCOUNTS_PAYABLE Accounts Payable Notes Payable 44 2022-05-23 11:33:46.966481+00 2022-05-23 11:33:46.966513+00 4 t {"account_type": "Long Term Liability", "fully_qualified_name": "Notes Payable"} f \N
711 ACCOUNTS_PAYABLE Accounts Payable Office Expenses 15 2022-05-23 11:33:46.966588+00 2022-05-23 11:33:46.96673+00 4 t {"account_type": "Expense", "fully_qualified_name": "Office Expenses"} f \N
712 ACCOUNTS_PAYABLE Accounts Payable Opening Balance Equity 34 2022-05-23 11:33:46.966996+00 2022-05-23 11:33:46.96716+00 4 t {"account_type": "Equity", "fully_qualified_name": "Opening Balance Equity"} f \N
Expand Down Expand Up @@ -3446,9 +3451,6 @@ COPY public.destination_attributes (id, attribute_type, display_name, value, des
752 VENDOR vendor Bob's Burger Joint 56 2022-05-23 11:33:51.369122+00 2022-05-23 11:33:51.36915+00 4 t {"email": null} f \N
753 VENDOR vendor Books by Bessie 30 2022-05-23 11:33:51.369213+00 2022-05-23 11:33:51.369241+00 4 t {"email": "Books@Intuit.com"} f \N
754 VENDOR vendor Brian Foster 76 2022-05-23 11:33:51.369303+00 2022-05-23 11:33:51.369331+00 4 t {"email": "user2@fyleforfyleforme.org"} f \N
755 VENDOR vendor Brosnahan Insurance Agency 31 2022-05-23 11:33:51.369394+00 2022-05-23 11:33:51.369422+00 4 t {"email": null} f \N
756 VENDOR vendor Cal Telephone 32 2022-05-23 11:33:51.369484+00 2022-05-23 11:33:51.369512+00 4 t {"email": null} f \N
757 VENDOR vendor Chin's Gas and Oil 33 2022-05-23 11:33:51.369573+00 2022-05-23 11:33:51.369601+00 4 t {"email": null} f \N
758 VENDOR vendor Cigna Health Care 34 2022-05-23 11:33:51.369761+00 2022-05-23 11:33:51.369789+00 4 t {"email": null} f \N
759 VENDOR vendor Computers by Jenni 35 2022-05-23 11:33:51.369852+00 2022-05-23 11:33:51.36988+00 4 t {"email": "Msfixit@Intuit.com"} f \N
760 VENDOR vendor Credit Card Misc 74 2022-05-23 11:33:51.369942+00 2022-05-23 11:33:51.369969+00 4 t {"email": null} f \N
Expand Down Expand Up @@ -3516,7 +3518,6 @@ COPY public.destination_attributes (id, attribute_type, display_name, value, des
827 CUSTOMER customer Weiskopf Consulting 29 2022-05-23 11:33:54.828007+00 2022-05-23 11:33:54.828035+00 4 t \N f \N
828 CLASS class Adidas 5000000000000142238 2022-05-23 11:33:57.279868+00 2022-05-23 11:33:57.279915+00 4 t \N f \N
829 CLASS class cc1 5000000000000142239 2022-05-23 11:33:57.280087+00 2022-05-23 11:33:57.280118+00 4 t \N f \N
830 CLASS class cc2 5000000000000142240 2022-05-23 11:33:57.280177+00 2022-05-23 11:33:57.280207+00 4 t \N f \N
831 CLASS class Coachella 5000000000000142241 2022-05-23 11:33:57.280265+00 2022-05-23 11:33:57.280295+00 4 t \N f \N
832 CLASS class Radio 5000000000000142242 2022-05-23 11:33:57.280352+00 2022-05-23 11:33:57.280382+00 4 t \N f \N
833 DEPARTMENT Department Bangalore 2 2022-05-23 11:34:01.996179+00 2022-05-23 11:34:01.996254+00 4 t \N f \N
Expand Down Expand Up @@ -3602,6 +3603,7 @@ COPY public.destination_attributes (id, attribute_type, display_name, value, des
897 CREDIT_CARD_ACCOUNT Credit Card Account 2285 Fyle Credit Card 106 2022-05-25 14:39:12.017261+00 2022-05-25 14:39:12.017339+00 5 t {"account_type": "Credit Card", "fully_qualified_name": "2285 Fyle Credit Card"} f \N
898 CREDIT_CARD_ACCOUNT Credit Card Account 3420 Fyle Credit Card 107 2022-05-25 14:39:12.017495+00 2022-05-25 14:39:12.017545+00 5 t {"account_type": "Credit Card", "fully_qualified_name": "3420 Fyle Credit Card"} f \N
899 CREDIT_CARD_ACCOUNT Credit Card Account Credit Card 103 2022-05-25 14:39:12.017683+00 2022-05-25 14:39:12.017731+00 5 t {"account_type": "Credit Card", "fully_qualified_name": "Credit Card"} f \N
1022 CUSTOMER customer Whitehead and Sons 5 2022-05-25 14:39:19.866292+00 2022-05-25 14:39:19.866335+00 5 t \N f \N
900 BANK_ACCOUNT Bank Account Auto 95 2022-05-25 14:39:12.032124+00 2022-05-25 14:39:12.032169+00 5 t {"account_type": "Bank", "fully_qualified_name": "Auto"} f \N
901 BANK_ACCOUNT Bank Account Cash on hand 94 2022-05-25 14:39:12.032243+00 2022-05-25 14:39:12.032274+00 5 t {"account_type": "Bank", "fully_qualified_name": "Cash on hand"} f \N
902 BANK_ACCOUNT Bank Account Current 81 2022-05-25 14:39:12.032344+00 2022-05-25 14:39:12.032375+00 5 t {"account_type": "Bank", "fully_qualified_name": "Current"} f \N
Expand Down Expand Up @@ -3714,7 +3716,6 @@ COPY public.destination_attributes (id, attribute_type, display_name, value, des
1019 CUSTOMER customer Oxon Insurance Agency:Oxon - Retreat 2014 62 2022-05-25 14:39:19.865835+00 2022-05-25 14:39:19.865864+00 5 t \N f \N
1020 CUSTOMER customer Rob deMontarnal 22 2022-05-25 14:39:19.865921+00 2022-05-25 14:39:19.86607+00 5 t \N f \N
1021 CUSTOMER customer Vendor KSKS 90 2022-05-25 14:39:19.866163+00 2022-05-25 14:39:19.866204+00 5 t \N f \N
1022 CUSTOMER customer Whitehead and Sons 5 2022-05-25 14:39:19.866292+00 2022-05-25 14:39:19.866335+00 5 t \N f \N
1023 CUSTOMER customer Whitehead and Sons:QBO 77 2022-05-25 14:39:19.866426+00 2022-05-25 14:39:19.866471+00 5 t \N f \N
1024 CUSTOMER customer Whitehead and Sons:Whitehead - Employee celebration 60 2022-05-25 14:39:19.866563+00 2022-05-25 14:39:19.866638+00 5 t \N f \N
1025 CLASS class Adidas 5100000000000030664 2022-05-25 14:39:22.066011+00 2022-05-25 14:39:22.066058+00 5 t \N f \N
Expand Down Expand Up @@ -4045,6 +4046,8 @@ COPY public.django_migrations (id, app, name, applied) FROM stdin;
187 workspaces 0046_workspacegeneralsettings_import_code_fields 2024-08-02 07:52:56.494868+00
188 fyle_accounting_mappings 0026_destinationattribute_code 2024-08-02 08:35:52.537882+00
189 quickbooks_online 0015_add_bill_number 2024-08-29 14:29:50.588003+00
190 quickbooks_online 0015_bill_is_retired 2024-09-03 15:08:15.085332+00
191 fyle 0038_expensegroup_export_url 2024-08-03 14:24:57.600169+00
\.


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

SELECT pg_catalog.setval('public.django_migrations_id_seq', 188, true);
SELECT pg_catalog.setval('public.django_migrations_id_seq', 191, true);


--
Expand Down
70 changes: 69 additions & 1 deletion tests/test_quickbooks_online/test_tasks.py
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
Expand Down Expand Up @@ -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)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Avoid hardcoding object IDs in tests

Using hardcoded IDs like id=14 in tests can make the tests brittle and dependent on the state of the database. Consider creating the necessary test data within the test function using factories or fixtures to ensure the test is self-contained and reliable.

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)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Use timezone.now() instead of datetime.now().replace(tzinfo=timezone.utc)

Using timezone.now() is preferred in Django to get the current time, as it respects the project's time zone settings and provides a time zone-aware datetime object.

Apply this diff to make the change:

-now = datetime.now().replace(tzinfo=timezone.utc)
+from django.utils import timezone
+now = timezone.now()

Committable suggestion was skipped due to low confidence.

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
Loading