diff --git a/Dockerfile b/Dockerfile index ab5f4333..6ede4da4 100644 --- a/Dockerfile +++ b/Dockerfile @@ -28,7 +28,7 @@ COPY . /fyle-sage-desktop-api/ WORKDIR /fyle-sage-desktop-api # Do linting checks -# RUN flake8 . +RUN flake8 . # Expose development port EXPOSE 8000 diff --git a/apps/sage300/serializers.py b/apps/sage300/serializers.py index 678ba976..6a29aadf 100644 --- a/apps/sage300/serializers.py +++ b/apps/sage300/serializers.py @@ -1,9 +1,12 @@ import logging +from django.db.models import Q from datetime import datetime from rest_framework import serializers from rest_framework.response import Response from rest_framework.views import status +from fyle_accounting_mappings.models import DestinationAttribute + from apps.workspaces.models import Workspace, Sage300Credential from apps.sage300.helpers import sync_dimensions, check_interval_and_sync_dimension @@ -27,7 +30,9 @@ def create(self, validated_data): # Retrieve the workspace and Sage 300 credentials workspace = Workspace.objects.get(pk=workspace_id) - sage_intacct_credentials = Sage300Credential.objects.get(workspace_id=workspace.id) + sage_intacct_credentials = Sage300Credential.objects.get( + workspace_id=workspace.id + ) if refresh_dimension: # If 'refresh' is true, perform a full sync of dimensions @@ -45,10 +50,56 @@ def create(self, validated_data): except Sage300Credential.DoesNotExist: # Handle the case when Sage 300 credentials are not found or invalid - raise serializers.ValidationError({'message': 'Sage300 credentials not found / invalid in workspace'}) + raise serializers.ValidationError( + {'message': 'Sage300 credentials not found / invalid in workspace'} + ) except Exception as exception: # Handle unexpected exceptions and log the error - logger.error('Something unexpected happened workspace_id: %s %s', workspace_id, exception) + logger.error( + 'Something unexpected happened workspace_id: %s %s', + workspace_id, + exception, + ) # Raise a custom exception or re-raise the original exception - raise Exception() + raise + + +class DestinationAttributeSerializer(serializers.Serializer): + attribute_type = serializers.CharField() + display_name = serializers.CharField() + + +class Sage300FieldSerializer(serializers.Serializer): + """ + Sage300 Expense Fields Serializer + """ + + attribute_type = serializers.CharField() + display_name = serializers.CharField() + + def format_sage300_fields(self, workspace_id): + attribute_types = [ + "VENDOR", + "ACCOUNT", + "JOB", + "CATEGORY", + "COST_CODE", + "PAYMENT", + ] + attributes = ( + DestinationAttribute.objects.filter( + ~Q(attribute_type__in=attribute_types), + workspace_id=workspace_id, + ) + .values("attribute_type", "display_name") + .distinct() + ) + + serialized_attributes = Sage300FieldSerializer(attributes, many=True).data + + # Adding "Job" by default since it can be supported even if it doesn't exist + attributes_list = list(serialized_attributes) + attributes_list.append({"attribute_type": "JOB", "display_name": "Job"}) + + return attributes_list diff --git a/apps/sage300/urls.py b/apps/sage300/urls.py index 919e6227..d5d8a954 100644 --- a/apps/sage300/urls.py +++ b/apps/sage300/urls.py @@ -1,9 +1,13 @@ - from django.urls import path -from apps.sage300.views import ImportSage300AttributesView +from apps.sage300.views import ImportSage300AttributesView, Sage300FieldsView urlpatterns = [ - path('import_attributes/', ImportSage300AttributesView.as_view(), name='import-sage300-attributes'), + path( + "import_attributes/", + ImportSage300AttributesView.as_view(), + name="import-sage300-attributes", + ), + path("fields/", Sage300FieldsView.as_view(), name="sage300-fields"), ] diff --git a/apps/sage300/views.py b/apps/sage300/views.py index 353c6f8c..e9dd506a 100644 --- a/apps/sage300/views.py +++ b/apps/sage300/views.py @@ -2,8 +2,10 @@ from rest_framework import generics +from apps.sage300.serializers import Sage300FieldSerializer from apps.sage300.serializers import ImportSage300AttributesSerializer + logger = logging.getLogger(__name__) logger.level = logging.INFO @@ -12,5 +14,14 @@ class ImportSage300AttributesView(generics.CreateAPIView): """ Import Sage300 Attributes View """ - serializer_class = ImportSage300AttributesSerializer + + +class Sage300FieldsView(generics.ListAPIView): + """ + Sage300 Expense Fields View + """ + serializer_class = Sage300FieldSerializer + + def get_queryset(self): + return Sage300FieldSerializer().format_sage300_fields(self.kwargs["workspace_id"]) diff --git a/sage_desktop_api/settings.py b/sage_desktop_api/settings.py index e5c96f6e..1e12e8a1 100644 --- a/sage_desktop_api/settings.py +++ b/sage_desktop_api/settings.py @@ -176,8 +176,6 @@ } } - - CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.db.DatabaseCache', diff --git a/sage_desktop_api/tests/settings.py b/sage_desktop_api/tests/settings.py index d24f0082..2f274aa3 100644 --- a/sage_desktop_api/tests/settings.py +++ b/sage_desktop_api/tests/settings.py @@ -179,11 +179,9 @@ FYLE_APP_URL = os.environ.get('APP_URL') FYLE_EXPENSE_URL = os.environ.get('FYLE_APP_URL') - SD_API_KEY = os.environ.get('SD_API_KEY') SD_API_SECRET = os.environ.get('SD_API_SECRET') - # Internationalization # https://docs.djangoproject.com/en/3.1/topics/i18n/ diff --git a/tests/test_sage300/test_views.py b/tests/test_sage300/test_views.py index 9927de67..d97208dc 100644 --- a/tests/test_sage300/test_views.py +++ b/tests/test_sage300/test_views.py @@ -8,18 +8,11 @@ def test_sync_dimensions(api_client, test_connection, mocker, create_temp_worksp workspace_id = 1 access_token = test_connection.access_token - url = reverse( - 'import-sage300-attributes', kwargs={ - 'workspace_id': workspace_id - } - ) + url = reverse('import-sage300-attributes', kwargs={'workspace_id': workspace_id}) api_client.credentials(HTTP_AUTHORIZATION='Bearer {}'.format(access_token)) - mocker.patch( - 'apps.sage300.helpers.sync_dimensions', - return_value=None - ) + mocker.patch('apps.sage300.helpers.sync_dimensions', return_value=None) response = api_client.post(url) assert response.status_code == 201 @@ -32,3 +25,17 @@ def test_sync_dimensions(api_client, test_connection, mocker, create_temp_worksp response = json.loads(response.content) assert response['message'] == 'Sage300 credentials not found / invalid in workspace' + + +def test_sage300_fields(api_client, test_connection): + workspace_id = 1 + + access_token = test_connection.access_token + url = reverse('sage300-fields', kwargs={'workspace_id': workspace_id}) + + api_client.credentials(HTTP_AUTHORIZATION='Bearer {}'.format(access_token)) + + response = api_client.get(url) + assert response.status_code == 200 + + assert response.data['results'] == [{'attribute_type': 'JOB', 'display_name': 'Job'}]