Skip to content

Commit

Permalink
Merge pull request #44 from fylein/errors_apis
Browse files Browse the repository at this point in the history
Errors APIs
  • Loading branch information
ruuushhh authored Oct 26, 2023
2 parents cf54a75 + 2190812 commit c808437
Show file tree
Hide file tree
Showing 11 changed files with 225 additions and 8 deletions.
30 changes: 30 additions & 0 deletions apps/accounting_exports/models.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
from django.db import models
from django.contrib.postgres.fields import ArrayField

from fyle_accounting_mappings.models import ExpenseAttribute

from sage_desktop_api.models.fields import (
StringNotNullField,
StringNullField,
CustomJsonField,
CustomDateTimeField,
BooleanFalseField,
TextNotNullField,
StringOptionsField
)
from apps.workspaces.models import BaseForeignWorkspaceModel
Expand All @@ -18,6 +23,9 @@
)


ERROR_TYPE_CHOICES = (('EMPLOYEE_MAPPING', 'EMPLOYEE_MAPPING'), ('CATEGORY_MAPPING', 'CATEGORY_MAPPING'), ('SAGE300_ERROR', 'SAGE300_ERROR'))


class AccountingExport(BaseForeignWorkspaceModel):
"""
Table to store accounting exports
Expand All @@ -36,3 +44,25 @@ class AccountingExport(BaseForeignWorkspaceModel):

class Meta:
db_table = 'accounting_exports'


class Error(BaseForeignWorkspaceModel):
"""
Table to store errors
"""
id = models.AutoField(primary_key=True)
type = StringOptionsField(max_length=50, choices=ERROR_TYPE_CHOICES, help_text='Error type')
accounting_export = models.ForeignKey(
AccountingExport, on_delete=models.PROTECT,
null=True, help_text='Reference to Expense group'
)
expense_attribute = models.OneToOneField(
ExpenseAttribute, on_delete=models.PROTECT,
null=True, help_text='Reference to Expense Attribute'
)
is_resolved = BooleanFalseField(help_text='Is resolved')
error_title = StringNotNullField(help_text='Error title')
error_detail = TextNotNullField(help_text='Error detail')

class Meta:
db_table = 'errors'
12 changes: 11 additions & 1 deletion apps/accounting_exports/serializers.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from rest_framework import serializers

from .models import AccountingExport
from .models import AccountingExport, Error


class AccountingExportSerializer(serializers.ModelSerializer):
Expand All @@ -11,3 +11,13 @@ class AccountingExportSerializer(serializers.ModelSerializer):
class Meta:
model = AccountingExport
fields = '__all__'


class ErrorSerializer(serializers.ModelSerializer):
"""
Serializer for the Errors
"""

class Meta:
model = Error
fields = '__all__'
5 changes: 3 additions & 2 deletions apps/accounting_exports/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,10 @@
"""
from django.urls import path

from .views import AccountingExportView
from .views import AccountingExportView, ErrorsView


urlpatterns = [
path('', AccountingExportView.as_view(), name='accounting-exports')
path('', AccountingExportView.as_view(), name='accounting-exports'),
path('errors/', ErrorsView.as_view(), name='errors'),
]
11 changes: 9 additions & 2 deletions apps/accounting_exports/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework import generics
from sage_desktop_api.utils import LookupFieldMixin
from apps.accounting_exports.serializers import AccountingExportSerializer
from apps.accounting_exports.models import AccountingExport
from apps.accounting_exports.serializers import AccountingExportSerializer, ErrorSerializer
from apps.accounting_exports.models import AccountingExport, Error

logger = logging.getLogger(__name__)
logger.level = logging.INFO
Expand All @@ -18,3 +18,10 @@ class AccountingExportView(LookupFieldMixin, generics.ListAPIView):
queryset = AccountingExport.objects.all().order_by("-updated_at")
filter_backends = (DjangoFilterBackend,)
filterset_fields = {"type": {"in"}, "updated_at": {"lte", "gte"}, "id": {"in"}, "status": {"in"}}


