Skip to content

Commit

Permalink
Added Error Model, API and test (#441)
Browse files Browse the repository at this point in the history
* onboarding state implementation

* tests migrations

* added onboarding state

* changed comment

* added subsidiary state to onboarding state

* changed script to add subsidiary state and fixed some bug

* bug fix

* state change on connection and subsidiary change

* map employees v2 api

* map_employees typos

* bug fix

* export setting changes

* export settings V2 api

* added test for export settings api

* resolved comments

* import settings v2 api

* test added for import settings v2 api

* advanced settings v2 api

* advanced settings v2 api with test case

* First schedule should be triggered after interval hours

* Handle Admin GET in a safer way

* Making reimbursbale expense object nullable and checking edge cases for the same

* comment resolved

* resolving comments

* all comment resolved

* added code in test for the changes

* added test code for the changes

* Error model, API and test

* corrected test

---------

Co-authored-by: Ashutosh619-sudo <[email protected]>
Co-authored-by: Nilesh Pant <[email protected]>
Co-authored-by: Nilesh Pant <[email protected]>
  • Loading branch information
4 people authored Nov 14, 2023
1 parent 64d953d commit dd5eab7
Show file tree
Hide file tree
Showing 14 changed files with 259 additions and 1 deletion.
1 change: 1 addition & 0 deletions apps/mappings/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -477,6 +477,7 @@ def auto_create_category_mappings(workspace_id):
configuration: Configuration = Configuration.objects.get(workspace_id=workspace_id)

reimbursable_expenses_object = configuration.reimbursable_expenses_object
employee_field_mapping = configuration.employee_field_mapping
corporate_credit_card_expenses_object = configuration.corporate_credit_card_expenses_object

if configuration.reimbursable_expenses_object == 'EXPENSE_REPORT' or configuration.corporate_credit_card_expenses_object == 'EXPENSE_REPORT':
Expand Down
35 changes: 35 additions & 0 deletions apps/tasks/migrations/0009_error.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Generated by Django 3.1.14 on 2023-11-06 12:20

from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

dependencies = [
('fyle_accounting_mappings', '0023_auto_20231010_1139'),
('fyle', '0026_auto_20231025_0913'),
('workspaces', '0036_auto_20231027_0709'),
('tasks', '0008_auto_20220301_1300'),
]

operations = [
migrations.CreateModel(
name='Error',
fields=[
('id', models.AutoField(primary_key=True, serialize=False)),
('type', models.CharField(choices=[('EMPLOYEE_MAPPING', 'EMPLOYEE_MAPPING'), ('CATEGORY_MAPPING', 'CATEGORY_MAPPING'), ('TAX_MAPPING', 'TAX_MAPPING'), ('NETSUITE_ERROR', 'NETSUITE_ERROR')], help_text='Error type', max_length=50)),
('is_resolved', models.BooleanField(default=False, help_text='Is resolved')),
('error_title', models.CharField(help_text='Error title', max_length=255)),
('error_detail', models.TextField(help_text='Error detail')),
('created_at', models.DateTimeField(auto_now_add=True, help_text='Created at datetime')),
('updated_at', models.DateTimeField(auto_now=True, help_text='Updated at datetime')),
('expense_attribute', models.OneToOneField(help_text='Reference to Expense Attribute', null=True, on_delete=django.db.models.deletion.PROTECT, to='fyle_accounting_mappings.expenseattribute')),
('expense_group', models.ForeignKey(help_text='Reference to Expense group', null=True, on_delete=django.db.models.deletion.PROTECT, to='fyle.expensegroup')),
('workspace', models.ForeignKey(help_text='Reference to Workspace model', on_delete=django.db.models.deletion.PROTECT, to='workspaces.workspace')),
],
options={
'db_table': 'errors',
},
),
]
28 changes: 28 additions & 0 deletions apps/tasks/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
from apps.workspaces.models import Workspace
from apps.fyle.models import ExpenseGroup

from fyle_accounting_mappings.models import ExpenseAttribute


def get_default():
return {
Expand All @@ -30,6 +32,7 @@ def get_default():
('ENQUEUED', 'ENQUEUED')
)

ERROR_TYPE_CHOICES = (('EMPLOYEE_MAPPING', 'EMPLOYEE_MAPPING'), ('CATEGORY_MAPPING', 'CATEGORY_MAPPING'), ('TAX_MAPPING', 'TAX_MAPPING'), ('NETSUITE_ERROR', 'NETSUITE_ERROR'))

class TaskLog(models.Model):
"""
Expand Down Expand Up @@ -57,3 +60,28 @@ class TaskLog(models.Model):

class Meta:
db_table = 'task_logs'


class Error(models.Model):
"""
Table to store errors
"""
id = models.AutoField(primary_key=True)
workspace = models.ForeignKey(Workspace, on_delete=models.PROTECT, help_text='Reference to Workspace model')
type = models.CharField(max_length=50, choices=ERROR_TYPE_CHOICES, help_text='Error type')
expense_group = models.ForeignKey(
ExpenseGroup, 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 = models.BooleanField(default=False, help_text='Is resolved')
error_title = models.CharField(max_length=255, help_text='Error title')
error_detail = models.TextField(help_text='Error detail')
created_at = models.DateTimeField(auto_now_add=True, help_text='Created at datetime')
updated_at = models.DateTimeField(auto_now=True, help_text='Updated at datetime')

class Meta:
db_table = 'errors'
Empty file.
41 changes: 41 additions & 0 deletions apps/workspaces/apis/errors/serializers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
from fyle_accounting_mappings.models import ExpenseAttribute
from rest_framework import serializers

from apps.fyle.models import ExpenseGroup
from apps.fyle.serializers import ExpenseSerializer
from apps.tasks.models import Error


class ExpenseAttributeSerializer(serializers.ModelSerializer):
"""
Serializer for Expense Attribute
"""

class Meta:
model = ExpenseAttribute
fields = '__all__'


class ExpenseGroupSerializer(serializers.ModelSerializer):
"""
Serializer for Expense Group
"""

expenses = ExpenseSerializer(many=True)

class Meta:
model = ExpenseGroup
fields = '__all__'


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

expense_attribute = ExpenseAttributeSerializer()
expense_group = ExpenseGroupSerializer()

class Meta:
model = Error
fields = '__all__'
16 changes: 16 additions & 0 deletions apps/workspaces/apis/errors/views.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
from django_filters.rest_framework import DjangoFilterBackend
from fyle_netsuite_api.utils import LookupFieldMixin
from rest_framework import generics
from apps.tasks.models import Error
from apps.workspaces.apis.errors.serializers import ErrorSerializer


class ErrorsView(LookupFieldMixin, generics.ListAPIView):

queryset = Error.objects.all()
serializer_class = ErrorSerializer
pagination_class = None
filter_backends = (DjangoFilterBackend,)
filterset_fields = {'type':{'exact'}, 'is_resolved':{'exact'}}


2 changes: 2 additions & 0 deletions apps/workspaces/apis/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from apps.workspaces.apis.map_employees.views import MapEmployeesView
from apps.workspaces.apis.export_settings.views import ExportSettingsView
from apps.workspaces.apis.import_settings.views import ImportSettingsView
from apps.workspaces.apis.errors.views import ErrorsView



Expand All @@ -12,4 +13,5 @@
path('<int:workspace_id>/export_settings/', ExportSettingsView.as_view(), name='export-settings'),
path('<int:workspace_id>/import_settings/', ImportSettingsView.as_view(), name='import-settings'),
path('<int:workspace_id>/advanced_settings/', AdvancedSettingsView.as_view(), name='advanced-settings'),
path('<int:workspace_id>/errors/', ErrorsView.as_view(), name='errors')
]
3 changes: 2 additions & 1 deletion fyle_netsuite_api/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,8 @@
'apps.tasks',
'apps.mappings',
'apps.netsuite',
'django_q'
'django_q',
'django_filters',
]

MIDDLEWARE = [
Expand Down
10 changes: 10 additions & 0 deletions fyle_netsuite_api/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,13 @@ def assert_valid(condition: bool, message: str) -> Response or None:
raise ValidationError(detail={
'message': message
})

class LookupFieldMixin:
lookup_field = "workspace_id"

def filter_queryset(self, queryset):
if self.lookup_field in self.kwargs:
lookup_value = self.kwargs[self.lookup_field]
filter_kwargs = {self.lookup_field: lookup_value}
queryset = queryset.filter(**filter_kwargs)
return super().filter_queryset(queryset)
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -62,3 +62,4 @@ urllib3==1.26.11
wcwidth==0.1.8
wrapt==1.12.1
zeep==4.1.0
django-filter
1 change: 1 addition & 0 deletions tests/test_mappings/test_tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,7 @@ def test_upload_categories_to_fyle(mocker, db):

assert count_of_accounts == 137

configuration.employee_field_mapping = 'EMPLOYEE'
configuration.reimbursable_expenses_object = 'BILL'
configuration.corporate_credit_card_expenses_object = 'BILL'
configuration.save()
Expand Down
Empty file.
62 changes: 62 additions & 0 deletions tests/test_workspaces/test_apis/test_errors/fixtures.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
data = {
'errors_response': [
{
"id": 1,
"expense_attribute": {
"id": 13044,
"attribute_type": "CATEGORY",
"display_name": "Category",
"value": "Internet",
"source_id": "142069",
"auto_mapped": False,
"auto_created": False,
"active": None,
"detail": None,
"created_at": "2022-10-07T06:02:54.076426Z",
"updated_at": "2022-10-07T06:02:54.076429Z",
"workspace": 8
},
"expense_group": None,
"type": "CATEGORY_MAPPING",
"is_resolved": False,
"error_title": "Internet",
"error_detail": "Category mapping is missing",
"created_at": "2022-10-07T06:07:32.823778Z",
"updated_at": "2022-10-07T06:31:53.211657Z",
"workspace": 1
},
{
"id": 2,
"expense_attribute": {
"id": 13020,
"attribute_type": "EMPLOYEE",
"display_name": "Employee",
"value": "[email protected]",
"source_id": "ouQmTCQE26dc",
"auto_mapped": False,
"auto_created": False,
"active": None,
"detail": {
"user_id": "usqywo0f3nBY",
"location": None,
"full_name": "Joanna",
"department": None,
"department_id": None,
"employee_code": None,
"department_code": None
},
"created_at": "2022-10-07T06:02:53.810543Z",
"updated_at": "2022-10-07T06:02:53.810548Z",
"workspace": 8
},
"expense_group": None,
"type": "EMPLOYEE_MAPPING",
"is_resolved": False,
"error_title": "[email protected]",
"error_detail": "Employee mapping is missing",
"created_at": "2022-10-07T06:31:48.338064Z",
"updated_at": "2022-10-07T06:31:48.338082Z",
"workspace": 1
}
]
}
60 changes: 60 additions & 0 deletions tests/test_workspaces/test_apis/test_errors/test_views.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import json
from datetime import datetime,timezone
from apps.tasks.models import Error
from .fixtures import data
from tests.helper import dict_compare_keys
import pytest
from django.urls import reverse

@pytest.mark.django_db(databases=['default'])
def test_errors(api_client, access_token):

Error.objects.create(
workspace_id=1,
type = 'EMPLOYEE_MAPPING',
expense_attribute_id=8,
is_resolved = False,
error_title = '[email protected]',
error_detail = 'Employee mapping is missing',
created_at = datetime.now(tz=timezone.utc),
updated_at = datetime.now(tz=timezone.utc)
)

url = reverse(
'errors', kwargs={
'workspace_id': 1
}
)
api_client.credentials(HTTP_AUTHORIZATION='Bearer {}'.format(access_token))
response = api_client.get(
url,
format='json'
)

assert response.status_code == 200

response = json.loads(response.content)
assert dict_compare_keys(response, data['errors_response']) == [], 'errors api returns a diff in the keys'

url = '/api/v2/workspaces/1/errors/?is_resolved=False&type=CATEGORY_MAPPING'
response = api_client.get(
url,
format='json'
)

assert response.status_code == 200

Error.objects.filter(
workspace_id=1,
type='EMPLOYEE_MAPPING',
error_detail = 'Employee mapping is missing',
is_resolved=False
).update(is_resolved=True)

url = '/api/v2/workspaces/1/errors/?is_resolved=true&type=EMPLOYEE_MAPPING'
response = api_client.get(
url,
format='json'
)

assert response.status_code == 200

0 comments on commit dd5eab7

Please sign in to comment.