diff --git a/apps/accounting_exports/models.py b/apps/accounting_exports/models.py index dd3f315d..3c703bee 100644 --- a/apps/accounting_exports/models.py +++ b/apps/accounting_exports/models.py @@ -10,9 +10,10 @@ CustomDateTimeField, BooleanFalseField, TextNotNullField, - StringOptionsField + StringOptionsField, + IntegerNullField ) -from apps.workspaces.models import BaseForeignWorkspaceModel +from apps.workspaces.models import BaseForeignWorkspaceModel, BaseModel from apps.fyle.models import Expense TYPE_CHOICES = ( @@ -22,9 +23,13 @@ ('FETCHING_CREDIT_CARD_EXPENENSES', 'FETCHING_CREDIT_CARD_EXPENENSES') ) - ERROR_TYPE_CHOICES = (('EMPLOYEE_MAPPING', 'EMPLOYEE_MAPPING'), ('CATEGORY_MAPPING', 'CATEGORY_MAPPING'), ('SAGE300_ERROR', 'SAGE300_ERROR')) +EXPORT_MODE_CHOICES = ( + ('MANUAL', 'MANUAL'), + ('AUTO', 'AUTO') +) + class AccountingExport(BaseForeignWorkspaceModel): """ @@ -66,3 +71,19 @@ class Error(BaseForeignWorkspaceModel): class Meta: db_table = 'errors' + + +class AccountingExportSummary(BaseModel): + """ + Table to store accounting export summary + """ + id = models.AutoField(primary_key=True) + last_exported_at = CustomDateTimeField(help_text='Last exported at datetime') + next_export_at = CustomDateTimeField(help_text='next export datetime') + export_mode = StringOptionsField(choices=EXPORT_MODE_CHOICES, help_text='Export mode') + total_accounting_export_count = IntegerNullField(help_text='Total count of accounting export exported') + successful_accounting_export_count = IntegerNullField(help_text='count of successful accounting export') + failed_accounting_export_count = IntegerNullField(help_text='count of failed accounting export') + + class Meta: + db_table = 'accounting_export_summary' diff --git a/apps/accounting_exports/serializers.py b/apps/accounting_exports/serializers.py index 38a2c903..56d1b76e 100644 --- a/apps/accounting_exports/serializers.py +++ b/apps/accounting_exports/serializers.py @@ -1,6 +1,6 @@ from rest_framework import serializers -from .models import AccountingExport, Error +from .models import AccountingExport, Error, AccountingExportSummary class AccountingExportSerializer(serializers.ModelSerializer): @@ -13,6 +13,16 @@ class Meta: fields = '__all__' +class AccountingExportSummarySerializer(serializers.ModelSerializer): + """ + Accounting Export Summary serializer + """ + + class Meta: + model = AccountingExportSummary + fields = '__all__' + + class ErrorSerializer(serializers.ModelSerializer): """ Serializer for the Errors diff --git a/apps/accounting_exports/urls.py b/apps/accounting_exports/urls.py index 0601ffae..8e1b5a01 100644 --- a/apps/accounting_exports/urls.py +++ b/apps/accounting_exports/urls.py @@ -14,11 +14,12 @@ """ from django.urls import path -from .views import AccountingExportView, ErrorsView, AccountingExportCountView +from .views import AccountingExportView, ErrorsView, AccountingExportCountView, AccountingExportSummaryView urlpatterns = [ path('', AccountingExportView.as_view(), name='accounting-exports'), path('count/', AccountingExportCountView.as_view(), name='accounting-exports-count'), + path('summary/', AccountingExportSummaryView.as_view(), name='accounting-exports-summary'), path('errors/', ErrorsView.as_view(), name='errors'), ] diff --git a/apps/accounting_exports/views.py b/apps/accounting_exports/views.py index 7664ccda..e7200371 100644 --- a/apps/accounting_exports/views.py +++ b/apps/accounting_exports/views.py @@ -5,8 +5,9 @@ from rest_framework.response import Response from sage_desktop_api.utils import LookupFieldMixin -from apps.accounting_exports.serializers import AccountingExportSerializer, ErrorSerializer -from apps.accounting_exports.models import AccountingExport, Error +from apps.accounting_exports.serializers import AccountingExportSerializer, ErrorSerializer, AccountingExportSummarySerializer +from apps.accounting_exports.models import AccountingExport, Error, AccountingExportSummary + logger = logging.getLogger(__name__) logger.level = logging.INFO @@ -36,6 +37,17 @@ def get(self, request, *args, **kwargs): return Response({"count": AccountingExport.objects.filter(**params).count()}) +class AccountingExportSummaryView(generics.RetrieveAPIView): + """ + Retrieve Accounting Export Summary + """ + lookup_field = 'workspace_id' + lookup_url_kwarg = 'workspace_id' + + queryset = AccountingExportSummary.objects.filter(last_exported_at__isnull=False, total_accounting_export_count__gt=0) + serializer_class = AccountingExportSummarySerializer + + class ErrorsView(LookupFieldMixin, generics.ListAPIView): serializer_class = ErrorSerializer queryset = Error.objects.all() diff --git a/tests/conftest.py b/tests/conftest.py index 24cc57c7..803231df 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -17,7 +17,7 @@ Sage300Credential ) from apps.fyle.models import ExpenseFilter -from apps.accounting_exports.models import AccountingExport, Error +from apps.accounting_exports.models import AccountingExport, Error, AccountingExportSummary from sage_desktop_api.tests import settings from .test_fyle.fixtures import fixtures as fyle_fixtures @@ -254,3 +254,24 @@ def add_errors(): error_detail='Sage Error', workspace_id=workspace_id ) + + +@pytest.fixture() +@pytest.mark.django_db(databases=['default']) +def add_accounting_export_summary(): + """ + Pytest fixture to add accounting export summary to a workspace + """ + workspace_ids = [ + 1, 2, 3 + ] + for workspace_id in workspace_ids: + AccountingExportSummary.objects.create( + workspace_id=workspace_id, + last_exported_at = datetime.now(tz=timezone.utc), + next_export_at = datetime.now(tz=timezone.utc), + export_mode = 'AUTO', + total_accounting_export_count = 10, + successful_accounting_export_count = 5, + failed_accounting_export_count = 5 + ) diff --git a/tests/test_accounting_exports/test_views.py b/tests/test_accounting_exports/test_views.py index 9cff65d4..f5e80f66 100644 --- a/tests/test_accounting_exports/test_views.py +++ b/tests/test_accounting_exports/test_views.py @@ -27,6 +27,17 @@ def test_get_accounting_exports(api_client, test_connection, create_temp_workspa assert response['count'] == 2, 'accounting export count api return diffs in keys' +def test_get_accounting_export_summary(api_client, test_connection, create_temp_workspace, add_fyle_credentials, add_accounting_export_summary): + url = reverse('accounting-exports-summary', kwargs={'workspace_id': 1}) + + api_client.credentials(HTTP_AUTHORIZATION='Bearer {}'.format(test_connection.access_token)) + + response = api_client.get(url) + assert response.status_code == 200 + response = json.loads(response.content) + assert dict_compare_keys(response, data['accounting_export_summary_response']) == [], 'expense group api return diffs in keys' + + def test_get_errors(api_client, test_connection, create_temp_workspace, add_fyle_credentials, add_errors): """ Test get errors diff --git a/tests/test_fyle/fixtures.py b/tests/test_fyle/fixtures.py index 6bb717da..a4878d48 100644 --- a/tests/test_fyle/fixtures.py +++ b/tests/test_fyle/fixtures.py @@ -188,5 +188,17 @@ 'display_name': 'Project', 'is_dependant': False } - ] + ], + 'accounting_export_summary_response': { + "id":1, + "created_at":"2023-10-27T04:53:59.287745Z", + "updated_at":"2023-10-27T04:53:59.287750Z", + "last_exported_at":"2023-10-27T04:53:59.287618Z", + "next_export_at":"2023-10-27T04:53:59.287619Z", + "export_mode":"AUTO", + "total_accounting_export_count":10, + "successful_accounting_export_count":5, + "failed_accounting_export_count":5, + "workspace":1 + } }