class ErrorsView(LookupFieldMixin, generics.ListAPIView):
serializer_class = ErrorSerializer
queryset = Error.objects.all()
filter_backends = (DjangoFilterBackend,)
filterset_fields = {"type": {"exact"}, "is_resolved": {"exact"}}
40 changes: 40 additions & 0 deletions apps/fyle/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,15 @@
Fyle Serializers
"""
import logging
from django.db.models import Q
from rest_framework import serializers
from rest_framework.response import Response
from rest_framework.exceptions import APIException
from rest_framework.views import status
from fyle_integrations_platform_connector import PlatformConnector

from fyle_accounting_mappings.models import ExpenseAttribute

from datetime import datetime, timezone
from apps.workspaces.models import Workspace, FyleCredential
from apps.fyle.models import ExpenseFilter
Expand Down Expand Up @@ -91,3 +95,39 @@ def get_expense_fields(self, validated_data):
expense_fields = get_expense_fields(workspace_id=workspace_id)

return expense_fields


class FyleFieldsSerializer(serializers.Serializer):
"""
Fyle Fields Serializer
"""

attribute_type = serializers.CharField()
display_name = serializers.CharField()
is_dependant = serializers.BooleanField()

def format_fyle_fields(self, workspace_id):
"""
Get Fyle Fields
"""

attribute_types = ['EMPLOYEE', 'CATEGORY', 'PROJECT', 'COST_CENTER', 'TAX_GROUP', 'CORPORATE_CARD', 'MERCHANT']

attributes = ExpenseAttribute.objects.filter(
~Q(attribute_type__in=attribute_types),
workspace_id=workspace_id
).values('attribute_type', 'display_name', 'detail__is_dependent').distinct()

attributes_list = [
{'attribute_type': 'COST_CENTER', 'display_name': 'Cost Center', 'is_dependant': False},
{'attribute_type': 'PROJECT', 'display_name': 'Project', 'is_dependant': False}
]

for attr in attributes:
attributes_list.append({
'attribute_type': attr['attribute_type'],
'display_name': attr['display_name'],
'is_dependant': attr['detail__is_dependent']
})

return attributes_list
3 changes: 2 additions & 1 deletion apps/fyle/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,13 @@
"""

from django.urls import path
from apps.fyle.views import ImportFyleAttributesView, ExpenseFilterView, ExpenseFilterDeleteView, CustomFieldView
from apps.fyle.views import ImportFyleAttributesView, ExpenseFilterView, ExpenseFilterDeleteView, CustomFieldView, FyleFieldsView


urlpatterns = [
path('import_attributes/', ImportFyleAttributesView.as_view(), name='import-fyle-attributes'),
path('expense_filters/<int:pk>/', ExpenseFilterDeleteView.as_view(), name='expense-filters'),
path('expense_filters/', ExpenseFilterView.as_view(), name='expense-filters'),
path('expense_fields/', CustomFieldView.as_view(), name='fyle-expense-fields'),
path('fields/', FyleFieldsView.as_view(), name='fyle-fields'),
]
13 changes: 12 additions & 1 deletion apps/fyle/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from rest_framework import generics
from sage_desktop_api.utils import LookupFieldMixin
from apps.workspaces.models import Workspace
from apps.fyle.serializers import ImportFyleAttributesSerializer, ExpenseFilterSerializer, ExpenseFieldSerializer
from apps.fyle.serializers import ImportFyleAttributesSerializer, ExpenseFilterSerializer, ExpenseFieldSerializer, FyleFieldsSerializer
from apps.fyle.models import ExpenseFilter

logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -42,3 +42,14 @@ class CustomFieldView(generics.ListAPIView):

serializer_class = ExpenseFieldSerializer
queryset = Workspace.objects.all()


class FyleFieldsView(generics.ListAPIView):
"""
Fyle Fields view
"""

serializer_class = FyleFieldsSerializer

def get_queryset(self):
return FyleFieldsSerializer().format_fyle_fields(self.kwargs["workspace_id"])
35 changes: 34 additions & 1 deletion tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
Sage300Credential
)
from apps.fyle.models import ExpenseFilter
from apps.accounting_exports.models import AccountingExport
from apps.accounting_exports.models import AccountingExport, Error
from sage_desktop_api.tests import settings

