Skip to content

Commit

Permalink
Quickbooks Integration: Generic Mapping Infra Port
Browse files Browse the repository at this point in the history
  • Loading branch information
Shwetabhk authored May 26, 2020
1 parent 6221a9f commit 25548b2
Show file tree
Hide file tree
Showing 35 changed files with 1,498 additions and 394 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/pylint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,5 @@ jobs:
- name: Python Pylin GitHub Action
uses: fylein/python-pylint-github-action@v4
with:
args: pip3 install -r requirements.txt && pip install pylint-django && pylint --load-plugins pylint_django --rcfile=.pylintrc **/**
args: pip3 install -r requirements.txt && pip install pylint-django && pylint --load-plugins pylint_django --rcfile=.pylintrc **/**.py

4 changes: 3 additions & 1 deletion .pylintrc
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,9 @@ disable=print-statement,
no-member,
simplifiable-if-expression,
broad-except,
too-many-arguments
too-many-arguments,
too-many-locals,
too-few-public-methods

# Enable the message, report, category or checker with the given id(s). You can
# either give multiple identifier separated by comma (,) or put this option
Expand Down
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ COPY . /fyle-qbo-api/
WORKDIR /fyle-qbo-api

# Do linting checks
RUN pylint --load-plugins pylint_django --rcfile=.pylintrc **/**
RUN pylint --load-plugins pylint_django --rcfile=.pylintrc **/**.py

# Expose development port
EXPOSE 8000
Expand Down
112 changes: 86 additions & 26 deletions apps/fyle/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@

from django.db import models
from django.contrib.postgres.fields import JSONField
from fyle_accounting_mappings.models import MappingSetting

from apps.workspaces.models import Workspace
from apps.workspaces.models import Workspace, WorkspaceGeneralSettings


