From ec3aaa079fb54bf7d7043b46e4f7ff3cecd18d9e Mon Sep 17 00:00:00 2001 From: Rodrigo Lino da Costa Date: Mon, 11 May 2026 14:44:46 -0300 Subject: [PATCH 01/13] Travel invoice reconciliation --- src/ONYXKEYS.ts | 16 ++ src/ROUTES.ts | 3 +- src/languages/en.ts | 4 + src/languages/es.ts | 4 + ...nvoicingReconciliationBankAccountParams.ts | 7 + ...InvoicingContinuousReconciliationParams.ts | 7 + src/libs/API/parameters/index.ts | 2 + src/libs/API/types.ts | 4 + src/libs/Navigation/types.ts | 3 +- src/libs/actions/TravelInvoicing.ts | 120 ++++++++++++ src/libs/actions/connections/index.ts | 12 ++ .../advanced/NetSuiteAdvancedPage.tsx | 44 +++++ ...namicReconciliationAccountSettingsPage.tsx | 185 +++++++++++++----- .../accounting/reconciliation/constants.ts | 6 + tests/unit/TravelInvoicingTest.ts | 83 ++++++++ 15 files changed, 448 insertions(+), 52 deletions(-) create mode 100644 src/libs/API/parameters/SetTravelInvoicingReconciliationBankAccountParams.ts create mode 100644 src/libs/API/parameters/ToggleTravelInvoicingContinuousReconciliationParams.ts create mode 100644 src/pages/workspace/accounting/reconciliation/constants.ts diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts index 2fc631ce1325..2c3231409228 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_', @@ -1335,6 +1347,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 a474bbc6a5e9..002e0700858f 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -303,7 +303,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/en.ts b/src/languages/en.ts index 345284c86209..deadd3f59cb0 100644 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -6364,14 +6364,18 @@ const translations = { cardReconciliation: 'Card reconciliation', reconciliationAccount: 'Reconciliation account', continuousReconciliation: 'Continuous Reconciliation', + syncCentralInvoicingSettlements: 'Sync central 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 central 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 central 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 cedf6fed8f2e..7b4cc91598d7 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -6173,14 +6173,18 @@ ${amount} para ${merchant} - ${date}`, cardReconciliation: 'Conciliación de tarjetas', reconciliationAccount: 'Cuenta de conciliación', continuousReconciliation: 'Conciliación continua', + syncCentralInvoicingSettlements: 'Sync central invoicing settlements', saveHoursOnReconciliation: 'Ahorra horas de conciliación en cada período contable haciendo que Expensify concilie continuamente los extractos y liquidaciones de la Tarjeta Expensify en tu nombre.', enableContinuousReconciliation: (accountingAdvancedSettingsLink, connectionName) => `Para activar la Conciliación Continua, activa la auto-sync para ${connectionName}.`, chooseReconciliationAccount: { chooseBankAccount: 'Elige la cuenta bancaria con la que se conciliarán los pagos de tu Tarjeta Expensify.', + chooseTravelInvoicingBankAccount: 'Choose the bank account that your central invoicing payments will be reconciled against.', 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.`, + travelInvoicingSettlementAccountReconciliation: (lastFourPAN) => + `Make sure this account matches your central invoicing settlement account (ending in ${lastFourPAN}) so Continuous Reconciliation works properly.`, }, }, card: { diff --git a/src/libs/API/parameters/SetTravelInvoicingReconciliationBankAccountParams.ts b/src/libs/API/parameters/SetTravelInvoicingReconciliationBankAccountParams.ts new file mode 100644 index 000000000000..3b4d07c0e32b --- /dev/null +++ b/src/libs/API/parameters/SetTravelInvoicingReconciliationBankAccountParams.ts @@ -0,0 +1,7 @@ +type SetTravelInvoicingReconciliationBankAccountParams = { + policyID: string; + workspaceAccountID: number; + 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..7cc2523de25a --- /dev/null +++ b/src/libs/API/parameters/ToggleTravelInvoicingContinuousReconciliationParams.ts @@ -0,0 +1,7 @@ +type ToggleTravelInvoicingContinuousReconciliationParams = { + workspaceAccountID: 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 7bac9635bae9..fe6c9d56dfe6 100644 --- a/src/libs/API/parameters/index.ts +++ b/src/libs/API/parameters/index.ts @@ -403,7 +403,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 34998bb50b34..7463b35b88f0 100644 --- a/src/libs/API/types.ts +++ b/src/libs/API/types.ts @@ -497,6 +497,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', @@ -1124,6 +1126,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 e8212b24c2d3..784320abb1da 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -1130,7 +1130,8 @@ type SettingsNavigatorParamList = { }; [SCREENS.WORKSPACE.ACCOUNTING.DYNAMIC_RECONCILIATION_ACCOUNT_SETTINGS]: { policyID: string; - connection: ValueOf; + connection?: ValueOf; + reconciliationAccountSettingsType?: string; }; [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..5ad8f198c1e4 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,121 @@ 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 + ? { + workspaceAccountID, + shouldUseContinuousReconciliation, + travelInvoicingContinuousReconciliationConnection: connectionName, + } + : { + 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( + policyID: string, + workspaceAccountID: number, + travelInvoicingReconciliationBankAccountID: string, + currentReconciliationBankAccountID?: string, +) { + const parameters: SetTravelInvoicingReconciliationBankAccountParams = { + policyID, + workspaceAccountID, + 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 +748,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 0d165dff1eab..43af1088a88a 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 2ebf83f04ad5..e200f1953eb8 100644 --- a/src/pages/workspace/accounting/netsuite/advanced/NetSuiteAdvancedPage.tsx +++ b/src/pages/workspace/accounting/netsuite/advanced/NetSuiteAdvancedPage.tsx @@ -1,3 +1,4 @@ +import {isExpensifyCardContinuousReconciliationEnabledSelector} from '@selectors/Card'; import {CONST as COMMON_CONST} from 'expensify-common'; import React, {useMemo} from 'react'; import {View} from 'react-native'; @@ -7,6 +8,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 +18,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'; @@ -27,6 +31,7 @@ import { getFilteredReimbursableAccountOptions, settingsPendingAction, } from '@libs/PolicyUtils'; +import {getIsTravelInvoicingEnabled} from '@libs/TravelInvoicingUtils'; import type {ExtendedMenuItemWithSubscribedSettings, MenuItemToRender} from '@pages/workspace/accounting/netsuite/types'; import { shouldHideCustomFormIDOptions, @@ -35,22 +40,37 @@ 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 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}`, { + selector: isExpensifyCardContinuousReconciliationEnabledSelector, + }); + 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 +93,14 @@ function NetSuiteAdvancedPage({policy}: WithPolicyConnectionsProps) { return findSelectedBankAccountWithDefaultSelect(getFilteredApprovalAccountOptions(payableList), 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 ( { + toggleTravelInvoicingContinuousReconciliation(workspaceAccountID, isEnabled, CONST.POLICY.CONNECTIONS.NAME.NETSUITE, travelInvoicingContinuousReconciliationConnection); + if (isEnabled) { + navigateToTravelInvoicingReconciliationAccountSettings(); + } + }, + onPress: navigateToTravelInvoicingReconciliationAccountSettings, + pendingAction: travelInvoicingContinuousReconciliationPendingAction, + shouldHide: !isTravelInvoicingEnabled, + }, { type: 'divider', key: 'divider1', diff --git a/src/pages/workspace/accounting/reconciliation/DynamicReconciliationAccountSettingsPage.tsx b/src/pages/workspace/accounting/reconciliation/DynamicReconciliationAccountSettingsPage.tsx index e74de28d7928..aed06cd85b8b 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,178 @@ 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 RECONCILIATION_ACCOUNT_SETTINGS_TYPE from './constants'; type DynamicReconciliationAccountSettingsPageProps = PlatformStackScreenProps; +type ReconciliationAccountSettingsRendererParams = { + 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; + bankAccountList: OnyxEntry; + goBack: () => void; + renderReconciliationAccountSettings: (params: ReconciliationAccountSettingsRendererParams) => React.ReactElement; +}; - const styles = useThemeStyles(); - const {translate} = useLocalize(); - const backPath = useDynamicBackPath(DYNAMIC_ROUTES.WORKSPACE_ACCOUNTING_RECONCILIATION_ACCOUNT_SETTINGS.path); +const policyConnectionsSelector = (policy: OnyxEntry) => policy?.connections; +const policyWorkspaceAccountIDSelector = (policy: OnyxEntry) => policy?.workspaceAccountID; - const connectionName = getConnectionNameFromRouteParam(connection); +function ExpensifyCardDynamicReconciliation({policyID, workspaceAccountID, bankAccountList, goBack, renderReconciliationAccountSettings}: 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 {environmentURL} = useEnvironment(); + const selectBankAccount = (newBankAccountID?: string) => { + if (!newBankAccountID) { + return; + } + setCardReconciliationAccount(workspaceAccountID, domainName, newBankAccountID, reconciliationBankAccountID); + goBack(); + }; + + return renderReconciliationAccountSettings({ + description: translate('workspace.accounting.chooseReconciliationAccount.chooseBankAccount'), + html: translate( + 'workspace.accounting.chooseReconciliationAccount.settlementAccountReconciliation', + `${environmentURL}${createDynamicRoute(DYNAMIC_ROUTES.WORKSPACE_EXPENSIFY_CARD_SETTINGS_ACCOUNT.path)}`, + settlementAccountEnding, + ), + selectedBankAccountID: reconciliationBankAccountID, + onSelectBankAccount: selectBankAccount, + }); +} + +function TravelInvoicingDynamicReconciliation({policyID, workspaceAccountID, bankAccountList, goBack, renderReconciliationAccountSettings}: 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; + } + setTravelInvoicingReconciliationBankAccount(policyID, workspaceAccountID, newBankAccountID, travelInvoicingReconciliationBankAccountID); + goBack(); + }; + + return renderReconciliationAccountSettings({ + description: translate('workspace.accounting.chooseReconciliationAccount.chooseTravelInvoicingBankAccount'), + html: translate('workspace.accounting.chooseReconciliationAccount.travelInvoicingSettlementAccountReconciliation', settlementAccountEnding), + selectedBankAccountID: travelInvoicingReconciliationBankAccountID, + onSelectBankAccount: selectBankAccount, + }); +} + +function DynamicReconciliationAccountSettingsPage({route}: DynamicReconciliationAccountSettingsPageProps) { + const {policyID, connection, reconciliationAccountSettingsType} = route.params; + const styles = useThemeStyles(); + const backPath = useDynamicBackPath(DYNAMIC_ROUTES.WORKSPACE_ACCOUNTING_RECONCILIATION_ACCOUNT_SETTINGS.path); + + const connectionName = getConnectionNameFromRouteParam(connection); + + 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]); - const options = useMemo(() => { - return connectionBankAccounts.map((bankAccount) => ({ + const renderReconciliationAccountSettings = ({description, html, selectedBankAccountID, onSelectBankAccount}: ReconciliationAccountSettingsRendererParams) => { + const options = connectionBankAccounts.map((bankAccount) => ({ text: bankAccount.name, value: bankAccount.id, keyForList: bankAccount.id, - isSelected: bankAccount.id === reconciliationBankAccountID, + isSelected: bankAccount.id === selectedBankAccountID, })); - }, [connectionBankAccounts, reconciliationBankAccountID]); - const selectBankAccount = (newBankAccountID?: string) => { - if (!newBankAccountID) { - return; - } - setCardReconciliationAccount(workspaceAccountID, domainName, newBankAccountID, reconciliationBankAccountID); - goBack(); - }; + return ( + + {description} + + + - return ( - - {translate('workspace.accounting.chooseReconciliationAccount.chooseBankAccount')} - - onSelectBankAccount(value)} + ListItem={SingleSelectListItem} + initiallyFocusedItemKey={selectedBankAccountID} /> - + + ); + }; + + const dynamicReconciliationProps: DynamicReconciliationProps = { + policyID, + workspaceAccountID, + bankAccountList, + goBack, + renderReconciliationAccountSettings, + }; - selectBankAccount(value)} - ListItem={SingleSelectListItem} - initiallyFocusedItemKey={reconciliationBankAccountID} + 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..12c5f3181d3e 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,87 @@ 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', + { + 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 policyID = '123'; + const workspaceAccountID = 456; + const selectedBankAccountID = 'account-123'; + const previousBankAccountID = 'account-111'; + + setTravelInvoicingReconciliationBankAccount(policyID, workspaceAccountID, selectedBankAccountID, previousBankAccountID); + + expect(spyAPIWrite).toHaveBeenCalledWith( + 'SetTravelInvoicingReconciliationBankAccount', + { + policyID, + workspaceAccountID, + 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); From b00307ec2c9dff7e0eb55fece55aec863379b193 Mon Sep 17 00:00:00 2001 From: Rodrigo Lino da Costa Date: Mon, 11 May 2026 14:45:14 -0300 Subject: [PATCH 02/13] missing type --- src/libs/Navigation/types.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index 784320abb1da..448ea9ca97bb 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -1130,7 +1130,7 @@ type SettingsNavigatorParamList = { }; [SCREENS.WORKSPACE.ACCOUNTING.DYNAMIC_RECONCILIATION_ACCOUNT_SETTINGS]: { policyID: string; - connection?: ValueOf; + connection: ValueOf; reconciliationAccountSettingsType?: string; }; [SCREENS.TWO_FACTOR_AUTH.DISABLED]: undefined; From e782aac06423303826eb81a8f1b19d98c63ad609 Mon Sep 17 00:00:00 2001 From: Rodrigo Lino da Costa Date: Wed, 13 May 2026 14:25:03 -0300 Subject: [PATCH 03/13] Make it work with the correct api. --- ...velInvoicingReconciliationBankAccountParams.ts | 3 +-- ...avelInvoicingContinuousReconciliationParams.ts | 2 +- src/libs/actions/TravelInvoicing.ts | 9 ++++----- .../DynamicReconciliationAccountSettingsPage.tsx | 15 ++++++++++----- tests/unit/TravelInvoicingTest.ts | 9 ++++----- 5 files changed, 20 insertions(+), 18 deletions(-) diff --git a/src/libs/API/parameters/SetTravelInvoicingReconciliationBankAccountParams.ts b/src/libs/API/parameters/SetTravelInvoicingReconciliationBankAccountParams.ts index 3b4d07c0e32b..d24a4567cab1 100644 --- a/src/libs/API/parameters/SetTravelInvoicingReconciliationBankAccountParams.ts +++ b/src/libs/API/parameters/SetTravelInvoicingReconciliationBankAccountParams.ts @@ -1,6 +1,5 @@ type SetTravelInvoicingReconciliationBankAccountParams = { - policyID: string; - workspaceAccountID: number; + domainName: string; travelInvoicingReconciliationBankAccountID: string; }; diff --git a/src/libs/API/parameters/ToggleTravelInvoicingContinuousReconciliationParams.ts b/src/libs/API/parameters/ToggleTravelInvoicingContinuousReconciliationParams.ts index 7cc2523de25a..634c9f986192 100644 --- a/src/libs/API/parameters/ToggleTravelInvoicingContinuousReconciliationParams.ts +++ b/src/libs/API/parameters/ToggleTravelInvoicingContinuousReconciliationParams.ts @@ -1,5 +1,5 @@ type ToggleTravelInvoicingContinuousReconciliationParams = { - workspaceAccountID: number; + policyAccountID: number; shouldUseContinuousReconciliation: boolean; travelInvoicingContinuousReconciliationConnection?: string; }; diff --git a/src/libs/actions/TravelInvoicing.ts b/src/libs/actions/TravelInvoicing.ts index 5ad8f198c1e4..111b0bf73cdf 100644 --- a/src/libs/actions/TravelInvoicing.ts +++ b/src/libs/actions/TravelInvoicing.ts @@ -176,12 +176,12 @@ function toggleTravelInvoicingContinuousReconciliation( ) { const parameters: ToggleTravelInvoicingContinuousReconciliationParams = shouldUseContinuousReconciliation ? { - workspaceAccountID, + policyAccountID: workspaceAccountID, shouldUseContinuousReconciliation, travelInvoicingContinuousReconciliationConnection: connectionName, } : { - workspaceAccountID, + policyAccountID: workspaceAccountID, shouldUseContinuousReconciliation, }; @@ -247,14 +247,13 @@ function toggleTravelInvoicingContinuousReconciliation( } function setTravelInvoicingReconciliationBankAccount( - policyID: string, workspaceAccountID: number, + domainName: string, travelInvoicingReconciliationBankAccountID: string, currentReconciliationBankAccountID?: string, ) { const parameters: SetTravelInvoicingReconciliationBankAccountParams = { - policyID, - workspaceAccountID, + domainName, travelInvoicingReconciliationBankAccountID, }; diff --git a/src/pages/workspace/accounting/reconciliation/DynamicReconciliationAccountSettingsPage.tsx b/src/pages/workspace/accounting/reconciliation/DynamicReconciliationAccountSettingsPage.tsx index aed06cd85b8b..ec3470181238 100644 --- a/src/pages/workspace/accounting/reconciliation/DynamicReconciliationAccountSettingsPage.tsx +++ b/src/pages/workspace/accounting/reconciliation/DynamicReconciliationAccountSettingsPage.tsx @@ -41,6 +41,7 @@ type ReconciliationAccountSettingsRendererParams = { type DynamicReconciliationProps = { policyID: string; workspaceAccountID: number; + domainName: string; bankAccountList: OnyxEntry; goBack: () => void; renderReconciliationAccountSettings: (params: ReconciliationAccountSettingsRendererParams) => React.ReactElement; @@ -49,7 +50,7 @@ type DynamicReconciliationProps = { const policyConnectionsSelector = (policy: OnyxEntry) => policy?.connections; const policyWorkspaceAccountIDSelector = (policy: OnyxEntry) => policy?.workspaceAccountID; -function ExpensifyCardDynamicReconciliation({policyID, workspaceAccountID, bankAccountList, goBack, renderReconciliationAccountSettings}: DynamicReconciliationProps) { +function ExpensifyCardDynamicReconciliation({policyID, workspaceAccountID, domainName, bankAccountList, goBack, renderReconciliationAccountSettings}: DynamicReconciliationProps) { const {translate} = useLocalize(); const defaultFundID = useDefaultFundID(policyID); @@ -62,14 +63,14 @@ function ExpensifyCardDynamicReconciliation({policyID, workspaceAccountID, bankA 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 selectBankAccount = (newBankAccountID?: string) => { if (!newBankAccountID) { return; } - setCardReconciliationAccount(workspaceAccountID, domainName, newBankAccountID, reconciliationBankAccountID); + setCardReconciliationAccount(workspaceAccountID, reconciliationDomainName, newBankAccountID, reconciliationBankAccountID); goBack(); }; @@ -85,7 +86,7 @@ function ExpensifyCardDynamicReconciliation({policyID, workspaceAccountID, bankA }); } -function TravelInvoicingDynamicReconciliation({policyID, workspaceAccountID, bankAccountList, goBack, renderReconciliationAccountSettings}: DynamicReconciliationProps) { +function TravelInvoicingDynamicReconciliation({workspaceAccountID, domainName, bankAccountList, goBack, renderReconciliationAccountSettings}: DynamicReconciliationProps) { const {translate} = useLocalize(); const [travelInvoicingCardSettings] = useOnyx(`${ONYXKEYS.COLLECTION.PRIVATE_EXPENSIFY_CARD_SETTINGS}${workspaceAccountID}`); @@ -98,7 +99,7 @@ function TravelInvoicingDynamicReconciliation({policyID, workspaceAccountID, ban if (!newBankAccountID) { return; } - setTravelInvoicingReconciliationBankAccount(policyID, workspaceAccountID, newBankAccountID, travelInvoicingReconciliationBankAccountID); + setTravelInvoicingReconciliationBankAccount(workspaceAccountID, domainName, newBankAccountID, travelInvoicingReconciliationBankAccountID); goBack(); }; @@ -116,6 +117,7 @@ function DynamicReconciliationAccountSettingsPage({route}: DynamicReconciliation 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}); @@ -165,6 +167,7 @@ function DynamicReconciliationAccountSettingsPage({route}: DynamicReconciliation const dynamicReconciliationProps: DynamicReconciliationProps = { policyID, workspaceAccountID, + domainName, bankAccountList, goBack, renderReconciliationAccountSettings, @@ -175,6 +178,7 @@ function DynamicReconciliationAccountSettingsPage({route}: DynamicReconciliation { expect(spyAPIWrite).toHaveBeenCalledWith( 'ToggleTravelInvoicingContinuousReconciliation', { - workspaceAccountID, + policyAccountID: workspaceAccountID, shouldUseContinuousReconciliation: true, travelInvoicingContinuousReconciliationConnection: connectionName, }, @@ -175,18 +175,17 @@ describe('TravelInvoicing', () => { }); it('setTravelInvoicingReconciliationBankAccount sends the selected bank account and reverts on failure', () => { - const policyID = '123'; const workspaceAccountID = 456; + const domainName = 'expensify_policy_123.expensify.com'; const selectedBankAccountID = 'account-123'; const previousBankAccountID = 'account-111'; - setTravelInvoicingReconciliationBankAccount(policyID, workspaceAccountID, selectedBankAccountID, previousBankAccountID); + setTravelInvoicingReconciliationBankAccount(workspaceAccountID, domainName, selectedBankAccountID, previousBankAccountID); expect(spyAPIWrite).toHaveBeenCalledWith( 'SetTravelInvoicingReconciliationBankAccount', { - policyID, - workspaceAccountID, + domainName, travelInvoicingReconciliationBankAccountID: selectedBankAccountID, }, expect.objectContaining({ From 03e9638855949134efeac551451526622416a533 Mon Sep 17 00:00:00 2001 From: Rodrigo Lino da Costa Date: Thu, 14 May 2026 12:08:13 -0300 Subject: [PATCH 04/13] move to the correct place --- Mobile-Expensify | 2 +- .../advanced/NetSuiteAdvancedPage.tsx | 42 ++++++++++++------- 2 files changed, 27 insertions(+), 17 deletions(-) diff --git a/Mobile-Expensify b/Mobile-Expensify index c3eb60f6c730..bdeb285bc969 160000 --- a/Mobile-Expensify +++ b/Mobile-Expensify @@ -1 +1 @@ -Subproject commit c3eb60f6c730a7f8018911137e6c4f77240fe3d7 +Subproject commit bdeb285bc9697e626bda8867302d2fffedfaf657 diff --git a/src/pages/workspace/accounting/netsuite/advanced/NetSuiteAdvancedPage.tsx b/src/pages/workspace/accounting/netsuite/advanced/NetSuiteAdvancedPage.tsx index 7b1909c0e472..ae5160b7d8db 100644 --- a/src/pages/workspace/accounting/netsuite/advanced/NetSuiteAdvancedPage.tsx +++ b/src/pages/workspace/accounting/netsuite/advanced/NetSuiteAdvancedPage.tsx @@ -135,22 +135,6 @@ function NetSuiteAdvancedPage({policy}: WithPolicyConnectionsProps) { })(), subscribedSettings: [CONST.NETSUITE_CONFIG.AUTO_SYNC, CONST.NETSUITE_CONFIG.ACCOUNTING_METHOD], }, - { - type: 'toggle', - title: translate('workspace.accounting.syncCentralInvoicingSettlements'), - subtitle: travelInvoicingContinuousReconciliation ? travelInvoicingReconciliationBankAccount?.name : undefined, - isActive: !!travelInvoicingContinuousReconciliation, - switchAccessibilityLabel: translate('workspace.accounting.syncCentralInvoicingSettlements'), - onToggle: (isEnabled) => { - toggleTravelInvoicingContinuousReconciliation(workspaceAccountID, isEnabled, CONST.POLICY.CONNECTIONS.NAME.NETSUITE, travelInvoicingContinuousReconciliationConnection); - if (isEnabled) { - navigateToTravelInvoicingReconciliationAccountSettings(); - } - }, - onPress: navigateToTravelInvoicingReconciliationAccountSettings, - pendingAction: travelInvoicingContinuousReconciliationPendingAction, - shouldHide: !isTravelInvoicingEnabled, - }, { type: 'divider', key: 'divider1', @@ -189,6 +173,32 @@ function NetSuiteAdvancedPage({policy}: WithPolicyConnectionsProps) { key: 'divider2', shouldHide: shouldHideReimbursedReportsSection(config), }, + { + type: 'toggle', + title: translate('workspace.accounting.syncCentralInvoicingSettlements'), + isActive: !!travelInvoicingContinuousReconciliation, + switchAccessibilityLabel: translate('workspace.accounting.syncCentralInvoicingSettlements'), + onToggle: (isEnabled) => { + toggleTravelInvoicingContinuousReconciliation(workspaceAccountID, isEnabled, CONST.POLICY.CONNECTIONS.NAME.NETSUITE, travelInvoicingContinuousReconciliationConnection); + if (isEnabled) { + navigateToTravelInvoicingReconciliationAccountSettings(); + } + }, + pendingAction: travelInvoicingContinuousReconciliationPendingAction, + shouldHide: !isTravelInvoicingEnabled, + }, + { + type: 'menuitem', + description: translate('workspace.expensifyCard.settlementAccount'), + onPress: navigateToTravelInvoicingReconciliationAccountSettings, + title: travelInvoicingReconciliationBankAccount?.name, + shouldHide: !isTravelInvoicingEnabled || !travelInvoicingContinuousReconciliation, + }, + { + type: 'divider', + key: 'dividerTravelInvoicing', + shouldHide: !isTravelInvoicingEnabled, + }, { type: 'toggle', title: translate('workspace.netsuite.advancedConfig.inviteEmployees'), From 0c8e7ce50f9802790c35296ed05ccd3269a718f9 Mon Sep 17 00:00:00 2001 From: Rodrigo Lino da Costa Date: Thu, 14 May 2026 12:29:08 -0300 Subject: [PATCH 05/13] remove es translation and let parrot take care of it --- src/languages/es.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/languages/es.ts b/src/languages/es.ts index 5b6a41757266..06054625dc18 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -6155,18 +6155,14 @@ ${amount} para ${merchant} - ${date}`, cardReconciliation: 'Conciliación de tarjetas', reconciliationAccount: 'Cuenta de conciliación', continuousReconciliation: 'Conciliación continua', - syncCentralInvoicingSettlements: 'Sync central invoicing settlements', saveHoursOnReconciliation: 'Ahorra horas de conciliación en cada período contable haciendo que Expensify concilie continuamente los extractos y liquidaciones de la Tarjeta Expensify en tu nombre.', enableContinuousReconciliation: (accountingAdvancedSettingsLink, connectionName) => `Para activar la Conciliación Continua, activa la auto-sync para ${connectionName}.`, chooseReconciliationAccount: { chooseBankAccount: 'Elige la cuenta bancaria con la que se conciliarán los pagos de tu Tarjeta Expensify.', - chooseTravelInvoicingBankAccount: 'Choose the bank account that your central invoicing payments will be reconciled against.', 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.`, - travelInvoicingSettlementAccountReconciliation: (lastFourPAN) => - `Make sure this account matches your central invoicing settlement account (ending in ${lastFourPAN}) so Continuous Reconciliation works properly.`, }, }, card: { From a441a6273d445307a8675a95e9eb9c90aa404754 Mon Sep 17 00:00:00 2001 From: Rodrigo Lino da Costa Date: Thu, 14 May 2026 12:36:10 -0300 Subject: [PATCH 06/13] translations --- src/languages/de.ts | 4 ++++ src/languages/es.ts | 20 ++++---------------- src/languages/fr.ts | 4 ++++ src/languages/it.ts | 4 ++++ src/languages/ja.ts | 4 ++++ src/languages/nl.ts | 4 ++++ src/languages/pl.ts | 4 ++++ src/languages/pt-BR.ts | 4 ++++ src/languages/zh-hans.ts | 3 +++ 9 files changed, 35 insertions(+), 16 deletions(-) diff --git a/src/languages/de.ts b/src/languages/de.ts index 04520cdc4801..d20518b74fc6 100644 --- a/src/languages/de.ts +++ b/src/languages/de.ts @@ -6365,7 +6365,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, mit dem Ihre zentralen Rechnungseingangszahlungen abgestimmt werden.', + travelInvoicingSettlementAccountReconciliation: (lastFourPAN: string) => + `Stellen Sie sicher, dass dieses Konto mit Ihrem zentralen Rechnungsabwicklungskonto (endet auf ${lastFourPAN}) übereinstimmt, damit die kontinuierliche Abstimmung ordnungsgemäß funktioniert.`, }, + syncCentralInvoicingSettlements: 'Zentrale Rechnungssiedlungen synchronisieren', }, export: { notReadyHeading: 'Nicht bereit zum Export', diff --git a/src/languages/es.ts b/src/languages/es.ts index 2b8a8449a7d3..21b145bd520c 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -1690,10 +1690,8 @@ const translations: TranslationDeepObject = { backdropLabel: 'Fondo del Modal', }, nextStep: { - /* eslint-disable @typescript-eslint/no-unused-vars */ message: { [CONST.NEXT_STEP.MESSAGE_KEY.WAITING_TO_ADD_TRANSACTIONS]: (actor, actorType, _eta, _etaType) => { - // eslint-disable-next-line default-case switch (actorType) { case CONST.NEXT_STEP.ACTOR_TYPE.CURRENT_USER: return `Esperando a que añadas gastos.`; @@ -1704,7 +1702,6 @@ const translations: TranslationDeepObject = { } }, [CONST.NEXT_STEP.MESSAGE_KEY.WAITING_TO_SUBMIT]: (actor, actorType, _eta, _etaType) => { - // eslint-disable-next-line default-case switch (actorType) { case CONST.NEXT_STEP.ACTOR_TYPE.CURRENT_USER: return `Esperando a que envíes los gastos.`; @@ -1716,7 +1713,6 @@ const translations: TranslationDeepObject = { }, [CONST.NEXT_STEP.MESSAGE_KEY.NO_FURTHER_ACTION]: (_actor, _actorType, _eta, _etaType) => `¡No se requiere ninguna acción adicional!`, [CONST.NEXT_STEP.MESSAGE_KEY.WAITING_FOR_SUBMITTER_ACCOUNT]: (actor, actorType, _eta, _etaType) => { - // eslint-disable-next-line default-case switch (actorType) { case CONST.NEXT_STEP.ACTOR_TYPE.CURRENT_USER: return `Esperando a que añadas una cuenta bancaria.`; @@ -1731,7 +1727,6 @@ const translations: TranslationDeepObject = { if (eta) { formattedETA = etaType === CONST.NEXT_STEP.ETA_TYPE.DATE_TIME ? ` el ${eta} de cada mes` : ` ${eta}`; } - // eslint-disable-next-line default-case switch (actorType) { case CONST.NEXT_STEP.ACTOR_TYPE.CURRENT_USER: return `Esperando a que tus gastos se envíen automáticamente${formattedETA}.`; @@ -1742,7 +1737,6 @@ const translations: TranslationDeepObject = { } }, [CONST.NEXT_STEP.MESSAGE_KEY.WAITING_TO_FIX_ISSUES]: (actor, actorType, _eta, _etaType) => { - // eslint-disable-next-line default-case switch (actorType) { case CONST.NEXT_STEP.ACTOR_TYPE.CURRENT_USER: return `Esperando a que soluciones ellos problemas.`; @@ -1753,7 +1747,6 @@ const translations: TranslationDeepObject = { } }, [CONST.NEXT_STEP.MESSAGE_KEY.WAITING_TO_APPROVE]: (actor, actorType, _eta, _etaType) => { - // eslint-disable-next-line default-case switch (actorType) { case CONST.NEXT_STEP.ACTOR_TYPE.CURRENT_USER: return `Esperando a que apruebes los gastos.`; @@ -1764,7 +1757,6 @@ const translations: TranslationDeepObject = { } }, [CONST.NEXT_STEP.MESSAGE_KEY.WAITING_TO_EXPORT]: (actor, actorType, _eta, _etaType) => { - // eslint-disable-next-line default-case switch (actorType) { case CONST.NEXT_STEP.ACTOR_TYPE.CURRENT_USER: return `Esperando a que exportes este informe.`; @@ -1775,7 +1767,6 @@ const translations: TranslationDeepObject = { } }, [CONST.NEXT_STEP.MESSAGE_KEY.WAITING_TO_PAY]: (actor, actorType, _eta, _etaType) => { - // eslint-disable-next-line default-case switch (actorType) { case CONST.NEXT_STEP.ACTOR_TYPE.CURRENT_USER: return `Esperando a que pagues los gastos.`; @@ -1786,7 +1777,6 @@ const translations: TranslationDeepObject = { } }, [CONST.NEXT_STEP.MESSAGE_KEY.WAITING_FOR_POLICY_BANK_ACCOUNT]: (actor, actorType, _eta, _etaType) => { - // eslint-disable-next-line default-case switch (actorType) { case CONST.NEXT_STEP.ACTOR_TYPE.CURRENT_USER: return `Esperando a que termines de configurar una cuenta bancaria de empresa.`; @@ -1806,7 +1796,6 @@ const translations: TranslationDeepObject = { [CONST.NEXT_STEP.MESSAGE_KEY.SUBMITTING_TO_SELF]: (_actor, _actorType, _eta, _etaType) => `¡Ups! Parece que estás enviando el informe a ti mismo. Aprobar tus propios informes está prohibido por tu espacio de trabajo. Por favor, envía este informe a otra persona o contacta a tu administrador para cambiar la persona a la que lo envías.`, [CONST.NEXT_STEP.MESSAGE_KEY.REJECTED_REPORT]: (actor, actorType) => { - // eslint-disable-next-line default-case switch (actorType) { case CONST.NEXT_STEP.ACTOR_TYPE.CURRENT_USER: return `Este informe fue rechazado. Esperando a que corrijas los problemas y lo vuelvas a enviar manualmente.`; @@ -2461,7 +2450,6 @@ ${amount} para ${merchant} - ${date}`, two: 'º', few: 'º', other: 'º', - /* eslint-disable @typescript-eslint/naming-convention */ '1': 'Primero', '2': 'Segundo', '3': 'Tercero', @@ -2472,7 +2460,6 @@ ${amount} para ${merchant} - ${date}`, '8': 'Octavo', '9': 'Noveno', '10': 'Décimo', - /* eslint-enable @typescript-eslint/naming-convention */ }, }, approverInMultipleWorkflows: 'Este miembro ya pertenece a otro flujo de aprobación. Cualquier actualización aquí se reflejará allí también.', @@ -6163,7 +6150,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 conciliará el pago de tu facturación centralizada.', + travelInvoicingSettlementAccountReconciliation: (lastFourPAN: string) => + `Asegúrate de que esta cuenta coincida con tu cuenta central de liquidación de facturas (terminada en ${lastFourPAN}) para que la Reconciliación Continua funcione correctamente.`, }, + syncCentralInvoicingSettlements: 'Sincronizar liquidaciones de facturación central', }, card: { issueCard: 'Emitir tarjeta', @@ -6936,7 +6927,6 @@ ${amount} para ${merchant} - ${date}`, restrictedDescription: 'Sólo las personas en tu espacio de trabajo pueden encontrar esta sala', privateDescription: 'Sólo las personas que están invitadas a esta sala pueden encontrarla', publicDescription: 'Cualquier persona puede unirse a esta sala', - // eslint-disable-next-line @typescript-eslint/naming-convention public_announceDescription: 'Cualquier persona puede unirse a esta sala', createRoom: 'Crea una sala de chat', roomAlreadyExistsError: 'Ya existe una sala con este nombre', @@ -6956,7 +6946,6 @@ ${amount} para ${merchant} - ${date}`, restricted: 'Espacio de trabajo', private: 'Privada', public: 'Público', - // eslint-disable-next-line @typescript-eslint/naming-convention public_announce: 'Anuncio Público', }, }, @@ -7271,7 +7260,6 @@ ${amount} para ${merchant} - ${date}`, updatedDefaultTitle: (newDefaultTitle, oldDefaultTitle) => `cambió la fórmula personalizada del nombre del informe a "${newDefaultTitle}" (previamente "${oldDefaultTitle}")`, updatedOwnership: (oldOwnerEmail, oldOwnerName, policyName) => `asumió la propiedad del espacio de trabajo ${policyName} de ${oldOwnerName} (${oldOwnerEmail})`, updatedAutoHarvesting: (enabled) => `${enabled ? 'habilitó' : 'deshabilitó'} el envío programado`, - // eslint-disable-next-line @typescript-eslint/max-params updatedIndividualBudgetNotification: ( budgetAmount, budgetFrequency, diff --git a/src/languages/fr.ts b/src/languages/fr.ts index 594787e50416..ca2adc4c57de 100644 --- a/src/languages/fr.ts +++ b/src/languages/fr.ts @@ -6387,7 +6387,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 avec lequel les paiements de votre facturation centralisée seront rapprochés.', + travelInvoicingSettlementAccountReconciliation: (lastFourPAN: string) => + `Assurez-vous que ce compte correspond à votre compte central de règlement de facturation (se terminant par ${lastFourPAN}) afin que la Réconciliation Continue fonctionne correctement.`, }, + syncCentralInvoicingSettlements: 'Synchroniser les règlements de facturation centralisée', }, export: { notReadyHeading: 'Pas prêt à être exporté', diff --git a/src/languages/it.ts b/src/languages/it.ts index b6dad031d9e2..b3b312b4c09a 100644 --- a/src/languages/it.ts +++ b/src/languages/it.ts @@ -6355,7 +6355,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 con cui verranno riconciliati i pagamenti della fatturazione centralizzata.', + travelInvoicingSettlementAccountReconciliation: (lastFourPAN: string) => + `Assicurati che questo conto corrisponda al tuo conto centrale di regolamento delle fatture (che termina con ${lastFourPAN}) così che la Riconciliazione continua funzioni correttamente.`, }, + syncCentralInvoicingSettlements: 'Sincronizza le liquidazioni di fatturazione centrale', }, export: { notReadyHeading: 'Non pronto per l’esportazione', diff --git a/src/languages/ja.ts b/src/languages/ja.ts index e060766dd971..822d6b1aec78 100644 --- a/src/languages/ja.ts +++ b/src/languages/ja.ts @@ -6284,7 +6284,11 @@ _詳しい手順については、[ヘルプサイトをご覧ください](${CO chooseBankAccount: 'Expensify カードの支払いを照合する銀行口座を選択してください。', settlementAccountReconciliation: (settlementAccountUrl: string, lastFourPAN: string) => `継続消込が正しく機能するように、この口座が、末尾が ${lastFourPAN} のExpensify カード精算口座と一致していることを確認してください。`, + chooseTravelInvoicingBankAccount: '集中請求の支払いを照合する銀行口座を選択してください。', + travelInvoicingSettlementAccountReconciliation: (lastFourPAN: string) => + `Continuous Reconciliation が正しく機能するよう、この口座が中央請求書決済口座(末尾 ${lastFourPAN})と一致していることを確認してください。`, }, + syncCentralInvoicingSettlements: '中央請求の精算を同期', }, export: { notReadyHeading: 'エクスポートの準備ができていません', diff --git a/src/languages/nl.ts b/src/languages/nl.ts index 896d6d803e27..684244763fa4 100644 --- a/src/languages/nl.ts +++ b/src/languages/nl.ts @@ -6333,7 +6333,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 je centrale facturatiebetalingen worden afgestemd.', + travelInvoicingSettlementAccountReconciliation: (lastFourPAN: string) => + `Zorg ervoor dat deze rekening overeenkomt met je centrale afrekenrekening voor facturatie (die eindigt op ${lastFourPAN}), zodat Continue Afstemming goed werkt.`, }, + syncCentralInvoicingSettlements: 'Centrale factureringsafrekeningen synchroniseren', }, export: { notReadyHeading: 'Niet klaar om te exporteren', diff --git a/src/languages/pl.ts b/src/languages/pl.ts index 0aecdc1fc371..919e7123d593 100644 --- a/src/languages/pl.ts +++ b/src/languages/pl.ts @@ -6326,7 +6326,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 z centralnej fakturowości.', + travelInvoicingSettlementAccountReconciliation: (lastFourPAN: string) => + `Upewnij się, że to konto jest takie samo jak twoje główne konto rozliczeniowe fakturowania (kończące się na ${lastFourPAN}), żeby Ciągłe Uzgadnianie działało poprawnie.`, }, + syncCentralInvoicingSettlements: 'Synchronizuj rozliczenia centralnego fakturowania', }, export: { notReadyHeading: 'Niegotowe do eksportu', diff --git a/src/languages/pt-BR.ts b/src/languages/pt-BR.ts index fda802d5660a..75f1a4bf1968 100644 --- a/src/languages/pt-BR.ts +++ b/src/languages/pt-BR.ts @@ -6334,7 +6334,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 da sua cobrança centralizada serão conciliados.', + travelInvoicingSettlementAccountReconciliation: (lastFourPAN: string) => + `Certifique-se de que esta conta corresponda à sua conta central de liquidação de faturas (terminando em ${lastFourPAN}) para que a Conciliação Contínua funcione corretamente.`, }, + syncCentralInvoicingSettlements: 'Sincronizar liquidações de faturamento central', }, export: { notReadyHeading: 'Não está pronto para exportar', diff --git a/src/languages/zh-hans.ts b/src/languages/zh-hans.ts index 14cc6fe243a6..78a967bc89e3 100644 --- a/src/languages/zh-hans.ts +++ b/src/languages/zh-hans.ts @@ -6179,7 +6179,10 @@ _如需更详细的说明,请[访问我们的帮助网站](${CONST.NETSUITE_IM chooseBankAccount: '选择用于对账 Expensify 卡付款的银行账户。', settlementAccountReconciliation: (settlementAccountUrl: string, lastFourPAN: string) => `请确保此账户与您的Expensify 卡结算账户(末尾为 ${lastFourPAN})一致,以便持续对账功能正常运行。`, + chooseTravelInvoicingBankAccount: '选择用于核对集中开票收款的银行账户。', + travelInvoicingSettlementAccountReconciliation: (lastFourPAN: string) => `请确保此账户与您的集中开票结算账户(以 ${lastFourPAN} 结尾)一致,以便持续对账功能正常运行。`, }, + syncCentralInvoicingSettlements: '同步集中开票结算', }, export: { notReadyHeading: '尚未准备好导出', From 397006f30060358a7ae8f790a341eb256ac884b4 Mon Sep 17 00:00:00 2001 From: Rodrigo Lino da Costa Date: Thu, 14 May 2026 13:29:57 -0300 Subject: [PATCH 07/13] add back // eslint-disable-next-line --- src/languages/es.ts | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/languages/es.ts b/src/languages/es.ts index 21b145bd520c..227e8bb0fe87 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -1690,8 +1690,10 @@ const translations: TranslationDeepObject = { backdropLabel: 'Fondo del Modal', }, nextStep: { + /* eslint-disable @typescript-eslint/no-unused-vars */ message: { [CONST.NEXT_STEP.MESSAGE_KEY.WAITING_TO_ADD_TRANSACTIONS]: (actor, actorType, _eta, _etaType) => { + // eslint-disable-next-line default-case switch (actorType) { case CONST.NEXT_STEP.ACTOR_TYPE.CURRENT_USER: return `Esperando a que añadas gastos.`; @@ -1702,6 +1704,7 @@ const translations: TranslationDeepObject = { } }, [CONST.NEXT_STEP.MESSAGE_KEY.WAITING_TO_SUBMIT]: (actor, actorType, _eta, _etaType) => { + // eslint-disable-next-line default-case switch (actorType) { case CONST.NEXT_STEP.ACTOR_TYPE.CURRENT_USER: return `Esperando a que envíes los gastos.`; @@ -1713,6 +1716,7 @@ const translations: TranslationDeepObject = { }, [CONST.NEXT_STEP.MESSAGE_KEY.NO_FURTHER_ACTION]: (_actor, _actorType, _eta, _etaType) => `¡No se requiere ninguna acción adicional!`, [CONST.NEXT_STEP.MESSAGE_KEY.WAITING_FOR_SUBMITTER_ACCOUNT]: (actor, actorType, _eta, _etaType) => { + // eslint-disable-next-line default-case switch (actorType) { case CONST.NEXT_STEP.ACTOR_TYPE.CURRENT_USER: return `Esperando a que añadas una cuenta bancaria.`; @@ -1727,6 +1731,7 @@ const translations: TranslationDeepObject = { if (eta) { formattedETA = etaType === CONST.NEXT_STEP.ETA_TYPE.DATE_TIME ? ` el ${eta} de cada mes` : ` ${eta}`; } + // eslint-disable-next-line default-case switch (actorType) { case CONST.NEXT_STEP.ACTOR_TYPE.CURRENT_USER: return `Esperando a que tus gastos se envíen automáticamente${formattedETA}.`; @@ -1737,6 +1742,7 @@ const translations: TranslationDeepObject = { } }, [CONST.NEXT_STEP.MESSAGE_KEY.WAITING_TO_FIX_ISSUES]: (actor, actorType, _eta, _etaType) => { + // eslint-disable-next-line default-case switch (actorType) { case CONST.NEXT_STEP.ACTOR_TYPE.CURRENT_USER: return `Esperando a que soluciones ellos problemas.`; @@ -1747,6 +1753,7 @@ const translations: TranslationDeepObject = { } }, [CONST.NEXT_STEP.MESSAGE_KEY.WAITING_TO_APPROVE]: (actor, actorType, _eta, _etaType) => { + // eslint-disable-next-line default-case switch (actorType) { case CONST.NEXT_STEP.ACTOR_TYPE.CURRENT_USER: return `Esperando a que apruebes los gastos.`; @@ -1757,6 +1764,7 @@ const translations: TranslationDeepObject = { } }, [CONST.NEXT_STEP.MESSAGE_KEY.WAITING_TO_EXPORT]: (actor, actorType, _eta, _etaType) => { + // eslint-disable-next-line default-case switch (actorType) { case CONST.NEXT_STEP.ACTOR_TYPE.CURRENT_USER: return `Esperando a que exportes este informe.`; @@ -1767,6 +1775,7 @@ const translations: TranslationDeepObject = { } }, [CONST.NEXT_STEP.MESSAGE_KEY.WAITING_TO_PAY]: (actor, actorType, _eta, _etaType) => { + // eslint-disable-next-line default-case switch (actorType) { case CONST.NEXT_STEP.ACTOR_TYPE.CURRENT_USER: return `Esperando a que pagues los gastos.`; @@ -1777,6 +1786,7 @@ const translations: TranslationDeepObject = { } }, [CONST.NEXT_STEP.MESSAGE_KEY.WAITING_FOR_POLICY_BANK_ACCOUNT]: (actor, actorType, _eta, _etaType) => { + // eslint-disable-next-line default-case switch (actorType) { case CONST.NEXT_STEP.ACTOR_TYPE.CURRENT_USER: return `Esperando a que termines de configurar una cuenta bancaria de empresa.`; @@ -1796,6 +1806,7 @@ const translations: TranslationDeepObject = { [CONST.NEXT_STEP.MESSAGE_KEY.SUBMITTING_TO_SELF]: (_actor, _actorType, _eta, _etaType) => `¡Ups! Parece que estás enviando el informe a ti mismo. Aprobar tus propios informes está prohibido por tu espacio de trabajo. Por favor, envía este informe a otra persona o contacta a tu administrador para cambiar la persona a la que lo envías.`, [CONST.NEXT_STEP.MESSAGE_KEY.REJECTED_REPORT]: (actor, actorType) => { + // eslint-disable-next-line default-case switch (actorType) { case CONST.NEXT_STEP.ACTOR_TYPE.CURRENT_USER: return `Este informe fue rechazado. Esperando a que corrijas los problemas y lo vuelvas a enviar manualmente.`; @@ -2450,6 +2461,7 @@ ${amount} para ${merchant} - ${date}`, two: 'º', few: 'º', other: 'º', + /* eslint-disable @typescript-eslint/naming-convention */ '1': 'Primero', '2': 'Segundo', '3': 'Tercero', @@ -2460,6 +2472,7 @@ ${amount} para ${merchant} - ${date}`, '8': 'Octavo', '9': 'Noveno', '10': 'Décimo', + /* eslint-enable @typescript-eslint/naming-convention */ }, }, approverInMultipleWorkflows: 'Este miembro ya pertenece a otro flujo de aprobación. Cualquier actualización aquí se reflejará allí también.', @@ -6927,6 +6940,7 @@ ${amount} para ${merchant} - ${date}`, restrictedDescription: 'Sólo las personas en tu espacio de trabajo pueden encontrar esta sala', privateDescription: 'Sólo las personas que están invitadas a esta sala pueden encontrarla', publicDescription: 'Cualquier persona puede unirse a esta sala', + // eslint-disable-next-line @typescript-eslint/naming-convention public_announceDescription: 'Cualquier persona puede unirse a esta sala', createRoom: 'Crea una sala de chat', roomAlreadyExistsError: 'Ya existe una sala con este nombre', @@ -6946,6 +6960,7 @@ ${amount} para ${merchant} - ${date}`, restricted: 'Espacio de trabajo', private: 'Privada', public: 'Público', + // eslint-disable-next-line @typescript-eslint/naming-convention public_announce: 'Anuncio Público', }, }, @@ -7260,6 +7275,7 @@ ${amount} para ${merchant} - ${date}`, updatedDefaultTitle: (newDefaultTitle, oldDefaultTitle) => `cambió la fórmula personalizada del nombre del informe a "${newDefaultTitle}" (previamente "${oldDefaultTitle}")`, updatedOwnership: (oldOwnerEmail, oldOwnerName, policyName) => `asumió la propiedad del espacio de trabajo ${policyName} de ${oldOwnerName} (${oldOwnerEmail})`, updatedAutoHarvesting: (enabled) => `${enabled ? 'habilitó' : 'deshabilitó'} el envío programado`, + // eslint-disable-next-line @typescript-eslint/max-params updatedIndividualBudgetNotification: ( budgetAmount, budgetFrequency, From a5d1d1e594608d2f20d5569550bd8d388c6ddf05 Mon Sep 17 00:00:00 2001 From: Rodrigo Lino da Costa Date: Thu, 14 May 2026 15:26:25 -0300 Subject: [PATCH 08/13] disable sync if autoSync is disabled --- .../accounting/netsuite/advanced/NetSuiteAdvancedPage.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/pages/workspace/accounting/netsuite/advanced/NetSuiteAdvancedPage.tsx b/src/pages/workspace/accounting/netsuite/advanced/NetSuiteAdvancedPage.tsx index ae5160b7d8db..c095952650b2 100644 --- a/src/pages/workspace/accounting/netsuite/advanced/NetSuiteAdvancedPage.tsx +++ b/src/pages/workspace/accounting/netsuite/advanced/NetSuiteAdvancedPage.tsx @@ -56,6 +56,7 @@ function NetSuiteAdvancedPage({policy}: WithPolicyConnectionsProps) { 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}`); @@ -122,11 +123,11 @@ function NetSuiteAdvancedPage({policy}: WithPolicyConnectionsProps) { const menuItems: ExtendedMenuItemWithSubscribedSettings[] = [ { type: 'menuitem', - title: autoSyncConfig?.autoSync?.enabled ? translate('common.enabled') : translate('common.disabled'), + title: autoSync ? translate('common.enabled') : translate('common.disabled'), description: translate('workspace.accounting.autoSync'), onPress: () => Navigation.navigate(createDynamicRoute(DYNAMIC_ROUTES.NETSUITE_AUTO_SYNC.path)), hintText: (() => { - if (!autoSyncConfig?.autoSync?.enabled) { + if (!autoSync) { return undefined; } return translate( @@ -178,6 +179,7 @@ function NetSuiteAdvancedPage({policy}: WithPolicyConnectionsProps) { title: translate('workspace.accounting.syncCentralInvoicingSettlements'), isActive: !!travelInvoicingContinuousReconciliation, switchAccessibilityLabel: translate('workspace.accounting.syncCentralInvoicingSettlements'), + disabled: !autoSync, onToggle: (isEnabled) => { toggleTravelInvoicingContinuousReconciliation(workspaceAccountID, isEnabled, CONST.POLICY.CONNECTIONS.NAME.NETSUITE, travelInvoicingContinuousReconciliationConnection); if (isEnabled) { From 3fe8c86709be20647ee0a64ea123a75b38cdbf58 Mon Sep 17 00:00:00 2001 From: Rodrigo Lino da Costa Date: Thu, 14 May 2026 15:30:26 -0300 Subject: [PATCH 09/13] React best practices --- ...namicReconciliationAccountSettingsPage.tsx | 175 ++++++++++-------- 1 file changed, 98 insertions(+), 77 deletions(-) diff --git a/src/pages/workspace/accounting/reconciliation/DynamicReconciliationAccountSettingsPage.tsx b/src/pages/workspace/accounting/reconciliation/DynamicReconciliationAccountSettingsPage.tsx index ec3470181238..c45550774e13 100644 --- a/src/pages/workspace/accounting/reconciliation/DynamicReconciliationAccountSettingsPage.tsx +++ b/src/pages/workspace/accounting/reconciliation/DynamicReconciliationAccountSettingsPage.tsx @@ -28,10 +28,15 @@ 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 ReconciliationAccountSettingsRendererParams = { +type ReconciliationAccountSettingsLayoutProps = { + policyID: string; + connectionName: ConnectionName; + connectionBankAccounts: ReturnType; + goBack: () => void; description: string; html: string; selectedBankAccountID?: string; @@ -44,13 +49,60 @@ type DynamicReconciliationProps = { domainName: string; bankAccountList: OnyxEntry; goBack: () => void; - renderReconciliationAccountSettings: (params: ReconciliationAccountSettingsRendererParams) => React.ReactElement; + connectionName: ConnectionName; + connectionBankAccounts: ReturnType; }; const policyConnectionsSelector = (policy: OnyxEntry) => policy?.connections; const policyWorkspaceAccountIDSelector = (policy: OnyxEntry) => policy?.workspaceAccountID; -function ExpensifyCardDynamicReconciliation({policyID, workspaceAccountID, domainName, bankAccountList, goBack, renderReconciliationAccountSettings}: DynamicReconciliationProps) { +function ReconciliationAccountSettingsLayout({ + policyID, + connectionName, + connectionBankAccounts, + goBack, + description, + html, + selectedBankAccountID, + onSelectBankAccount, +}: ReconciliationAccountSettingsLayoutProps) { + const styles = useThemeStyles(); + + 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); @@ -74,19 +126,25 @@ function ExpensifyCardDynamicReconciliation({policyID, workspaceAccountID, domai goBack(); }; - return renderReconciliationAccountSettings({ - description: translate('workspace.accounting.chooseReconciliationAccount.chooseBankAccount'), - html: translate( - 'workspace.accounting.chooseReconciliationAccount.settlementAccountReconciliation', - `${environmentURL}${createDynamicRoute(DYNAMIC_ROUTES.WORKSPACE_EXPENSIFY_CARD_SETTINGS_ACCOUNT.path)}`, - settlementAccountEnding, - ), - selectedBankAccountID: reconciliationBankAccountID, - onSelectBankAccount: selectBankAccount, - }); + return ( + + ); } -function TravelInvoicingDynamicReconciliation({workspaceAccountID, domainName, bankAccountList, goBack, renderReconciliationAccountSettings}: DynamicReconciliationProps) { +function TravelInvoicingDynamicReconciliation({policyID, workspaceAccountID, domainName, bankAccountList, goBack, connectionName, connectionBankAccounts}: DynamicReconciliationProps) { const {translate} = useLocalize(); const [travelInvoicingCardSettings] = useOnyx(`${ONYXKEYS.COLLECTION.PRIVATE_EXPENSIFY_CARD_SETTINGS}${workspaceAccountID}`); @@ -103,17 +161,22 @@ function TravelInvoicingDynamicReconciliation({workspaceAccountID, domainName, b goBack(); }; - return renderReconciliationAccountSettings({ - description: translate('workspace.accounting.chooseReconciliationAccount.chooseTravelInvoicingBankAccount'), - html: translate('workspace.accounting.chooseReconciliationAccount.travelInvoicingSettlementAccountReconciliation', settlementAccountEnding), - selectedBankAccountID: travelInvoicingReconciliationBankAccountID, - onSelectBankAccount: selectBankAccount, - }); + return ( + + ); } function DynamicReconciliationAccountSettingsPage({route}: DynamicReconciliationAccountSettingsPageProps) { const {policyID, connection, reconciliationAccountSettingsType} = route.params; - const styles = useThemeStyles(); const backPath = useDynamicBackPath(DYNAMIC_ROUTES.WORKSPACE_ACCOUNTING_RECONCILIATION_ACCOUNT_SETTINGS.path); const connectionName = getConnectionNameFromRouteParam(connection); @@ -129,71 +192,29 @@ function DynamicReconciliationAccountSettingsPage({route}: DynamicReconciliation Navigation.goBack(backPath); }, [backPath]); - const renderReconciliationAccountSettings = ({description, html, selectedBankAccountID, onSelectBankAccount}: ReconciliationAccountSettingsRendererParams) => { - 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} - /> - - ); - }; - - const dynamicReconciliationProps: DynamicReconciliationProps = { - policyID, - workspaceAccountID, - domainName, - bankAccountList, - goBack, - renderReconciliationAccountSettings, - }; - if (reconciliationAccountSettingsType === RECONCILIATION_ACCOUNT_SETTINGS_TYPE.TRAVEL_INVOICING) { return ( ); } return ( ); } From d0a63f92a216c852f674e520233d5e0a5f6542f9 Mon Sep 17 00:00:00 2001 From: Rodrigo Lino da Costa Date: Fri, 15 May 2026 11:14:39 -0300 Subject: [PATCH 10/13] Address PR comments --- src/languages/en.ts | 6 +++--- src/libs/Navigation/types.ts | 3 ++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/languages/en.ts b/src/languages/en.ts index 353efa62ed08..1d7506711d3d 100644 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -6373,18 +6373,18 @@ const translations = { cardReconciliation: 'Card reconciliation', reconciliationAccount: 'Reconciliation account', continuousReconciliation: 'Continuous Reconciliation', - syncCentralInvoicingSettlements: 'Sync central invoicing settlements', + syncCentralInvoicingSettlements: '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 central invoicing 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 central invoicing settlement account (ending in ${lastFourPAN}) so Continuous Reconciliation works properly.`, + `Make sure this account matches your travel invoicing settlement account (ending in ${lastFourPAN}) so Continuous Reconciliation works properly.`, }, }, hr: { diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index 28d59a1bd6b3..7fd480d26552 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'; @@ -1105,7 +1106,7 @@ type SettingsNavigatorParamList = { [SCREENS.WORKSPACE.ACCOUNTING.DYNAMIC_RECONCILIATION_ACCOUNT_SETTINGS]: { policyID: string; connection: ValueOf; - reconciliationAccountSettingsType?: string; + reconciliationAccountSettingsType?: ValueOf; }; [SCREENS.TWO_FACTOR_AUTH.DISABLED]: undefined; [SCREENS.TWO_FACTOR_AUTH.DISABLE]: undefined; From f4a819181f0c7dc042d7cb11fa351e2888677207 Mon Sep 17 00:00:00 2001 From: Rodrigo Lino da Costa Date: Fri, 15 May 2026 11:23:20 -0300 Subject: [PATCH 11/13] cleanup from PR comments --- .../accounting/netsuite/advanced/NetSuiteAdvancedPage.tsx | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/pages/workspace/accounting/netsuite/advanced/NetSuiteAdvancedPage.tsx b/src/pages/workspace/accounting/netsuite/advanced/NetSuiteAdvancedPage.tsx index c095952650b2..71d8dc3ea0e1 100644 --- a/src/pages/workspace/accounting/netsuite/advanced/NetSuiteAdvancedPage.tsx +++ b/src/pages/workspace/accounting/netsuite/advanced/NetSuiteAdvancedPage.tsx @@ -1,4 +1,3 @@ -import {isExpensifyCardContinuousReconciliationEnabledSelector} from '@selectors/Card'; import {CONST as COMMON_CONST} from 'expensify-common'; import React, {useMemo} from 'react'; import {View} from 'react-native'; @@ -62,9 +61,7 @@ function NetSuiteAdvancedPage({policy}: WithPolicyConnectionsProps) { 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}`, { - selector: isExpensifyCardContinuousReconciliationEnabledSelector, - }); + 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}`); @@ -191,7 +188,7 @@ function NetSuiteAdvancedPage({policy}: WithPolicyConnectionsProps) { }, { type: 'menuitem', - description: translate('workspace.expensifyCard.settlementAccount'), + description: translate('workspace.accounting.reconciliationAccount'), onPress: navigateToTravelInvoicingReconciliationAccountSettings, title: travelInvoicingReconciliationBankAccount?.name, shouldHide: !isTravelInvoicingEnabled || !travelInvoicingContinuousReconciliation, From ff484270f7f29154e9f53ca06dc464e25bf42494 Mon Sep 17 00:00:00 2001 From: Rodrigo Lino da Costa Date: Fri, 15 May 2026 11:39:06 -0300 Subject: [PATCH 12/13] update trnaslatiosn --- src/languages/de.ts | 6 +++--- src/languages/es.ts | 22 +++------------------- src/languages/fr.ts | 6 +++--- src/languages/it.ts | 6 +++--- src/languages/ja.ts | 6 +++--- src/languages/nl.ts | 6 +++--- src/languages/pl.ts | 6 +++--- src/languages/pt-BR.ts | 6 +++--- src/languages/zh-hans.ts | 6 +++--- 9 files changed, 27 insertions(+), 43 deletions(-) diff --git a/src/languages/de.ts b/src/languages/de.ts index d20518b74fc6..d57506be911d 100644 --- a/src/languages/de.ts +++ b/src/languages/de.ts @@ -6365,11 +6365,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, mit dem Ihre zentralen Rechnungseingangszahlungen abgestimmt werden.', + 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 zentralen Rechnungsabwicklungskonto (endet auf ${lastFourPAN}) übereinstimmt, damit die kontinuierliche Abstimmung ordnungsgemäß funktioniert.`, + `Stellen Sie sicher, dass dieses Konto mit Ihrem Abrechnungskonto für Reiseabrechnungen (endet auf ${lastFourPAN}) übereinstimmt, damit die kontinuierliche Abstimmung ordnungsgemäß funktioniert.`, }, - syncCentralInvoicingSettlements: 'Zentrale Rechnungssiedlungen synchronisieren', + syncCentralInvoicingSettlements: 'Reiseabrechnungs­abgleiche synchronisieren', }, export: { notReadyHeading: 'Nicht bereit zum Export', diff --git a/src/languages/es.ts b/src/languages/es.ts index 227e8bb0fe87..7ef75f9cc272 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -1690,10 +1690,8 @@ const translations: TranslationDeepObject = { backdropLabel: 'Fondo del Modal', }, nextStep: { - /* eslint-disable @typescript-eslint/no-unused-vars */ message: { [CONST.NEXT_STEP.MESSAGE_KEY.WAITING_TO_ADD_TRANSACTIONS]: (actor, actorType, _eta, _etaType) => { - // eslint-disable-next-line default-case switch (actorType) { case CONST.NEXT_STEP.ACTOR_TYPE.CURRENT_USER: return `Esperando a que añadas gastos.`; @@ -1704,7 +1702,6 @@ const translations: TranslationDeepObject = { } }, [CONST.NEXT_STEP.MESSAGE_KEY.WAITING_TO_SUBMIT]: (actor, actorType, _eta, _etaType) => { - // eslint-disable-next-line default-case switch (actorType) { case CONST.NEXT_STEP.ACTOR_TYPE.CURRENT_USER: return `Esperando a que envíes los gastos.`; @@ -1716,7 +1713,6 @@ const translations: TranslationDeepObject = { }, [CONST.NEXT_STEP.MESSAGE_KEY.NO_FURTHER_ACTION]: (_actor, _actorType, _eta, _etaType) => `¡No se requiere ninguna acción adicional!`, [CONST.NEXT_STEP.MESSAGE_KEY.WAITING_FOR_SUBMITTER_ACCOUNT]: (actor, actorType, _eta, _etaType) => { - // eslint-disable-next-line default-case switch (actorType) { case CONST.NEXT_STEP.ACTOR_TYPE.CURRENT_USER: return `Esperando a que añadas una cuenta bancaria.`; @@ -1731,7 +1727,6 @@ const translations: TranslationDeepObject = { if (eta) { formattedETA = etaType === CONST.NEXT_STEP.ETA_TYPE.DATE_TIME ? ` el ${eta} de cada mes` : ` ${eta}`; } - // eslint-disable-next-line default-case switch (actorType) { case CONST.NEXT_STEP.ACTOR_TYPE.CURRENT_USER: return `Esperando a que tus gastos se envíen automáticamente${formattedETA}.`; @@ -1742,7 +1737,6 @@ const translations: TranslationDeepObject = { } }, [CONST.NEXT_STEP.MESSAGE_KEY.WAITING_TO_FIX_ISSUES]: (actor, actorType, _eta, _etaType) => { - // eslint-disable-next-line default-case switch (actorType) { case CONST.NEXT_STEP.ACTOR_TYPE.CURRENT_USER: return `Esperando a que soluciones ellos problemas.`; @@ -1753,7 +1747,6 @@ const translations: TranslationDeepObject = { } }, [CONST.NEXT_STEP.MESSAGE_KEY.WAITING_TO_APPROVE]: (actor, actorType, _eta, _etaType) => { - // eslint-disable-next-line default-case switch (actorType) { case CONST.NEXT_STEP.ACTOR_TYPE.CURRENT_USER: return `Esperando a que apruebes los gastos.`; @@ -1764,7 +1757,6 @@ const translations: TranslationDeepObject = { } }, [CONST.NEXT_STEP.MESSAGE_KEY.WAITING_TO_EXPORT]: (actor, actorType, _eta, _etaType) => { - // eslint-disable-next-line default-case switch (actorType) { case CONST.NEXT_STEP.ACTOR_TYPE.CURRENT_USER: return `Esperando a que exportes este informe.`; @@ -1775,7 +1767,6 @@ const translations: TranslationDeepObject = { } }, [CONST.NEXT_STEP.MESSAGE_KEY.WAITING_TO_PAY]: (actor, actorType, _eta, _etaType) => { - // eslint-disable-next-line default-case switch (actorType) { case CONST.NEXT_STEP.ACTOR_TYPE.CURRENT_USER: return `Esperando a que pagues los gastos.`; @@ -1786,7 +1777,6 @@ const translations: TranslationDeepObject = { } }, [CONST.NEXT_STEP.MESSAGE_KEY.WAITING_FOR_POLICY_BANK_ACCOUNT]: (actor, actorType, _eta, _etaType) => { - // eslint-disable-next-line default-case switch (actorType) { case CONST.NEXT_STEP.ACTOR_TYPE.CURRENT_USER: return `Esperando a que termines de configurar una cuenta bancaria de empresa.`; @@ -1806,7 +1796,6 @@ const translations: TranslationDeepObject = { [CONST.NEXT_STEP.MESSAGE_KEY.SUBMITTING_TO_SELF]: (_actor, _actorType, _eta, _etaType) => `¡Ups! Parece que estás enviando el informe a ti mismo. Aprobar tus propios informes está prohibido por tu espacio de trabajo. Por favor, envía este informe a otra persona o contacta a tu administrador para cambiar la persona a la que lo envías.`, [CONST.NEXT_STEP.MESSAGE_KEY.REJECTED_REPORT]: (actor, actorType) => { - // eslint-disable-next-line default-case switch (actorType) { case CONST.NEXT_STEP.ACTOR_TYPE.CURRENT_USER: return `Este informe fue rechazado. Esperando a que corrijas los problemas y lo vuelvas a enviar manualmente.`; @@ -2461,7 +2450,6 @@ ${amount} para ${merchant} - ${date}`, two: 'º', few: 'º', other: 'º', - /* eslint-disable @typescript-eslint/naming-convention */ '1': 'Primero', '2': 'Segundo', '3': 'Tercero', @@ -2472,7 +2460,6 @@ ${amount} para ${merchant} - ${date}`, '8': 'Octavo', '9': 'Noveno', '10': 'Décimo', - /* eslint-enable @typescript-eslint/naming-convention */ }, }, approverInMultipleWorkflows: 'Este miembro ya pertenece a otro flujo de aprobación. Cualquier actualización aquí se reflejará allí también.', @@ -6163,11 +6150,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 conciliará el pago de tu facturación centralizada.', + 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 central de liquidación de facturas (terminada en ${lastFourPAN}) para que la Reconciliación Continua funcione correctamente.`, + `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.`, }, - syncCentralInvoicingSettlements: 'Sincronizar liquidaciones de facturación central', + syncCentralInvoicingSettlements: 'Sincronizar liquidaciones de facturación de viajes', }, card: { issueCard: 'Emitir tarjeta', @@ -6940,7 +6927,6 @@ ${amount} para ${merchant} - ${date}`, restrictedDescription: 'Sólo las personas en tu espacio de trabajo pueden encontrar esta sala', privateDescription: 'Sólo las personas que están invitadas a esta sala pueden encontrarla', publicDescription: 'Cualquier persona puede unirse a esta sala', - // eslint-disable-next-line @typescript-eslint/naming-convention public_announceDescription: 'Cualquier persona puede unirse a esta sala', createRoom: 'Crea una sala de chat', roomAlreadyExistsError: 'Ya existe una sala con este nombre', @@ -6960,7 +6946,6 @@ ${amount} para ${merchant} - ${date}`, restricted: 'Espacio de trabajo', private: 'Privada', public: 'Público', - // eslint-disable-next-line @typescript-eslint/naming-convention public_announce: 'Anuncio Público', }, }, @@ -7275,7 +7260,6 @@ ${amount} para ${merchant} - ${date}`, updatedDefaultTitle: (newDefaultTitle, oldDefaultTitle) => `cambió la fórmula personalizada del nombre del informe a "${newDefaultTitle}" (previamente "${oldDefaultTitle}")`, updatedOwnership: (oldOwnerEmail, oldOwnerName, policyName) => `asumió la propiedad del espacio de trabajo ${policyName} de ${oldOwnerName} (${oldOwnerEmail})`, updatedAutoHarvesting: (enabled) => `${enabled ? 'habilitó' : 'deshabilitó'} el envío programado`, - // eslint-disable-next-line @typescript-eslint/max-params updatedIndividualBudgetNotification: ( budgetAmount, budgetFrequency, diff --git a/src/languages/fr.ts b/src/languages/fr.ts index ca2adc4c57de..70fbf6971802 100644 --- a/src/languages/fr.ts +++ b/src/languages/fr.ts @@ -6387,11 +6387,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 avec lequel les paiements de votre facturation centralisée seront rapprochés.', + 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 central de règlement de facturation (se terminant par ${lastFourPAN}) afin que la Réconciliation Continue fonctionne correctement.`, + `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.`, }, - syncCentralInvoicingSettlements: 'Synchroniser les règlements de facturation centralisée', + syncCentralInvoicingSettlements: '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 b3b312b4c09a..41b45e5f22cf 100644 --- a/src/languages/it.ts +++ b/src/languages/it.ts @@ -6355,11 +6355,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 con cui verranno riconciliati i pagamenti della fatturazione centralizzata.', + 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 centrale di regolamento delle fatture (che termina con ${lastFourPAN}) così che la Riconciliazione continua funzioni correttamente.`, + `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.`, }, - syncCentralInvoicingSettlements: 'Sincronizza le liquidazioni di fatturazione centrale', + syncCentralInvoicingSettlements: '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 822d6b1aec78..f8eb7eaf184d 100644 --- a/src/languages/ja.ts +++ b/src/languages/ja.ts @@ -6284,11 +6284,11 @@ _詳しい手順については、[ヘルプサイトをご覧ください](${CO chooseBankAccount: 'Expensify カードの支払いを照合する銀行口座を選択してください。', settlementAccountReconciliation: (settlementAccountUrl: string, lastFourPAN: string) => `継続消込が正しく機能するように、この口座が、末尾が ${lastFourPAN} のExpensify カード精算口座と一致していることを確認してください。`, - chooseTravelInvoicingBankAccount: '集中請求の支払いを照合する銀行口座を選択してください。', + chooseTravelInvoicingBankAccount: '出張請求の支払いの消込に使用する銀行口座を選択してください。', travelInvoicingSettlementAccountReconciliation: (lastFourPAN: string) => - `Continuous Reconciliation が正しく機能するよう、この口座が中央請求書決済口座(末尾 ${lastFourPAN})と一致していることを確認してください。`, + `Continuous Reconciliation が正しく機能するように、この口座が、旅行の請求書決済用口座(末尾が ${lastFourPAN} の口座)と一致していることを確認してください。`, }, - syncCentralInvoicingSettlements: '中央請求の精算を同期', + syncCentralInvoicingSettlements: '出張請求の精算を同期', }, export: { notReadyHeading: 'エクスポートの準備ができていません', diff --git a/src/languages/nl.ts b/src/languages/nl.ts index 684244763fa4..c9702976ff46 100644 --- a/src/languages/nl.ts +++ b/src/languages/nl.ts @@ -6333,11 +6333,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 je centrale facturatiebetalingen worden afgestemd.', + chooseTravelInvoicingBankAccount: 'Kies de bankrekening waarop de betalingen van je reiskostenfacturen worden afgeletterd.', travelInvoicingSettlementAccountReconciliation: (lastFourPAN: string) => - `Zorg ervoor dat deze rekening overeenkomt met je centrale afrekenrekening voor facturatie (die eindigt op ${lastFourPAN}), zodat Continue Afstemming goed werkt.`, + `Zorg ervoor dat deze rekening overeenkomt met je afwikkelingsrekening voor reiskostenfacturatie (die eindigt op ${lastFourPAN}), zodat Continue Afstemming goed werkt.`, }, - syncCentralInvoicingSettlements: 'Centrale factureringsafrekeningen synchroniseren', + syncCentralInvoicingSettlements: 'Reisfactureringsafrekeningen synchroniseren', }, export: { notReadyHeading: 'Niet klaar om te exporteren', diff --git a/src/languages/pl.ts b/src/languages/pl.ts index 919e7123d593..280400ca633d 100644 --- a/src/languages/pl.ts +++ b/src/languages/pl.ts @@ -6326,11 +6326,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 z centralnej fakturowości.', + 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 twoje główne konto rozliczeniowe fakturowania (kończące się na ${lastFourPAN}), żeby Ciągłe Uzgadnianie działało poprawnie.`, + `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.`, }, - syncCentralInvoicingSettlements: 'Synchronizuj rozliczenia centralnego fakturowania', + syncCentralInvoicingSettlements: '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 75f1a4bf1968..99052e604ed9 100644 --- a/src/languages/pt-BR.ts +++ b/src/languages/pt-BR.ts @@ -6334,11 +6334,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 da sua cobrança centralizada serão conciliados.', + 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 central de liquidação de faturas (terminando em ${lastFourPAN}) para que a Conciliação Contínua funcione corretamente.`, + `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.`, }, - syncCentralInvoicingSettlements: 'Sincronizar liquidações de faturamento central', + syncCentralInvoicingSettlements: '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 78a967bc89e3..a092a32f9814 100644 --- a/src/languages/zh-hans.ts +++ b/src/languages/zh-hans.ts @@ -6179,10 +6179,10 @@ _如需更详细的说明,请[访问我们的帮助网站](${CONST.NETSUITE_IM chooseBankAccount: '选择用于对账 Expensify 卡付款的银行账户。', settlementAccountReconciliation: (settlementAccountUrl: string, lastFourPAN: string) => `请确保此账户与您的Expensify 卡结算账户(末尾为 ${lastFourPAN})一致,以便持续对账功能正常运行。`, - chooseTravelInvoicingBankAccount: '选择用于核对集中开票收款的银行账户。', - travelInvoicingSettlementAccountReconciliation: (lastFourPAN: string) => `请确保此账户与您的集中开票结算账户(以 ${lastFourPAN} 结尾)一致,以便持续对账功能正常运行。`, + chooseTravelInvoicingBankAccount: '选择用于核对差旅开票付款的银行账户。', + travelInvoicingSettlementAccountReconciliation: (lastFourPAN: string) => `请确保此账户与您的差旅发票结算账户(以 ${lastFourPAN} 结尾)一致,以确保持续对账功能正常运行。`, }, - syncCentralInvoicingSettlements: '同步集中开票结算', + syncCentralInvoicingSettlements: '同步差旅开票结算', }, export: { notReadyHeading: '尚未准备好导出', From f98f55878fb2b9b2806780839567a4902cdadd2b Mon Sep 17 00:00:00 2001 From: Rodrigo Lino da Costa Date: Sat, 16 May 2026 11:40:59 -0300 Subject: [PATCH 13/13] rename centralInvoicing to travelInvoicing --- src/languages/de.ts | 2 +- src/languages/en.ts | 2 +- src/languages/es.ts | 2 +- src/languages/fr.ts | 2 +- src/languages/it.ts | 2 +- src/languages/ja.ts | 2 +- src/languages/nl.ts | 10 +++++----- src/languages/pl.ts | 2 +- src/languages/pt-BR.ts | 2 +- src/languages/zh-hans.ts | 2 +- .../netsuite/advanced/NetSuiteAdvancedPage.tsx | 4 ++-- 11 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/languages/de.ts b/src/languages/de.ts index 46ee05ee3f96..43e67c62e567 100644 --- a/src/languages/de.ts +++ b/src/languages/de.ts @@ -6388,7 +6388,7 @@ _Für ausführlichere Anweisungen [besuchen Sie unsere Hilfeseite](${CONST.NETSU 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.`, }, - syncCentralInvoicingSettlements: 'Reiseabrechnungs­abgleiche synchronisieren', + syncTravelInvoicingSettlements: 'Reiseabrechnungs­abgleiche synchronisieren', }, export: { notReadyHeading: 'Nicht bereit zum Export', diff --git a/src/languages/en.ts b/src/languages/en.ts index 6fc93a5561a5..1a3296a09169 100644 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -6401,7 +6401,7 @@ const translations = { cardReconciliation: 'Card reconciliation', reconciliationAccount: 'Reconciliation account', continuousReconciliation: 'Continuous Reconciliation', - syncCentralInvoicingSettlements: 'Sync travel invoicing settlements', + 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) => diff --git a/src/languages/es.ts b/src/languages/es.ts index 070d554752f4..7734add9f0b7 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -6171,7 +6171,7 @@ ${amount} para ${merchant} - ${date}`, 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.`, }, - syncCentralInvoicingSettlements: 'Sincronizar liquidaciones de facturación de viajes', + 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 2118534374c2..f868e47d5217 100644 --- a/src/languages/fr.ts +++ b/src/languages/fr.ts @@ -6410,7 +6410,7 @@ _Pour des instructions plus détaillées, [visitez notre site d’aide](${CONST. 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.`, }, - syncCentralInvoicingSettlements: 'Synchroniser les règlements de facturation de voyage', + 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 b176e134fc75..7a964999b879 100644 --- a/src/languages/it.ts +++ b/src/languages/it.ts @@ -6378,7 +6378,7 @@ _Per istruzioni più dettagliate, [visita il nostro sito di assistenza](${CONST. 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.`, }, - syncCentralInvoicingSettlements: 'Sincronizza le liquidazioni delle fatture di viaggio', + 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 b834672532b5..a3468e0f8f2b 100644 --- a/src/languages/ja.ts +++ b/src/languages/ja.ts @@ -6307,7 +6307,7 @@ _詳しい手順については、[ヘルプサイトをご覧ください](${CO travelInvoicingSettlementAccountReconciliation: (lastFourPAN: string) => `Continuous Reconciliation が正しく機能するように、この口座が、旅行の請求書決済用口座(末尾が ${lastFourPAN} の口座)と一致していることを確認してください。`, }, - syncCentralInvoicingSettlements: '出張請求の精算を同期', + syncTravelInvoicingSettlements: '出張請求の精算を同期', }, export: { notReadyHeading: 'エクスポートの準備ができていません', diff --git a/src/languages/nl.ts b/src/languages/nl.ts index 6d587be93783..92104dc6f956 100644 --- a/src/languages/nl.ts +++ b/src/languages/nl.ts @@ -6356,7 +6356,7 @@ _Voor meer gedetailleerde instructies, [bezoek onze help-site](${CONST.NETSUITE_ travelInvoicingSettlementAccountReconciliation: (lastFourPAN: string) => `Zorg ervoor dat deze rekening overeenkomt met je afwikkelingsrekening voor reiskostenfacturatie (die eindigt op ${lastFourPAN}), zodat Continue Afstemming goed werkt.`, }, - syncCentralInvoicingSettlements: 'Reisfactureringsafrekeningen synchroniseren', + syncTravelInvoicingSettlements: 'Reisfactureringsafrekeningen synchroniseren', }, export: { notReadyHeading: 'Niet klaar om te exporteren', @@ -6907,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 f5ac8402d3f2..51df414c80e4 100644 --- a/src/languages/pl.ts +++ b/src/languages/pl.ts @@ -6349,7 +6349,7 @@ _Aby uzyskać bardziej szczegółowe instrukcje, [odwiedź naszą stronę pomocy 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.`, }, - syncCentralInvoicingSettlements: 'Synchronizuj rozliczenia faktur podróżnych', + 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 0006fc1945ba..53ff031ebabd 100644 --- a/src/languages/pt-BR.ts +++ b/src/languages/pt-BR.ts @@ -6357,7 +6357,7 @@ _Para instruções mais detalhadas, [visite nossa central de ajuda](${CONST.NETS 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.`, }, - syncCentralInvoicingSettlements: 'Sincronizar liquidações de faturamento de viagens', + 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 48bbb2f49d37..5d5f6455c3e5 100644 --- a/src/languages/zh-hans.ts +++ b/src/languages/zh-hans.ts @@ -6201,7 +6201,7 @@ _如需更详细的说明,请[访问我们的帮助网站](${CONST.NETSUITE_IM chooseTravelInvoicingBankAccount: '选择用于核对差旅开票付款的银行账户。', travelInvoicingSettlementAccountReconciliation: (lastFourPAN: string) => `请确保此账户与您的差旅发票结算账户(以 ${lastFourPAN} 结尾)一致,以确保持续对账功能正常运行。`, }, - syncCentralInvoicingSettlements: '同步差旅开票结算', + syncTravelInvoicingSettlements: '同步差旅开票结算', }, export: { notReadyHeading: '尚未准备好导出', diff --git a/src/pages/workspace/accounting/netsuite/advanced/NetSuiteAdvancedPage.tsx b/src/pages/workspace/accounting/netsuite/advanced/NetSuiteAdvancedPage.tsx index 5c844db56d98..2a8d72f8a987 100644 --- a/src/pages/workspace/accounting/netsuite/advanced/NetSuiteAdvancedPage.tsx +++ b/src/pages/workspace/accounting/netsuite/advanced/NetSuiteAdvancedPage.tsx @@ -173,9 +173,9 @@ function NetSuiteAdvancedPage({policy}: WithPolicyConnectionsProps) { }, { type: 'toggle', - title: translate('workspace.accounting.syncCentralInvoicingSettlements'), + title: translate('workspace.accounting.syncTravelInvoicingSettlements'), isActive: !!travelInvoicingContinuousReconciliation, - switchAccessibilityLabel: translate('workspace.accounting.syncCentralInvoicingSettlements'), + switchAccessibilityLabel: translate('workspace.accounting.syncTravelInvoicingSettlements'), disabled: !autoSync, onToggle: (isEnabled) => { toggleTravelInvoicingContinuousReconciliation(workspaceAccountID, isEnabled, CONST.POLICY.CONNECTIONS.NAME.NETSUITE, travelInvoicingContinuousReconciliationConnection);