-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Workspace admin apis * Fyle accounting mappings enabled * Accounting exports api (#15) * Accounting exports api * Accounting Exports count API (#16) * Accounting Exports count API * Accounting exports summary APIs (#17) * Accounting exports summary APIs * Errors APIs (#18) * Errors APIs * Expense filter api (#19) * Expense Filter APIs * Expense Filter APIs * Expense Filter APIs * Resolved
- Loading branch information
Showing
22 changed files
with
813 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
from django.db import models | ||
from django.contrib.postgres.fields import ArrayField | ||
|
||
from fyle_accounting_mappings.models import ExpenseAttribute | ||
|
||
from ms_business_central_api.models.fields import ( | ||
StringNotNullField, | ||
StringNullField, | ||
CustomJsonField, | ||
CustomDateTimeField, | ||
StringOptionsField, | ||
IntegerNullField, | ||
BooleanFalseField, | ||
TextNotNullField | ||
) | ||
from apps.workspaces.models import BaseForeignWorkspaceModel, BaseModel | ||
from apps.fyle.models import Expense | ||
|
||
TYPE_CHOICES = ( | ||
('INVOICES', 'INVOICES'), | ||
('DIRECT_COST', 'DIRECT_COST'), | ||
('FETCHING_REIMBURSABLE_EXPENSES', 'FETCHING_REIMBURSABLE_EXPENSES'), | ||
('FETCHING_CREDIT_CARD_EXPENENSES', 'FETCHING_CREDIT_CARD_EXPENENSES') | ||
) | ||
|
||
ERROR_TYPE_CHOICES = (('EMPLOYEE_MAPPING', 'EMPLOYEE_MAPPING'), ('CATEGORY_MAPPING', 'CATEGORY_MAPPING'), ('BUSINESS_CENTRAL_ERROR', 'BUSINESS_CENTRAL_ERROR')) | ||
|
||
EXPORT_MODE_CHOICES = ( | ||
('MANUAL', 'MANUAL'), | ||
('AUTO', 'AUTO') | ||
) | ||
|
||
|
||
class AccountingExport(BaseForeignWorkspaceModel): | ||
""" | ||
Table to store accounting exports | ||
""" | ||
id = models.AutoField(primary_key=True) | ||
type = StringOptionsField(choices=TYPE_CHOICES, help_text='Task type') | ||
fund_source = StringNotNullField(help_text='Expense fund source') | ||
mapping_errors = ArrayField(help_text='Mapping errors', base_field=models.CharField(max_length=255), blank=True, null=True) | ||
expenses = models.ManyToManyField(Expense, help_text="Expenses under this Expense Group") | ||
task_id = StringNullField(help_text='Fyle Jobs task reference') | ||
description = CustomJsonField(help_text='Description') | ||
status = StringNotNullField(help_text='Task Status') | ||
detail = CustomJsonField(help_text='Task Response') | ||
business_central_errors = CustomJsonField(help_text='Business Central Errors') | ||
exported_at = CustomDateTimeField(help_text='time of export') | ||
|
||
class Meta: | ||
db_table = 'accounting_exports' | ||
|
||
|
||
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' | ||
|
||
|
||
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' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
from rest_framework import serializers | ||
|
||
from apps.accounting_exports.models import AccountingExport, AccountingExportSummary, Error | ||
|
||
|
||
class AccountingExportSerializer(serializers.ModelSerializer): | ||
""" | ||
Accounting Export serializer | ||
""" | ||
|
||
class Meta: | ||
model = AccountingExport | ||
fields = '__all__' | ||
|
||
|
||
class AccountingExportSummarySerializer(serializers.ModelSerializer): | ||
""" | ||
Accounting Export Summary serializer | ||
""" | ||
|
||
class Meta: | ||
model = AccountingExportSummary | ||
fields = '__all__' | ||
|
||
|
||
class ErrorSerializer(serializers.ModelSerializer): | ||
""" | ||
Serializer for the Errors | ||
""" | ||
|
||
class Meta: | ||
model = Error | ||
fields = '__all__' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
"""ms_business_central_api URL Configuration | ||
The `urlpatterns` list routes URLs to views. For more information please see: | ||
https://docs.djangoproject.com/en/3.0/topics/http/urls/ | ||
Examples: | ||
Function views | ||
1. Add an import: from my_app import views | ||
2. Add a URL to urlpatterns: path('', views.home, name='home') | ||
Class-based views | ||
1. Add an import: from other_app.views import Home | ||
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') | ||
Including another URLconf | ||
1. Import the include() function: from django.urls import include, path | ||
2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) | ||
""" | ||
from django.urls import path | ||
|
||
from apps.accounting_exports.views import AccountingExportView, AccountingExportCountView, AccountingExportSummaryView, ErrorsView | ||
|
||
|
||
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'), | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
import logging | ||
|
||
from django_filters.rest_framework import DjangoFilterBackend | ||
from rest_framework import generics | ||
from rest_framework.response import Response | ||
|
||
from ms_business_central_api.utils import LookupFieldMixin | ||
from apps.accounting_exports.serializers import AccountingExportSerializer, AccountingExportSummarySerializer, ErrorSerializer | ||
from apps.accounting_exports.models import AccountingExport, AccountingExportSummary, Error | ||
|
||
|
||
logger = logging.getLogger(__name__) | ||
logger.level = logging.INFO | ||
|
||
|
||
class AccountingExportView(LookupFieldMixin, generics.ListAPIView): | ||
""" | ||
Retrieve or Create Accounting Export | ||
""" | ||
serializer_class = AccountingExportSerializer | ||
queryset = AccountingExport.objects.all().order_by("-updated_at") | ||
filter_backends = (DjangoFilterBackend,) | ||
filterset_fields = {"type": {"in"}, "updated_at": {"lte", "gte"}, "id": {"in"}, "status": {"in"}} | ||
|
||
|
||
class AccountingExportCountView(generics.RetrieveAPIView): | ||
""" | ||
Retrieve Accounting Export Count | ||
""" | ||
|
||
def get(self, request, *args, **kwargs): | ||
params = {"workspace_id": self.kwargs['workspace_id']} | ||
|
||
if request.query_params.get("status__in"): | ||
params["status__in"] = request.query_params.get("status__in").split(",") | ||
|
||
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() | ||
filter_backends = (DjangoFilterBackend,) | ||
filterset_fields = {"type": {"exact"}, "is_resolved": {"exact"}} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
from django.db import models | ||
from django.contrib.postgres.fields import ArrayField | ||
from ms_business_central_api.models.fields import ( | ||
StringNotNullField, | ||
StringNullField, | ||
BooleanFalseField, | ||
CustomJsonField, | ||
CustomDateTimeField, | ||
CustomEmailField, | ||
FloatNullField, | ||
StringOptionsField, | ||
IntegerOptionsField, | ||
) | ||
from apps.workspaces.models import BaseModel, BaseForeignWorkspaceModel | ||
|
||
|
||
EXPENSE_FILTER_RANK = ( | ||
(1, 1), | ||
(2, 2) | ||
) | ||
|
||
EXPENSE_FILTER_JOIN_BY = ( | ||
('AND', 'AND'), | ||
('OR', 'OR') | ||
) | ||
|
||
EXPENSE_FILTER_CUSTOM_FIELD_TYPE = ( | ||
('SELECT', 'SELECT'), | ||
('NUMBER', 'NUMBER'), | ||
('TEXT', 'TEXT') | ||
) | ||
|
||
EXPENSE_FILTER_OPERATOR = ( | ||
('isnull', 'isnull'), | ||
('in', 'in'), | ||
('iexact', 'iexact'), | ||
('icontains', 'icontains'), | ||
('lt', 'lt'), | ||
('lte', 'lte'), | ||
('not_in', 'not_in') | ||
) | ||
|
||
|
||
class Expense(BaseModel): | ||
""" | ||
Expense | ||
""" | ||
id = models.AutoField(primary_key=True) | ||
employee_email = CustomEmailField(help_text='Email id of the Fyle employee') | ||
employee_name = StringNullField(help_text='Name of the Fyle employee') | ||
category = StringNullField(help_text='Fyle Expense Category') | ||
sub_category = StringNullField(help_text='Fyle Expense Sub-Category') | ||
project = StringNullField(help_text='Project') | ||
expense_id = StringNotNullField(unique=True, help_text='Expense ID') | ||
org_id = StringNullField(help_text='Organization ID') | ||
expense_number = StringNotNullField(help_text='Expense Number') | ||
claim_number = StringNotNullField(help_text='Claim Number') | ||
amount = models.FloatField(help_text='Home Amount') | ||
currency = StringNotNullField(max_length=5, help_text='Home Currency') | ||
foreign_amount = models.FloatField(null=True, help_text='Foreign Amount') | ||
foreign_currency = StringNotNullField(max_length=5, help_text='Foreign Currency') | ||
settlement_id = StringNullField(help_text='Settlement ID') | ||
reimbursable = BooleanFalseField(help_text='Expense reimbursable or not') | ||
state = StringNotNullField(help_text='Expense state') | ||
vendor = StringNotNullField(help_text='Vendor') | ||
cost_center = StringNullField(help_text='Fyle Expense Cost Center') | ||
corporate_card_id = StringNullField(help_text='Corporate Card ID') | ||
purpose = models.TextField(null=True, blank=True, help_text='Purpose') | ||
report_id = StringNotNullField(help_text='Report ID') | ||
billable = BooleanFalseField(help_text='Expense billable or not') | ||
file_ids = ArrayField(base_field=models.CharField(max_length=255), null=True, help_text='File IDs') | ||
spent_at = CustomDateTimeField(help_text='Expense spent at') | ||
approved_at = CustomDateTimeField(help_text='Expense approved at') | ||
posted_at = CustomDateTimeField(help_text='Date when the money is taken from the bank') | ||
expense_created_at = CustomDateTimeField(help_text='Expense created at') | ||
expense_updated_at = CustomDateTimeField(help_text='Expense created at') | ||
fund_source = StringNotNullField(help_text='Expense fund source') | ||
verified_at = CustomDateTimeField(help_text='Report verified at') | ||
custom_properties = CustomJsonField(help_text="Custom Properties") | ||
tax_amount = FloatNullField(help_text='Tax Amount') | ||
tax_group_id = StringNullField(help_text='Tax Group ID') | ||
exported = BooleanFalseField(help_text='Expense reimbursable or not') | ||
previous_export_state = StringNullField(max_length=255, help_text='Previous export state') | ||
accounting_export_summary = CustomJsonField(default=dict, help_text='Accounting Export Summary') | ||
|
||
class Meta: | ||
db_table = 'expenses' | ||
|
||
|
||
class ExpenseFilter(BaseForeignWorkspaceModel): | ||
""" | ||
Reimbursements | ||
""" | ||
id = models.AutoField(primary_key=True) | ||
condition = StringNotNullField(help_text='Condition for the filter') | ||
operator = StringOptionsField(choices=EXPENSE_FILTER_OPERATOR, help_text='Operator for the filter') | ||
values = ArrayField(base_field=models.CharField(max_length=255), null=True, help_text='Values for the operator') | ||
rank = IntegerOptionsField(choices=EXPENSE_FILTER_RANK, help_text='Rank for the filter') | ||
join_by = StringOptionsField(choices=EXPENSE_FILTER_JOIN_BY, max_length=3, help_text='Used to join the filter (AND/OR)') | ||
is_custom = BooleanFalseField(help_text='Custom Field or not') | ||
custom_field_type = StringOptionsField(help_text='Custom field type', choices=EXPENSE_FILTER_CUSTOM_FIELD_TYPE) | ||
|
||
class Meta: | ||
db_table = 'expense_filters' | ||
|
||
|
||
class Reimbursement: | ||
""" | ||
Creating a dummy class to be able to user | ||
fyle_integrations_platform_connector correctly | ||
""" | ||
pass |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
""" | ||
Fyle Serializers | ||
""" | ||
import logging | ||
from rest_framework import serializers | ||
|
||
from apps.fyle.models import ExpenseFilter | ||
|
||
logger = logging.getLogger(__name__) | ||
logger.level = logging.INFO | ||
|
||
|
||
class ExpenseFilterSerializer(serializers.ModelSerializer): | ||
""" | ||
Expense Filter Serializer | ||
""" | ||
|
||
class Meta: | ||
model = ExpenseFilter | ||
fields = '__all__' | ||
read_only_fields = ('id', 'workspace', 'created_at', 'updated_at') |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
"""fyle_ms_business_central URL Configuration | ||
The `urlpatterns` list routes URLs to views. For more information please see: | ||
https://docs.djangoproject.com/en/3.0/topics/http/urls/ | ||
Examples: | ||
Function views | ||
1. Add an import: from my_app import views | ||
2. Add a URL to urlpatterns: path('', views.home, name='home') | ||
Class-based views | ||
1. Add an import: from other_app.views import Home | ||
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') | ||
Including another URLconf | ||
1. Import the include() function: from django.urls import include, path | ||
2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) | ||
""" | ||
|
||
from django.urls import path | ||
from apps.fyle.views import ExpenseFilterView, ExpenseFilterDeleteView | ||
|
||
|
||
urlpatterns = [ | ||
path('expense_filters/<int:pk>/', ExpenseFilterDeleteView.as_view(), name='expense-filters'), | ||
path('expense_filters/', ExpenseFilterView.as_view(), name='expense-filters'), | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
import logging | ||
from rest_framework import generics | ||
from ms_business_central_api.utils import LookupFieldMixin | ||
from apps.fyle.serializers import ExpenseFilterSerializer | ||
from apps.fyle.models import ExpenseFilter | ||
|
||
logger = logging.getLogger(__name__) | ||
logger.level = logging.INFO | ||
|
||
|
||
class ExpenseFilterView(LookupFieldMixin, generics.ListCreateAPIView): | ||
""" | ||
Expense Filter view | ||
""" | ||
|
||
queryset = ExpenseFilter.objects.all() | ||
serializer_class = ExpenseFilterSerializer | ||
|
||
|
||
class ExpenseFilterDeleteView(generics.DestroyAPIView): | ||
""" | ||
Expense Filter Delete view | ||
""" | ||
|
||
queryset = ExpenseFilter.objects.all() | ||
serializer_class = ExpenseFilterSerializer |
Oops, something went wrong.