class Expense(models.Model):
Expand Down Expand Up @@ -105,38 +106,97 @@ def create_expense_groups_by_report_id_fund_source(expense_objects: List[Expense
"""
Group expense by report_id and fund_source
"""
expense_groups = groupby(
expense_objects, lambda expense: (expense.report_id, expense.employee_email, expense.claim_number,
expense.fund_source)
)
department_setting: MappingSetting = MappingSetting.objects.filter(
workspace_id=workspace_id,
destination_field='DEPARTMENT'
).first()

expense_group_objects = []
general_settings = WorkspaceGeneralSettings.objects.get(workspace_id=workspace_id)

reimbursable_expenses = list(filter(lambda expense: expense.fund_source == 'PERSONAL', expense_objects))

for expense_group, _ in expense_groups:
report_id = expense_group[0]
employee_email = expense_group[1]
claim_number = expense_group[2]
fund_source = expense_group[3]
ccc_expenses = list(filter(lambda expense: expense.fund_source == 'CCC', expense_objects))

expense_ids = Expense.objects.filter(report_id=report_id, fund_source=fund_source).values_list(
'id', flat=True
if department_setting and general_settings.reimbursable_expenses_object != 'JOURNAL_ENTRY':
reimbursable_expense_groups = groupby(
reimbursable_expenses, lambda expense: (
expense.report_id, expense.employee_email,
expense.claim_number, expense.fund_source,
expense.project if department_setting.source_field == 'PROJECT' else expense.cost_center
)
)
else:
reimbursable_expense_groups = groupby(
reimbursable_expenses, lambda expense: (
expense.report_id, expense.employee_email,
expense.claim_number, expense.fund_source
)
)

expense_group_object, _ = ExpenseGroup.objects.update_or_create(
fyle_group_id=report_id + '-' + fund_source.lower(),
workspace_id=workspace_id,
fund_source=fund_source,
defaults={
'description': {
'employee_email': employee_email,
'claim_number': claim_number,
'fund_source': fund_source
group_types = [reimbursable_expense_groups]

if general_settings.corporate_credit_card_expenses_object and ccc_expenses:
if department_setting and general_settings.corporate_credit_card_expenses_object != 'JOURNAL_ENTRY':
ccc_expense_groups = groupby(
ccc_expenses, lambda expense: (
expense.report_id, expense.employee_email,
expense.claim_number, expense.fund_source,
expense.project if department_setting.source_field == 'PROJECT' else expense.cost_center
)
)
else:
ccc_expense_groups = groupby(
ccc_expenses, lambda expense: (
expense.report_id, expense.employee_email,
expense.claim_number, expense.fund_source
)
)
group_types.append(ccc_expense_groups)

expense_group_objects = []

for expense_groups in group_types:
for expense_group, _ in expense_groups:
report_id = expense_group[0]
employee_email = expense_group[1]
claim_number = expense_group[2]
fund_source = expense_group[3]
department = None
if len(expense_group) > 4:
department = expense_group[4]

kwargs = {}

if department:
kwargs = {
'{0}'.format(department_setting.source_field.lower()): department,
}
}
)

expense_group_object.expenses.add(*expense_ids)
expense_ids = Expense.objects.filter(
report_id=report_id,
fund_source=fund_source,
**kwargs
).values_list(
'id', flat=True
)

expense_group_object, _ = ExpenseGroup.objects.update_or_create(
fyle_group_id='{0}-{1}'.format(claim_number, fund_source) if not department
else '{0}-{1}-{2}'.format(claim_number, fund_source, department),
workspace_id=workspace_id,
fund_source=fund_source,
defaults={
'description': {
'employee_email': employee_email,
'claim_number': claim_number,
'fund_source': fund_source,
department_setting.source_field.lower(): department
}
}
)

expense_group_object.expenses.add(*expense_ids)

expense_group_objects.append(expense_group_object)
expense_group_objects.append(expense_group_object)

return expense_group_objects
4 changes: 2 additions & 2 deletions apps/fyle/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ def schedule_expense_group_creation(workspace_id: int, user: str):
"""
fyle_credentials = FyleCredential.objects.get(
workspace_id=workspace_id)
fyle_connector = FyleConnector(fyle_credentials.refresh_token)
fyle_connector = FyleConnector(fyle_credentials.refresh_token, workspace_id)
fyle_sdk_connection = fyle_connector.connection

jobs = FyleJobsSDK(settings.FYLE_JOBS_URL, fyle_sdk_connection)
Expand Down Expand Up @@ -93,7 +93,7 @@ def async_create_expense_groups(workspace_id: int, state: List[str], fund_source

fyle_credentials = FyleCredential.objects.get(workspace_id=workspace_id)

fyle_connector = FyleConnector(fyle_credentials.refresh_token)
fyle_connector = FyleConnector(fyle_credentials.refresh_token, workspace_id)

expenses = fyle_connector.get_expenses(
state=state,
Expand Down
8 changes: 4 additions & 4 deletions apps/fyle/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@
path('expense_groups/trigger/', ExpenseGroupScheduleView.as_view()),
path('expense_groups/<int:expense_group_id>/', ExpenseGroupByIdView.as_view()),
path('expense_groups/<int:expense_group_id>/expenses/', ExpenseView.as_view()),
path('employees/', EmployeeView.as_view({'get': 'get_employees'})),
path('categories/', CategoryView.as_view({'get': 'get_categories'})),
path('cost_centers/', CostCenterView.as_view({'get': 'get_cost_centers'})),
path('projects/', ProjectView.as_view({'get': 'get_projects'}))
path('employees/', EmployeeView.as_view()),
path('categories/', CategoryView.as_view()),
path('cost_centers/', CostCenterView.as_view()),
path('projects/', ProjectView.as_view())
]
82 changes: 73 additions & 9 deletions apps/fyle/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,19 @@

from fylesdk import FyleSDK

from fyle_accounting_mappings.models import ExpenseAttribute


class FyleConnector:
"""
Fyle utility functions
"""
def __init__(self, refresh_token):

def __init__(self, refresh_token, workspace_id=None):
client_id = settings.FYLE_CLIENT_ID
client_secret = settings.FYLE_CLIENT_SECRET
base_url = settings.FYLE_BASE_URL
self.workspace_id = workspace_id

self.connection = FyleSDK(
base_url=base_url,
Expand All @@ -39,26 +43,86 @@ def get_expenses(self, state: List[str], updated_at: List[str], fund_source: Lis
expenses))
return expenses

def get_employees(self):
def sync_employees(self):
"""
Get employees from fyle
"""
return self.connection.Employees.get_all()
employees = self.connection.Employees.get_all()

employee_attributes = []

for employee in employees:
employee_attributes.append({
'attribute_type': 'EMPLOYEE',
'display_name': 'Employee',
'value': employee['employee_email'],
'source_id': employee['id']
})

employee_attributes = ExpenseAttribute.bulk_upsert_expense_attributes(employee_attributes, self.workspace_id)

def get_categories(self, active_only: bool):
return employee_attributes

def sync_categories(self, active_only: bool):
"""
Get categories from fyle
"""
return self.connection.Categories.get(active_only=active_only)['data']
categories = self.connection.Categories.get(active_only=active_only)['data']

category_attributes = []

for category in categories:
if category['name'] != category['sub_category']:
category['name'] = '{0} / {1}'.format(category['name'], category['sub_category'])

category_attributes.append({
'attribute_type': 'CATEGORY',
'display_name': 'Category',
'value': category['name'],
'source_id': category['id']
})

category_attributes = ExpenseAttribute.bulk_upsert_expense_attributes(category_attributes, self.workspace_id)

def get_cost_centers(self, active_only: bool):
return category_attributes

def sync_cost_centers(self, active_only: bool):
"""
Get cost centers from fyle
"""
return self.connection.CostCenters.get(active_only=active_only)['data']
cost_centers = self.connection.CostCenters.get(active_only=active_only)['data']

cost_center_attributes = []

for cost_center in cost_centers:
cost_center_attributes.append({
'attribute_type': 'COST_CENTER',
'display_name': 'Cost Center',
'value': cost_center['name'],
'source_id': cost_center['id']
})

cost_center_attributes = ExpenseAttribute.bulk_upsert_expense_attributes(
cost_center_attributes, self.workspace_id)

return cost_center_attributes

def get_projects(self, active_only: bool):
def sync_projects(self, active_only: bool):
"""
Get projects from fyle
"""
return self.connection.Projects.get(active_only=active_only)['data']
projects = self.connection.Projects.get(active_only=active_only)['data']

project_attributes = []

for project in projects:
project_attributes.append({
'attribute_type': 'PROJECT',
'display_name': 'Project',
'value': project['name'],
'source_id': project['id']
})

project_attributes = ExpenseAttribute.bulk_upsert_expense_attributes(project_attributes, self.workspace_id)

return project_attributes
Loading

0 comments on commit 25548b2

Please sign in to comment.