diff --git a/src/CONST.ts b/src/CONST.ts index 6adcf5dfb785..3eec148a0e2e 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -1298,8 +1298,15 @@ const CONST = { XERO_CONFIG: { AUTO_SYNC: 'autoSync', SYNC: 'sync', + ENABLE_NEW_CATEGORIES: 'enableNewCategories', + EXPORT: 'export', IMPORT_CUSTOMERS: 'importCustomers', IMPORT_TAX_RATES: 'importTaxRates', + INVOICE_STATUS: { + AWAITING_PAYMENT: 'AWT_PAYMENT', + DRAFT: 'DRAFT', + AWAITING_APPROVAL: 'AWT_APPROVAL', + }, IMPORT_TRACKING_CATEGORIES: 'importTrackingCategories', MAPPINGS: 'mappings', TRACKING_CATEGORY_PREFIX: 'trackingCategory_', @@ -1319,6 +1326,12 @@ const CONST = { JOURNAL_ENTRY: 'journal_entry', }, + XERO_EXPORT_DATE: { + LAST_EXPENSE: 'LAST_EXPENSE', + REPORT_EXPORTED: 'REPORT_EXPORTED', + REPORT_SUBMITTED: 'REPORT_SUBMITTED', + }, + QUICKBOOKS_EXPORT_DATE: { LAST_EXPENSE: 'LAST_EXPENSE', REPORT_EXPORTED: 'REPORT_EXPORTED', @@ -1784,6 +1797,8 @@ const CONST = { XERO_SYNC_IMPORT_CUSTOMERS: 'xeroSyncImportCustomers', XERO_SYNC_IMPORT_BANK_ACCOUNTS: 'xeroSyncImportBankAccounts', XERO_SYNC_IMPORT_TAX_RATES: 'xeroSyncImportTaxRates', + XERO_CHECK_CONNECTION: 'xeroCheckConnection', + XERO_SYNC_TITLE: 'xeroSyncTitle', }, }, ACCESS_VARIANTS: { diff --git a/src/ROUTES.ts b/src/ROUTES.ts index 900d12977a35..0dff2992fe91 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -787,6 +787,10 @@ const ROUTES = { route: 'settings/workspaces/:policyID/accounting/xero/import', getRoute: (policyID: string) => `settings/workspaces/${policyID}/accounting/xero/import` as const, }, + POLICY_ACCOUNTING_XERO_CHART_OF_ACCOUNTS: { + route: 'settings/workspaces/:policyID/accounting/xero/import/accounts', + getRoute: (policyID: string) => `settings/workspaces/${policyID}/accounting/xero/import/accounts` as const, + }, POLICY_ACCOUNTING_XERO_ORGANIZATION: { route: 'settings/workspaces/:policyID/accounting/xero/organization/:currentOrganizationID', getRoute: (policyID: string, currentOrganizationID: string) => `settings/workspaces/${policyID}/accounting/xero/organization/${currentOrganizationID}` as const, @@ -815,6 +819,18 @@ const ROUTES = { route: 'settings/workspaces/:policyID/accounting/xero/export', getRoute: (policyID: string) => `settings/workspaces/${policyID}/accounting/xero/export` as const, }, + POLICY_ACCOUNTING_XERO_PREFERRED_EXPORTER_SELECT: { + route: '/settings/workspaces/:policyID/connections/xero/export/preferred-exporter/select', + getRoute: (policyID: string) => `/settings/workspaces/${policyID}/connections/xero/export/preferred-exporter/select` as const, + }, + POLICY_ACCOUNTING_XERO_EXPORT_PURCHASE_BILL_DATE_SELECT: { + route: 'settings/workspaces/:policyID/accounting/xero/export/purchase-bill-date-select', + getRoute: (policyID: string) => `settings/workspaces/${policyID}/accounting/xero/export/purchase-bill-date-select` as const, + }, + POLICY_ACCOUNTING_XERO_EXPORT_BANK_ACCOUNT_SELECT: { + route: 'settings/workspaces/:policyID/accounting/xero/export/bank-account-select', + getRoute: (policyID: string) => `settings/workspaces/${policyID}/accounting/xero/export/bank-account-select` as const, + }, POLICY_ACCOUNTING_XERO_ADVANCED: { route: 'settings/workspaces/:policyID/accounting/xero/advanced', getRoute: (policyID: string) => `settings/workspaces/${policyID}/accounting/xero/advanced` as const, @@ -823,6 +839,10 @@ const ROUTES = { route: 'settings/workspaces/:policyID/accounting/xero/advanced/invoice-account-selector', getRoute: (policyID: string) => `settings/workspaces/${policyID}/accounting/xero/advanced/invoice-account-selector` as const, }, + POLICY_ACCOUNTING_XERO_BILL_PAYMENT_ACCOUNT_SELECTOR: { + route: 'settings/workspaces/:policyID/accounting/xero/advanced/bill-payment-account-selector', + getRoute: (policyID: string) => `settings/workspaces/${policyID}/accounting/xero/advanced/bill-payment-account-selector` as const, + }, POLICY_ACCOUNTING_QUICKBOOKS_ONLINE_IMPORT: { route: 'settings/workspaces/:policyID/accounting/quickbooks-online/import', getRoute: (policyID: string) => `settings/workspaces/${policyID}/accounting/quickbooks-online/import` as const, diff --git a/src/SCREENS.ts b/src/SCREENS.ts index c640ff1ddb7c..45a7bf1f255d 100644 --- a/src/SCREENS.ts +++ b/src/SCREENS.ts @@ -245,14 +245,19 @@ const SCREENS = { QUICKBOOKS_ONLINE_INVOICE_ACCOUNT_SELECTOR: 'Policy_Accounting_Quickbooks_Online_Invoice_Account_Selector', XERO_IMPORT: 'Policy_Accounting_Xero_Import', XERO_ORGANIZATION: 'Policy_Accounting_Xero_Customers', + XERO_CHART_OF_ACCOUNTS: 'Policy_Accounting_Xero_Import_Chart_Of_Accounts', XERO_CUSTOMER: 'Policy_Acounting_Xero_Import_Customer', XERO_TAXES: 'Policy_Accounting_Xero_Taxes', XERO_TRACKING_CATEGORIES: 'Policy_Accounting_Xero_Tracking_Categories', XERO_MAP_COST_CENTERS: 'Policy_Accounting_Xero_Map_Cost_Centers', XERO_MAP_REGION: 'Policy_Accounting_Xero_Map_Region', XERO_EXPORT: 'Policy_Accounting_Xero_Export', + XERO_EXPORT_PURCHASE_BILL_DATE_SELECT: 'Policy_Accounting_Xero_Export_Purchase_Bill_Date_Select', XERO_ADVANCED: 'Policy_Accounting_Xero_Advanced', XERO_INVOICE_ACCOUNT_SELECTOR: 'Policy_Accounting_Xero_Invoice_Account_Selector', + XERO_EXPORT_PREFERRED_EXPORTER_SELECT: 'Workspace_Accounting_Xero_Export_Preferred_Exporter_Select', + XERO_BILL_PAYMENT_ACCOUNT_SELECTOR: 'Policy_Accounting_Xero_Bill_Payment_Account_Selector', + XERO_EXPORT_BANK_ACCOUNT_SELECT: 'Policy_Accounting_Xero_Export_Bank_Account_Select', }, INITIAL: 'Workspace_Initial', PROFILE: 'Workspace_Profile', diff --git a/src/components/CollapsibleSection/index.tsx b/src/components/CollapsibleSection/index.tsx index f984906595c2..d339f005e3d3 100644 --- a/src/components/CollapsibleSection/index.tsx +++ b/src/components/CollapsibleSection/index.tsx @@ -18,6 +18,9 @@ type CollapsibleSectionProps = ChildrenProps & { /** Style of title of the collapsible section */ titleStyle?: StyleProp; + /** Style for the text */ + textStyle?: StyleProp; + /** Style for the wrapper view */ wrapperStyle?: StyleProp; @@ -25,7 +28,7 @@ type CollapsibleSectionProps = ChildrenProps & { shouldShowSectionBorder?: boolean; }; -function CollapsibleSection({title, children, titleStyle, wrapperStyle, shouldShowSectionBorder}: CollapsibleSectionProps) { +function CollapsibleSection({title, children, titleStyle, textStyle, wrapperStyle, shouldShowSectionBorder}: CollapsibleSectionProps) { const theme = useTheme(); const styles = useThemeStyles(); const [isExpanded, setIsExpanded] = useState(false); @@ -50,7 +53,7 @@ function CollapsibleSection({title, children, titleStyle, wrapperStyle, shouldSh pressDimmingValue={0.2} > {title} diff --git a/src/components/ConnectionLayout.tsx b/src/components/ConnectionLayout.tsx index a91dea9bfc92..8abe0e5759fc 100644 --- a/src/components/ConnectionLayout.tsx +++ b/src/components/ConnectionLayout.tsx @@ -20,15 +20,15 @@ type ConnectionLayoutProps = { /** Header title for the connection */ headerTitle: TranslationPaths; + /** The subtitle to show in the header */ + headerSubtitle?: string; + /** React nodes that will be shown */ children?: React.ReactNode; /** Title of the connection component */ title?: TranslationPaths; - /** Subtitle of the connection */ - subtitle?: TranslationPaths; - /** The current policyID */ policyID: string; @@ -44,22 +44,18 @@ type ConnectionLayoutProps = { /** Style of the title text */ titleStyle?: StyleProp | undefined; - /** Style of the subtitle text */ - subTitleStyle?: StyleProp | undefined; - /** Whether to use ScrollView or not */ shouldUseScrollView?: boolean; }; -type ConnectionLayoutContentProps = Pick; +type ConnectionLayoutContentProps = Pick; -function ConnectionLayoutContent({title, titleStyle, subtitle, subTitleStyle, children}: ConnectionLayoutContentProps) { +function ConnectionLayoutContent({title, titleStyle, children}: ConnectionLayoutContentProps) { const {translate} = useLocalize(); const styles = useThemeStyles(); return ( <> {title && {translate(title)}} - {subtitle && {translate(subtitle)}} {children} ); @@ -70,13 +66,12 @@ function ConnectionLayout({ headerTitle, children, title, - subtitle, + headerSubtitle, policyID, accessVariants, featureName, contentContainerStyle, titleStyle, - subTitleStyle, shouldUseScrollView = true, }: ConnectionLayoutProps) { const {translate} = useLocalize(); @@ -85,14 +80,12 @@ function ConnectionLayout({ () => ( {children} ), - [title, subtitle, titleStyle, subTitleStyle, children], + [title, titleStyle, children], ); return ( @@ -108,6 +101,7 @@ function ConnectionLayout({ > Navigation.goBack()} /> {shouldUseScrollView ? ( diff --git a/src/languages/en.ts b/src/languages/en.ts index 261a2be3090c..c83dbabeefe8 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -1890,6 +1890,7 @@ export default { travel: 'Travel', members: 'Members', accounting: 'Accounting', + displayedAs: 'Displayed as', plan: 'Plan', profile: 'Profile', bankAccount: 'Bank account', @@ -1920,7 +1921,6 @@ export default { classes: 'Classes', locations: 'Locations', customers: 'Customers/Projects', - displayedAs: 'Displayed as', accountsDescription: 'When connected to Quickbooks Online, chart of accounts are always imported to Expensify as categories.', accountsSwitchTitle: 'Below you can choose to have any new account imported as an enabled or disabled category by default.', accountsSwitchDescription: 'Enabled categories are available for members to select when creating their expenses.', @@ -2028,6 +2028,9 @@ export default { organization: 'Xero organization', organizationDescription: 'Select the organization in Xero you are importing data from.', importDescription: 'Choose which coding configurations are imported from Xero to Expensify.', + accountsDescription: 'When connected to Xero, chart of accounts are always imported to Expensify as categories.', + accountsSwitchTitle: 'Below you can choose to have any new account imported as an enabled or disabled category by default.', + accountsSwitchDescription: 'Enabled categories are available for members to select when creating their expenses.', trackingCategories: 'Tracking categories', trackingCategoriesDescription: 'Choose whether to import tracking categories and see where they are displayed.', mapXeroCostCentersTo: 'Map Xero cost centers to', @@ -2050,6 +2053,7 @@ export default { 'Each exported expense posts as a bank transaction to the Xero bank account you select below, and transaction dates will match the dates on your bank statement.', bankTransactions: 'Bank transactions', xeroBankAccount: 'Xero bank account', + xeroBankAccountDescription: 'Select the bank account expenses will be posted to as bank transactions.', preferredExporter: 'Preferred exporter', exportExpenses: 'Export out-of-pocket expenses as', exportExpensesDescription: 'Reports will export as a purchase bill, using the date and status you select below.', @@ -2067,7 +2071,36 @@ export default { xeroBillPaymentAccount: 'Xero Bill Payment Account', xeroInvoiceCollectionAccount: 'Xero Invoice Collections Account', invoiceAccountSelectorDescription: "As you've enabled exporting invoices from Expensify to Xero, this is the account the invoice will appear against once marked as paid.", + xeroBillPaymentAccountDescription: + "As you've enabled sync reimbursed reports, you will need to select the bank account your reimbursements are coming out of, and we'll create the payment in Xero.", }, + exportDate: { + label: 'Export date', + description: 'Use this date when exporting reports to Xero.', + values: { + [CONST.QUICKBOOKS_EXPORT_DATE.LAST_EXPENSE]: { + label: 'Date of last expense', + description: 'The date of the most recent expense on the report', + }, + [CONST.QUICKBOOKS_EXPORT_DATE.REPORT_EXPORTED]: { + label: 'Export date', + description: 'The date the report was exported to Xero', + }, + [CONST.QUICKBOOKS_EXPORT_DATE.REPORT_SUBMITTED]: { + label: 'Submitted date', + description: 'The date the report was submitted for approval', + }, + }, + }, + invoiceStatus: { + values: { + [CONST.XERO_CONFIG.INVOICE_STATUS.AWAITING_PAYMENT]: 'Authorised', + [CONST.XERO_CONFIG.INVOICE_STATUS.DRAFT]: 'Draft', + [CONST.XERO_CONFIG.INVOICE_STATUS.AWAITING_APPROVAL]: 'Submitted', + }, + }, + exportPreferredExporterNote: 'This can be any workspace admin, but must be a domain admin if you set different export accounts for individual company cards in domain settings.', + exportPreferredExporterSubNote: 'Once set, the preferred exporter will see reports for export in their account.', }, type: { free: 'Free', @@ -2367,6 +2400,28 @@ export default { return 'Updating people list'; case 'quickbooksOnlineSyncApplyClassesLocations': return 'Updating report fields'; + case 'xeroSyncImportChartOfAccounts': + return 'Syncing chart of accounts'; + case 'xeroSyncImportCategories': + return 'Syncing categories'; + case 'xeroSyncImportCustomers': + return 'Syncing customers'; + case 'xeroSyncXeroReimbursedReports': + return 'Marking Expensify reports as reimbursed'; + case 'xeroSyncExpensifyReimbursedReports': + return 'Marking Xero bills and invoices as paid'; + case 'xeroSyncImportTrackingCategories': + return 'Syncing tracking categories'; + case 'xeroSyncImportBankAccounts': + return 'Syncing bank accounts'; + case 'xeroSyncImportTaxRates': + return 'Syncing tax rates'; + case 'xeroCheckConnection': + return 'Checking Xero connection'; + case 'xeroSyncTitle': + return 'Synchronizing Xero data'; + case 'xeroSyncStep': + return 'Loading data'; default: { return `Translation missing for stage: ${stage}`; } diff --git a/src/languages/es.ts b/src/languages/es.ts index 36de1e2e38c9..8226253f72c1 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -1917,6 +1917,7 @@ export default { plan: 'Plan', profile: 'Perfil', bankAccount: 'Cuenta bancaria', + displayedAs: 'Mostrado como', connectBankAccount: 'Conectar cuenta bancaria', testTransactions: 'Transacciones de prueba', issueAndManageCards: 'Emitir y gestionar tarjetas', @@ -1944,7 +1945,6 @@ export default { classes: 'Clases', locations: 'Lugares', customers: 'Clientes/Proyectos', - displayedAs: 'Mostrado como', accountsDescription: 'Cuando estás conectado a Quickbooks Online, los planes de cuentas siempre se importan a Expensify como categorías.', accountsSwitchTitle: 'Elige abajo si las categorías importadas serán activadas o desactivadas por defecto.', accountsSwitchDescription: 'Las categorías activas estarán disponibles para ser escogidas cuando se crea un gasto.', @@ -2060,6 +2060,9 @@ export default { organization: 'Organización Xero', organizationDescription: 'Seleccione la organización en Xero desde la que está importando los datos.', importDescription: 'Elija qué configuraciones de codificación se importan de Xero a Expensify.', + accountsDescription: 'Cuando estás conectado a Xero, los planes de cuentas siempre se importan a Expensify como categorías.', + accountsSwitchTitle: 'Elige abajo si las categorías importadas serán activadas o desactivadas por defecto.', + accountsSwitchDescription: 'Las categorías activas estarán disponibles para ser escogidas cuando se crea un gasto.', trackingCategories: 'Categorías de seguimiento', trackingCategoriesDescription: 'Elige si deseas importar categorías de seguimiento y ver dónde se muestran.', mapXeroCostCentersTo: 'Asignar centros de coste de Xero a', @@ -2083,6 +2086,7 @@ export default { 'Cada gasto exportado se contabiliza como una transacción bancaria en la cuenta bancaria de Xero que selecciones a continuación. Las fechas de las transacciones coincidirán con las fechas de el extracto bancario.', bankTransactions: 'Transacciones bancarias', xeroBankAccount: 'Cuenta bancaria de Xero', + xeroBankAccountDescription: 'Selecciona la cuenta bancaria en la que aparecerán los gastos como transacciones bancarias.', preferredExporter: 'Exportador preferido', exportExpenses: 'Exportar gastos por cuenta propia como', exportExpensesDescription: 'Los informes se exportarán como una factura de compra utilizando la fecha y el estado que seleccione a continuación', @@ -2102,7 +2106,37 @@ export default { xeroInvoiceCollectionAccount: 'Cuenta de cobro de las facturas Xero', invoiceAccountSelectorDescription: 'Como ha activado la exportación de facturas de Expensify a Xero, esta es la cuenta en la que aparecerá la factura una vez marcada como pagada.', + xeroBillPaymentAccountDescription: + 'Como has activado la sincronización de los informes reembolsados, tendrás que seleccionar la cuenta bancaria de la que se producen estos reembolsos y crearemos el pago en Xero.', + }, + exportDate: { + label: 'Fecha de exportación', + description: 'Usa esta fecha al exportar informe a Xero.', + values: { + [CONST.XERO_EXPORT_DATE.LAST_EXPENSE]: { + label: 'Fecha del último gasto', + description: 'Fecha del gasto mas reciente en el informe', + }, + [CONST.XERO_EXPORT_DATE.REPORT_EXPORTED]: { + label: 'Fecha de exportación', + description: 'Fecha de exportación del informe a Xero', + }, + [CONST.XERO_EXPORT_DATE.REPORT_SUBMITTED]: { + label: 'Fecha de envío', + description: 'Fecha en la que el informe se envió para su aprobación', + }, + }, }, + invoiceStatus: { + values: { + [CONST.XERO_CONFIG.INVOICE_STATUS.AWAITING_PAYMENT]: 'Autorizado', + [CONST.XERO_CONFIG.INVOICE_STATUS.DRAFT]: 'Borrador', + [CONST.XERO_CONFIG.INVOICE_STATUS.AWAITING_APPROVAL]: 'Enviado', + }, + }, + exportPreferredExporterNote: + 'Puede ser cualquier administrador del espacio de trabajo, pero debe ser un administrador de dominio si configura diferentes cuentas de exportación para tarjetas de empresa individuales en la configuración del dominio.', + exportPreferredExporterSubNote: 'Una vez configurado, el exportador preferido verá los informes para exportar en su cuenta.', }, type: { free: 'Gratis', @@ -2370,6 +2404,28 @@ export default { return 'Actualizando empleados'; case 'quickbooksOnlineSyncApplyClassesLocations': return 'Actualizando clases'; + case 'xeroSyncImportChartOfAccounts': + return 'Sincronizando plan de cuentas'; + case 'xeroSyncImportCategories': + return 'Sincronizando categorias'; + case 'xeroSyncImportCustomers': + return 'Sincronizando clientes'; + case 'xeroSyncXeroReimbursedReports': + return 'Marcar los informes de Expensify como reembolsados'; + case 'xeroSyncExpensifyReimbursedReports': + return 'Marcar facturas y recibos de Xero como pagados'; + case 'xeroSyncImportTrackingCategories': + return 'Sincronizando categorías de seguimiento'; + case 'xeroSyncImportBankAccounts': + return 'Sincronizando cuentas bancarias'; + case 'xeroSyncImportTaxRates': + return 'Sincronizando las tasas de impuesto'; + case 'xeroCheckConnection': + return 'Comprobando la conexión a Xero'; + case 'xeroSyncTitle': + return 'Sincronizando los datos de Xero'; + case 'xeroSyncStep': + return 'Cargando datos'; default: { return `Translation missing for stage: ${stage}`; } diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx index 4a1712297c72..abfd7a9029bd 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx @@ -307,6 +307,7 @@ const SettingsModalStackNavigator = createModalStackNavigator require('../../../../pages/workspace/accounting/xero/XeroImportPage').default as React.ComponentType, [SCREENS.WORKSPACE.ACCOUNTING.XERO_ORGANIZATION]: () => require('../../../../pages/workspace/accounting/xero/XeroOrganizationConfigurationPage').default as React.ComponentType, + [SCREENS.WORKSPACE.ACCOUNTING.XERO_CHART_OF_ACCOUNTS]: () => require('../../../../pages/workspace/accounting/xero/import/XeroChartOfAccountsPage').default as React.ComponentType, [SCREENS.WORKSPACE.ACCOUNTING.XERO_CUSTOMER]: () => require('../../../../pages/workspace/accounting/xero/import/XeroCustomerConfigurationPage').default as React.ComponentType, [SCREENS.WORKSPACE.ACCOUNTING.XERO_TAXES]: () => require('../../../../pages/workspace/accounting/xero/XeroTaxesConfigurationPage').default as React.ComponentType, [SCREENS.WORKSPACE.ACCOUNTING.XERO_TRACKING_CATEGORIES]: () => @@ -314,9 +315,17 @@ const SettingsModalStackNavigator = createModalStackNavigator require('../../../../pages/workspace/accounting/xero/XeroMapCostCentersToConfigurationPage').default as React.ComponentType, [SCREENS.WORKSPACE.ACCOUNTING.XERO_MAP_REGION]: () => require('../../../../pages/workspace/accounting/xero/XeroMapRegionsToConfigurationPage').default as React.ComponentType, [SCREENS.WORKSPACE.ACCOUNTING.XERO_EXPORT]: () => require('../../../../pages/workspace/accounting/xero/export/XeroExportConfigurationPage').default as React.ComponentType, + [SCREENS.WORKSPACE.ACCOUNTING.XERO_EXPORT_PURCHASE_BILL_DATE_SELECT]: () => + require('../../../../pages/workspace/accounting/xero/export/XeroPurchaseBillDateSelectPage').default as React.ComponentType, + [SCREENS.WORKSPACE.ACCOUNTING.XERO_EXPORT_BANK_ACCOUNT_SELECT]: () => + require('../../../../pages/workspace/accounting/xero/export/XeroBankAccountSelectPage').default as React.ComponentType, [SCREENS.WORKSPACE.ACCOUNTING.XERO_ADVANCED]: () => require('../../../../pages/workspace/accounting/xero/advanced/XeroAdvancedPage').default as React.ComponentType, [SCREENS.WORKSPACE.ACCOUNTING.XERO_INVOICE_ACCOUNT_SELECTOR]: () => require('../../../../pages/workspace/accounting/xero/advanced/XeroInvoiceAccountSelectorPage').default as React.ComponentType, + [SCREENS.WORKSPACE.ACCOUNTING.XERO_EXPORT_PREFERRED_EXPORTER_SELECT]: () => + require('../../../../pages/workspace/accounting/xero/export/XeroPreferredExporterSelectPage').default as React.ComponentType, + [SCREENS.WORKSPACE.ACCOUNTING.XERO_BILL_PAYMENT_ACCOUNT_SELECTOR]: () => + require('../../../../pages/workspace/accounting/xero/advanced/XeroBillPaymentAccountSelectorPage').default as React.ComponentType, [SCREENS.WORKSPACE.WORKFLOWS_AUTO_REPORTING_FREQUENCY]: () => require('../../../../pages/workspace/workflows/WorkspaceAutoReportingFrequencyPage').default as React.ComponentType, [SCREENS.WORKSPACE.WORKFLOWS_AUTO_REPORTING_MONTHLY_OFFSET]: () => require('../../../../pages/workspace/workflows/WorkspaceAutoReportingMonthlyOffsetPage').default as React.ComponentType, diff --git a/src/libs/Navigation/linkingConfig/FULL_SCREEN_TO_RHP_MAPPING.ts b/src/libs/Navigation/linkingConfig/FULL_SCREEN_TO_RHP_MAPPING.ts index d7c92328ee04..d7808497916f 100755 --- a/src/libs/Navigation/linkingConfig/FULL_SCREEN_TO_RHP_MAPPING.ts +++ b/src/libs/Navigation/linkingConfig/FULL_SCREEN_TO_RHP_MAPPING.ts @@ -41,6 +41,7 @@ const FULL_SCREEN_TO_RHP_MAPPING: Partial> = { SCREENS.WORKSPACE.ACCOUNTING.QUICKBOOKS_ONLINE_ACCOUNT_SELECTOR, SCREENS.WORKSPACE.ACCOUNTING.QUICKBOOKS_ONLINE_INVOICE_ACCOUNT_SELECTOR, SCREENS.WORKSPACE.ACCOUNTING.XERO_IMPORT, + SCREENS.WORKSPACE.ACCOUNTING.XERO_CHART_OF_ACCOUNTS, SCREENS.WORKSPACE.ACCOUNTING.XERO_ORGANIZATION, SCREENS.WORKSPACE.ACCOUNTING.XERO_CUSTOMER, SCREENS.WORKSPACE.ACCOUNTING.XERO_TAXES, @@ -48,8 +49,12 @@ const FULL_SCREEN_TO_RHP_MAPPING: Partial> = { SCREENS.WORKSPACE.ACCOUNTING.XERO_MAP_COST_CENTERS, SCREENS.WORKSPACE.ACCOUNTING.XERO_MAP_REGION, SCREENS.WORKSPACE.ACCOUNTING.XERO_EXPORT, + SCREENS.WORKSPACE.ACCOUNTING.XERO_EXPORT_PURCHASE_BILL_DATE_SELECT, SCREENS.WORKSPACE.ACCOUNTING.XERO_ADVANCED, SCREENS.WORKSPACE.ACCOUNTING.XERO_INVOICE_ACCOUNT_SELECTOR, + SCREENS.WORKSPACE.ACCOUNTING.XERO_EXPORT_PREFERRED_EXPORTER_SELECT, + SCREENS.WORKSPACE.ACCOUNTING.XERO_BILL_PAYMENT_ACCOUNT_SELECTOR, + SCREENS.WORKSPACE.ACCOUNTING.XERO_EXPORT_BANK_ACCOUNT_SELECT, ], [SCREENS.WORKSPACE.TAXES]: [ SCREENS.WORKSPACE.TAXES_SETTINGS, diff --git a/src/libs/Navigation/linkingConfig/config.ts b/src/libs/Navigation/linkingConfig/config.ts index 9178618d7a80..3b4bfca471ad 100644 --- a/src/libs/Navigation/linkingConfig/config.ts +++ b/src/libs/Navigation/linkingConfig/config.ts @@ -334,6 +334,7 @@ const config: LinkingOptions['config'] = { path: ROUTES.WORKSPACE_ACCOUNTING_QUICKBOOKS_ONLINE_INVOICE_ACCOUNT_SELECTOR.route, }, [SCREENS.WORKSPACE.ACCOUNTING.XERO_IMPORT]: {path: ROUTES.POLICY_ACCOUNTING_XERO_IMPORT.route}, + [SCREENS.WORKSPACE.ACCOUNTING.XERO_CHART_OF_ACCOUNTS]: {path: ROUTES.POLICY_ACCOUNTING_XERO_CHART_OF_ACCOUNTS.route}, [SCREENS.WORKSPACE.ACCOUNTING.XERO_ORGANIZATION]: {path: ROUTES.POLICY_ACCOUNTING_XERO_ORGANIZATION.route}, [SCREENS.WORKSPACE.ACCOUNTING.XERO_TRACKING_CATEGORIES]: {path: ROUTES.POLICY_ACCOUNTING_XERO_TRACKING_CATEGORIES.route}, [SCREENS.WORKSPACE.ACCOUNTING.XERO_MAP_COST_CENTERS]: {path: ROUTES.POLICY_ACCOUNTING_XERO_TRACKING_CATEGORIES_MAP_COST_CENTERS.route}, @@ -341,8 +342,12 @@ const config: LinkingOptions['config'] = { [SCREENS.WORKSPACE.ACCOUNTING.XERO_CUSTOMER]: {path: ROUTES.POLICY_ACCOUNTING_XERO_CUSTOMER.route}, [SCREENS.WORKSPACE.ACCOUNTING.XERO_TAXES]: {path: ROUTES.POLICY_ACCOUNTING_XERO_TAXES.route}, [SCREENS.WORKSPACE.ACCOUNTING.XERO_EXPORT]: {path: ROUTES.POLICY_ACCOUNTING_XERO_EXPORT.route}, + [SCREENS.WORKSPACE.ACCOUNTING.XERO_EXPORT_PURCHASE_BILL_DATE_SELECT]: {path: ROUTES.POLICY_ACCOUNTING_XERO_EXPORT_PURCHASE_BILL_DATE_SELECT.route}, + [SCREENS.WORKSPACE.ACCOUNTING.XERO_EXPORT_BANK_ACCOUNT_SELECT]: {path: ROUTES.POLICY_ACCOUNTING_XERO_EXPORT_BANK_ACCOUNT_SELECT.route}, [SCREENS.WORKSPACE.ACCOUNTING.XERO_ADVANCED]: {path: ROUTES.POLICY_ACCOUNTING_XERO_ADVANCED.route}, [SCREENS.WORKSPACE.ACCOUNTING.XERO_INVOICE_ACCOUNT_SELECTOR]: {path: ROUTES.POLICY_ACCOUNTING_XERO_INVOICE_SELECTOR.route}, + [SCREENS.WORKSPACE.ACCOUNTING.XERO_EXPORT_PREFERRED_EXPORTER_SELECT]: {path: ROUTES.POLICY_ACCOUNTING_XERO_PREFERRED_EXPORTER_SELECT.route}, + [SCREENS.WORKSPACE.ACCOUNTING.XERO_BILL_PAYMENT_ACCOUNT_SELECTOR]: {path: ROUTES.POLICY_ACCOUNTING_XERO_BILL_PAYMENT_ACCOUNT_SELECTOR.route}, [SCREENS.WORKSPACE.DESCRIPTION]: { path: ROUTES.WORKSPACE_PROFILE_DESCRIPTION.route, }, diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index a697c49de39b..071c2840a9cd 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -322,6 +322,9 @@ type SettingsNavigatorParamList = { [SCREENS.WORKSPACE.ACCOUNTING.XERO_IMPORT]: { policyID: string; }; + [SCREENS.WORKSPACE.ACCOUNTING.XERO_CHART_OF_ACCOUNTS]: { + policyID: string; + }; [SCREENS.WORKSPACE.ACCOUNTING.XERO_CUSTOMER]: { policyID: string; }; @@ -344,12 +347,21 @@ type SettingsNavigatorParamList = { [SCREENS.WORKSPACE.ACCOUNTING.XERO_EXPORT]: { policyID: string; }; + [SCREENS.WORKSPACE.ACCOUNTING.XERO_EXPORT_PURCHASE_BILL_DATE_SELECT]: { + policyID: string; + }; [SCREENS.WORKSPACE.ACCOUNTING.XERO_ADVANCED]: { policyID: string; }; [SCREENS.WORKSPACE.ACCOUNTING.XERO_INVOICE_ACCOUNT_SELECTOR]: { policyID: string; }; + [SCREENS.WORKSPACE.ACCOUNTING.XERO_EXPORT_PREFERRED_EXPORTER_SELECT]: { + policyID: string; + }; + [SCREENS.WORKSPACE.ACCOUNTING.XERO_BILL_PAYMENT_ACCOUNT_SELECTOR]: { + policyID: string; + }; [SCREENS.GET_ASSISTANCE]: { backTo: Routes; }; diff --git a/src/libs/PolicyUtils.ts b/src/libs/PolicyUtils.ts index 371e38a09bd0..c234a61bdbd9 100644 --- a/src/libs/PolicyUtils.ts +++ b/src/libs/PolicyUtils.ts @@ -413,6 +413,10 @@ function findCurrentXeroOrganization(tenants: Tenant[] | undefined, organization return tenants?.find((tenant) => tenant.id === organizationID); } +function getCurrentXeroOrganizationName(policy: Policy | undefined): string | undefined { + return findCurrentXeroOrganization(getXeroTenants(policy), policy?.connections?.xero?.config?.tenantID)?.name; +} + export { getActivePolicies, hasAccountingConnections, @@ -461,6 +465,7 @@ export { canSendInvoice, getXeroTenants, findCurrentXeroOrganization, + getCurrentXeroOrganizationName, }; export type {MemberEmailsToAccountIDs}; diff --git a/src/pages/workspace/accounting/PolicyAccountingPage.tsx b/src/pages/workspace/accounting/PolicyAccountingPage.tsx index fbd5b669fa62..c6a63c3f61cc 100644 --- a/src/pages/workspace/accounting/PolicyAccountingPage.tsx +++ b/src/pages/workspace/accounting/PolicyAccountingPage.tsx @@ -25,7 +25,7 @@ import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; import {removePolicyConnection} from '@libs/actions/connections'; import {syncConnection} from '@libs/actions/connections/QuickBooksOnline'; -import {findCurrentXeroOrganization, getXeroTenants} from '@libs/PolicyUtils'; +import {findCurrentXeroOrganization, getCurrentXeroOrganizationName, getXeroTenants} from '@libs/PolicyUtils'; import Navigation from '@navigation/Navigation'; import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper'; import type {WithPolicyProps} from '@pages/workspace/withPolicy'; @@ -120,8 +120,8 @@ function PolicyAccountingPage({policy, connectionSyncProgress}: PolicyAccounting const policyConnectedToXero = connectedIntegration === CONST.POLICY.CONNECTIONS.NAME.XERO; const tenants = useMemo(() => getXeroTenants(policy), [policy]); - const currentXeroOrganization = findCurrentXeroOrganization(tenants, policy?.connections?.xero?.config?.tenantID); + const currentXeroOrganizationName = useMemo(() => getCurrentXeroOrganizationName(policy), [policy]); const overflowMenu: ThreeDotsMenuProps['menuItems'] = useMemo( () => [ @@ -200,7 +200,7 @@ function PolicyAccountingPage({policy, connectionSyncProgress}: PolicyAccounting { description: translate('workspace.xero.organization'), iconRight: Expensicons.ArrowRight, - title: currentXeroOrganization?.name, + title: currentXeroOrganizationName, wrapperStyle: [styles.sectionMenuItemTopDescription], titleStyle: styles.fontWeightNormal, shouldShowRightIcon: tenants.length > 1, @@ -247,6 +247,7 @@ function PolicyAccountingPage({policy, connectionSyncProgress}: PolicyAccounting connectedIntegration, connectionSyncProgress?.stageInProgress, currentXeroOrganization, + currentXeroOrganizationName, tenants, isSyncInProgress, overflowMenu, @@ -341,8 +342,9 @@ function PolicyAccountingPage({policy, connectionSyncProgress}: PolicyAccounting {otherIntegrationsItems && ( qboOnlineSelectorOptions?.find((mode) => mode.isSelected)?.keyForList, [qboOnlineSelectorOptions]); - const updateMode = useCallback( + const updateAccount = useCallback( ({value}: SelectorType) => { Connections.updatePolicyConnectionConfig(policyID, CONST.POLICY.CONNECTIONS.NAME.QBO, CONST.QUICK_BOOKS_CONFIG.COLLECTION_ACCOUNT_ID, value); Navigation.goBack(ROUTES.WORKSPACE_ACCOUNTING_QUICKBOOKS_ONLINE_ADVANCED.getRoute(policyID)); @@ -75,7 +75,7 @@ function QuickbooksInvoiceAccountSelectPage({policy}: WithPolicyConnectionsProps sections={[{data: qboOnlineSelectorOptions}]} ListItem={RadioListItem} headerContent={listHeaderComponent} - onSelectRow={updateMode} + onSelectRow={updateAccount} initiallyFocusedOptionKey={initiallyFocusedOptionKey} /> diff --git a/src/pages/workspace/accounting/qbo/import/QuickbooksChartOfAccountsPage.tsx b/src/pages/workspace/accounting/qbo/import/QuickbooksChartOfAccountsPage.tsx index 9fea1f323bd1..d433977e50d5 100644 --- a/src/pages/workspace/accounting/qbo/import/QuickbooksChartOfAccountsPage.tsx +++ b/src/pages/workspace/accounting/qbo/import/QuickbooksChartOfAccountsPage.tsx @@ -27,6 +27,7 @@ function QuickbooksChartOfAccountsPage({policy}: WithPolicyProps) { accessVariants={[CONST.POLICY.ACCESS_VARIANTS.ADMIN]} policyID={policyID} featureName={CONST.POLICY.MORE_FEATURES.ARE_CONNECTIONS_ENABLED} + contentContainerStyle={[styles.pb2, styles.ph5]} > @@ -44,7 +45,7 @@ function QuickbooksChartOfAccountsPage({policy}: WithPolicyProps) { {translate('workspace.qbo.accountsSwitchTitle')} diff --git a/src/pages/workspace/accounting/qbo/import/QuickbooksClassesPage.tsx b/src/pages/workspace/accounting/qbo/import/QuickbooksClassesPage.tsx index 45b7533648bb..4d9e2ff63dde 100644 --- a/src/pages/workspace/accounting/qbo/import/QuickbooksClassesPage.tsx +++ b/src/pages/workspace/accounting/qbo/import/QuickbooksClassesPage.tsx @@ -64,7 +64,7 @@ function QuickbooksClassesPage({policy}: WithPolicyProps) { diff --git a/src/pages/workspace/accounting/qbo/import/QuickbooksCustomersPage.tsx b/src/pages/workspace/accounting/qbo/import/QuickbooksCustomersPage.tsx index 3c38e542d455..f93adcbe3cff 100644 --- a/src/pages/workspace/accounting/qbo/import/QuickbooksCustomersPage.tsx +++ b/src/pages/workspace/accounting/qbo/import/QuickbooksCustomersPage.tsx @@ -63,7 +63,7 @@ function QuickbooksCustomersPage({policy}: WithPolicyProps) { diff --git a/src/pages/workspace/accounting/qbo/import/QuickbooksLocationsPage.tsx b/src/pages/workspace/accounting/qbo/import/QuickbooksLocationsPage.tsx index 307847809d62..1183b2a96c49 100644 --- a/src/pages/workspace/accounting/qbo/import/QuickbooksLocationsPage.tsx +++ b/src/pages/workspace/accounting/qbo/import/QuickbooksLocationsPage.tsx @@ -64,7 +64,7 @@ function QuickbooksLocationsPage({policy}: WithPolicyProps) { diff --git a/src/pages/workspace/accounting/xero/XeroImportPage.tsx b/src/pages/workspace/accounting/xero/XeroImportPage.tsx index 0dff36520922..28f53eb2f13e 100644 --- a/src/pages/workspace/accounting/xero/XeroImportPage.tsx +++ b/src/pages/workspace/accounting/xero/XeroImportPage.tsx @@ -8,7 +8,7 @@ import Text from '@components/Text'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; import Navigation from '@libs/Navigation/Navigation'; -import {getXeroTenants} from '@libs/PolicyUtils'; +import {getCurrentXeroOrganizationName} from '@libs/PolicyUtils'; import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper'; import withPolicy from '@pages/workspace/withPolicy'; import type {WithPolicyProps} from '@pages/workspace/withPolicy'; @@ -22,16 +22,15 @@ function XeroImportPage({policy}: WithPolicyProps) { const policyID = policy?.id ?? ''; const {importCustomers, importTaxRates, importTrackingCategories, pendingFields} = policy?.connections?.xero?.config ?? {}; - const tenants = useMemo(() => getXeroTenants(policy ?? undefined), [policy]); - const currentXeroOrganization = tenants.find((tenant) => tenant.id === policy?.connections?.xero?.config.tenantID); + const currentXeroOrganizationName = useMemo(() => getCurrentXeroOrganizationName(policy ?? undefined), [policy]); const sections = useMemo( () => [ { description: translate('workspace.accounting.accounts'), - action: () => {}, + action: () => Navigation.navigate(ROUTES.POLICY_ACCOUNTING_XERO_CHART_OF_ACCOUNTS.getRoute(policyID)), hasError: !!policy?.errors?.enableNewCategories, - title: translate('workspace.accounting.imported'), + title: translate('workspace.accounting.importAsCategory'), pendingAction: pendingFields?.enableNewCategories, }, { @@ -88,7 +87,7 @@ function XeroImportPage({policy}: WithPolicyProps) { > {translate('workspace.xero.importDescription')} diff --git a/src/pages/workspace/accounting/xero/advanced/XeroAdvancedPage.tsx b/src/pages/workspace/accounting/xero/advanced/XeroAdvancedPage.tsx index 154f5f8a97f9..2c9074b03c24 100644 --- a/src/pages/workspace/accounting/xero/advanced/XeroAdvancedPage.tsx +++ b/src/pages/workspace/accounting/xero/advanced/XeroAdvancedPage.tsx @@ -7,6 +7,7 @@ import useThemeStyles from '@hooks/useThemeStyles'; import * as Connections from '@libs/actions/connections'; import * as ErrorUtils from '@libs/ErrorUtils'; import Navigation from '@libs/Navigation/Navigation'; +import {getCurrentXeroOrganizationName} from '@libs/PolicyUtils'; import type {WithPolicyConnectionsProps} from '@pages/workspace/withPolicyConnections'; import withPolicyConnections from '@pages/workspace/withPolicyConnections'; import ToggleSettingOptionRow from '@pages/workspace/workflows/ToggleSettingsOptionRow'; @@ -22,18 +23,26 @@ function XeroAdvancedPage({policy}: WithPolicyConnectionsProps) { const xeroConfig = policy?.connections?.xero?.config; const {autoSync, pendingFields, sync} = xeroConfig ?? {}; const {bankAccounts} = policy?.connections?.xero?.data ?? {}; - const {invoiceCollectionsAccountID} = sync ?? {}; + const {invoiceCollectionsAccountID, reimbursementAccountID} = sync ?? {}; - const selectedBankAccountName = useMemo(() => { - const selectedAccount = (bankAccounts ?? []).find((bank) => bank.id === invoiceCollectionsAccountID); + const getSelectedAccountName = useMemo( + () => (accountID: string) => { + const selectedAccount = (bankAccounts ?? []).find((bank) => bank.id === accountID); + return selectedAccount?.name ?? ''; + }, + [bankAccounts], + ); + + const selectedBankAccountName = getSelectedAccountName(invoiceCollectionsAccountID ?? ''); + const selectedBillPaymentAccountName = getSelectedAccountName(reimbursementAccountID ?? ''); - return selectedAccount?.name ?? ''; - }, [bankAccounts, invoiceCollectionsAccountID]); + const currentXeroOrganizationName = useMemo(() => getCurrentXeroOrganizationName(policy ?? undefined), [policy]); return ( Policy.clearXeroErrorField(policyID, CONST.XERO_CONFIG.AUTO_SYNC)} /> - - {}} - /> - {}} + onPress={() => Navigation.navigate(ROUTES.POLICY_ACCOUNTING_XERO_BILL_PAYMENT_ACCOUNT_SELECTOR.getRoute(policyID))} /> diff --git a/src/pages/workspace/accounting/xero/advanced/XeroBillPaymentAccountSelectorPage.tsx b/src/pages/workspace/accounting/xero/advanced/XeroBillPaymentAccountSelectorPage.tsx new file mode 100644 index 000000000000..11ac106f516c --- /dev/null +++ b/src/pages/workspace/accounting/xero/advanced/XeroBillPaymentAccountSelectorPage.tsx @@ -0,0 +1,77 @@ +import React, {useCallback, useMemo} from 'react'; +import {View} from 'react-native'; +import RadioListItem from '@components/SelectionList/RadioListItem'; +import type {SelectorType} from '@components/SelectionScreen'; +import SelectionScreen from '@components/SelectionScreen'; +import Text from '@components/Text'; +import useLocalize from '@hooks/useLocalize'; +import useThemeStyles from '@hooks/useThemeStyles'; +import * as Connections from '@libs/actions/connections'; +import Navigation from '@libs/Navigation/Navigation'; +import type {WithPolicyConnectionsProps} from '@pages/workspace/withPolicyConnections'; +import withPolicyConnections from '@pages/workspace/withPolicyConnections'; +import CONST from '@src/CONST'; +import ROUTES from '@src/ROUTES'; + +function XeroBillPaymentAccountSelectorPage({policy}: WithPolicyConnectionsProps) { + const styles = useThemeStyles(); + const {translate} = useLocalize(); + + const policyID = policy?.id ?? ''; + const {bankAccounts} = policy?.connections?.xero?.data ?? {}; + + const {reimbursementAccountID, syncReimbursedReports} = policy?.connections?.xero?.config.sync ?? {}; + + const xeroSelectorOptions = useMemo( + () => + (bankAccounts ?? []).map(({id, name}) => ({ + value: id, + text: name, + keyForList: id, + isSelected: reimbursementAccountID === id, + })), + [reimbursementAccountID, bankAccounts], + ); + + const listHeaderComponent = useMemo( + () => ( + + {translate('workspace.xero.advancedConfig.xeroBillPaymentAccountDescription')} + + ), + [translate, styles.pb2, styles.ph5, styles.pb5, styles.textNormal], + ); + + const initiallyFocusedOptionKey = useMemo(() => xeroSelectorOptions?.find((mode) => mode.isSelected)?.keyForList, [xeroSelectorOptions]); + + const updateAccount = useCallback( + ({value}: SelectorType) => { + Connections.updatePolicyConnectionConfig(policyID, CONST.POLICY.CONNECTIONS.NAME.XERO, CONST.XERO_CONFIG.SYNC, { + reimbursementAccountID: value, + }); + Navigation.goBack(ROUTES.POLICY_ACCOUNTING_XERO_ADVANCED.getRoute(policyID)); + }, + [policyID], + ); + + return ( + Navigation.goBack(ROUTES.POLICY_ACCOUNTING_XERO_ADVANCED.getRoute(policyID))} + title="workspace.xero.advancedConfig.xeroBillPaymentAccount" + /> + ); +} + +XeroBillPaymentAccountSelectorPage.displayName = 'XeroBillPaymentAccountSelectorPage'; + +export default withPolicyConnections(XeroBillPaymentAccountSelectorPage); diff --git a/src/pages/workspace/accounting/xero/advanced/XeroInvoiceAccountSelectorPage.tsx b/src/pages/workspace/accounting/xero/advanced/XeroInvoiceAccountSelectorPage.tsx index 460eca1a153b..ba7749fef4f2 100644 --- a/src/pages/workspace/accounting/xero/advanced/XeroInvoiceAccountSelectorPage.tsx +++ b/src/pages/workspace/accounting/xero/advanced/XeroInvoiceAccountSelectorPage.tsx @@ -44,7 +44,7 @@ function XeroInvoiceAccountSelectorPage({policy}: WithPolicyConnectionsProps) { const initiallyFocusedOptionKey = useMemo(() => xeroSelectorOptions?.find((mode) => mode.isSelected)?.keyForList, [xeroSelectorOptions]); - const updateMode = useCallback( + const updateAccount = useCallback( ({value}: SelectorType) => { Connections.updatePolicyConnectionConfig(policyID, CONST.POLICY.CONNECTIONS.NAME.XERO, CONST.XERO_CONFIG.SYNC, { invoiceCollectionsAccountID: value, @@ -63,7 +63,7 @@ function XeroInvoiceAccountSelectorPage({policy}: WithPolicyConnectionsProps) { sections={[{data: xeroSelectorOptions}]} listItem={RadioListItem} shouldBeBlocked={!syncReimbursedReports} - onSelectRow={updateMode} + onSelectRow={updateAccount} initiallyFocusedOptionKey={initiallyFocusedOptionKey} headerContent={listHeaderComponent} onBackButtonPress={() => Navigation.goBack(ROUTES.POLICY_ACCOUNTING_XERO_ADVANCED.getRoute(policyID))} diff --git a/src/pages/workspace/accounting/xero/export/XeroBankAccountSelectPage.tsx b/src/pages/workspace/accounting/xero/export/XeroBankAccountSelectPage.tsx new file mode 100644 index 000000000000..897ed0b37d78 --- /dev/null +++ b/src/pages/workspace/accounting/xero/export/XeroBankAccountSelectPage.tsx @@ -0,0 +1,78 @@ +import React, {useCallback, useMemo} from 'react'; +import {View} from 'react-native'; +import RadioListItem from '@components/SelectionList/RadioListItem'; +import type {SelectorType} from '@components/SelectionScreen'; +import SelectionScreen from '@components/SelectionScreen'; +import Text from '@components/Text'; +import useLocalize from '@hooks/useLocalize'; +import useThemeStyles from '@hooks/useThemeStyles'; +import * as Connections from '@libs/actions/connections'; +import Navigation from '@libs/Navigation/Navigation'; +import type {WithPolicyConnectionsProps} from '@pages/workspace/withPolicyConnections'; +import withPolicyConnections from '@pages/workspace/withPolicyConnections'; +import CONST from '@src/CONST'; +import ROUTES from '@src/ROUTES'; + +function XeroBankAccountSelectPage({policy}: WithPolicyConnectionsProps) { + const styles = useThemeStyles(); + const {translate} = useLocalize(); + + const policyID = policy?.id ?? ''; + const {bankAccounts} = policy?.connections?.xero?.data ?? {}; + + const {nonReimbursableAccount: nonReimbursableAccountID} = policy?.connections?.xero?.config.export ?? {}; + + const xeroSelectorOptions = useMemo( + () => + (bankAccounts ?? []).map(({id, name}) => ({ + value: id, + text: name, + keyForList: id, + isSelected: nonReimbursableAccountID === id, + })), + [nonReimbursableAccountID, bankAccounts], + ); + + const listHeaderComponent = useMemo( + () => ( + + {translate('workspace.xero.xeroBankAccountDescription')} + + ), + [translate, styles.pb2, styles.ph5, styles.pb5, styles.textNormal], + ); + + const initiallyFocusedOptionKey = useMemo(() => xeroSelectorOptions?.find((mode) => mode.isSelected)?.keyForList, [xeroSelectorOptions]); + + const updateBankAccount = useCallback( + ({value}: SelectorType) => { + if (initiallyFocusedOptionKey !== value) { + Connections.updatePolicyConnectionConfig(policyID, CONST.POLICY.CONNECTIONS.NAME.XERO, CONST.XERO_CONFIG.EXPORT, { + nonReimbursableAccount: value, + }); + } + Navigation.goBack(ROUTES.POLICY_ACCOUNTING_XERO_EXPORT.getRoute(policyID)); + }, + [policyID, initiallyFocusedOptionKey], + ); + + return ( + Navigation.goBack(ROUTES.POLICY_ACCOUNTING_XERO_EXPORT.getRoute(policyID))} + title="workspace.xero.xeroBankAccount" + /> + ); +} + +XeroBankAccountSelectPage.displayName = 'XeroBankAccountSelectPage'; + +export default withPolicyConnections(XeroBankAccountSelectPage); diff --git a/src/pages/workspace/accounting/xero/export/XeroExportConfigurationPage.tsx b/src/pages/workspace/accounting/xero/export/XeroExportConfigurationPage.tsx index f78e7abd69e5..fd0213ed0f89 100644 --- a/src/pages/workspace/accounting/xero/export/XeroExportConfigurationPage.tsx +++ b/src/pages/workspace/accounting/xero/export/XeroExportConfigurationPage.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, {useMemo} from 'react'; import ConnectionLayout from '@components/ConnectionLayout'; import type {MenuItemProps} from '@components/MenuItem'; import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription'; @@ -6,9 +6,12 @@ import OfflineWithFeedback from '@components/OfflineWithFeedback'; import type {OfflineWithFeedbackProps} from '@components/OfflineWithFeedback'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; +import Navigation from '@libs/Navigation/Navigation'; +import {getCurrentXeroOrganizationName} from '@libs/PolicyUtils'; import type {WithPolicyConnectionsProps} from '@pages/workspace/withPolicyConnections'; import withPolicyConnections from '@pages/workspace/withPolicyConnections'; import CONST from '@src/CONST'; +import ROUTES from '@src/ROUTES'; type MenuItem = MenuItemProps & {pendingAction?: OfflineWithFeedbackProps['pendingAction']}; @@ -19,10 +22,21 @@ function XeroExportConfigurationPage({policy}: WithPolicyConnectionsProps) { const policyOwner = policy?.owner ?? ''; const {export: exportConfiguration, errorFields, pendingFields} = policy?.connections?.xero?.config ?? {}; + + const {bankAccounts} = policy?.connections?.xero?.data ?? {}; + const selectedBankAccountName = useMemo(() => { + const selectedAccount = (bankAccounts ?? []).find((bank) => bank.id === exportConfiguration?.nonReimbursableAccount); + return selectedAccount?.name ?? ''; + }, [bankAccounts, exportConfiguration?.nonReimbursableAccount]); + + const currentXeroOrganizationName = useMemo(() => getCurrentXeroOrganizationName(policy ?? undefined), [policy]); + const menuItems: MenuItem[] = [ { description: translate('workspace.xero.preferredExporter'), - onPress: () => {}, + onPress: () => { + Navigation.navigate(ROUTES.POLICY_ACCOUNTING_XERO_PREFERRED_EXPORTER_SELECT.getRoute(policyID)); + }, brickRoadIndicator: errorFields?.exporter ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined, title: exportConfiguration?.exporter ?? policyOwner, pendingAction: pendingFields?.export, @@ -37,15 +51,16 @@ function XeroExportConfigurationPage({policy}: WithPolicyConnectionsProps) { }, { description: translate('workspace.xero.purchaseBillDate'), + onPress: () => Navigation.navigate(ROUTES.POLICY_ACCOUNTING_XERO_EXPORT_PURCHASE_BILL_DATE_SELECT.getRoute(policyID)), brickRoadIndicator: errorFields?.billDate ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined, - title: exportConfiguration?.billDate, + title: exportConfiguration?.billDate ? translate(`workspace.xero.exportDate.values.${exportConfiguration.billDate}.label`) : undefined, pendingAction: pendingFields?.export, errorText: errorFields?.billDate ? translate('common.genericErrorMessage') : undefined, }, { description: translate('workspace.xero.advancedConfig.purchaseBillStatusTitle'), onPress: () => {}, - title: exportConfiguration?.billStatus?.purchase, + title: exportConfiguration?.billStatus?.purchase ? translate(`workspace.xero.invoiceStatus.values.${exportConfiguration.billStatus.purchase}`) : undefined, pendingAction: pendingFields?.export, errorText: errorFields?.purchase ? translate('common.genericErrorMessage') : undefined, }, @@ -65,11 +80,11 @@ function XeroExportConfigurationPage({policy}: WithPolicyConnectionsProps) { }, { description: translate('workspace.xero.xeroBankAccount'), - onPress: () => {}, + onPress: () => Navigation.navigate(ROUTES.POLICY_ACCOUNTING_XERO_EXPORT_BANK_ACCOUNT_SELECT.getRoute(policyID)), brickRoadIndicator: errorFields?.nonReimbursableAccount ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined, - title: undefined, + title: selectedBankAccountName, pendingAction: pendingFields?.export, - errorText: undefined, + errorText: errorFields?.nonReimbursableAccount ? translate('common.genericErrorMessage') : undefined, }, ]; @@ -77,6 +92,7 @@ function XeroExportConfigurationPage({policy}: WithPolicyConnectionsProps) { { + if (!isEmpty(policyOwner) && isEmpty(exporters)) { + return [ + { + value: policyOwner, + text: policyOwner, + keyForList: policyOwner, + isSelected: true, + }, + ]; + } + return exporters?.reduce((vendors, vendor) => { + if (vendor.email) { + vendors.push({ + value: vendor.email, + text: vendor.email, + keyForList: vendor.email, + isSelected: exportConfiguration?.exporter === vendor.email, + }); + } + return vendors; + }, []); + }, [exportConfiguration, exporters, policyOwner]); + + const selectExporter = useCallback( + (row: CardListItem) => { + if (row.value !== exportConfiguration?.exporter) { + Connections.updatePolicyConnectionConfig(policyID, CONST.POLICY.CONNECTIONS.NAME.XERO, CONST.XERO_CONFIG.EXPORT, {exporter: row.value}); + } + Navigation.goBack(ROUTES.POLICY_ACCOUNTING_XERO_EXPORT.getRoute(policyID)); + }, + [policyID, exportConfiguration], + ); + + const headerContent = useMemo( + () => ( + + {translate('workspace.xero.exportPreferredExporterNote')} + {translate('workspace.xero.exportPreferredExporterSubNote')} + + ), + [translate, styles.pb2, styles.ph5, styles.pb5, styles.textNormal], + ); + + return ( + mode.isSelected)?.keyForList} + onBackButtonPress={() => Navigation.goBack(ROUTES.POLICY_ACCOUNTING_XERO_EXPORT.getRoute(policyID))} + title="workspace.xero.preferredExporter" + /> + ); +} + +XeroPreferredExporterSelectPage.displayName = 'XeroPreferredExporterSelectPage'; + +export default withPolicyConnections(XeroPreferredExporterSelectPage); diff --git a/src/pages/workspace/accounting/xero/export/XeroPurchaseBillDateSelectPage.tsx b/src/pages/workspace/accounting/xero/export/XeroPurchaseBillDateSelectPage.tsx new file mode 100644 index 000000000000..2060e48fe987 --- /dev/null +++ b/src/pages/workspace/accounting/xero/export/XeroPurchaseBillDateSelectPage.tsx @@ -0,0 +1,73 @@ +import React, {useCallback, useMemo} from 'react'; +import {View} from 'react-native'; +import type {ValueOf} from 'type-fest'; +import RadioListItem from '@components/SelectionList/RadioListItem'; +import type {ListItem} from '@components/SelectionList/types'; +import SelectionScreen from '@components/SelectionScreen'; +import type {SelectorType} from '@components/SelectionScreen'; +import Text from '@components/Text'; +import useLocalize from '@hooks/useLocalize'; +import useThemeStyles from '@hooks/useThemeStyles'; +import * as Connections from '@libs/actions/connections'; +import Navigation from '@navigation/Navigation'; +import type {WithPolicyConnectionsProps} from '@pages/workspace/withPolicyConnections'; +import withPolicyConnections from '@pages/workspace/withPolicyConnections'; +import CONST from '@src/CONST'; +import ROUTES from '@src/ROUTES'; + +type MenuListItem = ListItem & { + value: ValueOf; +}; + +function XeroPurchaseBillDateSelectPage({policy}: WithPolicyConnectionsProps) { + const {translate} = useLocalize(); + const policyID = policy?.id ?? ''; + const styles = useThemeStyles(); + const {billDate} = policy?.connections?.xero?.config?.export ?? {}; + const data: MenuListItem[] = Object.values(CONST.XERO_EXPORT_DATE).map((dateType) => ({ + value: dateType, + text: translate(`workspace.xero.exportDate.values.${dateType}.label`), + alternateText: translate(`workspace.xero.exportDate.values.${dateType}.description`), + keyForList: dateType, + isSelected: billDate === dateType, + })); + + const headerContent = useMemo( + () => ( + + {translate('workspace.xero.exportDate.description')} + + ), + [translate, styles.pb5, styles.ph5], + ); + + const selectExportDate = useCallback( + (row: MenuListItem) => { + if (row.value !== billDate) { + Connections.updatePolicyConnectionConfig(policyID, CONST.POLICY.CONNECTIONS.NAME.XERO, CONST.XERO_CONFIG.EXPORT, {billDate: row.value}); + } + Navigation.goBack(ROUTES.POLICY_ACCOUNTING_XERO_EXPORT_PURCHASE_BILL_DATE_SELECT.getRoute(policyID)); + }, + [billDate, policyID], + ); + + return ( + selectExportDate(selection as MenuListItem)} + initiallyFocusedOptionKey={data.find((mode) => mode.isSelected)?.keyForList} + policyID={policyID} + accessVariants={[CONST.POLICY.ACCESS_VARIANTS.ADMIN]} + featureName={CONST.POLICY.MORE_FEATURES.ARE_CONNECTIONS_ENABLED} + onBackButtonPress={() => Navigation.goBack(ROUTES.POLICY_ACCOUNTING_XERO_EXPORT.getRoute(policyID))} + /> + ); +} + +XeroPurchaseBillDateSelectPage.displayName = 'XeroPurchaseBillDateSelectPage'; + +export default withPolicyConnections(XeroPurchaseBillDateSelectPage); diff --git a/src/pages/workspace/accounting/xero/import/XeroChartOfAccountsPage.tsx b/src/pages/workspace/accounting/xero/import/XeroChartOfAccountsPage.tsx new file mode 100644 index 000000000000..7492864c9abb --- /dev/null +++ b/src/pages/workspace/accounting/xero/import/XeroChartOfAccountsPage.tsx @@ -0,0 +1,71 @@ +import React from 'react'; +import {View} from 'react-native'; +import ConnectionLayout from '@components/ConnectionLayout'; +import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription'; +import Switch from '@components/Switch'; +import Text from '@components/Text'; +import useLocalize from '@hooks/useLocalize'; +import useThemeStyles from '@hooks/useThemeStyles'; +import * as Connections from '@libs/actions/connections'; +import * as ErrorUtils from '@libs/ErrorUtils'; +import type {WithPolicyProps} from '@pages/workspace/withPolicy'; +import withPolicyConnections from '@pages/workspace/withPolicyConnections'; +import ToggleSettingOptionRow from '@pages/workspace/workflows/ToggleSettingsOptionRow'; +import variables from '@styles/variables'; +import * as Policy from '@userActions/Policy'; +import CONST from '@src/CONST'; + +function XeroChartOfAccountsPage({policy}: WithPolicyProps) { + const {translate} = useLocalize(); + const styles = useThemeStyles(); + const policyID = policy?.id ?? ''; + const xeroConfig = policy?.connections?.xero?.config; + const {enableNewCategories, pendingFields} = xeroConfig ?? {}; + + return ( + + + + {translate('workspace.accounting.import')} + + + {}} + /> + + + + {translate('workspace.xero.accountsSwitchTitle')} + Connections.updatePolicyConnectionConfig(policyID, CONST.POLICY.CONNECTIONS.NAME.XERO, CONST.XERO_CONFIG.ENABLE_NEW_CATEGORIES, !enableNewCategories)} + pendingAction={pendingFields?.enableNewCategories} + errors={ErrorUtils.getLatestErrorField(xeroConfig ?? {}, CONST.XERO_CONFIG.ENABLE_NEW_CATEGORIES)} + onCloseError={() => Policy.clearXeroErrorField(policyID, CONST.XERO_CONFIG.ENABLE_NEW_CATEGORIES)} + /> + + ); +} + +XeroChartOfAccountsPage.displayName = 'XeroChartOfAccountsPage'; + +export default withPolicyConnections(XeroChartOfAccountsPage); diff --git a/src/pages/workspace/accounting/xero/import/XeroCustomerConfigurationPage.tsx b/src/pages/workspace/accounting/xero/import/XeroCustomerConfigurationPage.tsx index 1d1cf11f9791..560185d7f9ce 100644 --- a/src/pages/workspace/accounting/xero/import/XeroCustomerConfigurationPage.tsx +++ b/src/pages/workspace/accounting/xero/import/XeroCustomerConfigurationPage.tsx @@ -51,7 +51,7 @@ function XeroCustomerConfigurationPage({policy}: WithPolicyProps) {