Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sage intacct export: offline and error #44739

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
7a0ba43
update SelectionScreen and add Offline with feedback to components
war-in Jul 2, 2024
9088361
add offline with feedback to remaining pages
war-in Jul 2, 2024
91c62d9
show errors in correct places
war-in Jul 3, 2024
95da618
style selection screen correctly
war-in Jul 4, 2024
c420893
move error and pending fields up in onyx data
war-in Jul 4, 2024
42f683c
handle offline state
war-in Jul 4, 2024
d2c8472
Merge branch 'refs/heads/main' into war-in/sage-intacct-export-config…
war-in Jul 4, 2024
36ba4c9
Merge branch 'refs/heads/war-in/sage-intacct-export-config' into war-…
war-in Jul 4, 2024
80cc058
track errors in (non)reimbursable in main page
war-in Jul 4, 2024
968970a
Merge branch 'refs/heads/war-in/sage-intacct-export-config' into war-…
war-in Jul 4, 2024
c836521
address comments
war-in Jul 4, 2024
59c0fa8
use already defined type
war-in Jul 4, 2024
76b2328
Merge branch 'refs/heads/war-in/sage-intacct-export-config' into war-…
war-in Jul 5, 2024
32d879f
Merge branch 'refs/heads/war-in/sage-intacct-export-config' into war-…
war-in Jul 8, 2024
847706e
Merge branch 'refs/heads/war-in/sage-intacct-export-config' into war-…
war-in Jul 9, 2024
a6d6588
include safe area padding bottom on errors
war-in Jul 9, 2024
452d279
polish endpoints usages
war-in Jul 9, 2024
759c350
Merge branch 'refs/heads/war-in/sage-intacct-export-config' into war-…
war-in Jul 9, 2024
df588a0
Merge branch 'refs/heads/main' into war-in/sage-intacct-export-config…
war-in Jul 9, 2024
e39b199
Merge branch 'refs/heads/main' into war-in/sage-intacct-export-config…
war-in Jul 10, 2024
97f476a
fix Policy.ts after merge
war-in Jul 10, 2024
43fc0f8
make error message sticky
war-in Jul 10, 2024
b70782a
fix margin in non-reimbursable config
war-in Jul 10, 2024
5f9dc94
fix prettier
war-in Jul 10, 2024
0bd28cc
dry the code
war-in Jul 11, 2024
d465864
Merge branch 'refs/heads/war-in/demonstrate-sticky-footer' into war-i…
war-in Jul 11, 2024
161f0ec
unify spacing
war-in Jul 11, 2024
51fe9f1
Merge branch 'refs/heads/main' into war-in/sage-intacct-export-config…
war-in Jul 11, 2024
4285951
fix selection list padding bottom in (non)reimbursable pages
war-in Jul 11, 2024
fcb95f9
Merge branch 'refs/heads/main' into war-in/sage-intacct-export-config…
war-in Jul 15, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions src/components/ErrorMessageRow.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import {mapValues} from 'lodash';
import React from 'react';
import type {StyleProp, ViewStyle} from 'react-native';
import type * as OnyxCommon from '@src/types/onyx/OnyxCommon';
import type {ReceiptError, ReceiptErrors} from '@src/types/onyx/Transaction';
import {isEmptyObject} from '@src/types/utils/EmptyObject';
import MessagesRow from './MessagesRow';

type ErrorMessageRowProps = {
/** The errors to display */
errors?: OnyxCommon.Errors | ReceiptErrors | null;

/** Additional style object for the error row */
errorRowStyles?: StyleProp<ViewStyle>;

/** A function to run when the X button next to the error is clicked */
onClose?: () => void;

/** Whether we can dismiss the error message */
canDismissError?: boolean;
};

function ErrorMessageRow({errors, errorRowStyles, onClose, canDismissError = true}: ErrorMessageRowProps) {
// Some errors have a null message. This is used to apply opacity only and to avoid showing redundant messages.
const errorEntries = Object.entries(errors ?? {});
const filteredErrorEntries = errorEntries.filter((errorEntry): errorEntry is [string, string | ReceiptError] => errorEntry[1] !== null);
const errorMessages = mapValues(Object.fromEntries(filteredErrorEntries), (error) => error);
const hasErrorMessages = !isEmptyObject(errorMessages);

return hasErrorMessages ? (
<MessagesRow
messages={errorMessages}
type="error"
onClose={onClose}
containerStyles={errorRowStyles}
canDismiss={canDismissError}
/>
) : null;
}

