Skip to content

Commit

Permalink
Max retry exports (#391)
Browse files Browse the repository at this point in the history
* Max retry export with test cases

* added interval hours

* fixed flake

* change log level

* cahnge log level

* flake resolved

* remove loggers
  • Loading branch information
Ashutosh619-sudo authored Sep 3, 2024
1 parent 178d999 commit 2582aa0
Show file tree
Hide file tree
Showing 3 changed files with 132 additions and 9 deletions.
7 changes: 5 additions & 2 deletions apps/workspaces/actions.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
from apps.xero.utils import XeroConnector

logger = logging.getLogger(__name__)
logger.level = logging.INFO


def post_workspace(access_token, request):
Expand Down Expand Up @@ -205,7 +206,8 @@ def export_to_xero(workspace_id, export_mode="MANUAL", expense_group_ids=[]):
workspace_id=workspace_id,
expense_group_ids=expense_group_ids,
is_auto_export=export_mode == 'AUTO',
fund_source='PERSONAL'
fund_source='PERSONAL',
interval_hours=workspace_schedule.interval_hours if workspace_schedule else 0
)

if general_settings.corporate_credit_card_expenses_object:
Expand All @@ -221,7 +223,8 @@ def export_to_xero(workspace_id, export_mode="MANUAL", expense_group_ids=[]):
workspace_id=workspace_id,
expense_group_ids=expense_group_ids,
is_auto_export=export_mode == 'AUTO',
fund_source='CCC'
fund_source='CCC',
interval_hours=workspace_schedule.interval_hours if workspace_schedule else 0
)

if is_expenses_exported:
Expand Down
42 changes: 38 additions & 4 deletions apps/xero/queue.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from datetime import datetime, timedelta
from datetime import datetime, timedelta, timezone
from typing import List
import logging

from django.db.models import Q
from django_q.models import Schedule
Expand All @@ -9,11 +10,26 @@
from apps.fyle.models import Expense, ExpenseGroup
from apps.mappings.models import GeneralMapping
from apps.tasks.enums import TaskLogStatusEnum, TaskLogTypeEnum
from apps.tasks.models import TaskLog
from apps.tasks.models import TaskLog, Error
from apps.workspaces.models import FyleCredential, XeroCredentials
from apps.xero.utils import XeroConnector


logger = logging.getLogger(__name__)
logger.level = logging.INFO


def validate_failing_export(is_auto_export: bool, interval_hours: int, error: Error):
"""
Validate failing export
:param is_auto_export: Is auto export
:param interval_hours: Interval hours
:param error: Error
"""
# If auto export is enabled and interval hours is set and error repetition count is greater than 100, export only once a day
return is_auto_export and interval_hours and error and error.repetition_count > 100 and datetime.now().replace(tzinfo=timezone.utc) - error.updated_at <= timedelta(hours=24)


