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

Expense Model updated, comments fixed #29

Merged
merged 8 commits into from
Nov 29, 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
42 changes: 42 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
default_stages: [commit]
fail_fast: true
exclude: "^(sql/|(.*\/)?migrations\/)"
default_language_version: # noqa
python: python3

repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.0.1
hooks:
- id: check-ast
- id: check-merge-conflict
- id: debug-statements
- id: detect-private-key
- id: end-of-file-fixer
- id: trailing-whitespace

- repo: https://github.com/timothycrosley/isort
rev: 5.12.0
hooks:
- id: isort
args: ["--profile", "black", "--trailing-comma", "--line-length=125"] # Fixed the syntax error here

- repo: https://github.com/pycqa/flake8
rev: 6.1.0
hooks:
- id: flake8
additional_dependencies: [flake8-isort, flake8-tidy-imports]

- repo: https://github.com/Lucas-C/pre-commit-hooks-bandit
rev: v1.0.5
hooks:
- id: python-bandit-vulnerability-check
args: ["-lll", "--recursive", "-c", "bandit.yaml", "."] # Fixed the syntax error here
files: '\.py$' # Fixed the regex syntax here

- repo: local
hooks:
- id: flake8
name: flake8
entry: flake8
language: system
11 changes: 5 additions & 6 deletions apps/business_central/serializers.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
import logging
from django.db.models import Q
from datetime import datetime

from django.db.models import Q
from fyle_accounting_mappings.models import DestinationAttribute
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, BusinessCentralCredentials
from apps.business_central.helpers import sync_dimensions, check_interval_and_sync_dimension

from apps.business_central.helpers import check_interval_and_sync_dimension, sync_dimensions
from apps.workspaces.models import BusinessCentralCredentials, Workspace

logger = logging.getLogger(__name__)
logger.level = logging.INFO
Expand Down
3 changes: 1 addition & 2 deletions apps/business_central/urls.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
from django.urls import path

from apps.business_central.views import ImportBusinessCentralAttributesView, BusinessCentralFieldsView

from apps.business_central.views import BusinessCentralFieldsView, ImportBusinessCentralAttributesView

