Skip to content

Commit

Permalink
fix: migrate expense suggestions to platform (#3298)
Browse files Browse the repository at this point in the history
Co-authored-by: Arjun <[email protected]>
  • Loading branch information
sumrender and arjunaj5 authored Dec 9, 2024
1 parent 3c8879f commit e763f17
Show file tree
Hide file tree
Showing 9 changed files with 342 additions and 68 deletions.
218 changes: 218 additions & 0 deletions src/app/core/mock-data/personal-card-txn-expense-suggestions.data.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
import deepFreeze from 'deep-freeze-strict';
import { PersonalCardTxnExpenseSuggestion } from '../models/personal-card-txn-expense-suggestion.model';

export const platformPersonalCardTxnExpenseSuggestionsRes = deepFreeze({
data: [
{
accounting_export_summary: {},
activity_details: null,
added_to_report_at: null,
admin_amount: null,
advance_wallet_id: null,
amount: 90,
approvals: [
{
approver_user: {
email: '[email protected]',
full_name: 'aastha',
id: 'usRjTPO4r69K',
},
approver_user_id: 'usRjTPO4r69K',
state: 'APPROVAL_PENDING',
},
],
approver_comments: [],
category: {
code: null,
display_name: 'Unspecified',
id: 38927,
name: 'Unspecified',
sub_category: null,
system_category: 'Unspecified',
},
category_id: 38927,
claim_amount: 90,
code: null,
commute_deduction: null,
commute_details: null,
commute_details_id: null,
cost_center: {
code: null,
id: 6335,
name: 'test-1',
},
cost_center_id: 6335,
created_at: '2024-10-24T10:34:31.922365+00:00',
creator_user_id: 'us2KhpQLpzX4',
currency: 'USD',
custom_fields: [],
custom_fields_flattened: {
dependent_field_1: null,
pp1: null,
project_field: null,
},
distance: null,
distance_unit: null,
employee: {
business_unit: null,
code: null,
custom_fields: [
{
name: 'Account Number',
value: 123,
},
],
department: {
code: null,
display_name: '1234',
id: 'deptfOJFLA8wxt',
name: '1234',
sub_department: null,
},
department_id: 'deptfOJFLA8wxt',
flattened_custom_field: {
account_number: 123,
},
has_accepted_invite: true,
id: 'ou6kAM3CXV7d',
is_enabled: true,
joined_at: null,
level: null,
location: null,
mobile: '+12512603805',
org_id: 'orrb8EW1zZsy',
org_name: 'CCC - Multiple statement support - USD',
title: null,
user: {
email: '[email protected]',
full_name: 'Kavya 2',
id: 'us2KhpQLpzX4',
},
user_id: 'us2KhpQLpzX4',
},
employee_id: 'ou6kAM3CXV7d',
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: 'txhoujHIA4OD',
invoice_number: null,
is_billable: true,
is_corporate_card_transaction_auto_matched: false,
is_duplicate_present: 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,
last_exported_at: null,
last_settled_at: null,
last_verified_at: null,
locations: [],
matched_corporate_card_transaction_ids: [],
matched_corporate_card_transactions: [],
merchant: 'UTILITY BILL PAYMENT',
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: true,
},
org_id: 'orrb8EW1zZsy',
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: 'asdf',
id: 305743,
name: 'asdf',
sub_project: null,
},
project_id: 305743,
purpose: 'UTILITY BILL PAYMENT',
report: null,
report_id: null,
report_last_approved_at: null,
report_last_paid_at: null,
seq_num: 'E/2024/10/T/21',
source: 'WEBAPP',
source_account: {
id: 'acc8vyjNsN3zh',
type: 'PERSONAL_CASH_ACCOUNT',
},
source_account_id: 'acc8vyjNsN3zh',
spent_at: new Date('2024-09-11T00:00:00+00:00'),
split_group_amount: null,
split_group_id: 'txhoujHIA4OD',
started_at: null,
state: 'DRAFT',
state_display_name: 'Incomplete',
tax_amount: 16.83,
tax_group: {
name: 'GST',
percentage: 0.23,
},
tax_group_id: 'tg6RDX1flCnt',
travel_classes: [],
updated_at: '2024-10-24T10:34:36.313431+00:00',
user: {
email: '[email protected]',
full_name: 'Kavya 2',
id: 'us2KhpQLpzX4',
},
user_id: 'us2KhpQLpzX4',
verifications: [],
verifier_comments: [],
},
],
});