from .test_fyle.fixtures import fixtures as fyle_fixtures
Expand Down Expand Up @@ -221,3 +221,36 @@ def add_accounting_export_expenses():
'status': 'IN_PROGRESS'
}
)


@pytest.fixture()
@pytest.mark.django_db(databases=['default'])
def add_errors():
"""
Pytest fixture to add errors to a workspace
"""
workspace_ids = [
1, 2, 3
]
for workspace_id in workspace_ids:
Error.objects.create(
type='EMPLOYEE_MAPPING',
is_resolved=False,
error_title='Employee Mapping Error',
error_detail='Employee Mapping Error',
workspace_id=workspace_id
)
Error.objects.create(
type='CATEGORY_MAPPING',
is_resolved=False,
error_title='Category Mapping Error',
error_detail='Category Mapping Error',
workspace_id=workspace_id
)
Error.objects.create(
type='SAGE300_ERROR',
is_resolved=False,
error_title='Sage Error',
error_detail='Sage Error',
workspace_id=workspace_id
)
14 changes: 14 additions & 0 deletions tests/test_accounting_exports/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,17 @@ def test_get_accounting_exports(api_client, test_connection, create_temp_workspa
response = json.loads(response.content)

assert dict_compare_keys(response, data['accounting_export_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
"""
url = reverse('errors', 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['errors_response']) == [], 'expense group api return diffs in keys'
55 changes: 55 additions & 0 deletions tests/test_fyle/fixtures.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,49 @@
}
]
},
"errors_response": {
"count":3,
"next":"None",
"previous":"None",
"results":[
{
"id":1,
"created_at":"2023-10-26T03:47:16.864421Z",
"updated_at":"2023-10-26T03:47:16.864428Z",
"type":"EMPLOYEE_MAPPING",
"is_resolved": "false",
"error_title":"Employee Mapping Error",
"error_detail":"Employee Mapping Error",
"workspace":1,
"accounting_export":"None",
"expense_attribute":"None"
},
{
"id":2,
"created_at":"2023-10-26T03:47:16.865103Z",
"updated_at":"2023-10-26T03:47:16.865108Z",
"type":"CATEGORY_MAPPING",
"is_resolved": "false",
"error_title":"Category Mapping Error",
"error_detail":"Category Mapping Error",
"workspace":1,
"accounting_export":"None",
"expense_attribute":"None"
},
{
"id":3,
"created_at":"2023-10-26T03:47:16.865303Z",
"updated_at":"2023-10-26T03:47:16.865307Z",
"type":"SAGE300_ERROR",
"is_resolved": "false",
"error_title":"Sage Error",
"error_detail":"Sage Error",
"workspace":1,
"accounting_export":"None",
"expense_attribute":"None"
}
]
},
"fyle_expense_custom_fields": [
{"field_name": "employee_email", "type": "SELECT", "is_custom": False},
{"field_name": "claim_number", "type": "TEXT", "is_custom": False},
Expand Down Expand Up @@ -134,4 +177,16 @@
]
}
],
"fyle_fields_response": [
{
'attribute_type': 'COST_CENTER',
'display_name': 'Cost Center',
'is_dependant': False
},
{
'attribute_type': 'PROJECT',
'display_name': 'Project',
'is_dependant': False
}
]
}
15 changes: 15 additions & 0 deletions tests/test_fyle/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,3 +85,18 @@ def test_fyle_expense_fields(api_client, test_connection, create_temp_workspace,
assert (
dict_compare_keys(response['results'], data['fyle_expense_custom_fields']) == []
), 'expense group api return diffs in keys'


def test_fyle_fields(api_client, test_connection, create_temp_workspace, add_fyle_credentials):

access_token = test_connection.access_token

url = reverse('fyle-fields', kwargs={'workspace_id': 1})

api_client.credentials(HTTP_AUTHORIZATION='Bearer {}'.format(access_token))

response = api_client.get(url)
assert response.status_code == 200

response = json.loads(response.content)
assert response['results'] == data['fyle_fields_response']

0 comments on commit c808437

Please sign in to comment.