diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts index 5e0276e9d8cd5..54b7da704cd1e 100755 --- a/src/ONYXKEYS.ts +++ b/src/ONYXKEYS.ts @@ -548,6 +548,9 @@ const ONYXKEYS = { /** Expensify cards settings */ PRIVATE_EXPENSIFY_CARD_SETTINGS: 'private_expensifyCardSettings_', + /** Expensify cards manual billing setting */ + PRIVATE_EXPENSIFY_CARD_MANUAL_BILLING: 'private_expensifyCardManualBilling_', + /** Stores which connection is set up to use Continuous Reconciliation */ EXPENSIFY_CARD_CONTINUOUS_RECONCILIATION_CONNECTION: 'expensifyCard_continuousReconciliationConnection_', @@ -898,6 +901,7 @@ type OnyxCollectionValuesMapping = { [ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_USER_BILLING_GRACE_PERIOD_END]: OnyxTypes.BillingGraceEndPeriod; [ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER]: OnyxTypes.CardFeeds; [ONYXKEYS.COLLECTION.PRIVATE_EXPENSIFY_CARD_SETTINGS]: OnyxTypes.ExpensifyCardSettings; + [ONYXKEYS.COLLECTION.PRIVATE_EXPENSIFY_CARD_MANUAL_BILLING]: boolean; [ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST]: OnyxTypes.WorkspaceCardsList; [ONYXKEYS.COLLECTION.EXPENSIFY_CARD_CONTINUOUS_RECONCILIATION_CONNECTION]: OnyxTypes.PolicyConnectionName; [ONYXKEYS.COLLECTION.EXPENSIFY_CARD_USE_CONTINUOUS_RECONCILIATION]: boolean; diff --git a/src/languages/en.ts b/src/languages/en.ts index b61e383dd0f86..22dc27d584845 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -145,6 +145,7 @@ import type { SetTheRequestParams, SettledAfterAddedBankAccountParams, SettleExpensifyCardParams, + SettlementDateParams, ShareParams, SignUpNewFaceCodeParams, SizeExceededParams, @@ -3506,6 +3507,8 @@ const translations = { limit: 'Limit', currentBalance: 'Current balance', currentBalanceDescription: 'Current balance is the sum of all posted Expensify Card transactions that have occurred since the last settlement date.', + balanceWillBeSettledOn: ({settlementDate}: SettlementDateParams) => `Balance will be settled on ${settlementDate}`, + settleBalance: 'Settle balance', cardLimit: 'Card limit', remainingLimit: 'Remaining limit', requestLimitIncrease: 'Request limit increase', diff --git a/src/languages/es.ts b/src/languages/es.ts index 70c2b16975dbc..9302f309d519b 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -144,6 +144,7 @@ import type { SetTheRequestParams, SettledAfterAddedBankAccountParams, SettleExpensifyCardParams, + SettlementDateParams, ShareParams, SignUpNewFaceCodeParams, SizeExceededParams, @@ -3547,6 +3548,8 @@ const translations = { currentBalance: 'Saldo actual', currentBalanceDescription: 'El saldo actual es la suma de todas las transacciones contabilizadas con la Tarjeta Expensify que se han producido desde la última fecha de liquidación.', + balanceWillBeSettledOn: ({settlementDate}: SettlementDateParams) => `El saldo se liquidará el ${settlementDate}.`, + settleBalance: 'Liquidar saldo', cardLimit: 'Límite de la tarjeta', remainingLimit: 'Límite restante', requestLimitIncrease: 'Solicitar aumento de límite', diff --git a/src/languages/params.ts b/src/languages/params.ts index cc5132bed2a0b..f3d48acc63b15 100644 --- a/src/languages/params.ts +++ b/src/languages/params.ts @@ -603,6 +603,10 @@ type FlightLayoverParams = { layover: string; }; +type SettlementDateParams = { + settlementDate: string; +}; + export type { AuthenticationErrorParams, ImportMembersSuccessfullDescriptionParams, @@ -816,4 +820,5 @@ export type { ChatWithAccountManagerParams, EditDestinationSubtitleParams, FlightLayoverParams, + SettlementDateParams, }; diff --git a/src/libs/API/parameters/QueueExpensifyCardForBillingParams.ts b/src/libs/API/parameters/QueueExpensifyCardForBillingParams.ts new file mode 100644 index 0000000000000..80326a7837c9e --- /dev/null +++ b/src/libs/API/parameters/QueueExpensifyCardForBillingParams.ts @@ -0,0 +1,6 @@ +type QueueExpensifyCardForBillingParams = { + feedCountry: string; + domainAccountID: number; +}; + +export default QueueExpensifyCardForBillingParams; diff --git a/src/libs/API/parameters/index.ts b/src/libs/API/parameters/index.ts index 460eb156b64b7..cc0b37b3fbcc4 100644 --- a/src/libs/API/parameters/index.ts +++ b/src/libs/API/parameters/index.ts @@ -363,6 +363,7 @@ export type {default as DismissProductTrainingParams} from './DismissProductTrai export type {default as OpenWorkspacePlanPageParams} from './OpenWorkspacePlanPage'; export type {default as ResetSMSDeliveryFailureStatusParams} from './ResetSMSDeliveryFailureStatusParams'; export type {default as CreatePerDiemRequestParams} from './CreatePerDiemRequestParams'; +export type {default as QueueExpensifyCardForBillingParams} from './QueueExpensifyCardForBillingParams'; export type {default as GetCorpayOnboardingFieldsParams} from './GetCorpayOnboardingFieldsParams'; export type {SaveCorpayOnboardingCompanyDetailsParams} from './SaveCorpayOnboardingCompanyDetailsParams'; export type {default as AcceptSpotnanaTermsParams} from './AcceptSpotnanaTermsParams'; diff --git a/src/libs/API/types.ts b/src/libs/API/types.ts index 4ed08ffef88c6..a1e23d1114821 100644 --- a/src/libs/API/types.ts +++ b/src/libs/API/types.ts @@ -405,6 +405,7 @@ const WRITE_COMMANDS = { CONFIGURE_EXPENSIFY_CARDS_FOR_POLICY: 'ConfigureExpensifyCardsForPolicy', CREATE_EXPENSIFY_CARD: 'CreateExpensifyCard', CREATE_ADMIN_ISSUED_VIRTUAL_CARD: 'CreateAdminIssuedVirtualCard', + QUEUE_EXPENSIFY_CARD_FOR_BILLING: 'Domain_QueueExpensifyCardForBilling', ADD_DELEGATE: 'AddDelegate', REMOVE_DELEGATE: 'RemoveDelegate', UPDATE_DELEGATE_ROLE: 'UpdateDelegateRole', @@ -873,6 +874,7 @@ type WriteCommandParameters = { [WRITE_COMMANDS.CONFIGURE_EXPENSIFY_CARDS_FOR_POLICY]: Parameters.ConfigureExpensifyCardsForPolicyParams; [WRITE_COMMANDS.CREATE_EXPENSIFY_CARD]: Omit; [WRITE_COMMANDS.CREATE_ADMIN_ISSUED_VIRTUAL_CARD]: Omit; + [WRITE_COMMANDS.QUEUE_EXPENSIFY_CARD_FOR_BILLING]: Parameters.QueueExpensifyCardForBillingParams; [WRITE_COMMANDS.ADD_DELEGATE]: Parameters.AddDelegateParams; [WRITE_COMMANDS.UPDATE_DELEGATE_ROLE]: Parameters.UpdateDelegateRoleParams; [WRITE_COMMANDS.REMOVE_DELEGATE]: Parameters.RemoveDelegateParams; diff --git a/src/libs/actions/Card.ts b/src/libs/actions/Card.ts index 8c784029e3ca1..9c56e5c2021be 100644 --- a/src/libs/actions/Card.ts +++ b/src/libs/actions/Card.ts @@ -897,6 +897,15 @@ function updateSelectedFeed(feed: CompanyCardFeed, policyID: string | undefined) ]); } +function queueExpensifyCardForBilling(feedCountry: string, domainAccountID: number) { + const parameters = { + feedCountry, + domainAccountID, + }; + + API.write(WRITE_COMMANDS.QUEUE_EXPENSIFY_CARD_FOR_BILLING, parameters); +} + export { requestReplacementExpensifyCard, activatePhysicalExpensifyCard, @@ -920,5 +929,6 @@ export { updateSelectedFeed, deactivateCard, getCardDefaultName, + queueExpensifyCardForBilling, }; export type {ReplacementReason}; diff --git a/src/pages/workspace/expensifyCard/WorkspaceCardListHeader.tsx b/src/pages/workspace/expensifyCard/WorkspaceCardListHeader.tsx index e9aac5685dc1b..bac9cfd387dda 100644 --- a/src/pages/workspace/expensifyCard/WorkspaceCardListHeader.tsx +++ b/src/pages/workspace/expensifyCard/WorkspaceCardListHeader.tsx @@ -1,11 +1,13 @@ import React from 'react'; import {View} from 'react-native'; import {useOnyx} from 'react-native-onyx'; +import FormHelpMessage from '@components/FormHelpMessage'; import Text from '@components/Text'; import useLocalize from '@hooks/useLocalize'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useThemeStyles from '@hooks/useThemeStyles'; -import * as PolicyUtils from '@libs/PolicyUtils'; +import {getLatestErrorMessage} from '@libs/ErrorUtils'; +import {getWorkspaceAccountID} from '@libs/PolicyUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import WorkspaceCardsListLabel from './WorkspaceCardsListLabel'; @@ -21,15 +23,55 @@ function WorkspaceCardListHeader({policyID}: WorkspaceCardListHeaderProps) { const styles = useThemeStyles(); const {translate} = useLocalize(); - const workspaceAccountID = PolicyUtils.getWorkspaceAccountID(policyID); + const workspaceAccountID = getWorkspaceAccountID(policyID); const isLessThanMediumScreen = isMediumScreenWidth || isSmallScreenWidth; const [cardSettings] = useOnyx(`${ONYXKEYS.COLLECTION.PRIVATE_EXPENSIFY_CARD_SETTINGS}${workspaceAccountID}`); + const [cardManualBilling] = useOnyx(`${ONYXKEYS.COLLECTION.PRIVATE_EXPENSIFY_CARD_MANUAL_BILLING}${workspaceAccountID}`); - return ( - - - + const errorMessage = getLatestErrorMessage(cardSettings) ?? ''; + + const shouldShowSettlementButtonOrDate = !!cardSettings?.isMonthlySettlementAllowed || cardManualBilling; + + const getLabelsLayout = () => { + if (!isLessThanMediumScreen) { + return ( + <> + + + + + ); + } + return shouldShowSettlementButtonOrDate ? ( + <> + + + + + + + ) : ( + <> + - + + ); + }; - + return ( + + {getLabelsLayout()} + {!!errorMessage && ( + + + + )} + policy?.outputCurrency ?? CONST.CURRENCY.USD, [policy]); const [cardSettings] = useOnyx(`${ONYXKEYS.COLLECTION.PRIVATE_EXPENSIFY_CARD_SETTINGS}${workspaceAccountID}`); + const [cardManualBilling] = useOnyx(`${ONYXKEYS.COLLECTION.PRIVATE_EXPENSIFY_CARD_MANUAL_BILLING}${workspaceAccountID}`); const paymentBankAccountID = cardSettings?.paymentBankAccountID; + const isLessThanMediumScreen = isMediumScreenWidth || shouldUseNarrowLayout; + const isConnectedWithPlaid = useMemo(() => { - const bankAccountData = bankAccountList?.[paymentBankAccountID ?? 0]?.accountData; + const bankAccountData = bankAccountList?.[paymentBankAccountID ?? CONST.DEFAULT_NUMBER_ID]?.accountData; // TODO: remove the extra check when plaidAccountID storing is aligned in https://github.com/Expensify/App/issues/47944 // Right after adding a bank account plaidAccountID is stored inside the accountData and not in the additionalData @@ -80,34 +86,57 @@ function WorkspaceCardsListLabel({type, value, style}: WorkspaceCardsListLabelPr }, [isVisible, windowWidth]); const requestLimitIncrease = () => { - Policy.requestExpensifyCardLimitIncrease(cardSettings?.paymentBankAccountID); + requestExpensifyCardLimitIncrease(cardSettings?.paymentBankAccountID); setVisible(false); - Report.navigateToConciergeChat(); + navigateToConciergeChat(); + }; + + const isCurrentBalanceType = type === CONST.WORKSPACE_CARDS_LIST_LABEL_TYPE.CURRENT_BALANCE; + const isSettleBalanceButtonDisplayed = !!cardSettings?.isMonthlySettlementAllowed && !cardManualBilling && isCurrentBalanceType; + const isSettleDateTextDisplayed = !!cardManualBilling && isCurrentBalanceType; + + const settlementDate = isSettleDateTextDisplayed ? format(addDays(new Date(), 1), CONST.DATE.FNS_FORMAT_STRING) : ''; + + const handleSettleBalanceButtonClick = () => { + queueExpensifyCardForBilling(CONST.COUNTRY.US, workspaceAccountID); }; return ( - - {translate(`workspace.expensifyCard.${type}`)} - setVisible(true)} + + - - + {translate(`workspace.expensifyCard.${type}`)} + setVisible(true)} + > + + + + + {convertToDisplayString(value, policyCurrency)} + {isSettleBalanceButtonDisplayed && ( + +