Skip to content

Commit

Permalink
Merge pull request Expensify#40718 from teneeto/qbo-hoc-with-policy-c…
Browse files Browse the repository at this point in the history
…onnection-for-advanced-config

Swap from withPolicy to withPolicyConnections for advanced Quickbooks pages
  • Loading branch information
Hayata Suenaga authored Apr 25, 2024
2 parents 748cc74 + 9235dec commit 675161d
Show file tree
Hide file tree
Showing 5 changed files with 91 additions and 121 deletions.
7 changes: 3 additions & 4 deletions src/languages/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1939,10 +1939,9 @@ export default {
createEntitiesDescription:
'Expensify will automatically create a vendor in Quickbooks, if one does not exist. Expensify will also automatically create a customer when exporting invoices.',
reimbursedReports: 'Sync reimbursed reports',
reimbursedReportsDescription: 'Any time report is paid using Expensify ACH, the corresponding bill payment will be created in the Quickbooks accounts below.',
qboAccount: 'Quickbooks account',
collectionAccount: 'Invoice collection account',
collectionAccountDescription: 'Once invoices have been paid, the payment will appear in the account configured below.',
reimbursedReportsDescription: 'Any time a report is paid using Expensify ACH, the corresponding bill payment will be created in the Quickbooks account below.',
qboBillPaymentAccount: 'QuickBooks bill payment account',
qboInvoiceCollectionAccount: 'QuickBooks invoice collections account',
accountSelectDescription:
"As you've enabled sync reimbursed reports, you will need select the bank account your reimbursements are coming out of, and we'll create the payment in QuickBooks.",
invoiceAccountSelectDescription:
Expand Down
7 changes: 3 additions & 4 deletions src/languages/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1966,10 +1966,9 @@ export default {
'Expensify creará automáticamente un proveedor en Quickbooks, si no existe. Expensify también creará automáticamente un cliente al exportar facturas.',
reimbursedReports: 'Sincronizar informes reembolsados',
reimbursedReportsDescription:
'Cada vez que se pague un informe utilizando Expensify ACH, se creará el pago de la factura correspondiente en las cuentas de Quickbooks indicadas a continuación.',
qboAccount: 'Cuenta Quickbooks',
collectionAccount: 'Cuenta de cobro de facturas',
collectionAccountDescription: 'Una vez abonadas las facturas, el pago aparecerá en la cuenta configurada a continuación.',
'Cada vez que se pague un informe utilizando Expensify ACH, se creará el correspondiente pago de la factura en la cuenta de Quickbooks indicadas a continuación.',
qboBillPaymentAccount: 'Cuenta de pago de las facturas de QuickBooks',
qboInvoiceCollectionAccount: 'Cuenta de cobro de las facturas QuickBooks',
accountSelectDescription:
'Como has activado la sincronización de los informes de reembolso, tendrás que seleccionar la cuenta bancaria de la que saldrán tus reembolsos y crearemos el pago en QuickBooks.',
invoiceAccountSelectDescription:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,42 +13,33 @@ import Navigation from '@libs/Navigation/Navigation';
import AdminPolicyAccessOrNotFoundWrapper from '@pages/workspace/AdminPolicyAccessOrNotFoundWrapper';
import FeatureEnabledAccessOrNotFoundWrapper from '@pages/workspace/FeatureEnabledAccessOrNotFoundWrapper';
import PaidPolicyAccessOrNotFoundWrapper from '@pages/workspace/PaidPolicyAccessOrNotFoundWrapper';
import withPolicy from '@pages/workspace/withPolicy';
import type {WithPolicyProps} from '@pages/workspace/withPolicy';
import type {WithPolicyConnectionsProps} from '@pages/workspace/withPolicyConnections';
import withPolicyConnections from '@pages/workspace/withPolicyConnections';
import CONST from '@src/CONST';
import ROUTES from '@src/ROUTES';

type SelectorType = ListItem & {
value: string;
};

// TODO: remove once UI is approved
const DRAFT = [
{name: 'Croissant Co Payroll Account', id: 'Croissant Co Payroll Account'},
{name: 'Croissant Co Money in Clearing', id: 'Croissant Co Money in Clearing'},
{name: 'Croissant Co Debts and Loans', id: 'Croissant Co Debts and Loans'},
];

function QuickbooksAccountSelectPage({policy}: WithPolicyProps) {
function QuickbooksAccountSelectPage({policy}: WithPolicyConnectionsProps) {
const styles = useThemeStyles();
const {translate} = useLocalize();

const selectedAccount = DRAFT[0].id; // selected

const policyID = policy?.id ?? '';
const {bankAccounts, creditCards} = policy?.connections?.quickbooksOnline?.data ?? {};
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
const accountOptions = useMemo(() => DRAFT || [...(bankAccounts ?? []), ...(creditCards ?? [])], [bankAccounts, creditCards]);
const {reimbursementAccountID} = policy?.connections?.quickbooksOnline?.config ?? {};
const accountOptions = useMemo(() => [...(bankAccounts ?? []), ...(creditCards ?? [])], [bankAccounts, creditCards]);

const qboOnlineSelectorOptions = useMemo<SelectorType[]>(
() =>
accountOptions?.map(({id, name}) => ({
value: id,
text: name,
keyForList: id,
isSelected: selectedAccount === id,
isSelected: reimbursementAccountID === id,
})),
[selectedAccount, accountOptions],
[reimbursementAccountID, accountOptions],
);
const listHeaderComponent = useMemo(
() => (
Expand Down Expand Up @@ -80,7 +71,7 @@ function QuickbooksAccountSelectPage({policy}: WithPolicyProps) {
includeSafeAreaPaddingBottom={false}
testID={QuickbooksAccountSelectPage.displayName}
>
<HeaderWithBackButton title={translate('workspace.qbo.advancedConfig.qboAccount')} />
<HeaderWithBackButton title={translate('workspace.qbo.advancedConfig.qboBillPaymentAccount')} />

<SelectionList
sections={[{data: qboOnlineSelectorOptions}]}
Expand All @@ -98,4 +89,4 @@ function QuickbooksAccountSelectPage({policy}: WithPolicyProps) {

QuickbooksAccountSelectPage.displayName = 'QuickbooksAccountSelectPage';

export default withPolicy(QuickbooksAccountSelectPage);
export default withPolicyConnections(QuickbooksAccountSelectPage);
144 changes: 67 additions & 77 deletions src/pages/workspace/accounting/qbo/advanced/QuickbooksAdvancedPage.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
import React from 'react';
import React, {useMemo} from 'react';
import {View} from 'react-native';
import HeaderWithBackButton from '@components/HeaderWithBackButton';
import MenuItem from '@components/MenuItem';
import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription';
import OfflineWithFeedback from '@components/OfflineWithFeedback';
import ScreenWrapper from '@components/ScreenWrapper';
import ScrollView from '@components/ScrollView';
import SpacerView from '@components/SpacerView';
import useLocalize from '@hooks/useLocalize';
import useThemeStyles from '@hooks/useThemeStyles';
import useWaitForNavigation from '@hooks/useWaitForNavigation';
Expand All @@ -16,24 +14,62 @@ import Navigation from '@libs/Navigation/Navigation';
import AdminPolicyAccessOrNotFoundWrapper from '@pages/workspace/AdminPolicyAccessOrNotFoundWrapper';
import FeatureEnabledAccessOrNotFoundWrapper from '@pages/workspace/FeatureEnabledAccessOrNotFoundWrapper';
import PaidPolicyAccessOrNotFoundWrapper from '@pages/workspace/PaidPolicyAccessOrNotFoundWrapper';
import withPolicy from '@pages/workspace/withPolicy';
import type {WithPolicyProps} from '@pages/workspace/withPolicy';
import type {WithPolicyConnectionsProps} from '@pages/workspace/withPolicyConnections';
import withPolicyConnections from '@pages/workspace/withPolicyConnections';
import ToggleSettingOptionRow from '@pages/workspace/workflows/ToggleSettingsOptionRow';
import type {ToggleSettingOptionRowProps} from '@pages/workspace/workflows/ToggleSettingsOptionRow';
import * as Policy from '@userActions/Policy';
import CONST from '@src/CONST';
import ROUTES from '@src/ROUTES';

function QuickbooksAdvancedPage({policy}: WithPolicyProps) {
function QuickbooksAdvancedPage({policy}: WithPolicyConnectionsProps) {
const styles = useThemeStyles();
const waitForNavigate = useWaitForNavigation();
const {translate} = useLocalize();

const policyID = policy?.id ?? '';
const qboConfig = policy?.connections?.quickbooksOnline?.config;
const {autoSync, syncPeople, autoCreateVendor, pendingFields, collectionAccountID, errorFields} = qboConfig ?? {};
const {autoSync, syncPeople, autoCreateVendor, pendingFields, collectionAccountID, reimbursementAccountID, errorFields} = qboConfig ?? {};
const {bankAccounts, creditCards, otherCurrentAssetAccounts} = policy?.connections?.quickbooksOnline?.data ?? {};

const qboAccountOptions = useMemo(() => [...(bankAccounts ?? []), ...(creditCards ?? [])], [bankAccounts, creditCards]);
const invoiceAccountCollectionOptions = useMemo(() => [...(bankAccounts ?? []), ...(otherCurrentAssetAccounts ?? [])], [bankAccounts, otherCurrentAssetAccounts]);

const isSyncReimbursedSwitchOn = !!collectionAccountID;
const selectedAccount = '92345'; // TODO: use fake selected account temporarily.

const selectedQboAccountName = useMemo(() => qboAccountOptions?.find(({id}) => id === reimbursementAccountID)?.name, [qboAccountOptions, reimbursementAccountID]);
const selectedInvoiceCollectionAccountName = useMemo(
() => invoiceAccountCollectionOptions?.find(({id}) => id === collectionAccountID)?.name,
[invoiceAccountCollectionOptions, collectionAccountID],
);

const syncReimbursedSubMenuItems = () => (
<View style={[styles.mt3]}>
<OfflineWithFeedback pendingAction={pendingFields?.reimbursementAccountID}>
<MenuItemWithTopDescription
shouldShowRightIcon
title={selectedQboAccountName}
description={translate('workspace.qbo.advancedConfig.qboBillPaymentAccount')}
wrapperStyle={[styles.sectionMenuItemTopDescription]}
onPress={waitForNavigate(() => Navigation.navigate(ROUTES.WORKSPACE_ACCOUNTING_QUICKBOOKS_ONLINE_ACCOUNT_SELECTOR.getRoute(policyID)))}
error={errorFields?.reimbursementAccountID ? translate('common.genericErrorMessage') : undefined}
brickRoadIndicator={errorFields?.reimbursementAccountID ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined}
/>
</OfflineWithFeedback>

<OfflineWithFeedback pendingAction={pendingFields?.collectionAccountID}>
<MenuItemWithTopDescription
shouldShowRightIcon
title={selectedInvoiceCollectionAccountName}
description={translate('workspace.qbo.advancedConfig.qboInvoiceCollectionAccount')}
wrapperStyle={[styles.sectionMenuItemTopDescription]}
onPress={waitForNavigate(() => Navigation.navigate(ROUTES.WORKSPACE_ACCOUNTING_QUICKBOOKS_ONLINE_INVOICE_ACCOUNT_SELECTOR.getRoute(policyID)))}
error={errorFields?.collectionAccountID ? translate('common.genericErrorMessage') : undefined}
brickRoadIndicator={errorFields?.collectionAccountID ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined}
/>
</OfflineWithFeedback>
</View>
);

const qboToggleSettingItems: ToggleSettingOptionRowProps[] = [
{
Expand All @@ -47,6 +83,7 @@ function QuickbooksAdvancedPage({policy}: WithPolicyProps) {
pendingAction: pendingFields?.autoSync,
errors: ErrorUtils.getLatestErrorField(qboConfig ?? {}, CONST.QUICK_BOOKS_CONFIG.AUTO_SYNC),
onCloseError: () => Policy.clearQBOErrorField(policyID, CONST.QUICK_BOOKS_CONFIG.AUTO_SYNC),
wrapperStyle: styles.mv3,
},
{
title: translate('workspace.qbo.advancedConfig.inviteEmployees'),
Expand All @@ -56,6 +93,7 @@ function QuickbooksAdvancedPage({policy}: WithPolicyProps) {
pendingAction: pendingFields?.syncPeople,
errors: ErrorUtils.getLatestErrorField(qboConfig ?? {}, CONST.QUICK_BOOKS_CONFIG.SYNCE_PEOPLE),
onCloseError: () => Policy.clearQBOErrorField(policyID, CONST.QUICK_BOOKS_CONFIG.SYNCE_PEOPLE),
wrapperStyle: styles.mv3,
},
{
title: translate('workspace.qbo.advancedConfig.createEntities'),
Expand All @@ -65,6 +103,24 @@ function QuickbooksAdvancedPage({policy}: WithPolicyProps) {
pendingAction: pendingFields?.autoCreateVendor,
errors: ErrorUtils.getLatestErrorField(qboConfig ?? {}, CONST.QUICK_BOOKS_CONFIG.AUTO_CREATE_VENDOR),
onCloseError: () => Policy.clearQBOErrorField(policyID, CONST.QUICK_BOOKS_CONFIG.AUTO_CREATE_VENDOR),
wrapperStyle: styles.mv3,
},
{
title: translate('workspace.qbo.advancedConfig.reimbursedReports'),
subtitle: translate('workspace.qbo.advancedConfig.reimbursedReportsDescription'),
isActive: isSyncReimbursedSwitchOn,
onToggle: () =>
Connections.updatePolicyConnectionConfig(
policyID,
CONST.POLICY.CONNECTIONS.NAME.QBO,
CONST.QUICK_BOOKS_CONFIG.COLLECTION_ACCOUNT_ID,
isSyncReimbursedSwitchOn ? '' : [...qboAccountOptions, ...invoiceAccountCollectionOptions][0].id,
),
pendingAction: pendingFields?.collectionAccountID,
errors: ErrorUtils.getLatestErrorField(qboConfig ?? {}, CONST.QUICK_BOOKS_CONFIG.COLLECTION_ACCOUNT_ID),
onCloseError: () => Policy.clearQBOErrorField(policyID, CONST.QUICK_BOOKS_CONFIG.COLLECTION_ACCOUNT_ID),
subMenuItems: syncReimbursedSubMenuItems(),
wrapperStyle: styles.mv3,
},
];

Expand All @@ -91,79 +147,13 @@ function QuickbooksAdvancedPage({policy}: WithPolicyProps) {
title={item.title}
subtitle={item.subtitle}
shouldPlaceSubtitleBelowSwitch
wrapperStyle={styles.mv3}
wrapperStyle={item.wrapperStyle}
isActive={item.isActive}
onToggle={item.onToggle}
pendingAction={item.pendingAction}
subMenuItems={item.subMenuItems}
/>
))}

<View style={styles.mv3}>
<SpacerView
shouldShow
style={[styles.chatItemComposeBoxColor]}
/>
</View>

<ToggleSettingOptionRow
title={translate('workspace.qbo.advancedConfig.reimbursedReports')}
errors={ErrorUtils.getLatestErrorField(qboConfig ?? {}, CONST.QUICK_BOOKS_CONFIG.COLLECTION_ACCOUNT_ID)}
onCloseError={() => Policy.clearQBOErrorField(policyID, CONST.QUICK_BOOKS_CONFIG.COLLECTION_ACCOUNT_ID)}
subtitle={translate('workspace.qbo.advancedConfig.reimbursedReportsDescription')}
shouldPlaceSubtitleBelowSwitch
wrapperStyle={styles.mv3}
pendingAction={pendingFields?.collectionAccountID}
isActive={isSyncReimbursedSwitchOn}
onToggle={() =>
Connections.updatePolicyConnectionConfig(
policyID,
CONST.POLICY.CONNECTIONS.NAME.QBO,
CONST.QUICK_BOOKS_CONFIG.COLLECTION_ACCOUNT_ID,
isSyncReimbursedSwitchOn ? '' : selectedAccount,
)
}
/>

{!!collectionAccountID && (
<>
<OfflineWithFeedback pendingAction={pendingFields?.reimbursementAccountID}>
<MenuItemWithTopDescription
shouldShowRightIcon
title="Croissant Co Payroll Account" // TODO: set to the current selected value
description={translate('workspace.qbo.advancedConfig.qboAccount')}
wrapperStyle={[styles.sectionMenuItemTopDescription]}
onPress={waitForNavigate(() => Navigation.navigate(ROUTES.WORKSPACE_ACCOUNTING_QUICKBOOKS_ONLINE_ACCOUNT_SELECTOR.getRoute(policyID)))}
error={errorFields?.reimbursementAccountID ? translate('common.genericErrorMessage') : undefined}
brickRoadIndicator={errorFields?.reimbursementAccountID ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined}
/>
</OfflineWithFeedback>

<View style={styles.mv3}>
<SpacerView
shouldShow
style={[styles.chatItemComposeBoxColor]}
/>
</View>

<OfflineWithFeedback pendingAction={pendingFields?.collectionAccountID}>
<MenuItem
title={translate('workspace.qbo.advancedConfig.collectionAccount')}
description={translate('workspace.qbo.advancedConfig.collectionAccountDescription')}
shouldShowBasicTitle
wrapperStyle={[styles.sectionMenuItemTopDescription]}
interactive={false}
/>
<MenuItemWithTopDescription
title="Croissant Co Money in Clearing" // TODO: set to the current selected value
shouldShowRightIcon
wrapperStyle={[styles.sectionMenuItemTopDescription]}
onPress={waitForNavigate(() => Navigation.navigate(ROUTES.WORKSPACE_ACCOUNTING_QUICKBOOKS_ONLINE_INVOICE_ACCOUNT_SELECTOR.getRoute(policyID)))}
error={errorFields?.collectionAccountID ? translate('common.genericErrorMessage') : undefined}
brickRoadIndicator={errorFields?.collectionAccountID ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined}
/>
</OfflineWithFeedback>
</>
)}
</ScrollView>
</ScreenWrapper>
</FeatureEnabledAccessOrNotFoundWrapper>
Expand All @@ -174,4 +164,4 @@ function QuickbooksAdvancedPage({policy}: WithPolicyProps) {

QuickbooksAdvancedPage.displayName = 'QuickbooksAdvancedPage';

export default withPolicy(QuickbooksAdvancedPage);
export default withPolicyConnections(QuickbooksAdvancedPage);
Loading

0 comments on commit 675161d

Please sign in to comment.