Skip to content

Commit

Permalink
Export View Direct export
Browse files Browse the repository at this point in the history
  • Loading branch information
ruuushhh committed Feb 13, 2024
1 parent c3ccaef commit 3526ad5
Show file tree
Hide file tree
Showing 11 changed files with 284 additions and 110 deletions.
65 changes: 65 additions & 0 deletions apps/fyle/helpers.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,17 @@
import json
import traceback
import logging
from typing import List

import requests
from django.conf import settings
from apps.fyle.models import ExpenseGroupSettings
from apps.tasks.models import TaskLog
from apps.workspaces.models import WorkspaceGeneralSettings

logger = logging.getLogger(__name__)

SOURCE_ACCOUNT_MAP = {'PERSONAL': 'PERSONAL_CASH_ACCOUNT', 'CCC': 'PERSONAL_CORPORATE_CREDIT_CARD_ACCOUNT'}


def post_request(url, body, refresh_token=None):
Expand All @@ -24,6 +34,61 @@ def post_request(url, body, refresh_token=None):
raise Exception(response.text)


def get_source_account_type(fund_source: List[str]) -> List[str]:
"""
Get source account type
:param fund_source: fund source
:return: source account type
"""
source_account_type = []
for source in fund_source:
source_account_type.append(SOURCE_ACCOUNT_MAP[source])

Check warning on line 45 in apps/fyle/helpers.py

View check run for this annotation

Codecov / codecov/patch

apps/fyle/helpers.py#L43-L45

Added lines #L43 - L45 were not covered by tests

return source_account_type

Check warning on line 47 in apps/fyle/helpers.py

View check run for this annotation

Codecov / codecov/patch

apps/fyle/helpers.py#L47

Added line #L47 was not covered by tests


def get_filter_credit_expenses(expense_group_settings: ExpenseGroupSettings) -> bool:
"""
Get filter credit expenses
:param expense_group_settings: expense group settings
:return: filter credit expenses
"""
filter_credit_expenses = True
if expense_group_settings.import_card_credits:
filter_credit_expenses = False

Check warning on line 58 in apps/fyle/helpers.py

View check run for this annotation

Codecov / codecov/patch

apps/fyle/helpers.py#L56-L58

Added lines #L56 - L58 were not covered by tests

return filter_credit_expenses

Check warning on line 60 in apps/fyle/helpers.py

View check run for this annotation

Codecov / codecov/patch

apps/fyle/helpers.py#L60

Added line #L60 was not covered by tests


def get_fund_source(workspace_id: int) -> List[str]:
"""
Get fund source
:param workspace_id: workspace id
:return: fund source
"""
general_settings = WorkspaceGeneralSettings.objects.get(workspace_id=workspace_id)
fund_source = []
if general_settings.reimbursable_expenses_object:
fund_source.append('PERSONAL')
if general_settings.corporate_credit_card_expenses_object:
fund_source.append('CCC')

Check warning on line 74 in apps/fyle/helpers.py

View check run for this annotation

Codecov / codecov/patch

apps/fyle/helpers.py#L69-L74

Added lines #L69 - L74 were not covered by tests

return fund_source

Check warning on line 76 in apps/fyle/helpers.py

View check run for this annotation

Codecov / codecov/patch

apps/fyle/helpers.py#L76

Added line #L76 was not covered by tests


def handle_import_exception(task_log: TaskLog) -> None:
"""
Handle import exception
:param task_log: task log
:return: None
"""
error = traceback.format_exc()
task_log.detail = {'error': error}
task_log.status = 'FATAL'
task_log.save()
logger.error('Something unexpected happened workspace_id: %s %s', task_log.workspace_id, task_log.detail)

Check warning on line 89 in apps/fyle/helpers.py

View check run for this annotation

Codecov / codecov/patch

apps/fyle/helpers.py#L85-L89

Added lines #L85 - L89 were not covered by tests


