diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts
index ab1332db0b2d..49deee5d0731 100755
--- a/src/ONYXKEYS.ts
+++ b/src/ONYXKEYS.ts
@@ -828,6 +828,18 @@ const ONYXKEYS = {
/** The selected accounting integration bank account ID for card reconciliation */
EXPENSIFY_CARD_RECONCILIATION_BANK_ACCOUNT_ID: 'expensifyCard_bankAccount_',
+ /** Stores which connection is set up to use Travel Invoicing Continuous Reconciliation */
+ TRAVEL_INVOICING_CONTINUOUS_RECONCILIATION_CONNECTION: 'travelInvoicing_continuousReconciliationConnection_',
+
+ /** The value that indicates whether Travel Invoicing Continuous Reconciliation should be used on the workspace */
+ TRAVEL_INVOICING_USE_CONTINUOUS_RECONCILIATION: 'travelInvoicing_useContinuousReconciliation_',
+
+ /** Pending action for Travel Invoicing Continuous Reconciliation enabled status */
+ TRAVEL_INVOICING_USE_CONTINUOUS_RECONCILIATION_PENDING_ACTION: 'travelInvoicing_useContinuousReconciliationPendingAction_',
+
+ /** The selected accounting integration bank account ID for Travel Invoicing reconciliation */
+ TRAVEL_INVOICING_RECONCILIATION_BANK_ACCOUNT_ID: 'travelInvoicing_reconciliationBankAccount_',
+
/** Currently displaying feed */
LAST_SELECTED_FEED: 'lastSelectedFeed_',
@@ -1344,6 +1356,10 @@ type OnyxCollectionValuesMapping = {
[ONYXKEYS.COLLECTION.EXPENSIFY_CARD_USE_CONTINUOUS_RECONCILIATION]: boolean | string;
[ONYXKEYS.COLLECTION.EXPENSIFY_CARD_USE_CONTINUOUS_RECONCILIATION_PENDING_ACTION]: OnyxTypes.CardContinuousReconciliation;
[ONYXKEYS.COLLECTION.EXPENSIFY_CARD_RECONCILIATION_BANK_ACCOUNT_ID]: string;
+ [ONYXKEYS.COLLECTION.TRAVEL_INVOICING_CONTINUOUS_RECONCILIATION_CONNECTION]: OnyxTypes.PolicyConnectionName;
+ [ONYXKEYS.COLLECTION.TRAVEL_INVOICING_USE_CONTINUOUS_RECONCILIATION]: boolean;
+ [ONYXKEYS.COLLECTION.TRAVEL_INVOICING_USE_CONTINUOUS_RECONCILIATION_PENDING_ACTION]: OnyxTypes.CardContinuousReconciliation;
+ [ONYXKEYS.COLLECTION.TRAVEL_INVOICING_RECONCILIATION_BANK_ACCOUNT_ID]: string;
[ONYXKEYS.COLLECTION.LAST_SELECTED_FEED]: OnyxTypes.CompanyCardFeedWithDomainID;
[ONYXKEYS.COLLECTION.LAST_SELECTED_EXPENSIFY_CARD_FEED]: OnyxTypes.FundID;
[ONYXKEYS.COLLECTION.NVP_EXPENSIFY_ON_CARD_WAITLIST]: OnyxTypes.CardOnWaitlist;
diff --git a/src/ROUTES.ts b/src/ROUTES.ts
index f585920b058f..2804e45c7e15 100644
--- a/src/ROUTES.ts
+++ b/src/ROUTES.ts
@@ -333,7 +333,8 @@ const DYNAMIC_ROUTES = {
},
WORKSPACE_ACCOUNTING_RECONCILIATION_ACCOUNT_SETTINGS: {
path: 'account-reconciliation-settings',
- entryScreens: [SCREENS.WORKSPACE.ACCOUNTING.CARD_RECONCILIATION, SCREENS.WORKSPACE.DYNAMIC_WORKSPACE_EXPENSIFY_CARD_SETTINGS_ACCOUNT],
+ entryScreens: [SCREENS.WORKSPACE.ACCOUNTING.CARD_RECONCILIATION, SCREENS.WORKSPACE.DYNAMIC_WORKSPACE_EXPENSIFY_CARD_SETTINGS_ACCOUNT, SCREENS.WORKSPACE.ACCOUNTING.NETSUITE_ADVANCED],
+ queryParams: ['connection', 'reconciliationAccountSettingsType'],
},
ADDRESS_COUNTRY: {
path: 'country',
diff --git a/src/languages/de.ts b/src/languages/de.ts
index d0ec1b4109b5..43e67c62e567 100644
--- a/src/languages/de.ts
+++ b/src/languages/de.ts
@@ -6384,7 +6384,11 @@ _Für ausführlichere Anweisungen [besuchen Sie unsere Hilfeseite](${CONST.NETSU
chooseBankAccount: 'Wählen Sie das Bankkonto aus, mit dem Ihre Zahlungen mit der Expensify Karte abgeglichen werden.',
settlementAccountReconciliation: (settlementAccountUrl: string, lastFourPAN: string) =>
`Stellen Sie sicher, dass dieses Konto mit Ihrem Expensify Karte-Abrechnungskonto (endend auf ${lastFourPAN}) übereinstimmt, damit die fortlaufende Abstimmung richtig funktioniert.`,
+ chooseTravelInvoicingBankAccount: 'Wählen Sie das Bankkonto aus, mit dem Ihre Zahlungen aus der Reiseabrechnung abgeglichen werden.',
+ travelInvoicingSettlementAccountReconciliation: (lastFourPAN: string) =>
+ `Stellen Sie sicher, dass dieses Konto mit Ihrem Abrechnungskonto für Reiseabrechnungen (endet auf ${lastFourPAN}) übereinstimmt, damit die kontinuierliche Abstimmung ordnungsgemäß funktioniert.`,
},
+ syncTravelInvoicingSettlements: 'Reiseabrechnungsabgleiche synchronisieren',
},
export: {
notReadyHeading: 'Nicht bereit zum Export',
diff --git a/src/languages/en.ts b/src/languages/en.ts
index bc1f220eb6d4..1a3296a09169 100644
--- a/src/languages/en.ts
+++ b/src/languages/en.ts
@@ -6401,14 +6401,18 @@ const translations = {
cardReconciliation: 'Card reconciliation',
reconciliationAccount: 'Reconciliation account',
continuousReconciliation: 'Continuous Reconciliation',
+ syncTravelInvoicingSettlements: 'Sync travel invoicing settlements',
saveHoursOnReconciliation:
'Save hours on reconciliation each accounting period by having Expensify continuously reconcile Expensify Card statements and settlements on your behalf.',
enableContinuousReconciliation: (accountingAdvancedSettingsLink: string, connectionName: string) =>
`In order to enable Continuous Reconciliation, please enable auto-sync for ${connectionName}.`,
chooseReconciliationAccount: {
chooseBankAccount: 'Choose the bank account that your Expensify Card payments will be reconciled against.',
+ chooseTravelInvoicingBankAccount: 'Choose the bank account that your travel invoicing payments will be reconciled against.',
settlementAccountReconciliation: (settlementAccountUrl: string, lastFourPAN: string) =>
`Make sure this account matches your Expensify Card settlement account (ending in ${lastFourPAN}) so Continuous Reconciliation works properly.`,
+ travelInvoicingSettlementAccountReconciliation: (lastFourPAN: string) =>
+ `Make sure this account matches your travel invoicing settlement account (ending in ${lastFourPAN}) so Continuous Reconciliation works properly.`,
},
},
hr: {
diff --git a/src/languages/es.ts b/src/languages/es.ts
index b0f575184446..7734add9f0b7 100644
--- a/src/languages/es.ts
+++ b/src/languages/es.ts
@@ -6167,7 +6167,11 @@ ${amount} para ${merchant} - ${date}`,
chooseBankAccount: 'Elige la cuenta bancaria con la que se conciliarán los pagos de tu Tarjeta Expensify.',
settlementAccountReconciliation: (settlementAccountUrl, lastFourPAN) =>
`Asegúrate de que esta cuenta coincide con la cuenta de liquidación de tu Tarjeta Expensify (que termina en ${lastFourPAN}) para que la conciliación continua funcione correctamente.`,
+ chooseTravelInvoicingBankAccount: 'Elige la cuenta bancaria con la que se reconciliarán los pagos de tus facturas de viaje.',
+ travelInvoicingSettlementAccountReconciliation: (lastFourPAN: string) =>
+ `Asegúrate de que esta cuenta coincida con tu cuenta de liquidación de facturas de viaje (que termina en ${lastFourPAN}) para que la conciliación continua funcione correctamente.`,
},
+ syncTravelInvoicingSettlements: 'Sincronizar liquidaciones de facturación de viajes',
},
card: {
issueCard: 'Emitir tarjeta',
diff --git a/src/languages/fr.ts b/src/languages/fr.ts
index 9f9ca8872ac8..f868e47d5217 100644
--- a/src/languages/fr.ts
+++ b/src/languages/fr.ts
@@ -6406,7 +6406,11 @@ _Pour des instructions plus détaillées, [visitez notre site d’aide](${CONST.
chooseBankAccount: 'Choisissez le compte bancaire avec lequel les paiements de votre Carte Expensify seront rapprochés.',
settlementAccountReconciliation: (settlementAccountUrl: string, lastFourPAN: string) =>
`Assurez-vous que ce compte correspond à votre compte de règlement Carte Expensify (se terminant par ${lastFourPAN}) afin que la réconciliation continue fonctionne correctement.`,
+ chooseTravelInvoicingBankAccount: 'Choisissez le compte bancaire sur lequel les paiements de facturation de voyage seront rapprochés.',
+ travelInvoicingSettlementAccountReconciliation: (lastFourPAN: string) =>
+ `Assurez-vous que ce compte correspond à votre compte de règlement de facturation de voyage (se terminant par ${lastFourPAN}) afin que le rapprochement continu fonctionne correctement.`,
},
+ syncTravelInvoicingSettlements: 'Synchroniser les règlements de facturation de voyage',
},
export: {
notReadyHeading: 'Pas prêt à être exporté',
diff --git a/src/languages/it.ts b/src/languages/it.ts
index bc2372f91dab..7a964999b879 100644
--- a/src/languages/it.ts
+++ b/src/languages/it.ts
@@ -6374,7 +6374,11 @@ _Per istruzioni più dettagliate, [visita il nostro sito di assistenza](${CONST.
chooseBankAccount: 'Scegli il conto bancario con cui verranno riconciliati i pagamenti della tua Carta Expensify.',
settlementAccountReconciliation: (settlementAccountUrl: string, lastFourPAN: string) =>
`Assicurati che questo conto corrisponda al tuo conto di regolamento della Carta Expensify (con finale ${lastFourPAN}) affinché la Riconciliazione continua funzioni correttamente.`,
+ chooseTravelInvoicingBankAccount: 'Scegli il conto bancario su cui verranno riconciliati i pagamenti della fatturazione di viaggio.',
+ travelInvoicingSettlementAccountReconciliation: (lastFourPAN: string) =>
+ `Assicurati che questo conto corrisponda al tuo conto di regolamento per la fatturazione dei viaggi (che termina con ${lastFourPAN}) in modo che la Riconciliazione continua funzioni correttamente.`,
},
+ syncTravelInvoicingSettlements: 'Sincronizza le liquidazioni delle fatture di viaggio',
},
export: {
notReadyHeading: 'Non pronto per l’esportazione',
diff --git a/src/languages/ja.ts b/src/languages/ja.ts
index ce66751429fb..a3468e0f8f2b 100644
--- a/src/languages/ja.ts
+++ b/src/languages/ja.ts
@@ -6303,7 +6303,11 @@ _詳しい手順については、[ヘルプサイトをご覧ください](${CO
chooseBankAccount: 'Expensify カードの支払いを照合する銀行口座を選択してください。',
settlementAccountReconciliation: (settlementAccountUrl: string, lastFourPAN: string) =>
`継続消込が正しく機能するように、この口座が、末尾が ${lastFourPAN} のExpensify カード精算口座と一致していることを確認してください。`,
+ chooseTravelInvoicingBankAccount: '出張請求の支払いの消込に使用する銀行口座を選択してください。',
+ travelInvoicingSettlementAccountReconciliation: (lastFourPAN: string) =>
+ `Continuous Reconciliation が正しく機能するように、この口座が、旅行の請求書決済用口座(末尾が ${lastFourPAN} の口座)と一致していることを確認してください。`,
},
+ syncTravelInvoicingSettlements: '出張請求の精算を同期',
},
export: {
notReadyHeading: 'エクスポートの準備ができていません',
diff --git a/src/languages/nl.ts b/src/languages/nl.ts
index f49b1a731a59..92104dc6f956 100644
--- a/src/languages/nl.ts
+++ b/src/languages/nl.ts
@@ -6352,7 +6352,11 @@ _Voor meer gedetailleerde instructies, [bezoek onze help-site](${CONST.NETSUITE_
chooseBankAccount: 'Kies de bankrekening waarop de betalingen met je Expensify Kaart worden afgestemd.',
settlementAccountReconciliation: (settlementAccountUrl: string, lastFourPAN: string) =>
`Zorg ervoor dat deze rekening overeenkomt met je Expensify Kaart-afwikkelingsrekening (eindigend op ${lastFourPAN}), zodat Continue Afstemming goed werkt.`,
+ chooseTravelInvoicingBankAccount: 'Kies de bankrekening waarop de betalingen van je reiskostenfacturen worden afgeletterd.',
+ travelInvoicingSettlementAccountReconciliation: (lastFourPAN: string) =>
+ `Zorg ervoor dat deze rekening overeenkomt met je afwikkelingsrekening voor reiskostenfacturatie (die eindigt op ${lastFourPAN}), zodat Continue Afstemming goed werkt.`,
},
+ syncTravelInvoicingSettlements: 'Reisfactureringsafrekeningen synchroniseren',
},
export: {
notReadyHeading: 'Niet klaar om te exporteren',
@@ -6903,10 +6907,10 @@ Vereis onkostendetails zoals bonnen en beschrijvingen, stel limieten en standaar
title: 'Expensify Kaarten bieden altijd ingebouwde bescherming',
description: `Expensify weigert deze uitgaven altijd:
- • Services voor volwassenen
- • Geldautomaten (ATM's)
- • Gokken
- • Geldoverschrijvingen
+ • Services voor volwassenen
+ • Geldautomaten (ATM's)
+ • Gokken
+ • Geldoverschrijvingen
er bestedingsregels toe om de kasstroom van het bedrijf te beschermen.`,
},
addSpendRule: 'Uitgaveregel toevoegen',
diff --git a/src/languages/pl.ts b/src/languages/pl.ts
index 7494a45ab943..51df414c80e4 100644
--- a/src/languages/pl.ts
+++ b/src/languages/pl.ts
@@ -6345,7 +6345,11 @@ _Aby uzyskać bardziej szczegółowe instrukcje, [odwiedź naszą stronę pomocy
chooseBankAccount: 'Wybierz konto bankowe, do którego będą uzgadniane płatności kartą Karta Expensify.',
settlementAccountReconciliation: (settlementAccountUrl: string, lastFourPAN: string) =>
`Upewnij się, że to konto jest takie samo jak twoje konto rozliczeniowe Karty Expensify (kończące się na ${lastFourPAN}), aby Ciągłe Uzgadnianie działało poprawnie.`,
+ chooseTravelInvoicingBankAccount: 'Wybierz konto bankowe, z którym będą uzgadniane płatności za faktury podróżne.',
+ travelInvoicingSettlementAccountReconciliation: (lastFourPAN: string) =>
+ `Upewnij się, że to konto jest takie samo jak konto rozliczeniowe do fakturowania podróży (kończące się na ${lastFourPAN}), żeby Ciągłe Uzgadnianie działało poprawnie.`,
},
+ syncTravelInvoicingSettlements: 'Synchronizuj rozliczenia faktur podróżnych',
},
export: {
notReadyHeading: 'Niegotowe do eksportu',
diff --git a/src/languages/pt-BR.ts b/src/languages/pt-BR.ts
index 62cd43e552f4..53ff031ebabd 100644
--- a/src/languages/pt-BR.ts
+++ b/src/languages/pt-BR.ts
@@ -6353,7 +6353,11 @@ _Para instruções mais detalhadas, [visite nossa central de ajuda](${CONST.NETS
chooseBankAccount: 'Escolha a conta bancária na qual os pagamentos do seu Cartão Expensify serão conciliados.',
settlementAccountReconciliation: (settlementAccountUrl: string, lastFourPAN: string) =>
`Certifique-se de que esta conta corresponda à sua conta de liquidação do Cartão Expensify (terminada em ${lastFourPAN}) para que a Reconciliação Contínua funcione corretamente.`,
+ chooseTravelInvoicingBankAccount: 'Escolha a conta bancária na qual os pagamentos de faturamento de viagem serão conciliados.',
+ travelInvoicingSettlementAccountReconciliation: (lastFourPAN: string) =>
+ `Certifique-se de que esta conta corresponda à sua conta de liquidação de faturamento de viagem (terminada em ${lastFourPAN}) para que a Conciliação Contínua funcione corretamente.`,
},
+ syncTravelInvoicingSettlements: 'Sincronizar liquidações de faturamento de viagens',
},
export: {
notReadyHeading: 'Não está pronto para exportar',
diff --git a/src/languages/zh-hans.ts b/src/languages/zh-hans.ts
index 131fdd725408..5d5f6455c3e5 100644
--- a/src/languages/zh-hans.ts
+++ b/src/languages/zh-hans.ts
@@ -6198,7 +6198,10 @@ _如需更详细的说明,请[访问我们的帮助网站](${CONST.NETSUITE_IM
chooseBankAccount: '选择用于对账 Expensify 卡付款的银行账户。',
settlementAccountReconciliation: (settlementAccountUrl: string, lastFourPAN: string) =>
`请确保此账户与您的Expensify 卡结算账户(末尾为 ${lastFourPAN})一致,以便持续对账功能正常运行。`,
+ chooseTravelInvoicingBankAccount: '选择用于核对差旅开票付款的银行账户。',
+ travelInvoicingSettlementAccountReconciliation: (lastFourPAN: string) => `请确保此账户与您的差旅发票结算账户(以 ${lastFourPAN} 结尾)一致,以确保持续对账功能正常运行。`,
},
+ syncTravelInvoicingSettlements: '同步差旅开票结算',
},
export: {
notReadyHeading: '尚未准备好导出',
diff --git a/src/libs/API/parameters/SetTravelInvoicingReconciliationBankAccountParams.ts b/src/libs/API/parameters/SetTravelInvoicingReconciliationBankAccountParams.ts
new file mode 100644
index 000000000000..d24a4567cab1
--- /dev/null
+++ b/src/libs/API/parameters/SetTravelInvoicingReconciliationBankAccountParams.ts
@@ -0,0 +1,6 @@
+type SetTravelInvoicingReconciliationBankAccountParams = {
+ domainName: string;
+ travelInvoicingReconciliationBankAccountID: string;
+};
+
+export default SetTravelInvoicingReconciliationBankAccountParams;
diff --git a/src/libs/API/parameters/ToggleTravelInvoicingContinuousReconciliationParams.ts b/src/libs/API/parameters/ToggleTravelInvoicingContinuousReconciliationParams.ts
new file mode 100644
index 000000000000..634c9f986192
--- /dev/null
+++ b/src/libs/API/parameters/ToggleTravelInvoicingContinuousReconciliationParams.ts
@@ -0,0 +1,7 @@
+type ToggleTravelInvoicingContinuousReconciliationParams = {
+ policyAccountID: number;
+ shouldUseContinuousReconciliation: boolean;
+ travelInvoicingContinuousReconciliationConnection?: string;
+};
+
+export default ToggleTravelInvoicingContinuousReconciliationParams;
diff --git a/src/libs/API/parameters/index.ts b/src/libs/API/parameters/index.ts
index 3f4d14b75143..5da12b9d1915 100644
--- a/src/libs/API/parameters/index.ts
+++ b/src/libs/API/parameters/index.ts
@@ -409,7 +409,9 @@ export type {default as UpdateCardSettlementAccountParams} from './UpdateCardSet
export type {default as ConfigureTravelInvoicingForPolicyParams} from './ConfigureTravelInvoicingForPolicyParams';
export type {default as DeactivateTravelInvoicingParams} from './DeactivateTravelInvoicingParams';
export type {default as SetTravelInvoicingSettlementAccountParams} from './SetTravelInvoicingSettlementAccountParams';
+export type {default as SetTravelInvoicingReconciliationBankAccountParams} from './SetTravelInvoicingReconciliationBankAccountParams';
export type {default as PayTravelInvoicingSpendParams} from './PayTravelInvoicingSpendParams';
+export type {default as ToggleTravelInvoicingContinuousReconciliationParams} from './ToggleTravelInvoicingContinuousReconciliationParams';
export type {default as UpdateTravelInvoicingMonthlyLimitParams} from './UpdateTravelInvoicingMonthlyLimitParams';
export type {default as UpdateTravelInvoicingSettlementFrequencyParams} from './UpdateTravelInvoicingSettlementFrequencyParams';
export type {default as RetryTravelCardsProvisioningParams} from './RetryTravelCardsProvisioningParams';
diff --git a/src/libs/API/types.ts b/src/libs/API/types.ts
index 62d5656211fa..c0f93e7ed5f7 100644
--- a/src/libs/API/types.ts
+++ b/src/libs/API/types.ts
@@ -510,6 +510,8 @@ const WRITE_COMMANDS = {
CONFIGURE_TRAVEL_INVOICING_FOR_POLICY: 'ConfigureTravelInvoicingForPolicy',
DEACTIVATE_TRAVEL_INVOICING: 'DeactivateTravelInvoicing',
SET_TRAVEL_INVOICING_SETTLEMENT_ACCOUNT: 'SetTravelInvoicingSettlementAccount',
+ SET_TRAVEL_INVOICING_RECONCILIATION_BANK_ACCOUNT: 'SetTravelInvoicingReconciliationBankAccount',
+ TOGGLE_TRAVEL_INVOICING_CONTINUOUS_RECONCILIATION: 'ToggleTravelInvoicingContinuousReconciliation',
UPDATE_TRAVEL_INVOICE_SETTLEMENT_FREQUENCY: 'UpdateTravelInvoiceSettlementFrequency',
UPDATE_TRAVEL_INVOICING_MONTHLY_LIMIT: 'UpdateTravelInvoicingMonthlyLimit',
PAY_TRAVEL_INVOICING_SPEND: 'PayTravelInvoicingSpend',
@@ -1157,6 +1159,8 @@ type WriteCommandParameters = {
[WRITE_COMMANDS.CONFIGURE_TRAVEL_INVOICING_FOR_POLICY]: Parameters.ConfigureTravelInvoicingForPolicyParams;
[WRITE_COMMANDS.DEACTIVATE_TRAVEL_INVOICING]: Parameters.DeactivateTravelInvoicingParams;
[WRITE_COMMANDS.SET_TRAVEL_INVOICING_SETTLEMENT_ACCOUNT]: Parameters.SetTravelInvoicingSettlementAccountParams;
+ [WRITE_COMMANDS.SET_TRAVEL_INVOICING_RECONCILIATION_BANK_ACCOUNT]: Parameters.SetTravelInvoicingReconciliationBankAccountParams;
+ [WRITE_COMMANDS.TOGGLE_TRAVEL_INVOICING_CONTINUOUS_RECONCILIATION]: Parameters.ToggleTravelInvoicingContinuousReconciliationParams;
[WRITE_COMMANDS.UPDATE_TRAVEL_INVOICE_SETTLEMENT_FREQUENCY]: Parameters.UpdateTravelInvoicingSettlementFrequencyParams;
[WRITE_COMMANDS.UPDATE_TRAVEL_INVOICING_MONTHLY_LIMIT]: Parameters.UpdateTravelInvoicingMonthlyLimitParams;
[WRITE_COMMANDS.PAY_TRAVEL_INVOICING_SPEND]: Parameters.PayTravelInvoicingSpendParams;
diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts
index d8012d8938d5..d064c348ed35 100644
--- a/src/libs/Navigation/types.ts
+++ b/src/libs/Navigation/types.ts
@@ -9,6 +9,7 @@ import type {SaveSearchParams} from '@libs/API/parameters';
import type {ReimbursementAccountStepToOpen} from '@libs/ReimbursementAccountUtils';
import type {AvatarSource} from '@libs/UserAvatarUtils';
import type {AttachmentModalContainerModalProps} from '@pages/media/AttachmentModalScreen/types';
+import type RECONCILIATION_ACCOUNT_SETTINGS_TYPE from '@pages/workspace/accounting/reconciliation/constants';
import type CONST from '@src/CONST';
import type {Country, IOUAction, IOUType, OdometerImageType} from '@src/CONST';
import type NAVIGATORS from '@src/NAVIGATORS';
@@ -1151,6 +1152,7 @@ type SettingsNavigatorParamList = {
[SCREENS.WORKSPACE.ACCOUNTING.DYNAMIC_RECONCILIATION_ACCOUNT_SETTINGS]: {
policyID: string;
connection: ValueOf;
+ reconciliationAccountSettingsType?: ValueOf;
};
[SCREENS.TWO_FACTOR_AUTH.DISABLED]: undefined;
[SCREENS.TWO_FACTOR_AUTH.DISABLE]: undefined;
diff --git a/src/libs/actions/TravelInvoicing.ts b/src/libs/actions/TravelInvoicing.ts
index 2397022fe544..111b0bf73cdf 100644
--- a/src/libs/actions/TravelInvoicing.ts
+++ b/src/libs/actions/TravelInvoicing.ts
@@ -9,7 +9,9 @@ import type {
OpenPolicyTravelPageParams,
PayTravelInvoicingSpendParams,
RetryTravelCardsProvisioningParams,
+ SetTravelInvoicingReconciliationBankAccountParams,
SetTravelInvoicingSettlementAccountParams,
+ ToggleTravelInvoicingContinuousReconciliationParams,
UpdateTravelInvoicingMonthlyLimitParams,
UpdateTravelInvoicingSettlementFrequencyParams,
} from '@libs/API/parameters';
@@ -22,6 +24,7 @@ import enhanceParameters from '@libs/Network/enhanceParameters';
import {getTravelInvoicingCardSettingsKey} from '@libs/TravelInvoicingUtils';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
+import type {ConnectionName} from '@src/types/onyx/Policy';
/**
* Opens the Travel page for a policy and fetches Travel Invoicing data.
@@ -159,6 +162,120 @@ function setTravelInvoicingSettlementAccount(policyID: string, workspaceAccountI
API.write(WRITE_COMMANDS.SET_TRAVEL_INVOICING_SETTLEMENT_ACCOUNT, params, {optimisticData, successData, failureData});
}
+type TravelInvoicingContinuousReconciliationUpdate = OnyxUpdate<
+ | typeof ONYXKEYS.COLLECTION.TRAVEL_INVOICING_USE_CONTINUOUS_RECONCILIATION
+ | typeof ONYXKEYS.COLLECTION.TRAVEL_INVOICING_USE_CONTINUOUS_RECONCILIATION_PENDING_ACTION
+ | typeof ONYXKEYS.COLLECTION.TRAVEL_INVOICING_CONTINUOUS_RECONCILIATION_CONNECTION
+>;
+
+function toggleTravelInvoicingContinuousReconciliation(
+ workspaceAccountID: number,
+ shouldUseContinuousReconciliation: boolean,
+ connectionName: ConnectionName,
+ oldConnectionName?: ConnectionName,
+) {
+ const parameters: ToggleTravelInvoicingContinuousReconciliationParams = shouldUseContinuousReconciliation
+ ? {
+ policyAccountID: workspaceAccountID,
+ shouldUseContinuousReconciliation,
+ travelInvoicingContinuousReconciliationConnection: connectionName,
+ }
+ : {
+ policyAccountID: workspaceAccountID,
+ shouldUseContinuousReconciliation,
+ };
+
+ const optimisticData: TravelInvoicingContinuousReconciliationUpdate[] = [
+ {
+ onyxMethod: Onyx.METHOD.MERGE,
+ key: `${ONYXKEYS.COLLECTION.TRAVEL_INVOICING_USE_CONTINUOUS_RECONCILIATION}${workspaceAccountID}`,
+ value: shouldUseContinuousReconciliation,
+ },
+ {
+ onyxMethod: Onyx.METHOD.MERGE,
+ key: `${ONYXKEYS.COLLECTION.TRAVEL_INVOICING_USE_CONTINUOUS_RECONCILIATION_PENDING_ACTION}${workspaceAccountID}`,
+ value: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE,
+ },
+ {
+ onyxMethod: Onyx.METHOD.MERGE,
+ key: `${ONYXKEYS.COLLECTION.TRAVEL_INVOICING_CONTINUOUS_RECONCILIATION_CONNECTION}${workspaceAccountID}`,
+ value: connectionName,
+ },
+ ];
+
+ const successData: TravelInvoicingContinuousReconciliationUpdate[] = [
+ {
+ onyxMethod: Onyx.METHOD.MERGE,
+ key: `${ONYXKEYS.COLLECTION.TRAVEL_INVOICING_USE_CONTINUOUS_RECONCILIATION}${workspaceAccountID}`,
+ value: shouldUseContinuousReconciliation,
+ },
+ {
+ onyxMethod: Onyx.METHOD.MERGE,
+ key: `${ONYXKEYS.COLLECTION.TRAVEL_INVOICING_USE_CONTINUOUS_RECONCILIATION_PENDING_ACTION}${workspaceAccountID}`,
+ value: null,
+ },
+ {
+ onyxMethod: Onyx.METHOD.MERGE,
+ key: `${ONYXKEYS.COLLECTION.TRAVEL_INVOICING_CONTINUOUS_RECONCILIATION_CONNECTION}${workspaceAccountID}`,
+ value: connectionName,
+ },
+ ];
+
+ const failureData: TravelInvoicingContinuousReconciliationUpdate[] = [
+ {
+ onyxMethod: Onyx.METHOD.MERGE,
+ key: `${ONYXKEYS.COLLECTION.TRAVEL_INVOICING_USE_CONTINUOUS_RECONCILIATION}${workspaceAccountID}`,
+ value: !shouldUseContinuousReconciliation,
+ },
+ {
+ onyxMethod: Onyx.METHOD.MERGE,
+ key: `${ONYXKEYS.COLLECTION.TRAVEL_INVOICING_USE_CONTINUOUS_RECONCILIATION_PENDING_ACTION}${workspaceAccountID}`,
+ value: null,
+ },
+ {
+ onyxMethod: Onyx.METHOD.MERGE,
+ key: `${ONYXKEYS.COLLECTION.TRAVEL_INVOICING_CONTINUOUS_RECONCILIATION_CONNECTION}${workspaceAccountID}`,
+ value: oldConnectionName ?? null,
+ },
+ ];
+
+ API.write(WRITE_COMMANDS.TOGGLE_TRAVEL_INVOICING_CONTINUOUS_RECONCILIATION, parameters, {
+ optimisticData,
+ successData,
+ failureData,
+ });
+}
+
+function setTravelInvoicingReconciliationBankAccount(
+ workspaceAccountID: number,
+ domainName: string,
+ travelInvoicingReconciliationBankAccountID: string,
+ currentReconciliationBankAccountID?: string,
+) {
+ const parameters: SetTravelInvoicingReconciliationBankAccountParams = {
+ domainName,
+ travelInvoicingReconciliationBankAccountID,
+ };
+
+ const optimisticData: Array> = [
+ {
+ onyxMethod: Onyx.METHOD.MERGE,
+ key: `${ONYXKEYS.COLLECTION.TRAVEL_INVOICING_RECONCILIATION_BANK_ACCOUNT_ID}${workspaceAccountID}`,
+ value: travelInvoicingReconciliationBankAccountID,
+ },
+ ];
+
+ const failureData: Array> = [
+ {
+ onyxMethod: Onyx.METHOD.MERGE,
+ key: `${ONYXKEYS.COLLECTION.TRAVEL_INVOICING_RECONCILIATION_BANK_ACCOUNT_ID}${workspaceAccountID}`,
+ value: currentReconciliationBankAccountID ?? null,
+ },
+ ];
+
+ API.write(WRITE_COMMANDS.SET_TRAVEL_INVOICING_RECONCILIATION_BANK_ACCOUNT, parameters, {optimisticData, failureData});
+}
+
/**
* Clears any errors from the Travel Invoicing settlement account settings.
* Also resets the paymentBankAccountID to the previous valid value (or null if none existed).
@@ -630,6 +747,8 @@ export {
exportTravelInvoiceStatementCSV,
configureTravelInvoicingForPolicy,
deactivateTravelInvoicing,
+ toggleTravelInvoicingContinuousReconciliation,
+ setTravelInvoicingReconciliationBankAccount,
clearTravelInvoicingErrors,
retryTravelCardsProvisioning,
updateTravelInvoicingMonthlyLimit,
diff --git a/src/libs/actions/connections/index.ts b/src/libs/actions/connections/index.ts
index 650919ddbe39..b81904a4f3fb 100644
--- a/src/libs/actions/connections/index.ts
+++ b/src/libs/actions/connections/index.ts
@@ -24,6 +24,8 @@ function removePolicyConnection(policy: Policy, connectionName: PolicyConnection
| typeof ONYXKEYS.COLLECTION.POLICY_CONNECTION_SYNC_PROGRESS
| typeof ONYXKEYS.COLLECTION.EXPENSIFY_CARD_CONTINUOUS_RECONCILIATION_CONNECTION
| typeof ONYXKEYS.COLLECTION.EXPENSIFY_CARD_USE_CONTINUOUS_RECONCILIATION
+ | typeof ONYXKEYS.COLLECTION.TRAVEL_INVOICING_CONTINUOUS_RECONCILIATION_CONNECTION
+ | typeof ONYXKEYS.COLLECTION.TRAVEL_INVOICING_USE_CONTINUOUS_RECONCILIATION
>
> = [
{
@@ -50,6 +52,16 @@ function removePolicyConnection(policy: Policy, connectionName: PolicyConnection
key: `${ONYXKEYS.COLLECTION.EXPENSIFY_CARD_USE_CONTINUOUS_RECONCILIATION}${workspaceAccountID}`,
value: null,
},
+ {
+ onyxMethod: Onyx.METHOD.SET,
+ key: `${ONYXKEYS.COLLECTION.TRAVEL_INVOICING_CONTINUOUS_RECONCILIATION_CONNECTION}${workspaceAccountID}`,
+ value: null,
+ },
+ {
+ onyxMethod: Onyx.METHOD.SET,
+ key: `${ONYXKEYS.COLLECTION.TRAVEL_INVOICING_USE_CONTINUOUS_RECONCILIATION}${workspaceAccountID}`,
+ value: null,
+ },
];
const successData: Array> = [];
diff --git a/src/pages/workspace/accounting/netsuite/advanced/NetSuiteAdvancedPage.tsx b/src/pages/workspace/accounting/netsuite/advanced/NetSuiteAdvancedPage.tsx
index 9913c5931b71..2a8d72f8a987 100644
--- a/src/pages/workspace/accounting/netsuite/advanced/NetSuiteAdvancedPage.tsx
+++ b/src/pages/workspace/accounting/netsuite/advanced/NetSuiteAdvancedPage.tsx
@@ -7,6 +7,7 @@ import ConnectionLayout from '@components/ConnectionLayout';
import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription';
import OfflineWithFeedback from '@components/OfflineWithFeedback';
import useLocalize from '@hooks/useLocalize';
+import useOnyx from '@hooks/useOnyx';
import useThemeStyles from '@hooks/useThemeStyles';
import {
updateNetSuiteAutoCreateEntities,
@@ -16,6 +17,8 @@ import {
updateNetSuiteSyncReimbursedReports,
} from '@libs/actions/connections/NetSuiteCommands';
import {clearNetSuiteErrorField} from '@libs/actions/Policy/Policy';
+import {toggleTravelInvoicingContinuousReconciliation} from '@libs/actions/TravelInvoicing';
+import {getCardSettings, getConnectionBankAccountsForReconciliation} from '@libs/CardUtils';
import {getLatestErrorField} from '@libs/ErrorUtils';
import createDynamicRoute from '@libs/Navigation/helpers/dynamicRoutesUtils/createDynamicRoute';
import Navigation from '@libs/Navigation/Navigation';
@@ -26,6 +29,7 @@ import {
getFilteredReimbursableAccountOptions,
settingsPendingAction,
} from '@libs/PolicyUtils';
+import {getIsTravelInvoicingEnabled} from '@libs/TravelInvoicingUtils';
import type {ExtendedMenuItemWithSubscribedSettings, MenuItemToRender} from '@pages/workspace/accounting/netsuite/types';
import {
shouldHideCustomFormIDOptions,
@@ -34,22 +38,36 @@ import {
shouldHideReimbursedReportsSection,
shouldHideReportsExportTo,
} from '@pages/workspace/accounting/netsuite/utils';
+import RECONCILIATION_ACCOUNT_SETTINGS_TYPE from '@pages/workspace/accounting/reconciliation/constants';
import type {WithPolicyConnectionsProps} from '@pages/workspace/withPolicyConnections';
import withPolicyConnections from '@pages/workspace/withPolicyConnections';
import ToggleSettingOptionRow from '@pages/workspace/workflows/ToggleSettingsOptionRow';
import CONST from '@src/CONST';
import type {TranslationPaths} from '@src/languages/types';
+import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES, {DYNAMIC_ROUTES} from '@src/ROUTES';
function NetSuiteAdvancedPage({policy}: WithPolicyConnectionsProps) {
const {translate} = useLocalize();
const styles = useThemeStyles();
const policyID = policy?.id ?? CONST.DEFAULT_NUMBER_ID.toString();
+ const workspaceAccountID = policy?.workspaceAccountID ?? CONST.DEFAULT_NUMBER_ID;
const config = policy?.connections?.netsuite?.options?.config;
const autoSyncConfig = policy?.connections?.netsuite?.config;
+ const autoSync = !!autoSyncConfig?.autoSync?.enabled;
const accountingMethod = policy?.connections?.netsuite?.options?.config?.accountingMethod;
const {payableList} = policy?.connections?.netsuite?.options?.data ?? {};
+ const [cardSettings] = useOnyx(`${ONYXKEYS.COLLECTION.PRIVATE_EXPENSIFY_CARD_SETTINGS}${workspaceAccountID}`);
+ const travelSettings = getCardSettings(cardSettings, CONST.TRAVEL.PROGRAM_TRAVEL_US);
+ const isTravelInvoicingEnabled = getIsTravelInvoicingEnabled(travelSettings);
+ const [travelInvoicingContinuousReconciliation] = useOnyx(`${ONYXKEYS.COLLECTION.TRAVEL_INVOICING_USE_CONTINUOUS_RECONCILIATION}${workspaceAccountID}`);
+ const [travelInvoicingContinuousReconciliationPendingAction] = useOnyx(`${ONYXKEYS.COLLECTION.TRAVEL_INVOICING_USE_CONTINUOUS_RECONCILIATION_PENDING_ACTION}${workspaceAccountID}`);
+ const [travelInvoicingContinuousReconciliationConnection] = useOnyx(`${ONYXKEYS.COLLECTION.TRAVEL_INVOICING_CONTINUOUS_RECONCILIATION_CONNECTION}${workspaceAccountID}`);
+ const [travelInvoicingReconciliationBankAccountID] = useOnyx(`${ONYXKEYS.COLLECTION.TRAVEL_INVOICING_RECONCILIATION_BANK_ACCOUNT_ID}${workspaceAccountID}`);
+ const travelInvoicingReconciliationBankAccount = getConnectionBankAccountsForReconciliation(policy?.connections, CONST.POLICY.CONNECTIONS.NAME.NETSUITE).find(
+ (account) => account.id === travelInvoicingReconciliationBankAccountID,
+ );
const shouldShowCustomFormIDOptions = useSharedValue(!shouldHideCustomFormIDOptions(config));
const shouldAnimateAccordionSection = useSharedValue(false);
@@ -73,6 +91,14 @@ function NetSuiteAdvancedPage({policy}: WithPolicyConnectionsProps) {
return getFilteredApprovalAccountOptions(payableList).find(({id}) => id === config?.approvalAccount);
}, [config?.approvalAccount, payableList, translate]);
+ const navigateToTravelInvoicingReconciliationAccountSettings = () => {
+ Navigation.navigate(
+ createDynamicRoute(
+ `${DYNAMIC_ROUTES.WORKSPACE_ACCOUNTING_RECONCILIATION_ACCOUNT_SETTINGS.path}?connection=${CONST.POLICY.CONNECTIONS.ROUTE.NETSUITE}&reconciliationAccountSettingsType=${RECONCILIATION_ACCOUNT_SETTINGS_TYPE.TRAVEL_INVOICING}`,
+ ),
+ );
+ };
+
const renderDefaultMenuItem = (item: MenuItemToRender) => {
return (
Navigation.navigate(createDynamicRoute(DYNAMIC_ROUTES.NETSUITE_AUTO_SYNC.path)),
hintText: (() => {
- if (!autoSyncConfig?.autoSync?.enabled) {
+ if (!autoSync) {
return undefined;
}
return translate(
@@ -145,6 +171,33 @@ function NetSuiteAdvancedPage({policy}: WithPolicyConnectionsProps) {
key: 'divider2',
shouldHide: shouldHideReimbursedReportsSection(config),
},
+ {
+ type: 'toggle',
+ title: translate('workspace.accounting.syncTravelInvoicingSettlements'),
+ isActive: !!travelInvoicingContinuousReconciliation,
+ switchAccessibilityLabel: translate('workspace.accounting.syncTravelInvoicingSettlements'),
+ disabled: !autoSync,
+ onToggle: (isEnabled) => {
+ toggleTravelInvoicingContinuousReconciliation(workspaceAccountID, isEnabled, CONST.POLICY.CONNECTIONS.NAME.NETSUITE, travelInvoicingContinuousReconciliationConnection);
+ if (isEnabled) {
+ navigateToTravelInvoicingReconciliationAccountSettings();
+ }
+ },
+ pendingAction: travelInvoicingContinuousReconciliationPendingAction,
+ shouldHide: !isTravelInvoicingEnabled,
+ },
+ {
+ type: 'menuitem',
+ description: translate('workspace.accounting.reconciliationAccount'),
+ onPress: navigateToTravelInvoicingReconciliationAccountSettings,
+ title: travelInvoicingReconciliationBankAccount?.name,
+ shouldHide: !isTravelInvoicingEnabled || !travelInvoicingContinuousReconciliation,
+ },
+ {
+ type: 'divider',
+ key: 'dividerTravelInvoicing',
+ shouldHide: !isTravelInvoicingEnabled,
+ },
{
type: 'toggle',
title: translate('workspace.netsuite.advancedConfig.inviteEmployees'),
diff --git a/src/pages/workspace/accounting/reconciliation/DynamicReconciliationAccountSettingsPage.tsx b/src/pages/workspace/accounting/reconciliation/DynamicReconciliationAccountSettingsPage.tsx
index e74de28d7928..c45550774e13 100644
--- a/src/pages/workspace/accounting/reconciliation/DynamicReconciliationAccountSettingsPage.tsx
+++ b/src/pages/workspace/accounting/reconciliation/DynamicReconciliationAccountSettingsPage.tsx
@@ -1,5 +1,6 @@
-import React, {useCallback, useMemo} from 'react';
+import React, {useCallback} from 'react';
import {View} from 'react-native';
+import type {OnyxEntry} from 'react-native-onyx';
import ConnectionLayout from '@components/ConnectionLayout';
import RenderHTML from '@components/RenderHTML';
import SelectionList from '@components/SelectionList';
@@ -17,94 +18,204 @@ import {getCardProgramKey, getCardSettings, getConnectionBankAccountsForReconcil
import createDynamicRoute from '@libs/Navigation/helpers/dynamicRoutesUtils/createDynamicRoute';
import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types';
import {getDomainNameForPolicy} from '@libs/PolicyUtils';
+import {getTravelSettlementAccount} from '@libs/TravelInvoicingUtils';
import Navigation from '@navigation/Navigation';
import type {SettingsNavigatorParamList} from '@navigation/types';
import {setCardReconciliationAccount} from '@userActions/Card';
+import {setTravelInvoicingReconciliationBankAccount} from '@userActions/TravelInvoicing';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import {DYNAMIC_ROUTES} from '@src/ROUTES';
import type SCREENS from '@src/SCREENS';
+import type {BankAccountList, Policy} from '@src/types/onyx';
+import type {ConnectionName} from '@src/types/onyx/Policy';
+import RECONCILIATION_ACCOUNT_SETTINGS_TYPE from './constants';
type DynamicReconciliationAccountSettingsPageProps = PlatformStackScreenProps;
+type ReconciliationAccountSettingsLayoutProps = {
+ policyID: string;
+ connectionName: ConnectionName;
+ connectionBankAccounts: ReturnType;
+ goBack: () => void;
+ description: string;
+ html: string;
+ selectedBankAccountID?: string;
+ onSelectBankAccount: (newBankAccountID?: string) => void;
+};
-function DynamicReconciliationAccountSettingsPage({route}: DynamicReconciliationAccountSettingsPageProps) {
- const {policyID, connection} = route.params;
+type DynamicReconciliationProps = {
+ policyID: string;
+ workspaceAccountID: number;
+ domainName: string;
+ bankAccountList: OnyxEntry;
+ goBack: () => void;
+ connectionName: ConnectionName;
+ connectionBankAccounts: ReturnType;
+};
+
+const policyConnectionsSelector = (policy: OnyxEntry) => policy?.connections;
+const policyWorkspaceAccountIDSelector = (policy: OnyxEntry) => policy?.workspaceAccountID;
+function ReconciliationAccountSettingsLayout({
+ policyID,
+ connectionName,
+ connectionBankAccounts,
+ goBack,
+ description,
+ html,
+ selectedBankAccountID,
+ onSelectBankAccount,
+}: ReconciliationAccountSettingsLayoutProps) {
const styles = useThemeStyles();
- const {translate} = useLocalize();
- const backPath = useDynamicBackPath(DYNAMIC_ROUTES.WORKSPACE_ACCOUNTING_RECONCILIATION_ACCOUNT_SETTINGS.path);
- const connectionName = getConnectionNameFromRouteParam(connection);
+ const options = connectionBankAccounts.map((bankAccount) => ({
+ text: bankAccount.name,
+ value: bankAccount.id,
+ keyForList: bankAccount.id,
+ isSelected: bankAccount.id === selectedBankAccountID,
+ }));
+
+ return (
+
+ {description}
+
+
+
+
+ onSelectBankAccount(value)}
+ ListItem={SingleSelectListItem}
+ initiallyFocusedItemKey={selectedBankAccountID}
+ />
+
+ );
+}
+
+function ExpensifyCardDynamicReconciliation({policyID, workspaceAccountID, domainName, bankAccountList, goBack, connectionName, connectionBankAccounts}: DynamicReconciliationProps) {
+ const {translate} = useLocalize();
const defaultFundID = useDefaultFundID(policyID);
- const [connections] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, {selector: (policy) => policy?.connections});
- const [workspaceAccountID = CONST.DEFAULT_NUMBER_ID] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, {selector: (policy) => policy?.workspaceAccountID});
- const [bankAccountList] = useOnyx(ONYXKEYS.BANK_ACCOUNT_LIST);
const [cardSettings] = useOnyx(`${ONYXKEYS.COLLECTION.PRIVATE_EXPENSIFY_CARD_SETTINGS}${defaultFundID}`);
const programKey = getCardProgramKey(cardSettings);
const settings = getCardSettings(cardSettings, programKey);
const paymentBankAccountID = settings?.paymentBankAccountID;
const [reconciliationBankAccountID] = useOnyx(`${ONYXKEYS.COLLECTION.EXPENSIFY_CARD_RECONCILIATION_BANK_ACCOUNT_ID}${workspaceAccountID}`);
- const selectedBankAccount = useMemo(() => bankAccountList?.[paymentBankAccountID?.toString() ?? ''], [paymentBankAccountID, bankAccountList]);
- const bankAccountNumber = useMemo(() => selectedBankAccount?.accountData?.accountNumber ?? '', [selectedBankAccount?.accountData?.accountNumber]);
+ const selectedBankAccount = bankAccountList?.[paymentBankAccountID?.toString() ?? ''];
+ const bankAccountNumber = selectedBankAccount?.accountData?.accountNumber ?? '';
const settlementAccountEnding = getLastFourDigits(bankAccountNumber);
- const domainName = settings?.domainName ?? getDomainNameForPolicy(policyID);
+ const reconciliationDomainName = settings?.domainName ?? domainName;
const {environmentURL} = useEnvironment();
- const connectionBankAccounts = getConnectionBankAccountsForReconciliation(connections, connectionName);
+ const selectBankAccount = (newBankAccountID?: string) => {
+ if (!newBankAccountID) {
+ return;
+ }
+ setCardReconciliationAccount(workspaceAccountID, reconciliationDomainName, newBankAccountID, reconciliationBankAccountID);
+ goBack();
+ };
- const goBack = useCallback(() => {
- Navigation.goBack(backPath);
- }, [backPath]);
+ return (
+
+ );
+}
- const options = useMemo(() => {
- return connectionBankAccounts.map((bankAccount) => ({
- text: bankAccount.name,
- value: bankAccount.id,
- keyForList: bankAccount.id,
- isSelected: bankAccount.id === reconciliationBankAccountID,
- }));
- }, [connectionBankAccounts, reconciliationBankAccountID]);
+function TravelInvoicingDynamicReconciliation({policyID, workspaceAccountID, domainName, bankAccountList, goBack, connectionName, connectionBankAccounts}: DynamicReconciliationProps) {
+ const {translate} = useLocalize();
+
+ const [travelInvoicingCardSettings] = useOnyx(`${ONYXKEYS.COLLECTION.PRIVATE_EXPENSIFY_CARD_SETTINGS}${workspaceAccountID}`);
+ const [travelInvoicingReconciliationBankAccountID] = useOnyx(`${ONYXKEYS.COLLECTION.TRAVEL_INVOICING_RECONCILIATION_BANK_ACCOUNT_ID}${workspaceAccountID}`);
+ const travelInvoicingSettings = getCardSettings(travelInvoicingCardSettings, CONST.TRAVEL.PROGRAM_TRAVEL_US);
+ const travelInvoicingSettlementAccount = getTravelSettlementAccount(travelInvoicingSettings, bankAccountList);
+ const settlementAccountEnding = travelInvoicingSettlementAccount?.last4 ?? '';
const selectBankAccount = (newBankAccountID?: string) => {
if (!newBankAccountID) {
return;
}
- setCardReconciliationAccount(workspaceAccountID, domainName, newBankAccountID, reconciliationBankAccountID);
+ setTravelInvoicingReconciliationBankAccount(workspaceAccountID, domainName, newBankAccountID, travelInvoicingReconciliationBankAccountID);
goBack();
};
return (
-
- {translate('workspace.accounting.chooseReconciliationAccount.chooseBankAccount')}
-
-
-
+ connectionBankAccounts={connectionBankAccounts}
+ goBack={goBack}
+ description={translate('workspace.accounting.chooseReconciliationAccount.chooseTravelInvoicingBankAccount')}
+ html={translate('workspace.accounting.chooseReconciliationAccount.travelInvoicingSettlementAccountReconciliation', settlementAccountEnding)}
+ selectedBankAccountID={travelInvoicingReconciliationBankAccountID}
+ onSelectBankAccount={selectBankAccount}
+ />
+ );
+}
- selectBankAccount(value)}
- ListItem={SingleSelectListItem}
- initiallyFocusedItemKey={reconciliationBankAccountID}
+function DynamicReconciliationAccountSettingsPage({route}: DynamicReconciliationAccountSettingsPageProps) {
+ const {policyID, connection, reconciliationAccountSettingsType} = route.params;
+ const backPath = useDynamicBackPath(DYNAMIC_ROUTES.WORKSPACE_ACCOUNTING_RECONCILIATION_ACCOUNT_SETTINGS.path);
+
+ const connectionName = getConnectionNameFromRouteParam(connection);
+ const domainName = getDomainNameForPolicy(policyID);
+
+ const [connections] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, {selector: policyConnectionsSelector});
+ const [workspaceAccountID = CONST.DEFAULT_NUMBER_ID] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, {selector: policyWorkspaceAccountIDSelector});
+ const [bankAccountList] = useOnyx(ONYXKEYS.BANK_ACCOUNT_LIST);
+
+ const connectionBankAccounts = getConnectionBankAccountsForReconciliation(connections, connectionName);
+
+ const goBack = useCallback(() => {
+ Navigation.goBack(backPath);
+ }, [backPath]);
+
+ if (reconciliationAccountSettingsType === RECONCILIATION_ACCOUNT_SETTINGS_TYPE.TRAVEL_INVOICING) {
+ return (
+
-
+ );
+ }
+
+ return (
+
);
}
diff --git a/src/pages/workspace/accounting/reconciliation/constants.ts b/src/pages/workspace/accounting/reconciliation/constants.ts
new file mode 100644
index 000000000000..1bed15f8e047
--- /dev/null
+++ b/src/pages/workspace/accounting/reconciliation/constants.ts
@@ -0,0 +1,6 @@
+const RECONCILIATION_ACCOUNT_SETTINGS_TYPE = {
+ EXPENSIFY_CARD: 'expensifyCard',
+ TRAVEL_INVOICING: 'travelInvoicing',
+} as const;
+
+export default RECONCILIATION_ACCOUNT_SETTINGS_TYPE;
diff --git a/tests/unit/TravelInvoicingTest.ts b/tests/unit/TravelInvoicingTest.ts
index 37b0aaa74ed0..537c2d23a5b4 100644
--- a/tests/unit/TravelInvoicingTest.ts
+++ b/tests/unit/TravelInvoicingTest.ts
@@ -5,7 +5,9 @@ import {
configureTravelInvoicingForPolicy,
deactivateTravelInvoicing,
retryTravelCardsProvisioning,
+ setTravelInvoicingReconciliationBankAccount,
setTravelInvoicingSettlementAccount,
+ toggleTravelInvoicingContinuousReconciliation,
updateTravelInvoiceSettlementFrequency,
} from '@libs/actions/TravelInvoicing';
// We need to import API because it is used in the tests
@@ -123,6 +125,86 @@ describe('TravelInvoicing', () => {
});
});
+ it('toggleTravelInvoicingContinuousReconciliation sends travel-specific optimistic, success, and failure data', () => {
+ const workspaceAccountID = 456;
+ const connectionName = CONST.POLICY.CONNECTIONS.NAME.NETSUITE;
+ const oldConnectionName = CONST.POLICY.CONNECTIONS.NAME.QBO;
+
+ toggleTravelInvoicingContinuousReconciliation(workspaceAccountID, true, connectionName, oldConnectionName);
+
+ expect(spyAPIWrite).toHaveBeenCalledWith(
+ 'ToggleTravelInvoicingContinuousReconciliation',
+ {
+ policyAccountID: workspaceAccountID,
+ shouldUseContinuousReconciliation: true,
+ travelInvoicingContinuousReconciliationConnection: connectionName,
+ },
+ expect.objectContaining({
+ optimisticData: expect.arrayContaining([
+ expect.objectContaining({
+ key: `${ONYXKEYS.COLLECTION.TRAVEL_INVOICING_USE_CONTINUOUS_RECONCILIATION}${workspaceAccountID}`,
+ value: true,
+ }),
+ expect.objectContaining({
+ key: `${ONYXKEYS.COLLECTION.TRAVEL_INVOICING_USE_CONTINUOUS_RECONCILIATION_PENDING_ACTION}${workspaceAccountID}`,
+ value: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE,
+ }),
+ expect.objectContaining({
+ key: `${ONYXKEYS.COLLECTION.TRAVEL_INVOICING_CONTINUOUS_RECONCILIATION_CONNECTION}${workspaceAccountID}`,
+ value: connectionName,
+ }),
+ ]),
+ successData: expect.arrayContaining([
+ expect.objectContaining({
+ key: `${ONYXKEYS.COLLECTION.TRAVEL_INVOICING_USE_CONTINUOUS_RECONCILIATION_PENDING_ACTION}${workspaceAccountID}`,
+ value: null,
+ }),
+ ]),
+ failureData: expect.arrayContaining([
+ expect.objectContaining({
+ key: `${ONYXKEYS.COLLECTION.TRAVEL_INVOICING_USE_CONTINUOUS_RECONCILIATION}${workspaceAccountID}`,
+ value: false,
+ }),
+ expect.objectContaining({
+ key: `${ONYXKEYS.COLLECTION.TRAVEL_INVOICING_CONTINUOUS_RECONCILIATION_CONNECTION}${workspaceAccountID}`,
+ value: oldConnectionName,
+ }),
+ ]),
+ }),
+ );
+ });
+
+ it('setTravelInvoicingReconciliationBankAccount sends the selected bank account and reverts on failure', () => {
+ const workspaceAccountID = 456;
+ const domainName = 'expensify_policy_123.expensify.com';
+ const selectedBankAccountID = 'account-123';
+ const previousBankAccountID = 'account-111';
+
+ setTravelInvoicingReconciliationBankAccount(workspaceAccountID, domainName, selectedBankAccountID, previousBankAccountID);
+
+ expect(spyAPIWrite).toHaveBeenCalledWith(
+ 'SetTravelInvoicingReconciliationBankAccount',
+ {
+ domainName,
+ travelInvoicingReconciliationBankAccountID: selectedBankAccountID,
+ },
+ expect.objectContaining({
+ optimisticData: expect.arrayContaining([
+ expect.objectContaining({
+ key: `${ONYXKEYS.COLLECTION.TRAVEL_INVOICING_RECONCILIATION_BANK_ACCOUNT_ID}${workspaceAccountID}`,
+ value: selectedBankAccountID,
+ }),
+ ]),
+ failureData: expect.arrayContaining([
+ expect.objectContaining({
+ key: `${ONYXKEYS.COLLECTION.TRAVEL_INVOICING_RECONCILIATION_BANK_ACCOUNT_ID}${workspaceAccountID}`,
+ value: previousBankAccountID,
+ }),
+ ]),
+ }),
+ );
+ });
+
it('clearTravelInvoicingSettlementFrequencyErrors clears errors', () => {
const workspaceAccountID = 456;
const cardSettingsKey = getTravelInvoicingCardSettingsKey(workspaceAccountID);