-
Notifications
You must be signed in to change notification settings - Fork 0
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
Add support for editing expenses #87
Changes from all commits
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 | ||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -5,8 +5,9 @@ | |||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||
from django.conf import settings | ||||||||||||||||||||||||||||||||||||
from fyle.platform import Platform | ||||||||||||||||||||||||||||||||||||
from rest_framework.exceptions import ValidationError | ||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||
from apps.workspaces.models import FyleCredential | ||||||||||||||||||||||||||||||||||||
from apps.workspaces.models import Workspace, FyleCredential | ||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||
def post_request(url, body, refresh_token=None): | ||||||||||||||||||||||||||||||||||||
|
@@ -142,3 +143,13 @@ def download_iif_file(file_id: str, workspace_id: int): | |||||||||||||||||||||||||||||||||||
)['data'][0]['download_url'] | ||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||
return download_url | ||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||
def assert_valid_request(workspace_id:int, org_id:str): | ||||||||||||||||||||||||||||||||||||
""" | ||||||||||||||||||||||||||||||||||||
Assert if the request is valid by checking | ||||||||||||||||||||||||||||||||||||
the url_workspace_id and fyle_org_id workspace | ||||||||||||||||||||||||||||||||||||
""" | ||||||||||||||||||||||||||||||||||||
workspace = Workspace.objects.get(org_id=org_id) | ||||||||||||||||||||||||||||||||||||
if workspace.id != workspace_id: | ||||||||||||||||||||||||||||||||||||
raise ValidationError('Workspace mismatch') | ||||||||||||||||||||||||||||||||||||
Comment on lines
+148
to
+155
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. Validate workspace and org ID matching logic in Ensure that the function - raise ValidationError('Workspace mismatch')
+ logger.warning("Workspace mismatch - attempted access with org_id: %s", org_id)
+ raise ValidationError(f"Workspace mismatch for org_id: {org_id}") Committable suggestion
Suggested change
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,13 +3,18 @@ | |
* User Triggered Async Tasks | ||
* Schedule Triggered Async Tasks | ||
""" | ||
import logging | ||
from django_q.tasks import async_task | ||
from apps.fyle.tasks import ( | ||
import_credit_card_expenses, | ||
import_reimbursable_expenses | ||
) | ||
from apps.workspaces.models import Workspace | ||
from apps.tasks.models import AccountingExport | ||
from apps.fyle.helpers import assert_valid_request | ||
|
||
logger = logging.getLogger(__name__) | ||
logger.level = logging.INFO | ||
|
||
|
||
def queue_import_reimbursable_expenses(workspace_id: int, synchronous: bool = False): | ||
|
@@ -60,14 +65,20 @@ def queue_import_credit_card_expenses(workspace_id: int, synchronous: bool = Fal | |
import_credit_card_expenses(workspace_id, accounting_export) | ||
|
||
|
||
def async_handle_webhook_callback(body: dict) -> None: | ||
def async_handle_webhook_callback(body: dict, workspace_id: int) -> None: | ||
""" | ||
Async'ly import and export expenses | ||
:param body: bodys | ||
:param body: body | ||
:return: None | ||
""" | ||
if body.get('action') == 'ACCOUNTING_EXPORT_INITIATED' and body.get('data'): | ||
org_id = body['data']['org_id'] | ||
|
||
assert_valid_request(workspace_id=workspace_id, org_id=org_id) | ||
workspace = Workspace.objects.get(org_id=org_id) | ||
async_task('apps.workspaces.tasks.run_import_export', workspace.id) | ||
|
||
elif body.get('action') == 'UPDATED_AFTER_APPROVAL' and body.get('data') and body.get('resource') == 'EXPENSE': | ||
org_id = body['data']['org_id'] | ||
logger.info("| Updating non-exported expenses through webhook | Content: {{WORKSPACE_ID: {} Payload: {}}}".format(workspace_id, body.get('data'))) | ||
assert_valid_request(workspace_id=workspace_id, org_id=org_id) | ||
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. move out |
||
async_task('apps.fyle.tasks.update_non_exported_expenses', body['data']) |
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -5,13 +5,15 @@ | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
2. Import Credit Card Expenses from Fyle | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
""" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import logging | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
from typing import Dict | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
from datetime import datetime | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import traceback | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
from django.db import transaction | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
from fyle_integrations_platform_connector import PlatformConnector | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
from fyle.platform.exceptions import RetryException, NoPrivilegeError | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
from fyle_integrations_platform_connector.apis.expenses import Expenses as FyleExpenses | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
from apps.tasks.models import AccountingExport | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
from apps.workspaces.models import Workspace, ExportSettings, FyleCredential | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
@@ -151,3 +153,21 @@ def import_credit_card_expenses(workspace_id, accounting_export: AccountingExpor | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
accounting_export.status = 'FATAL' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
accounting_export.save() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
logger.exception('Something unexpected happened workspace_id: %s %s', accounting_export.workspace_id, accounting_export.errors) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
def update_non_exported_expenses(data: Dict) -> None: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
""" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
To update expenses not in COMPLETE, IN_PROGRESS state | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
""" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
org_id = data['org_id'] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
expense_id = data['id'] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
workspace = Workspace.objects.get(org_id=org_id) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
expense = Expense.objects.filter(workspace_id=workspace.id, expense_id=expense_id).first() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if expense and not expense.exported: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
expense_obj = [] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
expense_obj.append(data) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
expense_objects = FyleExpenses().construct_expense_object(expense_obj, expense.workspace_id) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Expense.create_expense_objects( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
expense_objects, expense.workspace_id | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+158
to
+173
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. Ensure robust handling in The function should handle cases where the expense does not exist or has already been exported more robustly. Consider adding more detailed logging and error handling. - if expense and not expense.exported:
+ if not expense:
+ logger.error("Expense with ID %s not found in workspace %s", expense_id, workspace.id)
+ return
+ elif expense.exported:
+ logger.info("Expense with ID %s already exported from workspace %s", expense_id, workspace.id)
+ return Committable suggestion
Suggested change
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -31,6 +31,6 @@ class WebhookCallbackView(generics.CreateAPIView): | |
|
||
@handle_view_exceptions() | ||
def post(self, request, *args, **kwargs): | ||
async_handle_webhook_callback(request.data) | ||
async_handle_webhook_callback(request.data, int(kwargs['workspace_id'])) | ||
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. Tip Codebase Verification Ensure proper error handling and logging in webhook callback. The
Analysis chainEnsure proper error handling and logging in webhook callback. The Scripts executedThe following scripts were executed for the analysis: Script: #!/bin/bash
# Description: Verify the error handling in `async_handle_webhook_callback`.
# Test: Search for the function definition. Expect: Proper exception handling.
rg --type python 'def async_handle_webhook_callback' -A 20
Length of output: 91 Script: #!/bin/bash
# Description: Verify the error handling in `async_handle_webhook_callback`.
# Correct the file type and search for the function definition. Expect: Proper exception handling.
rg 'def async_handle_webhook_callback' -A 50
Length of output: 1328 Script: #!/bin/bash
# Description: Verify the error handling in `assert_valid_request`.
# Search for the function definition of `assert_valid_request`. Expect: Proper exception handling.
rg 'def assert_valid_request' -A 20
Length of output: 517 |
||
|
||
return Response(data={}, status=status.HTTP_200_OK) |
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.
Ensure detailed logging and user-friendly error messages for
ValidationError
.The new exception handling for
ValidationError
logs the exception and returns a detailed message. Ensure that the log level is appropriate (e.g., ERROR rather than INFO) and that the message is clear and user-friendly.Committable suggestion