def get_request(url, params, refresh_token):
"""
Create a HTTP get request.
Expand Down
1 change: 1 addition & 0 deletions apps/fyle/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,7 @@ def create_expense_objects(expenses: List[Dict], workspace_id: int):
"billable": expense["billable"]
if expense["billable"]
else False,
'workspace_id': workspace_id
},
)

Expand Down
13 changes: 13 additions & 0 deletions apps/fyle/queue.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
from django_q.tasks import async_task


def async_import_and_export_expenses(body: dict) -> None:
"""
Async'ly import and export expenses
:param body: body
:return: None
"""
if body.get('action') == 'ACCOUNTING_EXPORT_INITIATED' and body.get('data'):
report_id = body['data']['id']
org_id = body['data']['org_id']
async_task('apps.fyle.tasks.import_and_export_expenses', report_id, org_id)

Check warning on line 13 in apps/fyle/queue.py

View check run for this annotation

Codecov / codecov/patch

apps/fyle/queue.py#L10-L13

Added lines #L10 - L13 were not covered by tests
69 changes: 66 additions & 3 deletions apps/fyle/tasks.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import logging
import traceback
from datetime import datetime
from typing import List
from typing import List, Dict

from django.db import transaction
from fyle.platform.exceptions import InvalidTokenError as FyleInvalidTokenError
Expand All @@ -11,8 +11,10 @@
from apps.tasks.enums import TaskLogStatusEnum, TaskLogTypeEnum
from apps.workspaces.models import FyleCredential, Workspace, WorkspaceGeneralSettings

from .models import Expense, ExpenseGroup, ExpenseGroupSettings
from .enums import FundSourceEnum, PlatformExpensesEnum, ExpenseStateEnum
from apps.fyle.models import Expense, ExpenseGroup, ExpenseGroupSettings
from apps.fyle.enums import FundSourceEnum, PlatformExpensesEnum, ExpenseStateEnum
from apps.fyle.helpers import get_filter_credit_expenses, get_source_account_type, get_fund_source, handle_import_exception
from apps.workspaces.actions import export_to_xero


logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -159,3 +161,64 @@ def async_create_expense_groups(
def sync_dimensions(fyle_credentials):
platform = PlatformConnector(fyle_credentials)
platform.import_fyle_dimensions()


def group_expenses_and_save(expenses: List[Dict], task_log: TaskLog, workspace: Workspace):
expense_objects = Expense.create_expense_objects(expenses, workspace.id)
configuration: WorkspaceGeneralSettings = WorkspaceGeneralSettings.objects.get(workspace_id=workspace.id)
filtered_expenses = expense_objects
expenses_object_ids = [expense_object.id for expense_object in expense_objects]

Check warning on line 170 in apps/fyle/tasks.py

View check run for this annotation

Codecov / codecov/patch

apps/fyle/tasks.py#L167-L170

Added lines #L167 - L170 were not covered by tests

filtered_expenses = Expense.objects.filter(

Check warning on line 172 in apps/fyle/tasks.py

View check run for this annotation

Codecov / codecov/patch

apps/fyle/tasks.py#L172

Added line #L172 was not covered by tests
is_skipped=False,
id__in=expenses_object_ids,
expensegroup__isnull=True,
org_id=workspace.fyle_org_id
)

ExpenseGroup.create_expense_groups_by_report_id_fund_source(

Check warning on line 179 in apps/fyle/tasks.py

View check run for this annotation

Codecov / codecov/patch

apps/fyle/tasks.py#L179

Added line #L179 was not covered by tests
filtered_expenses, configuration, workspace.id
)

task_log.status = 'COMPLETE'
task_log.save()

Check warning on line 184 in apps/fyle/tasks.py

View check run for this annotation

Codecov / codecov/patch

apps/fyle/tasks.py#L183-L184

Added lines #L183 - L184 were not covered by tests


def import_and_export_expenses(report_id: str, org_id: str) -> None:
"""
Import and export expenses
:param report_id: report id
:param org_id: org id
:return: None
"""
workspace = Workspace.objects.get(fyle_org_id=org_id)
fyle_credentials = FyleCredential.objects.get(workspace_id=workspace.id)
expense_group_settings = ExpenseGroupSettings.objects.get(workspace_id=workspace.id)

Check warning on line 196 in apps/fyle/tasks.py

View check run for this annotation

Codecov / codecov/patch

apps/fyle/tasks.py#L194-L196

Added lines #L194 - L196 were not covered by tests

try:
with transaction.atomic():
task_log, _ = TaskLog.objects.update_or_create(workspace_id=workspace.id, type='FETCHING_EXPENSES', defaults={'status': 'IN_PROGRESS'})

Check warning on line 200 in apps/fyle/tasks.py

View check run for this annotation

Codecov / codecov/patch

apps/fyle/tasks.py#L198-L200

Added lines #L198 - L200 were not covered by tests

fund_source = get_fund_source(workspace.id)
source_account_type = get_source_account_type(fund_source)
filter_credit_expenses = get_filter_credit_expenses(expense_group_settings)

Check warning on line 204 in apps/fyle/tasks.py

View check run for this annotation

Codecov / codecov/patch

apps/fyle/tasks.py#L202-L204

Added lines #L202 - L204 were not covered by tests

platform = PlatformConnector(fyle_credentials)
expenses = platform.expenses.get(

Check warning on line 207 in apps/fyle/tasks.py

View check run for this annotation

Codecov / codecov/patch

apps/fyle/tasks.py#L206-L207

Added lines #L206 - L207 were not covered by tests
source_account_type,
filter_credit_expenses=filter_credit_expenses,
report_id=report_id
)

group_expenses_and_save(expenses, task_log, workspace)

Check warning on line 213 in apps/fyle/tasks.py

View check run for this annotation

Codecov / codecov/patch

apps/fyle/tasks.py#L213

Added line #L213 was not covered by tests

# Export only selected expense groups
expense_ids = Expense.objects.filter(report_id=report_id, org_id=org_id).values_list('id', flat=True)
expense_groups = ExpenseGroup.objects.filter(expenses__id__in=[expense_ids], workspace_id=workspace.id).distinct('id').values('id')
expense_group_ids = [expense_group['id'] for expense_group in expense_groups]

Check warning on line 218 in apps/fyle/tasks.py

View check run for this annotation

Codecov / codecov/patch

apps/fyle/tasks.py#L216-L218

Added lines #L216 - L218 were not covered by tests

if len(expense_group_ids):
export_to_xero(workspace.id, None, expense_group_ids)

Check warning on line 221 in apps/fyle/tasks.py

View check run for this annotation

Codecov / codecov/patch

apps/fyle/tasks.py#L220-L221

Added lines #L220 - L221 were not covered by tests

except Exception:
handle_import_exception(task_log)

Check warning on line 224 in apps/fyle/tasks.py

View check run for this annotation

Codecov / codecov/patch

apps/fyle/tasks.py#L223-L224

Added lines #L223 - L224 were not covered by tests
2 changes: 2 additions & 0 deletions apps/fyle/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
ExportableExpenseGroupsView,
RefreshFyleDimensionView,
SyncFyleDimensionView,
ExportView
)

urlpatterns = [
Expand All @@ -26,4 +27,5 @@
path("expense_group_settings/", ExpenseGroupSettingsView.as_view()),
path("sync_dimensions/", SyncFyleDimensionView.as_view()),
path("refresh_dimensions/", RefreshFyleDimensionView.as_view()),
path('exports/', ExportView.as_view(), name='exports')
]
14 changes: 14 additions & 0 deletions apps/fyle/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from apps.fyle.serializers import ExpenseFieldSerializer, ExpenseGroupSerializer, ExpenseGroupSettingsSerializer
from apps.fyle.tasks import async_create_expense_groups, get_task_log_and_fund_source
from fyle_xero_api.utils import LookupFieldMixin
from apps.fyle.queue import async_import_and_export_expenses


class ExpenseGroupView(LookupFieldMixin, generics.ListCreateAPIView):
Expand Down Expand Up @@ -106,3 +107,16 @@ def get(self, request, *args, **kwargs):
data={"exportable_expense_group_ids": expense_group_ids},
status=status.HTTP_200_OK,
)


class ExportView(generics.CreateAPIView):
"""
Export View
"""
authentication_classes = []
permission_classes = []

def post(self, request, *args, **kwargs):
async_import_and_export_expenses(request.data)

Check warning on line 120 in apps/fyle/views.py

View check run for this annotation

Codecov / codecov/patch

apps/fyle/views.py#L120

Added line #L120 was not covered by tests

return Response(data={}, status=status.HTTP_200_OK)

Check warning on line 122 in apps/fyle/views.py

View check run for this annotation

Codecov / codecov/patch

apps/fyle/views.py#L122

Added line #L122 was not covered by tests
63 changes: 62 additions & 1 deletion apps/workspaces/actions.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import logging
from datetime import datetime, timedelta

from django.contrib.auth import get_user_model
from django.core.cache import cache
Expand All @@ -11,10 +12,13 @@
from apps.fyle.helpers import get_cluster_domain
from apps.fyle.models import ExpenseGroupSettings
from apps.mappings.models import TenantMapping
from apps.workspaces.models import FyleCredential, LastExportDetail, Workspace, XeroCredentials
from apps.workspaces.models import FyleCredential, LastExportDetail, Workspace, XeroCredentials, WorkspaceGeneralSettings, WorkspaceSchedule
from apps.fyle.models import ExpenseGroup
from apps.workspaces.signals import post_delete_xero_connection
from apps.workspaces.utils import generate_xero_refresh_token
from apps.xero.utils import XeroConnector
from apps.fyle.enums import FundSourceEnum
from apps.xero.tasks import schedule_bank_transaction_creation, schedule_bills_creation

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -170,3 +174,60 @@ def get_workspace_admin(workspace_id):
{"name": employee.detail["full_name"], "email": admin.email}
)
return admin_email


def export_to_xero(workspace_id, export_mode="MANUAL", expense_group_ids=[]):
general_settings = WorkspaceGeneralSettings.objects.get(workspace_id=workspace_id)
last_export_detail = LastExportDetail.objects.get(workspace_id=workspace_id)
workspace_schedule = WorkspaceSchedule.objects.filter(workspace_id=workspace_id, interval_hours__gt=0, enabled=True).first()

last_exported_at = datetime.now()
is_expenses_exported = False
export_mode = export_mode or 'MANUAL'
expense_group_filters = {
'exported_at__isnull': True,
'workspace_id': workspace_id
}
if expense_group_ids:
expense_group_filters['id__in'] = expense_group_ids

Check warning on line 192 in apps/workspaces/actions.py

View check run for this annotation

Codecov / codecov/patch

apps/workspaces/actions.py#L192

Added line #L192 was not covered by tests

if general_settings.reimbursable_expenses_object:
expense_group_ids = ExpenseGroup.objects.filter(
fund_source=FundSourceEnum.PERSONAL,
**expense_group_filters
).values_list("id", flat=True)

if len(expense_group_ids):
is_expenses_exported = True

schedule_bills_creation(
workspace_id=workspace_id,
expense_group_ids=expense_group_ids,
is_auto_export=export_mode == 'AUTO',
fund_source='PERSONAL'
)

if general_settings.corporate_credit_card_expenses_object:
expense_group_ids = ExpenseGroup.objects.filter(
fund_source=FundSourceEnum.CCC,
**expense_group_filters
).values_list("id", flat=True)

if len(expense_group_ids):
is_expenses_exported = True

Check warning on line 217 in apps/workspaces/actions.py

View check run for this annotation

Codecov / codecov/patch

apps/workspaces/actions.py#L217

Added line #L217 was not covered by tests

schedule_bank_transaction_creation(
workspace_id=workspace_id,
expense_group_ids=expense_group_ids,
is_auto_export=export_mode == 'AUTO',
fund_source='CCC'
)

if is_expenses_exported:
last_export_detail.last_exported_at = last_exported_at
last_export_detail.export_mode = export_mode or 'MANUAL'

if workspace_schedule:
last_export_detail.next_export_at = last_exported_at + timedelta(hours=workspace_schedule.interval_hours)

Check warning on line 231 in apps/workspaces/actions.py

View check run for this annotation

Codecov / codecov/patch

apps/workspaces/actions.py#L231

Added line #L231 was not covered by tests

last_export_detail.save()
37 changes: 2 additions & 35 deletions apps/workspaces/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
from fyle_rest_auth.helpers import get_fyle_admin
from apps.fyle.helpers import post_request

from apps.fyle.models import ExpenseGroup
from apps.fyle.tasks import async_create_expense_groups
from apps.fyle.enums import FundSourceEnum

Expand All @@ -19,9 +18,8 @@
from apps.users.models import User

from apps.workspaces.email import get_admin_name, get_errors, get_failed_task_logs_count, send_failure_notification_email
from apps.workspaces.models import FyleCredential, LastExportDetail, Workspace, WorkspaceGeneralSettings, WorkspaceSchedule

from apps.xero.tasks import create_chain_and_export, schedule_bank_transaction_creation, schedule_bills_creation
from apps.workspaces.models import FyleCredential, Workspace, WorkspaceGeneralSettings, WorkspaceSchedule
from apps.workspaces.actions import export_to_xero


logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -57,37 +55,6 @@ def run_sync_schedule(workspace_id):
export_to_xero(workspace_id, "AUTO")


def export_to_xero(workspace_id, export_mode="MANUAL"):
general_settings = WorkspaceGeneralSettings.objects.get(workspace_id=workspace_id)
last_export_detail = LastExportDetail.objects.get(workspace_id=workspace_id)
last_exported_at = datetime.now()
chaining_attributes = []

if general_settings.reimbursable_expenses_object:
expense_group_ids = ExpenseGroup.objects.filter(
fund_source=FundSourceEnum.PERSONAL,
workspace_id=workspace_id
).values_list("id", flat=True)
chaining_attributes.extend(
schedule_bills_creation(workspace_id, expense_group_ids)
)

if general_settings.corporate_credit_card_expenses_object:
expense_group_ids = ExpenseGroup.objects.filter(
fund_source=FundSourceEnum.CCC,
workspace_id=workspace_id
).values_list("id", flat=True)
chaining_attributes.extend(
schedule_bank_transaction_creation(workspace_id, expense_group_ids)
)

if chaining_attributes:
create_chain_and_export(chaining_attributes, workspace_id)
last_export_detail.last_exported_at = last_exported_at
last_export_detail.export_mode = export_mode
last_export_detail.save()


def async_update_fyle_credentials(fyle_org_id: str, refresh_token: str):
fyle_credentials = FyleCredential.objects.filter(
workspace__fyle_org_id=fyle_org_id
Expand Down
2 changes: 1 addition & 1 deletion apps/workspaces/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
WorkspaceSerializer,
XeroCredentialSerializer,
)
from apps.workspaces.tasks import export_to_xero
from apps.workspaces.actions import export_to_xero
from apps.workspaces.utils import generate_xero_identity

logger = logging.getLogger(__name__)
Expand Down
Loading

0 comments on commit 3526ad5

Please sign in to comment.