Skip to content

Commit

Permalink
Merge pull request #40015 from VickyStash/feature/global-send-invoice
Browse files Browse the repository at this point in the history
Implement Send Invoice flow from Global Create
  • Loading branch information
cristipaval authored Apr 29, 2024
2 parents 6a37cd8 + 4371082 commit 84b7ce7
Show file tree
Hide file tree
Showing 34 changed files with 970 additions and 37 deletions.
15 changes: 15 additions & 0 deletions assets/images/invoice-generic.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 5 additions & 0 deletions src/ROUTES.ts
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,11 @@ const ROUTES = {
route: ':action/:iouType/start/:transactionID/:reportID',
getRoute: (action: IOUAction, iouType: IOUType, transactionID: string, reportID: string) => `${action as string}/${iouType as string}/start/${transactionID}/${reportID}` as const,
},
MONEY_REQUEST_STEP_SEND_FROM: {
route: 'create/:iouType/from/:transactionID/:reportID',
getRoute: (iouType: ValueOf<typeof CONST.IOU.TYPE>, transactionID: string, reportID: string, backTo = '') =>
getUrlWithBackToParam(`create/${iouType as string}/from/${transactionID}/${reportID}`, backTo),
},
MONEY_REQUEST_STEP_CONFIRMATION: {
route: ':action/:iouType/confirmation/:transactionID/:reportID',
getRoute: (action: IOUAction, iouType: IOUType, transactionID: string, reportID: string) =>
Expand Down
1 change: 1 addition & 0 deletions src/SCREENS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ const SCREENS = {
STEP_WAYPOINT: 'Money_Request_Step_Waypoint',
STEP_TAX_AMOUNT: 'Money_Request_Step_Tax_Amount',
STEP_TAX_RATE: 'Money_Request_Step_Tax_Rate',
STEP_SEND_FROM: 'Money_Request_Step_Send_From',
CURRENCY: 'Money_Request_Currency',
WAYPOINT: 'Money_Request_Waypoint',
EDIT_WAYPOINT: 'Money_Request_Edit_Waypoint',
Expand Down
2 changes: 2 additions & 0 deletions src/components/Icon/Expensicons.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ import ImageCropSquareMask from '@assets/images/image-crop-square-mask.svg';
import Info from '@assets/images/info.svg';
import QBOSquare from '@assets/images/integrationicons/qbo-icon-square.svg';
import XeroSquare from '@assets/images/integrationicons/xero-icon-square.svg';
import InvoiceGeneric from '@assets/images/invoice-generic.svg';
import Invoice from '@assets/images/invoice.svg';
import Key from '@assets/images/key.svg';
import Keyboard from '@assets/images/keyboard.svg';
Expand Down Expand Up @@ -250,6 +251,7 @@ export {
ImageCropSquareMask,
Info,
Invoice,
InvoiceGeneric,
Key,
Keyboard,
Link,
Expand Down
1 change: 1 addition & 0 deletions src/components/LHNOptionsList/OptionRowLHN.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,7 @@ function OptionRowLHN({reportID, isFocused = false, onSelectRow = () => {}, opti
!!optionItem.isTaskReport ||
!!optionItem.isThread ||
!!optionItem.isMoneyRequestReport ||
!!optionItem.isInvoiceReport ||
ReportUtils.isGroupChat(report)
}
/>
Expand Down
8 changes: 6 additions & 2 deletions src/components/MenuItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,9 @@ type MenuItemBaseProps = {
/** Any additional styles to apply on the badge element */
badgeStyle?: ViewStyle;

/** Any additional styles to apply to the label */
labelStyle?: StyleProp<ViewStyle>;

/** Any adjustments to style when menu item is hovered or pressed */
hoverAndPressStyle?: StyleProp<AnimatedStyle<ViewStyle>>;

Expand Down Expand Up @@ -267,6 +270,7 @@ function MenuItem(
outerWrapperStyle,
containerStyle,
titleStyle,
labelStyle,
hoverAndPressStyle,
descriptionTextStyle,
badgeStyle,
Expand Down Expand Up @@ -424,7 +428,7 @@ function MenuItem(
return (
<View>
{!!label && !isLabelHoverable && (
<View style={[styles.ph5]}>
<View style={[styles.ph5, labelStyle]}>
<Text style={StyleUtils.combineStyles([styles.sidebarLinkText, styles.optionAlternateText, styles.textLabelSupporting, styles.pre])}>{label}</Text>
</View>
)}
Expand Down Expand Up @@ -460,7 +464,7 @@ function MenuItem(
<>
<View style={[styles.flexColumn, styles.flex1]}>
{!!label && isLabelHoverable && (
<View style={icon ? styles.mb2 : null}>
<View style={[icon ? styles.mb2 : null, labelStyle]}>
<Text style={StyleUtils.combineStyles([styles.sidebarLinkText, styles.optionAlternateText, styles.textLabelSupporting, styles.pre])}>
{label}
</Text>
Expand Down
2 changes: 1 addition & 1 deletion src/components/MoneyReportHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ function MoneyReportHeader({

const shouldDisableApproveButton = shouldShowApproveButton && !ReportUtils.isAllowedToApproveExpenseReport(moneyRequestReport);

const shouldShowSettlementButton = shouldShowPayButton || shouldShowApproveButton;
const shouldShowSettlementButton = !ReportUtils.isInvoiceReport(moneyRequestReport) && (shouldShowPayButton || shouldShowApproveButton);

const shouldShowSubmitButton = isDraft && reimbursableSpend !== 0;
const shouldDisableSubmitButton = shouldShowSubmitButton && !ReportUtils.isAllowedToSubmitDraftExpenseReport(moneyRequestReport);
Expand Down
45 changes: 42 additions & 3 deletions src/components/MoneyRequestConfirmationList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import React, {useCallback, useEffect, useMemo, useReducer, useState} from 'reac
import {View} from 'react-native';
import type {StyleProp, ViewStyle} from 'react-native';
import {withOnyx} from 'react-native-onyx';
import type {OnyxEntry} from 'react-native-onyx';
import type {OnyxCollection, OnyxEntry} from 'react-native-onyx';
import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails';
import useLocalize from '@hooks/useLocalize';
import useNetwork from '@hooks/useNetwork';
Expand All @@ -25,6 +25,7 @@ import * as PolicyUtils from '@libs/PolicyUtils';
import {isTaxTrackingEnabled} from '@libs/PolicyUtils';
import * as ReceiptUtils from '@libs/ReceiptUtils';
import * as ReportUtils from '@libs/ReportUtils';
import {getDefaultWorkspaceAvatar} from '@libs/ReportUtils';
import playSound, {SOUNDS} from '@libs/Sound';
import * as TransactionUtils from '@libs/TransactionUtils';
import tryResolveUrlFromApiRoot from '@libs/tryResolveUrlFromApiRoot';
Expand All @@ -42,6 +43,7 @@ import type {DropdownOption} from './ButtonWithDropdownMenu/types';
import ConfirmedRoute from './ConfirmedRoute';
import ConfirmModal from './ConfirmModal';
import FormHelpMessage from './FormHelpMessage';
import MenuItem from './MenuItem';
import MenuItemWithTopDescription from './MenuItemWithTopDescription';
import OptionsSelector from './OptionsSelector';
import PDFThumbnail from './PDFThumbnail';
Expand Down Expand Up @@ -73,6 +75,9 @@ type MoneyRequestConfirmationListOnyxProps = {

/** Last selected distance rates */
lastSelectedDistanceRates: OnyxEntry<Record<string, string>>;

/** The list of all policies */
allPolicies: OnyxCollection<OnyxTypes.Policy>;
};

type MoneyRequestConfirmationListProps = MoneyRequestConfirmationListOnyxProps & {
Expand Down Expand Up @@ -217,6 +222,7 @@ function MoneyRequestConfirmationList({
reportActionID,
defaultMileageRate,
lastSelectedDistanceRates,
allPolicies,
action = CONST.IOU.ACTION.CREATE,
}: MoneyRequestConfirmationListProps) {
const theme = useTheme();
Expand All @@ -230,6 +236,7 @@ function MoneyRequestConfirmationList({
const isTypeSplit = iouType === CONST.IOU.TYPE.SPLIT;
const isTypeSend = iouType === CONST.IOU.TYPE.PAY;
const isTypeTrackExpense = iouType === CONST.IOU.TYPE.TRACK;
const isTypeInvoice = iouType === CONST.IOU.TYPE.INVOICE;

const transactionID = transaction?.transactionID ?? '';
const customUnitRateID = TransactionUtils.getRateID(transaction) ?? '';
Expand Down Expand Up @@ -275,6 +282,13 @@ function MoneyRequestConfirmationList({

const policyTagLists = useMemo(() => PolicyUtils.getTagLists(policyTags), [policyTags]);

const senderWorkspace = useMemo(() => {
const senderWorkspaceParticipant = selectedParticipantsProp.find((participant) => participant.isSender);
return allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${senderWorkspaceParticipant?.policyID}`];
}, [allPolicies, selectedParticipantsProp]);

const canUpdateSenderWorkspace = useMemo(() => PolicyUtils.canSendInvoice(allPolicies) && !!transaction?.isFromGlobalCreate, [allPolicies, transaction?.isFromGlobalCreate]);

// A flag for showing the tags field
const shouldShowTags = useMemo(() => isPolicyExpenseChat && OptionsListUtils.hasEnabledTags(policyTagLists), [isPolicyExpenseChat, policyTagLists]);

Expand Down Expand Up @@ -376,7 +390,9 @@ function MoneyRequestConfirmationList({

const splitOrRequestOptions: Array<DropdownOption<string>> = useMemo(() => {
let text;
if (isTypeTrackExpense) {
if (isTypeInvoice) {
text = translate('iou.sendInvoice', {amount: formattedAmount});
} else if (isTypeTrackExpense) {
text = translate('iou.trackExpense');
} else if (isTypeSplit && iouAmount === 0) {
text = translate('iou.splitExpense');
Expand All @@ -395,7 +411,7 @@ function MoneyRequestConfirmationList({
value: iouType,
},
];
}, [isTypeTrackExpense, isTypeSplit, iouAmount, receiptPath, isTypeRequest, isDistanceRequestWithPendingRoute, iouType, translate, formattedAmount]);
}, [isTypeTrackExpense, isTypeSplit, iouAmount, receiptPath, isTypeRequest, isDistanceRequestWithPendingRoute, iouType, translate, formattedAmount, isTypeInvoice]);

const selectedParticipants = useMemo(() => selectedParticipantsProp.filter((participant) => participant.selected), [selectedParticipantsProp]);
const payeePersonalDetails = useMemo(() => payeePersonalDetailsProp ?? currentUserPersonalDetails, [payeePersonalDetailsProp, currentUserPersonalDetails]);
Expand Down Expand Up @@ -984,6 +1000,26 @@ function MoneyRequestConfirmationList({
<ConfirmedRoute transaction={transaction ?? ({} as OnyxTypes.Transaction)} />
</View>
)}
{isTypeInvoice && (
<MenuItem
key={translate('workspace.invoices.sendFrom')}
shouldShowRightIcon={!isReadOnly && canUpdateSenderWorkspace}
title={senderWorkspace?.name}
icon={senderWorkspace?.avatar ? senderWorkspace?.avatar : getDefaultWorkspaceAvatar(senderWorkspace?.name)}
iconType={CONST.ICON_TYPE_WORKSPACE}
description={translate('workspace.common.workspace')}
label={translate('workspace.invoices.sendFrom')}
isLabelHoverable={false}
interactive={!isReadOnly && canUpdateSenderWorkspace}
onPress={() => {
Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_SEND_FROM.getRoute(iouType, transaction?.transactionID ?? '', reportID, Navigation.getActiveRouteWithoutParams()));
}}
style={styles.moneyRequestMenuItem}
labelStyle={styles.mt2}
titleStyle={styles.flex1}
disabled={didConfirm || !canUpdateSenderWorkspace}
/>
)}
{!isDistanceRequest &&
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
(receiptImage || receiptThumbnail
Expand Down Expand Up @@ -1047,4 +1083,7 @@ export default withOnyx<MoneyRequestConfirmationListProps, MoneyRequestConfirmat
lastSelectedDistanceRates: {
key: ONYXKEYS.NVP_LAST_SELECTED_DISTANCE_RATES,
},
allPolicies: {
key: ONYXKEYS.COLLECTION.POLICY,
},
})(MoneyRequestConfirmationList);
2 changes: 1 addition & 1 deletion src/components/ReportActionItem/ReportPreview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ function ReportPreview({

const shouldDisableApproveButton = shouldShowApproveButton && !ReportUtils.isAllowedToApproveExpenseReport(iouReport);

const shouldShowSettlementButton = shouldShowPayButton || shouldShowApproveButton;
const shouldShowSettlementButton = !ReportUtils.isInvoiceReport(iouReport) && (shouldShowPayButton || shouldShowApproveButton);

const shouldPromptUserToAddBankAccount = ReportUtils.hasMissingPaymentMethod(userWallet, iouReportID);
const shouldShowRBR = !iouSettled && hasErrors;
Expand Down
6 changes: 5 additions & 1 deletion src/components/ReportWelcomeText.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import * as OptionsListUtils from '@libs/OptionsListUtils';
import * as PolicyUtils from '@libs/PolicyUtils';
import * as ReportUtils from '@libs/ReportUtils';
import CONST from '@src/CONST';
import type {IOUType} from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
import type {PersonalDetailsList, Policy, Report} from '@src/types/onyx';
Expand Down Expand Up @@ -46,7 +47,10 @@ function ReportWelcomeText({report, policy, personalDetails}: ReportWelcomeTextP
const isUserPolicyAdmin = PolicyUtils.isPolicyAdmin(policy);
const roomWelcomeMessage = ReportUtils.getRoomWelcomeMessage(report, isUserPolicyAdmin);
const moneyRequestOptions = ReportUtils.temporary_getMoneyRequestOptions(report, policy, participantAccountIDs, canUseTrackExpense);
const additionalText = moneyRequestOptions.map((item) => translate(`reportActionsView.iouTypes.${item}`)).join(', ');
const additionalText = moneyRequestOptions
.filter((item): item is Exclude<IOUType, typeof CONST.IOU.TYPE.REQUEST | typeof CONST.IOU.TYPE.SEND | typeof CONST.IOU.TYPE.INVOICE> => item !== CONST.IOU.TYPE.INVOICE)
.map((item) => translate(`reportActionsView.iouTypes.${item}`))
.join(', ');
const canEditPolicyDescription = ReportUtils.canEditPolicyDescription(policy);
const reportName = ReportUtils.getReportName(report);

Expand Down
3 changes: 3 additions & 0 deletions src/languages/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -657,6 +657,7 @@ export default {
payElsewhere: ({formattedAmount}: SettleExpensifyCardParams) => (formattedAmount ? `Pay ${formattedAmount} elsewhere` : `Pay elsewhere`),
nextStep: 'Next Steps',
finished: 'Finished',
sendInvoice: ({amount}: RequestAmountParams) => `Send ${amount} invoice`,
submitAmount: ({amount}: RequestAmountParams) => `submit ${amount}`,
trackAmount: ({amount}: RequestAmountParams) => `track ${amount}`,
submittedAmount: ({formattedAmount, comment}: RequestedAmountMessageParams) => `submitted ${formattedAmount}${comment ? ` for ${comment}` : ''}`,
Expand Down Expand Up @@ -703,6 +704,7 @@ export default {
invalidSplit: 'Split amounts do not equal total amount',
other: 'Unexpected error, please try again later',
genericCreateFailureMessage: 'Unexpected error submitting this expense. Please try again later.',
genericCreateInvoiceFailureMessage: 'Unexpected error sending invoice, please try again later',
receiptFailureMessage: "The receipt didn't upload. ",
saveFileMessage: 'Download the file ',
loseFileMessage: 'or dismiss this error and lose it',
Expand Down Expand Up @@ -2238,6 +2240,7 @@ export default {
unlockVBACopy: "You're all set to accept payments by ACH or credit card!",
viewUnpaidInvoices: 'View unpaid invoices',
sendInvoice: 'Send invoice',
sendFrom: 'Send from',
},
travel: {
unlockConciergeBookingTravel: 'Unlock Concierge travel booking',
Expand Down
3 changes: 3 additions & 0 deletions src/languages/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -650,6 +650,7 @@ export default {
payElsewhere: ({formattedAmount}: SettleExpensifyCardParams) => (formattedAmount ? `Pagar ${formattedAmount} de otra forma` : `Pagar de otra forma`),
nextStep: 'Pasos Siguientes',
finished: 'Finalizado',
sendInvoice: ({amount}: RequestAmountParams) => `Enviar factura de ${amount}`,
submitAmount: ({amount}: RequestAmountParams) => `solicitar ${amount}`,
trackAmount: ({amount}: RequestAmountParams) => `seguimiento de ${amount}`,
submittedAmount: ({formattedAmount, comment}: RequestedAmountMessageParams) => `solicitó ${formattedAmount}${comment ? ` para ${comment}` : ''}`,
Expand Down Expand Up @@ -698,6 +699,7 @@ export default {
invalidSplit: 'La suma de las partes no equivale al importe total',
other: 'Error inesperado, por favor inténtalo más tarde',
genericCreateFailureMessage: 'Error inesperado al enviar este gasto. Por favor, inténtalo más tarde.',
genericCreateInvoiceFailureMessage: 'Error inesperado al enviar la factura, inténtalo de nuevo más tarde',
receiptFailureMessage: 'El recibo no se subió. ',
saveFileMessage: 'Guarda el archivo ',
loseFileMessage: 'o descarta este error y piérdelo',
Expand Down Expand Up @@ -2266,6 +2268,7 @@ export default {
unlockVBACopy: '¡Todo listo para recibir pagos por transferencia o con tarjeta!',
viewUnpaidInvoices: 'Ver facturas emitidas pendientes',
sendInvoice: 'Enviar factura',
sendFrom: 'Enviar desde',
},
travel: {
unlockConciergeBookingTravel: 'Desbloquea la reserva de viajes con Concierge',
Expand Down
19 changes: 19 additions & 0 deletions src/libs/API/parameters/SendInvoiceParams.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
type SendInvoiceParams = {
senderWorkspaceID: string;
accountID: number;
receiverEmail?: string;
receiverInvoiceRoomID?: string;
amount: number;
currency: string;
merchant: string;
date: string;
category?: string;
invoiceRoomReportID?: string;
createdChatReportActionID: string;
invoiceReportID: string;
reportPreviewReportActionID: string;
transactionID: string;
transactionThreadReportID: string;
};

export default SendInvoiceParams;
1 change: 1 addition & 0 deletions src/libs/API/parameters/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -217,3 +217,4 @@ export type {default as CategorizeTrackedExpenseParams} from './CategorizeTracke
export type {default as LeavePolicyParams} from './LeavePolicyParams';
export type {default as OpenPolicyAccountingPageParams} from './OpenPolicyAccountingPageParams';
export type {default as SearchParams} from './Search';
export type {default as SendInvoiceParams} from './SendInvoiceParams';
2 changes: 2 additions & 0 deletions src/libs/API/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,7 @@ const WRITE_COMMANDS = {
CATEGORIZE_TRACKED_EXPENSE: 'CategorizeTrackedExpense',
SHARE_TRACKED_EXPENSE: 'ShareTrackedExpense',
LEAVE_POLICY: 'LeavePolicy',
SEND_INVOICE: 'SendInvoice',
} as const;

type WriteCommand = ValueOf<typeof WRITE_COMMANDS>;
Expand Down Expand Up @@ -424,6 +425,7 @@ type WriteCommandParameters = {
[WRITE_COMMANDS.CATEGORIZE_TRACKED_EXPENSE]: Parameters.CategorizeTrackedExpenseParams;
[WRITE_COMMANDS.SHARE_TRACKED_EXPENSE]: Parameters.ShareTrackedExpenseParams;
[WRITE_COMMANDS.LEAVE_POLICY]: Parameters.LeavePolicyParams;
[WRITE_COMMANDS.SEND_INVOICE]: Parameters.SendInvoiceParams;
};

const READ_COMMANDS = {
Expand Down
Loading

0 comments on commit 84b7ce7

Please sign in to comment.