ErrorMessageRow.displayName = 'ErrorMessageRow';

export default ErrorMessageRow;
22 changes: 7 additions & 15 deletions src/components/OfflineWithFeedback.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import {mapValues} from 'lodash';
import React, {useCallback} from 'react';
import type {StyleProp, ViewStyle} from 'react-native';
import {View} from 'react-native';
Expand All @@ -10,11 +9,11 @@ import shouldRenderOffscreen from '@libs/shouldRenderOffscreen';
import type {AllStyles} from '@styles/utils/types';
import CONST from '@src/CONST';
import type * as OnyxCommon from '@src/types/onyx/OnyxCommon';
import type {ReceiptError, ReceiptErrors} from '@src/types/onyx/Transaction';
import type {ReceiptErrors} from '@src/types/onyx/Transaction';
import type ChildrenProps from '@src/types/utils/ChildrenProps';
import {isEmptyObject} from '@src/types/utils/EmptyObject';
import CustomStylesForChildrenProvider from './CustomStylesForChildrenProvider';
import MessagesRow from './MessagesRow';
import ErrorMessageRow from './ErrorMessageRow';

/**
* This component should be used when we are using the offline pattern B (offline with feedback).
Expand Down Expand Up @@ -83,12 +82,6 @@ function OfflineWithFeedback({

const hasErrors = !isEmptyObject(errors ?? {});

// Some errors have a null message. This is used to apply opacity only and to avoid showing redundant messages.
const errorEntries = Object.entries(errors ?? {});
const filteredErrorEntries = errorEntries.filter((errorEntry): errorEntry is [string, string | ReceiptError] => errorEntry[1] !== null);
const errorMessages = mapValues(Object.fromEntries(filteredErrorEntries), (error) => error);

const hasErrorMessages = !isEmptyObject(errorMessages);
const isOfflinePendingAction = !!isOffline && !!pendingAction;
const isUpdateOrDeleteError = hasErrors && (pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE || pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE);
const isAddError = hasErrors && pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD;
Expand Down Expand Up @@ -139,13 +132,12 @@ function OfflineWithFeedback({
<CustomStylesForChildrenProvider style={needsStrikeThrough ? [styles.offlineFeedback.deleted, styles.userSelectNone] : null}>{children}</CustomStylesForChildrenProvider>
</View>
)}
{shouldShowErrorMessages && hasErrorMessages && (
<MessagesRow
messages={errorMessages}
type="error"
{shouldShowErrorMessages && (
<ErrorMessageRow
errors={errors}
errorRowStyles={errorRowStyles}
onClose={onClose}
containerStyles={errorRowStyles}
canDismiss={canDismissError}
canDismissError={canDismissError}
/>
)}
</View>
Expand Down
3 changes: 2 additions & 1 deletion src/components/SelectionList/BaseSelectionList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ function BaseSelectionList<TItem extends ListItem>(
showConfirmButton = false,
shouldPreventDefaultFocusOnSelectRow = false,
containerStyle,
sectionListStyle,
disableKeyboardShortcuts = false,
children,
shouldStopPropagation = false,
Expand Down Expand Up @@ -729,7 +730,7 @@ function BaseSelectionList<TItem extends ListItem>(
viewabilityConfig={{viewAreaCoveragePercentThreshold: 95}}
testID="selection-list"
onLayout={onSectionListLayout}
style={(!maxToRenderPerBatch || (shouldHideListOnInitialRender && isInitialSectionListRender)) && styles.opacity0}
style={[(!maxToRenderPerBatch || (shouldHideListOnInitialRender && isInitialSectionListRender)) && styles.opacity0, sectionListStyle]}
ListHeaderComponent={listHeaderContent}
ListFooterComponent={listFooterContent ?? ShowMoreButtonInstance}
ListEmptyComponent={listEmptyContent}
Expand Down
3 changes: 3 additions & 0 deletions src/components/SelectionList/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -413,6 +413,9 @@ type BaseSelectionListProps<TItem extends ListItem> = Partial<ChildrenProps> & {
/** Styles to apply to SelectionList container */
containerStyle?: StyleProp<ViewStyle>;

