Skip to content
Open
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
49 changes: 47 additions & 2 deletions apps/workspaces/apis/export_settings/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ def to_internal_value(self, data):
class WorkspaceGeneralSettingsSerializer(serializers.ModelSerializer):
class Meta:
model = WorkspaceGeneralSettings
fields = ['reimbursable_expenses_object', 'corporate_credit_card_expenses_object', 'name_in_journal_entry']
fields = ['reimbursable_expenses_object', 'corporate_credit_card_expenses_object', 'name_in_journal_entry', 'employee_field_mapping']
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this will be sent in employee_settings call only, why are we updating contracts?



class ExpenseGroupSettingsSerializer(serializers.ModelSerializer):
Expand Down Expand Up @@ -179,7 +179,7 @@ def update(self, instance, validated):

return instance

def validate(self, data):
def validate(self, data): # noqa: C901
if not data.get('workspace_general_settings'):
raise serializers.ValidationError('Workspace general settings are required')

Expand All @@ -188,4 +188,49 @@ def validate(self, data):

if not data.get('general_mappings'):
raise serializers.ValidationError('General mappings are required')

general_settings = data.get('workspace_general_settings')
general_mappings = data.get('general_mappings')

# Validate based on reimbursable expenses object
if general_settings.get('reimbursable_expenses_object') == 'BILL':
if not (general_mappings.get('accounts_payable', {}).get('id') or general_mappings.get('accounts_payable', {}).get('name')):
raise serializers.ValidationError('Accounts Payable is required for BILL type reimbursable expenses')

elif general_settings.get('reimbursable_expenses_object') == 'CHECK':
if not (general_mappings.get('bank_account', {}).get('id') or general_mappings.get('bank_account', {}).get('name')):
raise serializers.ValidationError('Bank Account is required for CHECK type reimbursable expenses')

elif general_settings.get('reimbursable_expenses_object') == 'EXPENSE':
if not (general_mappings.get('qbo_expense_account', {}).get('id') or general_mappings.get('qbo_expense_account', {}).get('name')):
raise serializers.ValidationError('Expense Payment Account is required for EXPENSE type reimbursable expenses')

elif general_settings.get('reimbursable_expenses_object') == 'JOURNAL ENTRY':
if general_settings.get('employee_field_mapping') == 'VENDOR':
if not (general_mappings.get('accounts_payable', {}).get('id') or general_mappings.get('accounts_payable', {}).get('name')):
raise serializers.ValidationError('Accounts Payable is required for JOURNAL ENTRY with VENDOR mapping')
elif general_settings.get('employee_field_mapping') == 'EMPLOYEE':
if not (general_mappings.get('bank_account', {}).get('id') or general_mappings.get('bank_account', {}).get('name')):
raise serializers.ValidationError('Bank Account is required for JOURNAL ENTRY with EMPLOYEE mapping')

# Validate based on corporate credit card expenses object
if general_settings.get('corporate_credit_card_expenses_object') == 'BILL':
if not (general_mappings.get('accounts_payable', {}).get('id') or general_mappings.get('accounts_payable', {}).get('name')):
raise serializers.ValidationError('Accounts Payable is required for BILL type corporate credit card expenses')
if not (general_mappings.get('default_ccc_vendor', {}).get('id') or general_mappings.get('default_ccc_vendor', {}).get('name')):
raise serializers.ValidationError('Default Credit Card Vendor is required for BILL type corporate credit card expenses')

elif general_settings.get('corporate_credit_card_expenses_object') == 'CREDIT CARD PURCHASE':
if not (general_mappings.get('default_ccc_account', {}).get('id') or general_mappings.get('default_ccc_account', {}).get('name')):
raise serializers.ValidationError('Default Credit Card Account is required for CREDIT CARD PURCHASE type expenses')

elif general_settings.get('corporate_credit_card_expenses_object') == 'DEBIT CARD EXPENSE':
if not (general_mappings.get('default_debit_card_account', {}).get('id') or general_mappings.get('default_debit_card_account', {}).get('name')):
raise serializers.ValidationError('Default Debit Card Account is required for DEBIT CARD EXPENSE type expenses')

elif general_settings.get('corporate_credit_card_expenses_object') == 'JOURNAL ENTRY':
if general_settings.get('name_in_journal_entry') == 'MERCHANT':
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is this check added, can you point me to code ref?

if not (general_mappings.get('default_ccc_account', {}).get('id') or general_mappings.get('default_ccc_account', {}).get('name')):
raise serializers.ValidationError('Default Credit Card Account is required for JOURNAL ENTRY with MERCHANT name')

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's add validation for default_tax_code_id as well

