diff --git a/src/components/KYCWall/BaseKYCWall.tsx b/src/components/KYCWall/BaseKYCWall.tsx index ecad604578c06..8b272119dc87c 100644 --- a/src/components/KYCWall/BaseKYCWall.tsx +++ b/src/components/KYCWall/BaseKYCWall.tsx @@ -55,7 +55,6 @@ function KYCWall({ source, shouldShowPersonalBankAccountOption = false, ref, - currency, }: KYCWallProps) { const [userWallet] = useOnyx(ONYXKEYS.USER_WALLET, {canBeMissing: true}); const [walletTerms] = useOnyx(ONYXKEYS.WALLET_TERMS, {canBeMissing: true}); @@ -124,10 +123,10 @@ function KYCWall({ setPositionAddPaymentMenu(position); }, [getAnchorPosition]); - const canLinkExistingBusinessBankAccount = getEligibleExistingBusinessBankAccounts(bankAccountList, currency, true).length > 0; - const selectPaymentMethod = useCallback( (paymentMethod?: PaymentMethod, policy?: Policy) => { + const canLinkExistingBusinessBankAccount = getEligibleExistingBusinessBankAccounts(bankAccountList, policy?.outputCurrency, true).length > 0; + if (paymentMethod) { onSelectPaymentMethod(paymentMethod); } @@ -201,22 +200,22 @@ function KYCWall({ } }, [ + bankAccountList, onSelectPaymentMethod, iouReport, addDebitCardRoute, reimbursementAccount?.achData?.state, - canLinkExistingBusinessBankAccount, addBankAccountRoute, chatReport, policies, - introSelected, - formatPhoneNumber, - lastPaymentMethod, reportPreviewAction, currentUserEmail, employeeEmail, reportTransactions, isCustomReportNamesBetaEnabled, + introSelected, + formatPhoneNumber, + lastPaymentMethod, ], ); diff --git a/src/components/KYCWall/types.ts b/src/components/KYCWall/types.ts index 80f6be6afbc95..59f3485442d66 100644 --- a/src/components/KYCWall/types.ts +++ b/src/components/KYCWall/types.ts @@ -77,9 +77,6 @@ type KYCWallProps = { /** Reference to the KYCWall component */ ref: ForwardedRef; - - /** Currency associated with the payment */ - currency?: string; }; type KYCWallRef = { diff --git a/src/components/SettlementButton/index.tsx b/src/components/SettlementButton/index.tsx index 29022ecf107c2..f9771f270297d 100644 --- a/src/components/SettlementButton/index.tsx +++ b/src/components/SettlementButton/index.tsx @@ -611,7 +611,6 @@ function SettlementButton({ policy={lastPaymentPolicy} anchorAlignment={kycWallAnchorAlignment} shouldShowPersonalBankAccountOption={shouldShowPersonalBankAccountOption} - currency={currency} > {(triggerKYCFlow, buttonRef) => ( diff --git a/src/libs/actions/Search.ts b/src/libs/actions/Search.ts index e3e0360edb84c..3b04349685495 100644 --- a/src/libs/actions/Search.ts +++ b/src/libs/actions/Search.ts @@ -1145,7 +1145,7 @@ function handleBulkPayItemSelected(params: { showDelegateNoAccessModal, confirmPayment, } = params; - const {paymentType, selectedPolicy, shouldSelectPaymentMethod} = getActivePaymentType(item.key, activeAdminPolicies, latestBankItems); + const {paymentType, selectedPolicy, shouldSelectPaymentMethod} = getActivePaymentType(item.key, activeAdminPolicies, latestBankItems, policy?.id); // Policy id is also a last payment method so we shouldn't early return here for that case. if (!isValidBulkPayOption(item) && !selectedPolicy) { return; diff --git a/src/pages/ReimbursementAccount/ReimbursementAccountPage.tsx b/src/pages/ReimbursementAccount/ReimbursementAccountPage.tsx index 39bebe2feaf72..0894f2a625b84 100644 --- a/src/pages/ReimbursementAccount/ReimbursementAccountPage.tsx +++ b/src/pages/ReimbursementAccount/ReimbursementAccountPage.tsx @@ -415,6 +415,8 @@ function ReimbursementAccountPage({route, policy, isLoadingPolicy, navigation}: case CONST.BANK_ACCOUNT.STEP.VALIDATION: if ([CONST.BANK_ACCOUNT.STATE.VERIFYING, CONST.BANK_ACCOUNT.STATE.SETUP].some((value) => value === achData?.state)) { goToWithdrawalAccountSetupStep(CONST.BANK_ACCOUNT.STEP.ACH_CONTRACT); + } else if (CONST.BANK_ACCOUNT.STATE.PENDING === achData?.state) { + Navigation.closeRHPFlow(); } else { Navigation.goBack(); } diff --git a/src/pages/workspace/ConnectExistingBusinessBankAccountPage.tsx b/src/pages/workspace/ConnectExistingBusinessBankAccountPage.tsx index a34bc943557dd..6979860029306 100644 --- a/src/pages/workspace/ConnectExistingBusinessBankAccountPage.tsx +++ b/src/pages/workspace/ConnectExistingBusinessBankAccountPage.tsx @@ -42,6 +42,7 @@ function ConnectExistingBusinessBankAccountPage({route}: ConnectExistingBusiness if (policyID === undefined) { return; } + const newReimburserEmail = policy?.achAccount?.reimburser ?? policy?.owner ?? ''; setWorkspaceReimbursement({ policyID, diff --git a/tests/unit/PaymentUtilsTest.ts b/tests/unit/PaymentUtilsTest.ts index ebe0dfacbcd9b..cf59236bccdcc 100644 --- a/tests/unit/PaymentUtilsTest.ts +++ b/tests/unit/PaymentUtilsTest.ts @@ -1,9 +1,11 @@ import type {OnyxEntry} from 'react-native-onyx'; +import type {BankAccountMenuItem} from '@components/Search/types'; import Navigation from '@libs/Navigation/Navigation'; -import {handleUnvalidatedAccount} from '@libs/PaymentUtils'; +import {getActivePaymentType, handleUnvalidatedAccount} from '@libs/PaymentUtils'; import CONST from '@src/CONST'; import {calculateWalletTransferBalanceFee} from '@src/libs/PaymentUtils'; import type {Report} from '@src/types/onyx'; +import createRandomPolicy from '../utils/collections/policies'; jest.mock('@libs/Navigation/Navigation', () => ({ navigate: jest.fn(), @@ -58,4 +60,74 @@ describe('PaymentUtils', () => { expect(mockNavigate).toHaveBeenCalledWith(expectedRoute); }); }); + + describe('getActivePaymentType', () => { + const randomPolicyA = createRandomPolicy(1); + const randomPolicyB = createRandomPolicy(2); + const bankItem = { + text: 'Bank Account', + description: 'Test bank', + methodID: 1, + value: CONST.PAYMENT_METHODS.PERSONAL_BANK_ACCOUNT, + } as BankAccountMenuItem; + + it('should return EXPENSIFY payment type when paymentMethod is PERSONAL_BANK_ACCOUNT', () => { + const result = getActivePaymentType(CONST.PAYMENT_METHODS.PERSONAL_BANK_ACCOUNT, [], undefined); + + expect(result.paymentType).toBe(CONST.IOU.PAYMENT_TYPE.EXPENSIFY); + expect(result.shouldSelectPaymentMethod).toBe(true); + expect(result.selectedPolicy).toBeUndefined(); + }); + + it('should return VBBA payment type when paymentMethod is BUSINESS_BANK_ACCOUNT', () => { + const result = getActivePaymentType(CONST.PAYMENT_METHODS.BUSINESS_BANK_ACCOUNT, [], undefined); + + expect(result.paymentType).toBe(CONST.IOU.PAYMENT_TYPE.VBBA); + expect(result.shouldSelectPaymentMethod).toBe(true); + expect(result.selectedPolicy).toBeUndefined(); + }); + + it('should return ELSEWHERE payment type when paymentMethod is DEBIT_CARD', () => { + const result = getActivePaymentType(CONST.PAYMENT_METHODS.DEBIT_CARD, [], undefined); + + expect(result.paymentType).toBe(CONST.IOU.PAYMENT_TYPE.ELSEWHERE); + expect(result.shouldSelectPaymentMethod).toBe(true); + expect(result.selectedPolicy).toBeUndefined(); + }); + + it('should return ELSEWHERE payment type when paymentMethod is undefined', () => { + const result = getActivePaymentType(undefined, [], undefined); + + expect(result.paymentType).toBe(CONST.IOU.PAYMENT_TYPE.ELSEWHERE); + expect(result.shouldSelectPaymentMethod).toBe(false); + expect(result.selectedPolicy).toBeUndefined(); + }); + + it('should set shouldSelectPaymentMethod to true when latestBankItems is not empty', () => { + const result = getActivePaymentType(undefined, [], [bankItem]); + + expect(result.paymentType).toBe(CONST.IOU.PAYMENT_TYPE.ELSEWHERE); + expect(result.shouldSelectPaymentMethod).toBe(true); + }); + + it('should find selectedPolicy by policyID', () => { + const result = getActivePaymentType(undefined, [randomPolicyA, randomPolicyB], undefined, randomPolicyA.id); + + expect(result.selectedPolicy).toEqual(randomPolicyA); + }); + + it('should find selectedPolicy by paymentMethod when it matches policy id (Pay via workspace scenario)', () => { + const result = getActivePaymentType(randomPolicyB.id, [randomPolicyA, randomPolicyB], undefined); + + expect(result.selectedPolicy).toEqual(randomPolicyB); + expect(result.paymentType).toBe(CONST.IOU.PAYMENT_TYPE.ELSEWHERE); + expect(result.shouldSelectPaymentMethod).toBe(false); + }); + + it('should return undefined selectedPolicy when no matching policy is found', () => { + const result = getActivePaymentType(undefined, [randomPolicyA], undefined, 'non-existent-policy'); + + expect(result.selectedPolicy).toBeUndefined(); + }); + }); }); diff --git a/tests/unit/WorkflowUtilsTest.ts b/tests/unit/WorkflowUtilsTest.ts index 285b93caa5315..faa33b3d96507 100644 --- a/tests/unit/WorkflowUtilsTest.ts +++ b/tests/unit/WorkflowUtilsTest.ts @@ -1,11 +1,22 @@ /* eslint-disable @typescript-eslint/naming-convention */ -import * as WorkflowUtils from '@src/libs/WorkflowUtils'; +import CONST from '@src/CONST'; +import { + calculateApprovers, + convertApprovalWorkflowToPolicyEmployees, + convertPolicyEmployeesToApprovalWorkflows, + getApprovalLimitDescription, + getOpenConnectedToPolicyBusinessBankAccounts, + updateWorkflowDataOnApproverRemoval, +} from '@src/libs/WorkflowUtils'; +import type {Policy} from '@src/types/onyx'; import type {Approver, Member} from '@src/types/onyx/ApprovalWorkflow'; import type ApprovalWorkflow from '@src/types/onyx/ApprovalWorkflow'; +import type {BankAccountList} from '@src/types/onyx/BankAccount'; import type {PersonalDetailsList} from '@src/types/onyx/PersonalDetails'; import type {PolicyEmployeeList} from '@src/types/onyx/PolicyEmployee'; import type PolicyEmployee from '@src/types/onyx/PolicyEmployee'; -import * as TestHelper from '../utils/TestHelper'; +import createRandomPolicy from '../utils/collections/policies'; +import {buildPersonalDetails, localeCompare} from '../utils/TestHelper'; const personalDetails: PersonalDetailsList = {}; const personalDetailsByEmail: PersonalDetailsList = {}; @@ -50,7 +61,7 @@ describe('WorkflowUtils', () => { beforeAll(() => { for (let accountID = 0; accountID < 10; accountID++) { const email = `${accountID}@example.com`; - personalDetails[accountID] = TestHelper.buildPersonalDetails(email, accountID, email); + personalDetails[accountID] = buildPersonalDetails(email, accountID, email); personalDetailsByEmail[email] = personalDetails[accountID]; } }); @@ -59,7 +70,7 @@ describe('WorkflowUtils', () => { it('Should return no approvers for empty employees object', () => { const employees: PolicyEmployeeList = {}; const firstEmail = '1@example.com'; - const approvers = WorkflowUtils.calculateApprovers({employees, firstEmail, personalDetailsByEmail}); + const approvers = calculateApprovers({employees, firstEmail, personalDetailsByEmail}); expect(approvers).toEqual([]); }); @@ -76,7 +87,7 @@ describe('WorkflowUtils', () => { }, }; const firstEmail = '1@example.com'; - const approvers = WorkflowUtils.calculateApprovers({employees, firstEmail, personalDetailsByEmail}); + const approvers = calculateApprovers({employees, firstEmail, personalDetailsByEmail}); expect(approvers).toEqual([buildApprover(1)]); }); @@ -93,7 +104,7 @@ describe('WorkflowUtils', () => { }, }; const firstEmail = '1@example.com'; - const approvers = WorkflowUtils.calculateApprovers({employees, firstEmail, personalDetailsByEmail}); + const approvers = calculateApprovers({employees, firstEmail, personalDetailsByEmail}); expect(approvers).toEqual([buildApprover(1)]); }); @@ -122,21 +133,18 @@ describe('WorkflowUtils', () => { }, }; - expect(WorkflowUtils.calculateApprovers({employees, firstEmail: '1@example.com', personalDetailsByEmail})).toEqual([ + expect(calculateApprovers({employees, firstEmail: '1@example.com', personalDetailsByEmail})).toEqual([ buildApprover(1, {forwardsTo: '2@example.com'}), buildApprover(2, {forwardsTo: '3@example.com'}), buildApprover(3, {forwardsTo: '4@example.com'}), buildApprover(4), ]); - expect(WorkflowUtils.calculateApprovers({employees, firstEmail: '2@example.com', personalDetailsByEmail})).toEqual([ + expect(calculateApprovers({employees, firstEmail: '2@example.com', personalDetailsByEmail})).toEqual([ buildApprover(2, {forwardsTo: '3@example.com'}), buildApprover(3, {forwardsTo: '4@example.com'}), buildApprover(4), ]); - expect(WorkflowUtils.calculateApprovers({employees, firstEmail: '3@example.com', personalDetailsByEmail})).toEqual([ - buildApprover(3, {forwardsTo: '4@example.com'}), - buildApprover(4), - ]); + expect(calculateApprovers({employees, firstEmail: '3@example.com', personalDetailsByEmail})).toEqual([buildApprover(3, {forwardsTo: '4@example.com'}), buildApprover(4)]); }); it('Should return a list of approvers with circular references', () => { @@ -163,7 +171,7 @@ describe('WorkflowUtils', () => { }, }; - expect(WorkflowUtils.calculateApprovers({employees, firstEmail: '1@example.com', personalDetailsByEmail})).toEqual([ + expect(calculateApprovers({employees, firstEmail: '1@example.com', personalDetailsByEmail})).toEqual([ buildApprover(1, {forwardsTo: '2@example.com'}), buildApprover(2, {forwardsTo: '3@example.com'}), buildApprover(3, {forwardsTo: '4@example.com'}), @@ -171,7 +179,7 @@ describe('WorkflowUtils', () => { buildApprover(5, {forwardsTo: '1@example.com'}), buildApprover(1, {forwardsTo: '2@example.com', isCircularReference: true}), ]); - expect(WorkflowUtils.calculateApprovers({employees, firstEmail: '2@example.com', personalDetailsByEmail})).toEqual([ + expect(calculateApprovers({employees, firstEmail: '2@example.com', personalDetailsByEmail})).toEqual([ buildApprover(2, {forwardsTo: '3@example.com'}), buildApprover(3, {forwardsTo: '4@example.com'}), buildApprover(4, {forwardsTo: '5@example.com'}), @@ -189,7 +197,7 @@ describe('WorkflowUtils', () => { }, }; - expect(WorkflowUtils.calculateApprovers({employees, firstEmail: '1@example.com', personalDetailsByEmail})).toEqual([ + expect(calculateApprovers({employees, firstEmail: '1@example.com', personalDetailsByEmail})).toEqual([ buildApprover(1, {forwardsTo: '1@example.com'}), buildApprover(1, {forwardsTo: '1@example.com', isCircularReference: true}), ]); @@ -215,7 +223,7 @@ describe('WorkflowUtils', () => { }, }; - const approvers = WorkflowUtils.calculateApprovers({employees, firstEmail: '1@example.com', personalDetailsByEmail}); + const approvers = calculateApprovers({employees, firstEmail: '1@example.com', personalDetailsByEmail}); expect(approvers).toEqual([ buildApprover(1, {forwardsTo: '2@example.com', approvalLimit: 50000, overLimitForwardsTo: '3@example.com'}), @@ -233,7 +241,7 @@ describe('WorkflowUtils', () => { }, }; - const approvers = WorkflowUtils.calculateApprovers({employees, firstEmail: '1@example.com', personalDetailsByEmail}); + const approvers = calculateApprovers({employees, firstEmail: '1@example.com', personalDetailsByEmail}); expect(approvers).toEqual([buildApprover(1, {approvalLimit: null, overLimitForwardsTo: ''})]); }); @@ -257,7 +265,7 @@ describe('WorkflowUtils', () => { const defaultApprover = '1@example.com'; const policy = createMockPolicy(employees, defaultApprover); - const {approvalWorkflows} = WorkflowUtils.convertPolicyEmployeesToApprovalWorkflows({policy, personalDetails, localeCompare: TestHelper.localeCompare}); + const {approvalWorkflows} = convertPolicyEmployeesToApprovalWorkflows({policy, personalDetails, localeCompare}); expect(approvalWorkflows).toEqual([]); }); @@ -273,7 +281,7 @@ describe('WorkflowUtils', () => { const defaultApprover = '1@example.com'; const policy = createMockPolicy(employees, defaultApprover); - const {approvalWorkflows} = WorkflowUtils.convertPolicyEmployeesToApprovalWorkflows({policy, personalDetails, localeCompare: TestHelper.localeCompare}); + const {approvalWorkflows} = convertPolicyEmployeesToApprovalWorkflows({policy, personalDetails, localeCompare}); expect(approvalWorkflows).toEqual([]); }); @@ -294,7 +302,7 @@ describe('WorkflowUtils', () => { const defaultApprover = '1@example.com'; const policy = createMockPolicy(employees, defaultApprover); - const {approvalWorkflows} = WorkflowUtils.convertPolicyEmployeesToApprovalWorkflows({policy, personalDetails, localeCompare: TestHelper.localeCompare}); + const {approvalWorkflows} = convertPolicyEmployeesToApprovalWorkflows({policy, personalDetails, localeCompare}); expect(approvalWorkflows).toEqual([buildWorkflow([1, 2], [1], {isDefault: true})]); }); @@ -325,7 +333,7 @@ describe('WorkflowUtils', () => { const defaultApprover = '1@example.com'; const policy = createMockPolicy(employees, defaultApprover); - const {approvalWorkflows} = WorkflowUtils.convertPolicyEmployeesToApprovalWorkflows({policy, personalDetails, localeCompare: TestHelper.localeCompare}); + const {approvalWorkflows} = convertPolicyEmployeesToApprovalWorkflows({policy, personalDetails, localeCompare}); expect(approvalWorkflows).toEqual([buildWorkflow([2, 3], [1], {isDefault: true}), buildWorkflow([1, 4], [4])]); }); @@ -361,7 +369,7 @@ describe('WorkflowUtils', () => { const defaultApprover = '1@example.com'; const policy = createMockPolicy(employees, defaultApprover); - const {approvalWorkflows} = WorkflowUtils.convertPolicyEmployeesToApprovalWorkflows({policy, personalDetails, localeCompare: TestHelper.localeCompare}); + const {approvalWorkflows} = convertPolicyEmployeesToApprovalWorkflows({policy, personalDetails, localeCompare}); expect(approvalWorkflows).toEqual([buildWorkflow([3, 2], [1], {isDefault: true}), buildWorkflow([5], [3]), buildWorkflow([4, 1], [4])]); }); @@ -392,7 +400,7 @@ describe('WorkflowUtils', () => { const defaultApprover = '1@example.com'; const policy = createMockPolicy(employees, defaultApprover); - const {approvalWorkflows} = WorkflowUtils.convertPolicyEmployeesToApprovalWorkflows({policy, personalDetails, localeCompare: TestHelper.localeCompare}); + const {approvalWorkflows} = convertPolicyEmployeesToApprovalWorkflows({policy, personalDetails, localeCompare}); const defaultWorkflow = buildWorkflow([2, 3, 4], [1, 3, 4], {isDefault: true}); let firstApprover = defaultWorkflow.approvers.at(0); @@ -448,7 +456,7 @@ describe('WorkflowUtils', () => { const defaultApprover = '1@example.com'; const policy = createMockPolicy(employees, defaultApprover); - const {approvalWorkflows} = WorkflowUtils.convertPolicyEmployeesToApprovalWorkflows({policy, personalDetails, localeCompare: TestHelper.localeCompare}); + const {approvalWorkflows} = convertPolicyEmployeesToApprovalWorkflows({policy, personalDetails, localeCompare}); const defaultWorkflow = buildWorkflow([1, 4, 5, 6], [1], {isDefault: true}); const secondWorkflow = buildWorkflow([2, 3], [4, 5, 6]); @@ -471,7 +479,7 @@ describe('WorkflowUtils', () => { isDefault: true, }; - const convertedEmployees = WorkflowUtils.convertApprovalWorkflowToPolicyEmployees({previousEmployeeList: {}, approvalWorkflow, type: 'create'}); + const convertedEmployees = convertApprovalWorkflowToPolicyEmployees({previousEmployeeList: {}, approvalWorkflow, type: 'create'}); expect(convertedEmployees).toEqual({ '1@example.com': buildPolicyEmployee(1, {forwardsTo: '', overLimitForwardsTo: '', submitsTo: '1@example.com', pendingFields: {submitsTo: 'add'}}), @@ -486,7 +494,7 @@ describe('WorkflowUtils', () => { isDefault: false, }; - const convertedEmployees = WorkflowUtils.convertApprovalWorkflowToPolicyEmployees({previousEmployeeList: {}, approvalWorkflow, type: 'create'}); + const convertedEmployees = convertApprovalWorkflowToPolicyEmployees({previousEmployeeList: {}, approvalWorkflow, type: 'create'}); expect(convertedEmployees).toEqual({ '1@example.com': buildPolicyEmployee(1, {forwardsTo: '2@example.com', overLimitForwardsTo: '', pendingFields: {forwardsTo: 'add', overLimitForwardsTo: 'add'}}), @@ -505,7 +513,7 @@ describe('WorkflowUtils', () => { isDefault: false, }; - const convertedEmployees = WorkflowUtils.convertApprovalWorkflowToPolicyEmployees({previousEmployeeList: {}, approvalWorkflow, type: 'remove'}); + const convertedEmployees = convertApprovalWorkflowToPolicyEmployees({previousEmployeeList: {}, approvalWorkflow, type: 'remove'}); expect(convertedEmployees).toEqual({ '1@example.com': buildPolicyEmployee(1, { @@ -542,7 +550,7 @@ describe('WorkflowUtils', () => { isDefault: false, }; - const convertedEmployees = WorkflowUtils.convertApprovalWorkflowToPolicyEmployees({previousEmployeeList: {}, approvalWorkflow, type: 'create'}); + const convertedEmployees = convertApprovalWorkflowToPolicyEmployees({previousEmployeeList: {}, approvalWorkflow, type: 'create'}); expect(convertedEmployees).toEqual({ '1@example.com': buildPolicyEmployee(1, { @@ -570,7 +578,7 @@ describe('WorkflowUtils', () => { isDefault: false, }; - const convertedEmployees = WorkflowUtils.convertApprovalWorkflowToPolicyEmployees({previousEmployeeList, approvalWorkflow, type: 'remove'}); + const convertedEmployees = convertApprovalWorkflowToPolicyEmployees({previousEmployeeList, approvalWorkflow, type: 'remove'}); // approvalLimit should be null (not undefined) so it gets sent to the API and clears the field expect(convertedEmployees['1@example.com']?.approvalLimit).toBeNull(); @@ -592,7 +600,7 @@ describe('WorkflowUtils', () => { isDefault: false, }; - const convertedEmployees = WorkflowUtils.convertApprovalWorkflowToPolicyEmployees({previousEmployeeList, approvalWorkflow, type: 'update'}); + const convertedEmployees = convertApprovalWorkflowToPolicyEmployees({previousEmployeeList, approvalWorkflow, type: 'update'}); // pendingFields should include the fields that changed (forwardsTo didn't change since it's '' -> '') expect(convertedEmployees['1@example.com']?.pendingFields).toEqual({ @@ -618,7 +626,7 @@ describe('WorkflowUtils', () => { isDefault: false, }; - const convertedEmployees = WorkflowUtils.convertApprovalWorkflowToPolicyEmployees({previousEmployeeList, approvalWorkflow, type: 'update'}); + const convertedEmployees = convertApprovalWorkflowToPolicyEmployees({previousEmployeeList, approvalWorkflow, type: 'update'}); // Only overLimitForwardsTo changed, so only that should be in pendingFields expect(convertedEmployees['1@example.com']?.pendingFields).toEqual({ @@ -647,13 +655,13 @@ describe('WorkflowUtils', () => { return; } - const updateWorkflowDataOnApproverRemoval = WorkflowUtils.updateWorkflowDataOnApproverRemoval({ + const updateWorkflowDataOnApproverRemovalResult = updateWorkflowDataOnApproverRemoval({ approvalWorkflows: [approvalWorkflow1, approvalWorkflow2], removedApprover, ownerDetails, }); - expect(updateWorkflowDataOnApproverRemoval).toEqual([approvalWorkflow1, {...approvalWorkflow2, removeApprovalWorkflow: true}]); + expect(updateWorkflowDataOnApproverRemovalResult).toEqual([approvalWorkflow1, {...approvalWorkflow2, removeApprovalWorkflow: true}]); }); it('Should replace the approvers in Workflow 2 with the Workspace Owner if it has no approvers and the approver in Workspace (default) is different from the Workspace Owner', () => { const approvalWorkflow1: ApprovalWorkflow = { @@ -674,13 +682,13 @@ describe('WorkflowUtils', () => { return; } - const updateWorkflowDataOnApproverRemoval = WorkflowUtils.updateWorkflowDataOnApproverRemoval({ + const updateWorkflowDataOnApproverRemovalResult = updateWorkflowDataOnApproverRemoval({ approvalWorkflows: [approvalWorkflow1, approvalWorkflow2], removedApprover, ownerDetails, }); - expect(updateWorkflowDataOnApproverRemoval).toEqual([approvalWorkflow1, {...approvalWorkflow2, approvers: [buildApprover(1)]}]); + expect(updateWorkflowDataOnApproverRemovalResult).toEqual([approvalWorkflow1, {...approvalWorkflow2, approvers: [buildApprover(1)]}]); }); it('Should remove Workflow 2 if its approver is the Workspace Owner and the default Workspace approver is removed.', () => { const approvalWorkflow1: ApprovalWorkflow = { @@ -701,13 +709,13 @@ describe('WorkflowUtils', () => { return; } - const updateWorkflowDataOnApproverRemoval = WorkflowUtils.updateWorkflowDataOnApproverRemoval({ + const updateWorkflowDataOnApproverRemovalResult = updateWorkflowDataOnApproverRemoval({ approvalWorkflows: [approvalWorkflow1, approvalWorkflow2], removedApprover, ownerDetails, }); - expect(updateWorkflowDataOnApproverRemoval).toEqual([ + expect(updateWorkflowDataOnApproverRemovalResult).toEqual([ {...approvalWorkflow1, approvers: [buildApprover(1)]}, {...approvalWorkflow2, removeApprovalWorkflow: true}, ]); @@ -731,13 +739,13 @@ describe('WorkflowUtils', () => { return; } - const updateWorkflowDataOnApproverRemoval = WorkflowUtils.updateWorkflowDataOnApproverRemoval({ + const updateWorkflowDataOnApproverRemovalResult = updateWorkflowDataOnApproverRemoval({ approvalWorkflows: [approvalWorkflow1, approvalWorkflow2], removedApprover, ownerDetails, }); - expect(updateWorkflowDataOnApproverRemoval).toEqual([approvalWorkflow1, {...approvalWorkflow2, approvers: [buildApprover(2), buildApprover(3), buildApprover(1)]}]); + expect(updateWorkflowDataOnApproverRemovalResult).toEqual([approvalWorkflow1, {...approvalWorkflow2, approvers: [buildApprover(2), buildApprover(3), buildApprover(1)]}]); }); it('Should remove the approvers that have submitsTo set to the removed approver, update the removed approver to the Workspace Owner, and ensure there was a previous approver before this one', () => { const approvalWorkflow1: ApprovalWorkflow = { @@ -758,13 +766,13 @@ describe('WorkflowUtils', () => { return; } - const updateWorkflowDataOnApproverRemoval = WorkflowUtils.updateWorkflowDataOnApproverRemoval({ + const updateWorkflowDataOnApproverRemovalResult = updateWorkflowDataOnApproverRemoval({ approvalWorkflows: [approvalWorkflow1, approvalWorkflow2], removedApprover, ownerDetails, }); - expect(updateWorkflowDataOnApproverRemoval).toEqual([approvalWorkflow1, {...approvalWorkflow2, approvers: [buildApprover(2), buildApprover(1)]}]); + expect(updateWorkflowDataOnApproverRemovalResult).toEqual([approvalWorkflow1, {...approvalWorkflow2, approvers: [buildApprover(2), buildApprover(1)]}]); }); it('Should remove Workflow 2 if it has no approvers and the default Workspace approver is the approve', () => { const approvalWorkflow1: ApprovalWorkflow = { @@ -785,13 +793,13 @@ describe('WorkflowUtils', () => { return; } - const updateWorkflowDataOnApproverRemoval = WorkflowUtils.updateWorkflowDataOnApproverRemoval({ + const updateWorkflowDataOnApproverRemovalResult = updateWorkflowDataOnApproverRemoval({ approvalWorkflows: [approvalWorkflow1, approvalWorkflow2], removedApprover, ownerDetails, }); - expect(updateWorkflowDataOnApproverRemoval).toEqual([approvalWorkflow1, {...approvalWorkflow2, removeApprovalWorkflow: true}]); + expect(updateWorkflowDataOnApproverRemovalResult).toEqual([approvalWorkflow1, {...approvalWorkflow2, removeApprovalWorkflow: true}]); }); }); @@ -808,10 +816,10 @@ describe('WorkflowUtils', () => { }); it('Should return undefined when approver is undefined', () => { - const result = WorkflowUtils.getApprovalLimitDescription({ + const result = getApprovalLimitDescription({ approver: undefined, currency: 'USD', - translate: mockTranslate as unknown as Parameters[0]['translate'], + translate: mockTranslate as unknown as Parameters[0]['translate'], personalDetailsByEmail: {}, }); @@ -821,10 +829,10 @@ describe('WorkflowUtils', () => { it('Should return undefined when approvalLimit is null', () => { const approver = buildApprover(1, {approvalLimit: null, overLimitForwardsTo: '2@example.com'}); - const result = WorkflowUtils.getApprovalLimitDescription({ + const result = getApprovalLimitDescription({ approver, currency: 'USD', - translate: mockTranslate as unknown as Parameters[0]['translate'], + translate: mockTranslate as unknown as Parameters[0]['translate'], personalDetailsByEmail: {}, }); @@ -834,10 +842,10 @@ describe('WorkflowUtils', () => { it('Should return undefined when approvalLimit is undefined', () => { const approver = buildApprover(1, {approvalLimit: undefined, overLimitForwardsTo: '2@example.com'}); - const result = WorkflowUtils.getApprovalLimitDescription({ + const result = getApprovalLimitDescription({ approver, currency: 'USD', - translate: mockTranslate as unknown as Parameters[0]['translate'], + translate: mockTranslate as unknown as Parameters[0]['translate'], personalDetailsByEmail: {}, }); @@ -847,10 +855,10 @@ describe('WorkflowUtils', () => { it('Should return undefined when overLimitForwardsTo is missing', () => { const approver = buildApprover(1, {approvalLimit: 50000, overLimitForwardsTo: undefined}); - const result = WorkflowUtils.getApprovalLimitDescription({ + const result = getApprovalLimitDescription({ approver, currency: 'USD', - translate: mockTranslate as unknown as Parameters[0]['translate'], + translate: mockTranslate as unknown as Parameters[0]['translate'], personalDetailsByEmail: {}, }); @@ -860,10 +868,10 @@ describe('WorkflowUtils', () => { it('Should return description when approvalLimit and overLimitForwardsTo are set', () => { const approver = buildApprover(1, {approvalLimit: 50000, overLimitForwardsTo: '2@example.com'}); - const result = WorkflowUtils.getApprovalLimitDescription({ + const result = getApprovalLimitDescription({ approver, currency: 'USD', - translate: mockTranslate as unknown as Parameters[0]['translate'], + translate: mockTranslate as unknown as Parameters[0]['translate'], personalDetailsByEmail: {}, }); @@ -876,14 +884,146 @@ describe('WorkflowUtils', () => { '2@example.com': {accountID: 2, displayName: 'John Doe'}, }; - const result = WorkflowUtils.getApprovalLimitDescription({ + const result = getApprovalLimitDescription({ approver, currency: 'USD', - translate: mockTranslate as unknown as Parameters[0]['translate'], + translate: mockTranslate as unknown as Parameters[0]['translate'], personalDetailsByEmail: personalDetailsWithEmail, }); expect(result).toBe('Reports above $1,000.00 forward to John Doe'); }); }); + + describe('getOpenConnectedToPolicyBusinessBankAccounts', () => { + const matchingBankAccountID = 12345; + + const policyWithACH = { + ...createRandomPolicy(1), + outputCurrency: 'USD', + achAccount: { + bankAccountID: matchingBankAccountID, + }, + } as Policy; + + const openBusinessBankAccount = { + bankCurrency: 'USD', + bankCountry: 'US', + accountData: { + state: CONST.BANK_ACCOUNT.STATE.OPEN, + type: CONST.BANK_ACCOUNT.TYPE.BUSINESS, + bankAccountID: matchingBankAccountID, + }, + }; + + it('should return empty array when bankAccountList is undefined', () => { + const result = getOpenConnectedToPolicyBusinessBankAccounts(undefined, policyWithACH); + + expect(result).toEqual([]); + }); + + it('should return empty array when policy is undefined', () => { + const bankAccountList: BankAccountList = { + '1': openBusinessBankAccount, + }; + + const result = getOpenConnectedToPolicyBusinessBankAccounts(bankAccountList, undefined); + + expect(result).toEqual([]); + }); + + it('should return matching bank accounts that meet all criteria', () => { + const bankAccountList: BankAccountList = { + '1': openBusinessBankAccount, + }; + + const result = getOpenConnectedToPolicyBusinessBankAccounts(bankAccountList, policyWithACH); + + expect(result).toEqual([openBusinessBankAccount]); + }); + + it('should filter out accounts with non-matching currency', () => { + const nonMatchingCurrencyAccount = { + ...openBusinessBankAccount, + bankCurrency: 'EUR', + }; + const bankAccountList: BankAccountList = { + '1': nonMatchingCurrencyAccount, + }; + + const result = getOpenConnectedToPolicyBusinessBankAccounts(bankAccountList, policyWithACH); + + expect(result).toEqual([]); + }); + + it('should filter out accounts that are not in OPEN state', () => { + const pendingAccount = { + ...openBusinessBankAccount, + accountData: { + ...openBusinessBankAccount.accountData, + state: CONST.BANK_ACCOUNT.STATE.PENDING, + }, + }; + const bankAccountList: BankAccountList = { + '1': pendingAccount, + }; + + const result = getOpenConnectedToPolicyBusinessBankAccounts(bankAccountList, policyWithACH); + + expect(result).toEqual([]); + }); + + it('should filter out accounts that are not BUSINESS type', () => { + const personalAccount = { + ...openBusinessBankAccount, + accountData: { + ...openBusinessBankAccount.accountData, + type: CONST.BANK_ACCOUNT.TYPE.PERSONAL, + }, + }; + const bankAccountList: BankAccountList = { + '1': personalAccount, + }; + + const result = getOpenConnectedToPolicyBusinessBankAccounts(bankAccountList, policyWithACH); + + expect(result).toEqual([]); + }); + + it('should filter out accounts not linked to policy ACH account', () => { + const unlinkedAccount = { + ...openBusinessBankAccount, + accountData: { + ...openBusinessBankAccount.accountData, + bankAccountID: 99999, + }, + }; + const bankAccountList: BankAccountList = { + '1': unlinkedAccount, + }; + + const result = getOpenConnectedToPolicyBusinessBankAccounts(bankAccountList, policyWithACH); + + expect(result).toEqual([]); + }); + + it('should return multiple matching accounts', () => { + const secondMatchingAccount = { + ...openBusinessBankAccount, + accountData: { + ...openBusinessBankAccount.accountData, + bankAccountID: matchingBankAccountID, + accountNumber: '9999', + }, + }; + const bankAccountList: BankAccountList = { + '1': openBusinessBankAccount, + '2': secondMatchingAccount, + }; + + const result = getOpenConnectedToPolicyBusinessBankAccounts(bankAccountList, policyWithACH); + + expect(result).toEqual([openBusinessBankAccount, secondMatchingAccount]); + }); + }); });