diff --git a/assets/images/integrationicons/digits-icon-square.svg b/assets/images/integrationicons/digits-icon-square.svg new file mode 100644 index 000000000000..b28d5f45ef17 --- /dev/null +++ b/assets/images/integrationicons/digits-icon-square.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/knip.json b/knip.json index 730d221fe72e..f03420b50cbb 100644 --- a/knip.json +++ b/knip.json @@ -46,6 +46,7 @@ "web/cloudflare-worker/**", "src/libs/actions/connections/FinancialForce.ts", "src/libs/actions/connections/ZohoBooks.ts", + "src/libs/actions/connections/Digits.ts", "src/setup/telemetry/noopExpoUpdates.ts" ], "eslint": { diff --git a/src/CONST/index.ts b/src/CONST/index.ts index 821f33fcb6b4..f5153bc7e287 100644 --- a/src/CONST/index.ts +++ b/src/CONST/index.ts @@ -128,7 +128,9 @@ type UnofferedOnboardingAccountingConnection = // Covered by the Microsoft Dynamics option, and still behind the BUSINESS_CENTRAL beta. | typeof CONST.POLICY.CONNECTIONS.NAME.BUSINESS_CENTRAL // Still behind the ZOHO_BOOKS beta. - | typeof CONST.POLICY.CONNECTIONS.NAME.ZOHO_BOOKS; + | typeof CONST.POLICY.CONNECTIONS.NAME.ZOHO_BOOKS + // Still behind the DIGITS beta. + | typeof CONST.POLICY.CONNECTIONS.NAME.DIGITS; type UnhandledAccountingConnection = Exclude, OnboardingAccountingOption | UnofferedOnboardingAccountingConnection>; @@ -700,6 +702,7 @@ const CONST = { ZENEFITS: 'Zenefits', DUALENTRY: 'DualEntry', CAMPFIRE: 'Campfire', + DIGITS: 'Digits', BUSINESS_CENTRAL: 'Dynamics 365 Business Central', }, @@ -1434,6 +1437,7 @@ const CONST = { INDIRECT_TAG_UPLOADS: 'indirectTagUploads', BUSINESS_CENTRAL: 'businessCentral', ZOHO_BOOKS: 'zohoBooks', + DIGITS: 'digits', EXACT_ONLINE: 'exactOnline2026', COMMUTER_EXCLUSIONS: 'commuterExclusions', COMMUTER_EXCLUSIONS_ARRANGEMENTS: 'commuterExclusionsArrangements', @@ -4395,6 +4399,59 @@ const CONST = { EMPLOYEE: 'employee', }, + DIGITS_CONFIG: { + ENABLE_NEW_CATEGORIES: 'enableNewCategories', + EXPORTER: 'exporter', + EXPORT_DATE: 'exportDate', + REIMBURSABLE: 'reimbursable', + NON_REIMBURSABLE: 'nonReimbursable', + CREDIT_CARD_ACCOUNT_ID: 'creditCardAccountID', + EXPORT_TO_MULTIPLE_ACCOUNTS: 'exportToMultipleAccounts', + DEFAULT_VENDORID: 'defaultVendorID', + TRAVEL_BILLING_PAYABLE_ACCOUNT_ID: 'travelInvoicingPayableAccountID', + ACCOUNTING_METHOD: 'accountingMethod', + AUTO_SYNC: 'autoSync', + SYNC_REIMBURSED_REPORTS: 'syncReimbursedReports', + BILL_PAYMENT_ACCOUNT_ID: 'billPaymentAccountID', + SYNC_EXPENSIFY_CARD_SETTLEMENTS: 'syncExpensifyCardSettlements', + SETTLEMENTS_BANK_ACCOUNT_ID: 'settlementsBankAccountID', + SYNC_TRAVEL_BILLING_SETTLEMENTS: 'syncTravelInvoicingSettlements', + TRAVEL_BILLING_SETTLEMENTS_BANK_ACCOUNT_ID: 'travelInvoicingSettlementsBankAccountID', + FIELD_MAPPING_PREFIX: 'fieldMapping_', + CARD_PROGRAM_ACCOUNT_PREFIX: 'cardProgramAccount_', + }, + + // Digits dimensions that can import as tags. Departments are the only dimension the Digits API exposes so far. + DIGITS_FIELD: { + DEPARTMENTS: 'departments', + }, + + DIGITS_MAPPING_VALUE: { + NONE: 'NONE', + TAG: 'TAG', + }, + + DIGITS_EXPORT_REIMBURSABLE: { + VENDOR_BILL: 'VENDOR_BILL', + }, + + DIGITS_EXPORT_NON_REIMBURSABLE: { + TRANSACTION: 'TRANSACTION', + }, + + DIGITS_EXPORT_DATE: { + LAST_EXPENSE: 'LAST_EXPENSE', + REPORT_EXPORTED: 'REPORT_EXPORTED', + REPORT_SUBMITTED: 'REPORT_SUBMITTED', + }, + + // The Digits chart of accounts subtypes that account pickers filter on. Every other subtype only imports as a category. + DIGITS_ACCOUNT_SUBTYPE: { + BANK_ACCOUNTS: 'BankAccounts', + CREDIT_CARDS: 'CreditCards', + OTHER_CURRENT_LIABILITIES: 'OtherCurrentLiabilities', + }, + ZOHO_BOOKS_CONFIG: { SUBSIDIARY_ID: 'subsidiaryID', ENABLE_NEW_CATEGORIES: 'enableNewCategories', @@ -5324,6 +5381,7 @@ const CONST = { RILLET: 'rillet', DUALENTRY: 'dualEntry', CAMPFIRE: 'campfire', + DIGITS: 'digits', ZOHO_BOOKS: 'zohoBooks', BUSINESS_CENTRAL: 'businessCentral', EXACT_ONLINE: 'exactOnline', @@ -5346,6 +5404,7 @@ const CONST = { RILLET: 'rillet', DUALENTRY: 'dualentry', CAMPFIRE: 'campfire', + DIGITS: 'digits', ZOHO_BOOKS: 'zoho-books', BUSINESS_CENTRAL: 'business-central', GUSTO: 'gusto', @@ -5363,6 +5422,7 @@ const CONST = { rillet: 'Rillet', dualEntry: 'DualEntry', campfire: 'Campfire', + digits: 'Digits', zohoBooks: 'Zoho Books', businessCentral: 'Dynamics 365 Business Central', exactOnline: 'Exact Online', @@ -5390,6 +5450,7 @@ const CONST = { this.NAME.CERTINIA, this.NAME.RILLET, this.NAME.DUALENTRY, + this.NAME.DIGITS, this.NAME.CAMPFIRE, this.NAME.ZOHO_BOOKS, this.NAME.BUSINESS_CENTRAL, @@ -5408,6 +5469,7 @@ const CONST = { [this.NAME.RILLET]: 'rillet', [this.NAME.DUALENTRY]: 'dualentry', [this.NAME.CAMPFIRE]: 'campfire', + [this.NAME.DIGITS]: 'digits', [this.NAME.ZOHO_BOOKS]: 'zohoBooks', [this.NAME.BUSINESS_CENTRAL]: 'businessCentral', } as const; @@ -5530,6 +5592,12 @@ const CONST = { CAMPFIRE_SYNC_PAYMENTS: 'campfireSyncPayments', CAMPFIRE_SYNC_CARD_SETTLEMENTS: 'campfireSyncCardSettlements', CAMPFIRE_SYNC_TRAVEL_SETTLEMENTS: 'campfireSyncTravelSettlements', + DIGITS_SYNC_TITLE: 'digitsSyncTitle', + DIGITS_SYNC_CONNECTION: 'digitsSyncConnection', + DIGITS_SYNC_IMPORT_DATA: 'digitsSyncImportData', + DIGITS_SYNC_PAYMENTS: 'digitsSyncPayments', + DIGITS_SYNC_CARD_SETTLEMENTS: 'digitsSyncCardSettlements', + DIGITS_SYNC_TRAVEL_SETTLEMENTS: 'digitsSyncTravelSettlements', ZOHO_BOOKS_SYNC_CONNECTION: 'zohoBooksSyncConnection', BUSINESS_CENTRAL_SYNC_TITLE: 'businessCentralSyncTitle', BUSINESS_CENTRAL_SYNC_CONNECTION: 'businessCentralSyncConnection', diff --git a/src/ROUTES.ts b/src/ROUTES.ts index f7325a02a1b7..44ff38e5c63b 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -5444,6 +5444,18 @@ const ROUTES = { route: 'workspaces/:policyID/accounting/campfire/advanced/travel-billing-payable-account', getRoute: (policyID: string) => `workspaces/${policyID}/accounting/campfire/advanced/travel-billing-payable-account` as const, }, + POLICY_ACCOUNTING_DIGITS_OAUTH: { + route: 'workspaces/:policyID/accounting/digits/oauth', + getRoute: (policyID: string) => `workspaces/${policyID}/accounting/digits/oauth` as const, + }, + POLICY_ACCOUNTING_DIGITS_EXISTING_CONNECTIONS: { + route: 'workspaces/:policyID/accounting/digits/existing-connections', + getRoute: (policyID: string) => `workspaces/${policyID}/accounting/digits/existing-connections` as const, + }, + POLICY_ACCOUNTING_DIGITS_IMPORT: { + route: 'workspaces/:policyID/accounting/digits/import', + getRoute: (policyID: string) => `workspaces/${policyID}/accounting/digits/import` as const, + }, POLICY_ACCOUNTING_BUSINESS_CENTRAL_PREREQUISITES: { route: 'workspaces/:policyID/accounting/business-central/prerequisites', getRoute: (policyID: string) => `workspaces/${policyID}/accounting/business-central/prerequisites` as const, diff --git a/src/SCREENS.ts b/src/SCREENS.ts index f78d275e709a..d1ee9e361298 100644 --- a/src/SCREENS.ts +++ b/src/SCREENS.ts @@ -765,6 +765,9 @@ const SCREENS = { CAMPFIRE_EXPENSIFY_CARD_SETTLEMENT_ACCOUNT: 'Policy_Accounting_Campfire_Expensify_Card_Settlement_Account', CAMPFIRE_TRAVEL_BILLING_SETTLEMENT_ACCOUNT: 'Policy_Accounting_Campfire_Travel_Billing_Settlement_Account', CAMPFIRE_TRAVEL_BILLING_PAYABLE_ACCOUNT: 'Policy_Accounting_Campfire_Travel_Billing_Payable_Account', + DIGITS_OAUTH: 'Policy_Accounting_Digits_OAuth', + DIGITS_EXISTING_CONNECTIONS: 'Policy_Accounting_Digits_Existing_Connections', + DIGITS_IMPORT: 'Policy_Accounting_Digits_Import', BUSINESS_CENTRAL_PREREQUISITES: 'Policy_Accounting_Business_Central_Prerequisites', BUSINESS_CENTRAL_SETUP: 'Policy_Accounting_Business_Central_Setup', BUSINESS_CENTRAL_COMPANY_SELECTOR: 'Policy_Accounting_Business_Central_Company_Selector', diff --git a/src/components/Icon/chunks/expensify-icons.chunk.ts b/src/components/Icon/chunks/expensify-icons.chunk.ts index 4fa39f38a242..7ce2d52e2c33 100644 --- a/src/components/Icon/chunks/expensify-icons.chunk.ts +++ b/src/components/Icon/chunks/expensify-icons.chunk.ts @@ -144,6 +144,7 @@ import ChatGPTSquare from '@assets/images/integrationicons/chatgpt-icon-square.s import CircleSlash from '@assets/images/integrationicons/circle-slash.svg'; import ClaudeSquare from '@assets/images/integrationicons/claude-icon-square.svg'; import CursorSquare from '@assets/images/integrationicons/cursor-icon-square.svg'; +import DigitsSquare from '@assets/images/integrationicons/digits-icon-square.svg'; import DualEntrySquare from '@assets/images/integrationicons/dualentry-icon-square.svg'; import ExactOnlineSquare from '@assets/images/integrationicons/exact-online-icon-square.svg'; import NetSuiteExport from '@assets/images/integrationicons/export/netsuite-icon.svg'; @@ -617,6 +618,7 @@ const Expensicons = { Contactless, CampfireSquare, ZohoBooksSquare, + DigitsSquare, }; // Create the ExpensifyIcons object from the imported Expensicons diff --git a/src/languages/de.ts b/src/languages/de.ts index f0ec5a85aa89..c550c377ddd4 100644 --- a/src/languages/de.ts +++ b/src/languages/de.ts @@ -7513,6 +7513,8 @@ Der Control-Tarif beginnt bei 9 $ pro aktivem Mitglied und Monat.`, return 'DualEntry'; case CONST.POLICY.CONNECTIONS.NAME.CAMPFIRE: return 'Campfire'; + case CONST.POLICY.CONNECTIONS.NAME.DIGITS: + return 'Digits'; case CONST.POLICY.CONNECTIONS.NAME.ZOHO_BOOKS: return 'Zoho Books'; case CONST.POLICY.CONNECTIONS.NAME.BUSINESS_CENTRAL: @@ -7758,6 +7760,18 @@ Der Control-Tarif beginnt bei 9 $ pro aktivem Mitglied und Monat.`, return 'Kartenausgleiche werden synchronisiert'; case 'campfireSyncTravelSettlements': return 'Reiseabrechnungen werden synchronisiert'; + case 'digitsSyncTitle': + return 'Digits-Daten werden synchronisiert'; + case 'digitsSyncConnection': + return 'Verbindung zu Digits wird initialisiert'; + case 'digitsSyncImportData': + return 'Daten werden geladen'; + case 'digitsSyncPayments': + return 'Lieferantenzahlungen werden synchronisiert'; + case 'digitsSyncCardSettlements': + return 'Kartenausgleiche werden synchronisiert'; + case 'digitsSyncTravelSettlements': + return 'Reiseabrechnungen werden synchronisiert'; case 'businessCentralSyncTitle': return 'Dynamics 365 Business Central-Daten werden synchronisiert'; case 'businessCentralSyncConnection': @@ -7807,6 +7821,7 @@ Der Control-Tarif beginnt bei 9 $ pro aktivem Mitglied und Monat.`, syncTravelBillingSettlementsNoAccountTooltip: 'Zum Aktivieren legen Sie ein Konto für Ihre Exporte fest.', syncTravelBillingSettlementsNoAutoSyncTooltip: 'Zum Entsperren automatische Synchronisierung aktivieren.', campfire: 'Campfire', + digits: 'Digits', zohoBooks: 'Zoho Books', continuousReconciliationFeedSelection: 'Die fortlaufende Abstimmung wird pro Kartenfeed konfiguriert. Wählen Sie einen Feed aus, um zu ändern, welchen Sie konfigurieren.', diff --git a/src/languages/el.ts b/src/languages/el.ts index cf208a1a3120..3abe6db46d5c 100644 --- a/src/languages/el.ts +++ b/src/languages/el.ts @@ -7641,6 +7641,8 @@ _Για πιο αναλυτικές οδηγίες, [επισκεφθείτε τ return 'DualEntry'; case CONST.POLICY.CONNECTIONS.NAME.CAMPFIRE: return 'Campfire'; + case CONST.POLICY.CONNECTIONS.NAME.DIGITS: + return 'Digits'; case CONST.POLICY.CONNECTIONS.NAME.ZOHO_BOOKS: return 'Zoho Books'; case CONST.POLICY.CONNECTIONS.NAME.BUSINESS_CENTRAL: @@ -7887,6 +7889,18 @@ _Για πιο αναλυτικές οδηγίες, [επισκεφθείτε τ return 'Συγχρονισμός διακανονισμών κάρτας'; case 'campfireSyncTravelSettlements': return 'Συγχρονισμός εκκαθαρίσεων ταξιδιών'; + case 'digitsSyncTitle': + return 'Γίνεται συγχρονισμός δεδομένων Digits'; + case 'digitsSyncConnection': + return 'Γίνεται αρχικοποίηση σύνδεσης με το Digits'; + case 'digitsSyncImportData': + return 'Φόρτωση δεδομένων'; + case 'digitsSyncPayments': + return 'Συγχρονισμός πληρωμών προμηθευτών'; + case 'digitsSyncCardSettlements': + return 'Συγχρονισμός διακανονισμών κάρτας'; + case 'digitsSyncTravelSettlements': + return 'Συγχρονισμός εκκαθαρίσεων ταξιδιών'; case 'businessCentralSyncTitle': return 'Συγχρονισμός δεδομένων Dynamics 365 Business Central'; case 'businessCentralSyncConnection': @@ -7936,6 +7950,7 @@ _Για πιο αναλυτικές οδηγίες, [επισκεφθείτε τ syncTravelBillingSettlementsNoAccountTooltip: 'Για να το ενεργοποιήσετε, ορίστε έναν λογαριασμό για τις εξαγωγές σας.', syncTravelBillingSettlementsNoAutoSyncTooltip: 'Για να το ξεκλειδώσετε, ενεργοποιήστε τον αυτόματο συγχρονισμό.', campfire: 'Campfire', + digits: 'Digits', zohoBooks: 'Zoho Books', continuousReconciliationFeedSelection: 'Η συνεχής συμφωνία είναι ρυθμισμένη ανά ροή καρτών. Επιλέξτε μια ροή για να αλλάξετε ποια ρυθμίζετε.', diff --git a/src/languages/en.ts b/src/languages/en.ts index ec5bed28d37e..fe7f3527e672 100644 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -7924,6 +7924,7 @@ const translations = { rillet: 'Rillet', dualEntry: 'DualEntry', campfire: 'Campfire', + digits: 'Digits', zohoBooks: 'Zoho Books', businessCentral: 'Dynamics 365 Business Central', sap: 'SAP', @@ -7949,6 +7950,8 @@ const translations = { return 'DualEntry'; case CONST.POLICY.CONNECTIONS.NAME.CAMPFIRE: return 'Campfire'; + case CONST.POLICY.CONNECTIONS.NAME.DIGITS: + return 'Digits'; case CONST.POLICY.CONNECTIONS.NAME.ZOHO_BOOKS: return 'Zoho Books'; case CONST.POLICY.CONNECTIONS.NAME.BUSINESS_CENTRAL: @@ -8200,6 +8203,18 @@ const translations = { return 'Syncing card settlements'; case 'campfireSyncTravelSettlements': return 'Syncing travel settlements'; + case 'digitsSyncTitle': + return 'Syncing Digits data'; + case 'digitsSyncConnection': + return 'Initializing connection to Digits'; + case 'digitsSyncImportData': + return 'Loading data'; + case 'digitsSyncPayments': + return 'Syncing vendor payments'; + case 'digitsSyncCardSettlements': + return 'Syncing card settlements'; + case 'digitsSyncTravelSettlements': + return 'Syncing travel settlements'; case 'businessCentralSyncTitle': return 'Syncing Dynamics 365 Business Central data'; case 'businessCentralSyncConnection': diff --git a/src/languages/es.ts b/src/languages/es.ts index 6d13b9f0666f..f86bbf9661e1 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -7309,6 +7309,8 @@ El plan Controlar empieza en 9 $ por miembro activo al mes.`, return 'DualEntry'; case CONST.POLICY.CONNECTIONS.NAME.CAMPFIRE: return 'Campfire'; + case CONST.POLICY.CONNECTIONS.NAME.DIGITS: + return 'Digits'; case CONST.POLICY.CONNECTIONS.NAME.ZOHO_BOOKS: return 'Zoho Books'; case CONST.POLICY.CONNECTIONS.NAME.BUSINESS_CENTRAL: @@ -7555,6 +7557,18 @@ El plan Controlar empieza en 9 $ por miembro activo al mes.`, return 'Sincronizando liquidaciones de tarjetas'; case 'campfireSyncTravelSettlements': return 'Sincronizando liquidaciones de viaje'; + case 'digitsSyncTitle': + return 'Sincronizando datos de Digits'; + case 'digitsSyncConnection': + return 'Iniciando conexión con Digits'; + case 'digitsSyncImportData': + return 'Cargando datos'; + case 'digitsSyncPayments': + return 'Sincronizando pagos a proveedores'; + case 'digitsSyncCardSettlements': + return 'Sincronizando liquidaciones de tarjetas'; + case 'digitsSyncTravelSettlements': + return 'Sincronizando liquidaciones de viaje'; case 'businessCentralSyncTitle': return 'Sincronizando datos de Dynamics 365 Business Central'; case 'businessCentralSyncConnection': @@ -7604,6 +7618,7 @@ El plan Controlar empieza en 9 $ por miembro activo al mes.`, syncTravelBillingSettlementsNoAccountTooltip: 'Para desbloquearlo, configura una cuenta para tus exportaciones.', syncTravelBillingSettlementsNoAutoSyncTooltip: 'Para desbloquear, habilita la sincronización automática.', campfire: 'Campfire', + digits: 'Digits', zohoBooks: 'Zoho Books', continuousReconciliationFeedSelection: 'La conciliación continua se configura por cada fuente de tarjetas. Selecciona una fuente para cambiar cuál estás configurando.', diff --git a/src/languages/fr.ts b/src/languages/fr.ts index cf72d1909f84..276597114689 100644 --- a/src/languages/fr.ts +++ b/src/languages/fr.ts @@ -7533,6 +7533,8 @@ Le forfait Control commence à 9 $ par Membre actif et par mois.`, return 'DualEntry'; case CONST.POLICY.CONNECTIONS.NAME.CAMPFIRE: return 'Campfire'; + case CONST.POLICY.CONNECTIONS.NAME.DIGITS: + return 'Digits'; case CONST.POLICY.CONNECTIONS.NAME.ZOHO_BOOKS: return 'Zoho Books'; case CONST.POLICY.CONNECTIONS.NAME.BUSINESS_CENTRAL: @@ -7779,6 +7781,18 @@ Le forfait Control commence à 9 $ par Membre actif et par mois.`, return 'Synchronisation des règlements de carte'; case 'campfireSyncTravelSettlements': return 'Synchronisation des règlements de voyage'; + case 'digitsSyncTitle': + return 'Synchronisation des données Digits'; + case 'digitsSyncConnection': + return 'Initialisation de la connexion à Digits'; + case 'digitsSyncImportData': + return 'Chargement des données'; + case 'digitsSyncPayments': + return 'Synchronisation des paiements fournisseurs'; + case 'digitsSyncCardSettlements': + return 'Synchronisation des règlements de carte'; + case 'digitsSyncTravelSettlements': + return 'Synchronisation des règlements de voyage'; case 'businessCentralSyncTitle': return 'Synchronisation des données Dynamics 365 Business Central'; case 'businessCentralSyncConnection': @@ -7828,6 +7842,7 @@ Le forfait Control commence à 9 $ par Membre actif et par mois.`, syncTravelBillingSettlementsNoAccountTooltip: 'Pour le déverrouiller, définissez un compte pour vos exports.', syncTravelBillingSettlementsNoAutoSyncTooltip: 'Pour le déverrouiller, activez la synchronisation automatique.', campfire: 'Campfire', + digits: 'Digits', zohoBooks: 'Zoho Books', continuousReconciliationFeedSelection: 'Le rapprochement continu est configuré par flux de carte. Sélectionnez un flux pour changer celui que vous configurez.', diff --git a/src/languages/it.ts b/src/languages/it.ts index a359dd417d8d..513fc2eeedd2 100644 --- a/src/languages/it.ts +++ b/src/languages/it.ts @@ -7473,6 +7473,8 @@ Il piano Control parte da 9 $ al mese per ogni membro attivo.`, return 'DualEntry'; case CONST.POLICY.CONNECTIONS.NAME.CAMPFIRE: return 'Campfire'; + case CONST.POLICY.CONNECTIONS.NAME.DIGITS: + return 'Digits'; case CONST.POLICY.CONNECTIONS.NAME.ZOHO_BOOKS: return 'Zoho Books'; case CONST.POLICY.CONNECTIONS.NAME.BUSINESS_CENTRAL: @@ -7718,6 +7720,18 @@ Il piano Control parte da 9 $ al mese per ogni membro attivo.`, return 'Sincronizzazione delle chiusure carta'; case 'campfireSyncTravelSettlements': return 'Sincronizzazione dei conguagli di viaggio'; + case 'digitsSyncTitle': + return 'Sincronizzazione dei dati Digits'; + case 'digitsSyncConnection': + return 'Inizializzazione della connessione a Digits'; + case 'digitsSyncImportData': + return 'Caricamento dei dati'; + case 'digitsSyncPayments': + return 'Sincronizzazione dei pagamenti ai fornitori'; + case 'digitsSyncCardSettlements': + return 'Sincronizzazione delle chiusure carta'; + case 'digitsSyncTravelSettlements': + return 'Sincronizzazione dei conguagli di viaggio'; case 'businessCentralSyncTitle': return 'Sincronizzazione dei dati Dynamics 365 Business Central'; case 'businessCentralSyncConnection': @@ -7767,6 +7781,7 @@ Il piano Control parte da 9 $ al mese per ogni membro attivo.`, syncTravelBillingSettlementsNoAccountTooltip: 'Per sbloccare, imposta un conto per le tue esportazioni.', syncTravelBillingSettlementsNoAutoSyncTooltip: 'Per sbloccare, abilita la sincronizzazione automatica.', campfire: 'Campfire', + digits: 'Digits', zohoBooks: 'Zoho Books', continuousReconciliationFeedSelection: 'La Riconciliazione continua è configurata per ogni flusso di carte. Seleziona un flusso per cambiare quello che stai configurando.', diff --git a/src/languages/ja.ts b/src/languages/ja.ts index dc04cf45f283..9d7bc32e1ce9 100644 --- a/src/languages/ja.ts +++ b/src/languages/ja.ts @@ -7377,6 +7377,8 @@ Control プランは、アクティブメンバー1人あたり月額 $9 から return 'DualEntry'; case CONST.POLICY.CONNECTIONS.NAME.CAMPFIRE: return 'Campfire'; + case CONST.POLICY.CONNECTIONS.NAME.DIGITS: + return 'Digits'; case CONST.POLICY.CONNECTIONS.NAME.ZOHO_BOOKS: return 'Zoho Books'; case CONST.POLICY.CONNECTIONS.NAME.BUSINESS_CENTRAL: @@ -7623,6 +7625,18 @@ Control プランは、アクティブメンバー1人あたり月額 $9 から return 'カード精算を同期しています'; case 'campfireSyncTravelSettlements': return '出張精算を同期しています'; + case 'digitsSyncTitle': + return 'Digits データを同期しています'; + case 'digitsSyncConnection': + return 'Digits への接続を初期化しています'; + case 'digitsSyncImportData': + return 'データを読み込んでいます'; + case 'digitsSyncPayments': + return '仕入先への支払いを同期しています'; + case 'digitsSyncCardSettlements': + return 'カード精算を同期しています'; + case 'digitsSyncTravelSettlements': + return '出張精算を同期しています'; case 'businessCentralSyncTitle': return 'Dynamics 365 Business Central データを同期しています'; case 'businessCentralSyncConnection': @@ -7671,6 +7685,7 @@ Control プランは、アクティブメンバー1人あたり月額 $9 から syncTravelBillingSettlementsNoAccountTooltip: 'ロックを解除するには、エクスポート用の口座を設定してください。', syncTravelBillingSettlementsNoAutoSyncTooltip: 'ロックを解除するには、自動同期を有効にしてください。', campfire: 'Campfire', + digits: 'Digits', zohoBooks: 'Zoho Books', continuousReconciliationFeedSelection: '継続的な照合はカードフィードごとに設定されます。設定するフィードを変更するには、フィードを選択してください。', diff --git a/src/languages/nl.ts b/src/languages/nl.ts index 7f66c58a20f4..1b4adc836ef5 100644 --- a/src/languages/nl.ts +++ b/src/languages/nl.ts @@ -7455,6 +7455,8 @@ Het Control-abonnement begint bij $9 per actieve deelnemer per maand.`, return 'DualEntry'; case CONST.POLICY.CONNECTIONS.NAME.CAMPFIRE: return 'Campfire'; + case CONST.POLICY.CONNECTIONS.NAME.DIGITS: + return 'Digits'; case CONST.POLICY.CONNECTIONS.NAME.ZOHO_BOOKS: return 'Zoho Books'; case CONST.POLICY.CONNECTIONS.NAME.BUSINESS_CENTRAL: @@ -7700,6 +7702,18 @@ Het Control-abonnement begint bij $9 per actieve deelnemer per maand.`, return 'Kaartafrekeningen synchroniseren'; case 'campfireSyncTravelSettlements': return 'Reisverrekeningen synchroniseren'; + case 'digitsSyncTitle': + return 'Digits-gegevens synchroniseren'; + case 'digitsSyncConnection': + return 'Verbinding met Digits initialiseren'; + case 'digitsSyncImportData': + return 'Gegevens laden'; + case 'digitsSyncPayments': + return 'Leveranciersbetalingen synchroniseren'; + case 'digitsSyncCardSettlements': + return 'Kaartafrekeningen synchroniseren'; + case 'digitsSyncTravelSettlements': + return 'Reisverrekeningen synchroniseren'; case 'businessCentralSyncTitle': return 'Dynamics 365 Business Central-gegevens synchroniseren'; case 'businessCentralSyncConnection': @@ -7749,6 +7763,7 @@ Het Control-abonnement begint bij $9 per actieve deelnemer per maand.`, syncTravelBillingSettlementsNoAccountTooltip: 'Om dit te ontgrendelen, stel je een rekening in voor je exporten.', syncTravelBillingSettlementsNoAutoSyncTooltip: 'Schakel automatisch synchroniseren in om dit te ontgrendelen.', campfire: 'Campfire', + digits: 'Digits', zohoBooks: 'Zoho Books', continuousReconciliationFeedSelection: 'Doorlopende afstemming wordt per kaartfeed geconfigureerd. Selecteer een feed om te wijzigen welke je configureert.', diff --git a/src/languages/pl.ts b/src/languages/pl.ts index 689a79bdaad3..233af57663ed 100644 --- a/src/languages/pl.ts +++ b/src/languages/pl.ts @@ -7483,6 +7483,8 @@ Plan Control zaczyna się od 9 USD za aktywnego członka miesięcznie.`, return 'DualEntry'; case CONST.POLICY.CONNECTIONS.NAME.CAMPFIRE: return 'Campfire'; + case CONST.POLICY.CONNECTIONS.NAME.DIGITS: + return 'Digits'; case CONST.POLICY.CONNECTIONS.NAME.ZOHO_BOOKS: return 'Zoho Books'; case CONST.POLICY.CONNECTIONS.NAME.BUSINESS_CENTRAL: @@ -7728,6 +7730,18 @@ Plan Control zaczyna się od 9 USD za aktywnego członka miesięcznie.`, return 'Synchronizowanie rozliczeń karty'; case 'campfireSyncTravelSettlements': return 'Synchronizowanie rozliczeń podróży'; + case 'digitsSyncTitle': + return 'Synchronizowanie danych Digits'; + case 'digitsSyncConnection': + return 'Inicjowanie połączenia z Digits'; + case 'digitsSyncImportData': + return 'Wczytywanie danych'; + case 'digitsSyncPayments': + return 'Synchronizowanie płatności dla dostawców'; + case 'digitsSyncCardSettlements': + return 'Synchronizowanie rozliczeń karty'; + case 'digitsSyncTravelSettlements': + return 'Synchronizowanie rozliczeń podróży'; case 'businessCentralSyncTitle': return 'Synchronizowanie danych Dynamics 365 Business Central'; case 'businessCentralSyncConnection': @@ -7776,6 +7790,7 @@ Plan Control zaczyna się od 9 USD za aktywnego członka miesięcznie.`, syncTravelBillingSettlementsNoAccountTooltip: 'Aby odblokować, ustaw konto dla swoich eksportów.', syncTravelBillingSettlementsNoAutoSyncTooltip: 'Aby odblokować, włącz automatyczną synchronizację.', campfire: 'Campfire', + digits: 'Digits', zohoBooks: 'Zoho Books', continuousReconciliationFeedSelection: 'Ciągłe uzgadnianie jest konfigurowane dla każdego źródła kart osobno. Wybierz źródło, aby zmienić, które konfigurujesz.', diff --git a/src/languages/pt-BR.ts b/src/languages/pt-BR.ts index 23c8762e1f4a..b286689a1066 100644 --- a/src/languages/pt-BR.ts +++ b/src/languages/pt-BR.ts @@ -7455,6 +7455,8 @@ O plano Control começa em US$ 9 por membro ativo por mês.`, return 'DualEntry'; case CONST.POLICY.CONNECTIONS.NAME.CAMPFIRE: return 'Campfire'; + case CONST.POLICY.CONNECTIONS.NAME.DIGITS: + return 'Digits'; case CONST.POLICY.CONNECTIONS.NAME.ZOHO_BOOKS: return 'Zoho Books'; case CONST.POLICY.CONNECTIONS.NAME.BUSINESS_CENTRAL: @@ -7700,6 +7702,18 @@ O plano Control começa em US$ 9 por membro ativo por mês.`, return 'Sincronizando liquidações de cartão'; case 'campfireSyncTravelSettlements': return 'Sincronizando acertos de viagem'; + case 'digitsSyncTitle': + return 'Sincronizando dados do Digits'; + case 'digitsSyncConnection': + return 'Inicializando conexão com o Digits'; + case 'digitsSyncImportData': + return 'Carregando dados'; + case 'digitsSyncPayments': + return 'Sincronizando pagamentos a fornecedores'; + case 'digitsSyncCardSettlements': + return 'Sincronizando liquidações de cartão'; + case 'digitsSyncTravelSettlements': + return 'Sincronizando acertos de viagem'; case 'businessCentralSyncTitle': return 'Sincronizando dados do Dynamics 365 Business Central'; case 'businessCentralSyncConnection': @@ -7749,6 +7763,7 @@ O plano Control começa em US$ 9 por membro ativo por mês.`, syncTravelBillingSettlementsNoAccountTooltip: 'Para desbloquear, defina uma conta para suas exportações.', syncTravelBillingSettlementsNoAutoSyncTooltip: 'Para desbloquear, ative a sincronização automática.', campfire: 'Campfire', + digits: 'Digits', zohoBooks: 'Zoho Books', continuousReconciliationFeedSelection: 'A Conciliação Contínua é configurada por feed de cartão. Selecione um feed para alterar qual você está configurando.', diff --git a/src/languages/zh-hans.ts b/src/languages/zh-hans.ts index a7447e3f4ef3..7a56e2cc270c 100644 --- a/src/languages/zh-hans.ts +++ b/src/languages/zh-hans.ts @@ -7197,6 +7197,8 @@ _如需更详细的说明,请[访问我们的帮助网站](${CONST.NETSUITE_IM return 'DualEntry'; case CONST.POLICY.CONNECTIONS.NAME.CAMPFIRE: return 'Campfire'; + case CONST.POLICY.CONNECTIONS.NAME.DIGITS: + return 'Digits'; case CONST.POLICY.CONNECTIONS.NAME.ZOHO_BOOKS: return 'Zoho Books'; case CONST.POLICY.CONNECTIONS.NAME.BUSINESS_CENTRAL: @@ -7441,6 +7443,18 @@ _如需更详细的说明,请[访问我们的帮助网站](${CONST.NETSUITE_IM return '正在同步信用卡结算'; case 'campfireSyncTravelSettlements': return '正在同步差旅结算'; + case 'digitsSyncTitle': + return '正在同步 Digits 数据'; + case 'digitsSyncConnection': + return '正在初始化与 Digits 的连接'; + case 'digitsSyncImportData': + return '正在加载数据'; + case 'digitsSyncPayments': + return '正在同步供应商付款'; + case 'digitsSyncCardSettlements': + return '正在同步信用卡结算'; + case 'digitsSyncTravelSettlements': + return '正在同步差旅结算'; case 'businessCentralSyncTitle': return '同步 Dynamics 365 Business Central 数据'; case 'businessCentralSyncConnection': @@ -7485,6 +7499,7 @@ _如需更详细的说明,请[访问我们的帮助网站](${CONST.NETSUITE_IM syncTravelBillingSettlementsNoAccountTooltip: '要解锁,请为导出设置一个账户。', syncTravelBillingSettlementsNoAutoSyncTooltip: '若要解锁,请启用自动同步。', campfire: 'Campfire', + digits: 'Digits', zohoBooks: 'Zoho Books', continuousReconciliationFeedSelection: '按卡片流水单独配置持续对账。选择一个流水以更改您正在配置的流水。', businessCentral: 'Dynamics 365 Business Central', diff --git a/src/libs/API/parameters/ConnectPolicyToDigitsParams.ts b/src/libs/API/parameters/ConnectPolicyToDigitsParams.ts new file mode 100644 index 000000000000..1a5cfa3ecd81 --- /dev/null +++ b/src/libs/API/parameters/ConnectPolicyToDigitsParams.ts @@ -0,0 +1,5 @@ +type ConnectPolicyToDigitsParams = { + policyID: string; +}; + +export default ConnectPolicyToDigitsParams; diff --git a/src/libs/API/parameters/SyncPolicyToDigitsParams.ts b/src/libs/API/parameters/SyncPolicyToDigitsParams.ts new file mode 100644 index 000000000000..27c30a577469 --- /dev/null +++ b/src/libs/API/parameters/SyncPolicyToDigitsParams.ts @@ -0,0 +1,6 @@ +type SyncPolicyToDigitsParams = { + policyID: string; + idempotencyKey: string; +}; + +export default SyncPolicyToDigitsParams; diff --git a/src/libs/API/parameters/UpdateDigitsAccountingMethodParams.ts b/src/libs/API/parameters/UpdateDigitsAccountingMethodParams.ts new file mode 100644 index 000000000000..88361d5c6829 --- /dev/null +++ b/src/libs/API/parameters/UpdateDigitsAccountingMethodParams.ts @@ -0,0 +1,9 @@ +import type {CONST as COMMON_CONST} from 'expensify-common'; +import type {ValueOf} from 'type-fest'; + +type UpdateDigitsAccountingMethodParams = { + policyID: string; + accountingMethod: ValueOf; +}; + +export default UpdateDigitsAccountingMethodParams; diff --git a/src/libs/API/parameters/UpdateDigitsAutoSyncParams.ts b/src/libs/API/parameters/UpdateDigitsAutoSyncParams.ts new file mode 100644 index 000000000000..0e67b16dbaad --- /dev/null +++ b/src/libs/API/parameters/UpdateDigitsAutoSyncParams.ts @@ -0,0 +1,6 @@ +type UpdateDigitsAutoSyncParams = { + policyID: string; + enabled: boolean; +}; + +export default UpdateDigitsAutoSyncParams; diff --git a/src/libs/API/parameters/UpdateDigitsBillPaymentAccountParams.ts b/src/libs/API/parameters/UpdateDigitsBillPaymentAccountParams.ts new file mode 100644 index 000000000000..b1c5bde78870 --- /dev/null +++ b/src/libs/API/parameters/UpdateDigitsBillPaymentAccountParams.ts @@ -0,0 +1,8 @@ +import type {DigitsAccount} from '@src/types/onyx/Policy'; + +type UpdateDigitsBillPaymentAccountParams = { + policyID: string; + billPaymentAccountID: DigitsAccount['id']; +}; + +export default UpdateDigitsBillPaymentAccountParams; diff --git a/src/libs/API/parameters/UpdateDigitsCardProgramAccountParams.ts b/src/libs/API/parameters/UpdateDigitsCardProgramAccountParams.ts new file mode 100644 index 000000000000..755f2eb32da1 --- /dev/null +++ b/src/libs/API/parameters/UpdateDigitsCardProgramAccountParams.ts @@ -0,0 +1,10 @@ +import type {CardFeedWithNumber} from '@src/types/onyx/CardFeeds'; +import type {DigitsAccount} from '@src/types/onyx/Policy'; + +type UpdateDigitsCardProgramAccountParams = { + policyID: string; + feedKey: CardFeedWithNumber; + cardProgramAccountID: DigitsAccount['id']; +}; + +export default UpdateDigitsCardProgramAccountParams; diff --git a/src/libs/API/parameters/UpdateDigitsCreditCardAccountParams.ts b/src/libs/API/parameters/UpdateDigitsCreditCardAccountParams.ts new file mode 100644 index 000000000000..15db621de54e --- /dev/null +++ b/src/libs/API/parameters/UpdateDigitsCreditCardAccountParams.ts @@ -0,0 +1,8 @@ +import type {DigitsAccount} from '@src/types/onyx/Policy'; + +type UpdateDigitsCreditCardAccountParams = { + policyID: string; + creditCardAccountID: DigitsAccount['id']; +}; + +export default UpdateDigitsCreditCardAccountParams; diff --git a/src/libs/API/parameters/UpdateDigitsDefaultVendorParams.ts b/src/libs/API/parameters/UpdateDigitsDefaultVendorParams.ts new file mode 100644 index 000000000000..1d5abe1e5217 --- /dev/null +++ b/src/libs/API/parameters/UpdateDigitsDefaultVendorParams.ts @@ -0,0 +1,8 @@ +import type {DigitsVendor} from '@src/types/onyx/Policy'; + +type UpdateDigitsDefaultVendorParams = { + policyID: string; + vendorID: DigitsVendor['id']; +}; + +export default UpdateDigitsDefaultVendorParams; diff --git a/src/libs/API/parameters/UpdateDigitsEnableNewCategoriesParams.ts b/src/libs/API/parameters/UpdateDigitsEnableNewCategoriesParams.ts new file mode 100644 index 000000000000..bdaff2b5b7e5 --- /dev/null +++ b/src/libs/API/parameters/UpdateDigitsEnableNewCategoriesParams.ts @@ -0,0 +1,9 @@ +type UpdateDigitsEnableNewCategoriesParams = { + /** The workspace where the setting is updated. */ + policyID: string; + + /** Whether categories newly created in Digits are imported. */ + enabled: boolean; +}; + +export default UpdateDigitsEnableNewCategoriesParams; diff --git a/src/libs/API/parameters/UpdateDigitsExportDateParams.ts b/src/libs/API/parameters/UpdateDigitsExportDateParams.ts new file mode 100644 index 000000000000..f93156692503 --- /dev/null +++ b/src/libs/API/parameters/UpdateDigitsExportDateParams.ts @@ -0,0 +1,8 @@ +import type {DigitsExportDate} from '@src/types/onyx/Policy'; + +type UpdateDigitsExportDateParams = { + policyID: string; + value: DigitsExportDate; +}; + +export default UpdateDigitsExportDateParams; diff --git a/src/libs/API/parameters/UpdateDigitsExportToMultipleAccountsParams.ts b/src/libs/API/parameters/UpdateDigitsExportToMultipleAccountsParams.ts new file mode 100644 index 000000000000..238575d5dbe9 --- /dev/null +++ b/src/libs/API/parameters/UpdateDigitsExportToMultipleAccountsParams.ts @@ -0,0 +1,6 @@ +type UpdateDigitsExportToMultipleAccountsParams = { + policyID: string; + enabled: boolean; +}; + +export default UpdateDigitsExportToMultipleAccountsParams; diff --git a/src/libs/API/parameters/UpdateDigitsExporterParams.ts b/src/libs/API/parameters/UpdateDigitsExporterParams.ts new file mode 100644 index 000000000000..4518e386fe15 --- /dev/null +++ b/src/libs/API/parameters/UpdateDigitsExporterParams.ts @@ -0,0 +1,6 @@ +type UpdateDigitsExporterParams = { + policyID: string; + email: string; +}; + +export default UpdateDigitsExporterParams; diff --git a/src/libs/API/parameters/UpdateDigitsFieldMappingParams.ts b/src/libs/API/parameters/UpdateDigitsFieldMappingParams.ts new file mode 100644 index 000000000000..229884fa1956 --- /dev/null +++ b/src/libs/API/parameters/UpdateDigitsFieldMappingParams.ts @@ -0,0 +1,16 @@ +import type CONST from '@src/CONST'; + +import type {ValueOf} from 'type-fest'; + +type UpdateDigitsFieldMappingParams = { + /** The workspace where the field mapping is updated. */ + policyID: string; + + /** The Digits dimension to map. */ + fieldID: ValueOf; + + /** The Expensify dimension mapped to the Digits dimension. */ + mapping: ValueOf; +}; + +export default UpdateDigitsFieldMappingParams; diff --git a/src/libs/API/parameters/UpdateDigitsSettlementsAccountParams.ts b/src/libs/API/parameters/UpdateDigitsSettlementsAccountParams.ts new file mode 100644 index 000000000000..d7f343e7313b --- /dev/null +++ b/src/libs/API/parameters/UpdateDigitsSettlementsAccountParams.ts @@ -0,0 +1,8 @@ +import type {DigitsAccount} from '@src/types/onyx/Policy'; + +type UpdateDigitsSettlementsAccountParams = { + policyID: string; + settlementsBankAccountID: DigitsAccount['id']; +}; + +export default UpdateDigitsSettlementsAccountParams; diff --git a/src/libs/API/parameters/UpdateDigitsSyncExpensifyCardSettlementsParams.ts b/src/libs/API/parameters/UpdateDigitsSyncExpensifyCardSettlementsParams.ts new file mode 100644 index 000000000000..ab924645f9b5 --- /dev/null +++ b/src/libs/API/parameters/UpdateDigitsSyncExpensifyCardSettlementsParams.ts @@ -0,0 +1,6 @@ +type UpdateDigitsSyncExpensifyCardSettlementsParams = { + policyID: string; + enabled: boolean; +}; + +export default UpdateDigitsSyncExpensifyCardSettlementsParams; diff --git a/src/libs/API/parameters/UpdateDigitsSyncReimbursedReportsParams.ts b/src/libs/API/parameters/UpdateDigitsSyncReimbursedReportsParams.ts new file mode 100644 index 000000000000..7e033121d00f --- /dev/null +++ b/src/libs/API/parameters/UpdateDigitsSyncReimbursedReportsParams.ts @@ -0,0 +1,6 @@ +type UpdateDigitsSyncReimbursedReportsParams = { + policyID: string; + enabled: boolean; +}; + +export default UpdateDigitsSyncReimbursedReportsParams; diff --git a/src/libs/API/parameters/UpdateDigitsSyncTravelInvoicingSettlementsParams.ts b/src/libs/API/parameters/UpdateDigitsSyncTravelInvoicingSettlementsParams.ts new file mode 100644 index 000000000000..3a1ee144b869 --- /dev/null +++ b/src/libs/API/parameters/UpdateDigitsSyncTravelInvoicingSettlementsParams.ts @@ -0,0 +1,6 @@ +type UpdateDigitsSyncTravelInvoicingSettlementsParams = { + policyID: string; + enabled: boolean; +}; + +export default UpdateDigitsSyncTravelInvoicingSettlementsParams; diff --git a/src/libs/API/parameters/UpdateDigitsTravelInvoicingPayableAccountParams.ts b/src/libs/API/parameters/UpdateDigitsTravelInvoicingPayableAccountParams.ts new file mode 100644 index 000000000000..aa7b446fb3e2 --- /dev/null +++ b/src/libs/API/parameters/UpdateDigitsTravelInvoicingPayableAccountParams.ts @@ -0,0 +1,8 @@ +import type {DigitsAccount} from '@src/types/onyx/Policy'; + +type UpdateDigitsTravelInvoicingPayableAccountParams = { + policyID: string; + travelInvoicingPayableAccountID: DigitsAccount['id']; +}; + +export default UpdateDigitsTravelInvoicingPayableAccountParams; diff --git a/src/libs/API/parameters/UpdateDigitsTravelInvoicingSettlementsAccountParams.ts b/src/libs/API/parameters/UpdateDigitsTravelInvoicingSettlementsAccountParams.ts new file mode 100644 index 000000000000..3b4c204d936c --- /dev/null +++ b/src/libs/API/parameters/UpdateDigitsTravelInvoicingSettlementsAccountParams.ts @@ -0,0 +1,8 @@ +import type {DigitsAccount} from '@src/types/onyx/Policy'; + +type UpdateDigitsTravelInvoicingSettlementsAccountParams = { + policyID: string; + travelInvoicingSettlementsBankAccountID: DigitsAccount['id']; +}; + +export default UpdateDigitsTravelInvoicingSettlementsAccountParams; diff --git a/src/libs/API/parameters/index.ts b/src/libs/API/parameters/index.ts index 4b8058c6a92f..1734eb9deb54 100644 --- a/src/libs/API/parameters/index.ts +++ b/src/libs/API/parameters/index.ts @@ -31,6 +31,7 @@ export type {default as ConnectPolicyToZenefitsParams} from './ConnectPolicyToZe export type {default as ConnectPolicyToRilletParams} from './ConnectPolicyToRilletParams'; export type {default as ConnectPolicyToDualEntryParams} from './ConnectPolicyToDualEntryParams'; export type {default as ConnectPolicyToCampfireParams} from './ConnectPolicyToCampfireParams'; +export type {default as ConnectPolicyToDigitsParams} from './ConnectPolicyToDigitsParams'; export type {default as ConnectPolicyToZohoBooksParams} from './ConnectPolicyToZohoBooksParams'; export type {default as ConnectPolicyToBusinessCentralParams} from './ConnectPolicyToBusinessCentralParams'; export type {default as OpenPolicyProfilePageParams} from './OpenPolicyProfilePageParams'; @@ -104,6 +105,24 @@ export type {default as UpdateCampfireSettlementsAccountParams} from './UpdateCa export type {default as UpdateCampfireSyncTravelInvoicingSettlementsParams} from './UpdateCampfireSyncTravelInvoicingSettlementsParams'; export type {default as UpdateCampfireTravelInvoicingSettlementsAccountParams} from './UpdateCampfireTravelInvoicingSettlementsAccountParams'; export type {default as UpdateCampfireTravelInvoicingPayableAccountParams} from './UpdateCampfireTravelInvoicingPayableAccountParams'; +export type {default as SyncPolicyToDigitsParams} from './SyncPolicyToDigitsParams'; +export type {default as UpdateDigitsFieldMappingParams} from './UpdateDigitsFieldMappingParams'; +export type {default as UpdateDigitsEnableNewCategoriesParams} from './UpdateDigitsEnableNewCategoriesParams'; +export type {default as UpdateDigitsExporterParams} from './UpdateDigitsExporterParams'; +export type {default as UpdateDigitsExportDateParams} from './UpdateDigitsExportDateParams'; +export type {default as UpdateDigitsDefaultVendorParams} from './UpdateDigitsDefaultVendorParams'; +export type {default as UpdateDigitsCreditCardAccountParams} from './UpdateDigitsCreditCardAccountParams'; +export type {default as UpdateDigitsExportToMultipleAccountsParams} from './UpdateDigitsExportToMultipleAccountsParams'; +export type {default as UpdateDigitsCardProgramAccountParams} from './UpdateDigitsCardProgramAccountParams'; +export type {default as UpdateDigitsAutoSyncParams} from './UpdateDigitsAutoSyncParams'; +export type {default as UpdateDigitsAccountingMethodParams} from './UpdateDigitsAccountingMethodParams'; +export type {default as UpdateDigitsSyncReimbursedReportsParams} from './UpdateDigitsSyncReimbursedReportsParams'; +export type {default as UpdateDigitsBillPaymentAccountParams} from './UpdateDigitsBillPaymentAccountParams'; +export type {default as UpdateDigitsSyncExpensifyCardSettlementsParams} from './UpdateDigitsSyncExpensifyCardSettlementsParams'; +export type {default as UpdateDigitsSettlementsAccountParams} from './UpdateDigitsSettlementsAccountParams'; +export type {default as UpdateDigitsSyncTravelInvoicingSettlementsParams} from './UpdateDigitsSyncTravelInvoicingSettlementsParams'; +export type {default as UpdateDigitsTravelInvoicingSettlementsAccountParams} from './UpdateDigitsTravelInvoicingSettlementsAccountParams'; +export type {default as UpdateDigitsTravelInvoicingPayableAccountParams} from './UpdateDigitsTravelInvoicingPayableAccountParams'; export type {default as SyncPolicyToZohoBooksParams} from './SyncPolicyToZohoBooksParams'; export type {default as UpdateZohoBooksSubsidiaryParams} from './UpdateZohoBooksSubsidiaryParams'; export type {default as UpdateZohoBooksFieldMappingParams} from './UpdateZohoBooksFieldMappingParams'; diff --git a/src/libs/API/types.ts b/src/libs/API/types.ts index a934ec144f10..3af61651bbc1 100644 --- a/src/libs/API/types.ts +++ b/src/libs/API/types.ts @@ -639,6 +639,23 @@ const WRITE_COMMANDS = { UPDATE_CAMPFIRE_SYNC_TRAVEL_INVOICING_SETTLEMENTS: 'UpdateCampfireSyncTravelInvoicingSettlements', UPDATE_CAMPFIRE_TRAVEL_INVOICING_SETTLEMENTS_ACCOUNT: 'UpdateCampfireTravelInvoicingSettlementsAccount', UPDATE_CAMPFIRE_TRAVEL_INVOICING_PAYABLE_ACCOUNT: 'UpdateCampfireTravelInvoicingPayableAccount', + UPDATE_DIGITS_FIELD_MAPPING: 'UpdateDigitsFieldMapping', + UPDATE_DIGITS_ENABLE_NEW_CATEGORIES: 'UpdateDigitsEnableNewCategories', + UPDATE_DIGITS_EXPORTER: 'UpdateDigitsExporter', + UPDATE_DIGITS_EXPORT_DATE: 'UpdateDigitsExportDate', + UPDATE_DIGITS_DEFAULT_VENDOR: 'UpdateDigitsDefaultVendor', + UPDATE_DIGITS_CREDIT_CARD_ACCOUNT: 'UpdateDigitsCreditCardAccount', + UPDATE_DIGITS_EXPORT_TO_MULTIPLE_ACCOUNTS: 'UpdateDigitsExportToMultipleAccounts', + UPDATE_DIGITS_CARD_PROGRAM_ACCOUNT: 'UpdateDigitsCardProgramAccount', + UPDATE_DIGITS_AUTO_SYNC: 'UpdateDigitsAutoSync', + UPDATE_DIGITS_ACCOUNTING_METHOD: 'UpdateDigitsAccountingMethod', + UPDATE_DIGITS_SYNC_REIMBURSED_REPORTS: 'UpdateDigitsSyncReimbursedReports', + UPDATE_DIGITS_BILL_PAYMENT_ACCOUNT: 'UpdateDigitsBillPaymentAccount', + UPDATE_DIGITS_SYNC_EXPENSIFY_CARD_SETTLEMENTS: 'UpdateDigitsSyncExpensifyCardSettlements', + UPDATE_DIGITS_SETTLEMENTS_ACCOUNT: 'UpdateDigitsSettlementsAccount', + UPDATE_DIGITS_SYNC_TRAVEL_INVOICING_SETTLEMENTS: 'UpdateDigitsSyncTravelInvoicingSettlements', + UPDATE_DIGITS_TRAVEL_INVOICING_SETTLEMENTS_ACCOUNT: 'UpdateDigitsTravelInvoicingSettlementsAccount', + UPDATE_DIGITS_TRAVEL_INVOICING_PAYABLE_ACCOUNT: 'UpdateDigitsTravelInvoicingPayableAccount', UPDATE_ZOHO_BOOKS_SUBSIDIARY: 'UpdateZohoBooksSubsidiary', UPDATE_ZOHO_BOOKS_FIELD_MAPPING: 'UpdateZohoBooksFieldMapping', UPDATE_ZOHO_BOOKS_ENABLE_NEW_CATEGORIES: 'UpdateZohoBooksEnableNewCategories', @@ -1416,6 +1433,23 @@ type WriteCommandParameters = { [WRITE_COMMANDS.UPDATE_CAMPFIRE_SYNC_TRAVEL_INVOICING_SETTLEMENTS]: Parameters.UpdateCampfireSyncTravelInvoicingSettlementsParams; [WRITE_COMMANDS.UPDATE_CAMPFIRE_TRAVEL_INVOICING_SETTLEMENTS_ACCOUNT]: Parameters.UpdateCampfireTravelInvoicingSettlementsAccountParams; [WRITE_COMMANDS.UPDATE_CAMPFIRE_TRAVEL_INVOICING_PAYABLE_ACCOUNT]: Parameters.UpdateCampfireTravelInvoicingPayableAccountParams; + [WRITE_COMMANDS.UPDATE_DIGITS_FIELD_MAPPING]: Parameters.UpdateDigitsFieldMappingParams; + [WRITE_COMMANDS.UPDATE_DIGITS_ENABLE_NEW_CATEGORIES]: Parameters.UpdateDigitsEnableNewCategoriesParams; + [WRITE_COMMANDS.UPDATE_DIGITS_EXPORTER]: Parameters.UpdateDigitsExporterParams; + [WRITE_COMMANDS.UPDATE_DIGITS_EXPORT_DATE]: Parameters.UpdateDigitsExportDateParams; + [WRITE_COMMANDS.UPDATE_DIGITS_DEFAULT_VENDOR]: Parameters.UpdateDigitsDefaultVendorParams; + [WRITE_COMMANDS.UPDATE_DIGITS_CREDIT_CARD_ACCOUNT]: Parameters.UpdateDigitsCreditCardAccountParams; + [WRITE_COMMANDS.UPDATE_DIGITS_EXPORT_TO_MULTIPLE_ACCOUNTS]: Parameters.UpdateDigitsExportToMultipleAccountsParams; + [WRITE_COMMANDS.UPDATE_DIGITS_CARD_PROGRAM_ACCOUNT]: Parameters.UpdateDigitsCardProgramAccountParams; + [WRITE_COMMANDS.UPDATE_DIGITS_AUTO_SYNC]: Parameters.UpdateDigitsAutoSyncParams; + [WRITE_COMMANDS.UPDATE_DIGITS_ACCOUNTING_METHOD]: Parameters.UpdateDigitsAccountingMethodParams; + [WRITE_COMMANDS.UPDATE_DIGITS_SYNC_REIMBURSED_REPORTS]: Parameters.UpdateDigitsSyncReimbursedReportsParams; + [WRITE_COMMANDS.UPDATE_DIGITS_BILL_PAYMENT_ACCOUNT]: Parameters.UpdateDigitsBillPaymentAccountParams; + [WRITE_COMMANDS.UPDATE_DIGITS_SYNC_EXPENSIFY_CARD_SETTLEMENTS]: Parameters.UpdateDigitsSyncExpensifyCardSettlementsParams; + [WRITE_COMMANDS.UPDATE_DIGITS_SETTLEMENTS_ACCOUNT]: Parameters.UpdateDigitsSettlementsAccountParams; + [WRITE_COMMANDS.UPDATE_DIGITS_SYNC_TRAVEL_INVOICING_SETTLEMENTS]: Parameters.UpdateDigitsSyncTravelInvoicingSettlementsParams; + [WRITE_COMMANDS.UPDATE_DIGITS_TRAVEL_INVOICING_SETTLEMENTS_ACCOUNT]: Parameters.UpdateDigitsTravelInvoicingSettlementsAccountParams; + [WRITE_COMMANDS.UPDATE_DIGITS_TRAVEL_INVOICING_PAYABLE_ACCOUNT]: Parameters.UpdateDigitsTravelInvoicingPayableAccountParams; [WRITE_COMMANDS.UPDATE_ZOHO_BOOKS_SUBSIDIARY]: Parameters.UpdateZohoBooksSubsidiaryParams; [WRITE_COMMANDS.UPDATE_ZOHO_BOOKS_FIELD_MAPPING]: Parameters.UpdateZohoBooksFieldMappingParams; [WRITE_COMMANDS.UPDATE_ZOHO_BOOKS_ENABLE_NEW_CATEGORIES]: Parameters.UpdateZohoBooksEnableNewCategoriesParams; @@ -1658,6 +1692,8 @@ const READ_COMMANDS = { SYNC_POLICY_TO_RILLET: 'SyncPolicyToRillet', SYNC_POLICY_TO_DUALENTRY: 'SyncPolicyToDualEntry', SYNC_POLICY_TO_CAMPFIRE: 'SyncPolicyToCampfire', + SYNC_POLICY_TO_DIGITS: 'SyncPolicyToDigits', + CONNECT_POLICY_TO_DIGITS: 'ConnectPolicyToDigits', SYNC_POLICY_TO_ZOHO_BOOKS: 'SyncPolicyToZohoBooks', CONNECT_POLICY_TO_ZOHO_BOOKS: 'ConnectPolicyToZohoBooks', SYNC_POLICY_TO_BUSINESS_CENTRAL: 'SyncPolicyToBusinessCentral', @@ -1785,6 +1821,8 @@ type ReadCommandParameters = { [READ_COMMANDS.SYNC_POLICY_TO_RILLET]: Parameters.SyncPolicyToRilletParams; [READ_COMMANDS.SYNC_POLICY_TO_DUALENTRY]: Parameters.SyncPolicyToDualEntryParams; [READ_COMMANDS.SYNC_POLICY_TO_CAMPFIRE]: Parameters.SyncPolicyToCampfireParams; + [READ_COMMANDS.SYNC_POLICY_TO_DIGITS]: Parameters.SyncPolicyToDigitsParams; + [READ_COMMANDS.CONNECT_POLICY_TO_DIGITS]: Parameters.ConnectPolicyToDigitsParams; [READ_COMMANDS.SYNC_POLICY_TO_ZOHO_BOOKS]: Parameters.SyncPolicyToZohoBooksParams; [READ_COMMANDS.CONNECT_POLICY_TO_ZOHO_BOOKS]: Parameters.ConnectPolicyToZohoBooksParams; [READ_COMMANDS.SYNC_POLICY_TO_BUSINESS_CENTRAL]: Parameters.SyncPolicyToBusinessCentralParams; diff --git a/src/libs/AccountingUtils.ts b/src/libs/AccountingUtils.ts index bbf03d1bd4e5..3d9268d3c249 100644 --- a/src/libs/AccountingUtils.ts +++ b/src/libs/AccountingUtils.ts @@ -23,6 +23,7 @@ const ROUTE_NAME_MAPPING = { [CONST.POLICY.CONNECTIONS.ROUTE.MERGE_ATS]: CONST.POLICY.CONNECTIONS.NAME.MERGE_ATS, [CONST.POLICY.CONNECTIONS.ROUTE.DUALENTRY]: CONST.POLICY.CONNECTIONS.NAME.DUALENTRY, [CONST.POLICY.CONNECTIONS.ROUTE.CAMPFIRE]: CONST.POLICY.CONNECTIONS.NAME.CAMPFIRE, + [CONST.POLICY.CONNECTIONS.ROUTE.DIGITS]: CONST.POLICY.CONNECTIONS.NAME.DIGITS, [CONST.POLICY.CONNECTIONS.ROUTE.ZOHO_BOOKS]: CONST.POLICY.CONNECTIONS.NAME.ZOHO_BOOKS, [CONST.POLICY.CONNECTIONS.ROUTE.BUSINESS_CENTRAL]: CONST.POLICY.CONNECTIONS.NAME.BUSINESS_CENTRAL, }; @@ -41,6 +42,7 @@ const NAME_ROUTE_MAPPING = { [CONST.POLICY.CONNECTIONS.NAME.MERGE_ATS]: CONST.POLICY.CONNECTIONS.ROUTE.MERGE_ATS, [CONST.POLICY.CONNECTIONS.NAME.DUALENTRY]: CONST.POLICY.CONNECTIONS.ROUTE.DUALENTRY, [CONST.POLICY.CONNECTIONS.NAME.CAMPFIRE]: CONST.POLICY.CONNECTIONS.ROUTE.CAMPFIRE, + [CONST.POLICY.CONNECTIONS.NAME.DIGITS]: CONST.POLICY.CONNECTIONS.ROUTE.DIGITS, [CONST.POLICY.CONNECTIONS.NAME.ZOHO_BOOKS]: CONST.POLICY.CONNECTIONS.ROUTE.ZOHO_BOOKS, [CONST.POLICY.CONNECTIONS.NAME.BUSINESS_CENTRAL]: CONST.POLICY.CONNECTIONS.ROUTE.BUSINESS_CENTRAL, }; diff --git a/src/libs/CopyPolicySettingsUtils.ts b/src/libs/CopyPolicySettingsUtils.ts index 697fd7f8b638..26c6e69b89b6 100644 --- a/src/libs/CopyPolicySettingsUtils.ts +++ b/src/libs/CopyPolicySettingsUtils.ts @@ -117,6 +117,12 @@ function getConnectionCompanyID(policy: Policy | undefined, connectionName: Conn return readCredentialsCompanyID(connections[CONST.POLICY.CONNECTIONS.NAME.CAMPFIRE]?.config); case CONST.POLICY.CONNECTIONS.NAME.ZOHO_BOOKS: return readCredentialsCompanyID(connections[CONST.POLICY.CONNECTIONS.NAME.ZOHO_BOOKS]?.config); + case CONST.POLICY.CONNECTIONS.NAME.DIGITS: { + // Digits credentials carry a per-sign-in connection key rather than the company, so two workspaces signed + // in to the same Digits company are only recognized as such by the company the sync returned. + const companyID = connections[CONST.POLICY.CONNECTIONS.NAME.DIGITS]?.data?.company?.id; + return companyID || undefined; + } default: return undefined; } diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index b1d299082bb8..887138b8f1b7 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -14147,6 +14147,7 @@ function getIntegrationIcon( | 'RilletSquare' | 'DualEntrySquare' | 'CampfireSquare' + | 'DigitsSquare' | 'ZohoBooksSquare' | 'BusinessCentralSquare' | 'GustoSquare' @@ -14187,6 +14188,9 @@ function getIntegrationIcon( if (connectionName === CONST.POLICY.CONNECTIONS.NAME.CAMPFIRE) { return expensifyIcons?.CampfireSquare; } + if (connectionName === CONST.POLICY.CONNECTIONS.NAME.DIGITS) { + return expensifyIcons?.DigitsSquare; + } if (connectionName === CONST.POLICY.CONNECTIONS.NAME.ZOHO_BOOKS) { return expensifyIcons?.ZohoBooksSquare; } diff --git a/src/libs/actions/connections/Digits.ts b/src/libs/actions/connections/Digits.ts new file mode 100644 index 000000000000..48629d33f82d --- /dev/null +++ b/src/libs/actions/connections/Digits.ts @@ -0,0 +1,716 @@ +/** + * Onyx actions for the Digits accounting connection. Every setting saves optimistically with a pending field, and + * rolls back to its previous value with an error when the server rejects it. + */ +import {write} from '@libs/API'; +import type { + UpdateDigitsAccountingMethodParams, + UpdateDigitsAutoSyncParams, + UpdateDigitsBillPaymentAccountParams, + UpdateDigitsCardProgramAccountParams, + UpdateDigitsCreditCardAccountParams, + UpdateDigitsDefaultVendorParams, + UpdateDigitsEnableNewCategoriesParams, + UpdateDigitsExportDateParams, + UpdateDigitsExporterParams, + UpdateDigitsExportToMultipleAccountsParams, + UpdateDigitsFieldMappingParams, + UpdateDigitsSettlementsAccountParams, + UpdateDigitsSyncExpensifyCardSettlementsParams, + UpdateDigitsSyncReimbursedReportsParams, + UpdateDigitsSyncTravelInvoicingSettlementsParams, + UpdateDigitsTravelInvoicingPayableAccountParams, + UpdateDigitsTravelInvoicingSettlementsAccountParams, +} from '@libs/API/parameters'; +import {READ_COMMANDS, WRITE_COMMANDS} from '@libs/API/types'; +import {getCommandURL} from '@libs/ApiUtils'; +import {getMicroSecondOnyxErrorWithTranslationKey} from '@libs/ErrorUtils'; + +import CONST from '@src/CONST'; +import ONYXKEYS from '@src/ONYXKEYS'; +import type {DigitsAutoSync, DigitsCoding, DigitsConnectionsConfig, DigitsExport, DigitsSync} from '@src/types/onyx/Policy'; + +import type {OnyxUpdate} from 'react-native-onyx'; +import type {ValueOf} from 'type-fest'; + +import Onyx from 'react-native-onyx'; + +function getDigitsSetupLink(policyID: string) { + // Digits authenticates with OAuth, so the connector opens this URL in the browser. + const params = new URLSearchParams({policyID}); + const commandURL = getCommandURL({command: READ_COMMANDS.CONNECT_POLICY_TO_DIGITS, shouldSkipWebProxy: true}); + return commandURL + params.toString(); +} + +function clearDigitsErrorField(policyID: string, fieldName: string) { + Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, { + connections: { + [CONST.POLICY.CONNECTIONS.NAME.DIGITS]: { + config: {errorFields: {[fieldName]: null}}, + }, + }, + }); +} + +function prepareDigitsOnyxData( + policyID: string, + settingName: TSettingName, + settingValue: Partial, + oldSettingValue: Partial | null, +) { + const optimisticData: Array> = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + connections: { + [CONST.POLICY.CONNECTIONS.NAME.DIGITS]: { + config: { + [settingName]: settingValue ?? null, + pendingFields: { + [settingName]: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, + }, + errorFields: { + [settingName]: null, + }, + }, + }, + }, + }, + }, + ]; + + const successData: Array> = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + connections: { + [CONST.POLICY.CONNECTIONS.NAME.DIGITS]: { + config: { + pendingFields: { + [settingName]: null, + }, + }, + }, + }, + }, + }, + ]; + + const failureData: Array> = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + connections: { + [CONST.POLICY.CONNECTIONS.NAME.DIGITS]: { + config: { + [settingName]: oldSettingValue ?? null, + pendingFields: { + [settingName]: null, + }, + errorFields: { + [settingName]: getMicroSecondOnyxErrorWithTranslationKey('common.genericErrorMessage'), + }, + }, + }, + }, + }, + }, + ]; + + return {optimisticData, successData, failureData}; +} + +function prepareDigitsFieldMappingOnyxData( + policyID: string, + fieldID: keyof NonNullable, + mapping: ValueOf, + oldMapping: ValueOf | null, +) { + const fieldOfflineFeedbackKey = `${CONST.DIGITS_CONFIG.FIELD_MAPPING_PREFIX}${fieldID}`; + + const optimisticData: Array> = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + connections: { + [CONST.POLICY.CONNECTIONS.NAME.DIGITS]: { + config: { + coding: { + fieldMappings: { + [fieldID]: mapping, + }, + }, + pendingFields: { + [fieldOfflineFeedbackKey]: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, + }, + errorFields: { + [fieldOfflineFeedbackKey]: null, + }, + }, + }, + }, + }, + }, + ]; + + const successData: Array> = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + connections: { + [CONST.POLICY.CONNECTIONS.NAME.DIGITS]: { + config: { + pendingFields: { + [fieldOfflineFeedbackKey]: null, + }, + }, + }, + }, + }, + }, + ]; + + const failureData: Array> = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + connections: { + [CONST.POLICY.CONNECTIONS.NAME.DIGITS]: { + config: { + coding: { + fieldMappings: { + [fieldID]: oldMapping ?? null, + }, + }, + pendingFields: { + [fieldOfflineFeedbackKey]: null, + }, + errorFields: { + [fieldOfflineFeedbackKey]: getMicroSecondOnyxErrorWithTranslationKey('common.genericErrorMessage'), + }, + }, + }, + }, + }, + }, + ]; + + return {optimisticData, successData, failureData}; +} + +function prepareDigitsExportOnyxData( + policyID: string, + settingName: TSettingName, + settingValue: Partial, + oldSettingValue: Partial | null, +) { + const optimisticData: Array> = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + connections: { + [CONST.POLICY.CONNECTIONS.NAME.DIGITS]: { + config: { + export: { + [settingName]: settingValue ?? null, + }, + pendingFields: { + [settingName]: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, + }, + errorFields: { + [settingName]: null, + }, + }, + }, + }, + }, + }, + ]; + + const successData: Array> = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + connections: { + [CONST.POLICY.CONNECTIONS.NAME.DIGITS]: { + config: { + pendingFields: { + [settingName]: null, + }, + }, + }, + }, + }, + }, + ]; + + const failureData: Array> = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + connections: { + [CONST.POLICY.CONNECTIONS.NAME.DIGITS]: { + config: { + export: { + [settingName]: oldSettingValue ?? null, + }, + pendingFields: { + [settingName]: null, + }, + errorFields: { + [settingName]: getMicroSecondOnyxErrorWithTranslationKey('common.genericErrorMessage'), + }, + }, + }, + }, + }, + }, + ]; + + return {optimisticData, successData, failureData}; +} + +function prepareDigitsAutoSyncOnyxData(policyID: string, enabled: DigitsAutoSync['enabled'], oldEnabled?: DigitsAutoSync['enabled'] | null) { + const optimisticData: Array> = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + connections: { + [CONST.POLICY.CONNECTIONS.NAME.DIGITS]: { + config: { + autoSync: { + enabled, + }, + pendingFields: { + [CONST.DIGITS_CONFIG.AUTO_SYNC]: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, + }, + errorFields: { + [CONST.DIGITS_CONFIG.AUTO_SYNC]: null, + }, + }, + }, + }, + }, + }, + ]; + + const successData: Array> = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + connections: { + [CONST.POLICY.CONNECTIONS.NAME.DIGITS]: { + config: { + pendingFields: { + [CONST.DIGITS_CONFIG.AUTO_SYNC]: null, + }, + }, + }, + }, + }, + }, + ]; + + const failureData: Array> = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + connections: { + [CONST.POLICY.CONNECTIONS.NAME.DIGITS]: { + config: { + autoSync: { + enabled: oldEnabled ?? null, + }, + pendingFields: { + [CONST.DIGITS_CONFIG.AUTO_SYNC]: null, + }, + errorFields: { + [CONST.DIGITS_CONFIG.AUTO_SYNC]: getMicroSecondOnyxErrorWithTranslationKey('common.genericErrorMessage'), + }, + }, + }, + }, + }, + }, + ]; + + return {optimisticData, successData, failureData}; +} + +function prepareDigitsSyncOnyxData( + policyID: string, + settingName: TSettingName, + settingValue: Partial, + oldSettingValue: Partial | null, +) { + const optimisticData: Array> = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + connections: { + [CONST.POLICY.CONNECTIONS.NAME.DIGITS]: { + config: { + sync: { + [settingName]: settingValue ?? null, + }, + pendingFields: { + [settingName]: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, + }, + errorFields: { + [settingName]: null, + }, + }, + }, + }, + }, + }, + ]; + + const successData: Array> = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + connections: { + [CONST.POLICY.CONNECTIONS.NAME.DIGITS]: { + config: { + pendingFields: { + [settingName]: null, + }, + }, + }, + }, + }, + }, + ]; + + const failureData: Array> = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + connections: { + [CONST.POLICY.CONNECTIONS.NAME.DIGITS]: { + config: { + sync: { + [settingName]: oldSettingValue ?? null, + }, + pendingFields: { + [settingName]: null, + }, + errorFields: { + [settingName]: getMicroSecondOnyxErrorWithTranslationKey('common.genericErrorMessage'), + }, + }, + }, + }, + }, + }, + ]; + + return {optimisticData, successData, failureData}; +} + +function prepareDigitsCardProgramAccountOnyxData( + policyID: string, + feedKey: keyof DigitsExport['cardProgramAccounts'], + accountID: ValueOf, + oldAccountID?: ValueOf | null, +) { + const cardProgramAccountOfflineFeedbackKey = `${CONST.DIGITS_CONFIG.CARD_PROGRAM_ACCOUNT_PREFIX}${feedKey}`; + + const optimisticData: Array> = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + connections: { + [CONST.POLICY.CONNECTIONS.NAME.DIGITS]: { + config: { + export: { + cardProgramAccounts: { + // An empty accountID string implies clearing the custom account + [feedKey]: accountID || null, + }, + }, + pendingFields: { + [cardProgramAccountOfflineFeedbackKey]: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, + }, + errorFields: { + [cardProgramAccountOfflineFeedbackKey]: null, + }, + }, + }, + }, + }, + }, + ]; + + const successData: Array> = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + connections: { + [CONST.POLICY.CONNECTIONS.NAME.DIGITS]: { + config: { + pendingFields: { + [cardProgramAccountOfflineFeedbackKey]: null, + }, + }, + }, + }, + }, + }, + ]; + + const failureData: Array> = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + connections: { + [CONST.POLICY.CONNECTIONS.NAME.DIGITS]: { + config: { + export: { + cardProgramAccounts: { + [feedKey]: oldAccountID ?? null, + }, + }, + pendingFields: { + [cardProgramAccountOfflineFeedbackKey]: null, + }, + errorFields: { + [cardProgramAccountOfflineFeedbackKey]: getMicroSecondOnyxErrorWithTranslationKey('common.genericErrorMessage'), + }, + }, + }, + }, + }, + }, + ]; + + return {optimisticData, successData, failureData}; +} + +function updateDigitsEnableNewCategories(policyID: string, enabled: DigitsConnectionsConfig['enableNewCategories'], oldEnabled?: DigitsConnectionsConfig['enableNewCategories']) { + const onyxData = prepareDigitsOnyxData(policyID, CONST.DIGITS_CONFIG.ENABLE_NEW_CATEGORIES, enabled, oldEnabled ?? null); + const parameters: UpdateDigitsEnableNewCategoriesParams = { + policyID, + enabled, + }; + write(WRITE_COMMANDS.UPDATE_DIGITS_ENABLE_NEW_CATEGORIES, parameters, onyxData); +} + +function updateDigitsFieldMapping( + policyID: string, + fieldID: keyof NonNullable, + mapping: ValueOf, + oldMapping?: ValueOf, +) { + const onyxData = prepareDigitsFieldMappingOnyxData(policyID, fieldID, mapping, oldMapping ?? null); + const parameters: UpdateDigitsFieldMappingParams = { + policyID, + fieldID, + mapping, + }; + write(WRITE_COMMANDS.UPDATE_DIGITS_FIELD_MAPPING, parameters, onyxData); +} + +function updateDigitsExporter(policyID: string, email: DigitsExport['exporter'], oldEmail?: DigitsExport['exporter']) { + const onyxData = prepareDigitsExportOnyxData(policyID, CONST.DIGITS_CONFIG.EXPORTER, email, oldEmail ?? null); + const parameters: UpdateDigitsExporterParams = { + policyID, + email, + }; + write(WRITE_COMMANDS.UPDATE_DIGITS_EXPORTER, parameters, onyxData); +} + +function updateDigitsExportDate(policyID: string, value: DigitsExport['exportDate'], oldValue?: DigitsExport['exportDate']) { + const onyxData = prepareDigitsExportOnyxData(policyID, CONST.DIGITS_CONFIG.EXPORT_DATE, value, oldValue ?? null); + const parameters: UpdateDigitsExportDateParams = { + policyID, + value, + }; + write(WRITE_COMMANDS.UPDATE_DIGITS_EXPORT_DATE, parameters, onyxData); +} + +function updateDigitsDefaultVendor(policyID: string, vendorID: DigitsExport['defaultVendorID'], oldVendorID?: DigitsExport['defaultVendorID']) { + const onyxData = prepareDigitsExportOnyxData(policyID, CONST.DIGITS_CONFIG.DEFAULT_VENDORID, vendorID, oldVendorID ?? null); + const parameters: UpdateDigitsDefaultVendorParams = { + policyID, + vendorID, + }; + write(WRITE_COMMANDS.UPDATE_DIGITS_DEFAULT_VENDOR, parameters, onyxData); +} + +function updateDigitsCreditCardAccount(policyID: string, creditCardAccountID: DigitsExport['creditCardAccountID'], oldCreditCardAccountID?: DigitsExport['creditCardAccountID']) { + const onyxData = prepareDigitsExportOnyxData(policyID, CONST.DIGITS_CONFIG.CREDIT_CARD_ACCOUNT_ID, creditCardAccountID, oldCreditCardAccountID ?? null); + const parameters: UpdateDigitsCreditCardAccountParams = { + policyID, + creditCardAccountID, + }; + write(WRITE_COMMANDS.UPDATE_DIGITS_CREDIT_CARD_ACCOUNT, parameters, onyxData); +} + +function updateDigitsAutoSync(policyID: string, enabled: DigitsAutoSync['enabled'], oldEnabled?: DigitsAutoSync['enabled']) { + const onyxData = prepareDigitsAutoSyncOnyxData(policyID, enabled, oldEnabled ?? null); + const parameters: UpdateDigitsAutoSyncParams = { + policyID, + enabled, + }; + write(WRITE_COMMANDS.UPDATE_DIGITS_AUTO_SYNC, parameters, onyxData); +} + +function updateDigitsAccountingMethod(policyID: string, accountingMethod: DigitsExport['accountingMethod'], oldAccountingMethod?: DigitsExport['accountingMethod']) { + const onyxData = prepareDigitsExportOnyxData(policyID, CONST.DIGITS_CONFIG.ACCOUNTING_METHOD, accountingMethod, oldAccountingMethod ?? null); + const parameters: UpdateDigitsAccountingMethodParams = { + policyID, + accountingMethod, + }; + write(WRITE_COMMANDS.UPDATE_DIGITS_ACCOUNTING_METHOD, parameters, onyxData); +} + +function updateDigitsSyncReimbursedReports(policyID: string, enabled: DigitsSync['syncReimbursedReports'], oldEnabled?: DigitsSync['syncReimbursedReports']) { + const onyxData = prepareDigitsSyncOnyxData(policyID, CONST.DIGITS_CONFIG.SYNC_REIMBURSED_REPORTS, enabled, oldEnabled ?? null); + const parameters: UpdateDigitsSyncReimbursedReportsParams = { + policyID, + enabled, + }; + write(WRITE_COMMANDS.UPDATE_DIGITS_SYNC_REIMBURSED_REPORTS, parameters, onyxData); +} + +function updateDigitsBillPaymentAccount(policyID: string, billPaymentAccountID: DigitsSync['billPaymentAccountID'], oldBillPaymentAccountID?: DigitsSync['billPaymentAccountID']) { + const onyxData = prepareDigitsSyncOnyxData(policyID, CONST.DIGITS_CONFIG.BILL_PAYMENT_ACCOUNT_ID, billPaymentAccountID, oldBillPaymentAccountID ?? null); + const parameters: UpdateDigitsBillPaymentAccountParams = { + policyID, + billPaymentAccountID, + }; + write(WRITE_COMMANDS.UPDATE_DIGITS_BILL_PAYMENT_ACCOUNT, parameters, onyxData); +} + +function updateDigitsSyncExpensifyCardSettlements(policyID: string, enabled: DigitsSync['syncExpensifyCardSettlements'], oldEnabled?: DigitsSync['syncExpensifyCardSettlements']) { + const onyxData = prepareDigitsSyncOnyxData(policyID, CONST.DIGITS_CONFIG.SYNC_EXPENSIFY_CARD_SETTLEMENTS, enabled, oldEnabled ?? null); + const parameters: UpdateDigitsSyncExpensifyCardSettlementsParams = { + policyID, + enabled, + }; + write(WRITE_COMMANDS.UPDATE_DIGITS_SYNC_EXPENSIFY_CARD_SETTLEMENTS, parameters, onyxData); +} + +function updateDigitsSettlementsAccount( + policyID: string, + settlementsBankAccountID: DigitsSync['settlementsBankAccountID'], + oldSettlementsBankAccountID?: DigitsSync['settlementsBankAccountID'], +) { + const onyxData = prepareDigitsSyncOnyxData(policyID, CONST.DIGITS_CONFIG.SETTLEMENTS_BANK_ACCOUNT_ID, settlementsBankAccountID, oldSettlementsBankAccountID ?? null); + const parameters: UpdateDigitsSettlementsAccountParams = { + policyID, + settlementsBankAccountID, + }; + write(WRITE_COMMANDS.UPDATE_DIGITS_SETTLEMENTS_ACCOUNT, parameters, onyxData); +} + +function updateDigitsSyncTravelInvoicingSettlements(policyID: string, enabled: DigitsSync['syncTravelInvoicingSettlements'], oldEnabled?: DigitsSync['syncTravelInvoicingSettlements']) { + const onyxData = prepareDigitsSyncOnyxData(policyID, CONST.DIGITS_CONFIG.SYNC_TRAVEL_BILLING_SETTLEMENTS, enabled, oldEnabled ?? null); + const parameters: UpdateDigitsSyncTravelInvoicingSettlementsParams = { + policyID, + enabled, + }; + write(WRITE_COMMANDS.UPDATE_DIGITS_SYNC_TRAVEL_INVOICING_SETTLEMENTS, parameters, onyxData); +} + +function updateDigitsTravelInvoicingSettlementsAccount( + policyID: string, + travelInvoicingSettlementsBankAccountID: DigitsSync['travelInvoicingSettlementsBankAccountID'], + oldTravelInvoicingSettlementsBankAccountID?: DigitsSync['travelInvoicingSettlementsBankAccountID'], +) { + const onyxData = prepareDigitsSyncOnyxData( + policyID, + CONST.DIGITS_CONFIG.TRAVEL_BILLING_SETTLEMENTS_BANK_ACCOUNT_ID, + travelInvoicingSettlementsBankAccountID, + oldTravelInvoicingSettlementsBankAccountID ?? null, + ); + const parameters: UpdateDigitsTravelInvoicingSettlementsAccountParams = { + policyID, + travelInvoicingSettlementsBankAccountID, + }; + write(WRITE_COMMANDS.UPDATE_DIGITS_TRAVEL_INVOICING_SETTLEMENTS_ACCOUNT, parameters, onyxData); +} + +function updateDigitsTravelInvoicingPayableAccount( + policyID: string, + travelInvoicingPayableAccountID: DigitsExport['travelInvoicingPayableAccountID'], + oldTravelInvoicingPayableAccountID?: DigitsExport['travelInvoicingPayableAccountID'], +) { + const onyxData = prepareDigitsExportOnyxData( + policyID, + CONST.DIGITS_CONFIG.TRAVEL_BILLING_PAYABLE_ACCOUNT_ID, + travelInvoicingPayableAccountID, + oldTravelInvoicingPayableAccountID ?? null, + ); + const parameters: UpdateDigitsTravelInvoicingPayableAccountParams = { + policyID, + travelInvoicingPayableAccountID, + }; + write(WRITE_COMMANDS.UPDATE_DIGITS_TRAVEL_INVOICING_PAYABLE_ACCOUNT, parameters, onyxData); +} + +function updateDigitsExportToMultipleAccounts(policyID: string, enabled: DigitsExport['exportToMultipleAccounts'], oldEnabled?: DigitsExport['exportToMultipleAccounts']) { + const onyxData = prepareDigitsExportOnyxData(policyID, CONST.DIGITS_CONFIG.EXPORT_TO_MULTIPLE_ACCOUNTS, enabled, oldEnabled ?? null); + const parameters: UpdateDigitsExportToMultipleAccountsParams = { + policyID, + enabled, + }; + write(WRITE_COMMANDS.UPDATE_DIGITS_EXPORT_TO_MULTIPLE_ACCOUNTS, parameters, onyxData); +} + +function updateDigitsCardProgramAccount( + policyID: string, + feedKey: keyof DigitsExport['cardProgramAccounts'], + cardProgramAccountID: ValueOf, + oldCardProgramAccountID?: ValueOf, +) { + const onyxData = prepareDigitsCardProgramAccountOnyxData(policyID, feedKey, cardProgramAccountID, oldCardProgramAccountID ?? null); + const parameters: UpdateDigitsCardProgramAccountParams = { + policyID, + feedKey, + cardProgramAccountID, + }; + write(WRITE_COMMANDS.UPDATE_DIGITS_CARD_PROGRAM_ACCOUNT, parameters, onyxData); +} + +export { + getDigitsSetupLink, + clearDigitsErrorField, + updateDigitsEnableNewCategories, + updateDigitsFieldMapping, + updateDigitsExporter, + updateDigitsExportDate, + updateDigitsDefaultVendor, + updateDigitsCreditCardAccount, + updateDigitsAutoSync, + updateDigitsAccountingMethod, + updateDigitsSyncReimbursedReports, + updateDigitsBillPaymentAccount, + updateDigitsSyncExpensifyCardSettlements, + updateDigitsSettlementsAccount, + updateDigitsSyncTravelInvoicingSettlements, + updateDigitsTravelInvoicingSettlementsAccount, + updateDigitsTravelInvoicingPayableAccount, + updateDigitsExportToMultipleAccounts, + updateDigitsCardProgramAccount, +}; diff --git a/src/libs/actions/connections/index.ts b/src/libs/actions/connections/index.ts index 850f9ed50cf7..8afc6927d12c 100644 --- a/src/libs/actions/connections/index.ts +++ b/src/libs/actions/connections/index.ts @@ -163,6 +163,9 @@ function getSyncConnectionParameters(connectionName: PolicyConnectionName) { case CONST.POLICY.CONNECTIONS.NAME.CAMPFIRE: { return {readCommand: READ_COMMANDS.SYNC_POLICY_TO_CAMPFIRE, stageInProgress: CONST.POLICY.CONNECTIONS.SYNC_STAGE_NAME.CAMPFIRE_SYNC_CONNECTION}; } + case CONST.POLICY.CONNECTIONS.NAME.DIGITS: { + return {readCommand: READ_COMMANDS.SYNC_POLICY_TO_DIGITS, stageInProgress: CONST.POLICY.CONNECTIONS.SYNC_STAGE_NAME.DIGITS_SYNC_CONNECTION}; + } case CONST.POLICY.CONNECTIONS.NAME.ZOHO_BOOKS: { return {readCommand: READ_COMMANDS.SYNC_POLICY_TO_ZOHO_BOOKS, stageInProgress: CONST.POLICY.CONNECTIONS.SYNC_STAGE_NAME.ZOHO_BOOKS_SYNC_CONNECTION}; } @@ -390,6 +393,9 @@ function copyExistingPolicyConnection(connectedPolicyID: string, targetPolicyID: case CONST.POLICY.CONNECTIONS.NAME.CAMPFIRE: stageInProgress = CONST.POLICY.CONNECTIONS.SYNC_STAGE_NAME.CAMPFIRE_SYNC_CONNECTION; break; + case CONST.POLICY.CONNECTIONS.NAME.DIGITS: + stageInProgress = CONST.POLICY.CONNECTIONS.SYNC_STAGE_NAME.DIGITS_SYNC_CONNECTION; + break; default: stageInProgress = null; } diff --git a/src/pages/workspace/accounting/useConnectedAccountingIntegration.tsx b/src/pages/workspace/accounting/useConnectedAccountingIntegration.tsx index 0e0b442f15b9..e80dfa8525b3 100644 --- a/src/pages/workspace/accounting/useConnectedAccountingIntegration.tsx +++ b/src/pages/workspace/accounting/useConnectedAccountingIntegration.tsx @@ -135,6 +135,7 @@ function useConnectedAccountingIntegration(policy: OnyxEntry, {menuItemW const connectionSyncStage = connectionSyncProgress?.stageInProgress; const canUseBusinessCentralIntegration = isBetaEnabled(CONST.BETAS.BUSINESS_CENTRAL) || !!policy?.connections?.businessCentral; const canUseZohoBooksIntegration = isBetaEnabled(CONST.BETAS.ZOHO_BOOKS) || !!policy?.connections?.zohoBooks; + const canUseDigitsIntegration = isBetaEnabled(CONST.BETAS.DIGITS) || !!policy?.connections?.digits; const accountingIntegrations = CONST.POLICY.CONNECTIONS.ACCOUNTING_CONNECTION_NAMES.filter((name) => { if (name === CONST.POLICY.CONNECTIONS.NAME.BUSINESS_CENTRAL) { return canUseBusinessCentralIntegration; @@ -142,6 +143,9 @@ function useConnectedAccountingIntegration(policy: OnyxEntry, {menuItemW if (name === CONST.POLICY.CONNECTIONS.NAME.ZOHO_BOOKS) { return canUseZohoBooksIntegration; } + if (name === CONST.POLICY.CONNECTIONS.NAME.DIGITS) { + return canUseDigitsIntegration; + } return true; }); const syncingAccountingIntegration = accountingIntegrations.find((integration) => integration === connectionSyncProgress?.connectionName); diff --git a/src/pages/workspace/connections/useAccountingConnectionListings.ts b/src/pages/workspace/connections/useAccountingConnectionListings.ts index f3dd0639b920..93eddd3a178b 100644 --- a/src/pages/workspace/connections/useAccountingConnectionListings.ts +++ b/src/pages/workspace/connections/useAccountingConnectionListings.ts @@ -63,6 +63,7 @@ function useAccountingConnectionListings(policy: OnyxEntry): ConnectionL const canUseBusinessCentralIntegration = isBetaEnabled(CONST.BETAS.BUSINESS_CENTRAL) || !!policy?.connections?.businessCentral; const canUseZohoBooksIntegration = isBetaEnabled(CONST.BETAS.ZOHO_BOOKS) || !!policy?.connections?.zohoBooks; + const canUseDigitsIntegration = isBetaEnabled(CONST.BETAS.DIGITS) || !!policy?.connections?.digits; const accountingIntegrations = CONST.POLICY.CONNECTIONS.ACCOUNTING_CONNECTION_NAMES.filter((name) => { if (name === CONST.POLICY.CONNECTIONS.NAME.BUSINESS_CENTRAL) { return canUseBusinessCentralIntegration; @@ -70,6 +71,9 @@ function useAccountingConnectionListings(policy: OnyxEntry): ConnectionL if (name === CONST.POLICY.CONNECTIONS.NAME.ZOHO_BOOKS) { return canUseZohoBooksIntegration; } + if (name === CONST.POLICY.CONNECTIONS.NAME.DIGITS) { + return canUseDigitsIntegration; + } return true; }); diff --git a/src/selectors/Policy.ts b/src/selectors/Policy.ts index 0370a3da0d49..7bb8ea6f93d1 100644 --- a/src/selectors/Policy.ts +++ b/src/selectors/Policy.ts @@ -44,7 +44,8 @@ type ReusablePolicyConnectionName = | typeof CONST.POLICY.CONNECTIONS.NAME.CERTINIA | typeof CONST.POLICY.CONNECTIONS.NAME.RILLET | typeof CONST.POLICY.CONNECTIONS.NAME.DUALENTRY - | typeof CONST.POLICY.CONNECTIONS.NAME.CAMPFIRE; + | typeof CONST.POLICY.CONNECTIONS.NAME.CAMPFIRE + | typeof CONST.POLICY.CONNECTIONS.NAME.DIGITS; const ownerPoliciesSelector = (policies: OnyxCollection, currentUserAccountID: number) => getOwnedPaidPolicies(policies, currentUserAccountID); @@ -477,6 +478,9 @@ const adminPoliciesConnectedToDualEntrySelector = (policies: OnyxCollection) => Object.values(policies ?? {}).filter((policy): policy is Policy => isAdminPolicyConnectedTo(policy, CONST.POLICY.CONNECTIONS.NAME.CAMPFIRE)); +const adminPoliciesConnectedToDigitsSelector = (policies: OnyxCollection) => + Object.values(policies ?? {}).filter((policy): policy is Policy => isAdminPolicyConnectedTo(policy, CONST.POLICY.CONNECTIONS.NAME.DIGITS)); + const reusableConnectionAdminSelectors: Record) => Policy[]> = { [CONST.POLICY.CONNECTIONS.NAME.NETSUITE]: adminPoliciesConnectedToNetSuiteSelector, [CONST.POLICY.CONNECTIONS.NAME.SAGE_INTACCT]: adminPoliciesConnectedToSageIntacctSelector, @@ -485,6 +489,7 @@ const reusableConnectionAdminSelectors: Record; +/** + * The Digits company the connection is authorized for. A Digits sign-in covers exactly one company. + */ +type DigitsCompany = { + /** Unique identifier of the company. */ + id: string; + + /** Name of the company. */ + name: string; + + /** Currency the company operates in. Digits only supports USD companies. */ + currency: string; +}; + +/** + * Chart of accounts entry retrieved from Digits. + */ +type DigitsAccount = { + /** Unique identifier of the account. */ + id: string; + + /** Name of the account. */ + name: string; + + /** Account number shown in Digits, imported as the GL code. */ + displayNumber?: string; + + /** Unique identifier of the parent account. */ + parentID?: string; + + /** Type of the account, as the Digits API names it. */ + type: string; + + /** Subtype of the account. Account pickers filter on the values in `CONST.DIGITS_ACCOUNT_SUBTYPE`. */ + subtype: string; + + /** Whether the account is active. */ + isActive: boolean; +}; + +/** + * Vendor retrieved from Digits. + */ +type DigitsVendor = { + /** The vendor's Digits external ID, serialized as a JSON string of its issuer and ID. */ + id: string; + + /** Name of the vendor. */ + name: string; + + /** Email address associated with the vendor. */ + email?: string; + + /** Roles Digits derived from the vendor's ledger activity. A vendor without activity has none yet. */ + roles: string[]; +}; + +/** + * Connection data retrieved from Digits. + */ +type DigitsConnectionData = { + /** The company the connection is authorized for. */ + company?: DigitsCompany; + + /** Accounts available in Digits. */ + accounts?: DigitsAccount[]; + + /** Vendors available in Digits. */ + vendors?: DigitsVendor[]; +}; + +/** + * Digits dimensions that can be mapped to Expensify tags. + */ +type DigitsField = ValueOf; + +/** + * Coding configuration used when importing data from Digits. + */ +type DigitsCoding = { + /** Mapping of Digits dimensions to their configured mapping behavior. */ + fieldMappings?: Partial>>; +}; + +/** Offline feedback key for field mapping */ +type DigitsCodingFieldMappingsOfflineFeedbackKey = `${typeof CONST.DIGITS_CONFIG.FIELD_MAPPING_PREFIX}${DigitsField}`; + +/** + * Available dates that can be used as the export date. + */ +type DigitsExportDate = ValueOf; + +/** + * Export strategy for reimbursable expenses. + */ +type DigitsExportReimbursable = ValueOf; + +/** + * Export strategy for company card expenses. + */ +type DigitsExportNonReimbursable = ValueOf; + +/** + * Export configuration for sending accounting data to Digits. + */ +type DigitsExport = { + /** Identifier of the export implementation to use. */ + exporter: string; + + /** Date source used when generating exported transactions. */ + exportDate: DigitsExportDate; + + /** Export behavior for reimbursable expenses. */ + reimbursable: DigitsExportReimbursable; + + /** Export behavior for company card expenses. */ + nonReimbursable: DigitsExportNonReimbursable; + + /** Account used when exporting company card expenses. */ + creditCardAccountID: string; + + /** + * Whether card transactions should be exported to multiple + * accounts based on card program mappings. + */ + exportToMultipleAccounts: boolean; + + /** + * Mapping of card program identifiers to account IDs. + */ + cardProgramAccounts: Record; + + /** Default vendor used when exporting transactions. */ + defaultVendorID: string; + + /** Payable account used when exporting travel billings. */ + travelInvoicingPayableAccountID: string; + + /** Accounting method used during export. */ + accountingMethod: ValueOf; +}; + +/** Offline feedback key for card program account */ +type DigitsExportCardProgramAccountsOfflineFeedbackKey = `${typeof CONST.DIGITS_CONFIG.CARD_PROGRAM_ACCOUNT_PREFIX}${string}`; + +/** + * Offline feedback keys for `DigitsExport` + */ +type DigitsExportOfflineFeedbackKeys = keyof Omit | DigitsExportCardProgramAccountsOfflineFeedbackKey; + +/** + * Automatic synchronization settings for Digits. + */ +type DigitsAutoSync = { + /** Whether automatic synchronization is enabled. */ + enabled: boolean; +}; + +/** + * Synchronization settings for importing and updating data in Digits. + */ +type DigitsSync = { + /** Whether reimbursed expense reports should be synchronized. */ + syncReimbursedReports: boolean; + + /** Account used for bill payment transactions. */ + billPaymentAccountID: string; + + /** Whether Expensify Card settlement transactions should be synchronized. */ + syncExpensifyCardSettlements: boolean; + + /** Bank account used for Expensify Card settlements. */ + settlementsBankAccountID: string; + + /** Whether travel billing settlement transactions should be synchronized. */ + syncTravelInvoicingSettlements: boolean; + + /** Bank account used for travel billing settlements. */ + travelInvoicingSettlementsBankAccountID: string; +}; + +/** + * Connection config for Digits. Credentials, the auto-sync job and settlement progress stay on the server. + */ +type DigitsConnectionsConfig = OnyxCommon.OnyxValueWithOfflineFeedback< + { + /** Whether the connection has been configured */ + isConfigured: boolean; + + /** Whether to enable a new Expense Category into Expensify */ + enableNewCategories: boolean; + + /** Coding settings */ + coding?: DigitsCoding; + + /** Export settings */ + export?: DigitsExport; + + /** Auto-sync settings */ + autoSync?: DigitsAutoSync; + + /** Sync settings */ + sync?: DigitsSync; + + /** Collection of errors coming from BE */ + errors?: OnyxCommon.Errors; + + /** Collection of form field errors */ + errorFields?: OnyxCommon.ErrorFields; + }, + DigitsCodingFieldMappingsOfflineFeedbackKey | DigitsExportOfflineFeedbackKeys | keyof DigitsAutoSync | keyof DigitsSync +>; + /** * An organization configured in Zoho Books. */ @@ -3400,6 +3613,9 @@ type Connections = { /** Campfire integration connection */ [CONST.POLICY.CONNECTIONS.NAME.CAMPFIRE]: Connection; + /** Digits integration connection */ + [CONST.POLICY.CONNECTIONS.NAME.DIGITS]: Connection; + /** Zoho Books integration connection */ [CONST.POLICY.CONNECTIONS.NAME.ZOHO_BOOKS]: Connection; @@ -4299,6 +4515,9 @@ export type { CampfireExport, CampfireAutoSync, CampfireSync, + DigitsExportDate, + DigitsVendor, + DigitsAccount, ZohoBooksExportDate, ZohoBooksVendor, ZohoBooksAccount, @@ -4318,3 +4537,10 @@ export type { * @public */ export type {ZohoBooksAutoSync, ZohoBooksCoding, ZohoBooksConnectionsConfig, ZohoBooksExport, ZohoBooksSync}; + +/** + * The Digits action module is the only importer of these config types, and Knip ignores that module until workspace screens import it. + * + * @public + */ +export type {DigitsAutoSync, DigitsCoding, DigitsConnectionsConfig, DigitsExport, DigitsSync}; diff --git a/tests/unit/CopyPolicySettingsUtilsTest.ts b/tests/unit/CopyPolicySettingsUtilsTest.ts index 9f60274cfadc..322651a25ffb 100644 --- a/tests/unit/CopyPolicySettingsUtilsTest.ts +++ b/tests/unit/CopyPolicySettingsUtilsTest.ts @@ -73,6 +73,31 @@ describe('CopyPolicySettingsUtils', () => { expect(getConnectionCompanyID(policy, CONST.POLICY.CONNECTIONS.NAME.QBD)).toBe('QBD-1'); }); + it('returns the synced company ID for Digits', () => { + // Given a Digits connection whose credentials hold a per-sign-in key, while the sync stored the company it reached + const policy = makePolicyWithConnection(CONST.POLICY.CONNECTIONS.NAME.DIGITS, { + config: {credentials: {companyID: 'sign-in-key'}}, + data: {company: {id: 'DIGITS-1', name: 'Merlin Technologies Inc.', currency: 'USD'}}, + }); + + // When the company ID is read for the copy and bulk export checks + const companyID = getConnectionCompanyID(policy, CONST.POLICY.CONNECTIONS.NAME.DIGITS); + + // Then it is the synced company, so two sign-ins to one Digits company count as the same company + expect(companyID).toBe('DIGITS-1'); + }); + + it('returns undefined for Digits before the company has synced', () => { + // Given a Digits connection that authorized but has not completed its first sync + const policy = makePolicyWithConnection(CONST.POLICY.CONNECTIONS.NAME.DIGITS, {config: {isConfigured: false}}); + + // When the company ID is read + const companyID = getConnectionCompanyID(policy, CONST.POLICY.CONNECTIONS.NAME.DIGITS); + + // Then there is no company to match on yet + expect(companyID).toBeUndefined(); + }); + it('returns undefined when the connection field is missing', () => { const policy = makePolicyWithConnection(CONST.POLICY.CONNECTIONS.NAME.QBO, {config: {}}); expect(getConnectionCompanyID(policy, CONST.POLICY.CONNECTIONS.NAME.QBO)).toBeUndefined();