urlpatterns = [
path(
Expand Down
9 changes: 2 additions & 7 deletions apps/business_central/views.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,6 @@
import logging

from rest_framework import generics

from apps.business_central.serializers import ImportBusinessCentralAttributesSerializer, BusinessCentralFieldSerializer


logger = logging.getLogger(__name__)
logger.level = logging.INFO
from apps.business_central.serializers import BusinessCentralFieldSerializer, ImportBusinessCentralAttributesSerializer


class ImportBusinessCentralAttributesView(generics.CreateAPIView):
Expand All @@ -21,6 +15,7 @@ class BusinessCentralFieldsView(generics.ListAPIView):
Business Central Fields View
"""
serializer_class = BusinessCentralFieldSerializer
pagination_class = None

def get_queryset(self):
return BusinessCentralFieldSerializer().format_business_central_fields(self.kwargs["workspace_id"])
27 changes: 25 additions & 2 deletions apps/fyle/helpers.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import json

import requests
from django.conf import settings

from fyle_integrations_platform_connector import PlatformConnector

from apps.workspaces.models import FyleCredential
from apps.accounting_exports.models import AccountingExport
from apps.fyle.constants import DEFAULT_FYLE_CONDITIONS
from apps.workspaces.models import ExportSetting, FyleCredential


def post_request(url, body, refresh_token=None):
Expand Down Expand Up @@ -79,3 +80,25 @@ def get_expense_fields(workspace_id: int):
})

return response


def get_exportable_accounting_exports_ids(workspace_id: int):
"""
Get List of accounting exports ids
"""

export_setting = ExportSetting.objects.get(workspace_id=workspace_id)
fund_source = []

if export_setting.reimbursable_expenses_export_type:
fund_source.append('PERSONAL')
if export_setting.credit_card_expense_export_type:
fund_source.append('CCC')

accounting_export_ids = AccountingExport.objects.filter(
workspace_id=workspace_id,
exported_at__isnull=True,
fund_source__in=fund_source
).values_list('id', flat=True)

return accounting_export_ids
85 changes: 78 additions & 7 deletions apps/fyle/models.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from typing import List, Dict

from django.db import models
from django.contrib.postgres.fields import ArrayField
from ms_business_central_api.models.fields import (
Expand All @@ -11,7 +13,7 @@
StringOptionsField,
IntegerOptionsField,
)
from apps.workspaces.models import BaseModel, BaseForeignWorkspaceModel
from apps.workspaces.models import BaseForeignWorkspaceModel


EXPENSE_FILTER_RANK = (
Expand Down Expand Up @@ -40,8 +42,13 @@
('not_in', 'not_in')
)

SOURCE_ACCOUNT_MAP = {
'PERSONAL_CASH_ACCOUNT': 'PERSONAL',
'PERSONAL_CORPORATE_CREDIT_CARD_ACCOUNT': 'CCC'
}


class Expense(BaseModel):
class Expense(BaseForeignWorkspaceModel):
"""
Expense
"""
Expand All @@ -55,14 +62,14 @@ class Expense(BaseModel):
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')
amount = FloatNullField(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')
foreign_amount = FloatNullField(help_text='Foreign Amount')
foreign_currency = StringNullField(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')
vendor = StringNullField(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')
Expand All @@ -77,15 +84,79 @@ class Expense(BaseModel):
fund_source = StringNotNullField(help_text='Expense fund source')
verified_at = CustomDateTimeField(help_text='Report verified at')
custom_properties = CustomJsonField(help_text="Custom Properties")
report_title = models.TextField(null=True, blank=True, help_text='Report title')
payment_number = StringNullField(max_length=55, help_text='Expense payment number')
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'

@staticmethod
def create_expense_objects(expenses: List[Dict], workspace_id: int):
ruuushhh marked this conversation as resolved.
Show resolved Hide resolved
"""
Bulk create expense objects
"""

# Create an empty list to store expense objects
expense_objects = []

for expense in expenses:
# Iterate through custom property fields and handle empty values
for custom_property_field in expense['custom_properties']:
if expense['custom_properties'][custom_property_field] == '':
expense['custom_properties'][custom_property_field] = None

# Create or update an Expense object based on expense_id
expense_object, _ = Expense.objects.update_or_create(
expense_id=expense['id'],
defaults={
'employee_email': expense['employee_email'],
'employee_name': expense['employee_name'],
'category': expense['category'],
'sub_category': expense['sub_category'],
'project': expense['project'],
'expense_number': expense['expense_number'],
'org_id': expense['org_id'],
'claim_number': expense['claim_number'],
'amount': round(expense['amount'], 2),
'currency': expense['currency'],
'foreign_amount': expense['foreign_amount'],
'foreign_currency': expense['foreign_currency'],
'tax_amount': expense['tax_amount'],
'tax_group_id': expense['tax_group_id'],
'settlement_id': expense['settlement_id'],
'reimbursable': expense['reimbursable'],
'billable': expense['billable'] if expense['billable'] else False,
'state': expense['state'],
'vendor': expense['vendor'][:250] if expense['vendor'] else None,
'cost_center': expense['cost_center'],
'purpose': expense['purpose'],
'report_id': expense['report_id'],
'report_title': expense['report_title'],
'spent_at': expense['spent_at'],
'approved_at': expense['approved_at'],
'posted_at': expense['posted_at'],
'expense_created_at': expense['expense_created_at'],
'expense_updated_at': expense['expense_updated_at'],
'fund_source': SOURCE_ACCOUNT_MAP[expense['source_account_type']],
'verified_at': expense['verified_at'],
'custom_properties': expense['custom_properties'],
'payment_number': expense['payment_number'],
'file_ids': expense['file_ids'],
'corporate_card_id': expense['corporate_card_id'],
'workspace_id': workspace_id
}
)

# Check if an AccountingExport related to the expense object already exists
if not Expense.objects.filter(accountingexport__isnull=False).distinct():
expense_objects.append(expense_object)

return expense_objects


class ExpenseFilter(BaseForeignWorkspaceModel):
"""
Expand Down
24 changes: 21 additions & 3 deletions apps/fyle/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,32 @@
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
"""

import itertools

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

from apps.fyle.views import (
CustomFieldView,
ExpenseFilterDeleteView,
ExpenseFilterView,
ExportableExpenseGroupsView,
FyleFieldsView,
ImportFyleAttributesView,
)

accounting_exports_path = [
path('exportable_accounting_exports/', ExportableExpenseGroupsView.as_view(), name='exportable-accounting-exports')
]

urlpatterns = [
other_paths = [
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'),
]

fyle_dimension_paths = [
path('import_attributes/', ImportFyleAttributesView.as_view(), name='import-fyle-attributes')
]

urlpatterns = list(itertools.chain(accounting_exports_path, fyle_dimension_paths, other_paths))
30 changes: 27 additions & 3 deletions apps/fyle/views.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,19 @@
import logging

from rest_framework import generics
from ms_business_central_api.utils import LookupFieldMixin
from apps.workspaces.models import Workspace
from apps.fyle.serializers import ExpenseFilterSerializer, ImportFyleAttributesSerializer, FyleFieldsSerializer, ExpenseFieldSerializer
from rest_framework.response import Response
from rest_framework.views import status

from apps.fyle.helpers import get_exportable_accounting_exports_ids
from apps.fyle.models import ExpenseFilter
from apps.fyle.serializers import (
ExpenseFieldSerializer,
ExpenseFilterSerializer,
FyleFieldsSerializer,
ImportFyleAttributesSerializer,
)
from apps.workspaces.models import Workspace
from ms_business_central_api.utils import LookupFieldMixin

logger = logging.getLogger(__name__)
logger.level = logging.INFO
Expand Down Expand Up @@ -52,3 +62,17 @@ class CustomFieldView(generics.ListAPIView):

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


class ExportableExpenseGroupsView(generics.RetrieveAPIView):
"""
List Exportable Expense Groups
"""
def get(self, request, *args, **kwargs):

exportable_ids = get_exportable_accounting_exports_ids(workspace_id=kwargs['workspace_id'])

return Response(
data={'exportable_expense_group_ids': exportable_ids},
status=status.HTTP_200_OK
)
1 change: 1 addition & 0 deletions bandit.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
exclude_dirs: ['./venv', './.venv'] # noqa
Loading
Loading