return data
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
"workspace_general_settings": {
"reimbursable_expenses_object": "EXPENSE",
"corporate_credit_card_expenses_object": None,
"name_in_journal_entry": "MERCHANT"
"name_in_journal_entry": "MERCHANT",
"employee_field_mapping": "EMPLOYEE"
},
"expense_group_settings": {
"reimbursable_expense_group_fields": [
Expand Down Expand Up @@ -142,7 +143,8 @@
"workspace_general_settings": {
"reimbursable_expenses_object": "EXPENSE",
"corporate_credit_card_expenses_object": None,
"name_in_journal_entry": "MERCHANT"
"name_in_journal_entry": "MERCHANT",
"employee_field_mapping": "EMPLOYEE"
},
"expense_group_settings": {
"reimbursable_expense_group_fields": [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
'ccc_export_date_type': '',
'split_expense_grouping': 'SINGLE_LINE_ITEM'
},
'workspace_general_settings': {'reimbursable_expenses_object': 'EXPENSE', 'corporate_credit_card_expenses_object': 'BILL','name_in_journal_entry': 'MERCHANT'},
'workspace_general_settings': {'reimbursable_expenses_object': 'EXPENSE', 'corporate_credit_card_expenses_object': 'BILL','name_in_journal_entry': 'MERCHANT', 'employee_field_mapping': 'VENDOR'},
'general_mappings': {
'bank_account': {'id': '', 'name': ''},
'default_ccc_account': {'id': '', 'name': ''},
Expand Down Expand Up @@ -40,7 +40,7 @@
},
},
'response': {
'workspace_general_settings': {'reimbursable_expenses_object': 'EXPENSE', 'corporate_credit_card_expenses_object': 'BILL', 'name_in_journal_entry': 'MERCHANT'},
'workspace_general_settings': {'reimbursable_expenses_object': 'EXPENSE', 'corporate_credit_card_expenses_object': 'BILL', 'name_in_journal_entry': 'MERCHANT', 'employee_field_mapping': 'VENDOR'},
'expense_group_settings': {
'reimbursable_expense_group_fields': ['fund_source', 'claim_number', 'employee_email', 'report_id'],
'corporate_credit_card_expense_group_fields': ['fund_source', 'claim_number', 'employee_email', 'report_id'],
Expand Down
96 changes: 93 additions & 3 deletions tests/test_workspaces/test_apis/test_export_settings/test_views.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import copy
import json

from apps.workspaces.models import Workspace, WorkspaceGeneralSettings
Expand All @@ -6,7 +7,6 @@


def test_export_settings(api_client, test_connection):

workspace = Workspace.objects.get(id=3)
workspace.onboarding_state = 'EXPORT_SETTINGS'
workspace.save()
Expand All @@ -25,16 +25,106 @@ def test_export_settings(api_client, test_connection):
response = json.loads(response.content)
assert dict_compare_keys(response, data['response']) == [], 'workspaces api returns a diff in the keys'

invalid_workspace_general_settings = data['export_settings']
invalid_workspace_general_settings = copy.deepcopy(data['export_settings'])
invalid_workspace_general_settings['workspace_general_settings'] = {}
response = api_client.put(url, data=invalid_workspace_general_settings, format='json')

assert response.status_code == 400

invalid_expense_group_settings = data['export_settings']
invalid_expense_group_settings = copy.deepcopy(data['export_settings'])
invalid_expense_group_settings['expense_group_settings'] = {}
invalid_expense_group_settings['workspace_general_settings'] = {'reimbursable_expenses_object': 'EXPENSE', 'corporate_credit_card_expenses_object': 'BILL'}

response = api_client.put(url, data=invalid_expense_group_settings, format='json')

assert response.status_code == 400


def test_export_settings_validation(api_client, test_connection):
url = '/api/v2/workspaces/3/export_settings/'
api_client.credentials(HTTP_AUTHORIZATION='Bearer {}'.format(test_connection.access_token))

# Test BILL type reimbursable expenses validation
bill_settings = copy.deepcopy(data['export_settings'])
bill_settings['workspace_general_settings']['reimbursable_expenses_object'] = 'BILL'
bill_settings['workspace_general_settings']['corporate_credit_card_expenses_object'] = 'EXPENSE' # Set to different value to avoid conflict
bill_settings['general_mappings']['accounts_payable'] = {'id': '', 'name': ''}
response = api_client.put(url, data=bill_settings, format='json')
assert response.status_code == 400
assert 'Accounts Payable is required for BILL type reimbursable expenses' in str(response.content)

# Test CHECK type reimbursable expenses validation
check_settings = copy.deepcopy(data['export_settings'])
check_settings['workspace_general_settings']['reimbursable_expenses_object'] = 'CHECK'
check_settings['workspace_general_settings']['corporate_credit_card_expenses_object'] = 'EXPENSE' # Set to different value to avoid conflict
check_settings['general_mappings']['bank_account'] = {'id': '', 'name': ''}
response = api_client.put(url, data=check_settings, format='json')
assert response.status_code == 400
assert 'Bank Account is required for CHECK type reimbursable expenses' in str(response.content)

# Test EXPENSE type reimbursable expenses validation
expense_settings = copy.deepcopy(data['export_settings'])
expense_settings['workspace_general_settings']['reimbursable_expenses_object'] = 'EXPENSE'
expense_settings['workspace_general_settings']['corporate_credit_card_expenses_object'] = 'BILL' # Set to different value to avoid conflict
expense_settings['general_mappings']['qbo_expense_account'] = {'id': '', 'name': ''}
response = api_client.put(url, data=expense_settings, format='json')
assert response.status_code == 400
assert 'Expense Payment Account is required for EXPENSE type reimbursable expenses' in str(response.content)

# Test JOURNAL ENTRY type with VENDOR mapping validation
je_vendor_settings = copy.deepcopy(data['export_settings'])
je_vendor_settings['workspace_general_settings']['reimbursable_expenses_object'] = 'JOURNAL ENTRY'
je_vendor_settings['workspace_general_settings']['corporate_credit_card_expenses_object'] = None
je_vendor_settings['workspace_general_settings']['employee_field_mapping'] = 'VENDOR'
je_vendor_settings['general_mappings']['accounts_payable'] = {'id': '', 'name': ''}
response = api_client.put(url, data=je_vendor_settings, format='json')
assert response.status_code == 400
assert 'Accounts Payable is required for JOURNAL ENTRY with VENDOR mapping' in str(response.content)

# Test JOURNAL ENTRY type with EMPLOYEE mapping validation
je_employee_settings = copy.deepcopy(data['export_settings'])
je_employee_settings['workspace_general_settings']['employee_field_mapping'] = 'EMPLOYEE'
je_employee_settings['workspace_general_settings']['reimbursable_expenses_object'] = 'JOURNAL ENTRY'
je_employee_settings['workspace_general_settings']['corporate_credit_card_expenses_object'] = None
je_employee_settings['general_mappings']['bank_account'] = {'id': '', 'name': ''}
response = api_client.put(url, data=je_employee_settings, format='json')
assert response.status_code == 400
assert 'Bank Account is required for JOURNAL ENTRY with EMPLOYEE mapping' in str(response.content)

# Test BILL type corporate credit card expenses validation
ccc_bill_settings = copy.deepcopy(data['export_settings'])
ccc_bill_settings['workspace_general_settings']['corporate_credit_card_expenses_object'] = 'BILL'
ccc_bill_settings['workspace_general_settings']['reimbursable_expenses_object'] = None # Set to different value to avoid conflict
ccc_bill_settings['general_mappings']['accounts_payable'] = {'id': '', 'name': ''}
ccc_bill_settings['general_mappings']['default_ccc_vendor'] = {'id': '', 'name': ''}
response = api_client.put(url, data=ccc_bill_settings, format='json')
assert response.status_code == 400
assert 'Accounts Payable is required for BILL type corporate credit card expenses' in str(response.content)

# Test CREDIT CARD PURCHASE type validation
ccp_settings = copy.deepcopy(data['export_settings'])
ccp_settings['workspace_general_settings']['corporate_credit_card_expenses_object'] = 'CREDIT CARD PURCHASE'
ccp_settings['workspace_general_settings']['reimbursable_expenses_object'] = None
ccp_settings['general_mappings']['default_ccc_account'] = {'id': '', 'name': ''}
response = api_client.put(url, data=ccp_settings, format='json')
assert response.status_code == 400
assert 'Default Credit Card Account is required for CREDIT CARD PURCHASE type expenses' in str(response.content)

# Test DEBIT CARD EXPENSE type validation
debit_settings = copy.deepcopy(data['export_settings'])
debit_settings['workspace_general_settings']['corporate_credit_card_expenses_object'] = 'DEBIT CARD EXPENSE'
debit_settings['workspace_general_settings']['reimbursable_expenses_object'] = None
debit_settings['general_mappings']['default_debit_card_account'] = {'id': '', 'name': ''}
response = api_client.put(url, data=debit_settings, format='json')
assert response.status_code == 400
assert 'Default Debit Card Account is required for DEBIT CARD EXPENSE type expenses' in str(response.content)

# Test JOURNAL ENTRY with MERCHANT name validation
je_merchant_settings = copy.deepcopy(data['export_settings'])
je_merchant_settings['workspace_general_settings']['corporate_credit_card_expenses_object'] = 'JOURNAL ENTRY'
je_merchant_settings['workspace_general_settings']['name_in_journal_entry'] = 'MERCHANT'
je_merchant_settings['workspace_general_settings']['reimbursable_expenses_object'] = None
je_merchant_settings['general_mappings']['default_ccc_account'] = {'id': '', 'name': ''}
response = api_client.put(url, data=je_merchant_settings, format='json')
assert response.status_code == 400
assert 'Default Credit Card Account is required for JOURNAL ENTRY with MERCHANT name' in str(response.content)
Loading