Skip to content

Commit

Permalink
feat: Receipt mandatory settings combined changes (#2477)
Browse files Browse the repository at this point in the history
  • Loading branch information
OmkarJ13 authored Nov 3, 2023
1 parent 47ecbe0 commit 1b97583
Show file tree
Hide file tree
Showing 14 changed files with 613 additions and 220 deletions.
15 changes: 15 additions & 0 deletions src/app/core/mock-data/missing-mandatory-fields.data.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { PlatformMissingMandatoryFields } from '../models/platform/platform-missing-mandatory-fields.model';

export const missingMandatoryFieldsData1: PlatformMissingMandatoryFields = {
missing_amount: false,
missing_currency: false,
missing_receipt: true,
missing_expense_field_ids: [],
};

export const missingMandatoryFieldsData2: PlatformMissingMandatoryFields = {
missing_amount: false,
missing_currency: false,
missing_receipt: false,
missing_expense_field_ids: [],
};
13 changes: 13 additions & 0 deletions src/app/core/mock-data/org-category.data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1161,6 +1161,19 @@ export const expectedOrgCategoryByName2: OrgCategory = {
updated_at: new Date('2022-07-01T05:51:31.800Z'),
};

export const unspecifiedCategory: OrgCategory = {
code: null,
created_at: new Date('2018-01-31T23:50:27.235056+00:00'),
displayName: 'Unspecified',
enabled: true,
fyle_category: 'Unspecified',
id: 16569,
name: 'Unspecified',
org_id: 'orNVthTo2Zyo',
sub_category: 'Unspecified',
updated_at: new Date('2022-05-05T17:45:42.092507+00:00'),
};

export const mileageCategoryWithoutId: OrgCategory[] = [
{
code: '93',
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { PlatformMissingMandatoryFields } from './platform-missing-mandatory-fields.model';

export interface PlatformMissingMandatoryFieldsResponse {
data: PlatformMissingMandatoryFields;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export interface PlatformMissingMandatoryFields {
missing_amount: boolean;
missing_receipt: boolean;
missing_currency: boolean;
missing_expense_field_ids: string[];
}
83 changes: 82 additions & 1 deletion src/app/core/services/policy.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,25 @@ import {
platformPolicyExpenseData3,
platformPolicyExpenseData4,
platformPolicyExpenseData5,
platformPolicyExpenseData1,
} from '../mock-data/platform-policy-expense.data';
import { of } from 'rxjs';
import { CategoriesService } from './categories.service';
import { fileObject4 } from '../mock-data/file-object.data';
import { unspecifiedCategory } from '../mock-data/org-category.data';
import { eCCCData1 } from '../mock-data/corporate-card-expense-unflattened.data';

describe('PolicyService', () => {
let policyService: PolicyService;
let spenderPlatformV1ApiService: jasmine.SpyObj<SpenderPlatformV1ApiService>;
let approverPlatformApiService: jasmine.SpyObj<ApproverPlatformApiService>;
let categoriesService: jasmine.SpyObj<CategoriesService>;

beforeEach(() => {
const spenderPlatformV1ApiServiceSpy = jasmine.createSpyObj('SpenderPlatformV1ApiService', ['get']);
const approverPlatformApiServiceSpy = jasmine.createSpyObj('ApproverPlatformApiService', ['get']);
const categoriesServiceSpy = jasmine.createSpyObj('CategoriesService', ['getCategoryByName']);

TestBed.configureTestingModule({
providers: [
PolicyService,
Expand All @@ -44,6 +52,10 @@ describe('PolicyService', () => {
provide: ApproverPlatformApiService,
useValue: approverPlatformApiServiceSpy,
},
{
provide: CategoriesService,
useValue: categoriesServiceSpy,
},
],
});
policyService = TestBed.inject(PolicyService);
Expand All @@ -53,9 +65,10 @@ describe('PolicyService', () => {
approverPlatformApiService = TestBed.inject(
ApproverPlatformApiService
) as jasmine.SpyObj<ApproverPlatformApiService>;
categoriesService = TestBed.inject(CategoriesService) as jasmine.SpyObj<CategoriesService>;
});

it(' should be created', () => {
it('should be created', () => {
expect(policyService).toBeTruthy();
});

Expand Down Expand Up @@ -206,4 +219,72 @@ describe('PolicyService', () => {
expect(policyService.checkIfViolationsExist(violations)).toBe(false);
expect(policyService.getPolicyRules).toHaveBeenCalledOnceWith(policyViolationData);
});

describe('prepareEtxnForPolicyCheck()', () => {
it('should set num_files in the transaction based on the number of files', (done) => {
const etxn = {
tx: publicPolicyExpenseData1,
dataUrls: fileObject4,
};

policyService.prepareEtxnForPolicyCheck(etxn, null).subscribe((res) => {
expect(res.num_files).toEqual(1);
done();
});
});

it('should set is_matching_ccc_expense to false if the expense has no matching transactions', (done) => {
const etxn = {
tx: publicPolicyExpenseData1,
dataUrls: fileObject4,
};

policyService.prepareEtxnForPolicyCheck(etxn, null).subscribe((res) => {
expect(res.is_matching_ccc_expense).toBe(false);
done();
});
});

it('should set is_matching_ccc_expense to true if expense matching transactions', (done) => {
const etxn = {
tx: publicPolicyExpenseData1,
dataUrls: fileObject4,
};

policyService.prepareEtxnForPolicyCheck(etxn, eCCCData1.ccce).subscribe((res) => {
expect(res.is_matching_ccc_expense).toBe(true);
done();
});
});

it('should set org_category_id to unspecified if no category is present in the expense', (done) => {
categoriesService.getCategoryByName.and.returnValue(of(unspecifiedCategory));
const etxn = {
tx: { ...publicPolicyExpenseData1, org_category_id: null },
dataUrls: fileObject4,
};

policyService.prepareEtxnForPolicyCheck(etxn, null).subscribe((res) => {
expect(res.org_category_id).toEqual(unspecifiedCategory.id);
expect(categoriesService.getCategoryByName).toHaveBeenCalledOnceWith('Unspecified');
done();
});
});
});

it('getPlatformPolicyExpense(): should get platform policy expense', () => {
spyOn(policyService, 'prepareEtxnForPolicyCheck').and.returnValue(of(publicPolicyExpenseData1));
spyOn(policyService, 'transformTo').and.returnValue(platformPolicyExpenseData1);

const etxn = {
tx: publicPolicyExpenseData1,
dataUrls: fileObject4,
};

policyService.getPlatformPolicyExpense(etxn, null).subscribe((res) => {
expect(res).toEqual(platformPolicyExpenseData1);
expect(policyService.prepareEtxnForPolicyCheck).toHaveBeenCalledOnceWith(etxn, null);
expect(policyService.transformTo).toHaveBeenCalledOnceWith(publicPolicyExpenseData1);
});
});
});
52 changes: 51 additions & 1 deletion src/app/core/services/policy.service.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Injectable } from '@angular/core';
import { map, Observable } from 'rxjs';
import { map, Observable, of } from 'rxjs';
import { PlatformApiResponse } from '../models/platform/platform-api-response.model';
import { ExpensePolicy } from '../models/platform/platform-expense-policy.model';
import { ExpensePolicyStates } from '../models/platform/platform-expense-policy-states.model';
Expand All @@ -10,6 +10,10 @@ import { PublicPolicyExpense } from '../models/public-policy-expense.model';
import { ApproverPlatformApiService } from './approver-platform-api.service';
import { SpenderPlatformV1ApiService } from './spender-platform-v1-api.service';
import { Transaction } from '../models/v1/transaction.model';
import { cloneDeep } from 'lodash';
import { CategoriesService } from './categories.service';
import { FileObject } from '../models/file-obj.model';
import { CCCExpense } from '../models/corporate-card-expense-unflattened.model';

@Injectable({
providedIn: 'root',
Expand All @@ -18,6 +22,7 @@ export class PolicyService {
constructor(
private spenderPlatformV1ApiService: SpenderPlatformV1ApiService,
private approverPlatformApiService: ApproverPlatformApiService,
private categoriesService: CategoriesService
) {}

transformTo(transaction: PublicPolicyExpense | Partial<Transaction>): PlatformPolicyExpense {
Expand Down Expand Up @@ -156,4 +161,49 @@ export class PolicyService {
isExpenseCapped(policyActionDescription: string): boolean {
return policyActionDescription.toLowerCase().includes('expense will be capped to');
}

prepareEtxnForPolicyCheck(
etxn: { tx: PublicPolicyExpense; dataUrls: Partial<FileObject>[] },
selectedCCCTransaction: CCCExpense
): Observable<PublicPolicyExpense> {
const transactionCopy = cloneDeep(etxn.tx);
/* Adding number of attachements and sending in test call as tx_num_files
* If editing an expense with receipts, check for already uploaded receipts
*/
if (etxn.tx) {
transactionCopy.num_files = etxn.tx.num_files;

// Check for receipts uploaded from mobile
if (etxn.dataUrls && etxn.dataUrls.length > 0) {
transactionCopy.num_files = etxn.tx.num_files + etxn.dataUrls.length;
}
}

transactionCopy.is_matching_ccc_expense = !!selectedCCCTransaction;
let transaction$ = of(transactionCopy);
if (!transactionCopy.org_category_id) {
// Set unspecified org category if expense doesn't have a category
const categoryName = 'Unspecified';
transaction$ = this.categoriesService.getCategoryByName(categoryName).pipe(
map((category) => ({
...transactionCopy,
org_category_id: category.id,
}))
);
}

return transaction$;
}

getPlatformPolicyExpense(
etxn: {
tx: PublicPolicyExpense;
dataUrls: Partial<FileObject>[];
},
selectedCCCTransaction: CCCExpense
): Observable<PlatformPolicyExpense> {
return this.prepareEtxnForPolicyCheck(etxn, selectedCCCTransaction).pipe(
map((publicPolicyExpense) => this.transformTo(publicPolicyExpense))
);
}
}
12 changes: 12 additions & 0 deletions src/app/core/services/transaction.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ import { SortFiltersParams } from '../models/sort-filters-params.model';
import { PaymentModeSummary } from '../models/payment-mode-summary.model';
import { Datum, StatsResponse } from '../models/v2/stats-response.model';
import { TxnCustomProperties } from '../models/txn-custom-properties.model';
import { PlatformMissingMandatoryFields } from '../models/platform/platform-missing-mandatory-fields.model';
import { PlatformMissingMandatoryFieldsResponse } from '../models/platform/platform-missing-mandatory-fields-response.model';

enum FilterState {
READY_TO_REPORT = 'READY_TO_REPORT',
Expand Down Expand Up @@ -382,6 +384,16 @@ export class TransactionService {
.pipe(map((res) => this.fixDates(res.data[0])));
}

checkMandatoryFields(platformPolicyExpense: PlatformPolicyExpense): Observable<PlatformMissingMandatoryFields> {
const payload = {
data: platformPolicyExpense,
};

return this.spenderPlatformV1ApiService
.post<PlatformMissingMandatoryFieldsResponse>('/expenses/check_mandatory_fields', payload)
.pipe(map((res) => res.data));
}

checkPolicy(platformPolicyExpense: PlatformPolicyExpense): Observable<ExpensePolicy> {
return this.orgUserSettingsService.get().pipe(
switchMap((orgUserSettings) => {
Expand Down
Loading

0 comments on commit 1b97583

Please sign in to comment.