Skip to content
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

Fyle fields api #22

Merged
merged 5 commits into from
Nov 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions apps/fyle/constants.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
DEFAULT_FYLE_CONDITIONS = [
{
'field_name': 'employee_email',
'type': 'SELECT',
'is_custom': False
},
{
'field_name': 'claim_number',
'type': 'TEXT',
'is_custom': False
},
{
'field_name': 'report_title',
'type': 'TEXT',
'is_custom': False
},
{
'field_name': 'spent_at',
'type': 'DATE',
'is_custom': False
}
]
29 changes: 29 additions & 0 deletions apps/fyle/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@
import requests
from django.conf import settings

from fyle_integrations_platform_connector import PlatformConnector

from apps.workspaces.models import FyleCredential
from apps.fyle.constants import DEFAULT_FYLE_CONDITIONS


def post_request(url, body, refresh_token=None):
"""
Expand Down Expand Up @@ -50,3 +55,27 @@ def get_cluster_domain(refresh_token: str) -> str:
cluster_api_url = '{0}/oauth/cluster/'.format(settings.FYLE_BASE_URL)

return post_request(cluster_api_url, {}, refresh_token)['cluster_domain']


def get_expense_fields(workspace_id: int):
"""
Get expense custom fields from fyle
:param workspace_id: (int)
:return: list of custom expense fields
"""

fyle_credentails = FyleCredential.objects.get(workspace_id=workspace_id)
platform = PlatformConnector(fyle_credentails)
custom_fields = platform.expense_custom_fields.list_all()

response = []
response.extend(DEFAULT_FYLE_CONDITIONS)
for custom_field in custom_fields:
if custom_field['type'] in ('SELECT', 'NUMBER', 'TEXT', 'BOOLEAN'):
response.append({
'field_name': custom_field['field_name'],
'type': custom_field['type'],
'is_custom': custom_field['is_custom']
})

return response
57 changes: 57 additions & 0 deletions apps/fyle/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,18 @@
"""
import logging
from datetime import datetime, timezone
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 apps.fyle.models import ExpenseFilter
from apps.workspaces.models import Workspace, FyleCredential
from apps.fyle.helpers import get_expense_fields

logger = logging.getLogger(__name__)
logger.level = logging.INFO
Expand Down Expand Up @@ -66,3 +70,56 @@ def create(self, validated_data):
except Exception as exception:
logger.error('Something unexpected happened workspace_id: %s %s', workspace_id, exception)
raise APIException("Internal Server Error", code='server_error')


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


class ExpenseFieldSerializer(serializers.Serializer):
"""
Workspace Admin Serializer
"""
expense_fields = serializers.SerializerMethodField()

def get_expense_fields(self, validated_data):
"""
Get Expense Fields
"""

workspace_id = self.context['request'].parser_context.get('kwargs').get('workspace_id')
expense_fields = get_expense_fields(workspace_id=workspace_id)

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

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


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

logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -31,3 +32,23 @@ class ImportFyleAttributesView(generics.CreateAPIView):
Import Fyle Attributes View
"""
serializer_class = ImportFyleAttributesSerializer


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

serializer_class = FyleFieldsSerializer

def get_queryset(self):
return FyleFieldsSerializer().format_fyle_fields(self.kwargs["workspace_id"])


class CustomFieldView(generics.ListAPIView):
"""
Custom Field view
"""

serializer_class = ExpenseFieldSerializer
queryset = Workspace.objects.all()
48 changes: 48 additions & 0 deletions tests/test_fyle/fixtures.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,4 +153,52 @@
},
],
},
"fyle_fields_response": [
{
'attribute_type': 'COST_CENTER',
'display_name': 'Cost Center',
'is_dependant': False
},
{
'attribute_type': 'PROJECT',
'display_name': 'Project',
'is_dependant': False
}
],
"fyle_expense_custom_fields": [
{"field_name": "employee_email", "type": "SELECT", "is_custom": False},
{"field_name": "claim_number", "type": "TEXT", "is_custom": False},
{"field_name": "report_title", "type": "TEXT", "is_custom": False},
{"field_name": "spent_at", "type": "DATE", "is_custom": False},
{"field_name": "Class", "type": "SELECT", "is_custom": True},
{"field_name": "Fyle Categories", "type": "SELECT", "is_custom": True},
{"field_name": "Operating System", "type": "SELECT", "is_custom": True},
{"field_name": "User Dimension", "type": "SELECT", "is_custom": True},
{"field_name": "Asdasdas", "type": "SELECT", "is_custom": True},
{"field_name": "Nilesh Custom Field", "type": "SELECT", "is_custom": True},
],
"get_all_custom_fields": [
{
"data": [
{
"category_ids": [142151],
"code": None,
"column_name": "text_column6",
"created_at": "2021-10-22T07:50:04.613487+00:00",
"default_value": None,
"field_name": "Class",
"id": 197380,
"is_custom": True,
"is_enabled": True,
"is_mandatory": False,
"options": ["Servers", "Home", "Office"],
"org_id": "orGcBCVPijjO",
"placeholder": "Select Class",
"seq": 1,
"type": "SELECT",
"updated_at": "2023-01-01T05:35:26.345303+00:00",
},
]
}
],
}
37 changes: 37 additions & 0 deletions tests/test_fyle/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,3 +63,40 @@ def test_import_fyle_attributes(mocker, api_client, test_connection, create_temp
mock_call.side_effect = Exception()
response = api_client.post(url, payload)
assert response.status_code == 500


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']


def test_fyle_expense_fields(api_client, test_connection, create_temp_workspace, add_fyle_credentials, mocker):
workspace_id = 1

access_token = test_connection.access_token
url = reverse('fyle-expense-fields', kwargs={'workspace_id': workspace_id})

mocker.patch(
'fyle.platform.apis.v1beta.admin.ExpenseFields.list_all',
return_value = data['get_all_custom_fields'],
)

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 (
dict_compare_keys(response['results'], data['fyle_expense_custom_fields']) == []
), 'expense group api return diffs in keys'
Loading