From 7642a9fd4aec02ed4a837d74860a18d364ee33b9 Mon Sep 17 00:00:00 2001 From: Devendra Singh Rana <132900359+devendrafyle@users.noreply.github.com> Date: Tue, 5 Mar 2024 16:00:26 +0530 Subject: [PATCH] feat: Migrated getETxnUnflattened transaction service method to platform (#2737) * Migrated getETxnUnflattened transaction service method to platform * Updated transformExpenses method of transaction service and its usage * removed not used method initCCCTxn * minor changes added model for corporateCardTransaction response * update in key mapping, removed not used key and added required key * Updated the usage of switch map to map * update in markCCCPeronal and dismissCCE flow * test: Update unit test for add edit mileage (#2747) * Updated unit tests for add-edit-mileage * updated the mock data by removing not required keys * updated the mock data * Updated the missing type for mock data * update the types in add-edit mileage file for etxn * test: Updated unit tests for add-edit expense page (#2748) * Updated unit tests for add-edit expense page * Update in transform expense key mapping * update the sourceAccountTypePublicMapping method * test: Updated unit tests for add-edit per-diem page (#2749) * Updated unit tests for add-edit per-diem page * test: Updated unit tests for task component (#2750) * Updated unit tests for task component * test: Updated unit tests for switch org page (#2751) * Updated unit tests for switch org page * test: Updated unit tests for deep link redirection (#2752) * Updated unit tests for deep link redirection * test: Updated unit tests for expenses card and expenses card v2 component (#2753) * Updated unit tests for expenses card and expenses card v2 component * test: Updated unit tests for Transaction, Transaction-outbox, CCC Expense Service and Split Expense Page Unit Tests (#2754) * Updated unit tests for Transaction, Transaction-outbox, Corporate Credit Card Expense Service and Split Expense Page Unit Tests * Added mock data for track expense properties * clean up of not used methods and variable * feat: Migrated the method getAllExpenses to platform (#2759) * Migrated the method getAllExpenses to platform * feat: Migrated the method getEtxn to platform (#2760) * Migrated the method getEtxn to platform * updated concat map and of combo to map * feat: Migrated the method getEtxnc to platform (#2761) * Migrated the method getEtxnc to platform * updated the return of observable to map * feat: Migrated the method getAllEtxnc to platform (#2762) * Migrated the method getAllEtxnc to platform * feat: Migrated the method getTransactionStats to platform (#2764) * Migrated the method getTransactionStats to platform * test: fixed unit test for the methods getEtxnc, getTransactionStats, getEtxn and getAllETxnc migrated to platform (#2802) * Updated the unit test for the getEtxn, getEtxnc, getAllETxnc and getTransactionStats methods migration * fix: cleanup of depreacted keys and methods (#2803) * cleanup of depreacted keys and methods * minor updates in comments * test: fixed unit tests after cleanup for deprecated keys and methods (#2805) * test: fixed unit tests after cleanup for deprecated keys and methods * fixed the date unit tests * fixed the date unit tests-1 * update the file name to corporate-card-transaction-response * fixed unit tests for matchedCCTransaction date --- .../auth/switch-org/switch-org.page.spec.ts | 23 +- src/app/auth/switch-org/switch-org.page.ts | 8 +- ...orporate-card-transaction-response.data.ts | 261 ++++ src/app/core/mock-data/duplicate-sets.data.ts | 9 + src/app/core/mock-data/expense.data.ts | 309 ++++ .../mock-data/matchedCCTransaction.data.ts | 34 + .../mock-data/per-diem-form-value.data.ts | 3 - .../v1/expense-duplicate-sets.data.ts | 4 + .../mock-data/platform/v1/expense.data.ts | 1392 +++++++++++++++++ .../platform/v1/expenses-query-params.data.ts | 9 + .../platform/v1/expenses-stats.data.ts | 7 + .../core/mock-data/report-unflattened.data.ts | 93 ++ .../track-expense-properties.data.ts | 10 + .../core/mock-data/transaction-status.data.ts | 12 + src/app/core/mock-data/transaction.data.ts | 173 ++ .../mock-data/transformed-expense.data.ts | 441 ++++++ .../core/mock-data/unflattened-txn.data.ts | 144 +- src/app/core/models/custom-input.model.ts | 1 + src/app/core/models/expense.model.ts | 3 + .../models/matchedCCCTransaction.model.ts | 5 + .../core/models/per-diem-form-value.model.ts | 1 - .../models/platform/v1/assignor-user.model.ts | 5 + .../platform/v1/cc-matched-expense.model.ts | 14 + .../platform/v1/cc-transaction-metadata.ts | 38 + .../platform/v1/cc-transaction.model.ts | 51 + .../corporate-card-transaction-res.model.ts | 4 + .../platform/v1/corporate-card.model.ts | 8 + .../core/models/platform/v1/expense.model.ts | 6 +- .../models/txn-custom-properties.model.ts | 1 + src/app/core/models/v1/transaction.model.ts | 16 + ...porate-credit-card-expense.service.spec.ts | 50 +- .../corporate-credit-card-expense.service.ts | 43 +- src/app/core/services/deep-link.service.ts | 2 +- src/app/core/services/expenses-info.model.ts | 2 +- .../core/services/merge-expenses.service.ts | 89 +- .../v1/spender/expenses.service.spec.ts | 22 +- src/app/core/services/policy.service.ts | 6 +- .../services/split-expense.service.spec.ts | 14 +- .../core/services/transaction.service.spec.ts | 110 +- src/app/core/services/transaction.service.ts | 288 +++- .../transactions-outbox.service.spec.ts | 5 + .../services/transactions-outbox.service.ts | 31 +- .../deep-link-redirection.page.spec.ts | 28 +- .../deep-link-redirection.page.ts | 38 +- .../add-edit-expense-1.spec.ts | 45 +- .../add-edit-expense-2.spec.ts | 124 +- .../add-edit-expense-3.spec.ts | 9 - .../add-edit-expense-4.spec.ts | 245 +-- .../add-edit-expense-5.spec.ts | 45 +- .../add-edit-expense-6.spec.ts | 62 +- .../add-edit-expense.page.html | 7 - .../add-edit-expense/add-edit-expense.page.ts | 257 ++- .../add-edit-expense.setup.spec.ts | 6 +- .../add-edit-mileage-1.spec.ts | 52 +- .../add-edit-mileage-2.spec.ts | 1 - .../add-edit-mileage-3.spec.ts | 193 ++- .../add-edit-mileage-4.spec.ts | 2 - .../add-edit-mileage-5.spec.ts | 1 - .../add-edit-mileage.page.setup.spec.ts | 11 +- .../add-edit-mileage/add-edit-mileage.page.ts | 84 +- .../add-edit-per-diem-1.page.spec.ts | 37 +- .../add-edit-per-diem-2.page.spec.ts | 18 +- .../add-edit-per-diem-3.page.spec.ts | 49 +- .../add-edit-per-diem-4.page.spec.ts | 262 ++-- .../add-edit-per-diem-5.page.spec.ts | 48 +- .../add-edit-per-diem.page.setup.spec.ts | 8 +- .../add-edit-per-diem.page.ts | 58 +- .../dashboard/tasks/tasks-2.component.spec.ts | 69 +- .../dashboard/tasks/tasks-3.component.spec.ts | 1 + .../tasks/tasks.component.setup.spec.ts | 6 +- .../fyle/dashboard/tasks/tasks.component.ts | 14 +- .../merge-expense-1.page.spec.ts | 94 +- .../merge-expense.page.setup.spec.ts | 10 +- .../fyle/merge-expense/merge-expense.page.ts | 19 +- .../my-expenses-v2.page.spec.ts | 3 - .../my-expenses-v2/my-expenses-v2.page.ts | 8 - .../fyle/my-expenses/my-expenses.page.spec.ts | 4 - src/app/fyle/my-expenses/my-expenses.page.ts | 4 - .../fyle/my-reports/my-reports.page.spec.ts | 78 +- src/app/fyle/my-reports/my-reports.page.ts | 31 +- .../expenses-card.component.spec.ts | 58 +- .../expenses-card.component.ts | 7 +- .../expenses-card.component.spec.ts | 58 +- .../expenses-card/expenses-card.component.ts | 7 +- 84 files changed, 4586 insertions(+), 1322 deletions(-) create mode 100644 src/app/core/mock-data/corporate-card-transaction-response.data.ts create mode 100644 src/app/core/mock-data/matchedCCTransaction.data.ts create mode 100644 src/app/core/mock-data/transformed-expense.data.ts create mode 100644 src/app/core/models/platform/v1/assignor-user.model.ts create mode 100644 src/app/core/models/platform/v1/cc-matched-expense.model.ts create mode 100644 src/app/core/models/platform/v1/cc-transaction-metadata.ts create mode 100644 src/app/core/models/platform/v1/cc-transaction.model.ts create mode 100644 src/app/core/models/platform/v1/corporate-card-transaction-res.model.ts create mode 100644 src/app/core/models/platform/v1/corporate-card.model.ts diff --git a/src/app/auth/switch-org/switch-org.page.spec.ts b/src/app/auth/switch-org/switch-org.page.spec.ts index db21405848..b6af9c13f4 100644 --- a/src/app/auth/switch-org/switch-org.page.spec.ts +++ b/src/app/auth/switch-org/switch-org.page.spec.ts @@ -39,8 +39,10 @@ import { FyZeroStateComponent } from 'src/app/shared/components/fy-zero-state/fy import { click, getAllElementsBySelector, getElementBySelector, getTextContent } from 'src/app/core/dom-helpers'; import { globalCacheBusterNotifier } from 'ts-cacheable'; import { TransactionService } from 'src/app/core/services/transaction.service'; +import { ExpensesService } from 'src/app/core/services/platform/v1/spender/expenses.service'; import { DeepLinkService } from 'src/app/core/services/deep-link.service'; -import { unflattenedTxnData } from 'src/app/core/mock-data/unflattened-txn.data'; +import { platformExpenseData } from 'src/app/core/mock-data/platform/v1/expense.data'; +import { transformedExpenseData } from 'src/app/core/mock-data/transformed-expense.data'; const roles = ['OWNER', 'USER', 'FYLER']; const email = 'ajain@fyle.in'; @@ -68,6 +70,7 @@ describe('SwitchOrgPage', () => { let snackbarProperties: jasmine.SpyObj; let routerAuthService: jasmine.SpyObj; let transactionService: jasmine.SpyObj; + let expensesService: jasmine.SpyObj; let deepLinkService: jasmine.SpyObj; beforeEach(waitForAsync(() => { @@ -102,7 +105,8 @@ describe('SwitchOrgPage', () => { const matSnackBarSpy = jasmine.createSpyObj('MatSnackBar', ['openFromComponent']); const snackbarPropertiesSpy = jasmine.createSpyObj('SnackbarPropertiesService', ['setSnackbarProperties']); const routerAuthServiceSpy = jasmine.createSpyObj('RouterAuthService', ['resendVerificationLink']); - const transactionServiceSpy = jasmine.createSpyObj('TransactionService', ['getETxnUnflattened']); + const transactionServiceSpy = jasmine.createSpyObj('TransactionService', ['transformExpense']); + const expensesServiceSpy = jasmine.createSpyObj('ExpensesService', ['getExpenseById']); const deepLinkServiceSpy = jasmine.createSpyObj('DeepLinkService', ['getExpenseRoute']); TestBed.configureTestingModule({ @@ -208,6 +212,10 @@ describe('SwitchOrgPage', () => { provide: TransactionService, useValue: transactionServiceSpy, }, + { + provide: ExpensesService, + useValue: expensesServiceSpy, + }, { provide: DeepLinkService, useValue: deepLinkServiceSpy, @@ -241,6 +249,7 @@ describe('SwitchOrgPage', () => { routerAuthService = TestBed.inject(RouterAuthService) as jasmine.SpyObj; deepLinkService = TestBed.inject(DeepLinkService) as jasmine.SpyObj; transactionService = TestBed.inject(TransactionService) as jasmine.SpyObj; + expensesService = TestBed.inject(ExpensesService) as jasmine.SpyObj; component.searchRef = fixture.debugElement.query(By.css('#search')); component.searchOrgsInput = fixture.debugElement.query(By.css('.smartlook-show')); @@ -350,7 +359,8 @@ describe('SwitchOrgPage', () => { userEventService.clearTaskCache.and.returnValue(); recentLocalStorageItemsService.clearRecentLocalStorageCache.and.returnValue(); authService.getEou.and.resolveTo(apiEouRes); - transactionService.getETxnUnflattened.and.returnValue(of(unflattenedTxnData)); + expensesService.getExpenseById.and.returnValue(of(platformExpenseData)); + transactionService.transformExpense.and.returnValue(transformedExpenseData); deepLinkService.getExpenseRoute.and.returnValue(['/', 'enterprise', 'add_edit_expense']); spyOn(component, 'setSentryUser').and.returnValue(); @@ -358,7 +368,7 @@ describe('SwitchOrgPage', () => { }); it('should redirect to expense page if txn found in org', fakeAsync(() => { - const txnId = 'tx3qHxFNgRcZ'; + const txnId = 'txvslh8aQMbu'; const orgId = 'orNVthTo2Zyo'; component.redirectToExpensePage(orgId, txnId); @@ -373,10 +383,11 @@ describe('SwitchOrgPage', () => { expect(authService.getEou).toHaveBeenCalledOnceWith(); expect(component.setSentryUser).toHaveBeenCalledOnceWith(apiEouRes); - expect(transactionService.getETxnUnflattened).toHaveBeenCalledOnceWith(txnId); + expect(expensesService.getExpenseById).toHaveBeenCalledOnceWith(txnId); + expect(transactionService.transformExpense).toHaveBeenCalledOnceWith(platformExpenseData); expect(loaderService.hideLoader).toHaveBeenCalledOnceWith(); - expect(deepLinkService.getExpenseRoute).toHaveBeenCalledOnceWith(unflattenedTxnData); + expect(deepLinkService.getExpenseRoute).toHaveBeenCalledOnceWith(transformedExpenseData); expect(router.navigate).toHaveBeenCalledOnceWith(['/', 'enterprise', 'add_edit_expense', { id: txnId }]); })); diff --git a/src/app/auth/switch-org/switch-org.page.ts b/src/app/auth/switch-org/switch-org.page.ts index 8903dcdc00..8d80770398 100644 --- a/src/app/auth/switch-org/switch-org.page.ts +++ b/src/app/auth/switch-org/switch-org.page.ts @@ -29,6 +29,8 @@ import { ResendEmailVerification } from 'src/app/core/models/resend-email-verifi import { RouterAuthService } from 'src/app/core/services/router-auth.service'; import { TransactionService } from 'src/app/core/services/transaction.service'; import { DeepLinkService } from 'src/app/core/services/deep-link.service'; +import { UnflattenedTransaction } from 'src/app/core/models/unflattened-transaction.model'; +import { ExpensesService } from 'src/app/core/services/platform/v1/spender/expenses.service'; @Component({ selector: 'app-switch-org', @@ -82,7 +84,8 @@ export class SwitchOrgPage implements OnInit, AfterViewChecked { private snackbarProperties: SnackbarPropertiesService, private routerAuthService: RouterAuthService, private transactionService: TransactionService, - private deepLinkService: DeepLinkService + private deepLinkService: DeepLinkService, + private expensesService: ExpensesService ) {} ngOnInit() { @@ -191,8 +194,9 @@ export class SwitchOrgPage implements OnInit, AfterViewChecked { }), switchMap((eou) => { this.setSentryUser(eou); - return this.transactionService.getETxnUnflattened(txnId); + return this.expensesService.getExpenseById(txnId); }), + map((expense) => this.transactionService.transformExpense(expense)), finalize(() => this.loaderService.hideLoader()) ) .subscribe({ diff --git a/src/app/core/mock-data/corporate-card-transaction-response.data.ts b/src/app/core/mock-data/corporate-card-transaction-response.data.ts new file mode 100644 index 0000000000..02d5424425 --- /dev/null +++ b/src/app/core/mock-data/corporate-card-transaction-response.data.ts @@ -0,0 +1,261 @@ +import { CorporateCardTransactionRes } from '../models/platform/v1/corporate-card-transaction-res.model'; +import { TransactionStatus } from '../models/platform/v1/expense.model'; + +export const ccTransactionResponseData: CorporateCardTransactionRes = { + data: { + amount: 205.21, + assignor_user: null, + assignor_user_id: null, + auto_suggested_expense_ids: [], + can_delete: false, + category: null, + code: '6c4a6dcb15a94c0e976dcd1a507dcfd0', + corporate_card: { + bank_name: 'DSR', + card_number: '7620', + id: 'bacck9WlgA11Uh', + masked_number: '7620', + user_email: 'devendra.r@fyle.in', + user_full_name: 'Devendra Singh Rana', + }, + corporate_card_id: 'bacck9WlgA11Uh', + created_at: new Date('2024-01-23T12:17:34.473632+00:00'), + currency: 'USD', + description: null, + foreign_amount: null, + foreign_currency: null, + id: 'btxnBdS2Kpvzhy', + is_assigned: true, + is_auto_matched: true, + is_dismissed: false, + is_exported: false, + is_marked_personal: true, + last_assigned_at: new Date('2024-01-23T12:18:51.470532+00:00'), + last_auto_matched_at: new Date('2024-01-23T12:20:06.279340+00:00'), + last_dismissed_at: null, + last_marked_personal_at: new Date('2024-02-12T12:36:16.437731+00:00'), + last_user_matched_at: null, + matched_expense_ids: [], + matched_expenses: [], + mcc: null, + merchant: 'test description', + metadata: { + merchant_category_code: '', + flight_merchant_category_code: '', + flight_supplier_name: '', + flight_travel_agency_name: '', + flight_ticket_number: '', + flight_total_fare: 0, + flight_travel_date: undefined, + flight_service_class: '', + flight_carrier_code: '', + flight_fare_base_code: '', + flight_trip_leg_number: '', + hotel_merchant_category_code: '', + hotel_supplier_name: '', + hotel_checked_in_at: undefined, + hotel_nights: 0, + hotel_checked_out_at: undefined, + hotel_country: '', + hotel_city: '', + hotel_total_fare: 0, + fleet_product_merchant_category_code: '', + fleet_product_supplier_name: '', + fleet_service_merchant_category_code: '', + fleet_service_supplier_name: '', + car_rental_merchant_category_code: '', + car_rental_supplier_name: '', + car_rental_started_at: undefined, + car_rental_days: 0, + car_rental_ended_at: undefined, + general_ticket_issued_at: undefined, + general_ticket_number: '', + general_issuing_carrier: '', + general_travel_agency_name: '', + general_travel_agency_code: '', + general_ticket_total_fare: 0, + general_ticket_total_tax: 0, + merchant_address: '', + }, + org_id: 'oroLKHBYQVvj', + post_date: null, + settlement_id: null, + spent_at: new Date('2018-06-06T00:00:00+00:00'), + statement_id: 'stmt2K9aLunGU4', + updated_at: new Date('2024-02-12T12:36:16.437742+00:00'), + user: { + email: 'devendra.r@fyle.in', + full_name: 'Devendra Singh Rana', + id: 'usvMoPfCC9Xw', + }, + user_id: 'usvMoPfCC9Xw', + }, +}; + +export const ccTransactionResponseData1: CorporateCardTransactionRes = { + data: { + ...ccTransactionResponseData.data, + id: 'btxnSte7sVQCM8', + }, +}; + +export const unmatchCCCExpenseResponseData: CorporateCardTransactionRes = { + data: { + amount: 260.37, + assignor_user: null, + assignor_user_id: null, + auto_suggested_expense_ids: [], + can_delete: true, + category: null, + code: 'b1d89f85d1f44a22981e4b4c8b1af435', + corporate_card: { + bank_name: 'DSR', + card_number: '7620', + id: 'bacck9WlgA11Uh', + masked_number: '7620', + user_email: 'devendra.r@fyle.in', + user_full_name: 'Devendra Singh Rana', + }, + corporate_card_id: 'bacck9WlgA11Uh', + created_at: new Date('2024-01-23T12:17:31.316675+00:00'), + currency: 'USD', + description: null, + foreign_amount: null, + foreign_currency: null, + id: 'btxnSte7sVQCM8', + is_assigned: true, + is_auto_matched: false, + is_dismissed: false, + is_exported: false, + is_marked_personal: false, + last_assigned_at: new Date('2024-01-23T12:18:51.470532+00:00'), + last_auto_matched_at: new Date('2024-01-23T12:19:50.298547+00:00'), + last_dismissed_at: null, + last_marked_personal_at: null, + last_user_matched_at: new Date('2024-02-13T03:10:49.432011+00:00'), + matched_expense_ids: ['txmF3wgfj0Bs'], + matched_expenses: [ + { + amount: 260.37, + category_display_name: 'Unspecified', + currency: 'USD', + foreign_amount: null, + foreign_currency: null, + id: 'txmF3wgfj0Bs', + merchant: 'test description', + no_of_files: 0, + purpose: null, + seq_num: 'E/2024/01/T/39', + spent_at: new Date('2018-07-04T00:00:00+00:00'), + state: 'DRAFT', + }, + ], + mcc: null, + merchant: 'test description', + metadata: { + merchant_category_code: '', + flight_merchant_category_code: '', + flight_supplier_name: '', + flight_travel_agency_name: '', + flight_ticket_number: '', + flight_total_fare: 0, + flight_travel_date: undefined, + flight_service_class: '', + flight_carrier_code: '', + flight_fare_base_code: '', + flight_trip_leg_number: '', + hotel_merchant_category_code: '', + hotel_supplier_name: '', + hotel_checked_in_at: undefined, + hotel_nights: 0, + hotel_checked_out_at: undefined, + hotel_country: '', + hotel_city: '', + hotel_total_fare: 0, + fleet_product_merchant_category_code: '', + fleet_product_supplier_name: '', + fleet_service_merchant_category_code: '', + fleet_service_supplier_name: '', + car_rental_merchant_category_code: '', + car_rental_supplier_name: '', + car_rental_started_at: undefined, + car_rental_days: 0, + car_rental_ended_at: undefined, + general_ticket_issued_at: undefined, + general_ticket_number: '', + general_issuing_carrier: '', + general_travel_agency_name: '', + general_travel_agency_code: '', + general_ticket_total_fare: 0, + general_ticket_total_tax: 0, + merchant_address: '', + }, + org_id: 'oroLKHBYQVvj', + post_date: null, + settlement_id: null, + spent_at: new Date('2018-07-04T00:00:00+00:00'), + statement_id: 'stmt2K9aLunGU4', + transaction_status: TransactionStatus.PENDING, + updated_at: new Date('2024-02-13T03:10:49.432028+00:00'), + user: { + email: 'devendra.r@fyle.in', + full_name: 'Devendra Singh Rana', + id: 'usvMoPfCC9Xw', + }, + user_id: 'usvMoPfCC9Xw', + }, +}; + +export const matchCCCExpenseResponseData: CorporateCardTransactionRes = { + data: { + amount: 260.37, + assignor_user: null, + assignor_user_id: null, + auto_suggested_expense_ids: [], + can_delete: true, + category: null, + code: 'b1d89f85d1f44a22981e4b4c8b1af435', + corporate_card: { + bank_name: 'DSR', + card_number: '7620', + id: 'bacck9WlgA11Uh', + masked_number: '7620', + user_email: 'devendra.r@fyle.in', + user_full_name: 'Devendra Singh Rana', + }, + corporate_card_id: 'bacck9WlgA11Uh', + created_at: new Date('2024-01-23T12:17:31.316675+00:00'), + currency: 'USD', + description: null, + foreign_amount: null, + foreign_currency: null, + id: 'btxnSte7sVQCM8', + is_assigned: true, + is_auto_matched: true, + is_dismissed: false, + is_exported: false, + is_marked_personal: false, + last_assigned_at: new Date('2024-01-23T12:18:51.470532+00:00'), + last_auto_matched_at: new Date('2024-01-23T12:19:50.298547+00:00'), + last_dismissed_at: null, + last_marked_personal_at: null, + last_user_matched_at: null, + matched_expense_ids: [], + matched_expenses: [], + mcc: null, + merchant: 'test description', + org_id: 'oroLKHBYQVvj', + post_date: null, + settlement_id: null, + spent_at: new Date('2018-07-04T00:00:00+00:00'), + statement_id: 'stmt2K9aLunGU4', + transaction_status: TransactionStatus.PENDING, + updated_at: new Date('2024-02-13T03:10:48.454767+00:00'), + user: { + email: 'devendra.r@fyle.in', + full_name: 'Devendra Singh Rana', + id: 'usvMoPfCC9Xw', + }, + user_id: 'usvMoPfCC9Xw', + }, +}; diff --git a/src/app/core/mock-data/duplicate-sets.data.ts b/src/app/core/mock-data/duplicate-sets.data.ts index d0c00e3735..ae2bae13cd 100644 --- a/src/app/core/mock-data/duplicate-sets.data.ts +++ b/src/app/core/mock-data/duplicate-sets.data.ts @@ -18,3 +18,12 @@ export const duplicateSetData3: DuplicateSet = { export const duplicateSetData4: DuplicateSet = { transaction_ids: ['tx5fBcPBAxLv'], }; + +export const duplicateSetData5: DuplicateSet = { + fields: ['field1', 'field2'], + transaction_ids: ['txal5xGjbZ1R'], +}; + +export const duplicateSetData6: DuplicateSet = { + transaction_ids: ['txal5xGjbZ1R'], +}; diff --git a/src/app/core/mock-data/expense.data.ts b/src/app/core/mock-data/expense.data.ts index ce3251c2c4..1ee51fb177 100644 --- a/src/app/core/mock-data/expense.data.ts +++ b/src/app/core/mock-data/expense.data.ts @@ -5818,3 +5818,312 @@ export const expenseListwithoutID: Expense[] = [ { ...expenseList4[0], tx_id: null }, { ...expenseList4[1], tx_id: null }, ]; + +export const splitExpTransformedData: Partial[] = [ + { + tx_id: 'txal5xGjbZ1R', + tx_txn_dt: new Date('2024-01-31T06:30:00.000Z'), + tx_expense_number: 'E/2024/03/T/7', + tx_num_files: 0, + tx_org_category: 'Entertainment', + tx_amount: 7.2, + tx_purpose: null, + tx_currency: 'USD', + tx_vendor: 'Uber', + tx_split_group_id: 'tx6I9xcOZFU6', + tx_split_group_user_amount: 12, + tx_skip_reimbursement: true, + tx_file_ids: [], + tx_creator_id: 'ouokfwl9OEdl', + tx_state: 'COMPLETE', + tx_project_id: null, + tx_cost_center_id: null, + tx_corporate_credit_card_expense_group_id: null, + tx_custom_properties: [ + { + is_enabled: true, + name: 'Custom Field', + type: 'TEXT', + value: null, + }, + ], + tx_locations: [], + tx_hotel_is_breakfast_provided: false, + tx_billable: null, + tx_fyle_category: 'Entertainment', + tx_orig_currency: null, + tx_orig_amount: null, + tx_distance: null, + tx_distance_unit: null, + tx_num_days: null, + tx_mileage_rate_id: null, + tx_mileage_is_round_trip: null, + tx_mileage_calculated_distance: null, + tx_mileage_calculated_amount: null, + tx_manual_flag: null, + tx_policy_flag: null, + tx_extracted_data: null, + tx_matched_corporate_card_transactions: [], + tx_org_category_code: null, + tx_physical_bill: null, + tx_physical_bill_at: null, + source_account_id: 'acc7F6bwRa52p', + source_account_type: 'PERSONAL_CORPORATE_CREDIT_CARD_ACCOUNT', + }, + { + tx_id: 'txQNInZMIHgZ', + tx_txn_dt: new Date('2024-01-31T06:30:00.000Z'), + tx_expense_number: 'E/2024/03/T/6', + tx_num_files: 0, + tx_org_category: 'Food', + tx_amount: 4.8, + tx_purpose: null, + tx_currency: 'USD', + tx_vendor: 'Uber', + tx_split_group_id: 'tx6I9xcOZFU6', + tx_split_group_user_amount: 12, + tx_skip_reimbursement: true, + tx_file_ids: [], + tx_creator_id: 'ouokfwl9OEdl', + tx_state: 'COMPLETE', + tx_project_id: null, + tx_cost_center_id: null, + tx_corporate_credit_card_expense_group_id: null, + tx_custom_properties: [ + { + is_enabled: true, + name: 'Custom Field', + type: 'TEXT', + value: null, + }, + ], + tx_locations: [], + tx_hotel_is_breakfast_provided: false, + tx_billable: null, + tx_fyle_category: 'Food', + tx_orig_currency: null, + tx_orig_amount: null, + tx_distance: null, + tx_distance_unit: null, + tx_num_days: null, + tx_mileage_rate_id: null, + tx_mileage_is_round_trip: null, + tx_mileage_calculated_distance: null, + tx_mileage_calculated_amount: null, + tx_manual_flag: null, + tx_policy_flag: null, + tx_extracted_data: null, + tx_matched_corporate_card_transactions: [], + tx_org_category_code: null, + tx_physical_bill: null, + tx_physical_bill_at: null, + source_account_id: 'acc7F6bwRa52p', + source_account_type: 'PERSONAL_CORPORATE_CREDIT_CARD_ACCOUNT', + }, +]; + +export const splitExpTransformedData1: Partial[] = [ + { + tx_id: 'txQNInZMIHgZ', + tx_txn_dt: new Date('2024-01-31T06:30:00.000Z'), + tx_expense_number: 'E/2024/03/T/6', + tx_num_files: 0, + tx_org_category: 'Food', + tx_amount: 4.8, + tx_purpose: null, + tx_currency: 'USD', + tx_vendor: 'Uber', + tx_split_group_id: 'tx6I9xcOZFU6', + tx_split_group_user_amount: 12, + tx_skip_reimbursement: true, + tx_file_ids: [], + tx_creator_id: 'ouokfwl9OEdl', + tx_state: 'COMPLETE', + tx_project_id: null, + tx_cost_center_id: null, + tx_corporate_credit_card_expense_group_id: null, + tx_custom_properties: [ + { + is_enabled: true, + name: 'Custom Field', + type: 'TEXT', + value: null, + }, + ], + tx_locations: [], + tx_hotel_is_breakfast_provided: false, + tx_billable: null, + tx_fyle_category: 'Food', + tx_orig_currency: null, + tx_orig_amount: null, + tx_distance: null, + tx_distance_unit: null, + tx_num_days: null, + tx_mileage_rate_id: null, + tx_mileage_is_round_trip: null, + tx_mileage_calculated_distance: null, + tx_mileage_calculated_amount: null, + tx_manual_flag: null, + tx_policy_flag: null, + tx_extracted_data: null, + tx_matched_corporate_card_transactions: [], + tx_org_category_code: null, + tx_physical_bill: null, + tx_physical_bill_at: null, + source_account_id: 'acc7F6bwRa52p', + source_account_type: 'PERSONAL_CORPORATE_CREDIT_CARD_ACCOUNT', + }, + { + tx_id: 'txQNInZMIHgZ', + tx_txn_dt: new Date('2024-01-31T06:30:00.000Z'), + tx_expense_number: 'E/2024/03/T/6', + tx_num_files: 0, + tx_org_category: 'Food', + tx_amount: 4.8, + tx_purpose: null, + tx_currency: 'USD', + tx_vendor: 'Uber', + tx_split_group_id: 'tx6I9xcOZFU6', + tx_split_group_user_amount: 12, + tx_skip_reimbursement: true, + tx_file_ids: [], + tx_creator_id: 'ouokfwl9OEdl', + tx_state: 'COMPLETE', + tx_project_id: null, + tx_cost_center_id: null, + tx_corporate_credit_card_expense_group_id: null, + tx_custom_properties: [ + { + is_enabled: true, + name: 'Custom Field', + type: 'TEXT', + value: null, + }, + ], + tx_locations: [], + tx_hotel_is_breakfast_provided: false, + tx_billable: null, + tx_fyle_category: 'Food', + tx_orig_currency: null, + tx_orig_amount: null, + tx_distance: null, + tx_distance_unit: null, + tx_num_days: null, + tx_mileage_rate_id: null, + tx_mileage_is_round_trip: null, + tx_mileage_calculated_distance: null, + tx_mileage_calculated_amount: null, + tx_manual_flag: null, + tx_policy_flag: null, + tx_extracted_data: null, + tx_matched_corporate_card_transactions: [], + tx_org_category_code: null, + tx_physical_bill: null, + tx_physical_bill_at: null, + source_account_id: 'acc7F6bwRa52p', + source_account_type: 'PERSONAL_CORPORATE_CREDIT_CARD_ACCOUNT', + }, +]; + +export const transformedPlatformedExpense: Partial = splitExpTransformedData[0]; + +export const transformedPlatformedExpense1: Partial[] = [ + { + tx_id: 'txZA0Oj6TV9c', + tx_txn_dt: new Date('2024-01-31T11:30:00.000Z'), + tx_expense_number: 'E/2024/03/T/5', + tx_num_files: 0, + tx_org_category: 'Bus', + tx_amount: 4.8, + tx_purpose: null, + tx_currency: 'USD', + tx_vendor: null, + tx_split_group_id: 'tx9mWbP68zQX', + tx_split_group_user_amount: 12, + tx_skip_reimbursement: true, + tx_file_ids: [], + tx_creator_id: 'ouokfwl9OEdl', + tx_state: 'COMPLETE', + tx_project_id: null, + tx_cost_center_id: null, + tx_corporate_credit_card_expense_group_id: null, + tx_custom_properties: [ + { + is_enabled: true, + name: 'Custom Field', + type: 'TEXT', + value: null, + }, + ], + tx_locations: [], + tx_hotel_is_breakfast_provided: false, + tx_billable: null, + tx_fyle_category: 'Bus', + tx_orig_currency: null, + tx_orig_amount: null, + tx_distance: null, + tx_distance_unit: null, + tx_num_days: null, + tx_mileage_is_round_trip: null, + tx_mileage_calculated_distance: null, + tx_mileage_calculated_amount: null, + tx_manual_flag: null, + tx_policy_flag: null, + tx_extracted_data: null, + tx_matched_corporate_card_transactions: [], + tx_org_category_code: null, + tx_physical_bill: null, + tx_physical_bill_at: null, + source_account_id: 'acc7F6bwRa52p', + source_account_type: 'PERSONAL_CORPORATE_CREDIT_CARD_ACCOUNT', + }, + { + tx_id: 'txZA0Oj6TV9c', + tx_txn_dt: new Date('2024-01-31T11:30:00.000Z'), + tx_expense_number: 'E/2024/03/T/5', + tx_num_files: 0, + tx_org_category: 'Bus', + tx_amount: 4.8, + tx_purpose: null, + tx_currency: 'USD', + tx_vendor: null, + tx_split_group_id: 'tx9mWbP68zQX', + tx_split_group_user_amount: 12, + tx_skip_reimbursement: true, + tx_file_ids: [], + tx_creator_id: 'ouokfwl9OEdl', + tx_state: 'COMPLETE', + tx_project_id: null, + tx_cost_center_id: null, + tx_corporate_credit_card_expense_group_id: null, + tx_custom_properties: [ + { + is_enabled: true, + name: 'Custom Field', + type: 'TEXT', + value: null, + }, + ], + tx_locations: [], + tx_hotel_is_breakfast_provided: false, + tx_billable: null, + tx_fyle_category: 'Bus', + tx_orig_currency: null, + tx_orig_amount: null, + tx_distance: null, + tx_distance_unit: null, + tx_num_days: null, + tx_mileage_is_round_trip: null, + tx_mileage_calculated_distance: null, + tx_mileage_calculated_amount: null, + tx_manual_flag: null, + tx_policy_flag: null, + tx_extracted_data: null, + tx_matched_corporate_card_transactions: [], + tx_org_category_code: null, + tx_physical_bill: null, + tx_physical_bill_at: null, + source_account_id: 'acc7F6bwRa52p', + source_account_type: 'PERSONAL_CORPORATE_CREDIT_CARD_ACCOUNT', + }, +]; diff --git a/src/app/core/mock-data/matchedCCTransaction.data.ts b/src/app/core/mock-data/matchedCCTransaction.data.ts new file mode 100644 index 0000000000..68da126798 --- /dev/null +++ b/src/app/core/mock-data/matchedCCTransaction.data.ts @@ -0,0 +1,34 @@ +import { MatchedCCCTransaction } from '../models/matchedCCCTransaction.model'; +import { TransactionStatus } from '../models/platform/v1/expense.model'; + +export const matchedCCTransactionData: Partial = { + id: 'btxnSte7sVQCM8', + group_id: 'btxnSte7sVQCM8', + amount: 260.37, + vendor: 'test description', + txn_dt: '2018-07-03T13:00:00.000Z', + currency: 'USD', + description: null, + card_or_account_number: '7620', + corporate_credit_card_account_number: '7620', + orig_amount: null, + orig_currency: null, + status: TransactionStatus.PENDING, + displayObject: 'Jul 3, 2018 - test description260.37', +}; + +export const matchedCCTransactionData2: Partial = { + id: 'btxnBdS2Kpvzhy', + group_id: 'btxnBdS2Kpvzhy', + amount: 205.21, + vendor: 'test description', + txn_dt: '2018-06-06T08:30:00.000Z', + currency: 'USD', + description: null, + card_or_account_number: '9891', + corporate_credit_card_account_number: '9891', + orig_amount: null, + orig_currency: null, + status: TransactionStatus.PENDING, + displayObject: 'Jun 6, 2018 - test description205.21', +}; diff --git a/src/app/core/mock-data/per-diem-form-value.data.ts b/src/app/core/mock-data/per-diem-form-value.data.ts index 4e4f3bb8fd..9d8a49cacd 100644 --- a/src/app/core/mock-data/per-diem-form-value.data.ts +++ b/src/app/core/mock-data/per-diem-form-value.data.ts @@ -17,7 +17,6 @@ export const perDiemFormValuesData1: Partial = { from_dt: null, to_dt: null, billable: null, - duplicate_detection_reason: null, costCenter: undefined, project: projects[0], custom_inputs: [], @@ -79,7 +78,6 @@ export const perDiemFormValuesData8: PerDiemFormValue = { from_dt: '2023-08-01', to_dt: '2023-08-03', billable: true, - duplicate_detection_reason: null, costCenter: costCentersData2[0], project: projects[0], custom_inputs: [], @@ -110,7 +108,6 @@ export const perDiemFormValuesData9: PerDiemFormValue = { from_dt: '2023-08-01', to_dt: '2023-08-03', billable: true, - duplicate_detection_reason: null, costCenter: costCentersData2[0], project: projects[0], custom_inputs: [], diff --git a/src/app/core/mock-data/platform/v1/expense-duplicate-sets.data.ts b/src/app/core/mock-data/platform/v1/expense-duplicate-sets.data.ts index da0c48b72a..3a46d00386 100644 --- a/src/app/core/mock-data/platform/v1/expense-duplicate-sets.data.ts +++ b/src/app/core/mock-data/platform/v1/expense-duplicate-sets.data.ts @@ -8,6 +8,10 @@ export const expenseDuplicateSet2: ExpenseDuplicateSet = { expense_ids: ['tx5fBcPBAxLv'], }; +export const expenseDuplicateSet3: ExpenseDuplicateSet = { + expense_ids: ['txal5xGjbZ1R'], +}; + export const expenseDuplicateSets: ExpenseDuplicateSet[] = [ { expense_ids: ['tx3I0ccSGlhg', 'txvAmVCGZUZi'] }, { expense_ids: ['tx3rq5G9gzgf', 'txS1cDov9iZn'] }, diff --git a/src/app/core/mock-data/platform/v1/expense.data.ts b/src/app/core/mock-data/platform/v1/expense.data.ts index dddf5d21c1..c0ae9185f0 100644 --- a/src/app/core/mock-data/platform/v1/expense.data.ts +++ b/src/app/core/mock-data/platform/v1/expense.data.ts @@ -1770,3 +1770,1395 @@ export const draftExpense: Expense = { ...expenseData, state: ExpenseState.DRAFT, }; + +export const splitExpensesData: Expense[] = [ + { + accounting_export_summary: {}, + added_to_report_at: null, + admin_amount: null, + amount: 7.2, + approvals: [], + approver_comments: [], + category: { + code: null, + display_name: 'Entertainment', + id: 283896, + name: 'Entertainment', + sub_category: null, + system_category: 'Entertainment', + }, + category_id: 283896, + claim_amount: 7.2, + code: null, + cost_center: null, + cost_center_id: null, + created_at: new Date('2024-03-03T06:23:36.416Z'), + creator_user_id: 'usvMoPfCC9Xw', + currency: 'USD', + custom_fields: [ + { + is_enabled: true, + name: 'Custom Field', + type: 'TEXT', + value: null, + }, + ], + custom_fields_flattened: { + custom_field: null, + }, + distance: null, + distance_unit: null, + employee: { + business_unit: null, + code: null, + custom_fields: [], + department: null, + department_id: null, + flattened_custom_field: {}, + has_accepted_invite: true, + id: 'ouokfwl9OEdl', + is_enabled: true, + joined_at: null, + level: null, + location: null, + mobile: null, + org_id: 'oroLKHBYQVvj', + org_name: 'CC Transaction Testing', + title: null, + user: { + email: 'devendra.r@fyle.in', + full_name: 'Devendra Singh Rana', + id: 'usvMoPfCC9Xw', + }, + user_id: 'usvMoPfCC9Xw', + }, + employee_id: 'ouokfwl9OEdl', + ended_at: null, + expense_rule_data: null, + expense_rule_id: null, + extracted_data: null, + file_ids: [], + files: [], + foreign_amount: null, + foreign_currency: null, + hotel_is_breakfast_provided: false, + id: 'txal5xGjbZ1R', + invoice_number: null, + is_billable: null, + is_corporate_card_transaction_auto_matched: false, + is_exported: null, + is_manually_flagged: null, + is_physical_bill_submitted: null, + is_policy_flagged: null, + is_receipt_mandatory: null, + is_reimbursable: false, + is_split: true, + is_verified: false, + is_weekend_spend: false, + last_exported_at: null, + last_settled_at: null, + last_verified_at: null, + locations: [], + matched_corporate_card_transaction_ids: [], + matched_corporate_card_transactions: [], + merchant: 'Uber', + mileage_calculated_amount: null, + mileage_calculated_distance: null, + mileage_is_round_trip: null, + mileage_rate: null, + mileage_rate_id: null, + missing_mandatory_fields: { + expense_field_ids: [], + amount: false, + currency: false, + receipt: false, + }, + org_id: 'oroLKHBYQVvj', + per_diem_num_days: null, + per_diem_rate: null, + per_diem_rate_id: null, + physical_bill_submitted_at: null, + policy_amount: null, + policy_checks: { + are_approvers_added: false, + is_amount_limit_applied: false, + is_flagged_ever: false, + violations: [], + }, + project: null, + project_id: null, + purpose: null, + report: null, + report_id: null, + report_last_approved_at: null, + report_last_paid_at: null, + report_settlement_id: null, + seq_num: 'E/2024/03/T/7', + source: 'MOBILE', + source_account: { + id: 'acc7F6bwRa52p', + type: AccountType.PERSONAL_CORPORATE_CREDIT_CARD_ACCOUNT, + }, + source_account_id: 'acc7F6bwRa52p', + spent_at: new Date('2024-01-31T06:30:00.000Z'), + split_group_amount: 12, + split_group_id: 'tx6I9xcOZFU6', + started_at: null, + state: ExpenseState.COMPLETE, + state_display_name: 'Complete', + tax_amount: null, + tax_group: null, + tax_group_id: null, + travel_classes: [], + updated_at: new Date('2024-03-03T06:23:38.568Z'), + user: { + email: 'devendra.r@fyle.in', + full_name: 'Devendra Singh Rana', + id: 'usvMoPfCC9Xw', + }, + user_id: 'usvMoPfCC9Xw', + verifications: [], + verifier_comments: [], + }, + { + accounting_export_summary: {}, + added_to_report_at: null, + admin_amount: null, + amount: 4.8, + approvals: [], + approver_comments: [], + category: { + code: null, + display_name: 'Food', + id: 283899, + name: 'Food', + sub_category: null, + system_category: 'Food', + }, + category_id: 283899, + claim_amount: 4.8, + code: null, + cost_center: null, + cost_center_id: null, + created_at: new Date('2024-03-03T06:23:36.413Z'), + creator_user_id: 'usvMoPfCC9Xw', + currency: 'USD', + custom_fields: [ + { + is_enabled: true, + name: 'Custom Field', + type: 'TEXT', + value: null, + }, + ], + custom_fields_flattened: { + custom_field: null, + }, + distance: null, + distance_unit: null, + employee: { + business_unit: null, + code: null, + custom_fields: [], + department: null, + department_id: null, + flattened_custom_field: {}, + has_accepted_invite: true, + id: 'ouokfwl9OEdl', + is_enabled: true, + joined_at: null, + level: null, + location: null, + mobile: null, + org_id: 'oroLKHBYQVvj', + org_name: 'CC Transaction Testing', + title: null, + user: { + email: 'devendra.r@fyle.in', + full_name: 'Devendra Singh Rana', + id: 'usvMoPfCC9Xw', + }, + user_id: 'usvMoPfCC9Xw', + }, + employee_id: 'ouokfwl9OEdl', + ended_at: null, + expense_rule_data: null, + expense_rule_id: null, + extracted_data: null, + file_ids: [], + files: [], + foreign_amount: null, + foreign_currency: null, + hotel_is_breakfast_provided: false, + id: 'txQNInZMIHgZ', + invoice_number: null, + is_billable: null, + is_corporate_card_transaction_auto_matched: false, + is_exported: null, + is_manually_flagged: null, + is_physical_bill_submitted: null, + is_policy_flagged: null, + is_receipt_mandatory: null, + is_reimbursable: false, + is_split: true, + is_verified: false, + is_weekend_spend: false, + last_exported_at: null, + last_settled_at: null, + last_verified_at: null, + locations: [], + matched_corporate_card_transaction_ids: [], + matched_corporate_card_transactions: [], + merchant: 'Uber', + mileage_calculated_amount: null, + mileage_calculated_distance: null, + mileage_is_round_trip: null, + mileage_rate: null, + mileage_rate_id: null, + missing_mandatory_fields: { + expense_field_ids: [], + amount: false, + currency: false, + receipt: false, + }, + org_id: 'oroLKHBYQVvj', + per_diem_num_days: null, + per_diem_rate: null, + per_diem_rate_id: null, + physical_bill_submitted_at: null, + policy_amount: null, + policy_checks: { + are_approvers_added: false, + is_amount_limit_applied: false, + is_flagged_ever: false, + violations: [], + }, + project: null, + project_id: null, + purpose: null, + report: null, + report_id: null, + report_last_approved_at: null, + report_last_paid_at: null, + report_settlement_id: null, + seq_num: 'E/2024/03/T/6', + source: 'MOBILE', + source_account: { + id: 'acc7F6bwRa52p', + type: AccountType.PERSONAL_CORPORATE_CREDIT_CARD_ACCOUNT, + }, + source_account_id: 'acc7F6bwRa52p', + spent_at: new Date('2024-01-31T06:30:00.000Z'), + split_group_amount: 12, + split_group_id: 'tx6I9xcOZFU6', + started_at: null, + state: ExpenseState.COMPLETE, + state_display_name: 'Complete', + tax_amount: null, + tax_group: null, + tax_group_id: null, + travel_classes: [], + updated_at: new Date('2024-03-03T06:23:37.993Z'), + user: { + email: 'devendra.r@fyle.in', + full_name: 'Devendra Singh Rana', + id: 'usvMoPfCC9Xw', + }, + user_id: 'usvMoPfCC9Xw', + verifications: [], + verifier_comments: [], + }, +]; + +export const apiExpenses2: Expense[] = [splitExpensesData[0]]; + +export const apiExpenses3: Expense[] = [ + { + accounting_export_summary: {}, + added_to_report_at: null, + admin_amount: null, + amount: 4.8, + approvals: [], + approver_comments: [], + category: { + code: null, + display_name: 'Food', + id: 283899, + name: 'Food', + sub_category: null, + system_category: 'Food', + }, + category_id: 283899, + claim_amount: 4.8, + code: null, + cost_center: null, + cost_center_id: null, + created_at: new Date('2024-03-03T06:23:36.413Z'), + creator_user_id: 'ouokfwl9OEdl', + currency: 'USD', + custom_fields: [ + { + is_enabled: true, + name: 'Custom Field', + type: 'TEXT', + value: null, + }, + ], + custom_fields_flattened: { + custom_field: null, + }, + distance: null, + distance_unit: null, + employee: { + business_unit: null, + code: null, + custom_fields: [], + department: null, + department_id: null, + flattened_custom_field: {}, + has_accepted_invite: true, + id: 'ouokfwl9OEdl', + is_enabled: true, + joined_at: null, + level: null, + location: null, + mobile: null, + org_id: 'oroLKHBYQVvj', + org_name: 'CC Transaction Testing', + title: null, + user: { + email: 'devendra.r@fyle.in', + full_name: 'Devendra Singh Rana', + id: 'usvMoPfCC9Xw', + }, + user_id: 'usvMoPfCC9Xw', + }, + employee_id: 'ouokfwl9OEdl', + ended_at: null, + expense_rule_data: null, + expense_rule_id: null, + extracted_data: null, + file_ids: [], + files: [], + foreign_amount: null, + foreign_currency: null, + hotel_is_breakfast_provided: false, + id: 'txQNInZMIHgZ', + invoice_number: null, + is_billable: null, + is_corporate_card_transaction_auto_matched: false, + is_exported: null, + is_manually_flagged: null, + is_physical_bill_submitted: null, + is_policy_flagged: null, + is_receipt_mandatory: null, + is_reimbursable: false, + is_split: true, + is_verified: false, + is_weekend_spend: false, + last_exported_at: null, + last_settled_at: null, + last_verified_at: null, + locations: [], + matched_corporate_card_transaction_ids: [], + matched_corporate_card_transactions: [], + merchant: 'Uber', + mileage_calculated_amount: null, + mileage_calculated_distance: null, + mileage_is_round_trip: null, + mileage_rate: null, + mileage_rate_id: null, + missing_mandatory_fields: { + amount: false, + currency: false, + expense_field_ids: [], + receipt: false, + }, + org_id: 'oroLKHBYQVvj', + per_diem_num_days: null, + per_diem_rate: null, + per_diem_rate_id: null, + physical_bill_submitted_at: null, + policy_amount: null, + policy_checks: { + are_approvers_added: false, + is_amount_limit_applied: false, + is_flagged_ever: false, + violations: null, + }, + project: null, + project_id: null, + purpose: null, + report: null, + report_id: null, + report_last_approved_at: null, + report_last_paid_at: null, + report_settlement_id: null, + seq_num: 'E/2024/03/T/6', + source: 'MOBILE', + source_account: { + id: 'acc7F6bwRa52p', + type: AccountType.PERSONAL_CORPORATE_CREDIT_CARD_ACCOUNT, + }, + source_account_id: 'acc7F6bwRa52p', + spent_at: new Date('2024-01-31T11:30:00.000Z'), + split_group_amount: 12, + split_group_id: 'tx6I9xcOZFU6', + started_at: null, + state: ExpenseState.COMPLETE, + state_display_name: 'Complete', + tax_amount: null, + tax_group: null, + tax_group_id: null, + travel_classes: [], + updated_at: new Date('2024-03-03T06:23:41.011Z'), + user: { + email: 'devendra.r@fyle.in', + full_name: 'Devendra Singh Rana', + id: 'usvMoPfCC9Xw', + }, + user_id: 'usvMoPfCC9Xw', + verifications: [], + verifier_comments: [], + }, + { + accounting_export_summary: {}, + added_to_report_at: null, + admin_amount: null, + amount: 4.8, + approvals: [], + approver_comments: [], + category: { + code: null, + display_name: 'Bus', + id: 283900, + name: 'Bus', + sub_category: null, + system_category: 'Bus', + }, + category_id: 283900, + claim_amount: 4.8, + code: null, + cost_center: null, + cost_center_id: null, + created_at: new Date('2024-03-03T06:19:50.179Z'), + creator_user_id: 'ouokfwl9OEdl', + currency: 'USD', + custom_fields: [ + { + is_enabled: true, + name: 'Custom Field', + type: 'TEXT', + value: null, + }, + ], + custom_fields_flattened: { + custom_field: null, + }, + distance: null, + distance_unit: null, + employee: { + business_unit: null, + code: null, + custom_fields: [], + department: null, + department_id: null, + flattened_custom_field: {}, + has_accepted_invite: true, + id: 'ouokfwl9OEdl', + is_enabled: true, + joined_at: null, + level: null, + location: null, + mobile: null, + org_id: 'oroLKHBYQVvj', + org_name: 'CC Transaction Testing', + title: null, + user: { + email: 'devendra.r@fyle.in', + full_name: 'Devendra Singh Rana', + id: 'usvMoPfCC9Xw', + }, + user_id: 'usvMoPfCC9Xw', + }, + employee_id: 'ouokfwl9OEdl', + ended_at: null, + expense_rule_data: null, + expense_rule_id: null, + extracted_data: null, + file_ids: [], + files: [], + foreign_amount: null, + foreign_currency: null, + hotel_is_breakfast_provided: false, + id: 'txZA0Oj6TV9c', + invoice_number: null, + is_billable: null, + is_corporate_card_transaction_auto_matched: false, + is_exported: null, + is_manually_flagged: null, + is_physical_bill_submitted: null, + is_policy_flagged: null, + is_receipt_mandatory: null, + is_reimbursable: false, + is_split: true, + is_verified: false, + is_weekend_spend: false, + last_exported_at: null, + last_settled_at: null, + last_verified_at: null, + locations: [], + matched_corporate_card_transaction_ids: [], + matched_corporate_card_transactions: [], + merchant: null, + mileage_calculated_amount: null, + mileage_calculated_distance: null, + mileage_is_round_trip: null, + mileage_rate: null, + mileage_rate_id: null, + missing_mandatory_fields: { + amount: false, + currency: false, + expense_field_ids: [], + receipt: false, + }, + org_id: 'oroLKHBYQVvj', + per_diem_num_days: null, + per_diem_rate: null, + per_diem_rate_id: null, + physical_bill_submitted_at: null, + policy_amount: null, + policy_checks: { + are_approvers_added: false, + is_amount_limit_applied: false, + is_flagged_ever: false, + violations: null, + }, + project: null, + project_id: null, + purpose: null, + report: null, + report_id: null, + report_last_approved_at: null, + report_last_paid_at: null, + report_settlement_id: null, + seq_num: 'E/2024/03/T/5', + source: 'MOBILE', + source_account: { + id: 'acc7F6bwRa52p', + type: AccountType.PERSONAL_CORPORATE_CREDIT_CARD_ACCOUNT, + }, + source_account_id: 'acc7F6bwRa52p', + spent_at: new Date('2024-01-31T11:30:00.000Z'), + split_group_amount: 12, + split_group_id: 'tx9mWbP68zQX', + started_at: null, + state: ExpenseState.COMPLETE, + state_display_name: 'Complete', + tax_amount: null, + tax_group: null, + tax_group_id: null, + travel_classes: [], + updated_at: new Date('2024-03-03T06:19:55.582Z'), + user: { + email: 'devendra.r@fyle.in', + full_name: 'Devendra Singh Rana', + id: 'usvMoPfCC9Xw', + }, + user_id: 'usvMoPfCC9Xw', + verifications: [], + verifier_comments: [], + }, +]; + +export const platformExpenseData: Expense = { + accounting_export_summary: {}, + added_to_report_at: null, + admin_amount: null, + amount: 2263.68, + approvals: [ + { + approver_user: { + email: 'suyash.p+3@fyle.in', + full_name: 'Suyash 3', + id: 'usyg6aRkXnl8', + }, + approver_user_id: 'usyg6aRkXnl8', + state: ApprovalState.APPROVAL_DISABLED, + }, + ], + approver_comments: [], + category: { + code: null, + display_name: 'Mileage', + id: 256620, + name: 'Mileage', + sub_category: null, + system_category: 'Mileage', + }, + category_id: 256620, + claim_amount: 2263.68, + code: null, + cost_center: null, + cost_center_id: null, + created_at: new Date('2023-12-05T22:50:43.158Z'), + creator_user_id: 'usvMoPfCC9Xw', + currency: 'USD', + custom_fields: [ + { + is_enabled: true, + name: 'ASDF', + type: 'TEXT', + value: null, + }, + ], + custom_fields_flattened: { + asdf: null, + }, + distance: 3456, + distance_unit: MileageUnitEnum.MILES, + employee: { + business_unit: null, + code: null, + custom_fields: [], + department: null, + department_id: null, + flattened_custom_field: {}, + has_accepted_invite: true, + id: 'ou6cE4dCLH8d', + is_enabled: true, + joined_at: null, + level: { + band: null, + id: 'lvlqaReubGuJl', + name: 'Entry level', + }, + location: null, + mobile: null, + org_id: 'orNbIQloYtfa', + org_name: 'Advance-test', + title: null, + user: { + email: 'devendra.r@fyle.in', + full_name: 'devendra', + id: 'usvMoPfCC9Xw', + }, + user_id: 'usvMoPfCC9Xw', + }, + employee_id: 'ou6cE4dCLH8d', + ended_at: null, + expense_rule_data: null, + expense_rule_id: null, + extracted_data: null, + file_ids: [], + files: [], + foreign_amount: null, + foreign_currency: null, + hotel_is_breakfast_provided: null, + id: 'txvslh8aQMbu', + is_billable: null, + is_corporate_card_transaction_auto_matched: false, + is_exported: null, + is_manually_flagged: null, + is_physical_bill_submitted: null, + is_policy_flagged: false, + is_receipt_mandatory: null, + is_reimbursable: true, + is_split: false, + is_verified: false, + is_weekend_spend: false, + last_exported_at: null, + last_settled_at: null, + last_verified_at: null, + locations: [], + matched_corporate_card_transaction_ids: [], + matched_corporate_card_transactions: [], + merchant: null, + mileage_calculated_amount: null, + mileage_calculated_distance: null, + mileage_is_round_trip: null, + mileage_rate: { + code: null, + id: 332872, + rate: 0.655, + vehicle_type: 'IRS Rate', + }, + mileage_rate_id: 332872, + missing_mandatory_fields: { + amount: false, + currency: false, + expense_field_ids: [], + receipt: false, + }, + org_id: 'orNbIQloYtfa', + per_diem_num_days: null, + per_diem_rate: null, + per_diem_rate_id: null, + physical_bill_submitted_at: null, + policy_amount: null, + policy_checks: { + are_approvers_added: false, + is_amount_limit_applied: false, + is_flagged_ever: false, + violations: null, + }, + project: null, + project_id: null, + purpose: null, + report: null, + report_id: null, + report_last_approved_at: null, + report_last_paid_at: null, + report_settlement_id: null, + seq_num: 'E/2023/12/T/8', + source: 'WEBAPP', + source_account: { + id: 'accO6abI7gZ6T', + type: AccountType.PERSONAL_CASH_ACCOUNT, + }, + source_account_id: 'accO6abI7gZ6T', + spent_at: new Date('2023-12-06T11:30:00.000Z'), + split_group_amount: null, + split_group_id: 'txvslh8aQMbu', + started_at: null, + state: ExpenseState.DRAFT, + state_display_name: 'Incomplete', + tax_amount: null, + tax_group: null, + tax_group_id: null, + travel_classes: [], + updated_at: new Date('2024-01-22T07:17:05.949Z'), + user: { + email: 'devendra.r@fyle.in', + full_name: 'devendra', + id: 'usvMoPfCC9Xw', + }, + user_id: 'usvMoPfCC9Xw', + verifier_comments: ['Amount changed due to following reason(s) : No policy violation explanation provided'], +}; + +export const platformExpenseDataWithSubCategory: Expense = { + accounting_export_summary: {}, + added_to_report_at: null, + id: 'txD5hIQgLuR5', + created_at: new Date('2024-02-09T06:50:13.098Z'), + admin_amount: null, + amount: 32, + approvals: [], + approver_comments: [], + category: { + code: null, + display_name: 'Food / Burger', + id: 290006, + name: 'Food', + sub_category: 'Burger', + system_category: 'Food', + }, + category_id: 290006, + claim_amount: 32, + code: null, + cost_center: { + code: null, + id: 20423, + name: 'Cost Center', + }, + cost_center_id: 20423, + creator_user_id: 'usvMoPfCC9Xw', + currency: 'USD', + custom_fields: [ + { + is_enabled: true, + name: 'location desc', + type: 'TEXT', + value: 'Noida', + }, + { + is_enabled: true, + name: 'PP1 where', + type: 'DEPENDENT_SELECT', + value: null, + }, + { + is_enabled: true, + name: 'PP 1', + type: 'DEPENDENT_SELECT', + value: null, + }, + { + is_enabled: true, + name: 'CC 1', + type: 'DEPENDENT_SELECT', + value: null, + }, + ], + custom_fields_flattened: { + cc_1: null, + location_desc: 'Noida', + pp1_where: null, + pp_1: null, + }, + distance: null, + distance_unit: null, + employee: { + business_unit: null, + code: null, + custom_fields: [], + department: null, + department_id: null, + flattened_custom_field: {}, + has_accepted_invite: true, + id: 'ou6cE4dCLH8d', + is_enabled: true, + joined_at: null, + level: { + band: null, + id: 'lvlqaReubGuJl', + name: 'Entry level', + }, + location: null, + mobile: null, + org_id: 'orNbIQloYtfa', + org_name: 'Advance-test', + title: null, + user: { + email: 'devendra.r@fyle.in', + full_name: 'Devendra Singh Rana', + id: 'usvMoPfCC9Xw', + }, + user_id: 'usvMoPfCC9Xw', + }, + employee_id: 'ou6cE4dCLH8d', + ended_at: null, + expense_rule_data: null, + expense_rule_id: null, + extracted_data: null, + file_ids: [], + files: [], + foreign_amount: null, + foreign_currency: null, + hotel_is_breakfast_provided: null, + invoice_number: null, + is_billable: null, + is_corporate_card_transaction_auto_matched: false, + is_exported: null, + is_manually_flagged: null, + is_physical_bill_submitted: null, + is_policy_flagged: null, + is_receipt_mandatory: null, + is_reimbursable: true, + is_split: false, + is_verified: false, + is_weekend_spend: false, + last_exported_at: null, + last_settled_at: null, + last_verified_at: null, + locations: [], + matched_corporate_card_transaction_ids: [], + matched_corporate_card_transactions: [], + merchant: 'test-1', + mileage_calculated_amount: null, + mileage_calculated_distance: null, + mileage_is_round_trip: null, + mileage_rate: null, + mileage_rate_id: null, + missing_mandatory_fields: { + amount: false, + currency: false, + expense_field_ids: [], + receipt: false, + }, + org_id: 'orNbIQloYtfa', + per_diem_num_days: null, + per_diem_rate: null, + per_diem_rate_id: null, + physical_bill_submitted_at: null, + policy_amount: null, + policy_checks: { + are_approvers_added: false, + is_amount_limit_applied: false, + is_flagged_ever: false, + violations: null, + }, + project: { + code: null, + display_name: 'Project 1', + id: 325126, + name: 'Project 1', + sub_project: null, + }, + project_id: 325126, + purpose: 'Client Meeting', + report: { + amount: 12983.793345, + approvals: [ + { + approver_user: { + email: 'aniruddha.s@fyle.in', + full_name: 'Annii Dept head', + id: 'us2zNEhtHp8d', + }, + approver_user_id: 'us2zNEhtHp8d', + state: ApprovalState.APPROVAL_PENDING, + }, + { + approver_user: { + email: 'suyash.p+3@fyle.in', + full_name: 'Suyash 3', + id: 'usyg6aRkXnl8', + }, + approver_user_id: 'usyg6aRkXnl8', + state: ApprovalState.APPROVAL_DISABLED, + }, + ], + id: null, + last_approved_at: null, + last_paid_at: null, + last_submitted_at: new Date('2023-12-05T22:56:09.428Z'), + last_verified_at: null, + reimbursement_id: null, + reimbursement_seq_num: null, + seq_num: 'C/2023/12/R/2', + settlement_id: null, + state: ReportState.APPROVER_PENDING, + title: '#1: Dec 2023', + }, + report_id: null, + report_last_approved_at: null, + report_last_paid_at: null, + report_settlement_id: null, + seq_num: 'E/2024/02/T/137', + source: 'MOBILE', + source_account: { + id: 'accO6abI7gZ6T', + type: AccountType.PERSONAL_CASH_ACCOUNT, + }, + source_account_id: 'accO6abI7gZ6T', + spent_at: new Date('2024-02-09T11:30:00.000Z'), + split_group_amount: null, + split_group_id: 'txD5hIQgLuR5', + started_at: null, + state: ExpenseState.APPROVER_PENDING, + state_display_name: 'Reported', + tax_amount: 3.43, + tax_group: { + name: 'testing tax', + percentage: 0.12, + }, + tax_group_id: 'tgyvHlipn1sF', + travel_classes: [], + updated_at: new Date('2024-02-09T08:04:59.324Z'), + user: { + email: 'devendra.r@fyle.in', + full_name: 'Devendra Singh Rana', + id: 'usvMoPfCC9Xw', + }, + user_id: 'usvMoPfCC9Xw', + verifications: [], + verifier_comments: [], +}; + +export const platformExpenseDataWithReportId: Expense = { + ...platformExpenseDataWithSubCategory, + report_id: 'rpbNc3kn5baq', +}; + +export const platformExpenseDataWithReportId2: Expense = { + ...platformExpenseDataWithSubCategory, + report_id: 'rplD17WeBlha', +}; + +export const platformExpenseWithExtractedData: Expense = { + accounting_export_summary: {}, + added_to_report_at: null, + admin_amount: null, + amount: 2.64, + approvals: [], + approver_comments: [], + category: { + code: null, + display_name: 'Food / Burger', + id: 290006, + name: 'Food', + sub_category: 'Burger', + system_category: 'unspecified', + }, + category_id: 290006, + claim_amount: 2.64, + code: null, + cost_center: null, + cost_center_id: null, + created_at: new Date('2024-02-11T22:57:43.416Z'), + creator_user_id: 'usvMoPfCC9Xw', + currency: 'USD', + custom_fields: [ + { + is_enabled: true, + name: 'location desc', + type: 'TEXT', + value: 'noida', + }, + { + is_enabled: true, + name: 'PP1 where', + type: 'DEPENDENT_SELECT', + value: null, + }, + { + is_enabled: true, + name: 'PP 1', + type: 'DEPENDENT_SELECT', + value: 'lol', + }, + { + is_enabled: true, + name: 'CC 1', + type: 'DEPENDENT_SELECT', + value: null, + }, + ], + custom_fields_flattened: { + cc_1: null, + location_desc: 'noida', + pp1_where: null, + pp_1: 'lol', + }, + distance: null, + distance_unit: null, + employee: { + business_unit: null, + code: null, + custom_fields: [], + department: null, + department_id: null, + flattened_custom_field: {}, + has_accepted_invite: true, + id: 'ou6cE4dCLH8d', + is_enabled: true, + joined_at: null, + level: { + band: null, + id: 'lvlqaReubGuJl', + name: 'Entry level', + }, + location: null, + mobile: null, + org_id: 'orNbIQloYtfa', + org_name: 'Advance-test', + title: null, + user: { + email: 'devendra.r@fyle.in', + full_name: 'Devendra Singh Rana', + id: 'usvMoPfCC9Xw', + }, + user_id: 'usvMoPfCC9Xw', + }, + employee_id: 'ou6cE4dCLH8d', + ended_at: null, + expense_rule_data: null, + expense_rule_id: null, + extracted_data: { + amount: 219.66, + category: 'Food', + currency: 'INR', + date: new Date('2024-01-18T18:30:00.000Z'), + invoice_dt: null, + vendor_name: 'SWIGGY', + }, + file_ids: ['fijiKmWjhy3Y'], + files: [ + { + content_type: 'application/pdf', + id: 'fijiKmWjhy3Y', + name: 'Jan_Food_Swiggy1.pdf', + type: FileType.RECEIPT, + }, + ], + foreign_amount: 219.66, + foreign_currency: 'INR', + hotel_is_breakfast_provided: false, + id: 'txO6d6eiB4JF', + invoice_number: null, + is_billable: null, + is_corporate_card_transaction_auto_matched: false, + is_exported: null, + is_manually_flagged: null, + is_physical_bill_submitted: null, + is_policy_flagged: null, + is_receipt_mandatory: null, + is_reimbursable: true, + is_split: false, + is_verified: false, + is_weekend_spend: false, + last_exported_at: null, + last_settled_at: null, + last_verified_at: null, + locations: [], + matched_corporate_card_transaction_ids: [], + matched_corporate_card_transactions: [], + merchant: 'test-1', + mileage_calculated_amount: null, + mileage_calculated_distance: null, + mileage_is_round_trip: null, + mileage_rate: null, + mileage_rate_id: null, + missing_mandatory_fields: { + amount: false, + currency: false, + expense_field_ids: [], + receipt: false, + }, + org_id: 'orNbIQloYtfa', + per_diem_num_days: null, + per_diem_rate: null, + per_diem_rate_id: null, + physical_bill_submitted_at: null, + policy_amount: null, + policy_checks: { + are_approvers_added: false, + is_amount_limit_applied: false, + is_flagged_ever: false, + violations: [], + }, + project: { + code: null, + display_name: 'Project 1', + id: 325126, + name: 'Project 1', + sub_project: null, + }, + project_id: 325126, + purpose: null, + report: null, + report_id: null, + report_last_approved_at: null, + report_last_paid_at: null, + report_settlement_id: null, + seq_num: 'E/2024/02/T/152', + source: 'WEBAPP', + source_account: { + id: 'accO6abI7gZ6T', + type: AccountType.PERSONAL_CASH_ACCOUNT, + }, + source_account_id: 'accO6abI7gZ6T', + spent_at: new Date('2024-01-19T11:30:00.000Z'), + split_group_amount: null, + split_group_id: 'txO6d6eiB4JF', + started_at: null, + state: ExpenseState.DRAFT, + state_display_name: 'Complete', + tax_amount: null, + tax_group: null, + tax_group_id: null, + travel_classes: [], + updated_at: new Date('2024-02-11T22:57:47.856Z'), + user: { + email: 'devendra.r@fyle.in', + full_name: 'Devendra Singh Rana', + id: 'usvMoPfCC9Xw', + }, + user_id: 'usvMoPfCC9Xw', + verifications: [], + verifier_comments: [], +}; + +export const platformExpenseWithExtractedData2: Expense = { + ...platformExpenseWithExtractedData, + category: { + code: null, + display_name: 'Food', + id: 290006, + name: 'Food', + sub_category: null, + system_category: null, + }, + extracted_data: { + amount: 219.66, + category: 'Food', + currency: 'INR', + date: new Date('2024-01-18T18:30:00.000Z'), + invoice_dt: null, + vendor_name: 'SWIGGY', + }, +}; + +export const platformExpenseWithMatchCCC: Expense = { + accounting_export_summary: {}, + added_to_report_at: null, + admin_amount: null, + amount: 260.37, + approvals: [], + approver_comments: [], + category: { + code: null, + display_name: 'Unspecified', + id: 283907, + name: 'Unspecified', + sub_category: null, + system_category: 'Unspecified', + }, + category_id: 283907, + claim_amount: 260.37, + code: null, + cost_center: null, + cost_center_id: null, + created_at: new Date('2024-01-23T06:49:49.370Z'), + creator_user_id: 'SYSTEM_CORPORATE_CARD', + currency: 'USD', + custom_fields: [], + custom_fields_flattened: {}, + distance: null, + distance_unit: null, + employee: { + business_unit: null, + code: null, + custom_fields: [], + department: null, + department_id: null, + flattened_custom_field: {}, + has_accepted_invite: true, + id: 'ouokfwl9OEdl', + is_enabled: true, + joined_at: null, + level: null, + location: null, + mobile: null, + org_id: 'oroLKHBYQVvj', + org_name: 'CC Transaction Testing', + title: null, + user: { + email: 'devendra.r@fyle.in', + full_name: 'Devendra Singh Rana', + id: 'usvMoPfCC9Xw', + }, + user_id: 'usvMoPfCC9Xw', + }, + employee_id: 'ouokfwl9OEdl', + ended_at: null, + expense_rule_data: null, + expense_rule_id: null, + extracted_data: null, + file_ids: [], + files: [], + foreign_amount: null, + foreign_currency: null, + hotel_is_breakfast_provided: null, + id: 'txmF3wgfj0Bs', + invoice_number: null, + is_billable: null, + is_corporate_card_transaction_auto_matched: false, + is_exported: null, + is_manually_flagged: null, + is_physical_bill_submitted: null, + is_policy_flagged: null, + is_receipt_mandatory: null, + is_reimbursable: false, + is_split: false, + is_verified: null, + is_weekend_spend: false, + last_exported_at: null, + last_settled_at: null, + last_verified_at: null, + locations: [], + matched_corporate_card_transaction_ids: ['btxnSte7sVQCM8'], + matched_corporate_card_transactions: [ + { + amount: 260.37, + bank_name: 'DSR', + category: null, + corporate_card_id: 'bacck9WlgA11Uh', + corporate_card_number: '7620', + corporate_card_user_full_name: 'Devendra Singh Rana', + currency: 'USD', + description: null, + foreign_amount: null, + foreign_currency: null, + id: 'btxnSte7sVQCM8', + masked_corporate_card_number: '7620', + matched_by: null, + merchant: 'test description', + posted_at: null, + spent_at: new Date('2018-07-03T18:30:00.000Z'), + status: TransactionStatus.PENDING, + }, + ], + merchant: 'test description', + mileage_calculated_amount: null, + mileage_calculated_distance: null, + mileage_is_round_trip: null, + mileage_rate: null, + mileage_rate_id: null, + missing_mandatory_fields: { + amount: false, + currency: false, + expense_field_ids: [242425], + receipt: false, + }, + org_id: 'oroLKHBYQVvj', + per_diem_num_days: null, + per_diem_rate: null, + per_diem_rate_id: null, + physical_bill_submitted_at: null, + policy_amount: null, + policy_checks: { + are_approvers_added: false, + is_amount_limit_applied: false, + is_flagged_ever: false, + violations: null, + }, + project: null, + project_id: null, + purpose: null, + report: null, + report_id: null, + report_last_approved_at: null, + report_last_paid_at: null, + report_settlement_id: null, + seq_num: 'E/2024/01/T/39', + source: 'CORPORATE_CARD', + source_account: { + id: 'acc7F6bwRa52p', + type: AccountType.PERSONAL_CORPORATE_CREDIT_CARD_ACCOUNT, + }, + source_account_id: 'acc7F6bwRa52p', + spent_at: new Date('2018-07-03T18:30:00.000Z'), + split_group_amount: null, + split_group_id: 'txmF3wgfj0Bs', + started_at: null, + state: ExpenseState.DRAFT, + state_display_name: 'Incomplete', + tax_amount: null, + tax_group: null, + tax_group_id: null, + travel_classes: [], + updated_at: new Date('2024-01-23T06:49:49.370Z'), + user: { + email: 'devendra.r@fyle.in', + full_name: 'Devendra Singh Rana', + id: 'usvMoPfCC9Xw', + }, + user_id: 'usvMoPfCC9Xw', + verifications: [], + verifier_comments: [], +}; + +export const platformExpenseWithMatchCCC2: Expense = { + ...platformExpenseWithMatchCCC, + matched_corporate_card_transaction_ids: [], + matched_corporate_card_transactions: [], +}; + +export const mileageCategoryPlatformExpenseData: Expense = { + ...platformExpenseData, + category: { ...platformExpenseData.category, name: 'MILEAGE' }, +}; + +export const perDiemCategoryPlatformExpenseData: Expense = { + ...platformExpenseData, + category: { ...platformExpenseData.category, name: 'PER DIEM' }, +}; + +export const expensesList: Expense[] = [ + { + ...platformExpenseData, + }, +]; diff --git a/src/app/core/mock-data/platform/v1/expenses-query-params.data.ts b/src/app/core/mock-data/platform/v1/expenses-query-params.data.ts index 0adee3bada..dcf6f522a4 100644 --- a/src/app/core/mock-data/platform/v1/expenses-query-params.data.ts +++ b/src/app/core/mock-data/platform/v1/expenses-query-params.data.ts @@ -4,6 +4,15 @@ export const getExpensesQueryParams: ExpensesQueryParams = { report_id: 'eq.txOJVaaPxo9O', }; +export const allExpensesQueryParams: ExpensesQueryParams = { + queryParams: { + employee_id: 'eq.out3t2X258rd', + state: ['in.(COMPLETE)'], + or: ['(policy_amount.is.null,policy_amount.gt.0.0001)'], + report_id: ['is.null'], + }, +}; + export const unreportedExpensesQueryParams: ExpensesQueryParams = { queryParams: { state: 'in.(COMPLETE)', diff --git a/src/app/core/mock-data/platform/v1/expenses-stats.data.ts b/src/app/core/mock-data/platform/v1/expenses-stats.data.ts index 4110f544e6..ee6a3c4010 100644 --- a/src/app/core/mock-data/platform/v1/expenses-stats.data.ts +++ b/src/app/core/mock-data/platform/v1/expenses-stats.data.ts @@ -18,3 +18,10 @@ export const incompleteStats = { total_amount: 76234.4729069240282984, }, }; + +export const completeStats1 = { + data: { + count: 4, + total_amount: 3494, + }, +}; diff --git a/src/app/core/mock-data/report-unflattened.data.ts b/src/app/core/mock-data/report-unflattened.data.ts index 5505fce3ad..782e2b1c2d 100644 --- a/src/app/core/mock-data/report-unflattened.data.ts +++ b/src/app/core/mock-data/report-unflattened.data.ts @@ -235,6 +235,99 @@ export const expectedErpt: UnflattenedReport[] = [ }, ]; +export const expectedErptPlatform: UnflattenedReport[] = [ + { + rp: { + id: 'rpIfg2VWQKGJ', + org_user_id: 'ou6cE4dCLH8d', + created_at: new Date('2023-12-06T04:26:08.987Z'), + purpose: '#1: Dec 2023', + currency: 'USD', + amount: 12983.793345, + tax: 6.86, + state: 'APPROVER_PENDING', + source: 'WEBAPP', + num_transactions: 43, + approvals: [ + { + id: 55554, + created_at: null, + updated_at: null, + report_id: 'rpIfg2VWQKGJ', + approver_id: 'ouoi3rmbjYQA', + request_id: null, + state: 'APPROVAL_DISABLED', + added_by: null, + disabled_by: null, + last_updated_by: null, + rank: 99, + approver_name: 'Suyash 3', + approver_email: 'suyash.p+3@fyle.in', + comment: null, + }, + { + id: 55555, + created_at: null, + updated_at: null, + report_id: 'rpIfg2VWQKGJ', + approver_id: 'ouW0Y7djsQbR', + request_id: null, + state: 'APPROVAL_PENDING', + added_by: null, + disabled_by: null, + last_updated_by: null, + rank: 97, + approver_name: 'Annii Dept head', + approver_email: 'aniruddha.s@fyle.in', + comment: null, + }, + ], + settlement_id: null, + approved_at: null, + reimbursed_at: null, + submitted_at: new Date('2023-12-06T04:26:09.428Z'), + verification_state: null, + trip_request_id: null, + physical_bill: false, + physical_bill_at: null, + exported: false, + manual_flag: false, + policy_flag: true, + claim_number: 'C/2023/12/R/2', + from_dt: null, + to_dt: null, + location1: null, + location2: null, + location3: null, + location4: null, + location5: null, + type: 'EXPENSE', + locations: [], + risk_state_expense_count: null, + risk_state: null, + }, + ou: { + id: 'ou6cE4dCLH8d', + org_id: 'orNbIQloYtfa', + location: null, + business_unit: null, + department: null, + sub_department: null, + mobile: null, + title: null, + employee_id: null, + level: 'Entry level', + status: 'ACTIVE', + org_name: 'Advance-test', + department_id: null, + }, + us: { + full_name: 'Devendra Singh Rana', + email: 'devendra.r@fyle.in', + }, + }, +]; + export const expectedSingleErpt: UnflattenedReport = { rp: { id: 'rprAfNrce73O', diff --git a/src/app/core/mock-data/track-expense-properties.data.ts b/src/app/core/mock-data/track-expense-properties.data.ts index 3ed92995f4..9fe56d7e65 100644 --- a/src/app/core/mock-data/track-expense-properties.data.ts +++ b/src/app/core/mock-data/track-expense-properties.data.ts @@ -70,3 +70,13 @@ export const editExpenseProperties: ExpenseProperties = { ...createExpenseProperties3, Type: 'Per Diem', }; + +export const editExpensePropertiesPlatform: ExpenseProperties = { + Type: 'Per Diem', + Amount: 2263.68, + Currency: 'USD', + Category: 'Mileage', + Time_Spent: '180 secs', + Used_Autofilled_Project: null, + Used_Autofilled_CostCenter: null, +}; diff --git a/src/app/core/mock-data/transaction-status.data.ts b/src/app/core/mock-data/transaction-status.data.ts index 314615bde6..67693ec1c8 100644 --- a/src/app/core/mock-data/transaction-status.data.ts +++ b/src/app/core/mock-data/transaction-status.data.ts @@ -12,6 +12,18 @@ export const txnStatusData: TransactionStatus = { advance_request_id: null, }; +export const expenseStatusData: TransactionStatus = { + advance_request_id: null, + comment: 'A comment', + created_at: new Date('2024-02-11T16:43:25.675Z'), + diff: null, + id: 'stnn4fH4i8MU', + org_user_id: 'ou6cE4dCLH8d', + report_id: null, + state: null, + transaction_id: 'txNWAit6pFgw', +}; + export const txnStatusData1: TransactionStatus = { id: 'stjIdPp7BX81', created_at: new Date('2022-11-17T06:07:38.590Z'), diff --git a/src/app/core/mock-data/transaction.data.ts b/src/app/core/mock-data/transaction.data.ts index 688d337f35..1933336e12 100644 --- a/src/app/core/mock-data/transaction.data.ts +++ b/src/app/core/mock-data/transaction.data.ts @@ -1,3 +1,4 @@ +import { TransactionStatus } from '../models/platform/v1/expense.model'; import { Transaction } from '../models/v1/transaction.model'; import { optionsData15, optionsData33 } from './merge-expenses-options-data.data'; import { expectedTxnCustomProperties, txnCustomPropertiesData } from './txn-custom-properties.data'; @@ -4013,6 +4014,178 @@ export const editUnflattenedTransaction: Partial = { categoryDisplayName: 'Software / Subscriptions', }; +export const editUnflattenedTransactionPlatform: Partial = { + id: 'txD5hIQgLuR5', + created_at: new Date('2024-02-09T01:20:13.098Z'), + txn_dt: new Date('2024-02-09T06:00:00.000Z'), + categoryDisplayName: 'Food / Burger', + num_files: 0, + org_category: 'Food', + fyle_category: 'Food', + state: 'APPROVER_PENDING', + admin_amount: null, + policy_amount: null, + skip_reimbursement: false, + amount: 32, + currency: 'USD', + user_amount: 32, + orig_amount: null, + orig_currency: null, + from_dt: null, + to_dt: null, + vendor: 'test-1', + distance: null, + distance_unit: null, + locations: [], + verification_state: null, + org_user_id: 'ou6cE4dCLH8d', + expense_number: 'E/2024/02/T/137', + hotel_is_breakfast_provided: null, + tax_group_id: 'tgyvHlipn1sF', + creator_id: 'ou6cE4dCLH8d', + report_id: null, + org_category_id: 290006, + cost_center_id: 20423, + cost_center_name: 'Cost Center', + cost_center_code: null, + project_id: 325126, + project_name: 'Project 1', + custom_properties: [ + { + is_enabled: true, + name: 'location desc', + type: 'TEXT', + value: 'Noida', + }, + { + is_enabled: true, + name: 'PP1 where', + type: 'DEPENDENT_SELECT', + value: null, + }, + { + is_enabled: true, + name: 'PP 1', + type: 'DEPENDENT_SELECT', + value: null, + }, + { + is_enabled: true, + name: 'CC 1', + type: 'DEPENDENT_SELECT', + value: null, + }, + ], + purpose: 'Client Meeting', + billable: null, + sub_category: 'Burger', + tax_amount: 3.43, + corporate_credit_card_expense_group_id: null, + split_group_id: 'txD5hIQgLuR5', + split_group_user_amount: null, + receipt_required: null, + per_diem_rate_id: null, + num_days: null, + mileage_rate_id: null, + mileage_is_round_trip: null, + mileage_calculated_distance: null, + mileage_calculated_amount: null, + manual_flag: null, + policy_flag: null, + extracted_data: null, + matched_corporate_card_transactions: [], + source_account_id: 'accO6abI7gZ6T', + org_category_code: null, + project_code: null, + physical_bill: null, + physical_bill_at: null, +}; + +export const editUnflattenedTransactionPlatform2: Partial = { + ...editUnflattenedTransactionPlatform, + report_id: 'rpbNc3kn5baq', +}; + +export const editUnflattenedTransactionPlatform3: Partial = { + ...editUnflattenedTransactionPlatform, + report_id: 'rplD17WeBlha', +}; + +export const editUnflattenedTransactionWithMatchCCCPlatform: Partial = { + id: 'txmF3wgfj0Bs', + created_at: new Date('2024-01-23T01:19:49.370Z'), + txn_dt: new Date('2018-07-03T13:00:00.000Z'), + categoryDisplayName: 'Unspecified', + num_files: 0, + org_category: 'Unspecified', + fyle_category: 'Unspecified', + state: 'DRAFT', + admin_amount: null, + policy_amount: null, + skip_reimbursement: true, + amount: 260.37, + currency: 'USD', + user_amount: 260.37, + orig_amount: null, + orig_currency: null, + from_dt: null, + to_dt: null, + vendor: 'test description', + distance: null, + distance_unit: null, + locations: [], + verification_state: null, + org_user_id: 'ouokfwl9OEdl', + expense_number: 'E/2024/01/T/39', + hotel_is_breakfast_provided: null, + tax_group_id: null, + creator_id: 'ouokfwl9OEdl', + report_id: null, + org_category_id: 283907, + cost_center_id: null, + project_id: null, + custom_properties: [], + purpose: null, + billable: null, + sub_category: null, + tax_amount: null, + corporate_credit_card_expense_group_id: 'btxnSte7sVQCM8', + split_group_id: 'txmF3wgfj0Bs', + split_group_user_amount: null, + receipt_required: null, + per_diem_rate_id: null, + num_days: null, + mileage_rate_id: null, + mileage_is_round_trip: null, + mileage_calculated_distance: null, + mileage_calculated_amount: null, + manual_flag: null, + policy_flag: null, + extracted_data: null, + matched_corporate_card_transactions: [ + { + id: 'btxnSte7sVQCM8', + group_id: 'btxnSte7sVQCM8', + amount: 260.37, + vendor: 'test description', + txn_dt: '2018-07-03T18:30:00.000Z', + currency: 'USD', + description: null, + card_or_account_number: '7620', + corporate_credit_card_account_number: '7620', + orig_amount: null, + orig_currency: null, + status: TransactionStatus.PENDING, + displayObject: 'Jul 4, 2018 - test description260.37', + }, + ], + source_account_id: 'acc7F6bwRa52p', + org_category_code: null, + physical_bill: null, + physical_bill_at: null, + matchCCCId: 'btxnSte7sVQCM8', +}; + export const editTransaction2: Partial = { risk_state: null, is_duplicate_expense: null, diff --git a/src/app/core/mock-data/transformed-expense.data.ts b/src/app/core/mock-data/transformed-expense.data.ts new file mode 100644 index 0000000000..1d2c9463f1 --- /dev/null +++ b/src/app/core/mock-data/transformed-expense.data.ts @@ -0,0 +1,441 @@ +import { TransactionStatus } from '../models/platform/v1/expense.model'; +import { UnflattenedTransaction } from '../models/unflattened-transaction.model'; + +export const transformedExpenseData: Partial = { + tx: { + id: 'txvslh8aQMbu', + created_at: new Date('2023-12-05T17:20:43.158Z'), + txn_dt: new Date('2023-12-06T06:00:00.000Z'), + categoryDisplayName: 'Mileage', + num_files: 0, + org_category: 'Mileage', + fyle_category: 'Mileage', + state: 'DRAFT', + admin_amount: null, + policy_amount: null, + skip_reimbursement: false, + amount: 2263.68, + currency: 'USD', + user_amount: 2263.68, + orig_amount: null, + orig_currency: null, + from_dt: null, + to_dt: null, + vendor: null, + distance: 3456, + distance_unit: 'MILES', + locations: [], + verification_state: null, + org_user_id: 'ou6cE4dCLH8d', + expense_number: 'E/2023/12/T/8', + hotel_is_breakfast_provided: null, + tax_group_id: null, + creator_id: 'ou6cE4dCLH8d', + report_id: null, + org_category_id: 256620, + cost_center_id: null, + project_id: null, + custom_properties: [ + { + name: 'ASDF', + type: 'TEXT', + value: null, + }, + ], + purpose: null, + billable: null, + sub_category: null, + tax_amount: null, + corporate_credit_card_expense_group_id: null, + split_group_id: 'txvslh8aQMbu', + split_group_user_amount: null, + receipt_required: null, + per_diem_rate_id: null, + num_days: null, + mileage_rate_id: 332872, + mileage_rate: 0.655, + mileage_vehicle_type: 'IRS Rate', + mileage_is_round_trip: null, + mileage_calculated_distance: null, + mileage_calculated_amount: null, + manual_flag: null, + policy_flag: false, + extracted_data: null, + matched_corporate_card_transactions: [], + source_account_id: 'accO6abI7gZ6T', + org_category_code: null, + physical_bill: null, + physical_bill_at: null, + }, + source: { + account_id: 'accO6abI7gZ6T', + account_type: 'PERSONAL_CASH_ACCOUNT', + }, + ou: { + id: 'ou6cE4dCLH8d', + org_id: 'orNbIQloYtfa', + }, +}; + +export const transformedExpenseDataWithSubCategory: Partial = { + tx: { + id: 'txD5hIQgLuR5', + created_at: new Date('2024-02-09T01:20:13.098Z'), + txn_dt: new Date('2024-02-09T06:00:00.000Z'), + categoryDisplayName: 'Food / Burger', + num_files: 0, + org_category: 'Food', + fyle_category: 'Food', + state: 'APPROVER_PENDING', + admin_amount: null, + policy_amount: null, + skip_reimbursement: false, + amount: 32, + currency: 'USD', + user_amount: 32, + orig_amount: null, + orig_currency: null, + from_dt: null, + to_dt: null, + vendor: 'test-1', + distance: null, + distance_unit: null, + locations: [], + verification_state: null, + org_user_id: 'ou6cE4dCLH8d', + expense_number: 'E/2024/02/T/137', + hotel_is_breakfast_provided: null, + tax_group_id: 'tgyvHlipn1sF', + creator_id: 'ou6cE4dCLH8d', + report_id: null, + org_category_id: 290006, + cost_center_id: 20423, + cost_center_name: 'Cost Center', + cost_center_code: null, + project_id: 325126, + project_name: 'Project 1', + custom_properties: [ + { + is_enabled: true, + name: 'location desc', + type: 'TEXT', + value: 'Noida', + }, + { + is_enabled: true, + name: 'PP1 where', + type: 'DEPENDENT_SELECT', + value: null, + }, + { + is_enabled: true, + name: 'PP 1', + type: 'DEPENDENT_SELECT', + value: null, + }, + { + is_enabled: true, + name: 'CC 1', + type: 'DEPENDENT_SELECT', + value: null, + }, + ], + purpose: 'Client Meeting', + billable: null, + sub_category: 'Burger', + tax_amount: 3.43, + corporate_credit_card_expense_group_id: null, + split_group_id: 'txD5hIQgLuR5', + split_group_user_amount: null, + receipt_required: null, + per_diem_rate_id: null, + num_days: null, + mileage_rate_id: null, + mileage_is_round_trip: null, + mileage_calculated_distance: null, + mileage_calculated_amount: null, + manual_flag: null, + policy_flag: null, + extracted_data: null, + matched_corporate_card_transactions: [], + source_account_id: 'accO6abI7gZ6T', + org_category_code: null, + project_code: null, + physical_bill: null, + physical_bill_at: null, + }, + source: { + account_id: 'accO6abI7gZ6T', + account_type: 'PERSONAL_CASH_ACCOUNT', + }, + ou: { + id: 'ou6cE4dCLH8d', + org_id: 'orNbIQloYtfa', + }, +}; + +export const transformedExpenseDataWithReportId: Partial = { + ...transformedExpenseDataWithSubCategory, + tx: { + ...transformedExpenseDataWithSubCategory.tx, + report_id: 'rpbNc3kn5baq', + }, +}; + +export const transformedExpenseDataWithReportId2: Partial = { + ...transformedExpenseDataWithSubCategory, + tx: { + ...transformedExpenseDataWithSubCategory.tx, + report_id: 'rplD17WeBlha', + }, +}; + +export const transformedExpenseWithExtractedData: Partial = { + tx: { + id: 'txO6d6eiB4JF', + created_at: new Date('2024-02-11T17:27:43.416Z'), + txn_dt: new Date('2024-01-19T06:00:00.000Z'), + categoryDisplayName: 'Food / Burger', + num_files: 1, + org_category: 'Food', + fyle_category: 'unspecified', + state: 'DRAFT', + admin_amount: null, + policy_amount: null, + skip_reimbursement: false, + amount: undefined, + currency: 'USD', + user_amount: 2.64, + orig_amount: 219.66, + orig_currency: 'INR', + from_dt: null, + to_dt: null, + vendor: 'test-1', + distance: null, + distance_unit: null, + locations: [], + verification_state: null, + org_user_id: 'ou6cE4dCLH8d', + expense_number: 'E/2024/02/T/152', + hotel_is_breakfast_provided: false, + tax_group_id: null, + creator_id: 'ou6cE4dCLH8d', + report_id: null, + org_category_id: 290006, + cost_center_id: null, + project_id: 325126, + project_name: 'Project 1', + custom_properties: [ + { + is_enabled: true, + name: 'location desc', + type: 'TEXT', + value: 'noida', + }, + { + is_enabled: true, + name: 'PP1 where', + type: 'DEPENDENT_SELECT', + value: null, + }, + { + is_enabled: true, + name: 'PP 1', + type: 'DEPENDENT_SELECT', + value: 'lol', + }, + { + is_enabled: true, + name: 'CC 1', + type: 'DEPENDENT_SELECT', + value: null, + }, + ], + purpose: null, + billable: null, + sub_category: 'Burger', + tax_amount: null, + corporate_credit_card_expense_group_id: null, + split_group_id: 'txO6d6eiB4JF', + split_group_user_amount: null, + receipt_required: null, + per_diem_rate_id: null, + num_days: null, + mileage_rate_id: null, + mileage_is_round_trip: null, + mileage_calculated_distance: null, + mileage_calculated_amount: null, + manual_flag: null, + policy_flag: null, + extracted_data: { + vendor: 'SWIGGY', + currency: 'INR', + amount: 219.66, + date: new Date('2024-01-18T18:30:00.000Z'), + invoice_dt: null, + category: 'Food', + }, + matched_corporate_card_transactions: [], + source_account_id: 'accO6abI7gZ6T', + org_category_code: null, + project_code: null, + physical_bill: null, + physical_bill_at: null, + }, + source: { + account_id: 'accO6abI7gZ6T', + account_type: 'PERSONAL_CASH_ACCOUNT', + }, + ou: { + id: 'ou6cE4dCLH8d', + org_id: 'orNbIQloYtfa', + }, +}; + +export const transformedExpenseWithExtractedData2: Partial = { + ...transformedExpenseWithExtractedData, + tx: { + ...transformedExpenseWithExtractedData.tx, + amount: undefined, + currency: undefined, + vendor: undefined, + fyle_category: null, + extracted_data: { + vendor: 'SWIGGY', + currency: 'INR', + amount: 219.66, + date: new Date('2024-01-18T18:30:00.000Z'), + invoice_dt: null, + category: 'Food', + }, + }, +}; + +export const transformedExpenseWithMatchCCCData: Partial = { + tx: { + id: 'txmF3wgfj0Bs', + created_at: new Date('2024-01-23T01:19:49.370Z'), + txn_dt: new Date('2018-07-03T13:00:00.000Z'), + categoryDisplayName: 'Unspecified', + num_files: 0, + org_category: 'Unspecified', + fyle_category: 'Unspecified', + state: 'DRAFT', + admin_amount: null, + policy_amount: null, + skip_reimbursement: true, + amount: 260.37, + currency: 'USD', + user_amount: 260.37, + orig_amount: null, + orig_currency: null, + from_dt: null, + to_dt: null, + vendor: 'test description', + distance: null, + distance_unit: null, + locations: [], + verification_state: null, + org_user_id: 'ouokfwl9OEdl', + expense_number: 'E/2024/01/T/39', + hotel_is_breakfast_provided: null, + tax_group_id: null, + creator_id: 'ouokfwl9OEdl', + report_id: null, + org_category_id: 283907, + cost_center_id: null, + project_id: null, + custom_properties: [], + purpose: null, + billable: null, + sub_category: null, + tax_amount: null, + corporate_credit_card_expense_group_id: 'btxnSte7sVQCM8', + split_group_id: 'txmF3wgfj0Bs', + split_group_user_amount: null, + receipt_required: null, + per_diem_rate_id: null, + num_days: null, + mileage_rate_id: null, + mileage_is_round_trip: null, + mileage_calculated_distance: null, + mileage_calculated_amount: null, + manual_flag: null, + policy_flag: null, + extracted_data: null, + matched_corporate_card_transactions: [ + { + id: 'btxnSte7sVQCM8', + group_id: 'btxnSte7sVQCM8', + amount: 260.37, + vendor: 'test description', + txn_dt: '2018-07-03T13:00:00.000Z', + currency: 'USD', + description: null, + card_or_account_number: '7620', + corporate_credit_card_account_number: '7620', + orig_amount: null, + orig_currency: null, + status: TransactionStatus.PENDING, + }, + ], + source_account_id: 'acc7F6bwRa52p', + org_category_code: null, + physical_bill: null, + physical_bill_at: null, + }, + source: { + account_id: 'acc7F6bwRa52p', + account_type: 'PERSONAL_CORPORATE_CREDIT_CARD_ACCOUNT', + }, + ou: { + id: 'ouokfwl9OEdl', + org_id: 'oroLKHBYQVvj', + }, +}; + +export const transformedExpenseWithMatchCCCData2: Partial = { + ...transformedExpenseWithMatchCCCData, + tx: { + ...transformedExpenseWithMatchCCCData.tx, + corporate_credit_card_expense_group_id: null, + matchCCCId: null, + matched_corporate_card_transactions: [], + }, +}; + +export const transformedExpenseWithMatchCCCData3: Partial = { + ...transformedExpenseWithMatchCCCData, + tx: { + ...transformedExpenseWithMatchCCCData.tx, + corporate_credit_card_expense_group_id: null, + matchCCCId: 'btxnBdS2Kpvzhy', + matched_corporate_card_transactions: [ + { + id: 'btxnBdS2Kpvzhy', + group_id: 'btxnBdS2Kpvzhy', + amount: 205.21, + vendor: 'test description', + txn_dt: '2018-06-06T08:30:00.000Z', + currency: 'USD', + description: null, + card_or_account_number: '9891', + corporate_credit_card_account_number: '9891', + orig_amount: null, + orig_currency: null, + status: TransactionStatus.PENDING, + displayObject: 'Jun 6, 2018 - test description205.21', + }, + ], + }, +}; + +export const mileageCategoryTransformedExpenseData: Partial = { + ...transformedExpenseData, + tx: { ...transformedExpenseData.tx, org_category: 'MILEAGE' }, +}; + +export const perDiemCategoryTransformedExpenseData: Partial = { + ...transformedExpenseData, + tx: { ...transformedExpenseData.tx, org_category: 'PER DIEM' }, +}; diff --git a/src/app/core/mock-data/unflattened-txn.data.ts b/src/app/core/mock-data/unflattened-txn.data.ts index 7ede675f7e..61652dda29 100644 --- a/src/app/core/mock-data/unflattened-txn.data.ts +++ b/src/app/core/mock-data/unflattened-txn.data.ts @@ -1811,6 +1811,14 @@ export const unflattenedExpWithCCCExpn: UnflattenedTransaction = { ], }; +export const unflattenedExpWithCCCExpn1: UnflattenedTransaction = { + ...unflattenedExpWithCCCExpn, + tx: { + ...unflattenedExpWithCCCExpn.tx, + split_group_id: 'tx6I9xcOZFU6', + }, +}; + export const trackCreateExpData: UnflattenedTransaction = { ...unflattenedExp2, tx: { @@ -1870,7 +1878,7 @@ export const trackAddExpenseWoCurrency: Partial = { dataUrls: null, }; -export const newExpFromFg = { +export const newExpFromFg: Partial = { tx: { risk_state: null, is_duplicate_expense: null, @@ -1905,7 +1913,7 @@ export const newExpFromFg = { project_code: null, skip_reimbursement: false, creator_id: 'ouX8dwsbLCLv', - user_reason_for_duplicate_expenses: 'reason', + user_reason_for_duplicate_expenses: null, external_id: null, cost_center_name: 'SMS1', cost_center_code: null, @@ -2023,7 +2031,7 @@ export const newExpFromFg = { dataUrls: [], }; -export const newExpFromFg2 = { +export const newExpFromFg2: Partial = { tx: { risk_state: null, is_duplicate_expense: null, @@ -2058,7 +2066,7 @@ export const newExpFromFg2 = { project_code: null, skip_reimbursement: false, creator_id: 'ouX8dwsbLCLv', - user_reason_for_duplicate_expenses: 'reason', + user_reason_for_duplicate_expenses: null, external_id: null, cost_center_name: 'SMS1', cost_center_code: null, @@ -2177,7 +2185,7 @@ export const newExpFromFg2 = { dataUrls: [], }; -export const newExpFromFg3 = { +export const newExpFromFg3: Partial = { tx: { risk_state: null, is_duplicate_expense: null, @@ -2212,7 +2220,7 @@ export const newExpFromFg3 = { project_code: null, skip_reimbursement: false, creator_id: 'ouX8dwsbLCLv', - user_reason_for_duplicate_expenses: 'reason', + user_reason_for_duplicate_expenses: null, external_id: null, cost_center_name: 'SMS1', cost_center_code: null, @@ -2314,7 +2322,7 @@ export const newExpFromFg3 = { dataUrls: [], }; -export const newExpFromFg4 = { +export const newExpFromFg4: Partial = { tx: { risk_state: null, is_duplicate_expense: null, @@ -2349,7 +2357,7 @@ export const newExpFromFg4 = { project_code: null, skip_reimbursement: false, creator_id: 'ouX8dwsbLCLv', - user_reason_for_duplicate_expenses: 'reason', + user_reason_for_duplicate_expenses: null, external_id: null, cost_center_name: null, cost_center_code: null, @@ -2451,7 +2459,121 @@ export const newExpFromFg4 = { dataUrls: [], }; -export const expWithSplitGroupID = { +export const newExpFromFgPlatform: Partial = { + tx: { + id: 'txD5hIQgLuR5', + created_at: new Date('2024-02-09T01:20:13.098Z'), + txn_dt: new Date('2024-02-08T18:30:00.000Z'), + categoryDisplayName: 'Food / Burger', + num_files: 0, + org_category: 'Food', + fyle_category: 'Food', + state: 'APPROVER_PENDING', + admin_amount: null, + policy_amount: null, + skip_reimbursement: false, + amount: 32, + currency: 'USD', + user_amount: 32, + orig_amount: null, + orig_currency: null, + from_dt: null, + to_dt: null, + vendor: 'test-1', + distance: null, + distance_unit: null, + locations: [], + verification_state: null, + org_user_id: 'ou6cE4dCLH8d', + expense_number: 'E/2024/02/T/137', + hotel_is_breakfast_provided: null, + tax_group_id: 'tgyvHlipn1sF', + creator_id: 'ou6cE4dCLH8d', + report_id: null, + org_category_id: 290006, + cost_center_id: 20423, + cost_center_name: 'Cost Center', + cost_center_code: null, + project_id: 325126, + project_name: 'Project 1', + custom_properties: [ + { + id: 231167, + mandatory: true, + name: 'location desc', + options: [], + placeholder: 'enter location desc', + prefix: '', + type: 'TEXT', + value: 'Noida', + }, + { + id: 231228, + prefix: '', + name: 'PP1 where', + value: null, + placeholder: 'PP1 where', + type: 'DEPENDENT_SELECT', + mandatory: false, + options: [], + parent_field_id: 231094, + }, + { + id: 231094, + prefix: '', + name: 'PP 1', + value: null, + placeholder: 'PP 1', + type: 'DEPENDENT_SELECT', + mandatory: false, + options: [], + parent_field_id: 223790, + }, + { + id: 230824, + prefix: '', + name: 'CC 1', + value: null, + placeholder: 'CC 1', + type: 'DEPENDENT_SELECT', + mandatory: false, + options: [], + parent_field_id: 223789, + }, + ], + purpose: 'Client Meeting', + billable: null, + sub_category: 'Burger', + tax_amount: 3.43, + corporate_credit_card_expense_group_id: null, + split_group_id: 'txD5hIQgLuR5', + split_group_user_amount: null, + receipt_required: null, + per_diem_rate_id: null, + num_days: null, + mileage_rate_id: null, + mileage_is_round_trip: null, + mileage_calculated_distance: null, + mileage_calculated_amount: null, + manual_flag: null, + policy_flag: null, + extracted_data: null, + matched_corporate_card_transactions: [], + source_account_id: 'accO6abI7gZ6T', + org_category_code: null, + project_code: null, + physical_bill: null, + physical_bill_at: null, + source: 'MOBILE', + }, + ou: { + id: 'ou6cE4dCLH8d', + org_id: 'orNbIQloYtfa', + }, + dataUrls: [], +}; + +export const expWithSplitGroupID: Partial = { ...unflattenedTxnData, tx: null, }; @@ -3047,7 +3169,7 @@ export const newMileageExpFromForm: Partial = { policy_amount: null, admin_amount: null, billable: undefined, - user_reason_for_duplicate_expenses: undefined, + user_reason_for_duplicate_expenses: null, tax: 52.47, tax_amount: 52.47, tax_group_id: 'tg3iWuqWhfzB', @@ -3253,7 +3375,7 @@ export const newMileageExpFromForm2: Partial = { receipt_required: false, user_can_delete: true, billable: undefined, - user_reason_for_duplicate_expenses: undefined, + user_reason_for_duplicate_expenses: null, mileage_vehicle_type: undefined, txn_dt: new Date('2023-02-13T01:00:00.000Z'), category: null, diff --git a/src/app/core/models/custom-input.model.ts b/src/app/core/models/custom-input.model.ts index 6f6826dcec..0003f5d3dd 100644 --- a/src/app/core/models/custom-input.model.ts +++ b/src/app/core/models/custom-input.model.ts @@ -12,4 +12,5 @@ export interface CustomInput { parent_field_id: number; displayValue: string; areSameValues?: boolean; + is_enabled?: boolean; } diff --git a/src/app/core/models/expense.model.ts b/src/app/core/models/expense.model.ts index d25f076da6..60c2a8b286 100644 --- a/src/app/core/models/expense.model.ts +++ b/src/app/core/models/expense.model.ts @@ -1,5 +1,6 @@ import { CustomInput } from './custom-input.model'; import { Destination } from './destination.model'; +import { MatchedCCCTransaction } from './matchedCCCTransaction.model'; export interface Expense { isCriticalPolicyViolated?: boolean; @@ -106,6 +107,7 @@ export interface Expense { tx_mileage_calculated_distance?: number; tx_mileage_is_round_trip?: boolean; tx_mileage_rate?: number; + tx_mileage_rate_id?: string; tx_mileage_vehicle_type?: string; tx_num_days?: number; tx_num_files: number; @@ -217,4 +219,5 @@ export interface Expense { duplicates?: { fields: string[]; percent: number; reason: string; transaction_id: string }[]; tx_is_split_expense?: boolean; custom_fields?: Record; + tx_matched_corporate_card_transactions?: Partial[]; } diff --git a/src/app/core/models/matchedCCCTransaction.model.ts b/src/app/core/models/matchedCCCTransaction.model.ts index fd972bc23d..71ab8a7b8c 100644 --- a/src/app/core/models/matchedCCCTransaction.model.ts +++ b/src/app/core/models/matchedCCCTransaction.model.ts @@ -1,3 +1,5 @@ +import { TransactionStatus } from './platform/v1/expense.model'; + export interface MatchedCCCTransaction { amount: number; balance_transfer_id: number | string; @@ -16,4 +18,7 @@ export interface MatchedCCCTransaction { txn_dt: string; updated_at: string; vendor: string; + corporate_credit_card_account_number?: string; + displayObject?: string; + status?: TransactionStatus; } diff --git a/src/app/core/models/per-diem-form-value.model.ts b/src/app/core/models/per-diem-form-value.model.ts index 767bae721a..7219ef623c 100644 --- a/src/app/core/models/per-diem-form-value.model.ts +++ b/src/app/core/models/per-diem-form-value.model.ts @@ -23,7 +23,6 @@ export interface PerDiemFormValue { from_dt: string; to_dt: string; custom_inputs: CustomInput[]; - duplicate_detection_reason: string; billable: boolean; costCenter: CostCenter; project_dependent_fields: TxnCustomProperties[]; diff --git a/src/app/core/models/platform/v1/assignor-user.model.ts b/src/app/core/models/platform/v1/assignor-user.model.ts new file mode 100644 index 0000000000..978f850920 --- /dev/null +++ b/src/app/core/models/platform/v1/assignor-user.model.ts @@ -0,0 +1,5 @@ +export interface AssignorUser { + id: string; + email: string; + full_name: string; +} diff --git a/src/app/core/models/platform/v1/cc-matched-expense.model.ts b/src/app/core/models/platform/v1/cc-matched-expense.model.ts new file mode 100644 index 0000000000..36cc456ed8 --- /dev/null +++ b/src/app/core/models/platform/v1/cc-matched-expense.model.ts @@ -0,0 +1,14 @@ +export interface CCMatchedExpense { + id: string; + currency: string; + amount: number; + spent_at: Date; + merchant: string; + foreign_currency: string; + foreign_amount: number; + purpose: string; + state: string; + seq_num: string; + no_of_files: number; + category_display_name: string; +} diff --git a/src/app/core/models/platform/v1/cc-transaction-metadata.ts b/src/app/core/models/platform/v1/cc-transaction-metadata.ts new file mode 100644 index 0000000000..420b989e4b --- /dev/null +++ b/src/app/core/models/platform/v1/cc-transaction-metadata.ts @@ -0,0 +1,38 @@ +export interface CCTransactionMetadata { + merchant_category_code: string; + flight_merchant_category_code: string; + flight_supplier_name: string; + flight_travel_agency_name: string; + flight_ticket_number: string; + flight_total_fare: number; + flight_travel_date: Date; + flight_service_class: string; + flight_carrier_code: string; + flight_fare_base_code: string; + flight_trip_leg_number: string; + hotel_merchant_category_code: string; + hotel_supplier_name: string; + hotel_checked_in_at: Date; + hotel_nights: number; + hotel_checked_out_at: Date; + hotel_country: string; + hotel_city: string; + hotel_total_fare: number; + fleet_product_merchant_category_code: string; + fleet_product_supplier_name: string; + fleet_service_merchant_category_code: string; + fleet_service_supplier_name: string; + car_rental_merchant_category_code: string; + car_rental_supplier_name: string; + car_rental_started_at: Date; + car_rental_days: number; + car_rental_ended_at: Date; + general_ticket_issued_at: Date; + general_ticket_number: string; + general_issuing_carrier: string; + general_travel_agency_name: string; + general_travel_agency_code: string; + general_ticket_total_fare: number; + general_ticket_total_tax: number; + merchant_address: string; +} diff --git a/src/app/core/models/platform/v1/cc-transaction.model.ts b/src/app/core/models/platform/v1/cc-transaction.model.ts new file mode 100644 index 0000000000..ee7f78627d --- /dev/null +++ b/src/app/core/models/platform/v1/cc-transaction.model.ts @@ -0,0 +1,51 @@ +import { AssignorUser } from './assignor-user.model'; +import { CorporateCard } from './corporate-card.model'; +import { CCMatchedExpense } from './cc-matched-expense.model'; +import { CCTransactionMetadata } from './cc-transaction-metadata'; +import { TransactionStatus } from './expense.model'; + +export interface corporateCardTransaction { + id: string; + org_id: string; + user_id: string; + user: { + id: string; + email: string; + full_name: string; + }; + created_at: Date; + updated_at: Date; + amount: number; + currency: string; + spent_at: Date; + post_date: Date; + description: string; + statement_id: string; + can_delete: boolean; + foreign_currency: string; + foreign_amount: number; + code: string; + merchant: string; + mcc: string; + category: string; + corporate_card_id: string; + corporate_card: CorporateCard; + assignor_user_id: string; + assignor_user: AssignorUser; + is_assigned: boolean; + last_assigned_at: Date; + is_marked_personal: boolean; + last_marked_personal_at: Date; + is_dismissed: boolean; + is_exported: boolean; + last_dismissed_at: Date; + is_auto_matched: boolean; + auto_suggested_expense_ids: string[]; + last_auto_matched_at: Date; + matched_expense_ids: string[]; + matched_expenses: CCMatchedExpense[]; + last_user_matched_at: Date; + settlement_id: string; + metadata?: CCTransactionMetadata; + transaction_status?: TransactionStatus; +} diff --git a/src/app/core/models/platform/v1/corporate-card-transaction-res.model.ts b/src/app/core/models/platform/v1/corporate-card-transaction-res.model.ts new file mode 100644 index 0000000000..4ccda71849 --- /dev/null +++ b/src/app/core/models/platform/v1/corporate-card-transaction-res.model.ts @@ -0,0 +1,4 @@ +import { corporateCardTransaction } from './cc-transaction.model'; +export interface CorporateCardTransactionRes { + data: corporateCardTransaction; +} diff --git a/src/app/core/models/platform/v1/corporate-card.model.ts b/src/app/core/models/platform/v1/corporate-card.model.ts new file mode 100644 index 0000000000..8db70c9bb2 --- /dev/null +++ b/src/app/core/models/platform/v1/corporate-card.model.ts @@ -0,0 +1,8 @@ +export interface CorporateCard { + id: string; + bank_name: string; + card_number: string; + masked_number: string; + user_full_name: string; + user_email: string; +} diff --git a/src/app/core/models/platform/v1/expense.model.ts b/src/app/core/models/platform/v1/expense.model.ts index 35e91f3730..188a4a029a 100644 --- a/src/app/core/models/platform/v1/expense.model.ts +++ b/src/app/core/models/platform/v1/expense.model.ts @@ -54,6 +54,7 @@ export interface Expense { foreign_currency: string; hotel_is_breakfast_provided: boolean; id: string; + invoice_number?: string; is_billable: boolean; is_corporate_card_transaction_auto_matched: boolean; is_manually_flagged: boolean; @@ -73,7 +74,7 @@ export interface Expense { mileage_calculated_amount: number; mileage_calculated_distance: number; mileage_is_round_trip: boolean; - mileage_rate: Pick; + mileage_rate: Partial; mileage_rate_id: number; missing_mandatory_fields: MissingMandatoryFields; org_id: string; @@ -106,6 +107,7 @@ export interface Expense { updated_at: Date; user: User; user_id: string; + verifications?: string[]; verifier_comments: string[]; report_last_paid_at: Date; report_last_approved_at: Date; @@ -122,6 +124,8 @@ export interface Employee { has_accepted_invite: boolean; id: string; is_enabled: boolean; + joined_at?: Date; + mobile?: string; level: Pick; location: string; org_id: string; diff --git a/src/app/core/models/txn-custom-properties.model.ts b/src/app/core/models/txn-custom-properties.model.ts index 9f5a315b95..5c14575be9 100644 --- a/src/app/core/models/txn-custom-properties.model.ts +++ b/src/app/core/models/txn-custom-properties.model.ts @@ -12,6 +12,7 @@ export interface TxnCustomProperties { parent_field_id?: number; label?: string; control?: AbstractControl; + is_enabled?: boolean; } export interface CustomInputsOption { diff --git a/src/app/core/models/v1/transaction.model.ts b/src/app/core/models/v1/transaction.model.ts index f9c8e4652b..05bc2a90f2 100644 --- a/src/app/core/models/v1/transaction.model.ts +++ b/src/app/core/models/v1/transaction.model.ts @@ -1,4 +1,5 @@ import { Destination } from '../destination.model'; +import { TransactionStatus } from '../platform/v1/expense.model'; import { TxnCustomProperties } from '../txn-custom-properties.model'; export interface Transaction { @@ -119,4 +120,19 @@ export interface Transaction { category?: string; invoice_dt?: Date; }; + matched_corporate_card_transactions?: { + id: string; + group_id: string; + amount: number; + vendor: string; + txn_dt: string; + currency: string; + description: string; + card_or_account_number: string; + corporate_credit_card_account_number?: string; + displayObject?: string; + orig_amount: number; + orig_currency: string; + status: TransactionStatus; + }[]; } diff --git a/src/app/core/services/corporate-credit-card-expense.service.spec.ts b/src/app/core/services/corporate-credit-card-expense.service.spec.ts index 24104617c6..dc4cb5be5a 100644 --- a/src/app/core/services/corporate-credit-card-expense.service.spec.ts +++ b/src/app/core/services/corporate-credit-card-expense.service.spec.ts @@ -19,6 +19,10 @@ import { eCCCApiResponse } from '../mock-data/corporate-card-expense-flattened.d import { mastercardRTFCard, statementUploadedCard } from '../mock-data/platform-corporate-card.data'; import { StatsResponse } from '../models/v2/stats-response.model'; import { bankFeedSourcesData } from '../mock-data/bank-feed-sources.data'; +import { + ccTransactionResponseData, + ccTransactionResponseData1, +} from '../mock-data/corporate-card-transaction-response.data'; import { statementUploadedCardDetail } from '../mock-data/platform-corporate-card-detail.data'; describe('CorporateCreditCardExpenseService', () => { @@ -34,7 +38,7 @@ describe('CorporateCreditCardExpenseService', () => { const apiServiceSpy = jasmine.createSpyObj('ApiService', ['get', 'post']); const apiV2ServiceSpy = jasmine.createSpyObj('ApiV2Service', ['get', 'getStats']); const authServiceSpy = jasmine.createSpyObj('AuthService', ['getEou']); - const spenderPlatformV1ApiServiceSpy = jasmine.createSpyObj('SpenderPlatformV1ApiService', ['get']); + const spenderPlatformV1ApiServiceSpy = jasmine.createSpyObj('SpenderPlatformV1ApiService', ['get', 'post']); TestBed.configureTestingModule({ providers: [ @@ -76,39 +80,29 @@ describe('CorporateCreditCardExpenseService', () => { }); it('markPersonal(): should mark an expense as personal', (done) => { - apiService.post.and.returnValue(of(null)); - const testId = 'ccceJN3PWAR94U'; - - cccExpenseService.markPersonal(testId).subscribe(() => { - expect(apiService.post).toHaveBeenCalledOnceWith('/corporate_credit_card_expenses/' + testId + '/personal'); + spenderPlatformV1ApiService.post.and.returnValue(of(ccTransactionResponseData1)); + const id = 'btxnSte7sVQCM8'; + const payload = { + id, + }; + cccExpenseService.markPersonal(id).subscribe(() => { + expect(spenderPlatformV1ApiService.post).toHaveBeenCalledOnceWith('/corporate_card_transactions/mark_personal', { + data: payload, + }); done(); }); }); it('dismissCreditTransaction(): should dismiss a transaction as corporate credit card expense', (done) => { - apiService.post.and.returnValue(of(null)); - const testId = 'ccceRhYsN8Fj78'; - - cccExpenseService.dismissCreditTransaction(testId).subscribe(() => { - expect(apiService.post).toHaveBeenCalledOnceWith('/corporate_credit_card_expenses/' + testId + '/ignore'); - done(); - }); - }); - - it('getEccceByGroupId(): should get Corporate Credit Card expenses by group ID', (done) => { - apiService.get.and.returnValue(of(eCCCApiResponse)); - spyOn(dataTransformService, 'unflatten').and.returnValue(expectedECccResponse[0]); - - const testID = 'ccceYIJhT8Aj6U'; - - cccExpenseService.getEccceByGroupId(testID).subscribe((res) => { - expect(res).toEqual(expectedECccResponse); - expect(apiService.get).toHaveBeenCalledOnceWith('/extended_corporate_credit_card_expenses', { - params: { - group_id: testID, - }, + spenderPlatformV1ApiService.post.and.returnValue(of(ccTransactionResponseData)); + const id = 'btxnBdS2Kpvzhy'; + const payload = { + id, + }; + cccExpenseService.dismissCreditTransaction(id).subscribe(() => { + expect(spenderPlatformV1ApiService.post).toHaveBeenCalledOnceWith('/corporate_card_transactions/ignore', { + data: payload, }); - expect(dataTransformService.unflatten).toHaveBeenCalledTimes(1); done(); }); }); diff --git a/src/app/core/services/corporate-credit-card-expense.service.ts b/src/app/core/services/corporate-credit-card-expense.service.ts index 7155684774..9389f4cc0d 100644 --- a/src/app/core/services/corporate-credit-card-expense.service.ts +++ b/src/app/core/services/corporate-credit-card-expense.service.ts @@ -3,23 +3,20 @@ import { Observable, Subject, from, of } from 'rxjs'; import { map, switchMap } from 'rxjs/operators'; import { CardAggregateStats } from '../models/card-aggregate-stats.model'; import { CCCDetails } from '../models/ccc-expense-details.model'; -import { CCCExpFlattened } from '../models/corporate-card-expense-flattened.model'; import { UniqueCardStats } from '../models/unique-cards-stats.model'; import { ApiV2Response } from '../models/v2/api-v2-response.model'; import { CorporateCardExpense } from '../models/v2/corporate-card-expense.model'; import { StatsResponse } from '../models/v2/stats-response.model'; import { ApiV2Service } from './api-v2.service'; -import { ApiService } from './api.service'; import { AuthService } from './auth.service'; -import { DataTransformService } from './data-transform.service'; import { SpenderPlatformV1ApiService } from './spender-platform-v1-api.service'; import { PlatformApiResponse } from '../models/platform/platform-api-response.model'; import { PlatformCorporateCard } from '../models/platform/platform-corporate-card.model'; import { CacheBuster, Cacheable } from 'ts-cacheable'; import { DataFeedSource } from '../enums/data-feed-source.enum'; -import { CCCExpUnflattened } from '../models/corporate-card-expense-unflattened.model'; import { PlatformCorporateCardDetail } from '../models/platform-corporate-card-detail.model'; import { UniqueCards } from '../models/unique-cards.model'; +import { CorporateCardTransactionRes } from '../models/platform/v1/corporate-card-transaction-res.model'; type Config = Partial<{ offset: number; @@ -35,11 +32,9 @@ const cacheBuster$ = new Subject(); }) export class CorporateCreditCardExpenseService { constructor( - private apiService: ApiService, private apiV2Service: ApiV2Service, - private dataTransformService: DataTransformService, private authService: AuthService, - private spenderPlatformV1ApiService: SpenderPlatformV1ApiService, + private spenderPlatformV1ApiService: SpenderPlatformV1ApiService ) {} @CacheBuster({ @@ -87,29 +82,23 @@ export class CorporateCreditCardExpenseService { limit: number; offset: number; url: string; - }, - ), + } + ) ); } - markPersonal(corporateCreditCardExpenseGroupId: string): Observable { - return this.apiService.post('/corporate_credit_card_expenses/' + corporateCreditCardExpenseGroupId + '/personal'); - } - - dismissCreditTransaction(corporateCreditCardExpenseId: string): Observable { - return this.apiService.post('/corporate_credit_card_expenses/' + corporateCreditCardExpenseId + '/ignore'); + markPersonal(id: string): Observable { + const payload = { + id, + }; + return this.spenderPlatformV1ApiService.post('/corporate_card_transactions/mark_personal', { data: payload }); } - getEccceByGroupId(groupId: string): Observable { - const data = { - params: { - group_id: groupId, - }, + dismissCreditTransaction(id: string): Observable { + const payload = { + id, }; - - return this.apiService - .get('/extended_corporate_credit_card_expenses', data) - .pipe(map((res) => (res && res.length && res.map((elem) => this.dataTransformService.unflatten(elem))) || [])); + return this.spenderPlatformV1ApiService.post('/corporate_card_transactions/ignore', { data: payload }); } constructInQueryParamStringForV2(params: string[]): string { @@ -194,8 +183,8 @@ export class CorporateCreditCardExpenseService { this.constructInQueryParamStringForV2(['COMPLETE', 'DRAFT']) + '&corporate_credit_card_account_number=not.is.null&debit=is.true&tx_org_user_id=eq.' + eou.ou.id, - {}, - ), + {} + ) ), map((statsResponse: StatsResponse) => { const stats = { @@ -212,7 +201,7 @@ export class CorporateCreditCardExpenseService { } }); return stats; - }), + }) ); } } diff --git a/src/app/core/services/deep-link.service.ts b/src/app/core/services/deep-link.service.ts index 642a0a5963..d25dd1fe78 100644 --- a/src/app/core/services/deep-link.service.ts +++ b/src/app/core/services/deep-link.service.ts @@ -117,7 +117,7 @@ export class DeepLinkService { } } - getExpenseRoute(etxn: UnflattenedTransaction): string[] { + getExpenseRoute(etxn: Partial): string[] { const category = etxn.tx.org_category?.toLowerCase(); const canEditTxn = ['DRAFT', 'COMPLETE', 'APPROVER_PENDING'].includes(etxn.tx.state); diff --git a/src/app/core/services/expenses-info.model.ts b/src/app/core/services/expenses-info.model.ts index 279406f249..434c94b72f 100644 --- a/src/app/core/services/expenses-info.model.ts +++ b/src/app/core/services/expenses-info.model.ts @@ -3,5 +3,5 @@ import { Expense } from '../models/expense.model'; export interface ExpensesInfo { isReportedAndAbove: boolean; isAdvancePresent: boolean; - defaultExpenses: Expense[]; + defaultExpenses: Partial[]; } diff --git a/src/app/core/services/merge-expenses.service.ts b/src/app/core/services/merge-expenses.service.ts index b9ff8d206e..cb5834efdf 100644 --- a/src/app/core/services/merge-expenses.service.ts +++ b/src/app/core/services/merge-expenses.service.ts @@ -61,15 +61,15 @@ export class MergeExpensesService { }); } - isAllAdvanceExpenses(expenses: Expense[]): boolean { + isAllAdvanceExpenses(expenses: Partial[]): boolean { return expenses.every((expense) => expense?.source_account_type === AccountType.ADVANCE); } - checkIfAdvanceExpensePresent(expenses: Expense[]): Expense[] { + checkIfAdvanceExpensePresent(expenses: Partial[]): Partial[] { return expenses.filter((expense) => expense?.source_account_type === AccountType.ADVANCE); } - setDefaultExpenseToKeep(expenses: Expense[]): ExpensesInfo { + setDefaultExpenseToKeep(expenses: Partial[]): ExpensesInfo { const advanceExpenses = this.checkIfAdvanceExpensePresent(expenses); const reportedAndAboveExpenses = expenses.filter((expense) => ['APPROVER_PENDING', 'APPROVED', 'PAYMENT_PENDING', 'PAYMENT_PROCESSING', 'PAID'].includes(expense.tx_state) @@ -89,7 +89,7 @@ export class MergeExpensesService { return expensesInfo; } - isApprovedAndAbove(expenses: Expense[]): Expense[] { + isApprovedAndAbove(expenses: Partial[]): Partial[] { const approvedAndAboveExpenses = expenses.filter((expense) => ['APPROVED', 'PAYMENT_PENDING', 'PAYMENT_PROCESSING', 'PAID'].includes(expense.tx_state) ); @@ -100,7 +100,7 @@ export class MergeExpensesService { return expensesInfo.defaultExpenses?.length === 1 && expensesInfo.isAdvancePresent; } - isReportedPresent(expenses: Expense[]): Expense[] { + isReportedPresent(expenses: Partial[]): Partial[] { return expenses.filter((expense) => expense.tx_state === 'APPROVER_PENDING'); } @@ -130,7 +130,7 @@ export class MergeExpensesService { ); } - getCorporateCardTransactions(expenses: Expense[]): Observable { + getCorporateCardTransactions(expenses: Partial[]): Observable { return this.customInputsService.getAll(true).pipe( switchMap(() => { const CCCGroupIds = expenses.map((expense) => expense?.tx_corporate_credit_card_expense_group_id); @@ -153,7 +153,7 @@ export class MergeExpensesService { ); } - generateExpenseToKeepOptions(expenses: Expense[]): Observable[]> { + generateExpenseToKeepOptions(expenses: Partial[]): Observable[]> { return from(expenses).pipe( map((expense) => { let vendorOrCategory = ''; @@ -189,7 +189,7 @@ export class MergeExpensesService { ); } - generateReceiptOptions(expenses: Expense[]): Observable[]> { + generateReceiptOptions(expenses: Partial[]): Observable[]> { return from(expenses).pipe( map((expense, index) => ({ label: `Receipt From Expense ${index + 1} `, @@ -202,7 +202,7 @@ export class MergeExpensesService { ); } - generateAmountOptions(expenses: Expense[]): Observable> { + generateAmountOptions(expenses: Partial[]): Observable> { return from(expenses).pipe( map((expense) => { const isForeignAmountPresent = expense.tx_orig_currency && expense.tx_orig_amount; @@ -242,7 +242,7 @@ export class MergeExpensesService { ); } - generateDateOfSpendOptions(expenses: Expense[]): Observable> { + generateDateOfSpendOptions(expenses: Partial[]): Observable> { return from(expenses).pipe( filter((expense) => expense.tx_txn_dt !== null), map((expense) => ({ @@ -263,7 +263,7 @@ export class MergeExpensesService { ); } - generatePaymentModeOptions(expenses: Expense[]): Observable> { + generatePaymentModeOptions(expenses: Partial[]): Observable> { return from(expenses).pipe( map((expense) => ({ label: expense.source_account_type, @@ -278,11 +278,11 @@ export class MergeExpensesService { ); } - generateVendorOptions(expenses: Expense[]): Observable> { + generateVendorOptions(expenses: Partial[]): Observable> { return from(expenses).pipe( filter((expense) => !!expense.tx_vendor), map((expense) => ({ - label: expense.tx_vendor.toString(), + label: expense.tx_vendor?.toString(), value: expense.tx_vendor, })), reduce((acc: MergeExpensesOption[], curr) => { @@ -293,11 +293,11 @@ export class MergeExpensesService { ); } - generateProjectOptions(expenses: Expense[]): Observable> { + generateProjectOptions(expenses: Partial[]): Observable> { return from(expenses).pipe( filter((expense) => !!expense.tx_project_id), map((expense) => ({ - label: expense.tx_project_id.toString(), + label: expense.tx_project_id?.toString(), value: expense.tx_project_id, })), mergeMap((option) => this.formatProjectOptions(option)), @@ -309,7 +309,7 @@ export class MergeExpensesService { ); } - generateCategoryOptions(expenses: Expense[]): Observable> { + generateCategoryOptions(expenses: Partial[]): Observable> { return from(expenses).pipe( map((expense) => ({ label: '', @@ -330,7 +330,7 @@ export class MergeExpensesService { ); } - generateTaxGroupOptions(expenses: Expense[]): Observable> { + generateTaxGroupOptions(expenses: Partial[]): Observable> { return from(expenses).pipe( filter((expense) => expense.tx_tax_group_id !== null), map((expense) => ({ @@ -346,11 +346,11 @@ export class MergeExpensesService { ); } - generateTaxAmountOptions(expenses: Expense[]): Observable> { + generateTaxAmountOptions(expenses: Partial[]): Observable> { return from(expenses).pipe( filter((expense) => expense.tx_tax !== null), map((expense) => ({ - label: expense.tx_tax.toString(), + label: expense.tx_tax?.toString(), value: expense.tx_tax, })), reduce((acc: MergeExpensesOption[], curr) => { @@ -361,11 +361,11 @@ export class MergeExpensesService { ); } - generateCostCenterOptions(expenses: Expense[]): Observable> { + generateCostCenterOptions(expenses: Partial[]): Observable> { return from(expenses).pipe( filter((expense) => expense.tx_cost_center_name !== null), map((expense) => ({ - label: expense.tx_cost_center_name.toString(), + label: expense.tx_cost_center_name?.toString(), value: expense.tx_cost_center_id, })), reduce((acc: MergeExpensesOption[], curr) => { @@ -376,11 +376,11 @@ export class MergeExpensesService { ); } - generatePurposeOptions(expenses: Expense[]): Observable> { + generatePurposeOptions(expenses: Partial[]): Observable> { return from(expenses).pipe( filter((expense) => expense.tx_purpose !== null), map((expense) => ({ - label: expense.tx_purpose.toString(), + label: expense.tx_purpose?.toString(), value: expense.tx_purpose, })), reduce((acc: MergeExpensesOption[], curr) => { @@ -391,7 +391,10 @@ export class MergeExpensesService { ); } - generateLocationOptions(expenses: Expense[], locationIndex: number): Observable> { + generateLocationOptions( + expenses: Partial[], + locationIndex: number + ): Observable> { return from(expenses).pipe( filter((expense) => !!expense.tx_locations[locationIndex]), map((expense) => ({ @@ -412,7 +415,7 @@ export class MergeExpensesService { ); } - generateOnwardDateOptions(expenses: Expense[]): Observable> { + generateOnwardDateOptions(expenses: Partial[]): Observable> { return from(expenses).pipe( filter((expense) => expense.tx_from_dt !== null), map((expense) => ({ @@ -433,7 +436,7 @@ export class MergeExpensesService { ); } - generateReturnDateOptions(expenses: Expense[]): Observable> { + generateReturnDateOptions(expenses: Partial[]): Observable> { return from(expenses).pipe( filter((expense) => expense.tx_to_dt !== null), map((expense) => ({ @@ -454,11 +457,11 @@ export class MergeExpensesService { ); } - generateFlightJourneyTravelClassOptions(expenses: Expense[]): Observable> { + generateFlightJourneyTravelClassOptions(expenses: Partial[]): Observable> { return from(expenses).pipe( filter((expense) => expense.tx_flight_journey_travel_class !== null), map((expense) => ({ - label: expense.tx_flight_journey_travel_class.toString(), + label: expense.tx_flight_journey_travel_class?.toString(), value: expense.tx_flight_journey_travel_class, })), reduce((acc: MergeExpensesOption[], curr) => { @@ -469,11 +472,11 @@ export class MergeExpensesService { ); } - generateFlightReturnTravelClassOptions(expenses: Expense[]): Observable> { + generateFlightReturnTravelClassOptions(expenses: Partial[]): Observable> { return from(expenses).pipe( filter((expense) => expense.tx_flight_return_travel_class !== null), map((expense) => ({ - label: expense.tx_flight_return_travel_class.toString(), + label: expense.tx_flight_return_travel_class?.toString(), value: expense.tx_flight_return_travel_class, })), reduce((acc: MergeExpensesOption[], curr) => { @@ -484,11 +487,11 @@ export class MergeExpensesService { ); } - generateTrainTravelClassOptions(expenses: Expense[]): Observable> { + generateTrainTravelClassOptions(expenses: Partial[]): Observable> { return from(expenses).pipe( filter((expense) => expense.tx_train_travel_class !== null), map((expense) => ({ - label: expense.tx_train_travel_class.toString(), + label: expense.tx_train_travel_class?.toString(), value: expense.tx_train_travel_class, })), reduce((acc: MergeExpensesOption[], curr) => { @@ -499,11 +502,11 @@ export class MergeExpensesService { ); } - generateBusTravelClassOptions(expenses: Expense[]): Observable> { + generateBusTravelClassOptions(expenses: Partial[]): Observable> { return from(expenses).pipe( filter((expense) => expense.tx_bus_travel_class !== null), map((expense) => ({ - label: expense.tx_bus_travel_class.toString(), + label: expense.tx_bus_travel_class?.toString(), value: expense.tx_bus_travel_class, })), reduce((acc: MergeExpensesOption[], curr) => { @@ -514,11 +517,11 @@ export class MergeExpensesService { ); } - generateDistanceOptions(expenses: Expense[]): Observable> { + generateDistanceOptions(expenses: Partial[]): Observable> { return from(expenses).pipe( filter((expense) => expense.tx_distance !== null), map((expense) => ({ - label: expense.tx_distance.toString(), + label: expense.tx_distance?.toString(), value: expense.tx_distance, })), reduce((acc: MergeExpensesOption[], curr) => { @@ -529,11 +532,11 @@ export class MergeExpensesService { ); } - generateDistanceUnitOptions(expenses: Expense[]): Observable> { + generateDistanceUnitOptions(expenses: Partial[]): Observable> { return from(expenses).pipe( filter((expense) => expense.tx_distance_unit !== null), map((expense) => ({ - label: expense.tx_distance_unit.toString(), + label: expense.tx_distance_unit?.toString(), value: expense.tx_distance_unit, })), reduce((acc: MergeExpensesOption[], curr) => { @@ -544,10 +547,10 @@ export class MergeExpensesService { ); } - generateBillableOptions(expenses: Expense[]): Observable> { + generateBillableOptions(expenses: Partial[]): Observable> { return from(expenses).pipe( map((expense) => ({ - label: expense.tx_billable.toString(), + label: expense.tx_billable?.toString(), value: expense.tx_billable, })), map((option) => this.formatBillableOptions(option)), @@ -568,7 +571,7 @@ export class MergeExpensesService { ); } - getCustomInputValues(expenses: Expense[]): Partial[][] { + getCustomInputValues(expenses: Partial[]): Partial[][] { //Create a copy so that we don't modify the expense object const expensesCopy = cloneDeep(expenses); return expensesCopy @@ -581,7 +584,7 @@ export class MergeExpensesService { } getDependentFieldsMapping( - expenses: Expense[], + expenses: Partial[], dependentFields: TxnCustomProperties[], parentField: 'PROJECT' | 'COST_CENTER' ): { @@ -678,7 +681,7 @@ export class MergeExpensesService { if (isValidDate) { formatedlabel = this.setFormattedDate(field.value); } else { - formatedlabel = field.value.toString(); + formatedlabel = field.value?.toString(); } if (existing) { const existingIndex = customProperty.indexOf(existing); diff --git a/src/app/core/services/platform/v1/spender/expenses.service.spec.ts b/src/app/core/services/platform/v1/spender/expenses.service.spec.ts index a160d10ac9..d38347bd3b 100644 --- a/src/app/core/services/platform/v1/spender/expenses.service.spec.ts +++ b/src/app/core/services/platform/v1/spender/expenses.service.spec.ts @@ -2,7 +2,11 @@ import { TestBed } from '@angular/core/testing'; import { of } from 'rxjs'; import { ExpensesService } from './expenses.service'; import { SpenderService } from './spender.service'; -import { expenseData, readyToReportExpensesData2 } from 'src/app/core/mock-data/platform/v1/expense.data'; +import { + expenseData, + readyToReportExpensesData2, + splitExpensesData, +} from 'src/app/core/mock-data/platform/v1/expense.data'; import { PAGINATION_SIZE } from 'src/app/constants'; import { expensesResponse } from 'src/app/core/mock-data/platform/v1/expenses-response.data'; import { getExpensesQueryParams } from 'src/app/core/mock-data/platform/v1/expenses-query-params.data'; @@ -169,7 +173,7 @@ describe('ExpensesService', () => { expect(spenderService.get).toHaveBeenCalledOnceWith('/expenses/duplicate_sets', { params: { expense_id: 'txaiCW1efU0n', - }, + }, }); done(); }); @@ -220,4 +224,18 @@ describe('ExpensesService', () => { done(); }); }); + + it('getSplitExpenses(): should return the list of expenses based on splitGroupId', (done) => { + const splitGroupId = 'tx6I9xcOZFU6'; + const queryParams = { + split_group_id: 'eq.tx6I9xcOZFU6', + }; + spyOn(service, 'getAllExpenses').and.returnValue(of(splitExpensesData)); + + service.getSplitExpenses(splitGroupId).subscribe((res) => { + expect(res).toEqual(splitExpensesData); + expect(service.getAllExpenses).toHaveBeenCalledOnceWith({ queryParams }); + done(); + }); + }); }); diff --git a/src/app/core/services/policy.service.ts b/src/app/core/services/policy.service.ts index f37d98465d..d1bfd8398b 100644 --- a/src/app/core/services/policy.service.ts +++ b/src/app/core/services/policy.service.ts @@ -13,7 +13,7 @@ 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'; +import { MatchedCCCTransaction } from '../models/matchedCCCTransaction.model'; @Injectable({ providedIn: 'root', @@ -164,7 +164,7 @@ export class PolicyService { prepareEtxnForPolicyCheck( etxn: { tx: PublicPolicyExpense; dataUrls: Partial[] }, - selectedCCCTransaction: CCCExpense + selectedCCCTransaction: Partial ): Observable { const transactionCopy = cloneDeep(etxn.tx); /* Adding number of attachements and sending in test call as tx_num_files @@ -200,7 +200,7 @@ export class PolicyService { tx: PublicPolicyExpense; dataUrls: Partial[]; }, - selectedCCCTransaction: CCCExpense + selectedCCCTransaction: Partial ): Observable { return this.prepareEtxnForPolicyCheck(etxn, selectedCCCTransaction).pipe( map((publicPolicyExpense) => this.transformTo(publicPolicyExpense)) diff --git a/src/app/core/services/split-expense.service.spec.ts b/src/app/core/services/split-expense.service.spec.ts index 782074a5d4..c98e9a0f41 100644 --- a/src/app/core/services/split-expense.service.spec.ts +++ b/src/app/core/services/split-expense.service.spec.ts @@ -1,6 +1,7 @@ import { TestBed } from '@angular/core/testing'; import { SplitExpenseService } from './split-expense.service'; import { TransactionService } from './transaction.service'; +import { ExpensesService } from './platform/v1/spender/expenses.service'; import { PolicyService } from './policy.service'; import { DataTransformService } from './data-transform.service'; import { FileService } from './file.service'; @@ -49,7 +50,7 @@ import { policyVoilationData2, splitPolicyExp4, } from '../mock-data/policy-violation.data'; -import { splitExpData, splitExpData2 } from '../mock-data/expense.data'; +import { splitExpData, splitExpData2, splitExpTransformedData } from '../mock-data/expense.data'; import { formattedTxnViolations, formattedTxnViolations2 } from '../mock-data/formatted-policy-violation.data'; import { txnStatusData, txnStatusData1, txnStatusData2 } from '../mock-data/transaction-status.data'; import { @@ -64,7 +65,6 @@ import { criticalPolicyViolation1, criticalPolicyViolation2 } from '../mock-data import { UtilityService } from './utility.service'; import { cloneDeep, split } from 'lodash'; import { expenseFieldResponse } from '../mock-data/expense-field.data'; -import { ExpensesService } from './platform/v1/spender/expenses.service'; import { splitPayloadData1, splitPayloadData2, splitPayloadData3 } from '../mock-data/split-payload.data'; import { splitPolicyExp1 } from '../mock-data/split-expense-policy.data'; import { splitData2, splitsData1 } from '../mock-data/splits.data'; @@ -82,20 +82,20 @@ import { describe('SplitExpenseService', () => { let splitExpenseService: SplitExpenseService; let transactionService: jasmine.SpyObj; + let expensesService: jasmine.SpyObj; let policyService: jasmine.SpyObj; let dataTransformService: jasmine.SpyObj; let fileService: jasmine.SpyObj; let statusService: jasmine.SpyObj; let categoriesService: jasmine.SpyObj; let utilityService: jasmine.SpyObj; - let expensesService: jasmine.SpyObj; beforeEach(() => { const transactionServiceSpy = jasmine.createSpyObj('TransactionService', [ 'uploadBase64File', 'checkPolicy', - 'getEtxn', 'upsert', + 'transformRawExpense', ]); const policyServiceSpy = jasmine.createSpyObj('PolicyService', [ 'transformTo', @@ -111,6 +111,7 @@ describe('SplitExpenseService', () => { 'splitExpenseCheckPolicies', 'splitExpenseCheckMissingFields', 'splitExpense', + 'getExpenseById', ]); TestBed.configureTestingModule({ @@ -120,6 +121,10 @@ describe('SplitExpenseService', () => { provide: TransactionService, useValue: transactionServiceSpy, }, + { + provide: ExpensesService, + useValue: expensesServiceSpy, + }, { provide: PolicyService, useValue: policyServiceSpy, @@ -153,6 +158,7 @@ describe('SplitExpenseService', () => { splitExpenseService = TestBed.inject(SplitExpenseService); transactionService = TestBed.inject(TransactionService) as jasmine.SpyObj; + expensesService = TestBed.inject(ExpensesService) as jasmine.SpyObj; policyService = TestBed.inject(PolicyService) as jasmine.SpyObj; fileService = TestBed.inject(FileService) as jasmine.SpyObj; statusService = TestBed.inject(StatusService) as jasmine.SpyObj; diff --git a/src/app/core/services/transaction.service.spec.ts b/src/app/core/services/transaction.service.spec.ts index 4fe7cd15a4..751c2c21e6 100644 --- a/src/app/core/services/transaction.service.spec.ts +++ b/src/app/core/services/transaction.service.spec.ts @@ -15,6 +15,7 @@ import { expenseData3, expenseList3, expenseList4, + expenseDataWithDateString, } from '../mock-data/expense.data'; import { UndoMergeData } from '../mock-data/undo-merge.data'; import { AccountsService } from './accounts.service'; @@ -53,6 +54,10 @@ import { expensePolicyData } from '../mock-data/expense-policy.data'; import { txnAccountData } from '../mock-data/txn-account.data'; import { txnCustomPropertiesData2, txnCustomPropertiesData6 } from '../mock-data/txn-custom-properties.data'; import { FilterQueryParams } from '../models/filter-query-params.model'; +import { + matchCCCExpenseResponseData, + unmatchCCCExpenseResponseData, +} from '../mock-data/corporate-card-transaction-response.data'; describe('TransactionService', () => { let transactionService: TransactionService; @@ -242,26 +247,6 @@ describe('TransactionService', () => { }); }); - it('getETxnc(): should get list of extended transactions', (done) => { - apiV2Service.get.and.returnValue(of(etxncData)); - - const params = { - offset: 0, - limit: 1, - params: { - tx_org_user_id: 'eq.ouX8dwsbLCLv', - tx_report_id: 'eq.rpFvmTgyeBjN', - order: 'tx_txn_dt.desc,tx_id.desc', - }, - }; - - transactionService.getETxnc(params).subscribe((res) => { - expect(res).toEqual(etxncData.data); - expect(apiV2Service.get).toHaveBeenCalledOnceWith('/expenses', params); - done(); - }); - }); - it('getDefaultVehicleType(): should get default vehicle type', (done) => { const defaultVehicleType = 'two_wheeler'; storageService.get.and.returnValue(Promise.resolve(defaultVehicleType)); @@ -426,6 +411,11 @@ describe('TransactionService', () => { expect(transactionService.generateTypeOrFilter(filters)).toEqual(typeOrFilter); }); + it('fixDates(): should fix dates', () => { + // @ts-ignore + expect(transactionService.fixDates(expenseDataWithDateString)).toEqual(expenseData1); + }); + it('getPaymentModeforEtxn(): should return payment mode for etxn', () => { spyOn(transactionService, 'isEtxnInPaymentMode').and.returnValue(true); const paymentModeList = [ @@ -867,35 +857,6 @@ describe('TransactionService', () => { }); }); - describe('getEtxn():', () => { - it('it should get etxn from transaction ID without sub category', (done) => { - apiService.get.and.returnValue(of(expenseData1)); - dateService.fixDates.and.returnValue(expenseData1); - - const transactionID = 'tx5fBcPBAxLv'; - transactionService.getEtxn(transactionID).subscribe((res) => { - expect(res).toEqual(expenseData1); - expect(apiService.get).toHaveBeenCalledOnceWith('/etxns/' + transactionID); - expect(dateService.fixDates).toHaveBeenCalledOnceWith(res); - done(); - }); - }); - - it('it should get etxn from transaction ID with sub category', (done) => { - apiService.get.and.returnValue(of(etxnData)); - dateService.fixDates.and.returnValue(etxnData); - - const transactionID = 'txCBp2jIK6G3'; - - transactionService.getEtxn(transactionID).subscribe((res) => { - expect(res).toEqual(etxnData); - expect(apiService.get).toHaveBeenCalledOnceWith('/etxns/' + transactionID); - expect(dateService.fixDates).toHaveBeenCalledOnceWith(res); - done(); - }); - }); - }); - it('getTransactionStats(): should return transaction stats', (done) => { authService.getEou.and.returnValue(Promise.resolve(eouRes2)); apiV2Service.getStats.and.returnValue(of(txnStats)); @@ -1033,16 +994,20 @@ describe('TransactionService', () => { }); it('unmatchCCCExpense(): should unmatch ccc expense', (done) => { - apiService.post.and.returnValue(of(null)); + spenderPlatformV1ApiService.post.and.returnValue(of(unmatchCCCExpenseResponseData)); - const transactionId = 'txBldpJrBafX'; - const corporateCreditCardExpenseId = 'ccce4xphr6tZQm'; + const id = 'btxnSte7sVQCM8'; + const expenseId = 'txmF3wgfj0Bs'; - transactionService.unmatchCCCExpense(transactionId, corporateCreditCardExpenseId).subscribe((res) => { - expect(res).toBeNull(); - expect(apiService.post).toHaveBeenCalledOnceWith('/transactions/unmatch', { - transaction_id: transactionId, - corporate_credit_card_expense_id: corporateCreditCardExpenseId, + const payload = { + id, + expense_ids: [expenseId], + }; + + transactionService.unmatchCCCExpense(id, expenseId).subscribe((res) => { + expect(res).toEqual(unmatchCCCExpenseResponseData); + expect(spenderPlatformV1ApiService.post).toHaveBeenCalledOnceWith('/corporate_card_transactions/unmatch', { + data: payload, }); done(); }); @@ -1083,16 +1048,6 @@ describe('TransactionService', () => { }); }); - describe('getDeletableTxns():', () => { - it('should return deletable transactions', () => { - expect(transactionService.getDeletableTxns(apiExpenseRes)).toEqual(apiExpenseRes); - }); - - it('should return undefined if no transaction ', () => { - expect(transactionService.getDeletableTxns(undefined)).toBeUndefined(); - }); - }); - describe('getExpenseDeletionMessage():', () => { it('should return expense deletion message for single', () => { expect(transactionService.getExpenseDeletionMessage(apiExpenseRes)).toEqual( @@ -1215,6 +1170,7 @@ describe('TransactionService', () => { }); }); + // Needs clean up : once we remove older my-expenses-page completely describe('getETxnUnflattened():', () => { it('it should get etxn from transaction ID without sub category', (done) => { apiService.get.and.returnValue(of(expenseData3)); @@ -1340,16 +1296,20 @@ describe('TransactionService', () => { }); it('matchCCCExpense(): should match ccc expense', (done) => { - apiService.post.and.returnValue(of(null)); + spenderPlatformV1ApiService.post.and.returnValue(of(matchCCCExpenseResponseData)); - const transactionId = 'txBRcjOg1spF'; - const corporateCreditCardExpenseId = 'cccetzVpWd2Pgz'; + const id = 'btxnSte7sVQCM8'; + const expenseId = 'txmF3wgfj0Bs'; - transactionService.matchCCCExpense(transactionId, corporateCreditCardExpenseId).subscribe((res) => { - expect(res).toBeNull(); - expect(apiService.post).toHaveBeenCalledOnceWith('/transactions/match', { - transaction_id: transactionId, - corporate_credit_card_expense_id: corporateCreditCardExpenseId, + const payload = { + id, + expense_ids: [expenseId], + }; + + transactionService.matchCCCExpense(id, expenseId).subscribe((res) => { + expect(res).toEqual(matchCCCExpenseResponseData); + expect(spenderPlatformV1ApiService.post).toHaveBeenCalledOnceWith('/corporate_card_transactions/match', { + data: payload, }); done(); }); diff --git a/src/app/core/services/transaction.service.ts b/src/app/core/services/transaction.service.ts index 0940ccf8c8..9bf7d8030a 100644 --- a/src/app/core/services/transaction.service.ts +++ b/src/app/core/services/transaction.service.ts @@ -40,6 +40,8 @@ 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'; import { AccountType } from '../enums/account-type.enum'; +import { Expense as PlatformExpense } from '../models/platform/v1/expense.model'; +import { CorporateCardTransactionRes } from '../models/platform/v1/corporate-card-transaction-res.model'; enum FilterState { READY_TO_REPORT = 'READY_TO_REPORT', @@ -76,8 +78,7 @@ export class TransactionService { private userEventService: UserEventService, private paymentModesService: PaymentModesService, private orgSettingsService: OrgSettingsService, - private accountsService: AccountsService, - private spenderPlatformAPIV1Service: SpenderPlatformV1ApiService + private accountsService: AccountsService ) { expensesCacheBuster$.subscribe(() => { this.userEventService.clearTaskCache(); @@ -97,27 +98,6 @@ export class TransactionService { return of(null); } - @Cacheable({ - cacheBusterObserver: expensesCacheBuster$, - }) - getEtxn(txnId: string): Observable { - // TODO api v2 - return this.apiService.get('/etxns/' + txnId).pipe( - map((transaction: Expense) => { - let categoryDisplayName = transaction.tx_org_category; - if ( - transaction.tx_sub_category && - transaction.tx_sub_category.toLowerCase() !== categoryDisplayName.toLowerCase() - ) { - categoryDisplayName += ' / ' + transaction.tx_sub_category; - } - transaction.tx_categoryDisplayName = categoryDisplayName; - - return this.dateService.fixDates(transaction); - }) - ); - } - @CacheBuster({ cacheBusterNotifier: expensesCacheBuster$, }) @@ -135,6 +115,8 @@ export class TransactionService { @Cacheable({ cacheBusterObserver: expensesCacheBuster$, }) + + // TODO: Remove/Update method once we remove older my-expenses-page completely getMyExpenses( config: Partial<{ offset: number; limit: number; order: string; queryParams: EtxnParams }> = { offset: 0, @@ -174,6 +156,8 @@ export class TransactionService { @Cacheable({ cacheBusterObserver: expensesCacheBuster$, }) + + // TODO: Remove/Update method once we remove older my-expenses-page completely getAllExpenses(config: Partial<{ order: string; queryParams: EtxnParams }>): Observable { return this.getMyExpensesCount(config.queryParams).pipe( switchMap((count) => { @@ -196,7 +180,8 @@ export class TransactionService { @Cacheable({ cacheBusterObserver: expensesCacheBuster$, }) - // TODO: Remove `any` type once the stats response implementation is fixed + + // TODO: Remove/Update method once we remove older my-expenses-page completely getTransactionStats(aggregates: string, queryParams: EtxnParams): Observable { return from(this.authService.getEou()).pipe( switchMap((eou) => @@ -329,6 +314,7 @@ export class TransactionService { ); } + // TODO: Remove/Update method once we remove older my-expenses-page completely getPaginatedETxncCount(): Observable<{ count: number }> { return this.networkService.isOnline().pipe( switchMap((isOnline) => { @@ -345,14 +331,7 @@ export class TransactionService { ); } - getETxnc(params: { offset: number; limit: number; params: EtxnParams }): Observable { - return this.apiV2Service - .get('/expenses', { - ...params, - }) - .pipe(map((etxns) => etxns.data)); - } - + // TODO: Remove/Update method once we remove older my-expenses-page completely getMyExpensesCount(queryParams: EtxnParams): Observable { return this.getMyExpenses({ offset: 0, @@ -415,12 +394,12 @@ export class TransactionService { ); } + // TODO: Remove/Update method once we remove older my-expenses-page completely getETxnUnflattened(txnId: string): Observable { return this.apiService.get('/etxns/' + txnId).pipe( map((data) => { const etxn: UnflattenedTransaction = this.dataTransformService.unflatten(data); this.dateService.fixDates(etxn.tx); - // Adding a field categoryDisplayName in transaction object to save funciton calls let categoryDisplayName = etxn.tx.org_category; if (etxn.tx.sub_category && etxn.tx.sub_category.toLowerCase() !== categoryDisplayName.toLowerCase()) { @@ -432,13 +411,13 @@ export class TransactionService { ); } - matchCCCExpense(txnId: string, corporateCreditCardExpenseId: string): Observable { - const data = { - transaction_id: txnId, - corporate_credit_card_expense_id: corporateCreditCardExpenseId, + matchCCCExpense(id: string, expenseId: string): Observable { + const payload = { + id, + expense_ids: [expenseId], }; - return this.apiService.post('/transactions/match', data); + return this.spenderPlatformV1ApiService.post('/corporate_card_transactions/match', { data: payload }); } review(txnId: string): Observable { @@ -457,13 +436,13 @@ export class TransactionService { return this.apiService.post('/transactions/' + txnId + '/upload_b64', data); } - unmatchCCCExpense(txnId: string, corporateCreditCardExpenseId: string): Observable { - const data = { - transaction_id: txnId, - corporate_credit_card_expense_id: corporateCreditCardExpenseId, + unmatchCCCExpense(id: string, expenseId: string): Observable { + const payload = { + id, + expense_ids: [expenseId], }; - return this.apiService.post('/transactions/unmatch', data); + return this.spenderPlatformV1ApiService.post('/corporate_card_transactions/unmatch', { data: payload }); } getVendorDetails(expense: Expense): string { @@ -503,10 +482,6 @@ export class TransactionService { return expenses.filter((expense) => expense && !expense.tx_corporate_credit_card_expense_group_id); } - getDeletableTxns(expenses: Partial[]): Partial[] { - return expenses?.filter((expense) => expense && expense.tx_user_can_delete); - } - getExpenseDeletionMessage(expensesToBeDeleted: Partial[]): string { return `You are about to permanently delete ${ expensesToBeDeleted.length === 1 ? '1 selected expense.' : expensesToBeDeleted.length + ' selected expenses.' @@ -808,6 +783,207 @@ export class TransactionService { } } + sourceAccountTypePublicMapping(type: string): string { + return type === 'PERSONAL_CASH_ACCOUNT' ? 'PERSONAL_ACCOUNT' : type; + } + + // Todo : Remove transformExpense method once upsert in migrated to platform + transformExpense(expense: PlatformExpense): Partial { + const updatedExpense = { + tx: { + id: expense.id, + created_at: expense.created_at, + txn_dt: expense.spent_at, + categoryDisplayName: expense.category?.display_name, + num_files: expense.files?.length, + org_category: expense.category?.name, + fyle_category: expense.category?.system_category, + state: expense.state, + admin_amount: expense.admin_amount, + policy_amount: expense.policy_amount, + skip_reimbursement: !expense.is_reimbursable, + amount: expense.amount, + currency: expense.currency, + user_amount: expense.claim_amount, + orig_amount: expense.foreign_amount, + orig_currency: expense.foreign_currency, + from_dt: expense.started_at, + to_dt: expense.ended_at, + vendor: expense.merchant, + distance: expense.distance, + distance_unit: expense.distance_unit, + locations: expense.locations, + verification_state: expense.is_verified ? 'VERIFIED' : null, + org_user_id: expense.employee_id, + expense_number: expense.seq_num, + taxi_travel_class: expense.travel_classes && expense.travel_classes[0], + bus_travel_class: expense.travel_classes && expense.travel_classes[0], + train_travel_class: expense.travel_classes && expense.travel_classes[0], + flight_journey_travel_class: expense.travel_classes && expense.travel_classes[0], + flight_return_travel_class: expense.travel_classes && expense.travel_classes[1], + hotel_is_breakfast_provided: expense.hotel_is_breakfast_provided, + tax_group_id: expense.tax_group_id, + creator_id: expense.employee?.id, + request_id: expense.is_receipt_mandatory, + report_id: expense.report_id, + org_category_id: expense.category_id, + cost_center_id: expense.cost_center_id, + cost_center_name: expense.cost_center?.name, + cost_center_code: expense.cost_center?.code, + project_id: expense.project_id, + project_name: expense.project?.name, + custom_properties: expense.custom_fields.map((item) => item as TxnCustomProperties), + purpose: expense.purpose, + billable: expense.is_billable, + sub_category: expense.category?.sub_category, + tax_amount: expense?.tax_amount, + corporate_credit_card_expense_group_id: + expense.matched_corporate_card_transactions?.length > 0 + ? expense.matched_corporate_card_transactions[0].id + : null, + split_group_id: expense.split_group_id, + split_group_user_amount: expense.split_group_amount, + receipt_required: expense.is_receipt_mandatory, + per_diem_rate_id: expense.per_diem_rate_id, + num_days: expense.per_diem_num_days, + mileage_rate_id: expense.mileage_rate_id, + mileage_rate: expense.mileage_rate?.rate, + mileage_vehicle_type: expense.mileage_rate?.vehicle_type, + mileage_is_round_trip: expense.mileage_is_round_trip, + mileage_calculated_distance: expense.mileage_calculated_distance, + mileage_calculated_amount: expense.mileage_calculated_amount, + manual_flag: expense.is_manually_flagged, + policy_flag: expense.is_policy_flagged, + extracted_data: expense.extracted_data + ? { + vendor: expense.extracted_data?.vendor_name, + currency: expense.extracted_data?.currency, + amount: expense.extracted_data?.amount, + date: expense.extracted_data?.date, + category: expense.extracted_data?.category, + invoice_dt: expense.extracted_data?.invoice_dt, + } + : null, + matched_corporate_card_transactions: Array.isArray(expense.matched_corporate_card_transactions) + ? expense.matched_corporate_card_transactions.map((transaction) => ({ + id: transaction.id, + group_id: transaction.id, + amount: transaction.amount, + vendor: transaction.merchant, + txn_dt: transaction.spent_at.toISOString(), + currency: transaction.currency, + description: transaction.description, + card_or_account_number: transaction.corporate_card_number, + corporate_credit_card_account_number: transaction.corporate_card_number, + orig_amount: transaction.foreign_amount, + orig_currency: transaction.foreign_currency, + status: transaction.status, + })) + : null, + source_account_id: expense.source_account_id, + org_category_code: expense.category?.code, + project_code: expense.project?.code, + physical_bill: expense.is_physical_bill_submitted, + physical_bill_at: expense.physical_bill_submitted_at, + }, + source: { + account_id: expense.source_account?.id, + account_type: this.sourceAccountTypePublicMapping(expense.source_account?.type), + }, + ou: { + id: expense.employee?.id, + org_id: expense.employee?.org_id, + }, + }; + this.dateService.fixDates(updatedExpense.tx); + return updatedExpense; + } + + // Todo : Remove transformRawExpenses method once upsert in migrated to platform + transformRawExpense(expense: PlatformExpense): Partial { + const updatedExpense = { + tx_id: expense.id, + tx_txn_dt: expense.spent_at, + tx_expense_number: expense.seq_num, + tx_num_files: expense.files?.length, + tx_org_category: expense.category?.name, + tx_amount: expense.amount, + tx_purpose: expense.purpose, + tx_currency: expense.currency, + tx_vendor: expense.merchant, + tx_split_group_id: expense.split_group_id, + tx_split_group_user_amount: expense.split_group_amount, + tx_skip_reimbursement: !expense.is_reimbursable, + tx_file_ids: expense.file_ids, + tx_creator_id: expense.employee?.id, + tx_state: expense.state, + tx_tax_org_id: expense.tax_group_id, + tg_name: expense.tax_group?.name, + tx_project_name: expense.project?.name, + tx_project_id: expense.project_id, + tx_cost_center_name: expense.cost_center?.name, + tx_cost_center_id: expense.cost_center_id, + tx_corporate_credit_card_expense_group_id: + expense.matched_corporate_card_transactions?.length > 0 + ? expense.matched_corporate_card_transactions[0].id + : null, + tx_custom_properties: expense.custom_fields.map((item) => item as TxnCustomProperties), + tx_locations: expense.locations, + tx_hotel_is_breakfast_provided: expense.hotel_is_breakfast_provided, + tx_billable: expense.is_billable, + tx_fyle_category: expense.category?.system_category, + tx_orig_currency: expense.foreign_currency, + tx_orig_amount: expense.foreign_amount, + tx_taxi_travel_class: expense.travel_classes && expense.travel_classes[0], + tx_bus_travel_class: expense.travel_classes && expense.travel_classes[0], + tx_flight_journey_travel_class: expense.travel_classes && expense.travel_classes[0], + tx_flight_return_travel_class: expense.travel_classes && expense.travel_classes[1], + tx_train_travel_class: expense.travel_classes && expense.travel_classes[0], + tx_distance: expense.distance, + tx_distance_unit: expense.distance_unit, + tx_per_diem_rate_id: expense.per_diem_rate_id?.toString(), + tx_num_days: expense.per_diem_num_days, + tx_mileage_rate_id: expense.mileage_rate_id?.toString(), + tx_mileage_rate: expense.mileage_rate?.rate, + tx_mileage_vehicle_type: expense.mileage_rate?.vehicle_type, + tx_mileage_is_round_trip: expense.mileage_is_round_trip, + tx_mileage_calculated_distance: expense.mileage_calculated_distance, + tx_mileage_calculated_amount: expense.mileage_calculated_amount, + tx_manual_flag: expense.is_manually_flagged, + tx_policy_flag: expense.is_policy_flagged, + tx_extracted_data: expense.extracted_data + ? { + vendor: expense.extracted_data?.vendor_name, + currency: expense.extracted_data?.currency, + amount: expense.extracted_data?.amount, + date: expense.extracted_data?.date, + } + : null, + tx_matched_corporate_card_transactions: Array.isArray(expense.matched_corporate_card_transactions) + ? expense.matched_corporate_card_transactions.map((transaction) => ({ + id: transaction.id, + group_id: transaction.id, + amount: transaction.amount, + vendor: transaction.merchant, + txn_dt: transaction.spent_at.toISOString(), + currency: transaction.currency, + description: transaction.description, + card_or_account_number: transaction.corporate_card_number, + orig_amount: transaction.foreign_amount, + orig_currency: transaction.foreign_currency, + status: transaction.status, + })) + : null, + tx_org_category_code: expense.category?.code, + tx_project_code: expense.project?.code, + tx_physical_bill: expense.is_physical_bill_submitted, + tx_physical_bill_at: expense.physical_bill_submitted_at, + source_account_id: expense.source_account_id, + source_account_type: this.sourceAccountTypePublicMapping(expense.source_account?.type), + }; + return updatedExpense; + } + private getTxnAccount(): Observable<{ source_account_id: string; skip_reimbursement: boolean }> { return forkJoin({ orgSettings: this.orgSettingsService.get(), @@ -827,6 +1003,24 @@ export class TransactionService { ); } + private fixDates(data: Expense): Expense { + data.tx_created_at = new Date(data.tx_created_at); + if (data.tx_txn_dt) { + data.tx_txn_dt = new Date(data.tx_txn_dt); + } + + if (data.tx_from_dt) { + data.tx_from_dt = new Date(data.tx_from_dt); + } + + if (data.tx_to_dt) { + data.tx_to_dt = new Date(data.tx_to_dt); + } + + data.tx_updated_at = new Date(data.tx_updated_at); + return data; + } + private generateStateOrFilter(filters: Partial, newQueryParamsCopy: FilterQueryParams): string[] { const stateOrFilter: string[] = []; if (filters.state) { diff --git a/src/app/core/services/transactions-outbox.service.spec.ts b/src/app/core/services/transactions-outbox.service.spec.ts index f3b5efa07d..a8d8137491 100644 --- a/src/app/core/services/transactions-outbox.service.spec.ts +++ b/src/app/core/services/transactions-outbox.service.spec.ts @@ -11,6 +11,7 @@ import { StatusService } from './status.service'; import { StorageService } from './storage.service'; import { TrackingService } from './tracking.service'; import { TransactionService } from './transaction.service'; +import { ExpensesService } from './platform/v1/spender/expenses.service'; import { TransactionsOutboxService } from './transactions-outbox.service'; import { outboxQueueData1 } from '../mock-data/outbox-queue.data'; @@ -25,6 +26,7 @@ describe('TransactionsOutboxService', () => { let storageService: jasmine.SpyObj; let dateService: jasmine.SpyObj; let transactionService: jasmine.SpyObj; + let expensesService: jasmine.SpyObj; let fileService: jasmine.SpyObj; let statusService: jasmine.SpyObj; let reportService: jasmine.SpyObj; @@ -38,6 +40,7 @@ describe('TransactionsOutboxService', () => { const storageServiceSpy = jasmine.createSpyObj('StorageService', ['get', 'set']); const dateServiceSpy = jasmine.createSpyObj('DateService', ['getUTCDate', 'fixDates']); const transactionServiceSpy = jasmine.createSpyObj('TransactionService', ['post', 'matchCCCExpense', 'upsert']); + const expensesServiceSpy = jasmine.createSpyObj('ExpensesService', ['getExpensesById']); const fileServiceSpy = jasmine.createSpyObj('FileService', ['post', 'uploadUrl', 'uploadComplete']); const statusServiceSpy = jasmine.createSpyObj('StatusService', ['post']); const reportServiceSpy = jasmine.createSpyObj('ReportService', ['post']); @@ -51,6 +54,7 @@ describe('TransactionsOutboxService', () => { { provide: StorageService, useValue: storageServiceSpy }, { provide: DateService, useValue: dateServiceSpy }, { provide: TransactionService, useValue: transactionServiceSpy }, + { provide: ExpensesService, useValue: expensesServiceSpy }, { provide: FileService, useValue: fileServiceSpy }, { provide: StatusService, useValue: statusServiceSpy }, { provide: ReportService, useValue: reportServiceSpy }, @@ -62,6 +66,7 @@ describe('TransactionsOutboxService', () => { storageService = TestBed.inject(StorageService) as jasmine.SpyObj; dateService = TestBed.inject(DateService) as jasmine.SpyObj; transactionService = TestBed.inject(TransactionService) as jasmine.SpyObj; + expensesService = TestBed.inject(ExpensesService) as jasmine.SpyObj; fileService = TestBed.inject(FileService) as jasmine.SpyObj; statusService = TestBed.inject(StatusService) as jasmine.SpyObj; reportService = TestBed.inject(ReportService) as jasmine.SpyObj; diff --git a/src/app/core/services/transactions-outbox.service.ts b/src/app/core/services/transactions-outbox.service.ts index 15b7685d4a..6d3d024638 100644 --- a/src/app/core/services/transactions-outbox.service.ts +++ b/src/app/core/services/transactions-outbox.service.ts @@ -18,6 +18,7 @@ import { OrgUserSettingsService } from './org-user-settings.service'; import { Transaction } from '../models/v1/transaction.model'; import { FileObject } from '../models/file-obj.model'; import { OutboxQueue } from '../models/outbox-queue.model'; +import { ExpensesService } from './platform/v1/spender/expenses.service'; @Injectable({ providedIn: 'root', @@ -48,7 +49,8 @@ export class TransactionsOutboxService { private reportService: ReportService, private trackingService: TrackingService, private currencyService: CurrencyService, - private orgUserSettingsService: OrgUserSettingsService + private orgUserSettingsService: OrgUserSettingsService, + private expensesService: ExpensesService ) { this.ROOT_ENDPOINT = environment.ROOT_URL; this.restoreQueue(); @@ -303,7 +305,7 @@ export class TransactionsOutboxService { return new Promise((resolve) => { if (cccId) { this.transactionService - .matchCCCExpense(transactionId, cccId) + .matchCCCExpense(cccId, transactionId) .toPromise() .then(() => { resolve(null); @@ -350,23 +352,24 @@ export class TransactionsOutboxService { }); } if (entry.dataUrls && entry.dataUrls.length > 0) { - that.transactionService - .getETxnUnflattened(resp.id) + that.expensesService + .getExpenseById(resp.id) .toPromise() - .then((etxn) => { + .then((expense) => { entry.dataUrls.forEach((dataUrl) => { if (dataUrl.callBackUrl) { + const transformedExpense = that.transactionService.transformExpense(expense); that.httpClient.post(dataUrl.callBackUrl, { entered_data: { - amount: etxn.tx.amount, - currency: etxn.tx.currency, - orig_currency: etxn.tx.orig_currency, - orig_amount: etxn.tx.orig_amount, - date: etxn.tx.txn_dt, - vendor: etxn.tx.vendor, - category: etxn.tx.fyle_category, - external_id: etxn.tx.external_id, - transaction_id: etxn.tx.id, + amount: transformedExpense.tx.amount, + currency: transformedExpense.tx.currency, + orig_currency: transformedExpense.tx.orig_currency, + orig_amount: transformedExpense.tx.orig_amount, + date: transformedExpense.tx.txn_dt, + vendor: transformedExpense.tx.vendor, + category: transformedExpense.tx.fyle_category, + external_id: transformedExpense.tx.external_id, + transaction_id: transformedExpense.tx.id, }, }); } diff --git a/src/app/deep-link-redirection/deep-link-redirection.page.spec.ts b/src/app/deep-link-redirection/deep-link-redirection.page.spec.ts index ac9f307b56..6fa18958b8 100644 --- a/src/app/deep-link-redirection/deep-link-redirection.page.spec.ts +++ b/src/app/deep-link-redirection/deep-link-redirection.page.spec.ts @@ -5,6 +5,7 @@ import { LoaderService } from '../core/services/loader.service'; import { AdvanceRequestService } from '../core/services/advance-request.service'; import { AuthService } from '../core/services/auth.service'; import { TransactionService } from '../core/services/transaction.service'; +import { ExpensesService } from '../core/services/platform/v1/spender/expenses.service'; import { ReportService } from '../core/services/report.service'; import { DeepLinkRedirectionPage } from './deep-link-redirection.page'; import { expectedSingleErpt } from '../core/mock-data/report-unflattened.data'; @@ -13,6 +14,8 @@ import { apiEouRes } from '../core/mock-data/extended-org-user.data'; import { unflattenedTxnData } from '../core/mock-data/unflattened-txn.data'; import { singleErqUnflattened } from '../core/mock-data/extended-advance-request.data'; import { DeepLinkService } from '../core/services/deep-link.service'; +import { platformExpenseData } from 'src/app/core/mock-data/platform/v1/expense.data'; +import { transformedExpenseData } from '../core/mock-data/transformed-expense.data'; describe('DeepLinkRedirectionPage', () => { let component: DeepLinkRedirectionPage; @@ -21,6 +24,7 @@ describe('DeepLinkRedirectionPage', () => { let loaderService: jasmine.SpyObj; let advanceRequestService: jasmine.SpyObj; let transactionService: jasmine.SpyObj; + let expensesService: jasmine.SpyObj; let reportService: jasmine.SpyObj; let authService: jasmine.SpyObj; let deepLinkService: jasmine.SpyObj; @@ -30,7 +34,8 @@ describe('DeepLinkRedirectionPage', () => { const routerSpy = jasmine.createSpyObj('Router', ['navigate']); const loaderServiceSpy = jasmine.createSpyObj('LoaderService', ['showLoader', 'hideLoader']); const advanceRequestServiceSpy = jasmine.createSpyObj('AdvanceRequestService', ['getEReq']); - const transactionServiceSpy = jasmine.createSpyObj('TransactionService', ['getETxnUnflattened']); + const transactionServiceSpy = jasmine.createSpyObj('TransactionService', ['transformExpense']); + const expensesServiceSpy = jasmine.createSpyObj('ExpensesService', ['getExpenseById']); const reportServiceSpy = jasmine.createSpyObj('ReportService', ['getERpt']); const authServiceSpy = jasmine.createSpyObj('AuthService', ['getEou']); const deepLinkServiceSpy = jasmine.createSpyObj('DeepLinkService', ['getExpenseRoute']); @@ -43,6 +48,7 @@ describe('DeepLinkRedirectionPage', () => { { provide: LoaderService, useValue: loaderServiceSpy }, { provide: AdvanceRequestService, useValue: advanceRequestServiceSpy }, { provide: TransactionService, useValue: transactionServiceSpy }, + { provide: ExpensesService, useValue: expensesServiceSpy }, { provide: ReportService, useValue: reportServiceSpy }, { provide: AuthService, useValue: authServiceSpy }, { provide: DeepLinkService, useValue: deepLinkServiceSpy }, @@ -64,6 +70,7 @@ describe('DeepLinkRedirectionPage', () => { loaderService = TestBed.inject(LoaderService) as jasmine.SpyObj; advanceRequestService = TestBed.inject(AdvanceRequestService) as jasmine.SpyObj; transactionService = TestBed.inject(TransactionService) as jasmine.SpyObj; + expensesService = TestBed.inject(ExpensesService) as jasmine.SpyObj; reportService = TestBed.inject(ReportService) as jasmine.SpyObj; authService = TestBed.inject(AuthService) as jasmine.SpyObj; deepLinkService = TestBed.inject(DeepLinkService) as jasmine.SpyObj; @@ -166,7 +173,8 @@ describe('DeepLinkRedirectionPage', () => { loaderService.showLoader.and.resolveTo(); loaderService.hideLoader.and.resolveTo(); - transactionService.getETxnUnflattened.and.returnValue(of(unflattenedTxnData)); + expensesService.getExpenseById.and.returnValue(of(platformExpenseData)); + transactionService.transformExpense.and.returnValue(transformedExpenseData); deepLinkService.getExpenseRoute.and.returnValue(['/', 'enterprise', 'view_expense']); authService.getEou.and.resolveTo(apiEouRes); @@ -174,7 +182,7 @@ describe('DeepLinkRedirectionPage', () => { }); it('should redirect to the expense page in logged-in org if orgId is not present in route param', fakeAsync(() => { - const txnId = 'txAfNrce75O'; + const txnId = 'txvslh8aQMbu'; activeroutemock.snapshot.params = { sub_module: 'expense', id: txnId, @@ -183,14 +191,15 @@ describe('DeepLinkRedirectionPage', () => { component.redirectToExpenseModule(); tick(200); - expect(transactionService.getETxnUnflattened).toHaveBeenCalledOnceWith(txnId); - expect(deepLinkService.getExpenseRoute).toHaveBeenCalledOnceWith(unflattenedTxnData); + expect(expensesService.getExpenseById).toHaveBeenCalledOnceWith(txnId); + expect(transactionService.transformExpense).toHaveBeenCalledOnceWith(platformExpenseData); + expect(deepLinkService.getExpenseRoute).toHaveBeenCalledOnceWith(transformedExpenseData); expect(router.navigate).toHaveBeenCalledOnceWith(['/', 'enterprise', 'view_expense', { id: txnId }]); })); it('should redirect to the expense page if expense orgId is same as logged-in org id', fakeAsync(() => { const orgId = 'orNVthTo2Zyo'; - const txnId = 'tx3qHxFNgRcZ'; + const txnId = 'txvslh8aQMbu'; activeroutemock.snapshot.params = { sub_module: 'expense', id: txnId, @@ -201,9 +210,10 @@ describe('DeepLinkRedirectionPage', () => { tick(200); expect(loaderService.showLoader).toHaveBeenCalledOnceWith('Loading....'); expect(authService.getEou).toHaveBeenCalledOnceWith(); - expect(transactionService.getETxnUnflattened).toHaveBeenCalledOnceWith(txnId); + expect(expensesService.getExpenseById).toHaveBeenCalledOnceWith(txnId); + expect(transactionService.transformExpense).toHaveBeenCalledOnceWith(platformExpenseData); expect(loaderService.hideLoader).toHaveBeenCalledTimes(2); - expect(deepLinkService.getExpenseRoute).toHaveBeenCalledOnceWith(unflattenedTxnData); + expect(deepLinkService.getExpenseRoute).toHaveBeenCalledOnceWith(transformedExpenseData); expect(router.navigate).toHaveBeenCalledOnceWith(['/', 'enterprise', 'view_expense', { id: txnId }]); })); @@ -246,7 +256,7 @@ describe('DeepLinkRedirectionPage', () => { id: txnId, orgId, }; - transactionService.getETxnUnflattened.and.returnValue(throwError(() => {})); + expensesService.getExpenseById.and.returnValue(throwError(() => {})); component.redirectToExpenseModule(); tick(500); expect(component.switchOrg).toHaveBeenCalledOnceWith(); diff --git a/src/app/deep-link-redirection/deep-link-redirection.page.ts b/src/app/deep-link-redirection/deep-link-redirection.page.ts index 6bb3b83a24..fe07b9c4d1 100644 --- a/src/app/deep-link-redirection/deep-link-redirection.page.ts +++ b/src/app/deep-link-redirection/deep-link-redirection.page.ts @@ -5,8 +5,9 @@ import { AdvanceRequestService } from '../core/services/advance-request.service' import { AuthService } from '../core/services/auth.service'; import { TransactionService } from '../core/services/transaction.service'; import { ReportService } from '../core/services/report.service'; -import { EMPTY, catchError, filter, finalize, from, shareReplay, switchMap } from 'rxjs'; +import { EMPTY, catchError, filter, finalize, from, shareReplay, switchMap, map } from 'rxjs'; import { DeepLinkService } from '../core/services/deep-link.service'; +import { ExpensesService } from '../core/services/platform/v1/spender/expenses.service'; @Component({ selector: 'app-deep-link-redirection', @@ -22,7 +23,8 @@ export class DeepLinkRedirectionPage { private transactionService: TransactionService, private authService: AuthService, private reportService: ReportService, - private deepLinkService: DeepLinkService + private deepLinkService: DeepLinkService, + private expensesService: ExpensesService ) {} ionViewWillEnter() { @@ -62,13 +64,14 @@ export class DeepLinkRedirectionPage { } async redirectToExpenseModule() { - const expenseOrgId = this.activatedRoute.snapshot.params.orgId; - const txnId = this.activatedRoute.snapshot.params.id; + const expenseOrgId = this.activatedRoute.snapshot.params.orgId as string; + const txnId = this.activatedRoute.snapshot.params.id as string; if (!expenseOrgId) { - this.transactionService.getETxnUnflattened(txnId).subscribe((etxn) => { + this.expensesService.getExpenseById(txnId).subscribe((expense) => { + const etxn = this.transactionService.transformExpense(expense); const route = this.deepLinkService.getExpenseRoute(etxn); - this.router.navigate([...route, { id: this.activatedRoute.snapshot.params.id }]); + this.router.navigate([...route, { id: txnId }]); }); } else { const eou$ = from(this.loaderService.showLoader('Loading....')).pipe( @@ -80,17 +83,18 @@ export class DeepLinkRedirectionPage { shareReplay(1) ); - //If expenseOrgId is same as user orgId, then redirect to expense page + // If expenseOrgId is the same as user orgId, then redirect to the expense page eou$ .pipe( filter((eou) => expenseOrgId === eou.ou.org_id), - switchMap(() => this.transactionService.getETxnUnflattened(txnId)), + switchMap(() => this.expensesService.getExpenseById(txnId)), + map((expense) => this.transactionService.transformExpense(expense)), finalize(() => from(this.loaderService.hideLoader())) ) .subscribe({ next: (etxn) => { const route = this.deepLinkService.getExpenseRoute(etxn); - this.router.navigate([...route, { id: this.activatedRoute.snapshot.params.id }]); + this.router.navigate([...route, { id: txnId }]); }, error: () => this.switchOrg(), }); @@ -119,12 +123,22 @@ export class DeepLinkRedirectionPage { await this.loaderService.showLoader('Loading....'); const currentEou = await this.authService.getEou(); - this.reportService.getERpt(this.activatedRoute.snapshot.params.id).subscribe( + this.reportService.getERpt(this.activatedRoute.snapshot.params.id as string).subscribe( (res) => { if (currentEou.ou.id === res.rp.org_user_id) { - this.router.navigate(['/', 'enterprise', 'my_view_report', { id: this.activatedRoute.snapshot.params.id }]); + this.router.navigate([ + '/', + 'enterprise', + 'my_view_report', + { id: this.activatedRoute.snapshot.params.id as string }, + ]); } else { - this.router.navigate(['/', 'enterprise', 'view_team_report', { id: this.activatedRoute.snapshot.params.id }]); + this.router.navigate([ + '/', + 'enterprise', + 'view_team_report', + { id: this.activatedRoute.snapshot.params.id as string }, + ]); } }, () => { diff --git a/src/app/fyle/add-edit-expense/add-edit-expense-1.spec.ts b/src/app/fyle/add-edit-expense/add-edit-expense-1.spec.ts index 2d3741de59..9ca33fed1a 100644 --- a/src/app/fyle/add-edit-expense/add-edit-expense-1.spec.ts +++ b/src/app/fyle/add-edit-expense/add-edit-expense-1.spec.ts @@ -192,7 +192,6 @@ export function TestCases1(getTestBed) { distance: [], distance_unit: [], custom_inputs: new FormArray([]), - duplicate_detection_reason: [], billable: [], costCenter: [], hotel_is_breakfast_provided: [], @@ -763,12 +762,10 @@ export function TestCases1(getTestBed) { describe('markCCCAsPersonal():', () => { it('should mark a CCC txn as personal', (done) => { - transactionService.delete.and.returnValue(of(expenseData1)); trackingService.deleteExpense.and.returnValue(null); corporateCreditCardExpenseService.markPersonal.and.returnValue(of(null)); - component.markCCCAsPersonal(expenseData1.tx_id).subscribe(() => { - expect(transactionService.delete).toHaveBeenCalledOnceWith(expenseData1.tx_id); + component.markCCCAsPersonal().subscribe(() => { expect(trackingService.deleteExpense).toHaveBeenCalledOnceWith({ Type: 'Marked Personal' }); expect(corporateCreditCardExpenseService.markPersonal).toHaveBeenCalledOnceWith( component.corporateCreditCardExpenseGroupId @@ -776,46 +773,20 @@ export function TestCases1(getTestBed) { done(); }); }); - - it('should return null if delete operation fails', (done) => { - transactionService.delete.and.returnValue(of(null)); - - component.markCCCAsPersonal(expenseData1.tx_id).subscribe((res) => { - expect(res).toBeNull(); - expect(transactionService.delete).toHaveBeenCalledOnceWith(expenseData1.tx_id); - done(); - }); - }); }); describe('dismissCCC():', () => { it('should dismiss CCC txn', (done) => { - transactionService.delete.and.returnValue(of(expenseData1)); trackingService.deleteExpense.and.returnValue(null); corporateCreditCardExpenseService.dismissCreditTransaction.and.returnValue(of(null)); - component - .dismissCCC(expenseData1.tx_id, expenseData1.tx_corporate_credit_card_expense_group_id) - .subscribe(() => { - expect(transactionService.delete).toHaveBeenCalledOnceWith(expenseData1.tx_id); - expect(trackingService.deleteExpense).toHaveBeenCalledOnceWith({ Type: 'Dismiss as Card Payment' }); - expect(corporateCreditCardExpenseService.dismissCreditTransaction).toHaveBeenCalledOnceWith( - expenseData1.tx_corporate_credit_card_expense_group_id - ); - done(); - }); - }); - - it('should return null if delete operation fails', (done) => { - transactionService.delete.and.returnValue(of(null)); - - component - .dismissCCC(expenseData1.tx_id, expenseData1.tx_corporate_credit_card_expense_group_id) - .subscribe((res) => { - expect(res).toBeNull(); - expect(transactionService.delete).toHaveBeenCalledOnceWith(expenseData1.tx_id); - done(); - }); + component.dismissCCC(expenseData1.tx_corporate_credit_card_expense_group_id).subscribe(() => { + expect(trackingService.deleteExpense).toHaveBeenCalledOnceWith({ Type: 'Dismiss as Card Payment' }); + expect(corporateCreditCardExpenseService.dismissCreditTransaction).toHaveBeenCalledOnceWith( + expenseData1.tx_corporate_credit_card_expense_group_id + ); + done(); + }); }); }); diff --git a/src/app/fyle/add-edit-expense/add-edit-expense-2.spec.ts b/src/app/fyle/add-edit-expense/add-edit-expense-2.spec.ts index 340c6633ce..e91a830c5c 100644 --- a/src/app/fyle/add-edit-expense/add-edit-expense-2.spec.ts +++ b/src/app/fyle/add-edit-expense/add-edit-expense-2.spec.ts @@ -8,8 +8,18 @@ import { ActionSheetController, ModalController, NavController, Platform, Popove import { Observable, Subscription, finalize, of, throwError } from 'rxjs'; import { AccountType } from 'src/app/core/enums/account-type.enum'; import { criticalPolicyViolation2 } from 'src/app/core/mock-data/crtical-policy-violations.data'; -import { duplicateSetData1, duplicateSetData4 } from 'src/app/core/mock-data/duplicate-sets.data'; -import { expenseData1, expenseData2 } from 'src/app/core/mock-data/expense.data'; +import { + duplicateSetData1, + duplicateSetData4, + duplicateSetData5, + duplicateSetData6, +} from 'src/app/core/mock-data/duplicate-sets.data'; +import { + expenseData1, + expenseData2, + splitExpTransformedData, + transformedPlatformedExpense, +} from 'src/app/core/mock-data/expense.data'; import { fileObject7, fileObjectData } from 'src/app/core/mock-data/file-object.data'; import { individualExpPolicyStateData2 } from 'src/app/core/mock-data/individual-expense-policy-state.data'; import { filterOrgCategoryParam, orgCategoryData } from 'src/app/core/mock-data/org-category.data'; @@ -86,8 +96,24 @@ import { PublicPolicyExpense } from 'src/app/core/models/public-policy-expense.m import { FileObject } from 'src/app/core/models/file-obj.model'; import { CustomField } from 'src/app/core/models/custom_field.model'; import { ExpensesService } from 'src/app/core/services/platform/v1/spender/expenses.service'; -import { expenseDuplicateSet2 } from 'src/app/core/mock-data/platform/v1/expense-duplicate-sets.data'; +import { + expenseDuplicateSet2, + expenseDuplicateSet3, +} from 'src/app/core/mock-data/platform/v1/expense-duplicate-sets.data'; import { cloneDeep } from 'lodash'; +import { + platformExpenseData, + platformExpenseDataWithSubCategory, + platformExpenseWithExtractedData, + platformExpenseWithExtractedData2, +} from 'src/app/core/mock-data/platform/v1/expense.data'; +import { + transformedExpenseData, + transformedExpenseDataWithSubCategory, + transformedExpenseWithExtractedData, + transformedExpenseWithExtractedData2, +} from 'src/app/core/mock-data/transformed-expense.data'; +import { apiExpenses1, apiExpenses2, splitExpensesData } from 'src/app/core/mock-data/platform/v1/expense.data'; const properties = { cssClass: 'fy-modal', @@ -227,7 +253,6 @@ export function TestCases2(getTestBed) { distance: [], distance_unit: [], custom_inputs: new FormArray([]), - duplicate_detection_reason: [], billable: [], costCenter: [], hotel_is_breakfast_provided: [], @@ -442,15 +467,17 @@ export function TestCases2(getTestBed) { describe('getEditExpenseObservable(): ', () => { it('should get editable expense observable if the txn is in DRAFT state', (done) => { - transactionService.getETxnUnflattened.and.returnValue(of(unflattenedTxnWithExtractedData)); + expensesService.getExpenseById.and.returnValue(of(platformExpenseWithExtractedData)); + transactionService.transformExpense.and.returnValue(transformedExpenseWithExtractedData); categoriesService.getCategoryByName.and.returnValue(of(orgCategoryData)); dateService.getUTCDate.and.returnValue(new Date('2023-01-24T11:30:00.000Z')); component.getEditExpenseObservable().subscribe((res) => { - expect(res).toEqual(expectedUnflattendedTxnData1); - expect(transactionService.getETxnUnflattened).toHaveBeenCalledOnceWith(activatedRoute.snapshot.params.id); + expect(res).toEqual(transformedExpenseWithExtractedData); + expect(expensesService.getExpenseById).toHaveBeenCalledOnceWith(activatedRoute.snapshot.params.id); + expect(transactionService.transformExpense).toHaveBeenCalledOnceWith(platformExpenseWithExtractedData); expect(categoriesService.getCategoryByName).toHaveBeenCalledOnceWith( - unflattenedTxnWithExtractedData.tx.extracted_data.category + transformedExpenseWithExtractedData.tx.extracted_data.category ); expect(dateService.getUTCDate).not.toHaveBeenCalled(); expect(component.isIncompleteExpense).toBeTrue(); @@ -459,36 +486,40 @@ export function TestCases2(getTestBed) { }); it('should return txn if state is not DRAFT', (done) => { - transactionService.getETxnUnflattened.and.returnValue(of(unflattenedTxnData)); + expensesService.getExpenseById.and.returnValue(of(platformExpenseData)); + transactionService.transformExpense.and.returnValue(transformedExpenseData); component.getEditExpenseObservable().subscribe((res) => { - expect(res).toEqual(unflattenedTxnData); - expect(transactionService.getETxnUnflattened).toHaveBeenCalledOnceWith(activatedRoute.snapshot.params.id); + expect(res).toEqual(transformedExpenseData); + expect(expensesService.getExpenseById).toHaveBeenCalledOnceWith(activatedRoute.snapshot.params.id); + expect(transactionService.transformExpense).toHaveBeenCalledOnceWith(platformExpenseData); done(); }); }); it('should return txn when the expense or the extracted data does not contain any category', (done) => { - transactionService.getETxnUnflattened.and.returnValue(of(unflattenedTxnWithExtractedData2)); + expensesService.getExpenseById.and.returnValue(of(platformExpenseWithExtractedData2)); + transactionService.transformExpense.and.returnValue(transformedExpenseWithExtractedData2); dateService.getUTCDate.and.returnValue(new Date('2023-01-24T11:30:00.000Z')); component.getEditExpenseObservable().subscribe((res) => { - expect(res).toEqual(unflattenedTxnWithExtractedData2); - expect(transactionService.getETxnUnflattened).toHaveBeenCalledTimes(1); + expect(res).toEqual(transformedExpenseWithExtractedData2); + expect(expensesService.getExpenseById).toHaveBeenCalledTimes(1); + expect(transactionService.transformExpense).toHaveBeenCalledOnceWith(platformExpenseWithExtractedData2); expect(dateService.getUTCDate).not.toHaveBeenCalled(); done(); }); }); it('should update txn date with extracted date if txn date is not defined in original expense', (done) => { - const mockedTxn = cloneDeep(unflattenedTxnWithExtractedData2); + const mockedTxn = cloneDeep(transformedExpenseWithExtractedData2); const extractedDate = new Date('2023-01-24'); mockedTxn.tx.txn_dt = null; mockedTxn.tx.extracted_data.invoice_dt = null; mockedTxn.tx.extracted_data.date = extractedDate; - - transactionService.getETxnUnflattened.and.returnValue(of(mockedTxn)); + expensesService.getExpenseById.and.returnValue(of(platformExpenseWithExtractedData2)); + transactionService.transformExpense.and.returnValue(mockedTxn); dateService.getUTCDate.and.returnValue(extractedDate); component.getEditExpenseObservable().subscribe((res) => { @@ -503,26 +534,33 @@ export function TestCases2(getTestBed) { it('goToPrev(): should go to the previous txn', () => { spyOn(component, 'goToTransaction'); activatedRoute.snapshot.params.activeIndex = 1; - component.reviewList = ['txSEM4DtjyKR', 'txNyI8ot5CuJ']; - transactionService.getETxnUnflattened.and.returnValue(of(unflattenedTxnData)); + component.reviewList = ['txvslh8aQMbu', 'txNyI8ot5CuJ']; + expensesService.getExpenseById.and.returnValue(of(platformExpenseData)); + transactionService.transformExpense.and.returnValue(transformedExpenseData); fixture.detectChanges(); component.goToPrev(); - expect(transactionService.getETxnUnflattened).toHaveBeenCalledOnceWith('txSEM4DtjyKR'); - expect(component.goToTransaction).toHaveBeenCalledOnceWith(unflattenedTxnData, component.reviewList, 0); + expect(expensesService.getExpenseById).toHaveBeenCalledOnceWith('txvslh8aQMbu'); + expect(transactionService.transformExpense).toHaveBeenCalledOnceWith(platformExpenseData); + expect(component.goToTransaction).toHaveBeenCalledOnceWith(transformedExpenseData, component.reviewList, 0); }); it('goToNext(): should got to the next txn', () => { - const etxn = { ...unflattenedTxnData, tx: { ...unflattenedTxnData.tx, id: 'txNyI8ot5CuJ' } }; spyOn(component, 'goToTransaction'); activatedRoute.snapshot.params.activeIndex = 0; - component.reviewList = ['txSEM4DtjyKR', 'txNyI8ot5CuJ']; - transactionService.getETxnUnflattened.and.returnValue(of(etxn)); + component.reviewList = ['txSEM4DtjyKR', 'txD5hIQgLuR5']; + expensesService.getExpenseById.and.returnValue(of(platformExpenseDataWithSubCategory)); + transactionService.transformExpense.and.returnValue(transformedExpenseDataWithSubCategory); fixture.detectChanges(); component.goToNext(); - expect(transactionService.getETxnUnflattened).toHaveBeenCalledOnceWith('txNyI8ot5CuJ'); - expect(component.goToTransaction).toHaveBeenCalledOnceWith(etxn, component.reviewList, 1); + expect(expensesService.getExpenseById).toHaveBeenCalledOnceWith('txD5hIQgLuR5'); + expect(transactionService.transformExpense).toHaveBeenCalledOnceWith(platformExpenseDataWithSubCategory); + expect(component.goToTransaction).toHaveBeenCalledOnceWith( + transformedExpenseDataWithSubCategory, + component.reviewList, + 1 + ); }); describe('goToTransaction():', () => { @@ -1271,8 +1309,9 @@ export function TestCases2(getTestBed) { it('should go to next expense if delete successful', fakeAsync(() => { spyOn(component, 'getDeleteReportParams'); spyOn(component, 'goToTransaction'); - transactionService.getETxnUnflattened.and.returnValue(of(unflattenedTxnData)); - component.reviewList = ['txfCdl3TEZ7K', 'txCYDX0peUw5']; + expensesService.getExpenseById.and.returnValue(of(platformExpenseData)); + transactionService.transformExpense.and.returnValue(transformedExpenseData); + component.reviewList = ['txvslh8aQMbu', 'txCYDX0peUw5']; component.activeIndex = 0; const deletePopoverSpy = jasmine.createSpyObj('deletePopover', ['present', 'onDidDismiss']); @@ -1303,11 +1342,10 @@ export function TestCases2(getTestBed) { expect(popoverController.create).toHaveBeenCalledOnceWith( component.getDeleteReportParams({ header, body, ctaText, ctaLoadingText }, undefined, undefined) ); - expect(transactionService.getETxnUnflattened).toHaveBeenCalledOnceWith( - component.reviewList[+component.activeIndex] - ); + expect(expensesService.getExpenseById).toHaveBeenCalledOnceWith(component.reviewList[+component.activeIndex]); + expect(transactionService.transformExpense).toHaveBeenCalledOnceWith(platformExpenseData); expect(component.goToTransaction).toHaveBeenCalledOnceWith( - unflattenedTxnData, + transformedExpenseData, component.reviewList, +component.activeIndex ); @@ -1449,26 +1487,32 @@ export function TestCases2(getTestBed) { component.getDuplicateExpenses(); expect(expensesService.getDuplicatesByExpense).not.toHaveBeenCalled(); - expect(transactionService.getETxnc).not.toHaveBeenCalled(); + expect(expensesService.getAllExpenses).not.toHaveBeenCalled(); }); it('should get duplicate expenses from platform', () => { - activatedRoute.snapshot.params.id = 'tx5fBcPBAxLv'; + activatedRoute.snapshot.params.id = 'txal5xGjbZ1R'; const expenseId = activatedRoute.snapshot.params.id; - expensesService.getDuplicatesByExpense.and.returnValue(of([expenseDuplicateSet2])); - transactionService.getETxnc.and.returnValue(of([expenseData1])); - spyOn(component, 'addExpenseDetailsToDuplicateSets').and.returnValue([expenseData1]); + expensesService.getDuplicatesByExpense.and.returnValue(of([expenseDuplicateSet3])); + expensesService.getAllExpenses.and.returnValue(of(apiExpenses2)); + transactionService.transformRawExpense.and.returnValue(transformedPlatformedExpense); + spyOn(component, 'addExpenseDetailsToDuplicateSets').and.returnValue([transformedPlatformedExpense]); component.getDuplicateExpenses(); expect(expensesService.getDuplicatesByExpense).toHaveBeenCalledWith(expenseId); - expect(transactionService.getETxnc).toHaveBeenCalledOnceWith({ + expect(expensesService.getAllExpenses).toHaveBeenCalledOnceWith({ offset: 0, limit: 100, - params: { tx_id: `in.(tx5fBcPBAxLv)` }, + queryParams: { + id: 'in.(txal5xGjbZ1R)', + }, }); - expect(component.addExpenseDetailsToDuplicateSets).toHaveBeenCalledOnceWith(duplicateSetData4, [expenseData1]); + expect(transactionService.transformRawExpense).toHaveBeenCalledOnceWith(apiExpenses2[0]); + expect(component.addExpenseDetailsToDuplicateSets).toHaveBeenCalledOnceWith(duplicateSetData6, [ + transformedPlatformedExpense, + ]); }); it('should return empty array if no duplicate is found in platform', () => { diff --git a/src/app/fyle/add-edit-expense/add-edit-expense-3.spec.ts b/src/app/fyle/add-edit-expense/add-edit-expense-3.spec.ts index 8e27b00d93..ad6fcf764e 100644 --- a/src/app/fyle/add-edit-expense/add-edit-expense-3.spec.ts +++ b/src/app/fyle/add-edit-expense/add-edit-expense-3.spec.ts @@ -231,7 +231,6 @@ export function TestCases3(getTestBed) { distance: [], distance_unit: [], custom_inputs: new FormArray([]), - duplicate_detection_reason: [], billable: [], costCenter: [], hotel_is_breakfast_provided: [], @@ -466,7 +465,6 @@ export function TestCases3(getTestBed) { spyOn(component, 'getDistance').and.returnValue(100); spyOn(component, 'getDistanceUnit').and.returnValue('KM'); spyOn(component, 'getBreakfastProvided').and.returnValue(true); - spyOn(component, 'getDuplicateReason').and.returnValue('reason'); spyOn(component, 'getAmount').and.returnValue(500); spyOn(component, 'getExpenseAttachments').and.returnValue(of(fileObject4)); @@ -508,7 +506,6 @@ export function TestCases3(getTestBed) { expect(component.getDistance).toHaveBeenCalledTimes(1); expect(component.getDistanceUnit).toHaveBeenCalledTimes(1); expect(component.getBreakfastProvided).toHaveBeenCalledTimes(1); - expect(component.getDuplicateReason).toHaveBeenCalledTimes(1); expect(component.getAmount).toHaveBeenCalledTimes(1); done(); }); @@ -539,7 +536,6 @@ export function TestCases3(getTestBed) { spyOn(component, 'getDistance').and.returnValue(100); spyOn(component, 'getDistanceUnit').and.returnValue('KM'); spyOn(component, 'getBreakfastProvided').and.returnValue(true); - spyOn(component, 'getDuplicateReason').and.returnValue('reason'); spyOn(component, 'getExpenseAttachments').and.returnValue(of(fileObject4)); spyOn(component, 'getAmount').and.returnValue(100); @@ -582,7 +578,6 @@ export function TestCases3(getTestBed) { expect(component.getDistance).toHaveBeenCalledTimes(1); expect(component.getDistanceUnit).toHaveBeenCalledTimes(1); expect(component.getBreakfastProvided).toHaveBeenCalledTimes(1); - expect(component.getDuplicateReason).toHaveBeenCalledTimes(1); expect(component.getAmount).toHaveBeenCalledTimes(1); done(); }); @@ -613,7 +608,6 @@ export function TestCases3(getTestBed) { spyOn(component, 'getDistance').and.returnValue(100); spyOn(component, 'getDistanceUnit').and.returnValue('KM'); spyOn(component, 'getBreakfastProvided').and.returnValue(true); - spyOn(component, 'getDuplicateReason').and.returnValue('reason'); spyOn(component, 'getAmount').and.returnValue(100); component.fg.controls.costCenter.setValue(costCenterApiRes1[0]); component.fg.controls.currencyObj.setValue({ @@ -654,7 +648,6 @@ export function TestCases3(getTestBed) { expect(component.getDistance).toHaveBeenCalledTimes(1); expect(component.getDistanceUnit).toHaveBeenCalledTimes(1); expect(component.getBreakfastProvided).toHaveBeenCalledTimes(1); - expect(component.getDuplicateReason).toHaveBeenCalledTimes(1); expect(component.getAmount).toHaveBeenCalledTimes(1); done(); }); @@ -684,7 +677,6 @@ export function TestCases3(getTestBed) { spyOn(component, 'getDistance').and.returnValue(100); spyOn(component, 'getDistanceUnit').and.returnValue('KM'); spyOn(component, 'getBreakfastProvided').and.returnValue(true); - spyOn(component, 'getDuplicateReason').and.returnValue('reason'); spyOn(component, 'getAmount').and.returnValue(100); component.mode = 'edit'; spyOn(component, 'getExpenseAttachments').and.returnValue(of(fileObject4)); @@ -724,7 +716,6 @@ export function TestCases3(getTestBed) { expect(component.getDistance).toHaveBeenCalledTimes(1); expect(component.getDistanceUnit).toHaveBeenCalledTimes(1); expect(component.getBreakfastProvided).toHaveBeenCalledTimes(1); - expect(component.getDuplicateReason).toHaveBeenCalledTimes(1); expect(component.getAmount).toHaveBeenCalledTimes(1); done(); }); diff --git a/src/app/fyle/add-edit-expense/add-edit-expense-4.spec.ts b/src/app/fyle/add-edit-expense/add-edit-expense-4.spec.ts index ba10430251..6af193ee4c 100644 --- a/src/app/fyle/add-edit-expense/add-edit-expense-4.spec.ts +++ b/src/app/fyle/add-edit-expense/add-edit-expense-4.spec.ts @@ -8,22 +8,26 @@ import { ActivatedRoute, Router } from '@angular/router'; import { ActionSheetController, ModalController, NavController, Platform, PopoverController } from '@ionic/angular'; import { Observable, Subscription, of, throwError } from 'rxjs'; import { expectedECccResponse } from 'src/app/core/mock-data/corporate-card-expense-unflattened.data'; +import { matchedCCTransactionData, matchedCCTransactionData2 } from 'src/app/core/mock-data/matchedCCTransaction.data'; import { expensePolicyData, expensePolicyDataWoData } from 'src/app/core/mock-data/expense-policy.data'; import { apiEouRes } from 'src/app/core/mock-data/extended-org-user.data'; import { fileObject4 } from 'src/app/core/mock-data/file-object.data'; import { outboxQueueData1 } from 'src/app/core/mock-data/outbox-queue.data'; import { apiPersonalCardTxnsRes } from 'src/app/core/mock-data/personal-card-txns.data'; -import { expectedErpt } from 'src/app/core/mock-data/report-unflattened.data'; +import { expectedErpt, expectedErptPlatform } from 'src/app/core/mock-data/report-unflattened.data'; import { createExpenseProperties, createExpenseProperties2, } from 'src/app/core/mock-data/track-expense-properties.data'; -import { txnStatusData } from 'src/app/core/mock-data/transaction-status.data'; +import { expenseStatusData, txnStatusData } from 'src/app/core/mock-data/transaction-status.data'; import { editExpTxn2, editExpTxn3, editExpTxn4, editExpTxn5, + editUnflattenedTransactionPlatform, + editUnflattenedTransactionPlatform2, + editUnflattenedTransactionPlatform3, personalCardTxn, txnData2, } from 'src/app/core/mock-data/transaction.data'; @@ -72,11 +76,28 @@ import { TaxGroupService } from 'src/app/core/services/tax-group.service'; import { TokenService } from 'src/app/core/services/token.service'; import { TrackingService } from 'src/app/core/services/tracking.service'; import { TransactionService } from 'src/app/core/services/transaction.service'; +import { ExpensesService } from 'src/app/core/services/platform/v1/spender/expenses.service'; import { TransactionsOutboxService } from 'src/app/core/services/transactions-outbox.service'; import { txnCustomProperties } from 'src/app/core/test-data/dependent-fields.service.spec.data'; import { CaptureReceiptComponent } from 'src/app/shared/components/capture-receipt/capture-receipt.component'; import { AddEditExpensePage } from './add-edit-expense.page'; import { CameraOptionsPopupComponent } from './camera-options-popup/camera-options-popup.component'; +import { + platformExpenseWithMatchCCC, + platformExpenseData, + platformExpenseDataWithReportId, + platformExpenseDataWithReportId2, + platformExpenseDataWithSubCategory, + platformExpenseWithMatchCCC2, +} from 'src/app/core/mock-data/platform/v1/expense.data'; +import { + transformedExpenseData, + transformedExpenseDataWithReportId, + transformedExpenseDataWithReportId2, + transformedExpenseDataWithSubCategory, + transformedExpenseWithMatchCCCData, + transformedExpenseWithMatchCCCData2, +} from 'src/app/core/mock-data/transformed-expense.data'; export function TestCases4(getTestBed) { return describe('AddEditExpensePage-4', () => { @@ -93,6 +114,7 @@ export function TestCases4(getTestBed) { let customInputsService: jasmine.SpyObj; let customFieldsService: jasmine.SpyObj; let transactionService: jasmine.SpyObj; + let expensesService: jasmine.SpyObj; let policyService: jasmine.SpyObj; let transactionOutboxService: jasmine.SpyObj; let router: jasmine.SpyObj; @@ -144,6 +166,7 @@ export function TestCases4(getTestBed) { customInputsService = TestBed.inject(CustomInputsService) as jasmine.SpyObj; customFieldsService = TestBed.inject(CustomFieldsService) as jasmine.SpyObj; transactionService = TestBed.inject(TransactionService) as jasmine.SpyObj; + expensesService = TestBed.inject(ExpensesService) as jasmine.SpyObj; policyService = TestBed.inject(PolicyService) as jasmine.SpyObj; transactionOutboxService = TestBed.inject(TransactionsOutboxService) as jasmine.SpyObj; router = TestBed.inject(Router) as jasmine.SpyObj; @@ -203,7 +226,6 @@ export function TestCases4(getTestBed) { distance: [], distance_unit: [], custom_inputs: new FormArray([]), - duplicate_detection_reason: [], billable: [], costCenter: [], hotel_is_breakfast_provided: [], @@ -892,17 +914,18 @@ export function TestCases4(getTestBed) { describe('editExpense():', () => { it('should edit an expense', (done) => { spyOn(component, 'getCustomFields').and.returnValue(of(txnCustomProperties)); - spyOn(component, 'generateEtxnFromFg').and.returnValue(of(newUnflattenedTxn)); + spyOn(component, 'generateEtxnFromFg').and.returnValue(of(transformedExpenseData)); spyOn(component, 'checkPolicyViolation').and.returnValue(of(expensePolicyData)); spyOn(component, 'trackPolicyCorrections'); spyOn(component, 'trackEditExpense'); policyService.getCriticalPolicyRules.and.returnValue([]); policyService.getPolicyRules.and.returnValue([]); authService.getEou.and.resolveTo(apiEouRes); - component.etxn$ = of(expectedUnflattendedTxnData3); - transactionService.upsert.and.returnValue(of(txnData2)); - transactionService.getETxnUnflattened.and.returnValue(of(unflattenedTxnData)); - component.fg.controls.report.setValue(expectedErpt[0]); + component.etxn$ = of(transformedExpenseData); + transactionService.upsert.and.returnValue(of(transformedExpenseData.tx)); + expensesService.getExpenseById.and.returnValue(of(platformExpenseData)); + transactionService.transformExpense.and.returnValue(transformedExpenseData); + component.fg.controls.report.setValue(expectedErptPlatform[0]); reportService.addTransactions.and.returnValue(of(undefined)); fixture.detectChanges(); @@ -916,33 +939,35 @@ export function TestCases4(getTestBed) { expect(policyService.getPolicyRules).toHaveBeenCalledTimes(1); expect(component.trackPolicyCorrections).toHaveBeenCalledTimes(1); expect(authService.getEou).toHaveBeenCalledTimes(1); - expect(component.trackEditExpense).toHaveBeenCalledOnceWith(newUnflattenedTxn); - expect(transactionService.upsert).toHaveBeenCalledOnceWith(newUnflattenedTxn.tx); - expect(transactionService.getETxnUnflattened).toHaveBeenCalledOnceWith('txNVtsqF8Siq'); - expect(reportService.addTransactions).toHaveBeenCalledOnceWith('rprAfNrce73O', ['tx3qHxFNgRcZ']); + expect(component.trackEditExpense).toHaveBeenCalledOnceWith(transformedExpenseData); + expect(transactionService.upsert).toHaveBeenCalledOnceWith(transformedExpenseData.tx); + expect(expensesService.getExpenseById).toHaveBeenCalledOnceWith('txvslh8aQMbu'); + expect(transactionService.transformExpense).toHaveBeenCalledOnceWith(platformExpenseData); + expect(reportService.addTransactions).toHaveBeenCalledOnceWith('rpIfg2VWQKGJ', ['txvslh8aQMbu']); done(); }); }); it('should add transaction from report while editing expense', (done) => { spyOn(component, 'getCustomFields').and.returnValue(of(txnCustomProperties)); - spyOn(component, 'generateEtxnFromFg').and.returnValue(of(unflattenedTxnDataWithReportID)); + spyOn(component, 'generateEtxnFromFg').and.returnValue(of(transformedExpenseDataWithReportId)); spyOn(component, 'checkPolicyViolation').and.returnValue(of(expensePolicyData)); spyOn(component, 'trackPolicyCorrections'); spyOn(component, 'trackEditExpense'); - component.etxn$ = of(unflattenedTxnDataWithReportID); - component.fg.controls.report.setValue(expectedErpt[0]); + component.etxn$ = of(transformedExpenseDataWithReportId); + component.fg.controls.report.setValue(expectedErptPlatform[0]); policyService.getCriticalPolicyRules.and.returnValue([]); policyService.getPolicyRules.and.returnValue([]); reportService.removeTransaction.and.returnValue(of(undefined)); reportService.addTransactions.and.returnValue(of(undefined)); authService.getEou.and.resolveTo(apiEouRes); - transactionService.upsert.and.returnValue(of(unflattenedTxnDataWithReportID.tx)); - transactionService.getETxnUnflattened.and.returnValue(of(unflattenedTxnDataWithReportID)); + transactionService.upsert.and.returnValue(of(transformedExpenseDataWithReportId.tx)); + expensesService.getExpenseById.and.returnValue(of(platformExpenseDataWithReportId)); + transactionService.transformExpense.and.returnValue(transformedExpenseDataWithReportId); fixture.detectChanges(); component.editExpense('SAVE_AND_NEW_EXPENSE').subscribe((res) => { - expect(res).toEqual(editExpTxn2); + expect(res).toEqual(editUnflattenedTransactionPlatform2); expect(component.getCustomFields).toHaveBeenCalledTimes(1); expect(component.generateEtxnFromFg).toHaveBeenCalledWith(component.etxn$, jasmine.any(Observable), true); expect(component.generateEtxnFromFg).toHaveBeenCalledWith(component.etxn$, jasmine.any(Observable)); @@ -952,33 +977,35 @@ export function TestCases4(getTestBed) { expect(policyService.getPolicyRules).toHaveBeenCalledTimes(1); expect(component.trackPolicyCorrections).toHaveBeenCalledTimes(1); expect(authService.getEou).toHaveBeenCalledTimes(1); - expect(component.trackEditExpense).toHaveBeenCalledOnceWith(unflattenedTxnDataWithReportID); - expect(transactionService.upsert).toHaveBeenCalledOnceWith(unflattenedTxnDataWithReportID.tx); - expect(transactionService.getETxnUnflattened).toHaveBeenCalledOnceWith('txbO4Xaj4N53'); - expect(reportService.addTransactions).toHaveBeenCalledOnceWith('rprAfNrce73O', ['txbO4Xaj4N53']); - expect(reportService.removeTransaction).toHaveBeenCalledOnceWith('rpGpzBpAxtSn', 'txbO4Xaj4N53'); + expect(component.trackEditExpense).toHaveBeenCalledOnceWith(transformedExpenseDataWithReportId); + expect(transactionService.upsert).toHaveBeenCalledOnceWith(transformedExpenseDataWithReportId.tx); + expect(expensesService.getExpenseById).toHaveBeenCalledOnceWith('txD5hIQgLuR5'); + expect(transactionService.transformExpense).toHaveBeenCalledOnceWith(platformExpenseDataWithReportId); + expect(reportService.addTransactions).toHaveBeenCalledOnceWith('rpIfg2VWQKGJ', ['txD5hIQgLuR5']); + expect(reportService.removeTransaction).toHaveBeenCalledOnceWith('rpbNc3kn5baq', 'txD5hIQgLuR5'); done(); }); }); it('should remove expense from report while editing and and ask for review', (done) => { spyOn(component, 'getCustomFields').and.returnValue(of(txnCustomProperties)); - spyOn(component, 'generateEtxnFromFg').and.returnValue(of(unflattenedTxnDataWithReportID2)); + spyOn(component, 'generateEtxnFromFg').and.returnValue(of(transformedExpenseDataWithReportId2)); spyOn(component, 'checkPolicyViolation').and.returnValue(of(expensePolicyData)); spyOn(component, 'trackPolicyCorrections'); spyOn(component, 'trackEditExpense'); - component.etxn$ = of(unflattenedTxnDataWithReportID2); + component.etxn$ = of(transformedExpenseDataWithReportId2); policyService.getCriticalPolicyRules.and.returnValue([]); policyService.getPolicyRules.and.returnValue([]); reportService.removeTransaction.and.returnValue(of(undefined)); authService.getEou.and.resolveTo(apiEouRes); - transactionService.upsert.and.returnValue(of(unflattenedTxnDataWithReportID2.tx)); - transactionService.getETxnUnflattened.and.returnValue(of(unflattenedTxnDataWithReportID2)); + transactionService.upsert.and.returnValue(of(transformedExpenseDataWithReportId2.tx)); + expensesService.getExpenseById.and.returnValue(of(platformExpenseDataWithReportId2)); + transactionService.transformExpense.and.returnValue(transformedExpenseDataWithReportId2); fixture.detectChanges(); component.editExpense('SAVE_AND_NEW_EXPENSE').subscribe((res) => { - expect(res).toEqual(editExpTxn3); + expect(res).toEqual(editUnflattenedTransactionPlatform3); expect(component.getCustomFields).toHaveBeenCalledTimes(1); expect(component.generateEtxnFromFg).toHaveBeenCalledWith(component.etxn$, jasmine.any(Observable), true); expect(component.generateEtxnFromFg).toHaveBeenCalledWith(component.etxn$, jasmine.any(Observable)); @@ -988,18 +1015,19 @@ export function TestCases4(getTestBed) { expect(policyService.getPolicyRules).toHaveBeenCalledTimes(1); expect(component.trackPolicyCorrections).toHaveBeenCalledTimes(1); expect(authService.getEou).toHaveBeenCalledTimes(1); - expect(component.trackEditExpense).toHaveBeenCalledOnceWith(unflattenedTxnDataWithReportID2); - expect(reportService.removeTransaction).toHaveBeenCalledOnceWith('rpGpzBpAxtSn', 'txbO4Xaj4N53'); + expect(component.trackEditExpense).toHaveBeenCalledOnceWith(transformedExpenseDataWithReportId2); + expect(reportService.removeTransaction).toHaveBeenCalledOnceWith('rplD17WeBlha', 'txD5hIQgLuR5'); expect(trackingService.removeFromExistingReportEditExpense).toHaveBeenCalledTimes(1); - expect(transactionService.upsert).toHaveBeenCalledOnceWith(unflattenedTxnDataWithReportID2.tx); - expect(transactionService.getETxnUnflattened).toHaveBeenCalledOnceWith('txbO4Xaj4N53'); + expect(transactionService.upsert).toHaveBeenCalledOnceWith(transformedExpenseDataWithReportId2.tx); + expect(expensesService.getExpenseById).toHaveBeenCalledOnceWith('txD5hIQgLuR5'); + expect(transactionService.transformExpense).toHaveBeenCalledOnceWith(platformExpenseDataWithReportId2); done(); }); }); it('should edit an expense with critical policy violation and require user review', (done) => { spyOn(component, 'getCustomFields').and.returnValue(of(txnCustomProperties)); - spyOn(component, 'generateEtxnFromFg').and.returnValue(of(unflattenedTxnDataWithViolationUserReview)); + spyOn(component, 'generateEtxnFromFg').and.returnValue(of(transformedExpenseDataWithSubCategory)); spyOn(component, 'checkPolicyViolation').and.returnValue(of(expensePolicyData)); spyOn(component, 'trackPolicyCorrections'); spyOn(component, 'trackEditExpense'); @@ -1008,17 +1036,18 @@ export function TestCases4(getTestBed) { 'The expense will be flagged when the total amount of all expenses in category Others in a month exceeds: INR 3000.', ]); spyOn(component, 'criticalPolicyViolationErrorHandler').and.returnValue( - of({ etxn: unflattenedTxnDataWithViolationUserReview, comment: null }) + of({ etxn: transformedExpenseDataWithSubCategory, comment: null }) ); - component.etxn$ = of(unflattenedTxnDataWithViolationUserReview); + component.etxn$ = of(transformedExpenseDataWithSubCategory); authService.getEou.and.resolveTo(apiEouRes); transactionService.review.and.returnValue(of(null)); - transactionService.upsert.and.returnValue(of(unflattenedTxnDataWithViolationUserReview.tx)); - transactionService.getETxnUnflattened.and.returnValue(of(unflattenedTxnDataWithViolationUserReview)); + transactionService.upsert.and.returnValue(of(transformedExpenseDataWithSubCategory.tx)); + expensesService.getExpenseById.and.returnValue(of(platformExpenseDataWithSubCategory)); + transactionService.transformExpense.and.returnValue(transformedExpenseDataWithSubCategory); fixture.detectChanges(); component.editExpense('SAVE_AND_NEW_EXPENSE').subscribe((res) => { - expect(res).toEqual(editExpTxn4); + expect(res).toEqual(editUnflattenedTransactionPlatform); expect(component.getCustomFields).toHaveBeenCalledTimes(1); expect(component.generateEtxnFromFg).toHaveBeenCalledWith(component.etxn$, jasmine.any(Observable), true); expect(component.generateEtxnFromFg).toHaveBeenCalledTimes(1); @@ -1030,24 +1059,24 @@ export function TestCases4(getTestBed) { policyViolations: [ 'The expense will be flagged when the total amount of all expenses in category Others in a month exceeds: INR 3000.', ], - etxn: unflattenedTxnDataWithViolationUserReview, + etxn: transformedExpenseDataWithSubCategory, }, jasmine.any(Observable) ); expect(component.trackPolicyCorrections).toHaveBeenCalledTimes(1); expect(authService.getEou).toHaveBeenCalledTimes(1); - expect(component.trackEditExpense).toHaveBeenCalledOnceWith(unflattenedTxnDataWithViolationUserReview); - expect(transactionService.upsert).toHaveBeenCalledOnceWith(unflattenedTxnDataWithViolationUserReview.tx); - expect(transactionService.getETxnUnflattened).toHaveBeenCalledOnceWith('txbO4Xaj4N53'); - expect(transactionService.review).toHaveBeenCalledOnceWith(unflattenedTxnDataWithViolationUserReview.tx.id); - expect(component.getIsPolicyExpense).toHaveBeenCalledTimes(2); + expect(component.trackEditExpense).toHaveBeenCalledOnceWith(transformedExpenseDataWithSubCategory); + expect(transactionService.upsert).toHaveBeenCalledOnceWith(transformedExpenseDataWithSubCategory.tx); + expect(expensesService.getExpenseById).toHaveBeenCalledOnceWith('txD5hIQgLuR5'); + expect(transactionService.transformExpense).toHaveBeenCalledOnceWith(platformExpenseDataWithSubCategory); + expect(component.getIsPolicyExpense).toHaveBeenCalledTimes(1); done(); }); }); it('should should edit an expense causing a policy violation', (done) => { spyOn(component, 'getCustomFields').and.returnValue(of(txnCustomProperties)); - spyOn(component, 'generateEtxnFromFg').and.returnValue(of(unflattenedTxnDataWithViolationUserReview)); + spyOn(component, 'generateEtxnFromFg').and.returnValue(of(transformedExpenseDataWithReportId)); spyOn(component, 'checkPolicyViolation').and.returnValue(of(expensePolicyData)); spyOn(component, 'trackPolicyCorrections'); spyOn(component, 'trackEditExpense'); @@ -1057,19 +1086,20 @@ export function TestCases4(getTestBed) { 'The expense will be flagged when the total amount of all expenses in category Others in a month exceeds: INR 3000.', ]); spyOn(component, 'policyViolationErrorHandler').and.returnValue( - of({ etxn: unflattenedTxnDataWithViolationUserReview, comment: 'comment' }) + of({ etxn: transformedExpenseDataWithReportId, comment: 'A comment' }) ); - component.etxn$ = of(unflattenedTxnDataWithViolationUserReview); + component.etxn$ = of(transformedExpenseDataWithReportId); authService.getEou.and.resolveTo(apiEouRes); - transactionService.upsert.and.returnValue(of(unflattenedTxnDataWithViolationUserReview.tx)); + transactionService.upsert.and.returnValue(of(transformedExpenseDataWithReportId.tx)); statusService.findLatestComment.and.returnValue(of('a comment')); - statusService.post.and.returnValue(of(txnStatusData)); - transactionService.getETxnUnflattened.and.returnValue(of(unflattenedTxnDataWithViolationUserReview)); + statusService.post.and.returnValue(of(expenseStatusData)); + expensesService.getExpenseById.and.returnValue(of(platformExpenseDataWithReportId)); + transactionService.transformExpense.and.returnValue(transformedExpenseDataWithReportId); fixture.detectChanges(); component.editExpense('SAVE_AND_NEW_EXPENSE').subscribe((res) => { - expect(res).toEqual(editExpTxn5); + expect(res).toEqual(editUnflattenedTransactionPlatform2); expect(component.getCustomFields).toHaveBeenCalledTimes(1); expect(component.generateEtxnFromFg).toHaveBeenCalledWith(component.etxn$, jasmine.any(Observable), true); expect(component.generateEtxnFromFg).toHaveBeenCalledTimes(1); @@ -1083,55 +1113,58 @@ export function TestCases4(getTestBed) { 'The expense will be flagged when the total amount of all expenses in category Others in a month exceeds: INR 3000.', ], policyAction: expensePolicyData.data.final_desired_state, - etxn: unflattenedTxnDataWithViolationUserReview, + etxn: transformedExpenseDataWithReportId, }, jasmine.any(Observable) ); expect(component.trackPolicyCorrections).toHaveBeenCalledTimes(1); expect(authService.getEou).toHaveBeenCalledTimes(1); - expect(component.trackEditExpense).toHaveBeenCalledOnceWith(unflattenedTxnDataWithViolationUserReview); - expect(transactionService.upsert).toHaveBeenCalledOnceWith(unflattenedTxnDataWithViolationUserReview.tx); - expect(transactionService.getETxnUnflattened).toHaveBeenCalledOnceWith('txbO4Xaj4N53'); + expect(component.trackEditExpense).toHaveBeenCalledOnceWith(transformedExpenseDataWithReportId); + expect(transactionService.upsert).toHaveBeenCalledOnceWith(transformedExpenseDataWithReportId.tx); + expect(expensesService.getExpenseById).toHaveBeenCalledOnceWith('txD5hIQgLuR5'); + expect(transactionService.transformExpense).toHaveBeenCalledOnceWith(platformExpenseDataWithReportId); expect(statusService.findLatestComment).toHaveBeenCalledOnceWith( - unflattenedTxnDataWithViolationUserReview.tx.id, + transformedExpenseDataWithReportId.tx.id, 'transactions', - unflattenedTxnDataWithViolationUserReview.tx.org_user_id + transformedExpenseDataWithReportId.tx.org_user_id ); expect(statusService.post).toHaveBeenCalledOnceWith( 'transactions', - unflattenedTxnDataWithViolationUserReview.tx.id, - { comment: 'comment' }, + transformedExpenseDataWithReportId.tx.id, + { comment: 'A comment' }, true ); - expect(component.getIsPolicyExpense).toHaveBeenCalledTimes(2); + expect(component.getIsPolicyExpense).toHaveBeenCalledTimes(1); done(); }); }); it('should edit an expense with policy violation and same comment', (done) => { spyOn(component, 'getCustomFields').and.returnValue(of(txnCustomProperties)); - spyOn(component, 'generateEtxnFromFg').and.returnValue(of(unflattenedTxnDataWithViolationUserReview)); + spyOn(component, 'generateEtxnFromFg').and.returnValue(of(transformedExpenseDataWithReportId2)); spyOn(component, 'checkPolicyViolation').and.returnValue(of(expensePolicyData)); spyOn(component, 'trackPolicyCorrections'); spyOn(component, 'trackEditExpense'); + spyOn(component, 'getIsPolicyExpense').and.returnValue(true); policyService.getCriticalPolicyRules.and.returnValue([]); policyService.getPolicyRules.and.returnValue([ 'The expense will be flagged when the total amount of all expenses in category Others in a month exceeds: INR 3000.', ]); spyOn(component, 'policyViolationErrorHandler').and.returnValue( - of({ etxn: unflattenedTxnDataWithViolationUserReview, comment: 'comment' }) + of({ etxn: transformedExpenseDataWithReportId2, comment: 'comment' }) ); - component.etxn$ = of(unflattenedTxnDataWithViolationUserReview); + component.etxn$ = of(transformedExpenseDataWithReportId2); authService.getEou.and.resolveTo(apiEouRes); transactionService.review.and.returnValue(of(null)); - transactionService.upsert.and.returnValue(of(unflattenedTxnDataWithViolationUserReview.tx)); - transactionService.getETxnUnflattened.and.returnValue(of(unflattenedTxnDataWithViolationUserReview)); + transactionService.upsert.and.returnValue(of(transformedExpenseDataWithReportId2.tx)); + expensesService.getExpenseById.and.returnValue(of(platformExpenseDataWithReportId2)); + transactionService.transformExpense.and.returnValue(transformedExpenseDataWithReportId2); statusService.findLatestComment.and.returnValue(of('comment')); fixture.detectChanges(); component.editExpense('SAVE_AND_NEW_EXPENSE').subscribe((res) => { - expect(res).toEqual(editExpTxn5); + expect(res).toEqual(editUnflattenedTransactionPlatform3); expect(component.getCustomFields).toHaveBeenCalledTimes(1); expect(component.generateEtxnFromFg).toHaveBeenCalledWith(component.etxn$, jasmine.any(Observable), true); expect(component.generateEtxnFromFg).toHaveBeenCalledTimes(1); @@ -1145,20 +1178,20 @@ export function TestCases4(getTestBed) { 'The expense will be flagged when the total amount of all expenses in category Others in a month exceeds: INR 3000.', ], policyAction: expensePolicyData.data.final_desired_state, - etxn: unflattenedTxnDataWithViolationUserReview, + etxn: transformedExpenseDataWithReportId2, }, jasmine.any(Observable) ); expect(component.trackPolicyCorrections).toHaveBeenCalledTimes(1); expect(authService.getEou).toHaveBeenCalledTimes(1); - expect(component.trackEditExpense).toHaveBeenCalledOnceWith(unflattenedTxnDataWithViolationUserReview); - expect(transactionService.upsert).toHaveBeenCalledOnceWith(unflattenedTxnDataWithViolationUserReview.tx); - expect(transactionService.getETxnUnflattened).toHaveBeenCalledOnceWith('txbO4Xaj4N53'); - expect(transactionService.review).toHaveBeenCalledOnceWith(unflattenedTxnDataWithViolationUserReview.tx.id); + expect(component.trackEditExpense).toHaveBeenCalledOnceWith(transformedExpenseDataWithReportId2); + expect(transactionService.upsert).toHaveBeenCalledOnceWith(transformedExpenseDataWithReportId2.tx); + expect(expensesService.getExpenseById).toHaveBeenCalledOnceWith('txD5hIQgLuR5'); + expect(transactionService.transformExpense).toHaveBeenCalledOnceWith(platformExpenseDataWithReportId2); expect(statusService.findLatestComment).toHaveBeenCalledOnceWith( - unflattenedTxnDataWithViolationUserReview.tx.id, + transformedExpenseDataWithReportId2.tx.id, 'transactions', - unflattenedTxnDataWithViolationUserReview.tx.org_user_id + transformedExpenseDataWithReportId2.tx.org_user_id ); expect(statusService.post).not.toHaveBeenCalled(); done(); @@ -1179,23 +1212,24 @@ export function TestCases4(getTestBed) { it('should match a normal expense', (done) => { spyOn(component, 'getCustomFields').and.returnValue(of(txnCustomProperties)); - spyOn(component, 'generateEtxnFromFg').and.returnValue(of(unflattenedExpenseWithCCCGroupId)); + spyOn(component, 'generateEtxnFromFg').and.returnValue(of(transformedExpenseWithMatchCCCData2)); spyOn(component, 'checkPolicyViolation').and.returnValue(of(null)); spyOn(component, 'trackPolicyCorrections'); spyOn(component, 'trackEditExpense'); policyService.getCriticalPolicyRules.and.returnValue([]); policyService.getPolicyRules.and.returnValue([]); - component.etxn$ = of(unflattenedExpenseWithCCCGroupId); + component.etxn$ = of(transformedExpenseWithMatchCCCData2); authService.getEou.and.resolveTo(apiEouRes); - transactionService.upsert.and.returnValue(of(unflattenedExpenseWithCCCGroupId.tx)); - transactionService.getETxnUnflattened.and.returnValue(of(unflattenedTxnData)); - component.selectedCCCTransaction = expectedECccResponse[0].ccce; - component.matchedCCCTransaction = expectedECccResponse[0].ccce; + transactionService.upsert.and.returnValue(of(transformedExpenseWithMatchCCCData2.tx)); + expensesService.getExpenseById.and.returnValue(of(platformExpenseWithMatchCCC2)); + transactionService.transformExpense.and.returnValue(transformedExpenseWithMatchCCCData2); + component.selectedCCCTransaction = matchedCCTransactionData; + component.matchedCCCTransaction = matchedCCTransactionData; transactionService.matchCCCExpense.and.returnValue(of(null)); fixture.detectChanges(); component.editExpense('SAVE_AND_NEW_EXPENSE').subscribe((res) => { - expect(res).toBeNull(); + expect(res).toBe(transformedExpenseWithMatchCCCData2.tx); expect(component.getCustomFields).toHaveBeenCalledTimes(1); expect(component.generateEtxnFromFg).toHaveBeenCalledWith(component.etxn$, jasmine.any(Observable), true); expect(component.generateEtxnFromFg).toHaveBeenCalledTimes(2); @@ -1204,32 +1238,34 @@ export function TestCases4(getTestBed) { expect(policyService.getPolicyRules).toHaveBeenCalledTimes(1); expect(component.trackPolicyCorrections).toHaveBeenCalledTimes(1); expect(authService.getEou).toHaveBeenCalledTimes(1); - expect(transactionService.upsert).toHaveBeenCalledOnceWith(unflattenedExpenseWithCCCGroupId.tx); - expect(transactionService.getETxnUnflattened).toHaveBeenCalledOnceWith('txbO4Xaj4N53'); - expect(transactionService.matchCCCExpense).toHaveBeenCalledOnceWith('tx3qHxFNgRcZ', 'ccceYIJhT8Aj6U'); + expect(transactionService.upsert).toHaveBeenCalledOnceWith(transformedExpenseWithMatchCCCData2.tx); + expect(expensesService.getExpenseById).toHaveBeenCalledOnceWith('txmF3wgfj0Bs'); + expect(transactionService.transformExpense).toHaveBeenCalledOnceWith(platformExpenseWithMatchCCC2); + expect(transactionService.matchCCCExpense).toHaveBeenCalledOnceWith('btxnSte7sVQCM8', 'txmF3wgfj0Bs'); done(); }); }); it('should unmatch a matched expense', (done) => { spyOn(component, 'getCustomFields').and.returnValue(of(txnCustomProperties)); - spyOn(component, 'generateEtxnFromFg').and.returnValue(of(unflattenedExpenseWithCCCGroupId)); + spyOn(component, 'generateEtxnFromFg').and.returnValue(of(transformedExpenseWithMatchCCCData)); spyOn(component, 'checkPolicyViolation').and.returnValue(of(null)); spyOn(component, 'trackPolicyCorrections'); spyOn(component, 'trackEditExpense'); policyService.getCriticalPolicyRules.and.returnValue([]); policyService.getPolicyRules.and.returnValue([]); - component.etxn$ = of(unflattenedExpenseWithCCCGroupId); + component.etxn$ = of(transformedExpenseWithMatchCCCData); authService.getEou.and.resolveTo(apiEouRes); - transactionService.upsert.and.returnValue(of(unflattenedExpenseWithCCCGroupId.tx)); - transactionService.getETxnUnflattened.and.returnValue(of(unflattenedExpenseWithCCCGroupId)); + transactionService.upsert.and.returnValue(of(transformedExpenseWithMatchCCCData.tx)); + expensesService.getExpenseById.and.returnValue(of(platformExpenseWithMatchCCC)); + transactionService.transformExpense.and.returnValue(transformedExpenseWithMatchCCCData); component.selectedCCCTransaction = null; - component.matchedCCCTransaction = expectedECccResponse[0].ccce; + component.matchedCCCTransaction = matchedCCTransactionData; transactionService.unmatchCCCExpense.and.returnValue(of(null)); fixture.detectChanges(); component.editExpense('SAVE_AND_NEW_EXPENSE').subscribe((res) => { - expect(res).toBeNull(); + expect(res).toBe(transformedExpenseWithMatchCCCData.tx); expect(component.generateEtxnFromFg).toHaveBeenCalledWith(component.etxn$, jasmine.any(Observable), true); expect(component.generateEtxnFromFg).toHaveBeenCalledTimes(2); expect(component.getCustomFields).toHaveBeenCalledTimes(1); @@ -1237,33 +1273,35 @@ export function TestCases4(getTestBed) { expect(policyService.getCriticalPolicyRules).toHaveBeenCalledTimes(1); expect(policyService.getPolicyRules).toHaveBeenCalledTimes(1); expect(authService.getEou).toHaveBeenCalledTimes(1); - expect(transactionService.upsert).toHaveBeenCalledOnceWith(unflattenedExpenseWithCCCGroupId.tx); - expect(transactionService.getETxnUnflattened).toHaveBeenCalledOnceWith('txbO4Xaj4N53'); - expect(transactionService.unmatchCCCExpense).toHaveBeenCalledOnceWith('txbO4Xaj4N53', 'ccceYIJhT8Aj6U'); + expect(transactionService.upsert).toHaveBeenCalledOnceWith(transformedExpenseWithMatchCCCData.tx); + expect(expensesService.getExpenseById).toHaveBeenCalledOnceWith('txmF3wgfj0Bs'); + expect(transactionService.transformExpense).toHaveBeenCalledOnceWith(platformExpenseWithMatchCCC); + expect(transactionService.unmatchCCCExpense).toHaveBeenCalledOnceWith('btxnSte7sVQCM8', 'txmF3wgfj0Bs'); done(); }); }); it('should match a ccc expense', (done) => { spyOn(component, 'getCustomFields').and.returnValue(of(txnCustomProperties)); - spyOn(component, 'generateEtxnFromFg').and.returnValue(of(unflattenedExpenseWithCCCGroupId)); + spyOn(component, 'generateEtxnFromFg').and.returnValue(of(transformedExpenseWithMatchCCCData)); spyOn(component, 'checkPolicyViolation').and.returnValue(of(null)); spyOn(component, 'trackPolicyCorrections'); spyOn(component, 'trackEditExpense'); policyService.getCriticalPolicyRules.and.returnValue([]); policyService.getPolicyRules.and.returnValue([]); - component.etxn$ = of(unflattenedExpenseWithCCCGroupId); + component.etxn$ = of(transformedExpenseWithMatchCCCData); authService.getEou.and.resolveTo(apiEouRes); - transactionService.upsert.and.returnValue(of(unflattenedExpenseWithCCCGroupId.tx)); - transactionService.getETxnUnflattened.and.returnValue(of(unflattenedExpenseWithCCCGroupId)); - component.selectedCCCTransaction = expectedECccResponse[0].ccce; - component.matchedCCCTransaction = expectedECccResponse[0].ccce; + transactionService.upsert.and.returnValue(of(transformedExpenseWithMatchCCCData.tx)); + expensesService.getExpenseById.and.returnValue(of(platformExpenseWithMatchCCC)); + transactionService.transformExpense.and.returnValue(transformedExpenseWithMatchCCCData); + component.selectedCCCTransaction = matchedCCTransactionData2; + component.matchedCCCTransaction = matchedCCTransactionData2; transactionService.unmatchCCCExpense.and.returnValue(of(null)); transactionService.matchCCCExpense.and.returnValue(of(null)); fixture.detectChanges(); component.editExpense('SAVE_AND_NEW_EXPENSE').subscribe((res) => { - expect(res).toBeNull(); + expect(res).toBe(transformedExpenseWithMatchCCCData.tx); expect(component.generateEtxnFromFg).toHaveBeenCalledWith(component.etxn$, jasmine.any(Observable), true); expect(component.generateEtxnFromFg).toHaveBeenCalledTimes(2); expect(component.getCustomFields).toHaveBeenCalledTimes(1); @@ -1271,10 +1309,11 @@ export function TestCases4(getTestBed) { expect(policyService.getCriticalPolicyRules).toHaveBeenCalledTimes(1); expect(policyService.getPolicyRules).toHaveBeenCalledTimes(1); expect(authService.getEou).toHaveBeenCalledTimes(1); - expect(transactionService.upsert).toHaveBeenCalledOnceWith(unflattenedExpenseWithCCCGroupId.tx); - expect(transactionService.getETxnUnflattened).toHaveBeenCalledOnceWith('txbO4Xaj4N53'); - expect(transactionService.unmatchCCCExpense).toHaveBeenCalledOnceWith('txbO4Xaj4N53', 'ccceYIJhT8Aj6U'); - expect(transactionService.matchCCCExpense).toHaveBeenCalledOnceWith('txbO4Xaj4N53', 'ccceYIJhT8Aj6U'); + expect(transactionService.upsert).toHaveBeenCalledOnceWith(transformedExpenseWithMatchCCCData.tx); + expect(expensesService.getExpenseById).toHaveBeenCalledOnceWith('txmF3wgfj0Bs'); + expect(transactionService.transformExpense).toHaveBeenCalledOnceWith(platformExpenseWithMatchCCC); + expect(transactionService.unmatchCCCExpense).toHaveBeenCalledOnceWith('btxnBdS2Kpvzhy', 'txmF3wgfj0Bs'); + expect(transactionService.matchCCCExpense).toHaveBeenCalledOnceWith('btxnBdS2Kpvzhy', 'txmF3wgfj0Bs'); done(); }); }); diff --git a/src/app/fyle/add-edit-expense/add-edit-expense-5.spec.ts b/src/app/fyle/add-edit-expense/add-edit-expense-5.spec.ts index 4b820c77ee..abb50290af 100644 --- a/src/app/fyle/add-edit-expense/add-edit-expense-5.spec.ts +++ b/src/app/fyle/add-edit-expense/add-edit-expense-5.spec.ts @@ -7,12 +7,12 @@ import { ActivatedRoute, Router } from '@angular/router'; import { ActionSheetController, ModalController, NavController, Platform, PopoverController } from '@ionic/angular'; import { BehaviorSubject, Observable, Subject, Subscription, of } from 'rxjs'; import { accountOptionData1 } from 'src/app/core/mock-data/account-option.data'; -import { eCCCData1, expectedECccResponse } from 'src/app/core/mock-data/corporate-card-expense-unflattened.data'; +import { expectedECccResponse } from 'src/app/core/mock-data/corporate-card-expense-unflattened.data'; import { costCentersData, expectedCCdata, expectedCCdata2 } from 'src/app/core/mock-data/cost-centers.data'; import { apiAllCurrencies } from 'src/app/core/mock-data/currency.data'; import { projectDependentFields } from 'src/app/core/mock-data/dependent-field.data'; import { dependentCustomFields2, expenseFieldResponse } from 'src/app/core/mock-data/expense-field.data'; -import { expenseData1, splitExpData } from 'src/app/core/mock-data/expense.data'; +import { splitExpData, splitExpTransformedData } from 'src/app/core/mock-data/expense.data'; import { expenseFieldObjData } from 'src/app/core/mock-data/expense-field-obj.data'; import { apiEouRes } from 'src/app/core/mock-data/extended-org-user.data'; @@ -64,7 +64,6 @@ import { CostCenter } from 'src/app/core/models/v1/cost-center.model'; import { AccountsService } from 'src/app/core/services/accounts.service'; import { AuthService } from 'src/app/core/services/auth.service'; import { CategoriesService } from 'src/app/core/services/categories.service'; -import { CorporateCreditCardExpenseService } from 'src/app/core/services/corporate-credit-card-expense.service'; import { CurrencyService } from 'src/app/core/services/currency.service'; import { CustomFieldsService } from 'src/app/core/services/custom-fields.service'; import { CustomInputsService } from 'src/app/core/services/custom-inputs.service'; @@ -105,8 +104,9 @@ import { apiV2ResponseMultiple, expectedProjectsResponse } from 'src/app/core/te import { getEstatusApiResponse } from 'src/app/core/test-data/status.service.spec.data'; import { AddEditExpensePage } from './add-edit-expense.page'; import { txnFieldsData2, txnFieldsFlightData } from 'src/app/core/mock-data/expense-fields-map.data'; -import { expenseData } from 'src/app/core/mock-data/platform/v1/expense.data'; +import { apiExpenses2, expenseData, splitExpensesData } from 'src/app/core/mock-data/platform/v1/expense.data'; import { ExpensesService } from 'src/app/core/services/platform/v1/spender/expenses.service'; +import { matchedCCTransactionData } from 'src/app/core/mock-data/matchedCCTransaction.data'; export function TestCases5(getTestBed) { return describe('AddEditExpensePage-5', () => { @@ -135,7 +135,6 @@ export function TestCases5(getTestBed) { let networkService: jasmine.SpyObj; let popupService: jasmine.SpyObj; let navController: jasmine.SpyObj; - let corporateCreditCardExpenseService: jasmine.SpyObj; let trackingService: jasmine.SpyObj; let recentLocalStorageItemsService: jasmine.SpyObj; let recentlyUsedItemsService: jasmine.SpyObj; @@ -187,9 +186,6 @@ export function TestCases5(getTestBed) { networkService = TestBed.inject(NetworkService) as jasmine.SpyObj; popupService = TestBed.inject(PopupService) as jasmine.SpyObj; navController = TestBed.inject(NavController) as jasmine.SpyObj; - corporateCreditCardExpenseService = TestBed.inject( - CorporateCreditCardExpenseService - ) as jasmine.SpyObj; trackingService = TestBed.inject(TrackingService) as jasmine.SpyObj; recentLocalStorageItemsService = TestBed.inject( RecentLocalStorageItemsService @@ -235,7 +231,6 @@ export function TestCases5(getTestBed) { distance: [], distance_unit: [], custom_inputs: new FormArray([]), - duplicate_detection_reason: [], billable: [], costCenter: [], hotel_is_breakfast_provided: [], @@ -257,10 +252,9 @@ export function TestCases5(getTestBed) { describe('getMarkDismissModalParams():', () => { it('should get modal params with method to mark as personal', (done) => { - transactionService.unmatchCCCExpense.and.returnValue(of(null)); spyOn(component, 'markCCCAsPersonal').and.returnValue(of(null)); activatedRoute.snapshot.params.id = 'txfCdl3TEZ7K'; - component.corporateCreditCardExpenseGroupId = 'cccet1B17R8gWZ'; + component.corporateCreditCardExpenseGroupId = 'btxnBdS2Kpvzhy'; fixture.detectChanges(); component @@ -275,17 +269,15 @@ export function TestCases5(getTestBed) { ) .componentProps.deleteMethod() .subscribe(() => { - expect(transactionService.unmatchCCCExpense).toHaveBeenCalledOnceWith('txfCdl3TEZ7K', 'cccet1B17R8gWZ'); - expect(component.markCCCAsPersonal).toHaveBeenCalledOnceWith('txfCdl3TEZ7K'); + expect(component.markCCCAsPersonal).toHaveBeenCalledOnceWith(); done(); }); }); it('should get modal params with method to dismiss expense', (done) => { - transactionService.unmatchCCCExpense.and.returnValue(of(null)); spyOn(component, 'dismissCCC').and.returnValue(of(null)); activatedRoute.snapshot.params.id = 'txfCdl3TEZ7K'; - component.matchedCCCTransaction = expectedECccResponse[0].ccce; + component.matchedCCCTransaction = matchedCCTransactionData; fixture.detectChanges(); component @@ -300,14 +292,12 @@ export function TestCases5(getTestBed) { ) .componentProps.deleteMethod() .subscribe(() => { - expect(transactionService.unmatchCCCExpense).toHaveBeenCalledOnceWith('txfCdl3TEZ7K', 'ccceYIJhT8Aj6U'); - expect(component.dismissCCC).toHaveBeenCalledOnceWith('txfCdl3TEZ7K', 'ccceYIJhT8Aj6U'); + expect(component.dismissCCC).toHaveBeenCalledOnceWith('btxnSte7sVQCM8'); done(); }); }); it('should get modal params with method to dismiss expense if matched expense does not exist', (done) => { - transactionService.unmatchCCCExpense.and.returnValue(of(null)); spyOn(component, 'dismissCCC').and.returnValue(of(null)); activatedRoute.snapshot.params.id = 'txfCdl3TEZ7K'; component.matchedCCCTransaction = null; @@ -325,14 +315,12 @@ export function TestCases5(getTestBed) { ) .componentProps.deleteMethod() .subscribe(() => { - expect(transactionService.unmatchCCCExpense).toHaveBeenCalledOnceWith('txfCdl3TEZ7K', undefined); - expect(component.dismissCCC).toHaveBeenCalledOnceWith('txfCdl3TEZ7K', undefined); + expect(component.dismissCCC).toHaveBeenCalledOnceWith(undefined); done(); }); }); it('should get modal params with method to dismiss expense if matched expense does not exist', (done) => { - transactionService.unmatchCCCExpense.and.returnValue(of(null)); spyOn(component, 'dismissCCC').and.returnValue(of(null)); activatedRoute.snapshot.params.id = 'txfCdl3TEZ7K'; component.matchedCCCTransaction = null; @@ -350,8 +338,7 @@ export function TestCases5(getTestBed) { ) .componentProps.deleteMethod() .subscribe(() => { - expect(transactionService.unmatchCCCExpense).toHaveBeenCalledOnceWith('txfCdl3TEZ7K', undefined); - expect(component.dismissCCC).toHaveBeenCalledOnceWith('txfCdl3TEZ7K', undefined); + expect(component.dismissCCC).toHaveBeenCalledOnceWith(undefined); done(); }); }); @@ -1507,10 +1494,6 @@ export function TestCases5(getTestBed) { expect(res).toBeFalse(); }); - component.isNotReimbursable$.subscribe((res) => { - expect(res).toBeFalse(); - }); - component.isAmountCapped$.subscribe((res) => { expect(res).toBeFalse(); }); @@ -1644,7 +1627,9 @@ export function TestCases5(getTestBed) { spyOn(component, 'getActiveCategories').and.returnValue(of(sortedCategory)); spyOn(component, 'getNewExpenseObservable').and.returnValue(of(expectedExpenseObservable)); spyOn(component, 'getEditExpenseObservable').and.returnValue(of(expectedUnflattendedTxnData1)); - corporateCreditCardExpenseService.getEccceByGroupId.and.returnValue(of(expectedECccResponse)); + expensesService.getSplitExpenses.and.returnValue(of(splitExpensesData)); + transactionService.transformRawExpense.and.returnValue(splitExpTransformedData[0]); + transactionService.transformRawExpense.and.returnValue(splitExpTransformedData[1]); fileService.findByTransactionId.and.returnValue(of(expectedFileData1)); fileService.downloadUrl.and.returnValue(of('url')); activatedRoute.snapshot.params.activeIndex = JSON.stringify(1); @@ -1792,10 +1777,6 @@ export function TestCases5(getTestBed) { expect(res).toBeFalse(); }); - component.isNotReimbursable$.subscribe((res) => { - expect(res).toBeFalse(); - }); - component.isAmountCapped$.subscribe((res) => { expect(res).toBeFalse(); }); diff --git a/src/app/fyle/add-edit-expense/add-edit-expense-6.spec.ts b/src/app/fyle/add-edit-expense/add-edit-expense-6.spec.ts index 04ff9d2420..8658d9f381 100644 --- a/src/app/fyle/add-edit-expense/add-edit-expense-6.spec.ts +++ b/src/app/fyle/add-edit-expense/add-edit-expense-6.spec.ts @@ -7,11 +7,6 @@ import { ActivatedRoute, Router } from '@angular/router'; import { ActionSheetController, ModalController, NavController, Platform, PopoverController } from '@ionic/angular'; import { BehaviorSubject, Observable, Subject, Subscription, of } from 'rxjs'; import { AccountType } from 'src/app/core/enums/account-type.enum'; -import { - eCCCData2, - eCCCData3, - expectedECccResponse, -} from 'src/app/core/mock-data/corporate-card-expense-unflattened.data'; import { expectedCCdata, expectedCCdata2 } from 'src/app/core/mock-data/cost-centers.data'; import { defaultTxnFieldValuesData3 } from 'src/app/core/mock-data/default-txn-field-values.data'; import { @@ -35,6 +30,7 @@ import { checkSplitExpData1, checkSplitExpData2, unflattenedExpWithCCCExpn, + unflattenedExpWithCCCExpn1, } from 'src/app/core/mock-data/unflattened-txn.data'; import { BackButtonActionPriority } from 'src/app/core/models/back-button-action-priority.enum'; import { AccountsService } from 'src/app/core/services/accounts.service'; @@ -77,6 +73,11 @@ import { TransactionStatus } from 'src/app/core/models/platform/v1/expense.model import { TransactionStatusInfoPopoverComponent } from 'src/app/shared/components/transaction-status-info-popover/transaction-status-info-popover.component'; import { ExpensesService } from 'src/app/core/services/platform/v1/spender/expenses.service'; import { expenseData } from 'src/app/core/mock-data/platform/v1/expense.data'; +import { + transformedExpenseWithMatchCCCData, + transformedExpenseWithMatchCCCData3, +} from 'src/app/core/mock-data/transformed-expense.data'; +import { matchedCCTransactionData, matchedCCTransactionData2 } from 'src/app/core/mock-data/matchedCCTransaction.data'; export function TestCases6(getTestBed) { describe('AddEditExpensePage-6', () => { @@ -152,6 +153,7 @@ export function TestCases6(getTestBed) { customInputsService = TestBed.inject(CustomInputsService) as jasmine.SpyObj; customFieldsService = TestBed.inject(CustomFieldsService) as jasmine.SpyObj; transactionService = TestBed.inject(TransactionService) as jasmine.SpyObj; + expensesService = TestBed.inject(ExpensesService) as jasmine.SpyObj; policyService = TestBed.inject(PolicyService) as jasmine.SpyObj; transactionOutboxService = TestBed.inject(TransactionsOutboxService) as jasmine.SpyObj; router = TestBed.inject(Router) as jasmine.SpyObj; @@ -213,7 +215,6 @@ export function TestCases6(getTestBed) { distance: [], distance_unit: [], custom_inputs: new FormArray([]), - duplicate_detection_reason: [], billable: [], costCenter: [], hotel_is_breakfast_provided: [], @@ -471,21 +472,19 @@ export function TestCases6(getTestBed) { describe('handleCCCExpenses():', () => { it('should handle CCC expenses', () => { - corporateCreditCardExpenseService.getEccceByGroupId.and.returnValue(of(expectedECccResponse)); - - component.handleCCCExpenses(unflattenedExpWithCCCExpn); - expect(corporateCreditCardExpenseService.getEccceByGroupId).toHaveBeenCalledOnceWith('cccet1B17R8gWZ'); - expect(component.cardNumber).toEqual('869'); - expect(component.matchedCCCTransaction).toEqual(expectedECccResponse[0].ccce); + const date = new Date('2018-07-03T13:00:00.000Z'); + jasmine.clock().mockDate(date); + component.handleCCCExpenses(transformedExpenseWithMatchCCCData); + expect(component.cardNumber).toEqual('7620'); + expect(component.matchedCCCTransaction).toEqual(matchedCCTransactionData); }); it('should show card digits and vendor description', () => { - corporateCreditCardExpenseService.getEccceByGroupId.and.returnValue(of([eCCCData2])); - - component.handleCCCExpenses(unflattenedExpWithCCCExpn); - expect(corporateCreditCardExpenseService.getEccceByGroupId).toHaveBeenCalledOnceWith('cccet1B17R8gWZ'); - expect(component.cardNumber).toEqual('869'); - expect(component.matchedCCCTransaction).toEqual(eCCCData2.ccce); + const date = new Date('2018-06-06T08:30:00.000Z'); + jasmine.clock().mockDate(date); + component.handleCCCExpenses(transformedExpenseWithMatchCCCData3); + expect(component.cardNumber).toEqual('9891'); + expect(component.matchedCCCTransaction).toEqual(matchedCCTransactionData2); }); }); @@ -519,17 +518,6 @@ export function TestCases6(getTestBed) { expect(component.fg.controls.bus_travel_class.value).toBeNull(); })); - it('initCCCTxn(): should initialize CCC txn and initialize card number and ending digits', () => { - activatedRoute.snapshot.params = { - bankTxn: JSON.stringify(eCCCData3), - }; - component.initCCCTxn(); - expect(component.showSelectedTransaction).toBeTrue(); - expect(component.selectedCCCTransaction).toEqual(eCCCData3.ccce); - expect(component.isCreatedFromCCC).toBeTrue(); - expect(component.cardEndingDigits).toEqual('6789'); - }); - it('ngOnInit(): should populate report permissions', () => { activatedRoute.snapshot.params.remove_from_report = JSON.stringify(true); fixture.detectChanges(); @@ -927,22 +915,6 @@ export function TestCases6(getTestBed) { }); }); - describe('getDuplicateReason():', () => { - it('should get duplicate expense reason', () => { - component.fg.controls.duplicate_detection_reason.setValue('reason'); - - const result = component.getDuplicateReason(); - expect(result).toEqual('reason'); - }); - - it('should return null', () => { - setFormValueNull(); - - const result = component.getDuplicateReason(); - expect(result).toBeUndefined(); - }); - }); - describe('getTxnDate():', () => { it('should get txn date', () => { dateService.getUTCDate.and.returnValue(null); diff --git a/src/app/fyle/add-edit-expense/add-edit-expense.page.html b/src/app/fyle/add-edit-expense/add-edit-expense.page.html index cb9eeac42e..bf9cfa15c3 100644 --- a/src/app/fyle/add-edit-expense/add-edit-expense.page.html +++ b/src/app/fyle/add-edit-expense/add-edit-expense.page.html @@ -1159,13 +1159,6 @@ - - - diff --git a/src/app/fyle/add-edit-expense/add-edit-expense.page.ts b/src/app/fyle/add-edit-expense/add-edit-expense.page.ts index 825ca36257..7964b1a423 100644 --- a/src/app/fyle/add-edit-expense/add-edit-expense.page.ts +++ b/src/app/fyle/add-edit-expense/add-edit-expense.page.ts @@ -47,7 +47,7 @@ import { AccountType } from 'src/app/core/enums/account-type.enum'; import { ExpenseType } from 'src/app/core/enums/expense-type.enum'; import { AccountOption } from 'src/app/core/models/account-option.model'; import { BackButtonActionPriority } from 'src/app/core/models/back-button-action-priority.enum'; -import { CCCExpUnflattened, CCCExpense } from 'src/app/core/models/corporate-card-expense-unflattened.model'; +import { CCCExpUnflattened } from 'src/app/core/models/corporate-card-expense-unflattened.model'; import { CostCenterOptions } from 'src/app/core/models/cost-centers-options.model'; import { CurrencyObj } from 'src/app/core/models/currency-obj.model'; import { Currency } from 'src/app/core/models/currency.model'; @@ -132,6 +132,7 @@ import { InstaFyleImageData } from 'src/app/core/models/insta-fyle-image-data.mo import { Expense as PlatformExpense, TransactionStatus } from 'src/app/core/models/platform/v1/expense.model'; import { ExpensesService } from 'src/app/core/services/platform/v1/spender/expenses.service'; import { TransactionStatusInfoPopoverComponent } from 'src/app/shared/components/transaction-status-info-popover/transaction-status-info-popover.component'; +import { CorporateCardTransactionRes } from 'src/app/core/models/platform/v1/corporate-card-transaction-res.model'; type FormValue = { currencyObj: { @@ -162,7 +163,6 @@ type FormValue = { distance: number; distance_unit: string; custom_inputs: CustomInput[]; - duplicate_detection_reason: string; billable: boolean; costCenter: CostCenter; hotel_is_breakfast_provided: boolean; @@ -184,7 +184,7 @@ export class AddEditExpensePage implements OnInit { @ViewChild('costCenterDependentFieldsRef') costCenterDependentFieldsRef: DependentFieldsComponent; - etxn$: Observable; + etxn$: Observable>; platformExpense$: Observable; @@ -234,7 +234,7 @@ export class AddEditExpensePage implements OnInit { isBalanceAvailableInAnyAdvanceAccount$: Observable; - selectedCCCTransaction: CCCExpense; + selectedCCCTransaction: Partial; canChangeMatchingCCCTransaction = true; @@ -248,8 +248,6 @@ export class AddEditExpensePage implements OnInit { individualProjectIds$: Observable; - isNotReimbursable$: Observable; - costCenters$: Observable; isAmountCapped$: Observable; @@ -284,7 +282,7 @@ export class AddEditExpensePage implements OnInit { isCCCPaymentModeSelected$: Observable; - matchedCCCTransaction: MatchedCCCTransaction; + matchedCCCTransaction: Partial; alreadyApprovedExpenses: PlatformExpense[]; @@ -750,30 +748,14 @@ export class AddEditExpensePage implements OnInit { ); } - markCCCAsPersonal(txnId: string): Observable { - return this.transactionService.delete(txnId).pipe( - switchMap((res) => { - if (res) { - this.trackingService.deleteExpense({ Type: 'Marked Personal' }); - return this.corporateCreditCardExpenseService.markPersonal(this.corporateCreditCardExpenseGroupId); - } else { - return of(null); - } - }) - ); + markCCCAsPersonal(): Observable { + this.trackingService.deleteExpense({ Type: 'Marked Personal' }); + return this.corporateCreditCardExpenseService.markPersonal(this.corporateCreditCardExpenseGroupId); } - dismissCCC(txnId: string, corporateCreditCardExpenseId: string): Observable { - return this.transactionService.delete(txnId).pipe( - switchMap((res) => { - if (res) { - this.trackingService.deleteExpense({ Type: 'Dismiss as Card Payment' }); - return this.corporateCreditCardExpenseService.dismissCreditTransaction(corporateCreditCardExpenseId); - } else { - return of(null); - } - }) - ); + dismissCCC(corporateCreditCardExpenseId: string): Observable { + this.trackingService.deleteExpense({ Type: 'Dismiss as Card Payment' }); + return this.corporateCreditCardExpenseService.dismissCreditTransaction(corporateCreditCardExpenseId); } getRemoveCCCExpModalParams( @@ -825,7 +807,7 @@ export class AddEditExpensePage implements OnInit { }; if (data?.status === 'success') { - let txnDetails: UnflattenedTransaction; + let txnDetails: Partial; this.etxn$.subscribe((etxn) => (txnDetails = etxn)); const properties = { Type: 'unlink corporate card expense', @@ -864,10 +846,9 @@ export class AddEditExpensePage implements OnInit { body: string; ctaText: string; ctaLoadingText: string; - deleteMethod: () => Observable; + deleteMethod: () => Observable; }; } { - const id = this.activatedRoute.snapshot.params.id as string; return { component: FyDeleteDialogComponent, cssClass: 'delete-dialog', @@ -877,15 +858,11 @@ export class AddEditExpensePage implements OnInit { body: componentPropsParam.body, ctaText: componentPropsParam.ctaText, ctaLoadingText: componentPropsParam.ctaLoadingText, - deleteMethod: (): Observable => { + deleteMethod: (): Observable => { if (isMarkPersonal) { - return this.transactionService - .unmatchCCCExpense(id, this.corporateCreditCardExpenseGroupId) - .pipe(switchMap(() => this.markCCCAsPersonal(id))); + return this.markCCCAsPersonal(); } else { - return this.transactionService - .unmatchCCCExpense(id, this.matchedCCCTransaction?.id) - .pipe(switchMap(() => this.dismissCCC(id, this.matchedCCCTransaction?.id))); + return this.dismissCCC(this.matchedCCCTransaction?.id); } }, }, @@ -1516,7 +1493,7 @@ export class AddEditExpensePage implements OnInit { reportOptions, }: { autoSubmissionReportName: string; - etxn: UnflattenedTransaction; + etxn: Partial; reportOptions: { label: string; value: UnflattenedReport }[]; }) => { if (etxn.tx.report_id) { @@ -1949,7 +1926,6 @@ export class AddEditExpensePage implements OnInit { bus_travel_class: etxn.tx.bus_travel_class, distance: etxn.tx.distance, distance_unit: etxn.tx.distance_unit, - duplicate_detection_reason: etxn.tx.user_reason_for_duplicate_expenses, billable: etxn.tx.billable, custom_inputs: customInputValues, @@ -2000,7 +1976,7 @@ export class AddEditExpensePage implements OnInit { isAutofillsEnabled: boolean; recentValue: RecentlyUsed; recentCategories: OrgCategoryListItem[]; - etxn: UnflattenedTransaction; + etxn: Partial; category: OrgCategory; }): OrgCategory { const { isAutofillsEnabled, recentValue, recentCategories, etxn } = config; @@ -2067,7 +2043,7 @@ export class AddEditExpensePage implements OnInit { orgSettings: OrgSettings; recentValues: RecentlyUsed; recentCategories: OrgCategoryListItem[]; - etxn: UnflattenedTransaction; + etxn: Partial; }) => { const isExpenseCategoryUnspecified = etxn.tx.fyle_category?.toLowerCase() === 'unspecified'; if (this.initialFetch && etxn.tx.org_category_id && !isExpenseCategoryUnspecified) { @@ -2610,10 +2586,11 @@ export class AddEditExpensePage implements OnInit { }); } - getEditExpenseObservable(): Observable { - return this.transactionService.getETxnUnflattened(this.activatedRoute.snapshot.params.id as string).pipe( - tap((etxn) => (this.isIncompleteExpense = etxn.tx.state === 'DRAFT')), - switchMap((etxn) => { + getEditExpenseObservable(): Observable> { + return this.expensesService.getExpenseById(this.activatedRoute.snapshot.params.id as string).pipe( + switchMap((expense) => { + const etxn = this.transactionService.transformExpense(expense); + this.isIncompleteExpense = etxn.tx.state === 'DRAFT'; this.source = etxn.tx.source || 'MOBILE'; if (etxn.tx.state === 'DRAFT' && etxn.tx.extracted_data) { if (etxn.tx.extracted_data.amount && !etxn.tx.amount) { @@ -2660,7 +2637,8 @@ export class AddEditExpensePage implements OnInit { goToPrev(): void { this.activeIndex = parseInt(this.activatedRoute.snapshot.params.activeIndex as string, 10); if (this.reviewList[+this.activeIndex - 1]) { - this.transactionService.getETxnUnflattened(this.reviewList[+this.activeIndex - 1]).subscribe((etxn) => { + this.expensesService.getExpenseById(this.reviewList[+this.activeIndex - 1]).subscribe((expense) => { + const etxn = this.transactionService.transformExpense(expense); this.goToTransaction(etxn, this.reviewList, +this.activeIndex - 1); }); } @@ -2669,13 +2647,14 @@ export class AddEditExpensePage implements OnInit { goToNext(): void { this.activeIndex = parseInt(this.activatedRoute.snapshot.params.activeIndex as string, 10); if (this.reviewList[+this.activeIndex + 1]) { - this.transactionService.getETxnUnflattened(this.reviewList[+this.activeIndex + 1]).subscribe((etxn) => { + this.expensesService.getExpenseById(this.reviewList[+this.activeIndex + 1]).subscribe((expense) => { + const etxn = this.transactionService.transformExpense(expense); this.goToTransaction(etxn, this.reviewList, +this.activeIndex + 1); }); } } - goToTransaction(expense: UnflattenedTransaction, reviewList: string[], activeIndex: number): void { + goToTransaction(expense: Partial, reviewList: string[], activeIndex: number): void { let category: string; if (expense.tx.org_category) { @@ -2785,49 +2764,30 @@ export class AddEditExpensePage implements OnInit { }); } - initCCCTxn(): void { - const bankTxn = - this.activatedRoute.snapshot.params.bankTxn && - (JSON.parse(this.activatedRoute.snapshot.params.bankTxn as string) as CCCExpUnflattened); - this.showSelectedTransaction = true; - this.selectedCCCTransaction = bankTxn.ccce; - let cccAccountNumber: string; - if (bankTxn.flow && bankTxn.flow === 'newCCCFlow') { - cccAccountNumber = this.selectedCCCTransaction.corporate_credit_card_account_number; + handleCCCExpenses(etxn: Partial): void { + this.matchedCCCTransaction = etxn.tx.matched_corporate_card_transactions[0]; + this.selectedCCCTransaction = this.matchedCCCTransaction; + this.cardEndingDigits = ( + this.selectedCCCTransaction.corporate_credit_card_account_number + ? this.selectedCCCTransaction.corporate_credit_card_account_number + : this.selectedCCCTransaction.card_or_account_number + ).slice(-4); + + etxn.tx.matchCCCId = this.selectedCCCTransaction.id; + + const txnDt = dayjs(this.selectedCCCTransaction.txn_dt).format('MMM D, YYYY'); + + this.selectedCCCTransaction.displayObject = + txnDt + + ' - ' + + (this.selectedCCCTransaction.vendor + ? this.selectedCCCTransaction.vendor + : this.selectedCCCTransaction.description) + + this.selectedCCCTransaction.amount; + + if (this.selectedCCCTransaction) { + this.cardNumber = this.selectedCCCTransaction.card_or_account_number; } - this.cardEndingDigits = cccAccountNumber && cccAccountNumber.slice(-4); - this.selectedCCCTransaction.corporate_credit_card_account_number = cccAccountNumber; - this.isCreatedFromCCC = true; - } - - handleCCCExpenses(etxn: UnflattenedTransaction): Subscription { - return this.corporateCreditCardExpenseService - .getEccceByGroupId(etxn.tx.corporate_credit_card_expense_group_id) - .subscribe((matchedExpense: CCCExpUnflattened[]) => { - this.matchedCCCTransaction = matchedExpense[0].ccce; - this.selectedCCCTransaction = this.matchedCCCTransaction; - this.cardEndingDigits = ( - this.selectedCCCTransaction.corporate_credit_card_account_number - ? this.selectedCCCTransaction.corporate_credit_card_account_number - : this.selectedCCCTransaction.card_or_account_number - ).slice(-4); - - etxn.tx.matchCCCId = this.selectedCCCTransaction.id; - - const txnDt = dayjs(this.selectedCCCTransaction.txn_dt).format('MMM D, YYYY'); - - this.selectedCCCTransaction.displayObject = - txnDt + - ' - ' + - (this.selectedCCCTransaction.vendor - ? this.selectedCCCTransaction.vendor - : this.selectedCCCTransaction.description) + - this.selectedCCCTransaction.amount; - - if (this.selectedCCCTransaction) { - this.cardNumber = this.selectedCCCTransaction.card_or_account_number; - } - }); } getSplitExpenses(splitExpenses: PlatformExpense[]): void { @@ -2846,7 +2806,7 @@ export class AddEditExpensePage implements OnInit { .pipe( switchMap((orgSettings) => this.etxn$.pipe(map((etxn) => ({ etxn, orgSettings })))), filter( - ({ orgSettings, etxn }: { orgSettings: OrgSettings; etxn: UnflattenedTransaction }) => + ({ orgSettings, etxn }: { orgSettings: OrgSettings; etxn: Partial }) => this.getCCCSettings(orgSettings) || !!etxn.tx.corporate_credit_card_expense_group_id ), filter(({ etxn }) => etxn.tx.corporate_credit_card_expense_group_id && !!etxn.tx.txn_dt), @@ -2872,19 +2832,19 @@ export class AddEditExpensePage implements OnInit { return isNumber(etxn.tx_policy_amount) && etxn.tx_policy_amount < 0.0001; } - getCheckSpiltExpense(etxn: UnflattenedTransaction): boolean { + getCheckSpiltExpense(etxn: Partial): boolean { return etxn?.tx?.split_group_id !== etxn?.tx?.id; } - getDebitCCCExpense(etxn: UnflattenedTransaction): boolean { + getDebitCCCExpense(etxn: Partial): boolean { return !!etxn?.tx?.corporate_credit_card_expense_group_id && etxn.tx.amount > 0; } - getDismissCCCExpense(etxn: UnflattenedTransaction): boolean { + getDismissCCCExpense(etxn: Partial): boolean { return !!etxn?.tx?.corporate_credit_card_expense_group_id && etxn.tx.amount < 0; } - getRemoveCCCExpense(etxn: UnflattenedTransaction): boolean { + getRemoveCCCExpense(etxn: Partial): boolean { return ( !!etxn?.tx?.corporate_credit_card_expense_group_id && ['APPROVER_PENDING', 'COMPLETE', 'DRAFT'].includes(etxn.tx.state) @@ -2924,7 +2884,6 @@ export class AddEditExpensePage implements OnInit { distance: [], distance_unit: [], custom_inputs: new FormArray([]), - duplicate_detection_reason: [], billable: [], costCenter: [], hotel_is_breakfast_provided: [], @@ -3095,7 +3054,7 @@ export class AddEditExpensePage implements OnInit { this.etxn$ = iif(() => this.activatedRoute.snapshot.params.id as boolean, editExpensePipe$, newExpensePipe$).pipe( shareReplay(1) - ) as Observable; + ); /** * Fetching the expense from platform APIs in edit case, this is required because corporate card transaction status (PENDING or POSTED) is not available in public transactions API @@ -3199,8 +3158,6 @@ export class AddEditExpensePage implements OnInit { map((etxn) => ['APPROVER_PENDING', 'APPROVER_INQUIRY'].indexOf(etxn.tx.state) > -1) ); - this.isNotReimbursable$ = this.etxn$.pipe(map((etxn) => !etxn.tx.user_can_delete && this.mode === 'edit')); - this.isAmountCapped$ = this.etxn$.pipe( map((etxn) => isNumber(etxn.tx.admin_amount) || isNumber(etxn.tx.policy_amount)) ); @@ -3351,16 +3308,12 @@ export class AddEditExpensePage implements OnInit { return this.getFormValues()?.hotel_is_breakfast_provided; } - getDuplicateReason(): string { - return this.getFormValues()?.duplicate_detection_reason; - } - getAmount(): number { return this.getFormValues()?.currencyObj?.amount; } generateEtxnFromFg( - etxn$: Observable, + etxn$: Observable>, standardisedCustomProperties$: Observable, isPolicyEtxn = false ): Observable> { @@ -3375,7 +3328,7 @@ export class AddEditExpensePage implements OnInit { attachments: attachements$, }).pipe( map((res) => { - const etxn: UnflattenedTransaction = res.etxn; + const etxn: Partial = res.etxn; let customProperties = res.customProperties; customProperties = customProperties.map((customProperty) => { if (customProperty.type === 'DATE') { @@ -3454,7 +3407,6 @@ export class AddEditExpensePage implements OnInit { distance: this.getDistance(), distance_unit: this.getDistanceUnit(), hotel_is_breakfast_provided: this.getBreakfastProvided(), - user_reason_for_duplicate_expenses: this.getDuplicateReason(), ...costCenter, }, ou: etxn.ou, @@ -3806,7 +3758,7 @@ export class AddEditExpensePage implements OnInit { }); } - trackEditExpense(etxn: UnflattenedTransaction): void { + trackEditExpense(etxn: Partial): void { this.trackingService.editExpense({ Type: 'Receipt', Amount: etxn.tx.amount, @@ -3828,9 +3780,7 @@ export class AddEditExpensePage implements OnInit { editExpense(redirectedFrom: string): Observable> { this.showSaveExpenseLoader(redirectedFrom); - this.trackPolicyCorrections(); - const customFields$ = this.getCustomFields(); return this.generateEtxnFromFg(this.etxn$, customFields$, true).pipe( @@ -3838,6 +3788,7 @@ export class AddEditExpensePage implements OnInit { const policyViolations$ = this.checkPolicyViolation( etxn as unknown as { tx: PublicPolicyExpense; dataUrls: Partial[] } ).pipe(shareReplay(1)); + return policyViolations$.pipe( map(this.policyService.getCriticalPolicyRules), switchMap((policyViolations) => { @@ -3893,12 +3844,12 @@ export class AddEditExpensePage implements OnInit { } } ), - switchMap(({ etxn, comment }: { etxn: UnflattenedTransaction; comment: string }) => + switchMap(({ etxn, comment }: { etxn: Partial; comment: string }) => forkJoin({ eou: from(this.authService.getEou()), txnCopy: this.etxn$, }).pipe( - switchMap(({ txnCopy }: { txnCopy: UnflattenedTransaction }) => { + switchMap(({ txnCopy }: { txnCopy: Partial }) => { if (!isEqual(etxn.tx, txnCopy)) { // only if the form is edited this.trackEditExpense(etxn); @@ -3907,14 +3858,12 @@ export class AddEditExpensePage implements OnInit { this.trackingService.viewExpense({ Type: 'Receipt' }); } - const reportControl = this.fg.value as { - report: UnflattenedReport; - }; + const reportControl = this.fg.value as { report: UnflattenedReport }; // NOTE: This double call is done as certain fields will not be present in return of upsert call. policy_amount in this case. return this.transactionService.upsert(etxn.tx as Transaction).pipe( - switchMap((txn) => this.transactionService.getETxnUnflattened(txn.id)), - map((savedEtxn) => savedEtxn && savedEtxn.tx), + switchMap((txn) => this.expensesService.getExpenseById(txn.id)), + map((expense) => this.transactionService.transformExpense(expense).tx), switchMap((tx) => { const selectedReportId = reportControl.report && reportControl.report.rp && reportControl.report.rp.id; const criticalPolicyViolated = this.getIsPolicyExpense(etxn as unknown as Expense); @@ -3941,33 +3890,24 @@ export class AddEditExpensePage implements OnInit { ); } } - return of(null).pipe(map(() => tx)); }), - switchMap((tx) => { - const criticalPolicyViolated = this.getIsPolicyExpense(etxn as unknown as Expense); - if (!criticalPolicyViolated && etxn.tx.user_review_needed) { - return this.transactionService.review(tx.id).pipe(map(() => tx)); + switchMap((txn) => { + if (comment) { + return this.statusService.findLatestComment(txn.id, 'transactions', txn.org_user_id).pipe( + switchMap((result) => { + if (result !== comment) { + return this.statusService.post('transactions', txn.id, { comment }, true).pipe(map(() => txn)); + } else { + return of(txn); + } + }) + ); + } else { + return of(txn); } - - return of(null).pipe(map(() => tx)); }) ); - }), - switchMap((txn) => { - if (comment) { - return this.statusService.findLatestComment(txn.id, 'transactions', txn.org_user_id).pipe( - switchMap((result) => { - if (result !== comment) { - return this.statusService.post('transactions', txn.id, { comment }, true).pipe(map(() => txn)); - } else { - return of(txn); - } - }) - ); - } else { - return of(txn); - } }) ) ), @@ -3982,9 +3922,13 @@ export class AddEditExpensePage implements OnInit { this.matchedCCCTransaction ) { return this.transactionService - .unmatchCCCExpense(transaction.id, this.matchedCCCTransaction.id) + .unmatchCCCExpense(this.matchedCCCTransaction.id, transaction.id) .pipe( - switchMap(() => this.transactionService.matchCCCExpense(transaction.id, this.selectedCCCTransaction.id)) + switchMap(() => + this.transactionService + .matchCCCExpense(this.selectedCCCTransaction.id, transaction.id) + .pipe(map(() => transaction)) + ) ); } } @@ -3995,12 +3939,16 @@ export class AddEditExpensePage implements OnInit { transaction.corporate_credit_card_expense_group_id && this.matchedCCCTransaction ) { - return this.transactionService.unmatchCCCExpense(transaction.id, this.matchedCCCTransaction.id); + return this.transactionService + .unmatchCCCExpense(this.matchedCCCTransaction.id, transaction.id) + .pipe(map(() => transaction)); } // Case is for matching a normal(unmatched) expense for the first time(edit) if (this.selectedCCCTransaction && !transaction.corporate_credit_card_expense_group_id) { - return this.transactionService.matchCCCExpense(transaction.id, this.selectedCCCTransaction.id); + return this.transactionService + .matchCCCExpense(this.selectedCCCTransaction.id, transaction.id) + .pipe(map(() => transaction)); } return of(transaction); @@ -4101,7 +4049,7 @@ export class AddEditExpensePage implements OnInit { ); } - trackCreateExpense(etxn: UnflattenedTransaction, isInstaFyleExpense: boolean): void { + trackCreateExpense(etxn: Partial, isInstaFyleExpense: boolean): void { this.trackingService.createExpense({ Type: 'Receipt', Amount: etxn.tx.amount, @@ -4200,7 +4148,7 @@ export class AddEditExpensePage implements OnInit { } } ), - switchMap(({ etxn, comment }: { etxn: UnflattenedTransaction; comment: string }) => + switchMap(({ etxn, comment }: { etxn: Partial; comment: string }) => from(this.authService.getEou()).pipe( switchMap(() => { const comments: string[] = []; @@ -4740,7 +4688,8 @@ export class AddEditExpensePage implements OnInit { if (data && data.status === 'success') { if (this.reviewList && this.reviewList.length && +this.activeIndex < this.reviewList.length - 1) { this.reviewList.splice(+this.activeIndex, 1); - this.transactionService.getETxnUnflattened(this.reviewList[+this.activeIndex]).subscribe((etxn) => { + this.expensesService.getExpenseById(this.reviewList[+this.activeIndex]).subscribe((expense) => { + const etxn = this.transactionService.transformExpense(expense); this.goToTransaction(etxn, this.reviewList, +this.activeIndex); }); } else if (removeExpenseFromReport) { @@ -4879,7 +4828,7 @@ export class AddEditExpensePage implements OnInit { } } ), - switchMap(({ etxn }: { etxn: UnflattenedTransaction }) => { + switchMap(({ etxn }: { etxn: Partial }) => { const personalCardTxn = this.activatedRoute.snapshot.params.personalCardTxn && (JSON.parse(this.activatedRoute.snapshot.params.personalCardTxn as string) as PersonalCardTxn); @@ -4959,12 +4908,14 @@ export class AddEditExpensePage implements OnInit { .reduce((acc, curVal) => acc.concat(curVal), []); if (duplicateIds.length > 0) { - const params = { - tx_id: `in.(${duplicateIds.join(',')})`, + const queryParams = { + id: `in.(${duplicateIds.join(',')})`, }; - return this.transactionService.getETxnc({ offset: 0, limit: 100, params }).pipe( + return this.expensesService.getAllExpenses({ offset: 0, limit: 100, queryParams }).pipe( map((expenses) => { - const expensesArray = expenses as []; + const expensesArray = expenses.map((expense) => + this.transactionService.transformRawExpense(expense) + ) as []; return transformedDuplicateSets.map((duplicateSet) => this.addExpenseDetailsToDuplicateSets(duplicateSet, expensesArray) ); @@ -4981,7 +4932,7 @@ export class AddEditExpensePage implements OnInit { }); } - addExpenseDetailsToDuplicateSets(duplicateSet: DuplicateSet, expensesArray: Expense[]): Expense[] { + addExpenseDetailsToDuplicateSets(duplicateSet: DuplicateSet, expensesArray: Partial[]): Partial[] { return duplicateSet.transaction_ids.map( (expenseId) => expensesArray[expensesArray.findIndex((duplicateTxn: Expense) => expenseId === duplicateTxn.tx_id)] ); diff --git a/src/app/fyle/add-edit-expense/add-edit-expense.setup.spec.ts b/src/app/fyle/add-edit-expense/add-edit-expense.setup.spec.ts index cc83d0b872..9748414c96 100644 --- a/src/app/fyle/add-edit-expense/add-edit-expense.setup.spec.ts +++ b/src/app/fyle/add-edit-expense/add-edit-expense.setup.spec.ts @@ -101,13 +101,13 @@ describe('AddEditExpensePage', () => { 'getRemoveCardExpenseDialogBody', 'removeCorporateCardExpense', 'unmatchCCCExpense', - 'getETxnUnflattened', + 'transformExpense', 'checkPolicy', 'checkMandatoryFields', 'upsert', 'review', 'matchCCCExpense', - 'getETxnc', + 'transformRawExpense', ]); const policyServiceSpy = jasmine.createSpyObj('PolicyService', [ 'transformTo', @@ -147,7 +147,6 @@ describe('AddEditExpensePage', () => { const corporateCreditCardExpenseServiceSpy = jasmine.createSpyObj('CorporateCreditCardExpenseService', [ 'markPersonal', 'dismissCreditTransaction', - 'getEccceByGroupId', ]); const trackingServiceSpy = jasmine.createSpyObj('TrackingService', [ 'viewExpense', @@ -210,6 +209,7 @@ describe('AddEditExpensePage', () => { const expensesServiceSpy = jasmine.createSpyObj('ExpensesService', [ 'getExpenseById', 'getDuplicatesByExpense', + 'getAllExpenses', 'getSplitExpenses', ]); diff --git a/src/app/fyle/add-edit-mileage/add-edit-mileage-1.spec.ts b/src/app/fyle/add-edit-mileage/add-edit-mileage-1.spec.ts index 6890599fd9..c11687df10 100644 --- a/src/app/fyle/add-edit-mileage/add-edit-mileage-1.spec.ts +++ b/src/app/fyle/add-edit-mileage/add-edit-mileage-1.spec.ts @@ -62,6 +62,7 @@ import { TaxGroupService } from 'src/app/core/services/tax-group.service'; import { TokenService } from 'src/app/core/services/token.service'; import { TrackingService } from 'src/app/core/services/tracking.service'; import { TransactionService } from 'src/app/core/services/transaction.service'; +import { ExpensesService } from 'src/app/core/services/platform/v1/spender/expenses.service'; import { TransactionsOutboxService } from 'src/app/core/services/transactions-outbox.service'; import { multiplePaymentModesData, @@ -74,6 +75,8 @@ import { FyCriticalPolicyViolationComponent } from 'src/app/shared/components/fy import { FyPolicyViolationComponent } from 'src/app/shared/components/fy-policy-violation/fy-policy-violation.component'; import { AddEditMileagePage } from './add-edit-mileage.page'; import { extendedAccountData1 } from 'src/app/core/mock-data/extended-account.data'; +import { platformExpenseData } from 'src/app/core/mock-data/platform/v1/expense.data'; +import { transformedExpenseData } from 'src/app/core/mock-data/transformed-expense.data'; export function TestCases1(getTestBed) { return describe('AddEditMileage-1', () => { @@ -90,6 +93,7 @@ export function TestCases1(getTestBed) { let customInputsService: jasmine.SpyObj; let customFieldsService: jasmine.SpyObj; let transactionService: jasmine.SpyObj; + let expensesService: jasmine.SpyObj; let policyService: jasmine.SpyObj; let transactionOutboxService: jasmine.SpyObj; let router: jasmine.SpyObj; @@ -144,6 +148,7 @@ export function TestCases1(getTestBed) { customInputsService = TestBed.inject(CustomInputsService) as jasmine.SpyObj; customFieldsService = TestBed.inject(CustomFieldsService) as jasmine.SpyObj; transactionService = TestBed.inject(TransactionService) as jasmine.SpyObj; + expensesService = TestBed.inject(ExpensesService) as jasmine.SpyObj; policyService = TestBed.inject(PolicyService) as jasmine.SpyObj; transactionOutboxService = TestBed.inject(TransactionsOutboxService) as jasmine.SpyObj; router = TestBed.inject(Router) as jasmine.SpyObj; @@ -196,7 +201,6 @@ export function TestCases1(getTestBed) { custom_inputs: new FormArray([]), costCenter: [], report: [], - duplicate_detection_reason: [], project_dependent_fields: formBuilder.array([]), cost_center_dependent_fields: formBuilder.array([]), }); @@ -214,26 +218,29 @@ export function TestCases1(getTestBed) { it('goToPrev(): should go to the previous txn', () => { spyOn(component, 'goToTransaction'); activatedRoute.snapshot.params.activeIndex = 1; - component.reviewList = ['txSEM4DtjyKR', 'txNyI8ot5CuJ']; - transactionService.getETxnUnflattened.and.returnValue(of(unflattenedTxnData)); + component.reviewList = ['txvslh8aQMbu', 'txNyI8ot5CuJ']; + expensesService.getExpenseById.and.returnValue(of(platformExpenseData)); + transactionService.transformExpense.and.returnValue(transformedExpenseData); fixture.detectChanges(); component.goToPrev(); - expect(transactionService.getETxnUnflattened).toHaveBeenCalledOnceWith('txSEM4DtjyKR'); - expect(component.goToTransaction).toHaveBeenCalledOnceWith(unflattenedTxnData, component.reviewList, 0); + expect(expensesService.getExpenseById).toHaveBeenCalledOnceWith('txvslh8aQMbu'); + expect(transactionService.transformExpense).toHaveBeenCalledOnceWith(platformExpenseData); + expect(component.goToTransaction).toHaveBeenCalledOnceWith(transformedExpenseData, component.reviewList, 0); }); it('goToNext(): should got to the next txn', () => { - const etxn = { ...unflattenedTxnData, tx: { ...unflattenedTxnData.tx, id: 'txNyI8ot5CuJ' } }; spyOn(component, 'goToTransaction'); activatedRoute.snapshot.params.activeIndex = 0; - component.reviewList = ['txSEM4DtjyKR', 'txNyI8ot5CuJ']; - transactionService.getETxnUnflattened.and.returnValue(of(etxn)); + component.reviewList = ['txNyI8ot5CuJ', 'txvslh8aQMbu']; + expensesService.getExpenseById.and.returnValue(of(platformExpenseData)); + transactionService.transformExpense.and.returnValue(transformedExpenseData); fixture.detectChanges(); component.goToNext(); - expect(transactionService.getETxnUnflattened).toHaveBeenCalledOnceWith('txNyI8ot5CuJ'); - expect(component.goToTransaction).toHaveBeenCalledOnceWith(etxn, component.reviewList, 1); + expect(expensesService.getExpenseById).toHaveBeenCalledOnceWith('txvslh8aQMbu'); + expect(transactionService.transformExpense).toHaveBeenCalledOnceWith(platformExpenseData); + expect(component.goToTransaction).toHaveBeenCalledOnceWith(transformedExpenseData, component.reviewList, 1); }); it('getPolicyDetails(): should get policy details', () => { @@ -665,11 +672,14 @@ export function TestCases1(getTestBed) { it('should delete mileage and go to the next transaction', fakeAsync(() => { spyOn(component, 'getDeleteReportParams'); spyOn(component, 'goToTransaction'); - transactionService.getETxnUnflattened.and.returnValue(of(unflattenedTxnData)); - component.reviewList = ['txfCdl3TEZ7K', 'txCYDX0peUw5']; + + expensesService.getExpenseById.and.returnValue(of(platformExpenseData)); + transactionService.transformExpense.and.returnValue(transformedExpenseData); + + component.reviewList = ['txvslh8aQMbu', 'txNyI8ot5CuJ']; component.activeIndex = 0; component.isRedirectedFromReport = true; - activatedRoute.snapshot.params.id = JSON.stringify('txfCdl3TEZ7K'); + activatedRoute.snapshot.params.id = JSON.stringify('txvslh8aQMbu'); fixture.detectChanges(); const deletePopoverSpy = jasmine.createSpyObj('deletePopover', ['present', 'onDidDismiss']); @@ -691,18 +701,17 @@ export function TestCases1(getTestBed) { body: 'Are you sure you want to delete this mileage expense?', ctaText: 'Delete', ctaLoadingText: 'Deleting', - id: '"txfCdl3TEZ7K"', + id: '"txvslh8aQMbu"', reportId: undefined, removeMileageFromReport: undefined, }); expect(popoverController.create).toHaveBeenCalledOnceWith( component.getDeleteReportParams({ header, body, ctaText, ctaLoadingText }) ); - expect(transactionService.getETxnUnflattened).toHaveBeenCalledOnceWith( - component.reviewList[+component.activeIndex] - ); + expect(expensesService.getExpenseById).toHaveBeenCalledOnceWith(component.reviewList[+component.activeIndex]); + expect(transactionService.transformExpense).toHaveBeenCalledOnceWith(platformExpenseData); expect(component.goToTransaction).toHaveBeenCalledOnceWith( - unflattenedTxnData, + transformedExpenseData, component.reviewList, +component.activeIndex ); @@ -921,10 +930,11 @@ export function TestCases1(getTestBed) { }); it('getEditExpense(): should return an unflattened expense to edit', (done) => { - transactionService.getETxnUnflattened.and.returnValue(of(unflattenedTxnData)); - activatedRoute.snapshot.params.id = unflattenedTxnData.tx.id; + expensesService.getExpenseById.and.returnValue(of(platformExpenseData)); + transactionService.transformExpense.and.returnValue(transformedExpenseData); + activatedRoute.snapshot.params.id = transformedExpenseData.tx.id; component.getEditExpense().subscribe((res) => { - expect(res).toEqual(unflattenedTxnData); + expect(res).toEqual(transformedExpenseData); done(); }); }); diff --git a/src/app/fyle/add-edit-mileage/add-edit-mileage-2.spec.ts b/src/app/fyle/add-edit-mileage/add-edit-mileage-2.spec.ts index cb2b7d6ab7..9ad5be7185 100644 --- a/src/app/fyle/add-edit-mileage/add-edit-mileage-2.spec.ts +++ b/src/app/fyle/add-edit-mileage/add-edit-mileage-2.spec.ts @@ -206,7 +206,6 @@ export function TestCases2(getTestBed) { custom_inputs: new FormArray([]), costCenter: [], report: [], - duplicate_detection_reason: [], project_dependent_fields: formBuilder.array([]), cost_center_dependent_fields: formBuilder.array([]), }); diff --git a/src/app/fyle/add-edit-mileage/add-edit-mileage-3.spec.ts b/src/app/fyle/add-edit-mileage/add-edit-mileage-3.spec.ts index d728049542..ba81781847 100644 --- a/src/app/fyle/add-edit-mileage/add-edit-mileage-3.spec.ts +++ b/src/app/fyle/add-edit-mileage/add-edit-mileage-3.spec.ts @@ -21,9 +21,9 @@ import { transformedOrgCategories, } from 'src/app/core/mock-data/org-category.data'; import { outboxQueueData1 } from 'src/app/core/mock-data/outbox-queue.data'; -import { expectedErpt } from 'src/app/core/mock-data/report-unflattened.data'; +import { expectedErpt, expectedErptPlatform } from 'src/app/core/mock-data/report-unflattened.data'; import { createExpenseProperties4, editExpenseProperties1 } from 'src/app/core/mock-data/track-expense-properties.data'; -import { txnStatusData } from 'src/app/core/mock-data/transaction-status.data'; +import { expenseStatusData, txnStatusData } from 'src/app/core/mock-data/transaction-status.data'; import { editTransaction2, editTransaction3, @@ -31,11 +31,14 @@ import { editTransaction5, editTransaction6, editUnflattenedTransaction, + editUnflattenedTransactionPlatform, + editUnflattenedTransactionPlatform2, } from 'src/app/core/mock-data/transaction.data'; import { txnCustomPropertiesData4 } from 'src/app/core/mock-data/txn-custom-properties.data'; import { expenseTrackCreate, newExpFromFg, + newExpFromFgPlatform, newExpenseMileageData2, unflattendedTxnWithPolicyAmount, unflattenedMileageDataWithPolicyAmount, @@ -79,9 +82,20 @@ import { TaxGroupService } from 'src/app/core/services/tax-group.service'; import { TokenService } from 'src/app/core/services/token.service'; import { TrackingService } from 'src/app/core/services/tracking.service'; import { TransactionService } from 'src/app/core/services/transaction.service'; +import { ExpensesService } from 'src/app/core/services/platform/v1/spender/expenses.service'; import { TransactionsOutboxService } from 'src/app/core/services/transactions-outbox.service'; import { expectedProjectsResponse } from 'src/app/core/test-data/projects.spec.data'; import { AddEditMileagePage } from './add-edit-mileage.page'; +import { + platformExpenseData, + platformExpenseDataWithReportId, + platformExpenseDataWithSubCategory, +} from 'src/app/core/mock-data/platform/v1/expense.data'; +import { + transformedExpenseData, + transformedExpenseDataWithReportId, + transformedExpenseDataWithSubCategory, +} from 'src/app/core/mock-data/transformed-expense.data'; export function TestCases3(getTestBed) { return describe('AddEditMileage-3', () => { @@ -98,6 +112,7 @@ export function TestCases3(getTestBed) { let customInputsService: jasmine.SpyObj; let customFieldsService: jasmine.SpyObj; let transactionService: jasmine.SpyObj; + let expensesService: jasmine.SpyObj; let policyService: jasmine.SpyObj; let transactionOutboxService: jasmine.SpyObj; let router: jasmine.SpyObj; @@ -152,6 +167,7 @@ export function TestCases3(getTestBed) { customInputsService = TestBed.inject(CustomInputsService) as jasmine.SpyObj; customFieldsService = TestBed.inject(CustomFieldsService) as jasmine.SpyObj; transactionService = TestBed.inject(TransactionService) as jasmine.SpyObj; + expensesService = TestBed.inject(ExpensesService) as jasmine.SpyObj; policyService = TestBed.inject(PolicyService) as jasmine.SpyObj; transactionOutboxService = TestBed.inject(TransactionsOutboxService) as jasmine.SpyObj; router = TestBed.inject(Router) as jasmine.SpyObj; @@ -204,7 +220,6 @@ export function TestCases3(getTestBed) { custom_inputs: new FormArray([]), costCenter: [], report: [], - duplicate_detection_reason: [], project_dependent_fields: formBuilder.array([]), cost_center_dependent_fields: formBuilder.array([]), }); @@ -585,22 +600,23 @@ export function TestCases3(getTestBed) { }); it('should edit an expense', (done) => { - spyOn(component, 'generateEtxnFromFg').and.returnValue(of(newExpFromFg)); + spyOn(component, 'generateEtxnFromFg').and.returnValue(of(newExpFromFgPlatform)); component.isConnected$ = of(true); - component.etxn$ = of(newExpFromFg); + component.etxn$ = of(newExpFromFgPlatform); spyOn(component, 'checkPolicyViolation').and.returnValue(of(null)); policyService.getCriticalPolicyRules.and.returnValue([]); policyService.getPolicyRules.and.returnValue([]); - transactionService.upsert.and.returnValue(of(newExpFromFg.tx)); - transactionService.getETxnUnflattened.and.returnValue(of(cloneDeep(unflattenedTxnDataWithSubCategory))); + transactionService.upsert.and.returnValue(of(newExpFromFgPlatform.tx)); + expensesService.getExpenseById.and.returnValue(of(cloneDeep(platformExpenseDataWithSubCategory))); + transactionService.transformExpense.and.returnValue(cloneDeep(transformedExpenseDataWithSubCategory)); spyOn(component, 'getFormValues').and.returnValue({ - report: expectedErpt[0], + report: expectedErptPlatform[0], }); spyOn(component, 'getIsPolicyExpense').and.returnValue(false); fixture.detectChanges(); component.editExpense('SAVE_MILEAGE').subscribe((res) => { - expect(res).toEqual(editUnflattenedTransaction); + expect(res).toEqual(editUnflattenedTransactionPlatform); expect(component.getCustomFields).toHaveBeenCalledTimes(1); expect(component.trackPolicyCorrections).toHaveBeenCalledTimes(1); expect(component.getFormControl).toHaveBeenCalledOnceWith('route'); @@ -610,16 +626,17 @@ export function TestCases3(getTestBed) { jasmine.any(Observable), jasmine.any(Observable) ); - expect(component.checkPolicyViolation).toHaveBeenCalledOnceWith(newExpFromFg); + expect(component.checkPolicyViolation).toHaveBeenCalledOnceWith(newExpFromFgPlatform); expect(policyService.getCriticalPolicyRules).toHaveBeenCalledTimes(1); expect(policyService.getPolicyRules).toHaveBeenCalledTimes(1); expect(component.trackEditExpense).toHaveBeenCalledTimes(1); - expect(transactionService.upsert).toHaveBeenCalledOnceWith(newExpFromFg.tx); - expect(transactionService.getETxnUnflattened).toHaveBeenCalledOnceWith(newExpFromFg.tx.id); + expect(transactionService.upsert).toHaveBeenCalledOnceWith(newExpFromFgPlatform.tx); + expect(expensesService.getExpenseById).toHaveBeenCalledOnceWith(newExpFromFgPlatform.tx.id); + expect(transactionService.transformExpense).toHaveBeenCalledOnceWith(platformExpenseDataWithSubCategory); expect(component.getFormValues).toHaveBeenCalledTimes(1); - expect(component.getIsPolicyExpense).toHaveBeenCalledTimes(2); - expect(reportService.addTransactions).toHaveBeenCalledOnceWith(expectedErpt[0].rp.id, [ - unflattenedTxnData.tx.id, + expect(component.getIsPolicyExpense).toHaveBeenCalledTimes(1); + expect(reportService.addTransactions).toHaveBeenCalledOnceWith(expectedErptPlatform[0].rp.id, [ + transformedExpenseDataWithSubCategory.tx.id, ]); expect(trackingService.addToExistingReportAddEditExpense).toHaveBeenCalledTimes(1); done(); @@ -627,22 +644,24 @@ export function TestCases3(getTestBed) { }); it('should edit an expense and add it to the report', (done) => { - spyOn(component, 'generateEtxnFromFg').and.returnValue(of(unflattenedTxnDataWithReportID)); + spyOn(component, 'generateEtxnFromFg').and.returnValue(of(transformedExpenseDataWithReportId)); component.isConnected$ = of(true); - component.etxn$ = of(unflattenedTxnDataWithReportID); + component.etxn$ = of(transformedExpenseDataWithReportId); spyOn(component, 'checkPolicyViolation').and.returnValue(of(null)); policyService.getCriticalPolicyRules.and.returnValue([]); policyService.getPolicyRules.and.returnValue([]); spyOn(component, 'getFormValues').and.returnValue({ - report: expectedErpt[0], + report: expectedErptPlatform[0], }); spyOn(component, 'getIsPolicyExpense').and.returnValue(false); - transactionService.upsert.and.returnValue(of(unflattenedTxnDataWithReportID.tx)); - transactionService.getETxnUnflattened.and.returnValue(of(unflattenedTxnDataWithReportID)); + transactionService.upsert.and.returnValue(of(transformedExpenseDataWithReportId.tx)); + expensesService.getExpenseById.and.returnValue(of(platformExpenseDataWithReportId)); + transactionService.transformExpense.and.returnValue(transformedExpenseDataWithReportId); + fixture.detectChanges(); component.editExpense('SAVE_MILEAGE').subscribe((res) => { - expect(res).toEqual(editTransaction3); + expect(res).toEqual(editUnflattenedTransactionPlatform2); expect(component.getCustomFields).toHaveBeenCalledTimes(1); expect(component.trackPolicyCorrections).toHaveBeenCalledTimes(1); expect(component.getFormControl).toHaveBeenCalledOnceWith('route'); @@ -656,36 +675,38 @@ export function TestCases3(getTestBed) { expect(policyService.getCriticalPolicyRules).toHaveBeenCalledTimes(1); expect(policyService.getPolicyRules).toHaveBeenCalledTimes(1); expect(component.trackEditExpense).toHaveBeenCalledTimes(1); - expect(transactionService.upsert).toHaveBeenCalledOnceWith(unflattenedTxnDataWithReportID.tx); - expect(transactionService.getETxnUnflattened).toHaveBeenCalledOnceWith(unflattenedTxnDataWithReportID.tx.id); + expect(transactionService.upsert).toHaveBeenCalledOnceWith(transformedExpenseDataWithReportId.tx); + expect(expensesService.getExpenseById).toHaveBeenCalledOnceWith(transformedExpenseDataWithReportId.tx.id); + expect(transactionService.transformExpense).toHaveBeenCalledOnceWith(platformExpenseDataWithReportId); expect(component.getFormValues).toHaveBeenCalledTimes(1); expect(reportService.removeTransaction).toHaveBeenCalledOnceWith( - unflattenedTxnDataWithReportID.tx.report_id, - unflattenedTxnDataWithReportID.tx.id + transformedExpenseDataWithReportId.tx.report_id, + transformedExpenseDataWithReportId.tx.id ); - expect(reportService.addTransactions).toHaveBeenCalledOnceWith('rprAfNrce73O', ['txbO4Xaj4N53']); + expect(reportService.addTransactions).toHaveBeenCalledOnceWith('rpIfg2VWQKGJ', ['txD5hIQgLuR5']); expect(trackingService.addToExistingReportAddEditExpense).toHaveBeenCalledTimes(1); done(); }); }); it('should edit an expense to remove transaction from report', (done) => { - spyOn(component, 'generateEtxnFromFg').and.returnValue(of(unflattenedTxnDataWithReportID2UserReview)); + spyOn(component, 'generateEtxnFromFg').and.returnValue(of(transformedExpenseDataWithReportId)); component.isConnected$ = of(true); - component.etxn$ = of(unflattenedTxnDataWithReportID2UserReview); + component.etxn$ = of(transformedExpenseDataWithReportId); spyOn(component, 'checkPolicyViolation').and.returnValue(of(null)); policyService.getCriticalPolicyRules.and.returnValue([]); policyService.getPolicyRules.and.returnValue([]); spyOn(component, 'getFormValues').and.returnValue({ report: null, }); - transactionService.upsert.and.returnValue(of(unflattenedTxnDataWithReportID2UserReview.tx)); - transactionService.getETxnUnflattened.and.returnValue(of(unflattenedTxnDataWithReportID2UserReview)); - transactionService.review.and.returnValue(of(null)); + transactionService.upsert.and.returnValue(of(transformedExpenseDataWithReportId.tx)); + expensesService.getExpenseById.and.returnValue(of(platformExpenseDataWithReportId)); + transactionService.transformExpense.and.returnValue(transformedExpenseDataWithReportId); + fixture.detectChanges(); component.editExpense('SAVE_MILEAGE').subscribe((res) => { - expect(res).toEqual(editTransaction4); + expect(res).toEqual(editUnflattenedTransactionPlatform2); expect(component.getCustomFields).toHaveBeenCalledTimes(1); expect(component.trackPolicyCorrections).toHaveBeenCalledTimes(1); expect(component.getFormControl).toHaveBeenCalledOnceWith('route'); @@ -699,43 +720,42 @@ export function TestCases3(getTestBed) { expect(policyService.getCriticalPolicyRules).toHaveBeenCalledTimes(1); expect(policyService.getPolicyRules).toHaveBeenCalledTimes(1); expect(component.trackEditExpense).toHaveBeenCalledTimes(1); - expect(transactionService.upsert).toHaveBeenCalledOnceWith(unflattenedTxnDataWithReportID2UserReview.tx); - expect(transactionService.getETxnUnflattened).toHaveBeenCalledOnceWith( - unflattenedTxnDataWithReportID2UserReview.tx.id - ); + expect(transactionService.upsert).toHaveBeenCalledOnceWith(transformedExpenseDataWithReportId.tx); + expect(expensesService.getExpenseById).toHaveBeenCalledOnceWith(transformedExpenseDataWithReportId.tx.id); + expect(transactionService.transformExpense).toHaveBeenCalledOnceWith(platformExpenseDataWithReportId); expect(component.getFormValues).toHaveBeenCalledTimes(1); expect(reportService.removeTransaction).toHaveBeenCalledOnceWith( - unflattenedTxnDataWithReportID2UserReview.tx.report_id, - unflattenedTxnDataWithReportID2UserReview.tx.id + transformedExpenseDataWithReportId.tx.report_id, + transformedExpenseDataWithReportId.tx.id ); expect(trackingService.removeFromExistingReportEditExpense).toHaveBeenCalledTimes(1); - expect(transactionService.review).toHaveBeenCalledTimes(1); done(); }); }); it('should edit an expense with critical policy violations', (done) => { - spyOn(component, 'generateEtxnFromFg').and.returnValue(of(newExpFromFg)); + spyOn(component, 'generateEtxnFromFg').and.returnValue(of(newExpFromFgPlatform)); component.isConnected$ = of(true); - component.etxn$ = of(unflattenedTxnDataWithReportID); + component.etxn$ = of(transformedExpenseDataWithReportId); spyOn(component, 'checkPolicyViolation').and.returnValue(of(null)); policyService.getCriticalPolicyRules.and.returnValue([ 'The expense will be flagged when the total amount of all expenses in category Others in a month exceeds: INR 3000.', ]); spyOn(component, 'criticalPolicyViolationHandler').and.returnValue( - of({ etxn: cloneDeep(unflattenedTxnDataWithSubCategory) }) + of({ etxn: cloneDeep(transformedExpenseDataWithSubCategory) }) ); transactionService.upsert.and.returnValue(of(newExpFromFg.tx)); - transactionService.getETxnUnflattened.and.returnValue(of(cloneDeep(unflattenedTxnDataWithSubCategory))); + expensesService.getExpenseById.and.returnValue(of(cloneDeep(platformExpenseDataWithReportId))); + transactionService.transformExpense.and.returnValue(transformedExpenseDataWithReportId); spyOn(component, 'getFormValues').and.returnValue({ - report: expectedErpt[0], + report: expectedErptPlatform[0], }); spyOn(component, 'getIsPolicyExpense').and.returnValue(true); fixture.detectChanges(); component.editExpense('SAVE_MILEAGE').subscribe((res) => { - expect(res).toEqual(editTransaction2); + expect(res).toEqual(editUnflattenedTransactionPlatform2); expect(component.getCustomFields).toHaveBeenCalledTimes(1); expect(component.trackPolicyCorrections).toHaveBeenCalledTimes(1); expect(component.getFormControl).toHaveBeenCalledOnceWith('route'); @@ -745,7 +765,7 @@ export function TestCases3(getTestBed) { jasmine.any(Observable), jasmine.any(Observable) ); - expect(component.checkPolicyViolation).toHaveBeenCalledOnceWith(newExpFromFg); + expect(component.checkPolicyViolation).toHaveBeenCalledOnceWith(newExpFromFgPlatform); expect(policyService.getCriticalPolicyRules).toHaveBeenCalledTimes(1); expect(component.criticalPolicyViolationHandler).toHaveBeenCalledTimes(1); expect(component.trackEditExpense).toHaveBeenCalledTimes(1); @@ -754,9 +774,9 @@ export function TestCases3(getTestBed) { }); it('should edit an expense with policy violation adding a comment to the expense as well', (done) => { - spyOn(component, 'generateEtxnFromFg').and.returnValue(of(newExpFromFg)); + spyOn(component, 'generateEtxnFromFg').and.returnValue(of(newExpFromFgPlatform)); component.isConnected$ = of(true); - component.etxn$ = of(unflattenedTxnDataWithReportID); + component.etxn$ = of(transformedExpenseDataWithSubCategory); spyOn(component, 'checkPolicyViolation').and.returnValue(of(expensePolicyData)); policyService.getCriticalPolicyRules.and.returnValue([]); policyService.getPolicyRules.and.returnValue([ @@ -764,21 +784,22 @@ export function TestCases3(getTestBed) { ]); spyOn(component, 'policyViolationHandler').and.returnValue( of({ - etxn: unflattenedTxnData, + etxn: transformedExpenseDataWithSubCategory, comment: 'A comment', }) ); - transactionService.upsert.and.returnValue(of(newExpFromFg.tx)); - transactionService.getETxnUnflattened.and.returnValue(of(cloneDeep(unflattenedTxnDataWithSubCategory))); + transactionService.upsert.and.returnValue(of(newExpFromFgPlatform.tx)); + expensesService.getExpenseById.and.returnValue(of(cloneDeep(platformExpenseDataWithSubCategory))); + transactionService.transformExpense.and.returnValue(cloneDeep(transformedExpenseDataWithSubCategory)); spyOn(component, 'getFormValues').and.returnValue({ - report: expectedErpt[0], + report: expectedErptPlatform[0], }); spyOn(component, 'getIsPolicyExpense').and.returnValue(true); statusService.findLatestComment.and.returnValue(of('A comment')); fixture.detectChanges(); component.editExpense('SAVE_MILEAGE').subscribe((res) => { - expect(res).toEqual(editTransaction5); + expect(res).toEqual(editUnflattenedTransactionPlatform); expect(component.getCustomFields).toHaveBeenCalledTimes(1); expect(component.trackPolicyCorrections).toHaveBeenCalledTimes(1); expect(component.getEditCalculatedDistance).toHaveBeenCalledTimes(1); @@ -796,26 +817,28 @@ export function TestCases3(getTestBed) { 'The expense will be flagged when the total amount of all expenses in category Others in a month exceeds: INR 3000.', ], policyAction: expensePolicyData.data.final_desired_state, - etxn: newExpFromFg, + etxn: newExpFromFgPlatform, }); expect(component.trackEditExpense).toHaveBeenCalledTimes(1); expect(authService.getEou).toHaveBeenCalledTimes(1); - expect(transactionService.upsert).toHaveBeenCalledOnceWith(unflattenedTxnData.tx); - expect(transactionService.getETxnUnflattened).toHaveBeenCalledOnceWith(newExpFromFg.tx.id); + expect(transactionService.upsert).toHaveBeenCalledOnceWith(transformedExpenseDataWithSubCategory.tx); + expect(expensesService.getExpenseById).toHaveBeenCalledOnceWith(newExpFromFgPlatform.tx.id); + expect(transactionService.transformExpense).toHaveBeenCalledOnceWith(platformExpenseDataWithSubCategory); + expect(component.getFormValues).toHaveBeenCalledTimes(1); expect(statusService.findLatestComment).toHaveBeenCalledOnceWith( - unflattenedTxnData.tx.id, + transformedExpenseDataWithSubCategory.tx.id, 'transactions', - unflattenedTxnData.tx.org_user_id + transformedExpenseDataWithSubCategory.tx.org_user_id ); done(); }); }); it('should edit an expense with policy violation adding a new comment', (done) => { - spyOn(component, 'generateEtxnFromFg').and.returnValue(of(newExpFromFg)); + spyOn(component, 'generateEtxnFromFg').and.returnValue(of(newExpFromFgPlatform)); component.isConnected$ = of(true); - component.etxn$ = of(unflattenedTxnDataWithReportID); + component.etxn$ = of(transformedExpenseDataWithReportId); spyOn(component, 'checkPolicyViolation').and.returnValue(of(expensePolicyData)); policyService.getCriticalPolicyRules.and.returnValue([]); policyService.getPolicyRules.and.returnValue([ @@ -823,23 +846,24 @@ export function TestCases3(getTestBed) { ]); spyOn(component, 'policyViolationHandler').and.returnValue( of({ - etxn: unflattenedTxnData, + etxn: transformedExpenseDataWithReportId, comment: 'A comment', }) ); - transactionService.upsert.and.returnValue(of(newExpFromFg.tx)); - transactionService.getETxnUnflattened.and.returnValue(of(cloneDeep(unflattenedTxnDataWithSubCategory))); + transactionService.upsert.and.returnValue(of(newExpFromFgPlatform.tx)); + expensesService.getExpenseById.and.returnValue(of(cloneDeep(platformExpenseDataWithReportId))); + transactionService.transformExpense.and.returnValue(transformedExpenseDataWithReportId); spyOn(component, 'getFormValues').and.returnValue({ - report: expectedErpt[0], + report: expectedErptPlatform[0], }); spyOn(component, 'getIsPolicyExpense').and.returnValue(true); statusService.findLatestComment.and.returnValue(of(null)); - statusService.post.and.returnValue(of(txnStatusData)); + statusService.post.and.returnValue(of(expenseStatusData)); fixture.detectChanges(); component.editExpense('SAVE_MILEAGE').subscribe((res) => { - expect(res).toEqual(editTransaction5); + expect(res).toEqual(editUnflattenedTransactionPlatform2); expect(component.getCustomFields).toHaveBeenCalledTimes(1); expect(component.trackPolicyCorrections).toHaveBeenCalledTimes(1); expect(component.getEditCalculatedDistance).toHaveBeenCalledTimes(1); @@ -857,21 +881,22 @@ export function TestCases3(getTestBed) { 'The expense will be flagged when the total amount of all expenses in category Others in a month exceeds: INR 3000.', ], policyAction: expensePolicyData.data.final_desired_state, - etxn: newExpFromFg, + etxn: newExpFromFgPlatform, }); expect(component.trackEditExpense).toHaveBeenCalledTimes(1); expect(authService.getEou).toHaveBeenCalledTimes(1); - expect(transactionService.upsert).toHaveBeenCalledOnceWith(unflattenedTxnData.tx); - expect(transactionService.getETxnUnflattened).toHaveBeenCalledOnceWith(newExpFromFg.tx.id); + expect(transactionService.upsert).toHaveBeenCalledOnceWith(transformedExpenseDataWithReportId.tx); + expect(expensesService.getExpenseById).toHaveBeenCalledOnceWith(newExpFromFgPlatform.tx.id); + expect(transactionService.transformExpense).toHaveBeenCalledOnceWith(platformExpenseDataWithReportId); expect(component.getFormValues).toHaveBeenCalledTimes(1); expect(statusService.findLatestComment).toHaveBeenCalledOnceWith( - unflattenedTxnData.tx.id, + transformedExpenseDataWithReportId.tx.id, 'transactions', - unflattenedTxnData.tx.org_user_id + transformedExpenseDataWithReportId.tx.org_user_id ); expect(statusService.post).toHaveBeenCalledOnceWith( 'transactions', - unflattenedTxnData.tx.id, + transformedExpenseDataWithReportId.tx.id, { comment: 'A comment' }, true ); @@ -901,20 +926,21 @@ export function TestCases3(getTestBed) { }); it('should edit an expense in offline mode', (done) => { - spyOn(component, 'generateEtxnFromFg').and.returnValue(of(newExpFromFg)); + spyOn(component, 'generateEtxnFromFg').and.returnValue(of(newExpFromFgPlatform)); component.isConnected$ = of(false); - component.etxn$ = of(newExpFromFg); + component.etxn$ = of(newExpFromFgPlatform); spyOn(component, 'getFormValues').and.returnValue({ - report: expectedErpt[0], + report: expectedErptPlatform[0], }); spyOn(component, 'getIsPolicyExpense').and.returnValue(false); - transactionService.upsert.and.returnValue(of(unflattenedTxnDataWithReportID.tx)); - transactionService.getETxnUnflattened.and.returnValue(of(unflattenedTxnDataWithReportID)); + transactionService.upsert.and.returnValue(of(transformedExpenseDataWithReportId.tx)); + expensesService.getExpenseById.and.returnValue(of(cloneDeep(platformExpenseDataWithReportId))); + transactionService.transformExpense.and.returnValue(transformedExpenseDataWithReportId); fixture.detectChanges(); component.editExpense('SAVE_MILEAGE').subscribe((res) => { - expect(res).toEqual(editTransaction6); + expect(res).toEqual(editUnflattenedTransactionPlatform2); expect(component.getCustomFields).toHaveBeenCalledTimes(1); expect(component.trackPolicyCorrections).toHaveBeenCalledTimes(1); expect(component.getFormControl).toHaveBeenCalledOnceWith('route'); @@ -926,11 +952,14 @@ export function TestCases3(getTestBed) { ); expect(authService.getEou).toHaveBeenCalledTimes(1); expect(component.trackEditExpense).toHaveBeenCalledTimes(1); - expect(transactionService.upsert).toHaveBeenCalledOnceWith(newExpFromFg.tx); - expect(transactionService.getETxnUnflattened).toHaveBeenCalledOnceWith(newExpFromFg.tx.id); + expect(transactionService.upsert).toHaveBeenCalledOnceWith(newExpFromFgPlatform.tx); + expect(expensesService.getExpenseById).toHaveBeenCalledOnceWith(newExpFromFgPlatform.tx.id); + expect(transactionService.transformExpense).toHaveBeenCalledOnceWith(platformExpenseDataWithReportId); expect(component.getFormValues).toHaveBeenCalledTimes(1); - expect(component.getIsPolicyExpense).toHaveBeenCalledTimes(2); - expect(reportService.addTransactions).toHaveBeenCalledOnceWith(expectedErpt[0].rp.id, ['txbO4Xaj4N53']); + expect(component.getIsPolicyExpense).toHaveBeenCalledTimes(1); + expect(reportService.addTransactions).toHaveBeenCalledOnceWith(expectedErptPlatform[0].rp.id, [ + 'txD5hIQgLuR5', + ]); expect(trackingService.addToExistingReportAddEditExpense).toHaveBeenCalledTimes(1); done(); }); diff --git a/src/app/fyle/add-edit-mileage/add-edit-mileage-4.spec.ts b/src/app/fyle/add-edit-mileage/add-edit-mileage-4.spec.ts index 9d35d6d021..fcb5c7634d 100644 --- a/src/app/fyle/add-edit-mileage/add-edit-mileage-4.spec.ts +++ b/src/app/fyle/add-edit-mileage/add-edit-mileage-4.spec.ts @@ -211,7 +211,6 @@ export function TestCases4(getTestBed) { custom_inputs: new FormArray([]), costCenter: [], report: [], - duplicate_detection_reason: [], project_dependent_fields: formBuilder.array([]), cost_center_dependent_fields: formBuilder.array([]), }); @@ -769,7 +768,6 @@ export function TestCases4(getTestBed) { custom_inputs: [], costCenter: null, report: null, - duplicate_detection_reason: null, project_dependent_fields: [], cost_center_dependent_fields: [], }); diff --git a/src/app/fyle/add-edit-mileage/add-edit-mileage-5.spec.ts b/src/app/fyle/add-edit-mileage/add-edit-mileage-5.spec.ts index 8dacbf39d1..43d7d98e52 100644 --- a/src/app/fyle/add-edit-mileage/add-edit-mileage-5.spec.ts +++ b/src/app/fyle/add-edit-mileage/add-edit-mileage-5.spec.ts @@ -202,7 +202,6 @@ export function TestCases5(getTestBed) { custom_inputs: new FormArray([]), costCenter: [], report: [], - duplicate_detection_reason: [], project_dependent_fields: formBuilder.array([]), cost_center_dependent_fields: formBuilder.array([]), }); diff --git a/src/app/fyle/add-edit-mileage/add-edit-mileage.page.setup.spec.ts b/src/app/fyle/add-edit-mileage/add-edit-mileage.page.setup.spec.ts index 9f08dbfe06..3d084a17b0 100644 --- a/src/app/fyle/add-edit-mileage/add-edit-mileage.page.setup.spec.ts +++ b/src/app/fyle/add-edit-mileage/add-edit-mileage.page.setup.spec.ts @@ -45,6 +45,7 @@ import { TaxGroupService } from 'src/app/core/services/tax-group.service'; import { TokenService } from 'src/app/core/services/token.service'; import { TrackingService } from 'src/app/core/services/tracking.service'; import { TransactionService } from 'src/app/core/services/transaction.service'; +import { ExpensesService } from 'src/app/core/services/platform/v1/spender/expenses.service'; import { TransactionsOutboxService } from 'src/app/core/services/transactions-outbox.service'; import { DependentFieldComponent } from 'src/app/shared/components/dependent-fields/dependent-field/dependent-field.component'; import { FySelectComponent } from 'src/app/shared/components/fy-select/fy-select.component'; @@ -104,15 +105,14 @@ describe('AddEditMileagePage', () => { 'getRemoveCardExpenseDialogBody', 'removeCorporateCardExpense', 'unmatchCCCExpense', - 'getETxnUnflattened', - 'getSplitExpenses', + 'transformExpense', 'checkPolicy', 'upsert', 'review', 'matchCCCExpense', - 'getETxnc', 'getDefaultVehicleType', ]); + const expensesServiceSpy = jasmine.createSpyObj('ExpensesService', ['getExpenseById']); const policyServiceSpy = jasmine.createSpyObj('PolicyService', [ 'transformTo', 'getCriticalPolicyRules', @@ -150,7 +150,6 @@ describe('AddEditMileagePage', () => { const corporateCreditCardExpenseServiceSpy = jasmine.createSpyObj('CorporateCreditCardExpenseService', [ 'markPersonal', 'dismissCreditTransaction', - 'getEccceByGroupId', ]); const trackingServiceSpy = jasmine.createSpyObj('TrackingService', [ 'viewExpense', @@ -433,6 +432,10 @@ describe('AddEditMileagePage', () => { provide: PlatformHandlerService, useValue: platformHandlerServiceSpy, }, + { + provide: ExpensesService, + useValue: expensesServiceSpy, + }, ], schemas: [CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA], }); diff --git a/src/app/fyle/add-edit-mileage/add-edit-mileage.page.ts b/src/app/fyle/add-edit-mileage/add-edit-mileage.page.ts index 0503fc6cb8..7c7d299ec1 100644 --- a/src/app/fyle/add-edit-mileage/add-edit-mileage.page.ts +++ b/src/app/fyle/add-edit-mileage/add-edit-mileage.page.ts @@ -103,6 +103,7 @@ import { ToastMessageComponent } from 'src/app/shared/components/toast-message/t import { TrackingService } from '../../core/services/tracking.service'; import { PlatformHandlerService } from 'src/app/core/services/platform-handler.service'; import { MileageRatesOptions } from 'src/app/core/models/mileage-rates-options.data'; +import { ExpensesService } from 'src/app/core/services/platform/v1/spender/expenses.service'; type FormValue = { route: { @@ -113,7 +114,6 @@ type FormValue = { category: OrgCategory; sub_category: OrgCategory; report: UnflattenedReport; - duplicate_detection_reason: string; paymentMode: ExtendedAccount; custom_inputs: CustomInput[]; mileage_rate_name: PlatformMileageRates; @@ -320,7 +320,8 @@ export class AddEditMileagePage implements OnInit { private categoriesService: CategoriesService, private orgSettingsService: OrgSettingsService, private platformHandlerService: PlatformHandlerService, - private storageService: StorageService + private storageService: StorageService, + private expensesService: ExpensesService ) {} get showSaveAndNext(): boolean { @@ -375,7 +376,8 @@ export class AddEditMileagePage implements OnInit { this.activeIndex = this.activatedRoute.snapshot.params.activeIndex as number; if (this.reviewList[+this.activeIndex - 1]) { - this.transactionService.getETxnUnflattened(this.reviewList[+this.activeIndex - 1]).subscribe((etxn) => { + this.expensesService.getExpenseById(this.reviewList[+this.activeIndex - 1]).subscribe((expense) => { + const etxn = this.transactionService.transformExpense(expense); this.goToTransaction(etxn, this.reviewList, +this.activeIndex - 1); }); } @@ -385,14 +387,15 @@ export class AddEditMileagePage implements OnInit { this.activeIndex = this.activatedRoute.snapshot.params.activeIndex as number; if (this.reviewList[+this.activeIndex + 1]) { - this.transactionService.getETxnUnflattened(this.reviewList[+this.activeIndex + 1]).subscribe((etxn) => { + this.expensesService.getExpenseById(this.reviewList[+this.activeIndex + 1]).subscribe((expense) => { + const etxn = this.transactionService.transformExpense(expense); this.goToTransaction(etxn, this.reviewList, +this.activeIndex + 1); }); } } - goToTransaction(expense: UnflattenedTransaction, reviewList, activeIndex: number): void { - let category; + goToTransaction(expense: Partial, reviewList, activeIndex: number): void { + let category: string; if (expense.tx.org_category) { category = expense.tx.org_category.toLowerCase(); @@ -908,10 +911,13 @@ export class AddEditMileagePage implements OnInit { ); } - getEditExpense(): Observable { - return this.transactionService - .getETxnUnflattened(this.activatedRoute.snapshot.params.id as string) - .pipe(shareReplay(1)); + getEditExpense(): Observable> { + const expenseId = this.activatedRoute.snapshot.params.id as string; + + return this.expensesService.getExpenseById(expenseId).pipe( + map((expense) => this.transactionService.transformExpense(expense)), + shareReplay(1) + ); } customDateValidator(control: AbstractControl): null | { invalidDateSelection: boolean } { @@ -928,7 +934,7 @@ export class AddEditMileagePage implements OnInit { } } - getCategories(etxn: UnflattenedTransaction): Observable { + getCategories(etxn: Partial): Observable { return this.categoriesService .getAll() .pipe( @@ -1196,15 +1202,23 @@ export class AddEditMileagePage implements OnInit { etxn: this.etxn$, mileageRates: this.mileageRates$, }).pipe( - map(({ etxn, mileageRates }: { etxn: UnflattenedTransaction; mileageRates: PlatformMileageRates[] }) => { - if (formValue) { - if (etxn.tx.mileage_rate && etxn.tx.mileage_vehicle_type === formValue.vehicle_type) { - return etxn.tx.mileage_rate; - } else { - return this.getRateByVehicleType(mileageRates, formValue.vehicle_type); + map( + ({ + etxn, + mileageRates, + }: { + etxn: Partial; + mileageRates: PlatformMileageRates[]; + }) => { + if (formValue) { + if (etxn.tx.mileage_rate && etxn.tx.mileage_vehicle_type === formValue.vehicle_type) { + return etxn.tx.mileage_rate; + } else { + return this.getRateByVehicleType(mileageRates, formValue.vehicle_type); + } } } - }) + ) ) ), shareReplay(1) @@ -1295,7 +1309,6 @@ export class AddEditMileagePage implements OnInit { custom_inputs: new FormArray([]), costCenter: [], report: [], - duplicate_detection_reason: [], project_dependent_fields: this.fb.array([]), cost_center_dependent_fields: this.fb.array([]), }); @@ -1477,7 +1490,7 @@ export class AddEditMileagePage implements OnInit { ); const selectedSubCategory$ = this.etxn$.pipe( - switchMap((etxn: UnflattenedTransaction) => + switchMap((etxn: Partial) => iif(() => !!etxn.tx.org_category_id, this.getCategories(etxn), of(null)) ) ); @@ -1664,7 +1677,6 @@ export class AddEditMileagePage implements OnInit { billable: etxn.tx.billable, sub_category: subCategory, costCenter, - duplicate_detection_reason: etxn.tx.user_reason_for_duplicate_expenses, report, }); @@ -1742,7 +1754,7 @@ export class AddEditMileagePage implements OnInit { amount: this.amount$.pipe(take(1)), etxn: this.etxn$, }).pipe( - map(({ etxn, amount }: { etxn: UnflattenedTransaction; amount: number }) => { + map(({ etxn, amount }: { etxn: Partial; amount: number }) => { const formValue = this.getFormValues(); const paymentAccount = formValue.paymentMode; const originalSourceAccountId = etxn && etxn.tx && etxn.tx.source_account_id; @@ -2078,7 +2090,6 @@ export class AddEditMileagePage implements OnInit { cost_center_id: formValue.costCenter && formValue.costCenter.id, cost_center_name: formValue.costCenter && formValue.costCenter.name, cost_center_code: formValue.costCenter && formValue.costCenter.code, - user_reason_for_duplicate_expenses: formValue.duplicate_detection_reason, }, dataUrls: [], ou: etxn.ou, @@ -2116,7 +2127,7 @@ export class AddEditMileagePage implements OnInit { return isNumber(etxn.tx_policy_amount) && etxn.tx_policy_amount < 0.0001; } - trackEditExpense(etxn: UnflattenedTransaction): void { + trackEditExpense(etxn: Partial): void { const location = etxn.tx.locations[0] as unknown as Destination; this.trackingService.editExpense({ Type: 'Mileage', @@ -2226,7 +2237,7 @@ export class AddEditMileagePage implements OnInit { status?: number; type?: string; policyViolations: string[]; - etxn: UnflattenedTransaction; + etxn: Partial; policyAction: FinalExpensePolicyState; }) => { if (err.status === 500) { @@ -2243,7 +2254,7 @@ export class AddEditMileagePage implements OnInit { } } ), - switchMap(({ etxn, comment }: { etxn: UnflattenedTransaction; comment: string }) => + switchMap(({ etxn, comment }: { etxn: Partial; comment: string }) => forkJoin({ eou: from(this.authService.getEou()), txnCopy: this.etxn$, @@ -2259,8 +2270,8 @@ export class AddEditMileagePage implements OnInit { // NOTE: This double call is done as certain fields will not be present in return of upsert call. policy_amount in this case. return this.transactionService.upsert(etxn.tx as Transaction).pipe( - switchMap((txn) => this.transactionService.getETxnUnflattened(txn.id)), - map((savedEtxn) => savedEtxn && savedEtxn.tx), + switchMap((txn) => this.expensesService.getExpenseById(txn.id)), + map((expense) => this.transactionService.transformExpense(expense).tx), switchMap((tx) => { const formValue = this.getFormValues(); const selectedReportId = formValue.report && formValue.report.rp && formValue.report.rp.id; @@ -2289,14 +2300,6 @@ export class AddEditMileagePage implements OnInit { } } - return of(null).pipe(map(() => tx)); - }), - switchMap((tx) => { - const criticalPolicyViolated = this.getIsPolicyExpense(tx as unknown as Expense); - if (!criticalPolicyViolated && etxn.tx.user_review_needed) { - return this.transactionService.review(tx.id).pipe(map(() => tx)); - } - return of(null).pipe(map(() => tx)); }) ); @@ -2367,7 +2370,7 @@ export class AddEditMileagePage implements OnInit { ); } - trackCreateExpense(etxn: UnflattenedTransaction): void { + trackCreateExpense(etxn: Partial): void { const location = etxn.tx.locations[0] as unknown as Destination; this.trackingService.createExpense({ Type: 'Mileage', @@ -2488,7 +2491,7 @@ export class AddEditMileagePage implements OnInit { status?: number; type?: string; policyViolations: string[]; - etxn: UnflattenedTransaction; + etxn: Partial; policyAction: FinalExpensePolicyState; }) => { if (err.status === 500) { @@ -2505,7 +2508,7 @@ export class AddEditMileagePage implements OnInit { } } ), - switchMap(({ etxn, comment }: { etxn: UnflattenedTransaction; comment: string }) => + switchMap(({ etxn, comment }: { etxn: Partial; comment: string }) => from(this.authService.getEou()).pipe( switchMap(() => { const comments: string[] = []; @@ -2621,7 +2624,8 @@ export class AddEditMileagePage implements OnInit { if (data && data.status === 'success') { if (this.reviewList && this.reviewList.length && +this.activeIndex < this.reviewList.length - 1) { this.reviewList.splice(+this.activeIndex, 1); - this.transactionService.getETxnUnflattened(this.reviewList[+this.activeIndex]).subscribe((etxn) => { + this.expensesService.getExpenseById(this.reviewList[+this.activeIndex]).subscribe((expense) => { + const etxn = this.transactionService.transformExpense(expense); this.goToTransaction(etxn, this.reviewList, +this.activeIndex); }); } else if (removeMileageFromReport) { diff --git a/src/app/fyle/add-edit-per-diem/add-edit-per-diem-1.page.spec.ts b/src/app/fyle/add-edit-per-diem/add-edit-per-diem-1.page.spec.ts index f5b8b3f7f8..eadb12a5a1 100644 --- a/src/app/fyle/add-edit-per-diem/add-edit-per-diem-1.page.spec.ts +++ b/src/app/fyle/add-edit-per-diem/add-edit-per-diem-1.page.spec.ts @@ -25,6 +25,7 @@ import { StorageService } from 'src/app/core/services/storage.service'; import { TokenService } from 'src/app/core/services/token.service'; import { TrackingService } from 'src/app/core/services/tracking.service'; import { TransactionService } from 'src/app/core/services/transaction.service'; +import { ExpensesService } from 'src/app/core/services/platform/v1/spender/expenses.service'; import { TransactionsOutboxService } from 'src/app/core/services/transactions-outbox.service'; import { FormArray, FormBuilder, Validators } from '@angular/forms'; @@ -53,6 +54,14 @@ import { defaultTxnFieldValuesData2 } from 'src/app/core/mock-data/default-txn-f import { orgSettingsCCCDisabled } from 'src/app/core/mock-data/org-settings.data'; import { ExpenseType } from 'src/app/core/enums/expense-type.enum'; import { expectedProjectsResponse } from 'src/app/core/test-data/projects.spec.data'; +import { + platformExpenseData, + platformExpenseDataWithSubCategory, +} from 'src/app/core/mock-data/platform/v1/expense.data'; +import { + transformedExpenseData, + transformedExpenseDataWithSubCategory, +} from 'src/app/core/mock-data/transformed-expense.data'; export function TestCases1(getTestBed) { return describe('add-edit-per-diem test cases set 1', () => { @@ -69,6 +78,7 @@ export function TestCases1(getTestBed) { let customInputsService: jasmine.SpyObj; let customFieldsService: jasmine.SpyObj; let transactionService: jasmine.SpyObj; + let expensesService: jasmine.SpyObj; let policyService: jasmine.SpyObj; let transactionOutboxService: jasmine.SpyObj; let router: jasmine.SpyObj; @@ -109,6 +119,7 @@ export function TestCases1(getTestBed) { customInputsService = TestBed.inject(CustomInputsService) as jasmine.SpyObj; customFieldsService = TestBed.inject(CustomFieldsService) as jasmine.SpyObj; transactionService = TestBed.inject(TransactionService) as jasmine.SpyObj; + expensesService = TestBed.inject(ExpensesService) as jasmine.SpyObj; policyService = TestBed.inject(PolicyService) as jasmine.SpyObj; transactionOutboxService = TestBed.inject(TransactionsOutboxService) as jasmine.SpyObj; router = TestBed.inject(Router) as jasmine.SpyObj; @@ -150,7 +161,6 @@ export function TestCases1(getTestBed) { from_dt: [], to_dt: [, component.customDateValidator.bind(component)], custom_inputs: new FormArray([]), - duplicate_detection_reason: [], billable: [], costCenter: [], project_dependent_fields: formBuilder.array([]), @@ -342,26 +352,33 @@ export function TestCases1(getTestBed) { it('goToPrev(): should go to the previous txn', () => { spyOn(component, 'goToTransaction'); activatedRoute.snapshot.params.activeIndex = 1; - component.reviewList = ['txSEM4DtjyKR', 'txNyI8ot5CuJ']; - transactionService.getETxnUnflattened.and.returnValue(of(unflattenedTxnData)); + component.reviewList = ['txvslh8aQMbu', 'txNyI8ot5CuJ']; + expensesService.getExpenseById.and.returnValue(of(platformExpenseData)); + transactionService.transformExpense.and.returnValue(transformedExpenseData); fixture.detectChanges(); component.goToPrev(); - expect(transactionService.getETxnUnflattened).toHaveBeenCalledOnceWith('txSEM4DtjyKR'); - expect(component.goToTransaction).toHaveBeenCalledOnceWith(unflattenedTxnData, component.reviewList, 0); + expect(expensesService.getExpenseById).toHaveBeenCalledOnceWith('txvslh8aQMbu'); + expect(transactionService.transformExpense).toHaveBeenCalledOnceWith(platformExpenseData); + expect(component.goToTransaction).toHaveBeenCalledOnceWith(transformedExpenseData, component.reviewList, 0); }); it('goToNext(): should got to the next txn', () => { - const etxn = { ...unflattenedTxnData, tx: { ...unflattenedTxnData.tx, id: 'txNyI8ot5CuJ' } }; spyOn(component, 'goToTransaction'); activatedRoute.snapshot.params.activeIndex = 0; - component.reviewList = ['txSEM4DtjyKR', 'txNyI8ot5CuJ']; - transactionService.getETxnUnflattened.and.returnValue(of(etxn)); + component.reviewList = ['txSEM4DtjyKR', 'txD5hIQgLuR5']; + expensesService.getExpenseById.and.returnValue(of(platformExpenseDataWithSubCategory)); + transactionService.transformExpense.and.returnValue(transformedExpenseDataWithSubCategory); fixture.detectChanges(); component.goToNext(); - expect(transactionService.getETxnUnflattened).toHaveBeenCalledOnceWith('txNyI8ot5CuJ'); - expect(component.goToTransaction).toHaveBeenCalledOnceWith(etxn, component.reviewList, 1); + expect(expensesService.getExpenseById).toHaveBeenCalledOnceWith('txD5hIQgLuR5'); + expect(transactionService.transformExpense).toHaveBeenCalledOnceWith(platformExpenseDataWithSubCategory); + expect(component.goToTransaction).toHaveBeenCalledOnceWith( + transformedExpenseDataWithSubCategory, + component.reviewList, + 1 + ); }); describe('goToTransaction():', () => { diff --git a/src/app/fyle/add-edit-per-diem/add-edit-per-diem-2.page.spec.ts b/src/app/fyle/add-edit-per-diem/add-edit-per-diem-2.page.spec.ts index b15f027f88..849a017829 100644 --- a/src/app/fyle/add-edit-per-diem/add-edit-per-diem-2.page.spec.ts +++ b/src/app/fyle/add-edit-per-diem/add-edit-per-diem-2.page.spec.ts @@ -25,6 +25,7 @@ import { StorageService } from 'src/app/core/services/storage.service'; import { TokenService } from 'src/app/core/services/token.service'; import { TrackingService } from 'src/app/core/services/tracking.service'; import { TransactionService } from 'src/app/core/services/transaction.service'; +import { ExpensesService } from 'src/app/core/services/platform/v1/spender/expenses.service'; import { TransactionsOutboxService } from 'src/app/core/services/transactions-outbox.service'; import { FormArray, FormBuilder, Validators } from '@angular/forms'; @@ -88,6 +89,8 @@ import { perDiemFormValuesData6, perDiemFormValuesData7, } from 'src/app/core/mock-data/per-diem-form-value.data'; +import { platformExpenseData } from 'src/app/core/mock-data/platform/v1/expense.data'; +import { transformedExpenseData } from 'src/app/core/mock-data/transformed-expense.data'; export function TestCases2(getTestBed) { return describe('add-edit-per-diem test cases set 2', () => { @@ -104,6 +107,7 @@ export function TestCases2(getTestBed) { let customInputsService: jasmine.SpyObj; let customFieldsService: jasmine.SpyObj; let transactionService: jasmine.SpyObj; + let expensesService: jasmine.SpyObj; let policyService: jasmine.SpyObj; let transactionOutboxService: jasmine.SpyObj; let router: jasmine.SpyObj; @@ -144,6 +148,7 @@ export function TestCases2(getTestBed) { customInputsService = TestBed.inject(CustomInputsService) as jasmine.SpyObj; customFieldsService = TestBed.inject(CustomFieldsService) as jasmine.SpyObj; transactionService = TestBed.inject(TransactionService) as jasmine.SpyObj; + expensesService = TestBed.inject(ExpensesService) as jasmine.SpyObj; policyService = TestBed.inject(PolicyService) as jasmine.SpyObj; transactionOutboxService = TestBed.inject(TransactionsOutboxService) as jasmine.SpyObj; router = TestBed.inject(Router) as jasmine.SpyObj; @@ -184,7 +189,6 @@ export function TestCases2(getTestBed) { from_dt: [], to_dt: [, component.customDateValidator.bind(component)], custom_inputs: new FormArray([]), - duplicate_detection_reason: [], billable: [], costCenter: [], project_dependent_fields: formBuilder.array([]), @@ -210,13 +214,15 @@ export function TestCases2(getTestBed) { }); }); - it('getEditExpense(): should call transactionService.getETxnUnflattened and return unflattened transaction data', () => { - transactionService.getETxnUnflattened.and.returnValue(of(unflattenedTxnData)); - activatedRoute.snapshot.params = { id: 'tx5n59fvxk4z' }; + it('getEditExpense(): should call expensesService.getExpensesById and return expense data', () => { + expensesService.getExpenseById.and.returnValue(of(platformExpenseData)); + transactionService.transformExpense.and.returnValue(transformedExpenseData); + activatedRoute.snapshot.params = { id: 'txvslh8aQMbu' }; component.getEditExpense().subscribe((res) => { - expect(transactionService.getETxnUnflattened).toHaveBeenCalledOnceWith('tx5n59fvxk4z'); - expect(res).toEqual(unflattenedTxnData); + expect(expensesService.getExpenseById).toHaveBeenCalledOnceWith('txvslh8aQMbu'); + expect(transactionService.transformExpense).toHaveBeenCalledOnceWith(platformExpenseData); + expect(res).toEqual(transformedExpenseData); }); }); diff --git a/src/app/fyle/add-edit-per-diem/add-edit-per-diem-3.page.spec.ts b/src/app/fyle/add-edit-per-diem/add-edit-per-diem-3.page.spec.ts index 73ee7aa992..d2d1b9c389 100644 --- a/src/app/fyle/add-edit-per-diem/add-edit-per-diem-3.page.spec.ts +++ b/src/app/fyle/add-edit-per-diem/add-edit-per-diem-3.page.spec.ts @@ -165,7 +165,6 @@ export function TestCases3(getTestBed) { from_dt: [], to_dt: [, component.customDateValidator.bind(component)], custom_inputs: new FormArray([]), - duplicate_detection_reason: [], billable: [], costCenter: [], project_dependent_fields: formBuilder.array([]), @@ -256,7 +255,7 @@ export function TestCases3(getTestBed) { dateService.getUTCDate.and.returnValues( new Date('2023-02-13T17:00:00.000Z'), new Date('2023-08-01T17:00:00.000Z'), - new Date('2023-08-03T17:00:00.000Z'), + new Date('2023-08-03T17:00:00.000Z') ); const expectedEtxn$ = component.generateEtxnFromFg(etxn, customProperties); @@ -280,7 +279,7 @@ export function TestCases3(getTestBed) { dateService.getUTCDate.and.returnValues( new Date('2023-02-13T17:00:00.000Z'), new Date('2023-08-01T17:00:00.000Z'), - new Date('2023-08-03T17:00:00.000Z'), + new Date('2023-08-03T17:00:00.000Z') ); const expectedEtxn$ = component.generateEtxnFromFg(etxn, customProperties); @@ -312,7 +311,7 @@ export function TestCases3(getTestBed) { value: null, }, ], - expenseFieldResponse, + expenseFieldResponse ); expect(res).toEqual(txnCustomPropertiesData5); @@ -338,7 +337,7 @@ export function TestCases3(getTestBed) { modalProperties.getModalDefaultProperties.and.returnValue(properties); const fyCriticalPolicyViolationPopOverSpy: jasmine.SpyObj = jasmine.createSpyObj( 'fyCriticalPolicyViolationPopOver', - ['present', 'onWillDismiss'], + ['present', 'onWillDismiss'] ); fyCriticalPolicyViolationPopOverSpy.onWillDismiss.and.resolveTo({ data: { @@ -384,7 +383,7 @@ export function TestCases3(getTestBed) { const result = component.continueWithPolicyViolations( criticalPolicyViolation2, - splitPolicyExp4.data.final_desired_state, + splitPolicyExp4.data.final_desired_state ); tick(100); @@ -464,7 +463,7 @@ export function TestCases3(getTestBed) { expect(loaderService.showLoader).toHaveBeenCalledTimes(1); expect(component.continueWithPolicyViolations).toHaveBeenCalledOnceWith( criticalPolicyViolation1, - policyViolation1.data.final_desired_state, + policyViolation1.data.final_desired_state ); expect(res).toEqual({ etxn: unflattenedTxnData, comment: 'comment' }); done(); @@ -489,7 +488,7 @@ export function TestCases3(getTestBed) { expect(loaderService.showLoader).toHaveBeenCalledTimes(1); expect(component.continueWithPolicyViolations).toHaveBeenCalledOnceWith( criticalPolicyViolation1, - policyViolation1.data.final_desired_state, + policyViolation1.data.final_desired_state ); expect(res).toEqual({ etxn: unflattenedTxnData, comment: 'No policy violation explanation provided' }); done(); @@ -530,7 +529,7 @@ export function TestCases3(getTestBed) { policyService.getPolicyRules.and.returnValue(['The expense will be flagged']); spyOn(component, 'criticalPolicyViolationErrorHandler').and.returnValue(of({ etxn: unflattenedTxnData })); spyOn(component, 'policyViolationErrorHandler').and.returnValue( - of({ etxn: unflattenedTxnData, comment: 'comment' }), + of({ etxn: unflattenedTxnData, comment: 'comment' }) ); authService.getEou.and.resolveTo(apiEouRes); spyOn(component, 'getFormValues').and.returnValue({ @@ -552,7 +551,7 @@ export function TestCases3(getTestBed) { expect(component.savePerDiemLoader).toBeFalse(); expect(component.saveAndNextPerDiemLoader).toBeFalse(); expect(component.saveAndPrevPerDiemLoader).toBeFalse(); - }), + }) ) .subscribe((res) => { expect(component.savePerDiemLoader).toBeTrue(); @@ -579,7 +578,7 @@ export function TestCases3(getTestBed) { unflattenedTxnData.tx, undefined, [], - 'rp5eUkeNm9wB', + 'rp5eUkeNm9wB' ); expect(res).toEqual(outboxQueueData1[0]); done(); @@ -595,7 +594,7 @@ export function TestCases3(getTestBed) { expect(component.savePerDiemLoader).toBeFalse(); expect(component.saveAndNextPerDiemLoader).toBeFalse(); expect(component.saveAndPrevPerDiemLoader).toBeFalse(); - }), + }) ) .subscribe((res) => { expect(component.savePerDiemLoader).toBeTrue(); @@ -622,7 +621,7 @@ export function TestCases3(getTestBed) { unflattenedTxnData.tx, undefined, ['comment'], - 'rp5eUkeNm9wB', + 'rp5eUkeNm9wB' ); expect(res).toEqual(outboxQueueData1[0]); done(); @@ -641,7 +640,7 @@ export function TestCases3(getTestBed) { expect(component.savePerDiemLoader).toBeFalse(); expect(component.saveAndNextPerDiemLoader).toBeFalse(); expect(component.saveAndPrevPerDiemLoader).toBeFalse(); - }), + }) ) .subscribe((res) => { expect(component.savePerDiemLoader).toBeTrue(); @@ -668,7 +667,7 @@ export function TestCases3(getTestBed) { unflattenedTxnData.tx, undefined, ['comment'], - 'rp5eUkeNm9wB', + 'rp5eUkeNm9wB' ); expect(res).toEqual(outboxQueueData1[0]); done(); @@ -685,7 +684,7 @@ export function TestCases3(getTestBed) { expect(component.savePerDiemLoader).toBeFalse(); expect(component.saveAndNextPerDiemLoader).toBeFalse(); expect(component.saveAndPrevPerDiemLoader).toBeFalse(); - }), + }) ) .subscribe((res) => { expect(component.savePerDiemLoader).toBeTrue(); @@ -712,7 +711,7 @@ export function TestCases3(getTestBed) { unflattenedTxnData.tx, undefined, ['comment'], - 'rp5eUkeNm9wB', + 'rp5eUkeNm9wB' ); expect(res).toEqual(outboxQueueData1[0]); done(); @@ -729,7 +728,7 @@ export function TestCases3(getTestBed) { expect(component.savePerDiemLoader).toBeFalse(); expect(component.saveAndNextPerDiemLoader).toBeFalse(); expect(component.saveAndPrevPerDiemLoader).toBeFalse(); - }), + }) ) .subscribe({ next: (res) => { @@ -752,7 +751,7 @@ export function TestCases3(getTestBed) { unflattenedTxnData.tx, undefined, ['comment'], - 'rp5eUkeNm9wB', + 'rp5eUkeNm9wB' ); expect(res).toEqual(outboxQueueData1[0]); }, @@ -774,7 +773,7 @@ export function TestCases3(getTestBed) { expect(component.savePerDiemLoader).toBeFalse(); expect(component.saveAndNextPerDiemLoader).toBeFalse(); expect(component.saveAndPrevPerDiemLoader).toBeFalse(); - }), + }) ) .subscribe((res) => { expect(component.savePerDiemLoader).toBeTrue(); @@ -797,7 +796,7 @@ export function TestCases3(getTestBed) { unflattenedTxnData.tx, [], [], - 'rp5eUkeNm9wB', + 'rp5eUkeNm9wB' ); expect(res).toEqual(outboxQueueData1[0]); done(); @@ -813,7 +812,7 @@ export function TestCases3(getTestBed) { expect(component.savePerDiemLoader).toBeFalse(); expect(component.saveAndNextPerDiemLoader).toBeFalse(); expect(component.saveAndPrevPerDiemLoader).toBeFalse(); - }), + }) ) .subscribe((res) => { expect(component.savePerDiemLoader).toBeTrue(); @@ -832,7 +831,7 @@ export function TestCases3(getTestBed) { unflattenedTxnData.tx, [], [], - 'rp5eUkeNm9wB', + 'rp5eUkeNm9wB' ); expect(res).toEqual(outboxQueueData1[0]); done(); @@ -854,7 +853,7 @@ export function TestCases3(getTestBed) { expect(component.savePerDiemLoader).toBeFalse(); expect(component.saveAndNextPerDiemLoader).toBeFalse(); expect(component.saveAndPrevPerDiemLoader).toBeFalse(); - }), + }) ) .subscribe((res) => { expect(component.savePerDiemLoader).toBeTrue(); @@ -877,7 +876,7 @@ export function TestCases3(getTestBed) { mockTxnData.tx, [], [], - 'rp5eUkeNm9wB', + 'rp5eUkeNm9wB' ); expect(res).toEqual(outboxQueueData1[0]); done(); diff --git a/src/app/fyle/add-edit-per-diem/add-edit-per-diem-4.page.spec.ts b/src/app/fyle/add-edit-per-diem/add-edit-per-diem-4.page.spec.ts index b7064927ec..44907cff86 100644 --- a/src/app/fyle/add-edit-per-diem/add-edit-per-diem-4.page.spec.ts +++ b/src/app/fyle/add-edit-per-diem/add-edit-per-diem-4.page.spec.ts @@ -25,6 +25,7 @@ import { StorageService } from 'src/app/core/services/storage.service'; import { TokenService } from 'src/app/core/services/token.service'; import { TrackingService } from 'src/app/core/services/tracking.service'; import { TransactionService } from 'src/app/core/services/transaction.service'; +import { ExpensesService } from 'src/app/core/services/platform/v1/spender/expenses.service'; import { TransactionsOutboxService } from 'src/app/core/services/transactions-outbox.service'; import { FormArray, FormBuilder, Validators } from '@angular/forms'; @@ -41,16 +42,17 @@ import { expensePolicyData } from 'src/app/core/mock-data/expense-policy.data'; import { txnCustomProperties4 } from 'src/app/core/mock-data/txn-custom-properties.data'; import { apiEouRes } from 'src/app/core/mock-data/extended-org-user.data'; import { perDiemFormValuesData10 } from 'src/app/core/mock-data/per-diem-form-value.data'; -import { extendedReportParam } from 'src/app/core/mock-data/report-unflattened.data'; -import { txnStatusData } from 'src/app/core/mock-data/transaction-status.data'; -import { createExpenseProperties3, editExpenseProperties } from 'src/app/core/mock-data/track-expense-properties.data'; +import { expectedErptPlatform } from 'src/app/core/mock-data/report-unflattened.data'; +import { expenseStatusData } from 'src/app/core/mock-data/transaction-status.data'; +import { editExpensePropertiesPlatform } from 'src/app/core/mock-data/track-expense-properties.data'; import { cloneDeep } from 'lodash'; -import { UnflattenedTransaction } from 'src/app/core/models/unflattened-transaction.model'; import { ToastMessageComponent } from 'src/app/shared/components/toast-message/toast-message.component'; import { snackbarPropertiesRes2 } from 'src/app/core/mock-data/snackbar-properties.data'; import { ToastType } from 'src/app/core/enums/toast-type.enum'; import { outboxQueueData1 } from 'src/app/core/mock-data/outbox-queue.data'; import { PerDiemRedirectedFrom } from 'src/app/core/models/per-diem-redirected-from.enum'; +import { platformExpenseData } from 'src/app/core/mock-data/platform/v1/expense.data'; +import { transformedExpenseData } from 'src/app/core/mock-data/transformed-expense.data'; export function TestCases4(getTestBed) { return describe('add-edit-per-diem test cases set 4', () => { @@ -67,6 +69,7 @@ export function TestCases4(getTestBed) { let customInputsService: jasmine.SpyObj; let customFieldsService: jasmine.SpyObj; let transactionService: jasmine.SpyObj; + let expensesService: jasmine.SpyObj; let policyService: jasmine.SpyObj; let transactionOutboxService: jasmine.SpyObj; let router: jasmine.SpyObj; @@ -107,6 +110,7 @@ export function TestCases4(getTestBed) { customInputsService = TestBed.inject(CustomInputsService) as jasmine.SpyObj; customFieldsService = TestBed.inject(CustomFieldsService) as jasmine.SpyObj; transactionService = TestBed.inject(TransactionService) as jasmine.SpyObj; + expensesService = TestBed.inject(ExpensesService) as jasmine.SpyObj; policyService = TestBed.inject(PolicyService) as jasmine.SpyObj; transactionOutboxService = TestBed.inject(TransactionsOutboxService) as jasmine.SpyObj; router = TestBed.inject(Router) as jasmine.SpyObj; @@ -147,7 +151,6 @@ export function TestCases4(getTestBed) { from_dt: [], to_dt: [, component.customDateValidator.bind(component)], custom_inputs: new FormArray([]), - duplicate_detection_reason: [], billable: [], costCenter: [], project_dependent_fields: formBuilder.array([]), @@ -223,7 +226,7 @@ export function TestCases4(getTestBed) { expect(loaderService.showLoader).toHaveBeenCalledTimes(1); expect(component.continueWithPolicyViolations).toHaveBeenCalledOnceWith( criticalPolicyViolation1, - policyViolation1.data.final_desired_state, + policyViolation1.data.final_desired_state ); expect(res).toEqual({ etxn: unflattenedTxnData, comment: 'comment' }); done(); @@ -246,7 +249,7 @@ export function TestCases4(getTestBed) { expect(loaderService.showLoader).toHaveBeenCalledTimes(1); expect(component.continueWithPolicyViolations).toHaveBeenCalledOnceWith( criticalPolicyViolation1, - policyViolation1.data.final_desired_state, + policyViolation1.data.final_desired_state ); expect(res).toEqual({ etxn: unflattenedTxnData, comment: 'No policy violation explanation provided' }); done(); @@ -275,7 +278,7 @@ export function TestCases4(getTestBed) { }); describe('editExpense():', () => { - const etxn$ = of({ tx: unflattenedTxnData.tx, ou: unflattenedTxnData.ou, dataUrls: [] }); + const etxn$ = of({ tx: transformedExpenseData.tx, ou: transformedExpenseData.ou, dataUrls: [] }); const customFields$ = of(txnCustomProperties4); beforeEach(() => { spyOn(component, 'trackPolicyCorrections'); @@ -285,23 +288,26 @@ export function TestCases4(getTestBed) { spyOn(component, 'checkPolicyViolation').and.returnValue(of(expensePolicyData)); policyService.getCriticalPolicyRules.and.returnValue(['The expense will be flagged']); policyService.getPolicyRules.and.returnValue(['The expense will be flagged']); - spyOn(component, 'editExpenseCriticalPolicyViolationHandler').and.returnValue(of({ etxn: unflattenedTxnData })); + spyOn(component, 'editExpenseCriticalPolicyViolationHandler').and.returnValue( + of({ etxn: transformedExpenseData }) + ); spyOn(component, 'editExpensePolicyViolationHandler').and.returnValue( - of({ etxn: unflattenedTxnData, comment: 'comment' }), + of({ etxn: transformedExpenseData, comment: 'comment' }) ); authService.getEou.and.resolveTo(apiEouRes); spyOn(component, 'getFormValues').and.returnValue({ ...perDiemFormValuesData10, - report: extendedReportParam[0], + report: expectedErptPlatform[0], }); - transactionService.upsert.and.returnValue(of(unflattenedTxnData.tx)); - transactionService.getETxnUnflattened.and.returnValue(of(unflattenedTxnData)); + transactionService.upsert.and.returnValue(of(transformedExpenseData.tx)); + expensesService.getExpenseById.and.returnValue(of(platformExpenseData)); + transactionService.transformExpense.and.returnValue(transformedExpenseData); reportService.addTransactions.and.returnValue(of(undefined)); reportService.removeTransaction.and.returnValue(of(undefined)); transactionService.review.and.returnValue(of(null)); statusService.findLatestComment.and.returnValue(of('comment1')); - statusService.post.and.returnValue(of(txnStatusData)); - component.etxn$ = of(unflattenedTxnData); + statusService.post.and.returnValue(of(expenseStatusData)); + component.etxn$ = of(transformedExpenseData); spyOn(component, 'getTimeSpentOnPage').and.returnValue(180); component.presetProjectId = 316443; component.presetCostCenterId = 13795; @@ -315,7 +321,7 @@ export function TestCases4(getTestBed) { expect(component.savePerDiemLoader).toBeFalse(); expect(component.saveAndNextPerDiemLoader).toBeFalse(); expect(component.saveAndPrevPerDiemLoader).toBeFalse(); - }), + }) ) .subscribe((res) => { expect(component.savePerDiemLoader).toBeTrue(); @@ -323,8 +329,8 @@ export function TestCases4(getTestBed) { expect(component.saveAndPrevPerDiemLoader).toBeFalse(); expect(component.generateEtxnFromFg).toHaveBeenCalledOnceWith(component.etxn$, customFields$); expect(component.checkPolicyViolation).toHaveBeenCalledOnceWith({ - tx: unflattenedTxnData.tx, - ou: unflattenedTxnData.ou, + tx: transformedExpenseData.tx, + ou: transformedExpenseData.ou, dataUrls: [], }); expect(policyService.getCriticalPolicyRules).toHaveBeenCalledTimes(1); @@ -332,26 +338,26 @@ export function TestCases4(getTestBed) { expect(component.editExpenseCriticalPolicyViolationHandler).toHaveBeenCalledOnceWith({ type: 'criticalPolicyViolations', policyViolations: ['The expense will be flagged'], - etxn: { tx: unflattenedTxnData.tx, ou: unflattenedTxnData.ou, dataUrls: [] }, + etxn: { tx: transformedExpenseData.tx, ou: transformedExpenseData.ou, dataUrls: [] }, }); expect(component.editExpensePolicyViolationHandler).not.toHaveBeenCalled(); expect(trackingService.editExpense).not.toHaveBeenCalled(); expect(trackingService.viewExpense).toHaveBeenCalledOnceWith({ Type: 'Per Diem' }); - expect(transactionService.upsert).toHaveBeenCalledOnceWith(unflattenedTxnData.tx); - expect(transactionService.getETxnUnflattened).toHaveBeenCalledOnceWith(unflattenedTxnData.tx.id); - expect(reportService.addTransactions).toHaveBeenCalledOnceWith('rp5eUkeNm9wB', ['tx3qHxFNgRcZ']); + expect(transactionService.upsert).toHaveBeenCalledOnceWith(transformedExpenseData.tx); + expect(expensesService.getExpenseById).toHaveBeenCalledOnceWith(platformExpenseData.id); + expect(transactionService.transformExpense).toHaveBeenCalledOnceWith(platformExpenseData); + expect(reportService.addTransactions).toHaveBeenCalledOnceWith('rpIfg2VWQKGJ', ['txvslh8aQMbu']); expect(reportService.removeTransaction).not.toHaveBeenCalled(); expect(trackingService.addToExistingReportAddEditExpense).toHaveBeenCalledTimes(1); expect(trackingService.removeFromExistingReportEditExpense).not.toHaveBeenCalled(); - expect(transactionService.review).toHaveBeenCalledOnceWith(unflattenedTxnData.tx.id); - expect(res).toEqual(unflattenedTxnData.tx); + expect(res).toEqual(transformedExpenseData.tx); done(); }); }); it('should throw criticalPolicyViolations error and save the edited expense in new report and remove the expense from previous report if reportId is different', (done) => { - const mockTxnData = cloneDeep(unflattenedTxnData); - mockTxnData.tx.report_id = 'rp8eUleN29dc'; + const mockTxnData = cloneDeep(transformedExpenseData); + mockTxnData.tx.report_id = 'rpbNc3kn5baq'; component.etxn$ = of(mockTxnData); component .editExpense(PerDiemRedirectedFrom.SAVE_PER_DIEM) @@ -360,7 +366,7 @@ export function TestCases4(getTestBed) { expect(component.savePerDiemLoader).toBeFalse(); expect(component.saveAndNextPerDiemLoader).toBeFalse(); expect(component.saveAndPrevPerDiemLoader).toBeFalse(); - }), + }) ) .subscribe((res) => { expect(component.savePerDiemLoader).toBeTrue(); @@ -368,8 +374,8 @@ export function TestCases4(getTestBed) { expect(component.saveAndPrevPerDiemLoader).toBeFalse(); expect(component.generateEtxnFromFg).toHaveBeenCalledOnceWith(component.etxn$, customFields$); expect(component.checkPolicyViolation).toHaveBeenCalledOnceWith({ - tx: unflattenedTxnData.tx, - ou: unflattenedTxnData.ou, + tx: transformedExpenseData.tx, + ou: transformedExpenseData.ou, dataUrls: [], }); expect(policyService.getCriticalPolicyRules).toHaveBeenCalledTimes(1); @@ -377,25 +383,25 @@ export function TestCases4(getTestBed) { expect(component.editExpenseCriticalPolicyViolationHandler).toHaveBeenCalledOnceWith({ type: 'criticalPolicyViolations', policyViolations: ['The expense will be flagged'], - etxn: { tx: unflattenedTxnData.tx, ou: unflattenedTxnData.ou, dataUrls: [] }, + etxn: { tx: transformedExpenseData.tx, ou: transformedExpenseData.ou, dataUrls: [] }, }); expect(component.editExpensePolicyViolationHandler).not.toHaveBeenCalled(); - expect(trackingService.editExpense).toHaveBeenCalledOnceWith(editExpenseProperties); + expect(trackingService.editExpense).toHaveBeenCalledOnceWith(editExpensePropertiesPlatform); expect(trackingService.viewExpense).not.toHaveBeenCalled(); - expect(transactionService.upsert).toHaveBeenCalledOnceWith(unflattenedTxnData.tx); - expect(transactionService.getETxnUnflattened).toHaveBeenCalledOnceWith(unflattenedTxnData.tx.id); - expect(reportService.addTransactions).toHaveBeenCalledOnceWith('rp5eUkeNm9wB', ['tx3qHxFNgRcZ']); - expect(reportService.removeTransaction).toHaveBeenCalledOnceWith('rp8eUleN29dc', 'tx3qHxFNgRcZ'); + expect(transactionService.upsert).toHaveBeenCalledOnceWith(transformedExpenseData.tx); + expect(expensesService.getExpenseById).toHaveBeenCalledOnceWith(platformExpenseData.id); + expect(transactionService.transformExpense).toHaveBeenCalledOnceWith(platformExpenseData); + expect(reportService.addTransactions).toHaveBeenCalledOnceWith('rpIfg2VWQKGJ', ['txvslh8aQMbu']); + expect(reportService.removeTransaction).toHaveBeenCalledOnceWith('rpbNc3kn5baq', 'txvslh8aQMbu'); expect(trackingService.addToExistingReportAddEditExpense).toHaveBeenCalledTimes(1); expect(trackingService.removeFromExistingReportEditExpense).not.toHaveBeenCalled(); - expect(transactionService.review).toHaveBeenCalledOnceWith(unflattenedTxnData.tx.id); - expect(res).toEqual(unflattenedTxnData.tx); + expect(res).toEqual(transformedExpenseData.tx); done(); }); }); it('should throw criticalPolicyViolations error and save the edited expense and remove the expense from previous report if reportId field is empty in form', (done) => { - const mockTxnData = cloneDeep(unflattenedTxnData); + const mockTxnData = cloneDeep(transformedExpenseData); mockTxnData.tx.report_id = 'rp8eUleN29dc'; component.etxn$ = of(mockTxnData); component.getFormValues = jasmine.createSpy().and.returnValue(perDiemFormValuesData10); @@ -406,7 +412,7 @@ export function TestCases4(getTestBed) { expect(component.savePerDiemLoader).toBeFalse(); expect(component.saveAndNextPerDiemLoader).toBeFalse(); expect(component.saveAndPrevPerDiemLoader).toBeFalse(); - }), + }) ) .subscribe((res) => { expect(component.savePerDiemLoader).toBeTrue(); @@ -414,8 +420,8 @@ export function TestCases4(getTestBed) { expect(component.saveAndPrevPerDiemLoader).toBeFalse(); expect(component.generateEtxnFromFg).toHaveBeenCalledOnceWith(component.etxn$, customFields$); expect(component.checkPolicyViolation).toHaveBeenCalledOnceWith({ - tx: unflattenedTxnData.tx, - ou: unflattenedTxnData.ou, + tx: transformedExpenseData.tx, + ou: transformedExpenseData.ou, dataUrls: [], }); expect(policyService.getCriticalPolicyRules).toHaveBeenCalledTimes(1); @@ -423,19 +429,19 @@ export function TestCases4(getTestBed) { expect(component.editExpenseCriticalPolicyViolationHandler).toHaveBeenCalledOnceWith({ type: 'criticalPolicyViolations', policyViolations: ['The expense will be flagged'], - etxn: { tx: unflattenedTxnData.tx, ou: unflattenedTxnData.ou, dataUrls: [] }, + etxn: { tx: transformedExpenseData.tx, ou: transformedExpenseData.ou, dataUrls: [] }, }); expect(component.editExpensePolicyViolationHandler).not.toHaveBeenCalled(); - expect(trackingService.editExpense).toHaveBeenCalledOnceWith(editExpenseProperties); + expect(trackingService.editExpense).toHaveBeenCalledOnceWith(editExpensePropertiesPlatform); expect(trackingService.viewExpense).not.toHaveBeenCalled(); - expect(transactionService.upsert).toHaveBeenCalledOnceWith(unflattenedTxnData.tx); - expect(transactionService.getETxnUnflattened).toHaveBeenCalledOnceWith(unflattenedTxnData.tx.id); + expect(transactionService.upsert).toHaveBeenCalledOnceWith(transformedExpenseData.tx); + expect(expensesService.getExpenseById).toHaveBeenCalledOnceWith(platformExpenseData.id); + expect(transactionService.transformExpense).toHaveBeenCalledOnceWith(platformExpenseData); expect(reportService.addTransactions).not.toHaveBeenCalled(); - expect(reportService.removeTransaction).toHaveBeenCalledOnceWith('rp8eUleN29dc', 'tx3qHxFNgRcZ'); + expect(reportService.removeTransaction).toHaveBeenCalledOnceWith('rp8eUleN29dc', 'txvslh8aQMbu'); expect(trackingService.addToExistingReportAddEditExpense).not.toHaveBeenCalled(); expect(trackingService.removeFromExistingReportEditExpense).toHaveBeenCalledTimes(1); - expect(transactionService.review).toHaveBeenCalledOnceWith(unflattenedTxnData.tx.id); - expect(res).toEqual(unflattenedTxnData.tx); + expect(res).toEqual(transformedExpenseData.tx); done(); }); }); @@ -449,7 +455,7 @@ export function TestCases4(getTestBed) { expect(component.savePerDiemLoader).toBeFalse(); expect(component.saveAndNextPerDiemLoader).toBeFalse(); expect(component.saveAndPrevPerDiemLoader).toBeFalse(); - }), + }) ) .subscribe((res) => { expect(component.savePerDiemLoader).toBeTrue(); @@ -457,8 +463,8 @@ export function TestCases4(getTestBed) { expect(component.saveAndPrevPerDiemLoader).toBeFalse(); expect(component.generateEtxnFromFg).toHaveBeenCalledOnceWith(component.etxn$, customFields$); expect(component.checkPolicyViolation).toHaveBeenCalledOnceWith({ - tx: unflattenedTxnData.tx, - ou: unflattenedTxnData.ou, + tx: transformedExpenseData.tx, + ou: transformedExpenseData.ou, dataUrls: [], }); @@ -469,29 +475,29 @@ export function TestCases4(getTestBed) { type: 'policyViolations', policyViolations: ['The expense will be flagged'], policyAction: expensePolicyData.data.final_desired_state, - etxn: { tx: unflattenedTxnData.tx, ou: unflattenedTxnData.ou, dataUrls: [] }, + etxn: { tx: transformedExpenseData.tx, ou: transformedExpenseData.ou, dataUrls: [] }, }); expect(trackingService.editExpense).not.toHaveBeenCalled(); expect(trackingService.viewExpense).toHaveBeenCalledOnceWith({ Type: 'Per Diem' }); - expect(transactionService.upsert).toHaveBeenCalledOnceWith(unflattenedTxnData.tx); - expect(transactionService.getETxnUnflattened).toHaveBeenCalledOnceWith(unflattenedTxnData.tx.id); - expect(reportService.addTransactions).toHaveBeenCalledOnceWith('rp5eUkeNm9wB', ['tx3qHxFNgRcZ']); + expect(transactionService.upsert).toHaveBeenCalledOnceWith(transformedExpenseData.tx); + expect(expensesService.getExpenseById).toHaveBeenCalledOnceWith(platformExpenseData.id); + expect(transactionService.transformExpense).toHaveBeenCalledOnceWith(platformExpenseData); + expect(reportService.addTransactions).toHaveBeenCalledOnceWith('rpIfg2VWQKGJ', ['txvslh8aQMbu']); expect(reportService.removeTransaction).not.toHaveBeenCalled(); expect(trackingService.addToExistingReportAddEditExpense).toHaveBeenCalledTimes(1); expect(trackingService.removeFromExistingReportEditExpense).not.toHaveBeenCalled(); - expect(transactionService.review).toHaveBeenCalledOnceWith(unflattenedTxnData.tx.id); expect(statusService.findLatestComment).toHaveBeenCalledOnceWith( - unflattenedTxnData.tx.id, + transformedExpenseData.tx.id, 'transactions', - unflattenedTxnData.tx.org_user_id, + transformedExpenseData.tx.org_user_id ); expect(statusService.post).toHaveBeenCalledOnceWith( 'transactions', - unflattenedTxnData.tx.id, + transformedExpenseData.tx.id, { comment: 'comment' }, - true, + true ); - expect(res).toEqual(unflattenedTxnData.tx); + expect(res).toEqual(transformedExpenseData.tx); done(); }); }); @@ -508,7 +514,7 @@ export function TestCases4(getTestBed) { expect(component.savePerDiemLoader).toBeFalse(); expect(component.saveAndNextPerDiemLoader).toBeFalse(); expect(component.saveAndPrevPerDiemLoader).toBeFalse(); - }), + }) ) .subscribe((res) => { expect(component.savePerDiemLoader).toBeTrue(); @@ -516,8 +522,8 @@ export function TestCases4(getTestBed) { expect(component.saveAndPrevPerDiemLoader).toBeFalse(); expect(component.generateEtxnFromFg).toHaveBeenCalledOnceWith(component.etxn$, customFields$); expect(component.checkPolicyViolation).toHaveBeenCalledOnceWith({ - tx: unflattenedTxnData.tx, - ou: unflattenedTxnData.ou, + tx: transformedExpenseData.tx, + ou: transformedExpenseData.ou, dataUrls: [], }); @@ -528,29 +534,29 @@ export function TestCases4(getTestBed) { type: 'policyViolations', policyViolations: ['The expense will be flagged'], policyAction: undefined, - etxn: { tx: unflattenedTxnData.tx, ou: unflattenedTxnData.ou, dataUrls: [] }, + etxn: { tx: transformedExpenseData.tx, ou: transformedExpenseData.ou, dataUrls: [] }, }); expect(trackingService.editExpense).not.toHaveBeenCalled(); expect(trackingService.viewExpense).toHaveBeenCalledOnceWith({ Type: 'Per Diem' }); - expect(transactionService.upsert).toHaveBeenCalledOnceWith(unflattenedTxnData.tx); - expect(transactionService.getETxnUnflattened).toHaveBeenCalledOnceWith(unflattenedTxnData.tx.id); - expect(reportService.addTransactions).toHaveBeenCalledOnceWith('rp5eUkeNm9wB', ['tx3qHxFNgRcZ']); + expect(transactionService.upsert).toHaveBeenCalledOnceWith(transformedExpenseData.tx); + expect(expensesService.getExpenseById).toHaveBeenCalledOnceWith(platformExpenseData.id); + expect(transactionService.transformExpense).toHaveBeenCalledOnceWith(platformExpenseData); + expect(reportService.addTransactions).toHaveBeenCalledOnceWith('rpIfg2VWQKGJ', ['txvslh8aQMbu']); expect(reportService.removeTransaction).not.toHaveBeenCalled(); expect(trackingService.addToExistingReportAddEditExpense).toHaveBeenCalledTimes(1); expect(trackingService.removeFromExistingReportEditExpense).not.toHaveBeenCalled(); - expect(transactionService.review).toHaveBeenCalledOnceWith(unflattenedTxnData.tx.id); expect(statusService.findLatestComment).toHaveBeenCalledOnceWith( - unflattenedTxnData.tx.id, + transformedExpenseData.tx.id, 'transactions', - unflattenedTxnData.tx.org_user_id, + transformedExpenseData.tx.org_user_id ); expect(statusService.post).toHaveBeenCalledOnceWith( 'transactions', - unflattenedTxnData.tx.id, + transformedExpenseData.tx.id, { comment: 'comment' }, - true, + true ); - expect(res).toEqual(unflattenedTxnData.tx); + expect(res).toEqual(transformedExpenseData.tx); done(); }); }); @@ -565,7 +571,7 @@ export function TestCases4(getTestBed) { expect(component.savePerDiemLoader).toBeFalse(); expect(component.saveAndNextPerDiemLoader).toBeFalse(); expect(component.saveAndPrevPerDiemLoader).toBeFalse(); - }), + }) ) .subscribe((res) => { expect(component.savePerDiemLoader).toBeTrue(); @@ -573,8 +579,8 @@ export function TestCases4(getTestBed) { expect(component.saveAndPrevPerDiemLoader).toBeFalse(); expect(component.generateEtxnFromFg).toHaveBeenCalledOnceWith(component.etxn$, customFields$); expect(component.checkPolicyViolation).toHaveBeenCalledOnceWith({ - tx: unflattenedTxnData.tx, - ou: unflattenedTxnData.ou, + tx: transformedExpenseData.tx, + ou: transformedExpenseData.ou, dataUrls: [], }); @@ -585,29 +591,29 @@ export function TestCases4(getTestBed) { type: 'policyViolations', policyViolations: ['The expense will be flagged'], policyAction: undefined, - etxn: { tx: unflattenedTxnData.tx, ou: unflattenedTxnData.ou, dataUrls: [] }, + etxn: { tx: transformedExpenseData.tx, ou: transformedExpenseData.ou, dataUrls: [] }, }); expect(trackingService.editExpense).not.toHaveBeenCalled(); expect(trackingService.viewExpense).toHaveBeenCalledOnceWith({ Type: 'Per Diem' }); - expect(transactionService.upsert).toHaveBeenCalledOnceWith(unflattenedTxnData.tx); - expect(transactionService.getETxnUnflattened).toHaveBeenCalledOnceWith(unflattenedTxnData.tx.id); - expect(reportService.addTransactions).toHaveBeenCalledOnceWith('rp5eUkeNm9wB', ['tx3qHxFNgRcZ']); + expect(transactionService.upsert).toHaveBeenCalledOnceWith(transformedExpenseData.tx); + expect(expensesService.getExpenseById).toHaveBeenCalledOnceWith(platformExpenseData.id); + expect(transactionService.transformExpense).toHaveBeenCalledOnceWith(platformExpenseData); + expect(reportService.addTransactions).toHaveBeenCalledOnceWith('rpIfg2VWQKGJ', ['txvslh8aQMbu']); expect(reportService.removeTransaction).not.toHaveBeenCalled(); expect(trackingService.addToExistingReportAddEditExpense).toHaveBeenCalledTimes(1); expect(trackingService.removeFromExistingReportEditExpense).not.toHaveBeenCalled(); - expect(transactionService.review).toHaveBeenCalledOnceWith(unflattenedTxnData.tx.id); expect(statusService.findLatestComment).toHaveBeenCalledOnceWith( - unflattenedTxnData.tx.id, + transformedExpenseData.tx.id, 'transactions', - unflattenedTxnData.tx.org_user_id, + transformedExpenseData.tx.org_user_id ); expect(statusService.post).toHaveBeenCalledOnceWith( 'transactions', - unflattenedTxnData.tx.id, + transformedExpenseData.tx.id, { comment: 'comment' }, - true, + true ); - expect(res).toEqual(unflattenedTxnData.tx); + expect(res).toEqual(transformedExpenseData.tx); done(); }); }); @@ -622,7 +628,7 @@ export function TestCases4(getTestBed) { expect(component.savePerDiemLoader).toBeFalse(); expect(component.saveAndNextPerDiemLoader).toBeFalse(); expect(component.saveAndPrevPerDiemLoader).toBeFalse(); - }), + }) ) .subscribe((res) => { expect(component.savePerDiemLoader).toBeTrue(); @@ -630,8 +636,8 @@ export function TestCases4(getTestBed) { expect(component.saveAndPrevPerDiemLoader).toBeFalse(); expect(component.generateEtxnFromFg).toHaveBeenCalledOnceWith(component.etxn$, customFields$); expect(component.checkPolicyViolation).toHaveBeenCalledOnceWith({ - tx: unflattenedTxnData.tx, - ou: unflattenedTxnData.ou, + tx: transformedExpenseData.tx, + ou: transformedExpenseData.ou, dataUrls: [], }); expect(policyService.getCriticalPolicyRules).toHaveBeenCalledTimes(1); @@ -641,31 +647,31 @@ export function TestCases4(getTestBed) { type: 'policyViolations', policyViolations: ['The expense will be flagged'], policyAction: expensePolicyData.data.final_desired_state, - etxn: { tx: unflattenedTxnData.tx, ou: unflattenedTxnData.ou, dataUrls: [] }, + etxn: { tx: transformedExpenseData.tx, ou: transformedExpenseData.ou, dataUrls: [] }, }); expect(trackingService.editExpense).not.toHaveBeenCalled(); expect(trackingService.viewExpense).toHaveBeenCalledOnceWith({ Type: 'Per Diem' }); - expect(transactionService.upsert).toHaveBeenCalledOnceWith(unflattenedTxnData.tx); - expect(transactionService.getETxnUnflattened).toHaveBeenCalledOnceWith(unflattenedTxnData.tx.id); - expect(reportService.addTransactions).toHaveBeenCalledOnceWith('rp5eUkeNm9wB', ['tx3qHxFNgRcZ']); + expect(transactionService.upsert).toHaveBeenCalledOnceWith(transformedExpenseData.tx); + expect(expensesService.getExpenseById).toHaveBeenCalledOnceWith(platformExpenseData.id); + expect(transactionService.transformExpense).toHaveBeenCalledOnceWith(platformExpenseData); + expect(reportService.addTransactions).toHaveBeenCalledOnceWith('rpIfg2VWQKGJ', ['txvslh8aQMbu']); expect(reportService.removeTransaction).not.toHaveBeenCalled(); expect(trackingService.addToExistingReportAddEditExpense).toHaveBeenCalledTimes(1); expect(trackingService.removeFromExistingReportEditExpense).not.toHaveBeenCalled(); - expect(transactionService.review).toHaveBeenCalledOnceWith(unflattenedTxnData.tx.id); expect(statusService.findLatestComment).toHaveBeenCalledOnceWith( - unflattenedTxnData.tx.id, + transformedExpenseData.tx.id, 'transactions', - unflattenedTxnData.tx.org_user_id, + transformedExpenseData.tx.org_user_id ); expect(statusService.post).not.toHaveBeenCalled(); - expect(res).toEqual(unflattenedTxnData.tx); + expect(res).toEqual(transformedExpenseData.tx); done(); }); }); it('should throw policyViolations error and save the edited expense and should not call transactionService.review if critical policy is violated', (done) => { policyService.getCriticalPolicyRules.and.returnValue([]); - const mockTxnData = cloneDeep(unflattenedTxnData); + const mockTxnData = cloneDeep(transformedExpenseData); mockTxnData.tx.policy_amount = 0.00009; component.editExpensePolicyViolationHandler = jasmine .createSpy() @@ -677,7 +683,7 @@ export function TestCases4(getTestBed) { expect(component.savePerDiemLoader).toBeFalse(); expect(component.saveAndNextPerDiemLoader).toBeFalse(); expect(component.saveAndPrevPerDiemLoader).toBeFalse(); - }), + }) ) .subscribe((res) => { expect(component.savePerDiemLoader).toBeTrue(); @@ -685,8 +691,8 @@ export function TestCases4(getTestBed) { expect(component.saveAndPrevPerDiemLoader).toBeFalse(); expect(component.generateEtxnFromFg).toHaveBeenCalledOnceWith(component.etxn$, customFields$); expect(component.checkPolicyViolation).toHaveBeenCalledOnceWith({ - tx: unflattenedTxnData.tx, - ou: unflattenedTxnData.ou, + tx: transformedExpenseData.tx, + ou: transformedExpenseData.ou, dataUrls: [], }); expect(policyService.getCriticalPolicyRules).toHaveBeenCalledTimes(1); @@ -696,29 +702,29 @@ export function TestCases4(getTestBed) { type: 'policyViolations', policyViolations: ['The expense will be flagged'], policyAction: expensePolicyData.data.final_desired_state, - etxn: { tx: unflattenedTxnData.tx, ou: unflattenedTxnData.ou, dataUrls: [] }, + etxn: { tx: transformedExpenseData.tx, ou: transformedExpenseData.ou, dataUrls: [] }, }); - expect(trackingService.editExpense).toHaveBeenCalledOnceWith(editExpenseProperties); + expect(trackingService.editExpense).toHaveBeenCalledOnceWith(editExpensePropertiesPlatform); expect(trackingService.viewExpense).not.toHaveBeenCalled(); expect(transactionService.upsert).toHaveBeenCalledOnceWith(mockTxnData.tx); - expect(transactionService.getETxnUnflattened).toHaveBeenCalledOnceWith(unflattenedTxnData.tx.id); + expect(expensesService.getExpenseById).toHaveBeenCalledOnceWith(platformExpenseData.id); + expect(transactionService.transformExpense).toHaveBeenCalledOnceWith(platformExpenseData); expect(reportService.addTransactions).not.toHaveBeenCalled(); expect(reportService.removeTransaction).not.toHaveBeenCalled(); expect(trackingService.addToExistingReportAddEditExpense).not.toHaveBeenCalled(); expect(trackingService.removeFromExistingReportEditExpense).not.toHaveBeenCalled(); - expect(transactionService.review).not.toHaveBeenCalled(); expect(statusService.findLatestComment).toHaveBeenCalledOnceWith( - unflattenedTxnData.tx.id, + transformedExpenseData.tx.id, 'transactions', - unflattenedTxnData.tx.org_user_id, + transformedExpenseData.tx.org_user_id ); expect(statusService.post).toHaveBeenCalledOnceWith( 'transactions', - unflattenedTxnData.tx.id, + transformedExpenseData.tx.id, { comment: 'comment' }, - true, + true ); - expect(res).toEqual(unflattenedTxnData.tx); + expect(res).toEqual(transformedExpenseData.tx); done(); }); }); @@ -733,7 +739,7 @@ export function TestCases4(getTestBed) { expect(component.savePerDiemLoader).toBeFalse(); expect(component.saveAndNextPerDiemLoader).toBeFalse(); expect(component.saveAndPrevPerDiemLoader).toBeFalse(); - }), + }) ) .subscribe((res) => { expect(component.savePerDiemLoader).toBeTrue(); @@ -741,8 +747,8 @@ export function TestCases4(getTestBed) { expect(component.saveAndPrevPerDiemLoader).toBeFalse(); expect(component.generateEtxnFromFg).toHaveBeenCalledOnceWith(component.etxn$, customFields$); expect(component.checkPolicyViolation).toHaveBeenCalledOnceWith({ - tx: unflattenedTxnData.tx, - ou: unflattenedTxnData.ou, + tx: transformedExpenseData.tx, + ou: transformedExpenseData.ou, dataUrls: [], }); expect(policyService.getCriticalPolicyRules).toHaveBeenCalledTimes(1); @@ -751,14 +757,14 @@ export function TestCases4(getTestBed) { expect(component.editExpensePolicyViolationHandler).not.toHaveBeenCalled(); expect(trackingService.editExpense).not.toHaveBeenCalled(); expect(trackingService.viewExpense).toHaveBeenCalledOnceWith({ Type: 'Per Diem' }); - expect(transactionService.upsert).toHaveBeenCalledOnceWith(unflattenedTxnData.tx); - expect(transactionService.getETxnUnflattened).toHaveBeenCalledOnceWith(unflattenedTxnData.tx.id); - expect(reportService.addTransactions).toHaveBeenCalledOnceWith('rp5eUkeNm9wB', ['tx3qHxFNgRcZ']); + expect(transactionService.upsert).toHaveBeenCalledOnceWith(transformedExpenseData.tx); + expect(expensesService.getExpenseById).toHaveBeenCalledOnceWith(platformExpenseData.id); + expect(transactionService.transformExpense).toHaveBeenCalledOnceWith(platformExpenseData); + expect(reportService.addTransactions).toHaveBeenCalledOnceWith('rpIfg2VWQKGJ', ['txvslh8aQMbu']); expect(reportService.removeTransaction).not.toHaveBeenCalled(); expect(trackingService.addToExistingReportAddEditExpense).toHaveBeenCalledTimes(1); expect(trackingService.removeFromExistingReportEditExpense).not.toHaveBeenCalled(); - expect(transactionService.review).toHaveBeenCalledOnceWith(unflattenedTxnData.tx.id); - expect(res).toEqual(unflattenedTxnData.tx); + expect(res).toEqual(transformedExpenseData.tx); done(); }); }); @@ -773,7 +779,7 @@ export function TestCases4(getTestBed) { expect(component.savePerDiemLoader).toBeFalse(); expect(component.saveAndNextPerDiemLoader).toBeFalse(); expect(component.saveAndPrevPerDiemLoader).toBeFalse(); - }), + }) ) .subscribe({ next: (res) => { @@ -782,8 +788,8 @@ export function TestCases4(getTestBed) { expect(component.saveAndPrevPerDiemLoader).toBeFalse(); expect(component.generateEtxnFromFg).toHaveBeenCalledOnceWith(component.etxn$, customFields$); expect(component.checkPolicyViolation).toHaveBeenCalledOnceWith({ - tx: unflattenedTxnData.tx, - ou: unflattenedTxnData.ou, + tx: transformedExpenseData.tx, + ou: transformedExpenseData.ou, dataUrls: [], }); expect(policyService.getCriticalPolicyRules).toHaveBeenCalledTimes(1); @@ -791,19 +797,19 @@ export function TestCases4(getTestBed) { expect(component.editExpenseCriticalPolicyViolationHandler).toHaveBeenCalledOnceWith({ type: 'criticalPolicyViolations', policyViolations: ['The expense will be flagged'], - etxn: { tx: unflattenedTxnData.tx, ou: unflattenedTxnData.ou, dataUrls: [] }, + etxn: { tx: transformedExpenseData.tx, ou: transformedExpenseData.ou, dataUrls: [] }, }); expect(component.editExpensePolicyViolationHandler).not.toHaveBeenCalled(); expect(trackingService.editExpense).not.toHaveBeenCalled(); expect(trackingService.viewExpense).toHaveBeenCalledOnceWith({ Type: 'Per Diem' }); - expect(transactionService.upsert).toHaveBeenCalledOnceWith(unflattenedTxnData.tx); - expect(transactionService.getETxnUnflattened).toHaveBeenCalledOnceWith(unflattenedTxnData.tx.id); - expect(reportService.addTransactions).toHaveBeenCalledOnceWith('rp5eUkeNm9wB', ['tx3qHxFNgRcZ']); + expect(transactionService.upsert).toHaveBeenCalledOnceWith(transformedExpenseData.tx); + expect(expensesService.getExpenseById).toHaveBeenCalledOnceWith(platformExpenseData.id); + expect(transactionService.transformExpense).toHaveBeenCalledOnceWith(platformExpenseData); + expect(reportService.addTransactions).toHaveBeenCalledOnceWith('rpIfg2VWQKGJ', ['txvslh8aQMbu']); expect(reportService.removeTransaction).not.toHaveBeenCalled(); expect(trackingService.addToExistingReportAddEditExpense).toHaveBeenCalledTimes(1); expect(trackingService.removeFromExistingReportEditExpense).not.toHaveBeenCalled(); - expect(transactionService.review).toHaveBeenCalledOnceWith(unflattenedTxnData.tx.id); - expect(res).toEqual(unflattenedTxnData.tx); + expect(res).toEqual(transformedExpenseData.tx); }, error: (err) => { expect(err).toBeTruthy(); diff --git a/src/app/fyle/add-edit-per-diem/add-edit-per-diem-5.page.spec.ts b/src/app/fyle/add-edit-per-diem/add-edit-per-diem-5.page.spec.ts index e769405821..5d7c4174d9 100644 --- a/src/app/fyle/add-edit-per-diem/add-edit-per-diem-5.page.spec.ts +++ b/src/app/fyle/add-edit-per-diem/add-edit-per-diem-5.page.spec.ts @@ -25,6 +25,7 @@ import { StorageService } from 'src/app/core/services/storage.service'; import { TokenService } from 'src/app/core/services/token.service'; import { TrackingService } from 'src/app/core/services/tracking.service'; import { TransactionService } from 'src/app/core/services/transaction.service'; +import { ExpensesService } from 'src/app/core/services/platform/v1/spender/expenses.service'; import { TransactionsOutboxService } from 'src/app/core/services/transactions-outbox.service'; import { FormArray, FormBuilder, FormControl, Validators } from '@angular/forms'; @@ -42,6 +43,8 @@ import { ViewCommentComponent } from 'src/app/shared/components/comments-history import { getElementRef } from 'src/app/core/dom-helpers'; import { individualExpPolicyStateData1 } from 'src/app/core/mock-data/individual-expense-policy-state.data'; import { PerDiemRedirectedFrom } from 'src/app/core/models/per-diem-redirected-from.enum'; +import { platformExpenseData } from 'src/app/core/mock-data/platform/v1/expense.data'; +import { transformedExpenseData } from 'src/app/core/mock-data/transformed-expense.data'; export function TestCases5(getTestBed) { return describe('add-edit-per-diem test cases set 5', () => { @@ -58,6 +61,7 @@ export function TestCases5(getTestBed) { let customInputsService: jasmine.SpyObj; let customFieldsService: jasmine.SpyObj; let transactionService: jasmine.SpyObj; + let expensesService: jasmine.SpyObj; let policyService: jasmine.SpyObj; let transactionOutboxService: jasmine.SpyObj; let router: jasmine.SpyObj; @@ -98,6 +102,7 @@ export function TestCases5(getTestBed) { customInputsService = TestBed.inject(CustomInputsService) as jasmine.SpyObj; customFieldsService = TestBed.inject(CustomFieldsService) as jasmine.SpyObj; transactionService = TestBed.inject(TransactionService) as jasmine.SpyObj; + expensesService = TestBed.inject(ExpensesService) as jasmine.SpyObj; policyService = TestBed.inject(PolicyService) as jasmine.SpyObj; transactionOutboxService = TestBed.inject(TransactionsOutboxService) as jasmine.SpyObj; router = TestBed.inject(Router) as jasmine.SpyObj; @@ -138,7 +143,6 @@ export function TestCases5(getTestBed) { from_dt: [], to_dt: [, component.customDateValidator.bind(component)], custom_inputs: new FormArray([]), - duplicate_detection_reason: [], billable: [], costCenter: [], project_dependent_fields: formBuilder.array([]), @@ -351,7 +355,7 @@ export function TestCases5(getTestBed) { { header: 'Header', body: 'body', ctaText: 'Action', ctaLoadingText: 'Loading' }, true, 'tx5n59fvxk4z', - 'rpFE5X1Pqi9P', + 'rpFE5X1Pqi9P' ) .componentProps.deleteMethod(); expect(reportService.removeTransaction).toHaveBeenCalledOnceWith('rpFE5X1Pqi9P', 'tx5n59fvxk4z'); @@ -364,7 +368,7 @@ export function TestCases5(getTestBed) { .getDeleteReportParams( { header: 'Header', body: 'body', ctaText: 'Action', ctaLoadingText: 'Loading' }, false, - 'tx5n59fvxk4z', + 'tx5n59fvxk4z' ) .componentProps.deleteMethod(); expect(transactionService.delete).toHaveBeenCalledOnceWith('tx5n59fvxk4z'); @@ -405,15 +409,15 @@ export function TestCases5(getTestBed) { { header, body, ctaText, ctaLoadingText }, true, 'tx5n59fvxk4z', - 'rpFE5X1Pqi9P', + 'rpFE5X1Pqi9P' ); expect(popoverController.create).toHaveBeenCalledOnceWith( component.getDeleteReportParams( { header, body, ctaText, ctaLoadingText }, true, 'tx5n59fvxk4z', - 'rpFE5X1Pqi9P', - ), + 'rpFE5X1Pqi9P' + ) ); })); @@ -443,23 +447,24 @@ export function TestCases5(getTestBed) { { header, body, ctaText, ctaLoadingText }, undefined, 'tx5n59fvxk4z', - undefined, + undefined ); expect(popoverController.create).toHaveBeenCalledOnceWith( component.getDeleteReportParams( { header, body, ctaText, ctaLoadingText }, undefined, 'tx5n59fvxk4z', - undefined, - ), + undefined + ) ); })); it('should go to next expense if delete is successful and expense is not the last one in list', fakeAsync(() => { spyOn(component, 'getDeleteReportParams'); spyOn(component, 'goToTransaction'); - transactionService.getETxnUnflattened.and.returnValue(of(unflattenedTxnData)); - component.reviewList = ['txfCdl3TEZ7K', 'txCYDX0peUw5']; + expensesService.getExpenseById.and.returnValue(of(platformExpenseData)); + transactionService.transformExpense.and.returnValue(transformedExpenseData); + component.reviewList = ['txvslh8aQMbu', 'txCYDX0peUw5']; component.activeIndex = 0; const deletePopoverSpy = jasmine.createSpyObj('deletePopover', ['present', 'onDidDismiss']); @@ -485,23 +490,22 @@ export function TestCases5(getTestBed) { { header, body, ctaText, ctaLoadingText }, undefined, 'tx5n59fvxk4z', - undefined, + undefined ); expect(popoverController.create).toHaveBeenCalledOnceWith( component.getDeleteReportParams( { header, body, ctaText, ctaLoadingText }, undefined, 'tx5n59fvxk4z', - undefined, - ), - ); - expect(transactionService.getETxnUnflattened).toHaveBeenCalledOnceWith( - component.reviewList[+component.activeIndex], + undefined + ) ); + expect(expensesService.getExpenseById).toHaveBeenCalledOnceWith(component.reviewList[+component.activeIndex]); + expect(transactionService.transformExpense).toHaveBeenCalledOnceWith(platformExpenseData); expect(component.goToTransaction).toHaveBeenCalledOnceWith( - unflattenedTxnData, + transformedExpenseData, component.reviewList, - +component.activeIndex, + +component.activeIndex ); })); @@ -531,15 +535,15 @@ export function TestCases5(getTestBed) { { header, body, ctaText, ctaLoadingText }, true, 'tx5n59fvxk4z', - 'rpFE5X1Pqi9P', + 'rpFE5X1Pqi9P' ); expect(popoverController.create).toHaveBeenCalledOnceWith( component.getDeleteReportParams( { header, body, ctaText, ctaLoadingText }, true, 'tx5n59fvxk4z', - 'rpFE5X1Pqi9P', - ), + 'rpFE5X1Pqi9P' + ) ); expect(trackingService.clickDeleteExpense).toHaveBeenCalledOnceWith({ Type: 'Per Diem' }); })); diff --git a/src/app/fyle/add-edit-per-diem/add-edit-per-diem.page.setup.spec.ts b/src/app/fyle/add-edit-per-diem/add-edit-per-diem.page.setup.spec.ts index 9d8cbf52ab..e68fa1426b 100644 --- a/src/app/fyle/add-edit-per-diem/add-edit-per-diem.page.setup.spec.ts +++ b/src/app/fyle/add-edit-per-diem/add-edit-per-diem.page.setup.spec.ts @@ -17,6 +17,7 @@ import { ReportService } from 'src/app/core/services/report.service'; import { ProjectsService } from 'src/app/core/services/projects.service'; import { TransactionsOutboxService } from 'src/app/core/services/transactions-outbox.service'; import { TransactionService } from 'src/app/core/services/transaction.service'; +import { ExpensesService } from 'src/app/core/services/platform/v1/spender/expenses.service'; import { AuthService } from 'src/app/core/services/auth.service'; import { PolicyService } from 'src/app/core/services/policy.service'; import { LoaderService } from 'src/app/core/services/loader.service'; @@ -70,12 +71,13 @@ describe('AddEditPerDiemPage', () => { ]); const transactionsOutboxServiceSpy = jasmine.createSpyObj('TransactionsOutboxService', ['addEntryAndSync']); const transactionServiceSpy = jasmine.createSpyObj('TransactionService', [ - 'getETxnUnflattened', + 'transformExpense', 'checkPolicy', 'upsert', 'review', 'delete', ]); + const expensesServiceSpy = jasmine.createSpyObj('ExpensesService', ['getExpenseById']); const authServiceSpy = jasmine.createSpyObj('AuthService', ['getEou']); const policyServiceSpy = jasmine.createSpyObj('PolicyService', [ 'transformTo', @@ -280,6 +282,10 @@ describe('AddEditPerDiemPage', () => { provide: Router, useValue: routerSpy, }, + { + provide: ExpensesService, + useValue: expensesServiceSpy, + }, ], schemas: [NO_ERRORS_SCHEMA], }).compileComponents(); diff --git a/src/app/fyle/add-edit-per-diem/add-edit-per-diem.page.ts b/src/app/fyle/add-edit-per-diem/add-edit-per-diem.page.ts index 4b6ea8d33e..98bf6d72ac 100644 --- a/src/app/fyle/add-edit-per-diem/add-edit-per-diem.page.ts +++ b/src/app/fyle/add-edit-per-diem/add-edit-per-diem.page.ts @@ -107,6 +107,7 @@ import { TransactionState } from 'src/app/core/models/transaction-state.enum'; import { ToastType } from 'src/app/core/enums/toast-type.enum'; import { Expense } from 'src/app/core/models/expense.model'; import { PerDiemRedirectedFrom } from 'src/app/core/models/per-diem-redirected-from.enum'; +import { ExpensesService } from 'src/app/core/services/platform/v1/spender/expenses.service'; @Component({ selector: 'app-add-edit-per-diem', @@ -152,7 +153,7 @@ export class AddEditPerDiemPage implements OnInit { isAmountDisabled = false; - etxn$: Observable; + etxn$: Observable>; isIndividualProjectsEnabled$: Observable; @@ -281,7 +282,8 @@ export class AddEditPerDiemPage implements OnInit { private orgUserSettingsService: OrgUserSettingsService, private orgSettingsService: OrgSettingsService, private platform: Platform, - private storageService: StorageService + private storageService: StorageService, + private expensesService: ExpensesService ) {} get minPerDiemDate(): string { @@ -385,7 +387,8 @@ export class AddEditPerDiemPage implements OnInit { this.activeIndex = this.activatedRoute.snapshot.params.activeIndex as number; if (this.reviewList[+this.activeIndex - 1]) { - this.transactionService.getETxnUnflattened(this.reviewList[+this.activeIndex - 1]).subscribe((etxn) => { + this.expensesService.getExpenseById(this.reviewList[+this.activeIndex - 1]).subscribe((expense) => { + const etxn = this.transactionService.transformExpense(expense); this.goToTransaction(etxn, this.reviewList, +this.activeIndex - 1); }); } @@ -395,13 +398,14 @@ export class AddEditPerDiemPage implements OnInit { this.activeIndex = this.activatedRoute.snapshot.params.activeIndex as number; if (this.reviewList[+this.activeIndex + 1]) { - this.transactionService.getETxnUnflattened(this.reviewList[+this.activeIndex + 1]).subscribe((etxn) => { + this.expensesService.getExpenseById(this.reviewList[+this.activeIndex + 1]).subscribe((expense) => { + const etxn = this.transactionService.transformExpense(expense); this.goToTransaction(etxn, this.reviewList, +this.activeIndex + 1); }); } } - goToTransaction(expense: UnflattenedTransaction, reviewList: string[], activeIndex: number): void { + goToTransaction(expense: Partial, reviewList: string[], activeIndex: number): void { let category: string; if (expense.tx.org_category) { @@ -661,10 +665,13 @@ export class AddEditPerDiemPage implements OnInit { ); } - getEditExpense(): Observable { - return this.transactionService - .getETxnUnflattened(this.activatedRoute.snapshot.params.id as string) - .pipe(shareReplay(1)); + getEditExpense(): Observable> { + const expenseId = this.activatedRoute.snapshot.params.id as string; + + return this.expensesService.getExpenseById(expenseId).pipe( + switchMap((expense) => of(this.transactionService.transformExpense(expense))), + shareReplay(1) + ); } setupFilteredCategories(activeCategories$: Observable): void { @@ -865,7 +872,6 @@ export class AddEditPerDiemPage implements OnInit { from_dt: [], to_dt: [, this.customDateValidator.bind(this)], custom_inputs: new FormArray([]), - duplicate_detection_reason: [], billable: [], costCenter: [], project_dependent_fields: this.fb.array([]), @@ -1021,11 +1027,7 @@ export class AddEditPerDiemPage implements OnInit { this.individualProjectIds$ = orgUserSettings$.pipe(map((orgUserSettings) => orgUserSettings.project_ids || [])); - this.etxn$ = iif( - () => this.mode === 'add', - this.getNewExpense(), - this.getEditExpense() - ) as Observable; + this.etxn$ = iif(() => this.mode === 'add', this.getNewExpense(), this.getEditExpense()); this.isProjectsEnabled$ = orgSettings$.pipe( map((orgSettings) => orgSettings.projects && orgSettings.projects.enabled) @@ -1555,7 +1557,6 @@ export class AddEditPerDiemPage implements OnInit { from_dt: etxn.tx.from_dt ? dayjs(new Date(etxn.tx.from_dt)).format('YYYY-MM-DD') : null, to_dt: etxn.tx.to_dt ? dayjs(new Date(etxn.tx.to_dt)).format('YYYY-MM-DD') : null, billable: etxn.tx.billable, - duplicate_detection_reason: etxn.tx.user_reason_for_duplicate_expenses, costCenter, }); @@ -1573,7 +1574,7 @@ export class AddEditPerDiemPage implements OnInit { } generateEtxnFromFg( - etxn$: Observable, + etxn$: Observable>, standardisedCustomProperties$: Observable ): Observable<{ tx: Partial; dataUrls: FileObject[]; ou: Partial }> { return forkJoin({ @@ -1626,7 +1627,6 @@ export class AddEditPerDiemPage implements OnInit { cost_center_id: formValue.costCenter && formValue.costCenter.id, cost_center_name: formValue.costCenter && formValue.costCenter.name, cost_center_code: formValue.costCenter && formValue.costCenter.code, - user_reason_for_duplicate_expenses: formValue.duplicate_detection_reason, }, dataUrls: [], ou: etxn.ou, @@ -1837,7 +1837,7 @@ export class AddEditPerDiemPage implements OnInit { } } ), - switchMap(({ etxn, comment }: { etxn: UnflattenedTransaction; comment: string }) => + switchMap(({ etxn, comment }: { etxn: Partial; comment: string }) => from(this.authService.getEou()).pipe( switchMap(() => { const comments: string[] = []; @@ -1991,7 +1991,7 @@ export class AddEditPerDiemPage implements OnInit { (err: { status: number; policyViolations: string[]; - etxn: UnflattenedTransaction; + etxn: Partial; type: string; policyAction: FinalExpensePolicyState; }) => { @@ -2007,7 +2007,7 @@ export class AddEditPerDiemPage implements OnInit { } } ), - switchMap(({ etxn, comment }: { etxn: UnflattenedTransaction; comment: string }) => + switchMap(({ etxn, comment }: { etxn: Partial; comment: string }) => this.etxn$.pipe( switchMap((txnCopy) => { if (!isEqual(etxn.tx, txnCopy.tx)) { @@ -2031,8 +2031,8 @@ export class AddEditPerDiemPage implements OnInit { } return this.transactionService.upsert(etxn.tx).pipe( - switchMap((txn) => this.transactionService.getETxnUnflattened(txn.id)), - map((savedEtxn) => savedEtxn && savedEtxn.tx), + switchMap((txn) => this.expensesService.getExpenseById(txn.id)), + map((expense) => this.transactionService.transformExpense(expense).tx), switchMap((tx) => { const formValue = this.getFormValues(); const selectedReportId = formValue.report && formValue.report.rp && formValue.report.rp.id; @@ -2060,15 +2060,6 @@ export class AddEditPerDiemPage implements OnInit { ); } } - - return of(null).pipe(map(() => tx)); - }), - switchMap((tx) => { - const criticalPolicyViolated = isNumber(etxn.tx.policy_amount) && etxn.tx.policy_amount < 0.0001; - if (!criticalPolicyViolated && etxn.tx.user_review_needed) { - return this.transactionService.review(tx.id).pipe(map(() => tx)); - } - return of(null).pipe(map(() => tx)); }) ); @@ -2311,7 +2302,8 @@ export class AddEditPerDiemPage implements OnInit { if (data && data.status === 'success') { if (this.reviewList && this.reviewList.length && +this.activeIndex < this.reviewList.length - 1) { this.reviewList.splice(+this.activeIndex, 1); - this.transactionService.getETxnUnflattened(this.reviewList[+this.activeIndex]).subscribe((etxn) => { + this.expensesService.getExpenseById(this.reviewList[+this.activeIndex]).subscribe((expense) => { + const etxn = this.transactionService.transformExpense(expense); this.goToTransaction(etxn, this.reviewList, +this.activeIndex); }); } else if (removePerDiemFromReport) { diff --git a/src/app/fyle/dashboard/tasks/tasks-2.component.spec.ts b/src/app/fyle/dashboard/tasks/tasks-2.component.spec.ts index 9a05c650bd..062d629b5b 100644 --- a/src/app/fyle/dashboard/tasks/tasks-2.component.spec.ts +++ b/src/app/fyle/dashboard/tasks/tasks-2.component.spec.ts @@ -4,6 +4,7 @@ import { ModalController } from '@ionic/angular'; import { TasksComponent } from './tasks.component'; import { TasksService } from 'src/app/core/services/tasks.service'; import { TransactionService } from 'src/app/core/services/transaction.service'; +import { ExpensesService } from 'src/app/core/services/platform/v1/spender/expenses.service'; import { ReportService } from 'src/app/core/services/report.service'; import { AdvanceRequestService } from 'src/app/core/services/advance-request.service'; import { TrackingService } from 'src/app/core/services/tracking.service'; @@ -28,13 +29,19 @@ import { import { taskCtaData3, taskCtaData9 } from 'src/app/core/mock-data/task-cta.data'; import { expenseList } from 'src/app/core/mock-data/expense.data'; import { cloneDeep } from 'lodash'; -import { - mileageCategoryUnflattenedExpense, - perDiemCategoryUnflattenedExpense, - unflattenedTxnData, -} from 'src/app/core/mock-data/unflattened-txn.data'; import { apiReportRes } from 'src/app/core/mock-data/api-reports.data'; import { singleExtendedAdvReqRes } from 'src/app/core/mock-data/extended-advance-request.data'; +import { + expensesList, + mileageCategoryPlatformExpenseData, + perDiemCategoryPlatformExpenseData, + platformExpenseData, +} from 'src/app/core/mock-data/platform/v1/expense.data'; +import { + mileageCategoryTransformedExpenseData, + perDiemCategoryTransformedExpenseData, + transformedExpenseData, +} from 'src/app/core/mock-data/transformed-expense.data'; export function TestCases2(getTestBed) { return describe('test case set 2', () => { @@ -42,6 +49,7 @@ export function TestCases2(getTestBed) { let fixture: ComponentFixture; let tasksService: jasmine.SpyObj; let transactionService: jasmine.SpyObj; + let expensesService: jasmine.SpyObj; let reportService: jasmine.SpyObj; let advanceRequestService: jasmine.SpyObj; let modalController: jasmine.SpyObj; @@ -61,6 +69,7 @@ export function TestCases2(getTestBed) { component = fixture.componentInstance; tasksService = TestBed.inject(TasksService) as jasmine.SpyObj; transactionService = TestBed.inject(TransactionService) as jasmine.SpyObj; + expensesService = TestBed.inject(ExpensesService) as jasmine.SpyObj; reportService = TestBed.inject(ReportService) as jasmine.SpyObj; advanceRequestService = TestBed.inject(AdvanceRequestService) as jasmine.SpyObj; modalController = TestBed.inject(ModalController) as jasmine.SpyObj; @@ -186,70 +195,78 @@ export function TestCases2(getTestBed) { beforeEach(() => { loaderService.showLoader.and.resolveTo(); loaderService.hideLoader.and.resolveTo(); - transactionService.getAllExpenses.and.returnValue(of(cloneDeep(expenseList))); - transactionService.getETxnUnflattened.and.returnValue(of(unflattenedTxnData)); + expensesService.getAllExpenses.and.returnValue(of(cloneDeep(expensesList))); + expensesService.getExpenseById.and.returnValue(of(platformExpenseData)); + transactionService.transformExpense.and.returnValue(transformedExpenseData); }); it('should get all expenses and navigate to add_edit_mileage if category is of type mileage', fakeAsync(() => { - transactionService.getETxnUnflattened.and.returnValue(of(mileageCategoryUnflattenedExpense)); + expensesService.getExpenseById.and.returnValue(of(mileageCategoryPlatformExpenseData)); + transactionService.transformExpense.and.returnValue(mileageCategoryTransformedExpenseData); + component.onReviewExpensesTaskClick(); tick(100); expect(loaderService.showLoader).toHaveBeenCalledOnceWith('please wait while we load your expenses', 3000); - expect(transactionService.getAllExpenses).toHaveBeenCalledOnceWith({ + expect(expensesService.getAllExpenses).toHaveBeenCalledOnceWith({ queryParams: { - tx_state: 'in.(DRAFT)', - tx_report_id: 'is.null', + state: 'in.(DRAFT)', + report_id: 'is.null', }, }); - expect(transactionService.getETxnUnflattened).toHaveBeenCalledOnceWith(expenseList[0].tx_id); + expect(expensesService.getExpenseById).toHaveBeenCalledOnceWith(expensesList[0].id); + expect(transactionService.transformExpense).toHaveBeenCalledOnceWith(mileageCategoryPlatformExpenseData); expect(loaderService.hideLoader).toHaveBeenCalledTimes(1); expect(router.navigate).toHaveBeenCalledOnceWith([ '/', 'enterprise', 'add_edit_mileage', - { id: mileageCategoryUnflattenedExpense.tx.id, txnIds: '["txBphgnCHHeO"]', activeIndex: 0 }, + { id: mileageCategoryTransformedExpenseData.tx.id, txnIds: '["txvslh8aQMbu"]', activeIndex: 0 }, ]); })); it('should get all expenses and navigate to add_edit_per_diem if category is of type per diem', fakeAsync(() => { - transactionService.getETxnUnflattened.and.returnValue(of(perDiemCategoryUnflattenedExpense)); + expensesService.getExpenseById.and.returnValue(of(perDiemCategoryPlatformExpenseData)); + transactionService.transformExpense.and.returnValue(perDiemCategoryTransformedExpenseData); component.onReviewExpensesTaskClick(); tick(100); expect(loaderService.showLoader).toHaveBeenCalledOnceWith('please wait while we load your expenses', 3000); - expect(transactionService.getAllExpenses).toHaveBeenCalledOnceWith({ + expect(expensesService.getAllExpenses).toHaveBeenCalledOnceWith({ queryParams: { - tx_state: 'in.(DRAFT)', - tx_report_id: 'is.null', + state: 'in.(DRAFT)', + report_id: 'is.null', }, }); - expect(transactionService.getETxnUnflattened).toHaveBeenCalledOnceWith(expenseList[0].tx_id); + expect(expensesService.getExpenseById).toHaveBeenCalledOnceWith(expensesList[0].id); + expect(transactionService.transformExpense).toHaveBeenCalledOnceWith(perDiemCategoryPlatformExpenseData); expect(loaderService.hideLoader).toHaveBeenCalledTimes(1); expect(router.navigate).toHaveBeenCalledOnceWith([ '/', 'enterprise', 'add_edit_per_diem', - { id: mileageCategoryUnflattenedExpense.tx.id, txnIds: '["txBphgnCHHeO"]', activeIndex: 0 }, + { id: perDiemCategoryTransformedExpenseData.tx.id, txnIds: '["txvslh8aQMbu"]', activeIndex: 0 }, ]); })); it('should get all expenses and navigate to add_edit_expense if category is other than mileage or per diem', fakeAsync(() => { - transactionService.getETxnUnflattened.and.returnValue(of(unflattenedTxnData)); + expensesService.getExpenseById.and.returnValue(of(platformExpenseData)); + transactionService.transformExpense.and.returnValue(transformedExpenseData); component.onReviewExpensesTaskClick(); tick(100); expect(loaderService.showLoader).toHaveBeenCalledOnceWith('please wait while we load your expenses', 3000); - expect(transactionService.getAllExpenses).toHaveBeenCalledOnceWith({ + expect(expensesService.getAllExpenses).toHaveBeenCalledOnceWith({ queryParams: { - tx_state: 'in.(DRAFT)', - tx_report_id: 'is.null', + state: 'in.(DRAFT)', + report_id: 'is.null', }, }); - expect(transactionService.getETxnUnflattened).toHaveBeenCalledOnceWith(expenseList[0].tx_id); + expect(expensesService.getExpenseById).toHaveBeenCalledOnceWith(expensesList[0].id); + expect(transactionService.transformExpense).toHaveBeenCalledOnceWith(platformExpenseData); expect(loaderService.hideLoader).toHaveBeenCalledTimes(1); expect(router.navigate).toHaveBeenCalledOnceWith([ '/', 'enterprise', - 'add_edit_expense', - { id: mileageCategoryUnflattenedExpense.tx.id, txnIds: '["txBphgnCHHeO"]', activeIndex: 0 }, + 'add_edit_mileage', + { id: mileageCategoryTransformedExpenseData.tx.id, txnIds: '["txvslh8aQMbu"]', activeIndex: 0 }, ]); })); }); diff --git a/src/app/fyle/dashboard/tasks/tasks-3.component.spec.ts b/src/app/fyle/dashboard/tasks/tasks-3.component.spec.ts index c8fdf6b144..1f0884265b 100644 --- a/src/app/fyle/dashboard/tasks/tasks-3.component.spec.ts +++ b/src/app/fyle/dashboard/tasks/tasks-3.component.spec.ts @@ -49,6 +49,7 @@ export function TestCases3(getTestBed) { component = fixture.componentInstance; tasksService = TestBed.inject(TasksService) as jasmine.SpyObj; transactionService = TestBed.inject(TransactionService) as jasmine.SpyObj; + expensesService = TestBed.inject(ExpensesService) as jasmine.SpyObj; reportService = TestBed.inject(ReportService) as jasmine.SpyObj; advanceRequestService = TestBed.inject(AdvanceRequestService) as jasmine.SpyObj; modalController = TestBed.inject(ModalController) as jasmine.SpyObj; diff --git a/src/app/fyle/dashboard/tasks/tasks.component.setup.spec.ts b/src/app/fyle/dashboard/tasks/tasks.component.setup.spec.ts index bd2a5d4f3f..41f83ffa96 100644 --- a/src/app/fyle/dashboard/tasks/tasks.component.setup.spec.ts +++ b/src/app/fyle/dashboard/tasks/tasks.component.setup.spec.ts @@ -31,10 +31,11 @@ describe('TasksComponent', () => { ]); const transactionServiceSpy = jasmine.createSpyObj('TransactionService', [ 'clearCache', + 'transformExpense', + 'transformRawExpense', 'getAllExpenses', - 'getETxnUnflattened', ]); - const expensesServiceSpy = jasmine.createSpyObj('ExpensesService', ['getAllExpenses']); + const expensesServiceSpy = jasmine.createSpyObj('ExpensesService', ['getExpenseById', 'getAllExpenses']); const reportServiceSpy = jasmine.createSpyObj('ReportService', [ 'getReportAutoSubmissionDetails', 'clearCache', @@ -89,6 +90,7 @@ describe('TasksComponent', () => { { provide: Router, useValue: routerSpy }, { provide: ActivatedRoute, useValue: activatedRouteSpy }, { provide: NetworkService, useValue: networkServiceSpy }, + { provide: ExpensesService, useValue: expensesServiceSpy }, ], schemas: [NO_ERRORS_SCHEMA], }).compileComponents(); diff --git a/src/app/fyle/dashboard/tasks/tasks.component.ts b/src/app/fyle/dashboard/tasks/tasks.component.ts index 75739ae453..10c8656f9a 100644 --- a/src/app/fyle/dashboard/tasks/tasks.component.ts +++ b/src/app/fyle/dashboard/tasks/tasks.component.ts @@ -389,24 +389,24 @@ export class TasksComponent implements OnInit { onReviewExpensesTaskClick(): void { const queryParams = { - tx_state: 'in.(DRAFT)', - tx_report_id: 'is.null', + state: 'in.(DRAFT)', + report_id: 'is.null', }; from(this.loaderService.showLoader('please wait while we load your expenses', 3000)) .pipe( switchMap(() => - this.transactionService.getAllExpenses({ + this.expensesService.getAllExpenses({ queryParams, }) ), - map((etxns) => etxns.map((etxn) => etxn.tx_id)), + map((expenses) => expenses.map((expense) => expense.id)), switchMap((selectedIds) => { const initial = selectedIds[0]; const allIds = selectedIds; - return this.transactionService.getETxnUnflattened(initial).pipe( - map((etxn) => ({ - inital: etxn, + return this.expensesService.getExpenseById(initial).pipe( + map((expense) => ({ + inital: this.transactionService.transformExpense(expense), allIds, })) ); diff --git a/src/app/fyle/merge-expense/merge-expense-1.page.spec.ts b/src/app/fyle/merge-expense/merge-expense-1.page.spec.ts index 101cf598f6..9fc1cf76d9 100644 --- a/src/app/fyle/merge-expense/merge-expense-1.page.spec.ts +++ b/src/app/fyle/merge-expense/merge-expense-1.page.spec.ts @@ -1,4 +1,4 @@ -import { ComponentFixture, fakeAsync, tick, waitForAsync } from '@angular/core/testing'; +import { ComponentFixture, waitForAsync } from '@angular/core/testing'; import { MergeExpensePage } from './merge-expense.page'; import { ActivatedRoute, Router } from '@angular/router'; import { CategoriesService } from 'src/app/core/services/categories.service'; @@ -9,11 +9,9 @@ import { MatSnackBar } from '@angular/material/snack-bar'; import { SnackbarPropertiesService } from 'src/app/core/services/snackbar-properties.service'; import { MergeExpensesService } from 'src/app/core/services/merge-expenses.service'; import { TrackingService } from 'src/app/core/services/tracking.service'; -import { ExpenseFieldsService } from 'src/app/core/services/expense-fields.service'; -import { DependentFieldsService } from 'src/app/core/services/dependent-fields.service'; -import { AbstractControl, FormBuilder, FormControl, Validators } from '@angular/forms'; +import { FormBuilder, FormControl, Validators } from '@angular/forms'; import { cloneDeep } from 'lodash'; -import { expenseData1, expenseList2 } from 'src/app/core/mock-data/expense.data'; +import { expenseList2, transformedPlatformedExpense1 } from 'src/app/core/mock-data/expense.data'; import { mergeExpensesOptionsData } from 'src/app/core/mock-data/merge-expenses-option.data'; import { of } from 'rxjs'; import { @@ -39,6 +37,8 @@ import { import { combinedOptionsData1 } from 'src/app/core/mock-data/combined-options.data'; import { expensesInfo } from 'src/app/core/mock-data/expenses-info.data'; import { TransactionService } from 'src/app/core/services/transaction.service'; +import { ExpensesService } from 'src/app/core/services/platform/v1/spender/expenses.service'; +import { apiExpenses3 } from 'src/app/core/mock-data/platform/v1/expense.data'; export function TestCases1(getTestBed) { return describe('test cases set 1', () => { @@ -54,10 +54,9 @@ export function TestCases1(getTestBed) { let snackbarProperties: jasmine.SpyObj; let mergeExpensesService: jasmine.SpyObj; let trackingService: jasmine.SpyObj; - let expenseFieldsService: jasmine.SpyObj; - let dependantFieldsService: jasmine.SpyObj; let formBuilder: FormBuilder; let transactionService: jasmine.SpyObj; + let expensesService: jasmine.SpyObj; beforeEach(waitForAsync(() => { const TestBed = getTestBed(); @@ -73,9 +72,8 @@ export function TestCases1(getTestBed) { snackbarProperties = TestBed.inject(SnackbarPropertiesService) as jasmine.SpyObj; mergeExpensesService = TestBed.inject(MergeExpensesService) as jasmine.SpyObj; trackingService = TestBed.inject(TrackingService) as jasmine.SpyObj; - expenseFieldsService = TestBed.inject(ExpenseFieldsService) as jasmine.SpyObj; - dependantFieldsService = TestBed.inject(DependentFieldsService) as jasmine.SpyObj; transactionService = TestBed.inject(TransactionService); + expensesService = TestBed.inject(ExpensesService); formBuilder = TestBed.inject(FormBuilder); component.fg = formBuilder.group({ target_txn_id: [, Validators.required], @@ -120,7 +118,9 @@ export function TestCases1(getTestBed) { describe('ionViewWillEnter():', () => { beforeEach(() => { - transactionService.getETxnc.and.returnValue(of(expenseList2)); + expensesService.getAllExpenses.and.returnValue(of(apiExpenses3)); + transactionService.transformRawExpense.and.returnValue(transformedPlatformedExpense1[0]); + transactionService.transformRawExpense.and.returnValue(transformedPlatformedExpense1[1]); categoriesService.getSystemCategories.and.returnValue(['Bus', 'Airlines', 'Lodging', 'Train']); mergeExpensesService.generateExpenseToKeepOptions.and.returnValue(of(mergeExpensesOptionsData)); mergeExpensesService.generateReceiptOptions.and.returnValue(of(mergeExpensesOptionsData)); @@ -152,7 +152,7 @@ export function TestCases1(getTestBed) { }); it('should setup class observables', () => { - component.expenses = expenseList2; + component.expenses = transformedPlatformedExpense1; component.ionViewWillEnter(); expect(component.fg.controls.target_txn_id.value).toEqual(null); @@ -161,75 +161,87 @@ export function TestCases1(getTestBed) { expect(component.fg.controls.categoryDependent.value).toEqual(null); expect(component.fg.controls.custom_inputs.value).toEqual(null); - expect(transactionService.getETxnc).toHaveBeenCalledOnceWith({ + expect(expensesService.getAllExpenses).toHaveBeenCalledOnceWith({ offset: 0, limit: 200, - params: { tx_id: 'in.(txBphgnCHHeO,tx3nHShG60zq)' }, + queryParams: { + id: 'in.(txQNInZMIHgZ,txZA0Oj6TV9c)', + }, }); + expect(transactionService.transformRawExpense).toHaveBeenCalledWith(apiExpenses3[0]); + expect(transactionService.transformRawExpense).toHaveBeenCalledWith(apiExpenses3[1]); expect(categoriesService.getSystemCategories).toHaveBeenCalledTimes(1); - expect(mergeExpensesService.generateReceiptOptions).toHaveBeenCalledOnceWith(expenseList2); + expect(mergeExpensesService.generateReceiptOptions).toHaveBeenCalledOnceWith(transformedPlatformedExpense1); expect(component.systemCategories).toEqual(['Bus', 'Airlines', 'Lodging', 'Train']); expect(component.receiptOptions).toEqual(mergeExpensesOptionsData); component.expenseOptions$.subscribe((res) => { expect(res).toEqual(mergeExpensesOptionsData); - expect(mergeExpensesService.generateExpenseToKeepOptions).toHaveBeenCalledOnceWith(expenseList2); + expect(mergeExpensesService.generateExpenseToKeepOptions).toHaveBeenCalledOnceWith( + transformedPlatformedExpense1 + ); }); component.amountOptionsData$.subscribe((res) => { expect(res).toEqual(optionsData3); - expect(mergeExpensesService.generateAmountOptions).toHaveBeenCalledOnceWith(expenseList2); + expect(mergeExpensesService.generateAmountOptions).toHaveBeenCalledOnceWith(transformedPlatformedExpense1); }); component.dateOfSpendOptionsData$.subscribe((res) => { expect(res).toEqual(optionsData6); - expect(mergeExpensesService.generateDateOfSpendOptions).toHaveBeenCalledOnceWith(expenseList2); + expect(mergeExpensesService.generateDateOfSpendOptions).toHaveBeenCalledOnceWith( + transformedPlatformedExpense1 + ); }); component.paymentModeOptionsData$.subscribe((res) => { expect(res).toEqual(optionsData7); - expect(mergeExpensesService.generatePaymentModeOptions).toHaveBeenCalledOnceWith(expenseList2); + expect(mergeExpensesService.generatePaymentModeOptions).toHaveBeenCalledOnceWith( + transformedPlatformedExpense1 + ); }); component.projectOptionsData$.subscribe((res) => { expect(res).toEqual(optionsData9); - expect(mergeExpensesService.generateProjectOptions).toHaveBeenCalledOnceWith(expenseList2); + expect(mergeExpensesService.generateProjectOptions).toHaveBeenCalledOnceWith(transformedPlatformedExpense1); }); component.billableOptionsData$.subscribe((res) => { expect(res).toEqual(optionsData2); - expect(mergeExpensesService.generateBillableOptions).toHaveBeenCalledOnceWith(expenseList2); + expect(mergeExpensesService.generateBillableOptions).toHaveBeenCalledOnceWith(transformedPlatformedExpense1); }); component.vendorOptionsData$.subscribe((res) => { expect(res).toEqual(optionsData8); - expect(mergeExpensesService.generateVendorOptions).toHaveBeenCalledOnceWith(expenseList2); + expect(mergeExpensesService.generateVendorOptions).toHaveBeenCalledOnceWith(transformedPlatformedExpense1); }); component.categoryOptionsData$.subscribe((res) => { expect(res).toEqual(optionsData10); - expect(mergeExpensesService.generateCategoryOptions).toHaveBeenCalledOnceWith(expenseList2); + expect(mergeExpensesService.generateCategoryOptions).toHaveBeenCalledOnceWith(transformedPlatformedExpense1); }); component.taxGroupOptionsData$.subscribe((res) => { expect(res).toEqual(optionsData11); - expect(mergeExpensesService.generateTaxGroupOptions).toHaveBeenCalledOnceWith(expenseList2); + expect(mergeExpensesService.generateTaxGroupOptions).toHaveBeenCalledOnceWith(transformedPlatformedExpense1); }); component.taxAmountOptionsData$.subscribe((res) => { expect(res).toEqual(optionsData12); - expect(mergeExpensesService.generateTaxAmountOptions).toHaveBeenCalledOnceWith(expenseList2); + expect(mergeExpensesService.generateTaxAmountOptions).toHaveBeenCalledOnceWith(transformedPlatformedExpense1); }); component.constCenterOptionsData$.subscribe((res) => { expect(res).toEqual(optionsData13); - expect(mergeExpensesService.generateCostCenterOptions).toHaveBeenCalledOnceWith(expenseList2); + expect(mergeExpensesService.generateCostCenterOptions).toHaveBeenCalledOnceWith( + transformedPlatformedExpense1 + ); }); component.purposeOptionsData$.subscribe((res) => { expect(res).toEqual(optionsData14); - expect(mergeExpensesService.generatePurposeOptions).toHaveBeenCalledOnceWith(expenseList2); + expect(mergeExpensesService.generatePurposeOptions).toHaveBeenCalledOnceWith(transformedPlatformedExpense1); }); component.location1OptionsData$.subscribe((res) => { @@ -240,8 +252,8 @@ export function TestCases1(getTestBed) { }); expect(mergeExpensesService.generateLocationOptions).toHaveBeenCalledTimes(2); - expect(mergeExpensesService.generateLocationOptions).toHaveBeenCalledWith(expenseList2, 0); - expect(mergeExpensesService.generateLocationOptions).toHaveBeenCalledWith(expenseList2, 1); + expect(mergeExpensesService.generateLocationOptions).toHaveBeenCalledWith(transformedPlatformedExpense1, 0); + expect(mergeExpensesService.generateLocationOptions).toHaveBeenCalledWith(transformedPlatformedExpense1, 1); component.onwardDateOptionsData$.subscribe((res) => { expect(res).toEqual(optionsData16); @@ -250,8 +262,8 @@ export function TestCases1(getTestBed) { expect(res).toEqual(optionsData16); }); - expect(mergeExpensesService.generateOnwardDateOptions).toHaveBeenCalledOnceWith(expenseList2); - expect(mergeExpensesService.generateReturnDateOptions).toHaveBeenCalledOnceWith(expenseList2); + expect(mergeExpensesService.generateOnwardDateOptions).toHaveBeenCalledOnceWith(transformedPlatformedExpense1); + expect(mergeExpensesService.generateReturnDateOptions).toHaveBeenCalledOnceWith(transformedPlatformedExpense1); component.flightJourneyTravelClassOptionsData$.subscribe((res) => { expect(res).toEqual(optionsData17); @@ -267,11 +279,19 @@ export function TestCases1(getTestBed) { expect(res).toEqual(optionsData19); }); - expect(mergeExpensesService.generateTrainTravelClassOptions).toHaveBeenCalledOnceWith(expenseList2); - expect(mergeExpensesService.generateBusTravelClassOptions).toHaveBeenCalledOnceWith(expenseList2); + expect(mergeExpensesService.generateTrainTravelClassOptions).toHaveBeenCalledOnceWith( + transformedPlatformedExpense1 + ); + expect(mergeExpensesService.generateBusTravelClassOptions).toHaveBeenCalledOnceWith( + transformedPlatformedExpense1 + ); - expect(mergeExpensesService.generateFlightJourneyTravelClassOptions).toHaveBeenCalledOnceWith(expenseList2); - expect(mergeExpensesService.generateFlightReturnTravelClassOptions).toHaveBeenCalledOnceWith(expenseList2); + expect(mergeExpensesService.generateFlightJourneyTravelClassOptions).toHaveBeenCalledOnceWith( + transformedPlatformedExpense1 + ); + expect(mergeExpensesService.generateFlightReturnTravelClassOptions).toHaveBeenCalledOnceWith( + transformedPlatformedExpense1 + ); component.distanceOptionsData$.subscribe((res) => { expect(res).toEqual(optionsData20); @@ -280,8 +300,10 @@ export function TestCases1(getTestBed) { expect(res).toEqual(optionsData21); }); - expect(mergeExpensesService.generateDistanceOptions).toHaveBeenCalledOnceWith(expenseList2); - expect(mergeExpensesService.generateDistanceUnitOptions).toHaveBeenCalledOnceWith(expenseList2); + expect(mergeExpensesService.generateDistanceOptions).toHaveBeenCalledOnceWith(transformedPlatformedExpense1); + expect(mergeExpensesService.generateDistanceUnitOptions).toHaveBeenCalledOnceWith( + transformedPlatformedExpense1 + ); }); }); diff --git a/src/app/fyle/merge-expense/merge-expense.page.setup.spec.ts b/src/app/fyle/merge-expense/merge-expense.page.setup.spec.ts index 6e2ed812af..28f208c980 100644 --- a/src/app/fyle/merge-expense/merge-expense.page.setup.spec.ts +++ b/src/app/fyle/merge-expense/merge-expense.page.setup.spec.ts @@ -20,6 +20,7 @@ import { TestCases1 } from './merge-expense-1.page.spec'; import { TestCases2 } from './merge-expense-2.page.spec'; import { TestCases3 } from './merge-expense-3.page.spec'; import { TransactionService } from 'src/app/core/services/transaction.service'; +import { ExpensesService } from 'src/app/core/services/platform/v1/spender/expenses.service'; describe('MergeExpensePage', () => { const getTestBed = () => { @@ -27,7 +28,7 @@ describe('MergeExpensePage', () => { const activatedRouteSpy = { snapshot: { params: { - expenseIDs: JSON.stringify(['txBphgnCHHeO', 'tx3nHShG60zq']), + expenseIDs: JSON.stringify(['txQNInZMIHgZ', 'txZA0Oj6TV9c']), }, }, }; @@ -82,7 +83,8 @@ describe('MergeExpensePage', () => { const dependentFieldsServiceSpy = jasmine.createSpyObj('DependentFieldsService', [ 'getDependentFieldsForBaseField', ]); - const transactionServiceSpy = jasmine.createSpyObj('TransactionService', ['getETxnc']); + const transactionServiceSpy = jasmine.createSpyObj('TransactionService', ['transformRawExpense']); + const expensesServiceSpy = jasmine.createSpyObj('ExpenseService', ['getAllExpenses']); TestBed.configureTestingModule({ declarations: [MergeExpensePage], @@ -114,6 +116,10 @@ describe('MergeExpensePage', () => { provide: TransactionService, useValue: transactionServiceSpy, }, + { + provide: ExpensesService, + useValue: expensesServiceSpy, + }, ], schemas: [NO_ERRORS_SCHEMA], }).compileComponents(); diff --git a/src/app/fyle/merge-expense/merge-expense.page.ts b/src/app/fyle/merge-expense/merge-expense.page.ts index bb1ed4ea0c..bc727ac14f 100644 --- a/src/app/fyle/merge-expense/merge-expense.page.ts +++ b/src/app/fyle/merge-expense/merge-expense.page.ts @@ -32,6 +32,7 @@ import { SnackbarPropertiesService } from 'src/app/core/services/snackbar-proper import { TrackingService } from 'src/app/core/services/tracking.service'; import { TransactionService } from 'src/app/core/services/transaction.service'; import { ToastMessageComponent } from 'src/app/shared/components/toast-message/toast-message.component'; +import { ExpensesService } from 'src/app/core/services/platform/v1/spender/expenses.service'; @Component({ selector: 'app-merge-expense', @@ -39,7 +40,7 @@ import { ToastMessageComponent } from 'src/app/shared/components/toast-message/t styleUrls: ['./merge-expense.page.scss'], }) export class MergeExpensePage implements OnInit, AfterViewChecked { - expenses: Expense[]; + expenses: Partial[]; fg: FormGroup; @@ -148,7 +149,8 @@ export class MergeExpensePage implements OnInit, AfterViewChecked { private trackingService: TrackingService, private expenseFieldsService: ExpenseFieldsService, private dependantFieldsService: DependentFieldsService, - private cdRef: ChangeDetectorRef + private cdRef: ChangeDetectorRef, + private expensesService: ExpensesService ) {} get genericFieldsForm(): AbstractControl { @@ -185,15 +187,18 @@ export class MergeExpensePage implements OnInit, AfterViewChecked { this.txnIDs = JSON.parse(this.activatedRoute.snapshot.params.expenseIDs as string) as string[]; - const expenses$ = this.transcationService - .getETxnc({ + const expenses$ = this.expensesService + .getAllExpenses({ offset: 0, limit: 200, - params: { - tx_id: `in.(${this.txnIDs.join(',')})`, + queryParams: { + id: `in.(${this.txnIDs.join(',')})`, }, }) - .pipe(shareReplay(1)); + .pipe( + map((expenses) => expenses.map((expense) => this.transcationService.transformRawExpense(expense))), + shareReplay(1) + ); this.expenseOptions$ = expenses$.pipe( switchMap((expenses) => this.mergeExpensesService.generateExpenseToKeepOptions(expenses)) diff --git a/src/app/fyle/my-expenses-v2/my-expenses-v2.page.spec.ts b/src/app/fyle/my-expenses-v2/my-expenses-v2.page.spec.ts index 16215bff34..c4a00b548f 100644 --- a/src/app/fyle/my-expenses-v2/my-expenses-v2.page.spec.ts +++ b/src/app/fyle/my-expenses-v2/my-expenses-v2.page.spec.ts @@ -187,7 +187,6 @@ describe('MyExpensesV2Page', () => { 'delete', 'getReportableExpenses', 'isMergeAllowed', - 'getDeletableTxns', 'excludeCCCExpenses', 'isCriticalPolicyViolatedExpense', 'isExpenseInDraft', @@ -1074,7 +1073,6 @@ describe('MyExpensesV2Page', () => { it('should set headerState to base if searchString is defined in loadData and if expense is selected', () => { component.loadExpenses$ = new BehaviorSubject({}); transactionService.getReportableExpenses.and.returnValue([]); - transactionService.getDeletableTxns.and.returnValue([]); transactionService.excludeCCCExpenses.and.returnValue([]); component.switchOutboxSelectionMode(apiExpenseRes[0]); @@ -3002,7 +3000,6 @@ describe('MyExpensesV2Page', () => { spyOn(component, 'setOutboxExpenseStatsOnSelect'); component.selectedOutboxExpenses = cloneDeep(apiExpenseRes); transactionService.isMergeAllowed.and.returnValue(true); - transactionService.getDeletableTxns.and.returnValue(apiExpenseRes); transactionService.excludeCCCExpenses.and.returnValue(apiExpenseRes); }); diff --git a/src/app/fyle/my-expenses-v2/my-expenses-v2.page.ts b/src/app/fyle/my-expenses-v2/my-expenses-v2.page.ts index 6d0316de8e..0c5eb29243 100644 --- a/src/app/fyle/my-expenses-v2/my-expenses-v2.page.ts +++ b/src/app/fyle/my-expenses-v2/my-expenses-v2.page.ts @@ -928,8 +928,6 @@ export class MyExpensesV2Page implements OnInit { this.transactionService.getReportableExpenses(this.selectedOutboxExpenses).length > 0; if (this.selectedOutboxExpenses.length > 0) { - this.outboxExpensesToBeDeleted = this.transactionService.getDeletableTxns(this.selectedOutboxExpenses); - this.outboxExpensesToBeDeleted = this.transactionService.excludeCCCExpenses(this.selectedOutboxExpenses); this.cccExpenses = this.selectedOutboxExpenses.length - this.outboxExpensesToBeDeleted.length; @@ -1453,12 +1451,6 @@ export class MyExpensesV2Page implements OnInit { .subscribe((allExpenses) => { this.selectedElements = this.selectedElements.concat(allExpenses); if (this.selectedElements.length > 0) { - if (this.outboxExpensesToBeDeleted?.length) { - this.outboxExpensesToBeDeleted = this.transactionService.getDeletableTxns( - this.outboxExpensesToBeDeleted - ); - } - this.expensesToBeDeleted = this.sharedExpenseService.excludeCCCExpenses(this.selectedElements); this.cccExpenses = this.selectedElements.length - this.expensesToBeDeleted.length; diff --git a/src/app/fyle/my-expenses/my-expenses.page.spec.ts b/src/app/fyle/my-expenses/my-expenses.page.spec.ts index e96744e496..d96cba4f05 100644 --- a/src/app/fyle/my-expenses/my-expenses.page.spec.ts +++ b/src/app/fyle/my-expenses/my-expenses.page.spec.ts @@ -167,7 +167,6 @@ describe('MyExpensesPage', () => { 'delete', 'getReportableExpenses', 'isMergeAllowed', - 'getDeletableTxns', 'excludeCCCExpenses', 'getIsCriticalPolicyViolated', 'getIsDraft', @@ -1707,7 +1706,6 @@ describe('MyExpensesPage', () => { spyOn(component, 'setExpenseStatsOnSelect'); component.selectedElements = cloneDeep(apiExpenseRes); transactionService.isMergeAllowed.and.returnValue(true); - transactionService.getDeletableTxns.and.returnValue(apiExpenseRes); transactionService.excludeCCCExpenses.and.returnValue(apiExpenseRes); }); @@ -2623,7 +2621,6 @@ describe('MyExpensesPage', () => { describe('onSelectAll(): ', () => { beforeEach(() => { transactionService.getAllExpenses.and.returnValue(of(cloneDeep(apiExpenseRes))); - transactionService.getDeletableTxns.and.returnValue(apiExpenseRes); transactionService.excludeCCCExpenses.and.returnValue(apiExpenseRes); transactionService.getReportableExpenses.and.returnValue(apiExpenseRes); apiV2Service.extendQueryParamsForTextSearch.and.returnValue({ @@ -2657,7 +2654,6 @@ describe('MyExpensesPage', () => { queryParams: { tx_report_id: 'is.null', tx_state: 'in.(COMPLETE,DRAFT)' }, }); expect(transactionService.excludeCCCExpenses).toHaveBeenCalledOnceWith([...apiExpenseRes, ...expenseList4]); - expect(transactionService.getDeletableTxns).toHaveBeenCalledOnceWith([...apiExpenseRes, ...expenseList4]); expect(component.cccExpenses).toBe(3); expect(component.selectedElements).toEqual([...apiExpenseRes, ...expenseList4]); expect(component.allExpensesCount).toBe(4); diff --git a/src/app/fyle/my-expenses/my-expenses.page.ts b/src/app/fyle/my-expenses/my-expenses.page.ts index 2daeb72c55..b1b250f820 100644 --- a/src/app/fyle/my-expenses/my-expenses.page.ts +++ b/src/app/fyle/my-expenses/my-expenses.page.ts @@ -890,8 +890,6 @@ export class MyExpensesPage implements OnInit { this.isReportableExpensesSelected = this.transactionService.getReportableExpenses(this.selectedElements).length > 0; if (this.selectedElements.length > 0) { - this.expensesToBeDeleted = this.transactionService.getDeletableTxns(this.selectedElements); - this.expensesToBeDeleted = this.transactionService.excludeCCCExpenses(this.selectedElements); this.cccExpenses = this.selectedElements.length - this.expensesToBeDeleted.length; @@ -1351,8 +1349,6 @@ export class MyExpensesPage implements OnInit { .subscribe((allExpenses) => { this.selectedElements = this.selectedElements.concat(allExpenses); if (this.selectedElements.length > 0) { - this.expensesToBeDeleted = this.transactionService.getDeletableTxns(this.selectedElements); - this.expensesToBeDeleted = this.transactionService.excludeCCCExpenses(this.selectedElements); this.cccExpenses = this.selectedElements.length - this.expensesToBeDeleted.length; diff --git a/src/app/fyle/my-reports/my-reports.page.spec.ts b/src/app/fyle/my-reports/my-reports.page.spec.ts index a9ad52b870..07404963a5 100644 --- a/src/app/fyle/my-reports/my-reports.page.spec.ts +++ b/src/app/fyle/my-reports/my-reports.page.spec.ts @@ -6,7 +6,7 @@ import { TasksService } from 'src/app/core/services/tasks.service'; import { CurrencyService } from 'src/app/core/services/currency.service'; import { ReportService } from 'src/app/core/services/report.service'; import { ApiV2Service } from 'src/app/core/services/api-v2.service'; -import { TransactionService } from 'src/app/core/services/transaction.service'; +import { ExpensesService } from 'src/app/core/services/platform/v1/spender/expenses.service'; import { OrgSettingsService } from 'src/app/core/services/org-settings.service'; import { ActivatedRoute, Router } from '@angular/router'; import { BehaviorSubject, of } from 'rxjs'; @@ -82,7 +82,7 @@ import { expectedFilterPill8, expectedFilterPill9, } from 'src/app/core/mock-data/my-reports-filterpills.data'; -import { transactionDatum1, transactionDatum2 } from 'src/app/core/mock-data/stats-response.data'; +import { completeStats1, emptyStats } from 'src/app/core/mock-data/platform/v1/expenses-stats.data'; describe('MyReportsPage', () => { let component: MyReportsPage; @@ -91,7 +91,7 @@ describe('MyReportsPage', () => { let currencyService: jasmine.SpyObj; let reportService: jasmine.SpyObj; let apiV2Service: jasmine.SpyObj; - let transactionService: jasmine.SpyObj; + let expensesService: jasmine.SpyObj; let orgSettingsService: jasmine.SpyObj; let activatedRoute: jasmine.SpyObj; let router: jasmine.SpyObj; @@ -114,7 +114,7 @@ describe('MyReportsPage', () => { 'delete', ]); const apiV2ServiceSpy = jasmine.createSpyObj('ApiV2Service', ['extendQueryParamsForTextSearch']); - const transactionServiceSpy = jasmine.createSpyObj('TransactionService', ['getTransactionStats']); + const expensesServiceSpy = jasmine.createSpyObj('ExpensesService', ['getExpenseStats']); const orgSettingsServiceSpy = jasmine.createSpyObj('OrgSettingsService', ['get']); const navControllerSpy = jasmine.createSpyObj('NavController', ['back']); const networkServiceSpy = jasmine.createSpyObj('NetworkService', ['isOnline', 'connectivityWatcher']); @@ -148,7 +148,7 @@ describe('MyReportsPage', () => { { provide: CurrencyService, useValue: currencyServiceSpy }, { provide: ReportService, useValue: reportServiceSpy }, { provide: ApiV2Service, useValue: apiV2ServiceSpy }, - { provide: TransactionService, useValue: transactionServiceSpy }, + { provide: ExpensesService, useValue: expensesServiceSpy }, { provide: OrgSettingsService, useValue: orgSettingsServiceSpy }, { provide: ActivatedRoute, useValue: activatedRouteSpy }, { provide: Router, useValue: jasmine.createSpyObj('Router', ['navigate', 'createUrlTree']) }, @@ -202,7 +202,7 @@ describe('MyReportsPage', () => { tasksService = TestBed.inject(TasksService) as jasmine.SpyObj; orgSettingsService = TestBed.inject(OrgSettingsService) as jasmine.SpyObj; apiV2Service = TestBed.inject(ApiV2Service) as jasmine.SpyObj; - transactionService = TestBed.inject(TransactionService) as jasmine.SpyObj; + expensesService = TestBed.inject(ExpensesService) as jasmine.SpyObj; networkService = TestBed.inject(NetworkService) as jasmine.SpyObj; reportService = TestBed.inject(ReportService) as jasmine.SpyObj; dateService = TestBed.inject(DateService) as jasmine.SpyObj; @@ -236,7 +236,7 @@ describe('MyReportsPage', () => { reportService.getMyReports.and.returnValue(of(paginatedPipeValue)); orgSettingsService.get.and.returnValue(of(orgSettingsRes)); - transactionService.getTransactionStats.and.returnValue(of(transactionDatum1)); + expensesService.getExpenseStats.and.returnValue(of(completeStats1)); component.simpleSearchInput = fixture.debugElement.query(By.css('.my-reports--simple-search-input')); @@ -287,11 +287,10 @@ describe('MyReportsPage', () => { ); component.expensesAmountStats$.subscribe((expenseAmountStates) => { - expect(transactionService.getTransactionStats).toHaveBeenCalledOnceWith('count(tx_id),sum(tx_amount)', { - scalar: true, - tx_report_id: 'is.null', - tx_state: 'in.(COMPLETE)', - or: '(tx_policy_amount.is.null,tx_policy_amount.gt.0.0001)', + expect(expensesService.getExpenseStats).toHaveBeenCalledOnceWith({ + state: 'in.(COMPLETE)', + or: '(policy_amount.is.null,policy_amount.gt.0.0001)', + report_id: 'is.null', }); expect(expenseAmountStates).toEqual({ @@ -375,7 +374,7 @@ describe('MyReportsPage', () => { reportService.getMyReports.and.returnValue(of(paginatedPipeValue)); orgSettingsService.get.and.returnValue(of(undefined)); - transactionService.getTransactionStats.and.returnValue(of(transactionDatum1)); + expensesService.getExpenseStats.and.returnValue(of(completeStats1)); component.simpleSearchInput = fixture.debugElement.query(By.css('.my-reports--simple-search-input')); @@ -426,11 +425,10 @@ describe('MyReportsPage', () => { ); component.expensesAmountStats$.subscribe((expenseAmountStates) => { - expect(transactionService.getTransactionStats).toHaveBeenCalledOnceWith('count(tx_id),sum(tx_amount)', { - scalar: true, - tx_report_id: 'is.null', - tx_state: 'in.(COMPLETE)', - or: '(tx_policy_amount.is.null,tx_policy_amount.gt.0.0001)', + expect(expensesService.getExpenseStats).toHaveBeenCalledOnceWith({ + state: 'in.(COMPLETE)', + or: '(policy_amount.is.null,policy_amount.gt.0.0001)', + report_id: 'is.null', }); expect(expenseAmountStates).toEqual({ @@ -514,7 +512,7 @@ describe('MyReportsPage', () => { reportService.getMyReports.and.returnValue(of(paginatedPipeValue)); orgSettingsService.get.and.returnValue(of({ payment_mode_settings: { allowed: true, enabled: true } })); - transactionService.getTransactionStats.and.returnValue(of(transactionDatum1)); + expensesService.getExpenseStats.and.returnValue(of(completeStats1)); component.simpleSearchInput = fixture.debugElement.query(By.css('.my-reports--simple-search-input')); @@ -565,11 +563,10 @@ describe('MyReportsPage', () => { ); component.expensesAmountStats$.subscribe((expenseAmountStates) => { - expect(transactionService.getTransactionStats).toHaveBeenCalledOnceWith('count(tx_id),sum(tx_amount)', { - scalar: true, - tx_report_id: 'is.null', - tx_state: 'in.(COMPLETE)', - or: '(tx_policy_amount.is.null,tx_policy_amount.gt.0.0001)', + expect(expensesService.getExpenseStats).toHaveBeenCalledOnceWith({ + state: 'in.(COMPLETE)', + or: '(policy_amount.is.null,policy_amount.gt.0.0001)', + report_id: 'is.null', }); expect(expenseAmountStates).toEqual({ @@ -649,7 +646,7 @@ describe('MyReportsPage', () => { reportService.getMyReportsCount.and.returnValue(of(0)); orgSettingsService.get.and.returnValue(of(orgSettingsParamsWithSimplifiedReport)); - transactionService.getTransactionStats.and.returnValue(of(transactionDatum2)); + expensesService.getExpenseStats.and.returnValue(of(emptyStats)); component.simpleSearchInput = fixture.debugElement.query(By.css('.my-reports--simple-search-input')); @@ -706,11 +703,10 @@ describe('MyReportsPage', () => { ); component.expensesAmountStats$.subscribe((expenseAmountStates) => { - expect(transactionService.getTransactionStats).toHaveBeenCalledOnceWith('count(tx_id),sum(tx_amount)', { - scalar: true, - tx_report_id: 'is.null', - tx_state: 'in.(COMPLETE)', - or: '(tx_policy_amount.is.null,tx_policy_amount.gt.0.0001)', + expect(expensesService.getExpenseStats).toHaveBeenCalledOnceWith({ + state: 'in.(COMPLETE)', + or: '(policy_amount.is.null,policy_amount.gt.0.0001)', + report_id: 'is.null', }); expect(expenseAmountStates).toEqual({ @@ -784,7 +780,7 @@ describe('MyReportsPage', () => { reportService.getMyReports.and.returnValue(of(paginatedPipeValue)); orgSettingsService.get.and.returnValue(of(orgSettingsRes)); - transactionService.getTransactionStats.and.returnValue(of(transactionDatum1)); + expensesService.getExpenseStats.and.returnValue(of(completeStats1)); activatedRoute.snapshot.queryParams.filters = '{"sortDir": "desc"}'; @@ -839,11 +835,10 @@ describe('MyReportsPage', () => { ); component.expensesAmountStats$.subscribe((expenseAmountStates) => { - expect(transactionService.getTransactionStats).toHaveBeenCalledOnceWith('count(tx_id),sum(tx_amount)', { - scalar: true, - tx_report_id: 'is.null', - tx_state: 'in.(COMPLETE)', - or: '(tx_policy_amount.is.null,tx_policy_amount.gt.0.0001)', + expect(expensesService.getExpenseStats).toHaveBeenCalledOnceWith({ + state: 'in.(COMPLETE)', + or: '(policy_amount.is.null,policy_amount.gt.0.0001)', + report_id: 'is.null', }); expect(expenseAmountStates).toEqual({ @@ -936,7 +931,7 @@ describe('MyReportsPage', () => { reportService.getMyReports.and.returnValue(of(paginatedPipeValue)); orgSettingsService.get.and.returnValue(of(orgSettingsRes)); - transactionService.getTransactionStats.and.returnValue(of(transactionDatum1)); + expensesService.getExpenseStats.and.returnValue(of(completeStats1)); activatedRoute.snapshot.params.state = 'needsreceipt'; @@ -991,11 +986,10 @@ describe('MyReportsPage', () => { ); component.expensesAmountStats$.subscribe((expenseAmountStates) => { - expect(transactionService.getTransactionStats).toHaveBeenCalledOnceWith('count(tx_id),sum(tx_amount)', { - scalar: true, - tx_report_id: 'is.null', - tx_state: 'in.(COMPLETE)', - or: '(tx_policy_amount.is.null,tx_policy_amount.gt.0.0001)', + expect(expensesService.getExpenseStats).toHaveBeenCalledOnceWith({ + state: 'in.(COMPLETE)', + or: '(policy_amount.is.null,policy_amount.gt.0.0001)', + report_id: 'is.null', }); expect(expenseAmountStates).toEqual({ diff --git a/src/app/fyle/my-reports/my-reports.page.ts b/src/app/fyle/my-reports/my-reports.page.ts index f7ef6e78f9..90d372c612 100644 --- a/src/app/fyle/my-reports/my-reports.page.ts +++ b/src/app/fyle/my-reports/my-reports.page.ts @@ -18,7 +18,6 @@ import { ReportService } from 'src/app/core/services/report.service'; import { ModalController, PopoverController } from '@ionic/angular'; import { DateService } from 'src/app/core/services/date.service'; import { CurrencyService } from 'src/app/core/services/currency.service'; -import { TransactionService } from '../../core/services/transaction.service'; import { capitalize, replace } from 'lodash'; import { TrackingService } from '../../core/services/tracking.service'; import { ApiV2Service } from 'src/app/core/services/api-v2.service'; @@ -37,6 +36,7 @@ import { ReportState } from 'src/app/shared/pipes/report-state.pipe'; import * as dayjs from 'dayjs'; import { AllowedPaymentModes } from 'src/app/core/models/allowed-payment-modes.enum'; import { DeletePopoverParams } from 'src/app/core/models/delete-popover-params.model'; +import { ExpensesService } from 'src/app/core/services/platform/v1/spender/expenses.service'; type Filters = Partial<{ state: string | string[]; @@ -118,14 +118,14 @@ export class MyReportsPage { private router: Router, private currencyService: CurrencyService, private activatedRoute: ActivatedRoute, - private transactionService: TransactionService, private popoverController: PopoverController, private trackingService: TrackingService, private apiV2Service: ApiV2Service, private tasksService: TasksService, private modalController: ModalController, private orgSettingsService: OrgSettingsService, - private reportStatePipe: ReportState + private reportStatePipe: ReportState, + private expensesService: ExpensesService ) {} get HeaderState(): typeof HeaderState { @@ -234,24 +234,17 @@ export class MyReportsPage { this.expensesAmountStats$ = this.loadData$.pipe( switchMap(() => - this.transactionService - .getTransactionStats('count(tx_id),sum(tx_amount)', { - scalar: true, - tx_report_id: 'is.null', - tx_state: 'in.(COMPLETE)', - or: '(tx_policy_amount.is.null,tx_policy_amount.gt.0.0001)', + this.expensesService + .getExpenseStats({ + state: 'in.(COMPLETE)', + report_id: 'is.null', + or: '(policy_amount.is.null,policy_amount.gt.0.0001)', }) .pipe( - map((stats) => { - const sum = - stats && stats[0] && stats[0].aggregates.find((stat) => stat.function_name === 'sum(tx_amount)'); - const count = - stats && stats[0] && stats[0].aggregates.find((stat) => stat.function_name === 'count(tx_id)'); - return { - sum: (sum && sum.function_value) || 0, - count: (count && count.function_value) || 0, - }; - }) + map((stats) => ({ + count: stats.data.count, + sum: stats.data.total_amount, + })) ) ) ); diff --git a/src/app/shared/components/expenses-card-v2/expenses-card.component.spec.ts b/src/app/shared/components/expenses-card-v2/expenses-card.component.spec.ts index e8157e68e2..72e35568b8 100644 --- a/src/app/shared/components/expenses-card-v2/expenses-card.component.spec.ts +++ b/src/app/shared/components/expenses-card-v2/expenses-card.component.spec.ts @@ -1,6 +1,7 @@ import { ComponentFixture, TestBed, fakeAsync, tick, waitForAsync } from '@angular/core/testing'; import { IonicModule } from '@ionic/angular'; import { TransactionService } from 'src/app/core/services/transaction.service'; +import { ExpensesService } from 'src/app/core/services/platform/v1/spender/expenses.service'; import { FileService } from 'src/app/core/services/file.service'; import { NetworkService } from 'src/app/core/services/network.service'; import { TransactionsOutboxService } from 'src/app/core/services/transactions-outbox.service'; @@ -38,11 +39,17 @@ import { expenseData, expenseResponseData } from 'src/app/core/mock-data/platfor import { AccountType } from 'src/app/core/models/platform/v1/account.model'; import { ExpensesService as SharedExpenseService } from 'src/app/core/services/platform/v1/shared/expenses.service'; import { PopupAlertComponent } from '../popup-alert/popup-alert.component'; +import { platformExpenseData, platformExpenseWithExtractedData } from 'src/app/core/mock-data/platform/v1/expense.data'; +import { + transformedExpenseData, + transformedExpenseWithExtractedData, +} from 'src/app/core/mock-data/transformed-expense.data'; describe('ExpensesCardComponent', () => { let component: ExpensesCardComponent; let fixture: ComponentFixture; let transactionService: jasmine.SpyObj; + let expensesService: jasmine.SpyObj; let sharedExpenseService: jasmine.SpyObj; let orgUserSettingsService: jasmine.SpyObj; let fileService: jasmine.SpyObj; @@ -60,7 +67,8 @@ describe('ExpensesCardComponent', () => { let componentElement: DebugElement; beforeEach(waitForAsync(() => { - const transactionServiceSpy = jasmine.createSpyObj('TransactionService', ['getETxnUnflattened']); + const transactionServiceSpy = jasmine.createSpyObj('TransactionService', ['transformExpense']); + const expensesServiceSpy = jasmine.createSpyObj('ExpensesService', ['getExpenseById']); const sharedExpenseServiceSpy = jasmine.createSpyObj('SharedExpenseService', [ 'isExpenseInDraft', 'isCriticalPolicyViolatedExpense', @@ -101,6 +109,7 @@ describe('ExpensesCardComponent', () => { imports: [IonicModule.forRoot(), MatIconModule, MatIconTestingModule, MatCheckboxModule, FormsModule], providers: [ { provide: TransactionService, useValue: transactionServiceSpy }, + { provide: ExpensesService, useValue: expensesServiceSpy }, { provide: SharedExpenseService, useValue: sharedExpenseServiceSpy }, { provide: OrgUserSettingsService, useValue: orgUserSettingsServiceSpy }, { provide: FileService, useValue: fileServiceSpy }, @@ -135,6 +144,7 @@ describe('ExpensesCardComponent', () => { expenseFieldsService = TestBed.inject(ExpenseFieldsService) as jasmine.SpyObj; orgSettingsService = TestBed.inject(OrgSettingsService) as jasmine.SpyObj; transactionService = TestBed.inject(TransactionService) as jasmine.SpyObj; + expensesService = TestBed.inject(ExpensesService) as jasmine.SpyObj; sharedExpenseService = TestBed.inject(SharedExpenseService) as jasmine.SpyObj; orgSettingsService.get.and.returnValue(of(orgSettingsGetData)); @@ -146,7 +156,8 @@ describe('ExpensesCardComponent', () => { platform.is.and.returnValue(true); fileService.getReceiptDetails.and.returnValue(fileObjectAdv[0].type); transactionsOutboxService.isDataExtractionPending.and.returnValue(true); - transactionService.getETxnUnflattened.and.returnValue(of(unflattenedTxnData)); + expensesService.getExpenseById.and.returnValue(of(platformExpenseData)); + transactionService.transformExpense.and.returnValue(transformedExpenseData); networkService.isOnline.and.returnValue(of(true)); sharedExpenseService.isExpenseInDraft.and.returnValue(true); @@ -360,24 +371,11 @@ describe('ExpensesCardComponent', () => { it('should handle status when the syncing is in progress and the extracted data is present', fakeAsync(() => { component.isOutboxExpense = false; component.homeCurrency = 'INR'; - const unflattenRes = { - ...unflattenedTxnData, - tx_id: 'tx5fBcPBAxLv', - tx: { - extracted_data: { - amount: 2500, - currency: 'INR', - category: 'Software', - date: null, - vendor: null, - invoice_dt: null, - }, - }, - }; - component.expense.id = 'tx5fBcPBAxLv'; + component.expense.id = 'txO6d6eiB4JF'; orgUserSettingsService.get.and.returnValue(of(orgUserSettingsData)); const isScanCompletedSpy = spyOn(component, 'checkIfScanIsCompleted').and.returnValue(false); - transactionService.getETxnUnflattened.and.returnValue(of(unflattenRes)); + expensesService.getExpenseById.and.returnValue(of(platformExpenseWithExtractedData)); + transactionService.transformExpense.and.returnValue(transformedExpenseWithExtractedData); component.isScanInProgress = true; spyOn(component, 'pollDataExtractionStatus').and.callFake((callback) => { callback(); @@ -390,28 +388,23 @@ describe('ExpensesCardComponent', () => { tick(500); expect(orgUserSettingsService.get).toHaveBeenCalledTimes(1); expect(isScanCompletedSpy).toHaveBeenCalledTimes(1); - expect(transactionsOutboxService.isDataExtractionPending).toHaveBeenCalledOnceWith('tx5fBcPBAxLv'); + expect(transactionsOutboxService.isDataExtractionPending).toHaveBeenCalledOnceWith('txO6d6eiB4JF'); expect(component.pollDataExtractionStatus).toHaveBeenCalledTimes(1); tick(500); - expect(transactionService.getETxnUnflattened).toHaveBeenCalledOnceWith(component.expense.id); + expect(expensesService.getExpenseById).toHaveBeenCalledOnceWith(component.expense.id); + expect(transactionService.transformExpense).toHaveBeenCalledOnceWith(platformExpenseWithExtractedData); expect(component.isScanCompleted).toBeTrue(); expect(component.isScanInProgress).toBeFalse(); - expect(component.expense.extracted_data).toEqual(unflattenRes.tx.extracted_data); + expect(component.expense.extracted_data).toEqual(transformedExpenseWithExtractedData.tx.extracted_data); })); it('should handle status when the sync is in progress and there is no extracted data present', fakeAsync(() => { component.isOutboxExpense = false; - const unflattenRes = { - ...unflattenedTxnData, - tx_id: 'tx5fBcPBAxLv', - tx: { - extracted_data: null, - }, - }; - component.expense.id = 'tx5fBcPBAxLv'; + component.expense.id = 'txvslh8aQMbu'; orgUserSettingsService.get.and.returnValue(of(orgUserSettingsData)); const isScanCompletedSpy = spyOn(component, 'checkIfScanIsCompleted').and.returnValue(false); - transactionService.getETxnUnflattened.and.returnValue(of(unflattenRes)); + expensesService.getExpenseById.and.returnValue(of(platformExpenseData)); + transactionService.transformExpense.and.returnValue(transformedExpenseData); component.isScanInProgress = true; const pollDataSpy = spyOn(component, 'pollDataExtractionStatus').and.callFake((callback) => { callback(); @@ -425,10 +418,11 @@ describe('ExpensesCardComponent', () => { expect(orgUserSettingsService.get).toHaveBeenCalledTimes(1); expect(component.checkIfScanIsCompleted).toHaveBeenCalledTimes(1); expect(isScanCompletedSpy).toHaveBeenCalledTimes(1); - expect(transactionsOutboxService.isDataExtractionPending).toHaveBeenCalledOnceWith('tx5fBcPBAxLv'); + expect(transactionsOutboxService.isDataExtractionPending).toHaveBeenCalledOnceWith('txvslh8aQMbu'); expect(pollDataSpy).toHaveBeenCalledTimes(1); tick(500); - expect(transactionService.getETxnUnflattened).toHaveBeenCalledOnceWith(component.expense.id); + expect(expensesService.getExpenseById).toHaveBeenCalledOnceWith(component.expense.id); + expect(transactionService.transformExpense).toHaveBeenCalledOnceWith(platformExpenseData); expect(component.isScanCompleted).toBeFalse(); expect(component.isScanInProgress).toBeFalse(); })); diff --git a/src/app/shared/components/expenses-card-v2/expenses-card.component.ts b/src/app/shared/components/expenses-card-v2/expenses-card.component.ts index 31f508d737..a44c0bff00 100644 --- a/src/app/shared/components/expenses-card-v2/expenses-card.component.ts +++ b/src/app/shared/components/expenses-card-v2/expenses-card.component.ts @@ -25,6 +25,7 @@ import { SnackbarPropertiesService } from '../../../core/services/snackbar-prope import { TrackingService } from '../../../core/services/tracking.service'; import { PopupAlertComponent } from '../popup-alert/popup-alert.component'; import { ExpensesService as SharedExpenseService } from 'src/app/core/services/platform/v1/shared/expenses.service'; +import { ExpensesService } from 'src/app/core/services/platform/v1/spender/expenses.service'; type ReceiptDetail = { dataUrl: string; @@ -141,7 +142,8 @@ export class ExpensesCardComponent implements OnInit { private trackingService: TrackingService, private currencyService: CurrencyService, private expenseFieldsService: ExpenseFieldsService, - private orgSettingsService: OrgSettingsService + private orgSettingsService: OrgSettingsService, + private expensesService: ExpensesService ) {} get isSelected(): boolean { @@ -236,7 +238,8 @@ export class ExpensesCardComponent implements OnInit { !that.isScanCompleted && that.transactionOutboxService.isDataExtractionPending(that.expense.id); if (that.isScanInProgress) { that.pollDataExtractionStatus(function () { - that.transactionService.getETxnUnflattened(that.expense.id).subscribe((etxn) => { + that.expensesService.getExpenseById(that.expense.id).subscribe((expense) => { + const etxn = that.transactionService.transformExpense(expense); const extractedData = etxn.tx.extracted_data; if (!!extractedData) { that.isScanCompleted = true; diff --git a/src/app/shared/components/expenses-card/expenses-card.component.spec.ts b/src/app/shared/components/expenses-card/expenses-card.component.spec.ts index c64d9bb3d4..65dbc23f1e 100644 --- a/src/app/shared/components/expenses-card/expenses-card.component.spec.ts +++ b/src/app/shared/components/expenses-card/expenses-card.component.spec.ts @@ -1,6 +1,7 @@ import { ComponentFixture, TestBed, fakeAsync, tick, waitForAsync } from '@angular/core/testing'; import { IonicModule } from '@ionic/angular'; import { TransactionService } from 'src/app/core/services/transaction.service'; +import { ExpensesService } from 'src/app/core/services/platform/v1/spender/expenses.service'; import { FileService } from 'src/app/core/services/file.service'; import { NetworkService } from 'src/app/core/services/network.service'; import { TransactionsOutboxService } from 'src/app/core/services/transactions-outbox.service'; @@ -36,11 +37,17 @@ import { CameraOptionsPopupComponent } from 'src/app/fyle/add-edit-expense/camer import { CaptureReceiptComponent } from 'src/app/shared/components/capture-receipt/capture-receipt.component'; import { ToastMessageComponent } from '../toast-message/toast-message.component'; import { DebugElement, EventEmitter } from '@angular/core'; +import { platformExpenseData, platformExpenseWithExtractedData } from 'src/app/core/mock-data/platform/v1/expense.data'; +import { + transformedExpenseData, + transformedExpenseWithExtractedData, +} from 'src/app/core/mock-data/transformed-expense.data'; describe('ExpensesCardComponent', () => { let component: ExpensesCardComponent; let fixture: ComponentFixture; let transactionService: jasmine.SpyObj; + let expensesService: jasmine.SpyObj; let orgUserSettingsService: jasmine.SpyObj; let fileService: jasmine.SpyObj; let popoverController: jasmine.SpyObj; @@ -58,11 +65,12 @@ describe('ExpensesCardComponent', () => { beforeEach(waitForAsync(() => { const transactionServiceSpy = jasmine.createSpyObj('TransactionService', [ - 'getETxnUnflattened', + 'transformExpense', 'getIsDraft', 'getIsCriticalPolicyViolated', 'getVendorDetails', ]); + const expensesServiceSpy = jasmine.createSpyObj('ExpensesService', ['getExpenseById']); const orgUserSettingsServiceSpy = jasmine.createSpyObj('OrgUserSettingsService', ['get']); const fileServiceSpy = jasmine.createSpyObj('FileService', [ 'downloadUrl', @@ -98,6 +106,7 @@ describe('ExpensesCardComponent', () => { imports: [IonicModule.forRoot(), MatIconModule, MatIconTestingModule, MatCheckboxModule, FormsModule], providers: [ { provide: TransactionService, useValue: transactionServiceSpy }, + { provide: ExpensesService, useValue: expensesServiceSpy }, { provide: OrgUserSettingsService, useValue: orgUserSettingsServiceSpy }, { provide: FileService, useValue: fileServiceSpy }, { provide: PopoverController, useValue: popoverControllerSpy }, @@ -131,6 +140,7 @@ describe('ExpensesCardComponent', () => { expenseFieldsService = TestBed.inject(ExpenseFieldsService) as jasmine.SpyObj; orgSettingsService = TestBed.inject(OrgSettingsService) as jasmine.SpyObj; transactionService = TestBed.inject(TransactionService) as jasmine.SpyObj; + expensesService = TestBed.inject(ExpensesService) as jasmine.SpyObj; orgSettingsService.get.and.returnValue(of(orgSettingsGetData)); transactionsOutboxService.isSyncInProgress.and.returnValue(true); @@ -141,7 +151,8 @@ describe('ExpensesCardComponent', () => { platform.is.and.returnValue(true); fileService.getReceiptDetails.and.returnValue(fileObjectAdv[0].type); transactionsOutboxService.isDataExtractionPending.and.returnValue(true); - transactionService.getETxnUnflattened.and.returnValue(of(unflattenedTxnData)); + expensesService.getExpenseById.and.returnValue(of(platformExpenseData)); + transactionService.transformExpense.and.returnValue(transformedExpenseData); networkService.isOnline.and.returnValue(of(true)); transactionService.getIsDraft.and.returnValue(true); @@ -337,26 +348,14 @@ describe('ExpensesCardComponent', () => { }); describe('handleScanStatus():', () => { - it('should handle status when the syncing is in progress and the extracted adata is present', fakeAsync(() => { + it('should handle status when the syncing is in progress and the extracted data is present', fakeAsync(() => { component.isOutboxExpense = false; component.homeCurrency = 'INR'; - const unflattenRes = { - ...unflattenedTxnData, - tx_id: 'tx5fBcPBAxLv', - tx: { - extracted_data: { - amount: 2500, - currency: 'INR', - category: 'Software', - date: null, - vendor: null, - invoice_dt: null, - }, - }, - }; + component.expense.tx_id = 'txO6d6eiB4JF'; orgUserSettingsService.get.and.returnValue(of(orgUserSettingsData)); const isScanCompletedSpy = spyOn(component, 'checkIfScanIsCompleted').and.returnValue(false); - transactionService.getETxnUnflattened.and.returnValue(of(unflattenRes)); + expensesService.getExpenseById.and.returnValue(of(platformExpenseWithExtractedData)); + transactionService.transformExpense.and.returnValue(transformedExpenseWithExtractedData); component.isScanInProgress = true; spyOn(component, 'pollDataExtractionStatus').and.callFake((callback) => { callback(); @@ -369,27 +368,23 @@ describe('ExpensesCardComponent', () => { tick(500); expect(orgUserSettingsService.get).toHaveBeenCalledTimes(1); expect(isScanCompletedSpy).toHaveBeenCalledTimes(1); - expect(transactionsOutboxService.isDataExtractionPending).toHaveBeenCalledOnceWith('tx5fBcPBAxLv'); + expect(transactionsOutboxService.isDataExtractionPending).toHaveBeenCalledOnceWith('txO6d6eiB4JF'); expect(component.pollDataExtractionStatus).toHaveBeenCalledTimes(1); tick(500); - expect(transactionService.getETxnUnflattened).toHaveBeenCalledOnceWith(component.expense.tx_id); + expect(expensesService.getExpenseById).toHaveBeenCalledOnceWith(component.expense.tx_id); + expect(transactionService.transformExpense).toHaveBeenCalledOnceWith(platformExpenseWithExtractedData); expect(component.isScanCompleted).toBeTrue(); expect(component.isScanInProgress).toBeFalse(); - expect(component.expense.tx_extracted_data).toEqual(unflattenRes.tx.extracted_data); + expect(component.expense.tx_extracted_data).toEqual(transformedExpenseWithExtractedData.tx.extracted_data); })); it('should handle status when the sync is in progress and there is no extracted data present', fakeAsync(() => { component.isOutboxExpense = false; - const unflattenRes = { - ...unflattenedTxnData, - tx_id: 'tx5fBcPBAxLv', - tx: { - extracted_data: null, - }, - }; + component.expense.tx_id = 'txvslh8aQMbu'; orgUserSettingsService.get.and.returnValue(of(orgUserSettingsData)); const isScanCompletedSpy = spyOn(component, 'checkIfScanIsCompleted').and.returnValue(false); - transactionService.getETxnUnflattened.and.returnValue(of(unflattenRes)); + expensesService.getExpenseById.and.returnValue(of(platformExpenseData)); + transactionService.transformExpense.and.returnValue(transformedExpenseData); component.isScanInProgress = true; const pollDataSpy = spyOn(component, 'pollDataExtractionStatus').and.callFake((callback) => { callback(); @@ -403,10 +398,11 @@ describe('ExpensesCardComponent', () => { expect(orgUserSettingsService.get).toHaveBeenCalledTimes(1); expect(component.checkIfScanIsCompleted).toHaveBeenCalledTimes(1); expect(isScanCompletedSpy).toHaveBeenCalledTimes(1); - expect(transactionsOutboxService.isDataExtractionPending).toHaveBeenCalledOnceWith('tx5fBcPBAxLv'); + expect(transactionsOutboxService.isDataExtractionPending).toHaveBeenCalledOnceWith('txvslh8aQMbu'); expect(pollDataSpy).toHaveBeenCalledTimes(1); tick(500); - expect(transactionService.getETxnUnflattened).toHaveBeenCalledOnceWith(component.expense.tx_id); + expect(expensesService.getExpenseById).toHaveBeenCalledOnceWith(component.expense.tx_id); + expect(transactionService.transformExpense).toHaveBeenCalledOnceWith(platformExpenseData); expect(component.isScanCompleted).toBeFalse(); expect(component.isScanInProgress).toBeFalse(); })); diff --git a/src/app/shared/components/expenses-card/expenses-card.component.ts b/src/app/shared/components/expenses-card/expenses-card.component.ts index cec6dd3e0d..0159836eca 100644 --- a/src/app/shared/components/expenses-card/expenses-card.component.ts +++ b/src/app/shared/components/expenses-card/expenses-card.component.ts @@ -24,6 +24,7 @@ import { ToastMessageComponent } from 'src/app/shared/components/toast-message/t import { SnackbarPropertiesService } from '../../../core/services/snackbar-properties.service'; import { TrackingService } from '../../../core/services/tracking.service'; import { PopupAlertComponent } from '../popup-alert/popup-alert.component'; +import { ExpensesService } from 'src/app/core/services/platform/v1/spender/expenses.service'; type ReceiptDetail = { dataUrl: string; @@ -135,7 +136,8 @@ export class ExpensesCardComponent implements OnInit { private trackingService: TrackingService, private currencyService: CurrencyService, private expenseFieldsService: ExpenseFieldsService, - private orgSettingsService: OrgSettingsService + private orgSettingsService: OrgSettingsService, + private expensesService: ExpensesService ) {} get isSelected(): boolean { @@ -230,7 +232,8 @@ export class ExpensesCardComponent implements OnInit { !that.isScanCompleted && that.transactionOutboxService.isDataExtractionPending(that.expense.tx_id); if (that.isScanInProgress) { that.pollDataExtractionStatus(function () { - that.transactionService.getETxnUnflattened(that.expense.tx_id).subscribe((etxn) => { + that.expensesService.getExpenseById(that.expense.tx_id).subscribe((expense) => { + const etxn = that.transactionService.transformExpense(expense); const extractedData = etxn.tx.extracted_data; if (!!extractedData) { that.isScanCompleted = true;