Skip to content
Merged
39 changes: 32 additions & 7 deletions src/ROUTES.ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,10 @@ const ROUTES = {
SETTINGS_TIMEZONE_SELECT: 'settings/profile/timezone/select',
SETTINGS_PRONOUNS: 'settings/profile/pronouns',
SETTINGS_PREFERENCES: 'settings/preferences',
SETTINGS_SUBSCRIPTION: {route: 'settings/subscription', getRoute: (backTo?: string) => getUrlWithBackToParam('settings/subscription', backTo)},
SETTINGS_SUBSCRIPTION: {
route: 'settings/subscription',
getRoute: (backTo?: string) => getUrlWithBackToParam('settings/subscription', backTo),
},
SETTINGS_SUBSCRIPTION_SIZE: {
route: 'settings/subscription/subscription-size',
getRoute: (canChangeSize: 0 | 1) => `settings/subscription/subscription-size?canChangeSize=${canChangeSize as number}` as const,
Expand Down Expand Up @@ -333,7 +336,10 @@ const ROUTES = {
route: 'r/:reportID/edit/policyField/:policyID/:fieldID',
getRoute: (reportID: string | undefined, policyID: string | undefined, fieldID: string, backTo?: string) => {
if (!policyID || !reportID) {
Log.warn('Invalid policyID or reportID is used to build the EDIT_REPORT_FIELD_REQUEST route', {policyID, reportID});
Log.warn('Invalid policyID or reportID is used to build the EDIT_REPORT_FIELD_REQUEST route', {
policyID,
reportID,
});
}
return getUrlWithBackToParam(`r/${reportID}/edit/policyField/${policyID}/${encodeURIComponent(fieldID)}` as const, backTo);
},
Expand Down Expand Up @@ -1110,7 +1116,12 @@ const ROUTES = {
},
WORKSPACE_ACCOUNTING_QUICKBOOKS_ONLINE_ADVANCED: {
route: 'settings/workspaces/:policyID/accounting/quickbooks-online/advanced',
getRoute: (policyID: string) => `settings/workspaces/${policyID}/accounting/quickbooks-online/advanced` as const,
getRoute: (policyID: string | undefined) => {
if (!policyID) {
Log.warn('Invalid policyID is used to build the WORKSPACE_ACCOUNTING_QUICKBOOKS_ONLINE_ADVANCED route');
}
return `settings/workspaces/${policyID}/accounting/quickbooks-online/advanced` as const;
},
},
WORKSPACE_ACCOUNTING_QUICKBOOKS_ONLINE_ACCOUNT_SELECTOR: {
route: 'settings/workspaces/:policyID/accounting/quickbooks-online/account-selector',
Expand All @@ -1127,8 +1138,12 @@ const ROUTES = {
},
WORKSPACE_ACCOUNTING_RECONCILIATION_ACCOUNT_SETTINGS: {
route: 'settings/workspaces/:policyID/accounting/:connection/card-reconciliation/account',
getRoute: (policyID: string, connection?: ValueOf<typeof CONST.POLICY.CONNECTIONS.ROUTE>) =>
`settings/workspaces/${policyID}/accounting/${connection as string}/card-reconciliation/account` as const,
getRoute: (policyID: string | undefined, connection?: ValueOf<typeof CONST.POLICY.CONNECTIONS.ROUTE>) => {
if (!policyID) {
Log.warn('Invalid policyID is used to build the WORKSPACE_ACCOUNTING_RECONCILIATION_ACCOUNT_SETTINGS route');
}
return `settings/workspaces/${policyID}/accounting/${connection as string}/card-reconciliation/account` as const;
},
},
WORKSPACE_ACCOUNTING_MULTI_CONNECTION_SELECTOR: {
route: 'settings/workspaces/:policyID/accounting/:connection/connection-selector',
Expand Down Expand Up @@ -1767,7 +1782,12 @@ const ROUTES = {
},
POLICY_ACCOUNTING_XERO_ADVANCED: {
route: 'settings/workspaces/:policyID/accounting/xero/advanced',
getRoute: (policyID: string) => `settings/workspaces/${policyID}/accounting/xero/advanced` as const,
getRoute: (policyID: string | undefined) => {
if (!policyID) {
Log.warn('Invalid policyID is used to build the POLICY_ACCOUNTING_XERO_ADVANCED route');
}
return `settings/workspaces/${policyID}/accounting/xero/advanced` as const;
},
},
POLICY_ACCOUNTING_XERO_BILL_STATUS_SELECTOR: {
route: 'settings/workspaces/:policyID/accounting/xero/export/purchase-bill-status-selector',
Expand Down Expand Up @@ -2128,7 +2148,12 @@ const ROUTES = {
},
POLICY_ACCOUNTING_SAGE_INTACCT_ADVANCED: {
route: 'settings/workspaces/:policyID/accounting/sage-intacct/advanced',
getRoute: (policyID: string) => `settings/workspaces/${policyID}/accounting/sage-intacct/advanced` as const,
getRoute: (policyID: string | undefined) => {
if (!policyID) {
Log.warn('Invalid policyID is used to build the POLICY_ACCOUNTING_SAGE_INTACCT_ADVANCED route');
}
return `settings/workspaces/${policyID}/accounting/sage-intacct/advanced` as const;
},
},
POLICY_ACCOUNTING_SAGE_INTACCT_PAYMENT_ACCOUNT: {
route: 'settings/workspaces/:policyID/accounting/sage-intacct/advanced/payment-account',
Expand Down
10 changes: 9 additions & 1 deletion src/libs/CardUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import CONST from '@src/CONST';
import type {TranslationPaths} from '@src/languages/types';
import type {OnyxValues} from '@src/ONYXKEYS';
import ONYXKEYS from '@src/ONYXKEYS';
import type {BankAccountList, Card, CardFeeds, CardList, CompanyCardFeed, PersonalDetailsList, WorkspaceCardsList} from '@src/types/onyx';
import type {BankAccountList, Card, CardFeeds, CardList, CompanyCardFeed, ExpensifyCardSettings, PersonalDetailsList, Policy, WorkspaceCardsList} from '@src/types/onyx';
import type {FilteredCardList} from '@src/types/onyx/Card';
import type {CompanyCardFeedWithNumber, CompanyCardNicknames, CompanyFeeds, DirectCardFeedData} from '@src/types/onyx/CardFeeds';
import {isEmptyObject} from '@src/types/utils/EmptyObject';
Expand Down Expand Up @@ -529,6 +529,13 @@ function hasCardListObject(workspaceAccountID: number, feedName: CompanyCardFeed
return !!workspaceCards.cardList;
}

/**
* Check if the Expensify Card is fully set up and a new card can be issued
*/
function isExpensifyCardFullySetUp(policy?: OnyxEntry<Policy>, cardSettings?: OnyxEntry<ExpensifyCardSettings>): boolean {
return !!(policy?.areExpensifyCardsEnabled && cardSettings?.paymentBankAccountID);
Comment thread
mountiny marked this conversation as resolved.
}

export {
isExpensifyCard,
isCorporateCard,
Expand Down Expand Up @@ -569,4 +576,5 @@ export {
checkIfFeedConnectionIsBroken,
hasIssuedExpensifyCard,
hasCardListObject,
isExpensifyCardFullySetUp,
};
15 changes: 12 additions & 3 deletions src/pages/workspace/accounting/PolicyAccountingPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import {getRouteParamForConnection} from '@libs/AccountingUtils';
import {isAuthenticationError, isConnectionInProgress, isConnectionUnverified, removePolicyConnection, syncConnection} from '@libs/actions/connections';
import {getAssignedSupportData} from '@libs/actions/Policy/Policy';
import {getConciergeReportID} from '@libs/actions/Report';
import {isExpensifyCardFullySetUp} from '@libs/CardUtils';
import {
areSettingsInErrorFields,
findCurrentXeroOrganization,
Expand Down Expand Up @@ -118,7 +119,7 @@ function PolicyAccountingPage({policy}: PolicyAccountingPageProps) {
const currentXeroOrganization = findCurrentXeroOrganization(tenants, policy?.connections?.xero?.config?.tenantID);
const shouldShowSynchronizationError = !!synchronizationError;
const shouldShowReinstallConnectorMenuItem = shouldShowSynchronizationError && connectedIntegration === CONST.POLICY.CONNECTIONS.NAME.QBD;
const shouldShowCardReconciliationOption = policy?.areExpensifyCardsEnabled && cardSettings?.paymentBankAccountID;
const shouldShowCardReconciliationOption = isExpensifyCardFullySetUp(policy, cardSettings);

const overflowMenu: ThreeDotsMenuProps['menuItems'] = useMemo(
() => [
Expand Down Expand Up @@ -292,7 +293,12 @@ function PolicyAccountingPage({policy}: PolicyAccountingPageProps) {
return;
}

const iconProps = integrationData?.icon ? {icon: integrationData.icon, iconType: CONST.ICON_TYPE_AVATAR} : {};
const iconProps = integrationData?.icon
? {
icon: integrationData.icon,
iconType: CONST.ICON_TYPE_AVATAR,
}
: {};

return {
...iconProps,
Expand Down Expand Up @@ -416,7 +422,10 @@ function PolicyAccountingPage({policy}: PolicyAccountingPageProps) {
<ThreeDotsMenu
menuItems={overflowMenu}
anchorPosition={threeDotsMenuPosition}
anchorAlignment={{horizontal: CONST.MODAL.ANCHOR_ORIGIN_HORIZONTAL.RIGHT, vertical: CONST.MODAL.ANCHOR_ORIGIN_VERTICAL.TOP}}
anchorAlignment={{
horizontal: CONST.MODAL.ANCHOR_ORIGIN_HORIZONTAL.RIGHT,
vertical: CONST.MODAL.ANCHOR_ORIGIN_VERTICAL.TOP,
}}
/>
</View>
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,16 @@ import Text from '@components/Text';
import TextLink from '@components/TextLink';
import useLocalize from '@hooks/useLocalize';
import useThemeStyles from '@hooks/useThemeStyles';
import * as AccountingUtils from '@libs/AccountingUtils';
import {getConnectionNameFromRouteParam} from '@libs/AccountingUtils';
import {isExpensifyCardFullySetUp} from '@libs/CardUtils';
import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types';
import Navigation from '@navigation/Navigation';
import type {SettingsNavigatorParamList} from '@navigation/types';
import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper';
import type {WithPolicyConnectionsProps} from '@pages/workspace/withPolicyConnections';
import withPolicyConnections from '@pages/workspace/withPolicyConnections';
import ToggleSettingOptionRow from '@pages/workspace/workflows/ToggleSettingsOptionRow';
import * as Card from '@userActions/Card';
import {toggleContinuousReconciliation} from '@userActions/Card';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
Expand All @@ -29,24 +30,24 @@ function CardReconciliationPage({policy, route}: CardReconciliationPageProps) {
const styles = useThemeStyles();
const {translate} = useLocalize();

const workspaceAccountID = policy?.workspaceAccountID ?? -1;
const workspaceAccountID = policy?.workspaceAccountID ?? CONST.DEFAULT_NUMBER_ID;

const [isContinuousReconciliationOn] = useOnyx(`${ONYXKEYS.COLLECTION.EXPENSIFY_CARD_USE_CONTINUOUS_RECONCILIATION}${workspaceAccountID}`);
const [cardSettings] = useOnyx(`${ONYXKEYS.COLLECTION.PRIVATE_EXPENSIFY_CARD_SETTINGS}${workspaceAccountID}`);
const [currentConnectionName] = useOnyx(`${ONYXKEYS.COLLECTION.EXPENSIFY_CARD_CONTINUOUS_RECONCILIATION_CONNECTION}${workspaceAccountID}`);
const [bankAccountList] = useOnyx(ONYXKEYS.BANK_ACCOUNT_LIST);

const paymentBankAccountID = cardSettings?.paymentBankAccountID ?? 0;
const paymentBankAccountID = cardSettings?.paymentBankAccountID ?? CONST.DEFAULT_NUMBER_ID;
const bankAccountTitle = bankAccountList?.[paymentBankAccountID]?.title ?? '';

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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need t change this line?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Eslint

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

https://github.com/Expensify/App/actions/runs/13524618354/job/37791922234?pr=57407

Do not default string IDs to any value.

As I mentioned above - I don't like this rule as we have many occurrences of defaulting to '-1'

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and TS fails, but it's my bad I didn't resolve the conflicts properly

const {connection} = route.params;
const connectionName = AccountingUtils.getConnectionNameFromRouteParam(connection) as ConnectionName;
const connectionName = getConnectionNameFromRouteParam(connection) as ConnectionName;
const autoSync = !!policy?.connections?.[connectionName]?.config?.autoSync?.enabled;
const shouldShow = policy?.areExpensifyCardsEnabled && cardSettings?.paymentBankAccountID;
const shouldShow = isExpensifyCardFullySetUp(policy, cardSettings);

const toggleContinuousReconciliation = (value: boolean) => {
Card.toggleContinuousReconciliation(workspaceAccountID, value, connectionName, currentConnectionName);
const handleToggleContinuousReconciliation = (value: boolean) => {
toggleContinuousReconciliation(workspaceAccountID, value, connectionName, currentConnectionName);
if (value) {
Navigation.navigate(ROUTES.WORKSPACE_ACCOUNTING_RECONCILIATION_ACCOUNT_SETTINGS.getRoute(policyID, connection));
}
Expand Down Expand Up @@ -93,7 +94,7 @@ function CardReconciliationPage({policy, route}: CardReconciliationPageProps) {
switchAccessibilityLabel={translate('workspace.accounting.continuousReconciliation')}
disabled={!autoSync}
isActive={!!isContinuousReconciliationOn}
onToggle={toggleContinuousReconciliation}
onToggle={handleToggleContinuousReconciliation}
wrapperStyle={styles.ph5}
/>
{!autoSync && (
Expand Down
5 changes: 2 additions & 3 deletions src/pages/workspace/members/WorkspaceMemberDetailsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import useStyleUtils from '@hooks/useStyleUtils';
import useThemeStyles from '@hooks/useThemeStyles';
import {setPolicyPreventSelfApproval} from '@libs/actions/Policy/Policy';
import {removeApprovalWorkflow as removeApprovalWorkflowAction, updateApprovalWorkflow} from '@libs/actions/Workflow';
import {getAllCardsForWorkspace, getCardFeedIcon, getCompanyFeeds, maskCardNumber} from '@libs/CardUtils';
import {getAllCardsForWorkspace, getCardFeedIcon, getCompanyFeeds, isExpensifyCardFullySetUp, maskCardNumber} from '@libs/CardUtils';
import {convertToDisplayString} from '@libs/CurrencyUtils';
import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types';
import {getDisplayNameOrDefault} from '@libs/PersonalDetailsUtils';
Expand Down Expand Up @@ -85,7 +85,6 @@ function WorkspaceMemberDetailsPage({personalDetails, policy, route}: WorkspaceM
const ownerDetails = useMemo(() => personalDetails?.[policy?.ownerAccountID ?? CONST.DEFAULT_NUMBER_ID] ?? ({} as PersonalDetails), [personalDetails, policy?.ownerAccountID]);
const policyOwnerDisplayName = formatPhoneNumber(getDisplayNameOrDefault(ownerDetails)) ?? policy?.owner ?? '';
const hasMultipleFeeds = Object.values(getCompanyFeeds(cardFeeds)).filter((feed) => !feed.pending).length > 0;
const paymentBankAccountID = cardSettings?.paymentBankAccountID;
const workspaceCards = getAllCardsForWorkspace(workspaceAccountID, cardList);

const policyApproverEmail = policy?.approver;
Expand Down Expand Up @@ -261,7 +260,7 @@ function WorkspaceMemberDetailsPage({personalDetails, policy, route}: WorkspaceM
return <NotFoundPage />;
}

const shouldShowCardsSection = (!!paymentBankAccountID && !!policy?.areExpensifyCardsEnabled) || hasMultipleFeeds;
const shouldShowCardsSection = isExpensifyCardFullySetUp(policy, cardSettings) || hasMultipleFeeds;

return (
<AccessOrNotFoundWrapper
Expand Down
43 changes: 23 additions & 20 deletions src/pages/workspace/members/WorkspaceMemberNewCardPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
hasCardListObject,
hasOnlyOneCardToAssign,
isCustomFeed,
isExpensifyCardFullySetUp,
isSelectedFeedExpired,
} from '@libs/CardUtils';
import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types';
Expand Down Expand Up @@ -54,6 +55,7 @@ function WorkspaceMemberNewCardPage({route, personalDetails}: WorkspaceMemberNew
const [cardFeeds] = useOnyx(`${ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER}${workspaceAccountID}`);
const [selectedFeed, setSelectedFeed] = useState('');
const [shouldShowError, setShouldShowError] = useState(false);
const [cardSettings] = useOnyx(`${ONYXKEYS.COLLECTION.PRIVATE_EXPENSIFY_CARD_SETTINGS}${workspaceAccountID}`);

const accountID = Number(route.params.accountID);
const memberLogin = personalDetails?.[accountID]?.login ?? '';
Expand All @@ -64,6 +66,8 @@ function WorkspaceMemberNewCardPage({route, personalDetails}: WorkspaceMemberNew
const [list] = useOnyx(`${ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST}${workspaceAccountID}_${selectedFeed}`);
const filteredCardList = getFilteredCardList(list, cardFeeds?.settings?.oAuthAccountDetails?.[selectedFeed as CompanyCardFeed]);

const shouldShowExpensifyCard = isExpensifyCardFullySetUp(policy, cardSettings);

const handleSubmit = () => {
if (!selectedFeed) {
setShouldShowError(true);
Expand Down Expand Up @@ -132,26 +136,25 @@ function WorkspaceMemberNewCardPage({route, personalDetails}: WorkspaceMemberNew
),
}));

const feeds =
workspaceAccountID && policy?.areExpensifyCardsEnabled
? [
...companyCardFeeds,
{
value: CONST.EXPENSIFY_CARD.NAME,
text: translate('workspace.common.expensifyCard'),
keyForList: CONST.EXPENSIFY_CARD.NAME,
isSelected: selectedFeed === CONST.EXPENSIFY_CARD.NAME,
leftElement: (
<Icon
src={ExpensifyCardImage}
width={variables.cardIconWidth}
height={variables.cardIconHeight}
additionalStyles={[styles.cardIcon, styles.mr3]}
/>
),
},
]
: companyCardFeeds;
const feeds = shouldShowExpensifyCard
? [
...companyCardFeeds,
{
value: CONST.EXPENSIFY_CARD.NAME,
text: translate('workspace.common.expensifyCard'),
keyForList: CONST.EXPENSIFY_CARD.NAME,
isSelected: selectedFeed === CONST.EXPENSIFY_CARD.NAME,
leftElement: (
<Icon
src={ExpensifyCardImage}
width={variables.cardIconWidth}
height={variables.cardIconHeight}
additionalStyles={[styles.cardIcon, styles.mr3]}
/>
),
},
]
: companyCardFeeds;

const goBack = () => Navigation.goBack();

Expand Down
Loading