export const platformPersonalCardTxnExpenseSuggestions = deepFreeze([
{
purpose: 'UTILITY BILL PAYMENT',
vendor: 'UTILITY BILL PAYMENT',
txn_dt: new Date('2024-09-11T00:00:00+00:00'),
currency: 'USD',
amount: 90,
split_group_id: 'txhoujHIA4OD',
},
]);

export const publicPersonalCardTxnExpenseSuggestionsRes: PersonalCardTxnExpenseSuggestion[] = deepFreeze([
{
id: 'txsZt7eBrWHN',
txn_dt: new Date('2024-09-14T00:00:00.000Z'),
amount: 20,
currency: 'USD',
split_group_id: 'txsZt7eBrWHN',
split_group_user_amount: 20,
orig_amount: null,
orig_currency: null,
purpose: 'sumrender ONLINE SUBSCRIPTION',
vendor: 'ONLINE SUBSCRIPTION',
ranking: 1,
distance: 0.0,
},
]);
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export interface PersonalCardTxnExpenseSuggestion {
purpose: string;
vendor: string;
txn_dt: Date;
currency: string;
amount: number;
split_group_id: string;
}
50 changes: 29 additions & 21 deletions src/app/core/services/personal-cards.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@ import * as dayjs from 'dayjs';
import { SpenderPlatformV1ApiService } from './spender-platform-v1-api.service';
import { PlatformPersonalCardQueryParams } from '../models/platform/platform-personal-card-query-params.model';
import { personalCardAccessTokenResponse } from '../mock-data/personal-cards-access-token.data';
import {
platformPersonalCardTxnExpenseSuggestions,
platformPersonalCardTxnExpenseSuggestionsRes,
publicPersonalCardTxnExpenseSuggestionsRes,
} from '../mock-data/personal-card-txn-expense-suggestions.data';

describe('PersonalCardsService', () => {
let personalCardsService: PersonalCardsService;
Expand Down Expand Up @@ -1423,34 +1428,37 @@ describe('PersonalCardsService', () => {
});
});