def schedule_payment_creation(sync_fyle_to_xero_payments, workspace_id):
general_mappings: GeneralMapping = GeneralMapping.objects.filter(
workspace_id=workspace_id
Expand Down Expand Up @@ -106,7 +122,7 @@ def __create_chain_and_run(fyle_credentials: FyleCredential, xero_connection, in
chain.run()


def schedule_bills_creation(workspace_id: int, expense_group_ids: List[str], is_auto_export: bool, fund_source: str) -> list:
def schedule_bills_creation(workspace_id: int, expense_group_ids: List[str], is_auto_export: bool, fund_source: str, interval_hours: int) -> list:
"""
Schedule bills creation
:param expense_group_ids: List of expense group ids
Expand All @@ -123,16 +139,25 @@ def schedule_bills_creation(workspace_id: int, expense_group_ids: List[str], is_
exported_at__isnull=True,
).all()

errors = Error.objects.filter(workspace_id=workspace_id, is_resolved=False, expense_group_id__in=expense_group_ids).all()

chain_tasks = []
in_progress_expenses = []

for index, expense_group in enumerate(expense_groups):
error = errors.filter(workspace_id=workspace_id, expense_group=expense_group, is_resolved=False).first()
skip_export = validate_failing_export(is_auto_export, interval_hours, error)
if skip_export:
logger.info('Skipping expense group %s as it has %s errors', expense_group.id, error.repetition_count)
continue

task_log, _ = TaskLog.objects.get_or_create(
workspace_id=expense_group.workspace_id,
expense_group=expense_group,
defaults={"status": TaskLogStatusEnum.ENQUEUED, "type": TaskLogTypeEnum.CREATING_BILL},
)
if task_log.status not in [TaskLogStatusEnum.IN_PROGRESS, TaskLogStatusEnum.ENQUEUED]:
task_log.type = TaskLogTypeEnum.CREATING_BILL
task_log.status = TaskLogStatusEnum.ENQUEUED
task_log.save()

Expand Down Expand Up @@ -173,7 +198,7 @@ def schedule_bills_creation(workspace_id: int, expense_group_ids: List[str], is_


def schedule_bank_transaction_creation(
workspace_id: int, expense_group_ids: List[str], is_auto_export: bool, fund_source: str
workspace_id: int, expense_group_ids: List[str], is_auto_export: bool, fund_source: str, interval_hours: int
) -> list:
"""
Schedule bank transaction creation
Expand All @@ -191,16 +216,25 @@ def schedule_bank_transaction_creation(
exported_at__isnull=True,
).all()

errors = Error.objects.filter(workspace_id=workspace_id, is_resolved=False, expense_group_id__in=expense_group_ids).all()

chain_tasks = []
in_progress_expenses = []

for index, expense_group in enumerate(expense_groups):
error = errors.filter(workspace_id=workspace_id, expense_group=expense_group, is_resolved=False).first()
skip_export = validate_failing_export(is_auto_export, interval_hours, error)
if skip_export:
logger.info('Skipping expense group %s as it has %s errors', expense_group.id, error.repetition_count)
continue

task_log, _ = TaskLog.objects.get_or_create(
workspace_id=expense_group.workspace_id,
expense_group=expense_group,
defaults={"status": TaskLogStatusEnum.ENQUEUED, "type": TaskLogTypeEnum.CREATING_BANK_TRANSACTION},
)
if task_log.status not in [TaskLogStatusEnum.IN_PROGRESS, TaskLogStatusEnum.ENQUEUED]:
task_log.type = TaskLogTypeEnum.CREATING_BANK_TRANSACTION
task_log.status = TaskLogStatusEnum.ENQUEUED
task_log.save()

Expand Down
92 changes: 89 additions & 3 deletions tests/test_xero/test_tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

from apps.fyle.models import Expense, ExpenseGroup, Reimbursement
from apps.mappings.models import GeneralMapping, TenantMapping
from apps.tasks.models import TaskLog
from apps.tasks.models import Error, TaskLog
from apps.workspaces.models import LastExportDetail, WorkspaceGeneralSettings, XeroCredentials
from apps.xero.exceptions import update_last_export_details
from apps.xero.models import BankTransaction, BankTransactionLineItem, Bill, BillLineItem
Expand Down Expand Up @@ -416,7 +416,7 @@ def test_schedule_bills_creation(db):
task_log.save()

schedule_bills_creation(
workspace_id=workspace_id, expense_group_ids=[4], is_auto_export=False, fund_source="PERSONAL"
workspace_id=workspace_id, expense_group_ids=[4], is_auto_export=False, fund_source="PERSONAL", interval_hours=0
)


Expand Down Expand Up @@ -514,7 +514,7 @@ def test_schedule_bank_transaction_creation(db):
task_log.save()

schedule_bank_transaction_creation(
workspace_id=workspace_id, expense_group_ids=[5], is_auto_export=False, fund_source="CCC"
workspace_id=workspace_id, expense_group_ids=[5], is_auto_export=False, fund_source="CCC", interval_hours=0
)


Expand Down Expand Up @@ -1019,3 +1019,89 @@ def test__validate_expense_group(mocker, db):
__validate_expense_group(expense_group)
except Exception:
logger.info("Mappings are missing")


def test_skipping_schedule_bills_creation(db):
workspace_id = 1

expense_group = ExpenseGroup.objects.get(id=4)
expense_group.exported_at = None
expense_group.save()

bill = Bill.objects.get(expense_group_id=4)
BillLineItem.objects.filter(bill=bill).delete()
TaskLog.objects.filter(bill=bill).update(bill=None)
bill.delete()

task_log = TaskLog.objects.filter(expense_group_id=expense_group.id).first()
task_log.type = 'FETCHING_EXPENSES'
task_log.status = "READY"
task_log.save()

error = Error.objects.create(
workspace_id=workspace_id,
type='NETSUITE_ERROR',
error_title='NetSuite System Error',
error_detail='An error occured in a upsert request: Please enter value(s) for: Location',
expense_group_id=expense_group.id,
repetition_count=106
)

schedule_bills_creation(
workspace_id=workspace_id, expense_group_ids=[4], is_auto_export=True, fund_source="CCC", interval_hours=1
)

task_log = TaskLog.objects.filter(expense_group_id=expense_group.id).first()
assert task_log.type == 'FETCHING_EXPENSES'

Error.objects.filter(id=error.id).update(updated_at=datetime(2024, 8, 20))

schedule_bills_creation(
workspace_id=workspace_id, expense_group_ids=[4], is_auto_export=True, fund_source="CCC", interval_hours=1
)

task_log = TaskLog.objects.filter(expense_group_id=expense_group.id).first()
assert task_log.type == 'CREATING_BILL'


def test_skipping_schedule_bank_transaction_creation(db):
workspace_id = 1

expense_group = ExpenseGroup.objects.get(id=5)
expense_group.exported_at = None
expense_group.save()

bank_tran = BankTransaction.objects.get(expense_group_id=5)
BankTransactionLineItem.objects.filter(bank_transaction=bank_tran).delete()
TaskLog.objects.filter(bank_transaction=bank_tran).update(bank_transaction=None)
bank_tran.delete()

task_log = TaskLog.objects.filter(expense_group_id=expense_group.id).first()
task_log.type = 'FETCHING_EXPENSES'
task_log.status = "READY"
task_log.save()

error = Error.objects.create(
workspace_id=workspace_id,
type='NETSUITE_ERROR',
error_title='NetSuite System Error',
error_detail='An error occured in a upsert request: Please enter value(s) for: Location',
expense_group_id=expense_group.id,
repetition_count=106
)

schedule_bank_transaction_creation(
workspace_id=workspace_id, expense_group_ids=[5], is_auto_export=True, fund_source="CCC", interval_hours=1
)

task_log = TaskLog.objects.filter(expense_group_id=expense_group.id).first()
assert task_log.type == 'FETCHING_EXPENSES'

Error.objects.filter(id=error.id).update(updated_at=datetime(2024, 8, 20))

schedule_bank_transaction_creation(
workspace_id=workspace_id, expense_group_ids=[5], is_auto_export=True, fund_source="CCC", interval_hours=1
)

task_log = TaskLog.objects.filter(expense_group_id=expense_group.id).first()
assert task_log.type == 'CREATING_BANK_TRANSACTION'

0 comments on commit 2582aa0

Please sign in to comment.