/** Styles to apply to SectionList component */
sectionListStyle?: StyleProp<ViewStyle>;

/** Whether focus event should be delayed */
shouldDelayFocus?: boolean;

Expand Down
13 changes: 9 additions & 4 deletions src/components/SelectionScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import type * as OnyxCommon from '@src/types/onyx/OnyxCommon';
import type {ConnectionName, PolicyFeatureName} from '@src/types/onyx/Policy';
import type {ReceiptErrors} from '@src/types/onyx/Transaction';
import {isEmptyObject} from '@src/types/utils/EmptyObject';
import ErrorMessageRow from './ErrorMessageRow';
import HeaderWithBackButton from './HeaderWithBackButton';
import OfflineWithFeedback from './OfflineWithFeedback';
import ScreenWrapper from './ScreenWrapper';
Expand Down Expand Up @@ -128,9 +129,6 @@ function SelectionScreen({
{headerContent}
<OfflineWithFeedback
pendingAction={pendingAction}
errors={errors}
errorRowStyles={[errorRowStyles]}
onClose={onClose}
style={[styles.flex1]}
contentContainerStyle={[styles.flex1]}
>
Expand All @@ -143,7 +141,14 @@ function SelectionScreen({
initiallyFocusedOptionKey={initiallyFocusedOptionKey}
listEmptyContent={listEmptyContent}
listFooterContent={listFooterContent}
/>
sectionListStyle={[styles.flexGrow0]}
>
<ErrorMessageRow
errors={errors}
errorRowStyles={errorRowStyles}
onClose={onClose}
/>
</SelectionList>
</OfflineWithFeedback>
</ScreenWrapper>
</AccessOrNotFoundWrapper>
Expand Down
10 changes: 5 additions & 5 deletions src/libs/actions/Policy/Policy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -530,14 +530,14 @@ function clearNetSuiteErrorField(policyID: string, fieldName: string) {
Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, {connections: {netsuite: {options: {config: {errorFields: {[fieldName]: null}}}}}});
}

function clearNetSuiteAutoSyncErrorField(policyID: string) {
Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, {connections: {netsuite: {config: {errorFields: {autoSync: null}}}}});
}

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

function clearNetSuiteAutoSyncErrorField(policyID: string) {
Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, {connections: {netsuite: {config: {errorFields: {autoSync: null}}}}});
}

function setWorkspaceReimbursement(policyID: string, reimbursementChoice: ValueOf<typeof CONST.POLICY.REIMBURSEMENT_CHOICES>, reimburserEmail: string) {
const policy = getPolicy(policyID);

Expand Down Expand Up @@ -3127,6 +3127,7 @@ export {
generateCustomUnitID,
clearQBOErrorField,
clearXeroErrorField,
clearSageIntacctErrorField,
clearNetSuiteErrorField,
clearNetSuiteAutoSyncErrorField,
clearWorkspaceReimbursementErrors,
Expand All @@ -3144,7 +3145,6 @@ export {
openPolicyExpensifyCardsPage,
requestExpensifyCardLimitIncrease,
getAdminPoliciesConnectedToSageIntacct,
clearSageIntacctErrorField,
};

export type {NewCustomUnit};
43 changes: 19 additions & 24 deletions src/libs/actions/connections/SageIntacct.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import type {
Connections,
SageIntacctConnectionsConfig,
SageIntacctDimension,
SageIntacctExportConfig,
SageIntacctMappingName,
SageIntacctMappingType,
SageIntacctMappingValue,
Expand Down Expand Up @@ -316,7 +317,7 @@ function removeSageIntacctUserDimensions(policyID: string, dimensionName: string
);
}

function prepareOnyxDataForExportUpdate(policyID: string, settingName: keyof Connections['intacct']['config']['export'], settingValue: string | null) {
function prepareOnyxDataForExportUpdate(policyID: string, settingName: keyof SageIntacctExportConfig, settingValue: string | null) {
const optimisticData: OnyxUpdate[] = [
{
onyxMethod: Onyx.METHOD.MERGE,
Expand All @@ -327,12 +328,12 @@ function prepareOnyxDataForExportUpdate(policyID: string, settingName: keyof Con
config: {
export: {
[settingName]: settingValue,
pendingFields: {
[settingName]: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE,
},
errorFields: {
[settingName]: null,
},
},
pendingFields: {
[settingName]: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE,
},
errorFields: {
[settingName]: null,
},
},
},
Expand All @@ -349,14 +350,11 @@ function prepareOnyxDataForExportUpdate(policyID: string, settingName: keyof Con
connections: {
intacct: {
config: {
export: {
[settingName]: settingValue,
pendingFields: {
[settingName]: null,
},
errorFields: {
[settingName]: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('common.genericErrorMessage'),
},
pendingFields: {
[settingName]: null,
},
errorFields: {
[settingName]: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('common.genericErrorMessage'),
},
},
},
Expand All @@ -373,14 +371,11 @@ function prepareOnyxDataForExportUpdate(policyID: string, settingName: keyof Con
connections: {
intacct: {
config: {
export: {
[settingName]: settingValue,
pendingFields: {
[settingName]: null,
},
errorFields: {
[settingName]: null,
},
pendingFields: {
[settingName]: null,
},
errorFields: {
[settingName]: null,
},
},
},
Expand Down Expand Up @@ -472,7 +467,7 @@ function updateSageIntacctNonreimbursableExpensesExportVendor(policyID: string,
API.write(WRITE_COMMANDS.UPDATE_SAGE_INTACCT_NON_REIMBURSABLE_EXPENSES_EXPORT_VENDOR, parameters, {optimisticData, failureData, successData});
}

function updateSageIntacctDefaultVendor(policyID: string, settingName: keyof Connections['intacct']['config']['export'], vendor: string) {
function updateSageIntacctDefaultVendor(policyID: string, settingName: keyof SageIntacctExportConfig, vendor: string) {
if (settingName === CONST.SAGE_INTACCT_CONFIG.REIMBURSABLE_VENDOR) {
updateSageIntacctReimbursableExpensesReportExportDefaultVendor(policyID, vendor);
} else if (settingName === CONST.SAGE_INTACCT_CONFIG.NON_REIMBURSABLE_CREDIT_CARD_VENDOR) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ function SageIntacctAdvancedPage({policy}: WithPolicyProps) {
switchAccessibilityLabel={section.label}
isActive={section.isActive}
onToggle={section.onToggle}
wrapperStyle={[styles.ph5, styles.pv5]}
wrapperStyle={[styles.ph5, styles.pb3]}
pendingAction={section.pendingAction}
errors={section.error}
onCloseError={section.onCloseError}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ function SageIntacctPaymentAccountPage({policy}: WithPolicyConnectionsProps) {
connectionName={CONST.POLICY.CONNECTIONS.NAME.SAGE_INTACCT}
pendingAction={config?.pendingFields?.reimbursementAccountID}
errors={ErrorUtils.getLatestErrorField(config ?? {}, CONST.SAGE_INTACCT_CONFIG.REIMBURSEMENT_ACCOUNT_ID)}
errorRowStyles={[styles.ph5, styles.mv2]}
errorRowStyles={[styles.ph5, styles.pv3]}
onClose={() => Policy.clearSageIntacctErrorField(policyID, CONST.SAGE_INTACCT_CONFIG.REIMBURSEMENT_ACCOUNT_ID)}
/>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,12 @@ import type {SelectorType} from '@components/SelectionScreen';
import Text from '@components/Text';
import useLocalize from '@hooks/useLocalize';
import useThemeStyles from '@hooks/useThemeStyles';
import * as ErrorUtils from '@libs/ErrorUtils';
import Navigation from '@navigation/Navigation';
import type {WithPolicyProps} from '@pages/workspace/withPolicy';
import withPolicyConnections from '@pages/workspace/withPolicyConnections';
import {updateSageIntacctExportDate} from '@userActions/connections/SageIntacct';
import * as Policy from '@userActions/Policy/Policy';
import CONST from '@src/CONST';
import ROUTES from '@src/ROUTES';

Expand All @@ -23,6 +25,7 @@ function SageIntacctDatePage({policy}: WithPolicyProps) {
const {translate} = useLocalize();
const policyID = policy?.id ?? '-1';
const styles = useThemeStyles();
const {config} = policy?.connections?.intacct ?? {};
const {export: exportConfig} = policy?.connections?.intacct?.config ?? {};
const data: MenuListItem[] = Object.values(CONST.SAGE_INTACCT_EXPORT_DATE).map((dateType) => ({
value: dateType,
Expand Down Expand Up @@ -65,6 +68,10 @@ function SageIntacctDatePage({policy}: WithPolicyProps) {
featureName={CONST.POLICY.MORE_FEATURES.ARE_CONNECTIONS_ENABLED}
onBackButtonPress={() => Navigation.goBack(ROUTES.POLICY_ACCOUNTING_SAGE_INTACCT_EXPORT.getRoute(policyID))}
connectionName={CONST.POLICY.CONNECTIONS.NAME.SAGE_INTACCT}
pendingAction={config?.pendingFields?.exportDate}
errors={ErrorUtils.getLatestErrorField(config ?? {}, CONST.SAGE_INTACCT_CONFIG.EXPORT_DATE)}
errorRowStyles={[styles.ph5, styles.pv3]}
onClose={() => Policy.clearSageIntacctErrorField(policyID, CONST.SAGE_INTACCT_CONFIG.EXPORT_DATE)}
/>
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,13 @@ import SelectionScreen from '@components/SelectionScreen';
import Text from '@components/Text';
import useLocalize from '@hooks/useLocalize';
import useThemeStyles from '@hooks/useThemeStyles';
import * as ErrorUtils from '@libs/ErrorUtils';
import Navigation from '@libs/Navigation/Navigation';
import {getSageIntacctNonReimbursableActiveDefaultVendor, getSageIntacctVendors} from '@libs/PolicyUtils';
import type {SettingsNavigatorParamList} from '@navigation/types';
import variables from '@styles/variables';
import {updateSageIntacctDefaultVendor} from '@userActions/connections/SageIntacct';
import * as Policy from '@userActions/Policy/Policy';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
Expand All @@ -29,20 +31,22 @@ function SageIntacctDefaultVendorPage({route}: SageIntacctDefaultVendorPageProps

const policyID = route.params.policyID ?? '-1';
const [policy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`);
const {config} = policy?.connections?.intacct ?? {};
const {export: exportConfig} = policy?.connections?.intacct?.config ?? {};

const isReimbursable = route.params.reimbursable === CONST.SAGE_INTACCT_CONFIG.REIMBURSABLE;

let defaultVendor;
let settingName: keyof Connections['intacct']['config']['export'];
if (!isReimbursable) {
const {nonReimbursable} = policy?.connections?.intacct?.config.export ?? {};
const {nonReimbursable} = exportConfig ?? {};
defaultVendor = getSageIntacctNonReimbursableActiveDefaultVendor(policy);
settingName =
nonReimbursable === CONST.SAGE_INTACCT_NON_REIMBURSABLE_EXPENSE_TYPE.CREDIT_CARD_CHARGE
? CONST.SAGE_INTACCT_CONFIG.NON_REIMBURSABLE_CREDIT_CARD_VENDOR
: CONST.SAGE_INTACCT_CONFIG.NON_REIMBURSABLE_VENDOR;
} else {
const {reimbursableExpenseReportDefaultVendor} = policy?.connections?.intacct?.config.export ?? {};
const {reimbursableExpenseReportDefaultVendor} = exportConfig ?? {};
defaultVendor = reimbursableExpenseReportDefaultVendor;
settingName = CONST.SAGE_INTACCT_CONFIG.REIMBURSABLE_VENDOR;
}
Expand Down Expand Up @@ -103,6 +107,10 @@ function SageIntacctDefaultVendorPage({route}: SageIntacctDefaultVendorPageProps
listEmptyContent={listEmptyContent}
accessVariants={[CONST.POLICY.ACCESS_VARIANTS.ADMIN, CONST.POLICY.ACCESS_VARIANTS.PAID]}
connectionName={CONST.POLICY.CONNECTIONS.NAME.SAGE_INTACCT}
pendingAction={config?.pendingFields?.[settingName]}
errors={ErrorUtils.getLatestErrorField(config, settingName)}
errorRowStyles={[styles.ph5, styles.pv3]}
onClose={() => Policy.clearSageIntacctErrorField(policyID, settingName)}
/>
);
}
Expand Down
Loading
Loading