it('getMatchedExpenses(): should get matched expenses', (done) => {
apiService.get.and.returnValue(of(apiExpenseRes));
describe('getMatchedExpensesSuggestions()', () => {
it('should get expense suggestions using public api', (done) => {
apiService.get.and.returnValue(of(publicPersonalCardTxnExpenseSuggestionsRes));

const amount = 3;
const txnDate = '2021-07-29T06:30:00.000Z';
const amount = 3;
const txnDate = '2021-07-29T06:30:00.000Z';
const usePlatformApi = false;

personalCardsService.getMatchedExpenses(amount, txnDate).subscribe((res) => {
expect(res).toEqual(apiExpenseRes);
expect(apiService.get).toHaveBeenCalledOnceWith('/expense_suggestions/personal_cards', {
params: {
amount,
txn_dt: txnDate,
},
personalCardsService.getMatchedExpensesSuggestions(amount, txnDate, usePlatformApi).subscribe((res) => {
expect(res).toEqual(publicPersonalCardTxnExpenseSuggestionsRes);
expect(apiService.get).toHaveBeenCalledOnceWith('/expense_suggestions/personal_cards', {
params: {
amount,
txn_dt: txnDate,
},
});
done();
});
done();
});
});

it('getMatchedExpensesCount(): should get matched expenses count', (done) => {
spyOn(personalCardsService, 'getMatchedExpenses').and.returnValue(of(apiExpenseRes));
it('should get expense suggestions using platform api', (done) => {
spenderPlatformV1ApiService.get.and.returnValue(of(platformPersonalCardTxnExpenseSuggestionsRes));

const amount = 3;
const txnDate = '2021-07-29T06:30:00.000Z';
const amount = 3;
const txnDate = '2021-07-29T06:30:00.000Z';
const usePlatformApi = true;

personalCardsService.getMatchedExpensesCount(amount, txnDate).subscribe((res) => {
expect(res).toEqual(apiExpenseRes.length);
expect(personalCardsService.getMatchedExpenses).toHaveBeenCalledOnceWith(amount, txnDate);
done();
personalCardsService.getMatchedExpensesSuggestions(amount, txnDate, usePlatformApi).subscribe((res) => {
expect(res).toEqual(platformPersonalCardTxnExpenseSuggestions);
done();
});
});
});

Expand Down
44 changes: 38 additions & 6 deletions src/app/core/services/personal-cards.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { PersonalCardFilter } from '../models/personal-card-filters.model';
import { ApiV2Service } from './api-v2.service';
import { ApiService } from './api.service';
import { ExpenseAggregationService } from './expense-aggregation.service';
import { Expense } from '../models/expense.model';
import { Expense } from '../models/platform/v1/expense.model';
import { DateService } from './date.service';
import { SelectedFilters } from 'src/app/shared/components/fy-filters/selected-filters.interface';
import { DateFilters } from 'src/app/shared/components/fy-filters/date-filters.enum';
Expand All @@ -26,6 +26,7 @@ import { TxnDetail } from '../models/v2/txn-detail.model';
import { PlatformPersonalCardQueryParams } from '../models/platform/platform-personal-card-query-params.model';
import { PersonalCardSyncTxns } from '../models/platform/platform-personal-card-syn-txns.model';
import { environment } from 'src/environments/environment';
import { PersonalCardTxnExpenseSuggestion } from '../models/personal-card-txn-expense-suggestion.model';

@Injectable({
providedIn: 'root',
Expand Down Expand Up @@ -107,6 +108,20 @@ export class PersonalCardsService {
});
}

transformPlatformPersonalCardTxnExpenseSuggestions(expenses: Expense[]): PersonalCardTxnExpenseSuggestion[] {
return expenses.map((expense) => {
const expenseSuggestion: PersonalCardTxnExpenseSuggestion = {
purpose: expense.purpose,
vendor: expense.merchant,
txn_dt: expense.spent_at,
currency: expense.currency,
amount: expense.amount,
split_group_id: expense.split_group_id,
};
return expenseSuggestion;
});
}

mapPublicQueryParamsToPlatform(queryParams: {
btxn_status?: string;
ba_id?: string;
Expand Down Expand Up @@ -223,7 +238,28 @@ export class PersonalCardsService {
.pipe(map((res) => res.count));
}

getMatchedExpenses(amount: number, txnDate: string): Observable<Expense[]> {
getMatchedExpensesSuggestionsPlatform(
amount: number,
txnDate: string
): Observable<PersonalCardTxnExpenseSuggestion[]> {
return this.spenderPlatformV1ApiService
.get<PlatformApiResponse<Expense[]>>('/personal_card_transactions/expense_suggestion', {
params: {
amount,
spent_at: txnDate,
},
})
.pipe(map((res) => this.transformPlatformPersonalCardTxnExpenseSuggestions(res.data)));
}

getMatchedExpensesSuggestions(
amount: number,
txnDate: string,
usePlatformApi: boolean
): Observable<PersonalCardTxnExpenseSuggestion[]> {
if (usePlatformApi) {
return this.getMatchedExpensesSuggestionsPlatform(amount, txnDate);
}
return this.apiService.get('/expense_suggestions/personal_cards', {
params: {
amount,
Expand Down Expand Up @@ -321,10 +357,6 @@ export class PersonalCardsService {
});
}

getMatchedExpensesCount(amount: number, txnDate: string): Observable<number> {
return this.getMatchedExpenses(amount, txnDate).pipe(map((res) => res.length));
}

matchExpensePlatform(
transactionSplitGroupId: string,
externalExpenseId: string
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,8 @@
<div class="matched-expenses--action-content">
<div class="matched-expenses--action-title">SELECT A MATCHING EXPENSE</div>
<div class="matched-expenses--expenses-preview">
<ng-container *ngIf="matchedExpenses$| async as matchedExpenses">
<ng-container *ngFor="let expense of matchedExpenses; let first = first;">
<ng-container *ngIf="expenseSuggestions && expenseSuggestions.length > 0">
<ng-container *ngFor="let expense of expenseSuggestions; let first = first;">
<div class="matched-expenses--divider" *ngIf="!first"></div>

<app-expense-card-lite [expense]="expense" (click)="openExpensePreview(expense.split_group_id)">
Expand Down
Loading

0 comments on commit e763f17

Please sign in to comment.