Skip to content

Commit

Permalink
refactor screens for using qbd
Browse files Browse the repository at this point in the history
  • Loading branch information
ZhenjaHorbach committed Oct 4, 2024
1 parent aac8d0f commit 74d3252
Show file tree
Hide file tree
Showing 10 changed files with 369 additions and 70 deletions.
9 changes: 9 additions & 0 deletions src/CONST.ts
Original file line number Diff line number Diff line change
Expand Up @@ -480,6 +480,7 @@ const CONST = {
NEW_DOT_COPILOT: 'newDotCopilot',
WORKSPACE_RULES: 'workspaceRules',
COMBINED_TRACK_SUBMIT: 'combinedTrackSubmit',
NEW_DOT_QBD: 'quickbooksDesktopOnNewDot',
},
BUTTON_STATES: {
DEFAULT: 'default',
Expand Down Expand Up @@ -1462,6 +1463,12 @@ const CONST = {
},
QUICKBOOKS_ONLINE: 'quickbooksOnline',

QUICKBOOKS_DESKTOP_CONFIG: {
MARK_CHECKS_TO_BE_PRINTED: 'markChecksToBePrinted',
REIMBURSABLE_EXPENSES_ACCOUNT: 'reimbursableExpensesAccount',
REIMBURSABLE_EXPENSES_EXPORT_DESTINATION: 'reimbursableExpensesExportDestination',
},

QUICKBOOKS_CONFIG: {
ENABLE_NEW_CATEGORIES: 'enableNewCategories',
SYNC_CLASSES: 'syncClasses',
Expand Down Expand Up @@ -2300,12 +2307,14 @@ const CONST = {
XERO: 'xero',
NETSUITE: 'netsuite',
SAGE_INTACCT: 'intacct',
QBD: 'quickbooksDesktop',
},
ROUTE: {
QBO: 'quickbooks-online',
XERO: 'xero',
NETSUITE: 'netsuite',
SAGE_INTACCT: 'sage-intacct',
QBD: 'quickbooks-desktop',
},
NAME_USER_FRIENDLY: {
netsuite: 'NetSuite',
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
type UpdateQuickbooksDesktopGenericTypeParams = {
policyID: string;
settingValue: string;
idempotencyKey: string;
};

export default UpdateQuickbooksDesktopGenericTypeParams;
2 changes: 2 additions & 0 deletions src/libs/API/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,8 @@ const WRITE_COMMANDS = {
UPDATE_QUICKBOOKS_ONLINE_REIMBURSEMENT_ACCOUNT_ID: 'UpdateQuickbooksOnlineReimbursementAccountID',
UPDATE_QUICKBOOKS_ONLINE_EXPORT: 'UpdateQuickbooksOnlineExport',
UPDATE_MANY_POLICY_CONNECTION_CONFIGS: 'UpdateManyPolicyConnectionConfigurations',
UPDATE_QUICKBOOKS_DESKTOP_MARK_CHECKS_TO_BE_PRINTED: 'UpdateQuickbooksDesktopMarkChecksToBePrinted',
UPDATE_QUICKBOOKS_DESKTOP_REIMBURSABLE_EXPENSES_ACCOUNT: 'UpdateQuickbooksDesktopReimbursableExpensesAccount',
REMOVE_POLICY_CONNECTION: 'RemovePolicyConnection',
SET_POLICY_TAXES_ENABLED: 'SetPolicyTaxesEnabled',
DELETE_POLICY_TAXES: 'DeletePolicyTaxes',
Expand Down
5 changes: 5 additions & 0 deletions src/libs/Permissions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@ function canUseCombinedTrackSubmit(betas: OnyxEntry<Beta[]>): boolean {
return !!betas?.includes(CONST.BETAS.COMBINED_TRACK_SUBMIT);
}

function canUseNewDotQBD(betas: OnyxEntry<Beta[]>): boolean {
return !!betas?.includes(CONST.BETAS.NEW_DOT_QBD) || canUseAllBetas(betas);
}

/**
* New Search Router is under construction and for now should be displayed only in dev to allow developers to work on it.
* We are not using BETA for this feature, as betas are heavier to cleanup,
Expand Down Expand Up @@ -81,4 +85,5 @@ export default {
canUseWorkspaceRules,
canUseCombinedTrackSubmit,
canUseNewSearchRouter,
canUseNewDotQBD,
};
5 changes: 5 additions & 0 deletions src/libs/actions/Policy/Policy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -611,6 +611,10 @@ function clearQBOErrorField(policyID: string, fieldName: string) {
Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, {connections: {quickbooksOnline: {config: {errorFields: {[fieldName]: null}}}}});
}

function clearQBDErrorField(policyID: string, fieldName: string) {
Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, {connections: {quickbooksDesktop: {config: {errorFields: {[fieldName]: null}}}}});
}

function clearXeroErrorField(policyID: string, fieldName: string) {
Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, {connections: {xero: {config: {errorFields: {[fieldName]: null}}}}});
}
Expand Down Expand Up @@ -4816,6 +4820,7 @@ export {
clearAllPolicies,
enablePolicyRules,
setPolicyDefaultReportTitle,
clearQBDErrorField,
setPolicyPreventMemberCreatedTitle,
setPolicyPreventSelfApproval,
setPolicyAutomaticApprovalLimit,
Expand Down
184 changes: 184 additions & 0 deletions src/libs/actions/connections/QuickbooksDesktop.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
import type {OnyxUpdate} from 'react-native-onyx';
import Onyx from 'react-native-onyx';
import * as API from '@libs/API';
import type UpdateQuickbooksDesktopGenericTypeParams from '@libs/API/parameters/UpdateQuickbooksDesktopGenericTypeParams';
import {WRITE_COMMANDS} from '@libs/API/types';
import * as ErrorUtils from '@libs/ErrorUtils';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import type {Connections} from '@src/types/onyx/Policy';

function buildOnyxDataForMultipleQuickbooksConfigurations<TConfigUpdate extends Partial<Connections['quickbooksDesktop']['config']>>(
policyID: string,
configUpdate: TConfigUpdate,
configCurrentData: TConfigUpdate,
) {
const optimisticData: OnyxUpdate[] = [
{
onyxMethod: Onyx.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`,
value: {
connections: {
[CONST.POLICY.CONNECTIONS.NAME.QBD]: {
config: {
...configUpdate,
pendingFields: Object.fromEntries(Object.keys(configUpdate).map((settingName) => [settingName, CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE])),
errorFields: Object.fromEntries(Object.keys(configUpdate).map((settingName) => [settingName, null])),
},
},
},
},
},
];

const failureData: OnyxUpdate[] = [
{
onyxMethod: Onyx.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`,
value: {
connections: {
[CONST.POLICY.CONNECTIONS.NAME.QBD]: {
config: {
...configCurrentData,
pendingFields: Object.fromEntries(Object.keys(configUpdate).map((settingName) => [settingName, null])),
errorFields: Object.fromEntries(
Object.keys(configUpdate).map((settingName) => [settingName, ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('common.genericErrorMessage')]),
),
},
},
},
},
},
];

const successData: OnyxUpdate[] = [
{
onyxMethod: Onyx.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`,
value: {
connections: {
[CONST.POLICY.CONNECTIONS.NAME.QBD]: {
config: {
pendingFields: Object.fromEntries(Object.keys(configUpdate).map((settingName) => [settingName, null])),
errorFields: Object.fromEntries(Object.keys(configUpdate).map((settingName) => [settingName, null])),
},
},
},
},
},
];
return {
optimisticData,
failureData,
successData,
};
}

function buildOnyxDataForQuickbooksConfiguration<TSettingName extends keyof Connections['quickbooksDesktop']['config']>(
policyID: string,
settingName: TSettingName,
settingValue: Partial<Connections['quickbooksDesktop']['config'][TSettingName]>,
oldSettingValue?: Partial<Connections['quickbooksDesktop']['config'][TSettingName]>,
) {
const optimisticData: OnyxUpdate[] = [
{
onyxMethod: Onyx.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`,
value: {
connections: {
[CONST.POLICY.CONNECTIONS.NAME.QBD]: {
config: {
[settingName]: settingValue ?? null,
pendingFields: {
[settingName]: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE,
},
errorFields: {
[settingName]: null,
},
},
},
},
},
},
];

const failureData: OnyxUpdate[] = [
{
onyxMethod: Onyx.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`,
value: {
connections: {
[CONST.POLICY.CONNECTIONS.NAME.QBD]: {
config: {
[settingName]: oldSettingValue ?? null,
pendingFields: {
[settingName]: null,
},
errorFields: {
[settingName]: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('common.genericErrorMessage'),
},
},
},
},
},
},
];

const successData: OnyxUpdate[] = [
{
onyxMethod: Onyx.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`,
value: {
connections: {
[CONST.POLICY.CONNECTIONS.NAME.QBD]: {
config: {
[settingName]: settingValue ?? null,
pendingFields: {
[settingName]: null,
},
errorFields: {
[settingName]: null,
},
},
},
},
},
},
];
return {
optimisticData,
failureData,
successData,
};
}

function updateQuickbooksDesktopMarkChecksToBePrinted<TSettingValue extends Connections['quickbooksDesktop']['config']['markChecksToBePrinted']>(
policyID: string,
settingValue: TSettingValue,
) {
const onyxData = buildOnyxDataForQuickbooksConfiguration(policyID, CONST.QUICKBOOKS_DESKTOP_CONFIG.MARK_CHECKS_TO_BE_PRINTED, settingValue, !settingValue);

const parameters: UpdateQuickbooksDesktopGenericTypeParams = {
policyID,
settingValue: JSON.stringify(settingValue),
idempotencyKey: String(CONST.QUICKBOOKS_CONFIG.AUTO_SYNC),
};
API.write(WRITE_COMMANDS.UPDATE_QUICKBOOKS_DESKTOP_MARK_CHECKS_TO_BE_PRINTED, parameters, onyxData);
}

function updateQuickbooksDesktopReimbursableExpensesAccount<TSettingValue extends Connections['quickbooksDesktop']['config']['reimbursableExpensesAccount']>(
policyID: string,
settingValue: TSettingValue,
oldSettingValue: TSettingValue,
) {
const onyxData = buildOnyxDataForQuickbooksConfiguration(policyID, CONST.QUICKBOOKS_DESKTOP_CONFIG.REIMBURSABLE_EXPENSES_ACCOUNT, settingValue, oldSettingValue);

const parameters: UpdateQuickbooksDesktopGenericTypeParams = {
policyID,
settingValue: JSON.stringify(settingValue),
idempotencyKey: String(CONST.QUICKBOOKS_DESKTOP_CONFIG.REIMBURSABLE_EXPENSES_ACCOUNT),
};
API.write(WRITE_COMMANDS.UPDATE_QUICKBOOKS_DESKTOP_REIMBURSABLE_EXPENSES_ACCOUNT, parameters, onyxData);
}

export {updateQuickbooksDesktopMarkChecksToBePrinted, updateQuickbooksDesktopReimbursableExpensesAccount, buildOnyxDataForMultipleQuickbooksConfigurations};
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,16 @@ import type {ListItem} from '@components/SelectionList/types';
import SelectionScreen from '@components/SelectionScreen';
import Text from '@components/Text';
import useLocalize from '@hooks/useLocalize';
import usePermissions from '@hooks/usePermissions';
import useThemeStyles from '@hooks/useThemeStyles';
import * as QuickbooksOnline from '@libs/actions/connections/QuickbooksOnline';
import * as QuickbooksDesktop from '@libs/actions/connections/QuickbooksDesktop';
import * as ErrorUtils from '@libs/ErrorUtils';
import * as PolicyUtils from '@libs/PolicyUtils';
import Navigation from '@navigation/Navigation';
import type {WithPolicyConnectionsProps} from '@pages/workspace/withPolicyConnections';
import withPolicyConnections from '@pages/workspace/withPolicyConnections';
import variables from '@styles/variables';
import {clearQBOErrorField} from '@userActions/Policy/Policy';
import {clearQBDErrorField} from '@userActions/Policy/Policy';
import CONST from '@src/CONST';
import type {TranslationPaths} from '@src/languages/types';
import ROUTES from '@src/ROUTES';
Expand All @@ -26,13 +27,15 @@ type CardListItem = ListItem & {
function QuickbooksDesktopOutOfPocketExpenseAccountSelectPage({policy}: WithPolicyConnectionsProps) {
const {translate} = useLocalize();
const styles = useThemeStyles();
const {bankAccounts, journalEntryAccounts, accountPayable} = policy?.connections?.quickbooksOnline?.data ?? {};
const qboConfig = policy?.connections?.quickbooksOnline?.config;
const {bankAccounts, journalEntryAccounts, accountPayable} = policy?.connections?.quickbooksOnline?.data ?? {}; // TODO: should be updated to use the new connections object;
const qbdConfig = policy?.connections?.quickbooksOnline?.config; // TODO: should be updated to use the new connections object;

const {canUseNewDotQBD} = usePermissions();

const [title, description] = useMemo(() => {
let titleText: TranslationPaths | undefined;
let descriptionText: string | undefined;
switch (qboConfig?.reimbursableExpensesExportDestination) {
switch (qbdConfig?.reimbursableExpensesExportDestination) {
case CONST.QUICKBOOKS_REIMBURSABLE_ACCOUNT_TYPE.CHECK:
titleText = 'workspace.qbo.bankAccount';
descriptionText = translate('workspace.qbo.bankAccountDescription');
Expand All @@ -50,11 +53,11 @@ function QuickbooksDesktopOutOfPocketExpenseAccountSelectPage({policy}: WithPoli
}

return [titleText, descriptionText];
}, [qboConfig?.reimbursableExpensesExportDestination, translate]);
}, [qbdConfig?.reimbursableExpensesExportDestination, translate]);

const data: CardListItem[] = useMemo(() => {
let accounts: Account[];
switch (qboConfig?.reimbursableExpensesExportDestination) {
switch (qbdConfig?.reimbursableExpensesExportDestination) {
case CONST.QUICKBOOKS_REIMBURSABLE_ACCOUNT_TYPE.CHECK:
accounts = bankAccounts ?? [];
break;
Expand All @@ -72,20 +75,20 @@ function QuickbooksDesktopOutOfPocketExpenseAccountSelectPage({policy}: WithPoli
value: account,
text: account.name,
keyForList: account.name,
isSelected: account.id === qboConfig?.reimbursableExpensesAccount?.id,
isSelected: account.id === qbdConfig?.reimbursableExpensesAccount?.id,
}));
}, [qboConfig?.reimbursableExpensesExportDestination, qboConfig?.reimbursableExpensesAccount?.id, bankAccounts, journalEntryAccounts, accountPayable]);
}, [qbdConfig?.reimbursableExpensesExportDestination, qbdConfig?.reimbursableExpensesAccount?.id, bankAccounts, journalEntryAccounts, accountPayable]);

const policyID = policy?.id ?? '-1';

const selectExportAccount = useCallback(
(row: CardListItem) => {
if (row.value.id !== qboConfig?.reimbursableExpensesAccount?.id) {
QuickbooksOnline.updateQuickbooksOnlineReimbursableExpensesAccount(policyID, row.value, qboConfig?.reimbursableExpensesAccount);
if (row.value.id !== qbdConfig?.reimbursableExpensesAccount?.id) {
QuickbooksDesktop.updateQuickbooksDesktopReimbursableExpensesAccount(policyID, row.value, qbdConfig?.reimbursableExpensesAccount);
}
Navigation.goBack(ROUTES.POLICY_ACCOUNTING_QUICKBOOKS_ONLINE_EXPORT_OUT_OF_POCKET_EXPENSES.getRoute(policyID));
Navigation.goBack(ROUTES.POLICY_ACCOUNTING_QUICKBOOKS_DESKTOP_EXPORT_OUT_OF_POCKET_EXPENSES.getRoute(policyID));
},
[qboConfig?.reimbursableExpensesAccount, policyID],
[qbdConfig?.reimbursableExpensesAccount, policyID],
);

const listEmptyContent = useMemo(
Expand All @@ -102,24 +105,26 @@ function QuickbooksDesktopOutOfPocketExpenseAccountSelectPage({policy}: WithPoli
[translate, styles.pb10],
);

const accessVariants = canUseNewDotQBD ? [] : [CONST.POLICY.ACCESS_VARIANTS.ADMIN];

return (
<SelectionScreen
policyID={policyID}
accessVariants={[CONST.POLICY.ACCESS_VARIANTS.ADMIN]}
accessVariants={accessVariants}
featureName={CONST.POLICY.MORE_FEATURES.ARE_CONNECTIONS_ENABLED}
displayName={QuickbooksDesktopOutOfPocketExpenseAccountSelectPage.displayName}
sections={data.length ? [{data}] : []}
listItem={RadioListItem}
headerContent={<Text style={[styles.ph5, styles.pb5]}>{description}</Text>}
onBackButtonPress={() => Navigation.goBack(ROUTES.POLICY_ACCOUNTING_QUICKBOOKS_ONLINE_EXPORT_OUT_OF_POCKET_EXPENSES.getRoute(policyID))}
onBackButtonPress={() => Navigation.goBack(ROUTES.POLICY_ACCOUNTING_QUICKBOOKS_DESKTOP_EXPORT_OUT_OF_POCKET_EXPENSES.getRoute(policyID))}
onSelectRow={selectExportAccount}
initiallyFocusedOptionKey={data.find((mode) => mode.isSelected)?.keyForList}
title={title}
connectionName={CONST.POLICY.CONNECTIONS.NAME.QBO}
pendingAction={PolicyUtils.settingsPendingAction([CONST.QUICKBOOKS_CONFIG.REIMBURSABLE_EXPENSES_ACCOUNT], qboConfig?.pendingFields)}
errors={ErrorUtils.getLatestErrorField(qboConfig, CONST.QUICKBOOKS_CONFIG.REIMBURSABLE_EXPENSES_ACCOUNT)}
connectionName={CONST.POLICY.CONNECTIONS.NAME.QBD}
pendingAction={PolicyUtils.settingsPendingAction([CONST.QUICKBOOKS_DESKTOP_CONFIG.REIMBURSABLE_EXPENSES_ACCOUNT], qbdConfig?.pendingFields)}
errors={ErrorUtils.getLatestErrorField(qbdConfig, CONST.QUICKBOOKS_DESKTOP_CONFIG.REIMBURSABLE_EXPENSES_ACCOUNT)}
errorRowStyles={[styles.ph5, styles.pv3]}
onClose={() => clearQBOErrorField(policyID, CONST.QUICKBOOKS_CONFIG.REIMBURSABLE_EXPENSES_ACCOUNT)}
onClose={() => clearQBDErrorField(policyID, CONST.QUICKBOOKS_DESKTOP_CONFIG.REIMBURSABLE_EXPENSES_ACCOUNT)}
listEmptyContent={listEmptyContent}
shouldSingleExecuteRowSelect
/>
Expand Down
Loading

0 comments on commit 74d3252

Please sign in to comment.