From 457af022b6017a5ee7efa9d2584b7a8f8c57fbf2 Mon Sep 17 00:00:00 2001 From: Monil Bhavsar Date: Wed, 29 May 2024 11:30:55 +0530 Subject: [PATCH 1/9] Revert "Revert "Handle tax for split requests"" --- .../MoneyRequestConfirmationList.tsx | 63 ++++++++++--------- src/libs/API/parameters/SplitBillParams.ts | 2 + src/libs/actions/IOU.ts | 29 +++++++-- .../iou/request/step/IOURequestStepAmount.tsx | 35 ++++------- .../step/IOURequestStepConfirmation.tsx | 10 ++- .../step/IOURequestStepParticipants.tsx | 4 +- .../step/IOURequestStepTaxAmountPage.tsx | 28 +++++++-- .../step/IOURequestStepTaxRatePage.tsx | 42 ++++++++++--- src/types/onyx/IOU.ts | 2 + 9 files changed, 143 insertions(+), 72 deletions(-) diff --git a/src/components/MoneyRequestConfirmationList.tsx b/src/components/MoneyRequestConfirmationList.tsx index 27a2fa6cdb55..faca0a8fba08 100755 --- a/src/components/MoneyRequestConfirmationList.tsx +++ b/src/components/MoneyRequestConfirmationList.tsx @@ -178,11 +178,15 @@ type MoneyRequestConfirmationListProps = MoneyRequestConfirmationListOnyxProps & type MoneyRequestConfirmationListItem = Participant | ReportUtils.OptionData; -const getTaxAmount = (transaction: OnyxEntry, policy: OnyxEntry) => { +/** + * Calculate and set tax amount in transaction draft + */ +const setTaxAmountInDraft = (transaction: OnyxEntry, policy: OnyxEntry) => { const defaultTaxCode = TransactionUtils.getDefaultTaxCode(policy, transaction) ?? ''; - const taxPercentage = TransactionUtils.getTaxValue(policy, transaction, transaction?.taxCode ?? defaultTaxCode) ?? ''; - return TransactionUtils.calculateTaxAmount(taxPercentage, transaction?.amount ?? 0); + const taxAmount = TransactionUtils.calculateTaxAmount(taxPercentage, transaction?.amount ?? 0); + const taxAmountInSmallestCurrencyUnits = CurrencyUtils.convertToBackendAmount(Number.parseFloat(taxAmount.toString())); + IOU.setMoneyRequestTaxAmount(transaction?.transactionID ?? '', taxAmountInSmallestCurrencyUnits, true); }; function MoneyRequestConfirmationList({ @@ -244,16 +248,6 @@ function MoneyRequestConfirmationList({ const transactionID = transaction?.transactionID ?? ''; const customUnitRateID = TransactionUtils.getRateID(transaction) ?? ''; - useEffect(() => { - if (customUnitRateID || !canUseP2PDistanceRequests) { - return; - } - if (!customUnitRateID) { - const rateID = lastSelectedDistanceRates?.[policy?.id ?? ''] ?? defaultMileageRate?.customUnitRateID ?? ''; - IOU.setCustomUnitRateID(transactionID, rateID); - } - }, [defaultMileageRate, customUnitRateID, lastSelectedDistanceRates, policy?.id, canUseP2PDistanceRequests, transactionID]); - const policyCurrency = policy?.outputCurrency ?? PolicyUtils.getPersonalPolicy()?.outputCurrency ?? CONST.CURRENCY.USD; const mileageRate = TransactionUtils.isCustomUnitRateIDForP2P(transaction) @@ -314,9 +308,9 @@ function MoneyRequestConfirmationList({ ); const formattedTaxAmount = CurrencyUtils.convertToDisplayString(transaction?.taxAmount, iouCurrencyCode); const taxRateTitle = TransactionUtils.getTaxName(policy, transaction); - - const previousTransactionAmount = usePrevious(transaction?.amount); - const previousTransactionCurrency = usePrevious(transaction?.currency); + const prevTaxAmount = usePrevious(transaction?.taxAmount); + const prevAmount = usePrevious(transaction?.amount); + const prevCurrency = usePrevious(transaction?.currency); const isFocused = useIsFocused(); const [formError, debouncedFormError, setFormError] = useDebouncedState(''); @@ -345,6 +339,31 @@ function MoneyRequestConfirmationList({ const isCategoryRequired = !!policy?.requiresCategory; + useEffect(() => { + if (!shouldShowTax) { + return; + } + setTaxAmountInDraft(transaction, policy); + // eslint-disable-next-line react-hooks/exhaustive-deps -- we want to call this function when component is mounted + }, []); + + useEffect(() => { + if (!shouldShowTax || prevTaxAmount !== transaction?.taxAmount || (prevAmount === transaction?.amount && prevCurrency === transaction?.currency)) { + return; + } + setTaxAmountInDraft(transaction, policy); + }, [policy, shouldShowTax, prevTaxAmount, prevAmount, prevCurrency, transaction]); + + useEffect(() => { + if (customUnitRateID || !canUseP2PDistanceRequests) { + return; + } + if (!customUnitRateID) { + const rateID = lastSelectedDistanceRates?.[policy?.id ?? ''] ?? defaultMileageRate?.customUnitRateID ?? ''; + IOU.setCustomUnitRateID(transactionID, rateID); + } + }, [defaultMileageRate, customUnitRateID, lastSelectedDistanceRates, policy?.id, canUseP2PDistanceRequests, transactionID]); + useEffect(() => { if (shouldDisplayFieldError && hasSmartScanFailed) { setFormError('iou.receiptScanningFailed'); @@ -369,18 +388,6 @@ function MoneyRequestConfirmationList({ IOU.setMoneyRequestAmount(transactionID, amount, currency ?? ''); }, [shouldCalculateDistanceAmount, distance, rate, unit, transactionID, currency]); - // Calculate and set tax amount in transaction draft - useEffect(() => { - const taxAmount = getTaxAmount(transaction, policy).toString(); - const amountInSmallestCurrencyUnits = CurrencyUtils.convertToBackendAmount(Number.parseFloat(taxAmount)); - - if (transaction?.taxAmount && previousTransactionAmount === transaction?.amount && previousTransactionCurrency === transaction?.currency) { - return IOU.setMoneyRequestTaxAmount(transactionID, transaction?.taxAmount ?? 0, true); - } - - IOU.setMoneyRequestTaxAmount(transactionID, amountInSmallestCurrencyUnits, true); - }, [policy, transaction, transactionID, previousTransactionAmount, previousTransactionCurrency]); - // If completing a split expense fails, set didConfirm to false to allow the user to edit the fields again if (isEditingSplitBill && didConfirm) { setDidConfirm(false); diff --git a/src/libs/API/parameters/SplitBillParams.ts b/src/libs/API/parameters/SplitBillParams.ts index dd5a88141222..76252abe3292 100644 --- a/src/libs/API/parameters/SplitBillParams.ts +++ b/src/libs/API/parameters/SplitBillParams.ts @@ -15,6 +15,8 @@ type SplitBillParams = { policyID: string | undefined; chatType: string | undefined; splitPayerAccountIDs: number[]; + taxCode: string; + taxAmount: number; }; export default SplitBillParams; diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index a08be2ab3d4b..1a1ad7e3c7e7 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -3753,6 +3753,8 @@ function createSplitsAndOnyxData( existingSplitChatReportID = '', billable = false, iouRequestType: IOURequestType = CONST.IOU.REQUEST_TYPE.MANUAL, + taxCode = '', + taxAmount = 0, ): SplitsAndOnyxData { const currentUserEmailForIOUSplit = PhoneNumber.addSMSDomainIfPhoneNumber(currentUserLogin); const participantAccountIDs = participants.map((participant) => Number(participant.accountID)); @@ -3774,8 +3776,8 @@ function createSplitsAndOnyxData( undefined, category, tag, - undefined, - undefined, + taxCode, + taxAmount, billable, ); @@ -3930,14 +3932,16 @@ function createSplitsAndOnyxData( // Loop through participants creating individual chats, iouReports and reportActionIDs as needed const currentUserAmount = splitShares?.[currentUserAccountID]?.amount ?? IOUUtils.calculateAmount(participants.length, amount, currency, true); + const currentUserTaxAmount = IOUUtils.calculateAmount(participants.length, taxAmount, currency, true); - const splits: Split[] = [{email: currentUserEmailForIOUSplit, accountID: currentUserAccountID, amount: currentUserAmount}]; + const splits: Split[] = [{email: currentUserEmailForIOUSplit, accountID: currentUserAccountID, amount: currentUserAmount, taxAmount: currentUserTaxAmount}]; const hasMultipleParticipants = participants.length > 1; participants.forEach((participant) => { // In a case when a participant is a workspace, even when a current user is not an owner of the workspace const isPolicyExpenseChat = ReportUtils.isPolicyExpenseChat(participant); const splitAmount = splitShares?.[participant.accountID ?? -1]?.amount ?? IOUUtils.calculateAmount(participants.length, amount, currency, false); + const splitTaxAmount = IOUUtils.calculateAmount(participants.length, taxAmount, currency, false); // To exclude someone from a split, the amount can be 0. The scenario for this is when creating a split from a group chat, we have remove the option to deselect users to exclude them. // We can input '0' next to someone we want to exclude. @@ -4007,8 +4011,8 @@ function createSplitsAndOnyxData( undefined, category, tag, - undefined, - undefined, + taxCode, + ReportUtils.isExpenseReport(oneOnOneIOUReport) ? -splitTaxAmount : splitTaxAmount, billable, ); @@ -4099,6 +4103,7 @@ function createSplitsAndOnyxData( reportPreviewReportActionID: oneOnOneReportPreviewAction.reportActionID, transactionThreadReportID: optimisticTransactionThread.reportID, createdReportActionIDForThread: optimisticCreatedActionForTransactionThread.reportActionID, + taxAmount: splitTaxAmount, }; splits.push(individualSplit); @@ -4152,6 +4157,8 @@ type SplitBillActionsParams = { existingSplitChatReportID?: string; splitShares?: SplitShares; splitPayerAccountIDs?: number[]; + taxCode?: string; + taxAmount?: number; }; /** @@ -4174,6 +4181,8 @@ function splitBill({ existingSplitChatReportID = '', splitShares = {}, splitPayerAccountIDs = [], + taxCode = '', + taxAmount = 0, }: SplitBillActionsParams) { const currentCreated = DateUtils.enrichMoneyRequestTimestamp(created); const {splitData, splits, onyxData} = createSplitsAndOnyxData( @@ -4191,6 +4200,8 @@ function splitBill({ existingSplitChatReportID, billable, iouRequestType, + taxCode, + taxAmount, ); const parameters: SplitBillParams = { @@ -4210,6 +4221,8 @@ function splitBill({ policyID: splitData.policyID, chatType: splitData.chatType, splitPayerAccountIDs, + taxCode, + taxAmount, }; API.write(WRITE_COMMANDS.SPLIT_BILL, parameters, onyxData); @@ -4236,6 +4249,8 @@ function splitBillAndOpenReport({ iouRequestType = CONST.IOU.REQUEST_TYPE.MANUAL, splitShares = {}, splitPayerAccountIDs = [], + taxCode = '', + taxAmount = 0, }: SplitBillActionsParams) { const currentCreated = DateUtils.enrichMoneyRequestTimestamp(created); const {splitData, splits, onyxData} = createSplitsAndOnyxData( @@ -4253,6 +4268,8 @@ function splitBillAndOpenReport({ '', billable, iouRequestType, + taxCode, + taxAmount, ); const parameters: SplitBillParams = { @@ -4272,6 +4289,8 @@ function splitBillAndOpenReport({ policyID: splitData.policyID, chatType: splitData.chatType, splitPayerAccountIDs, + taxCode, + taxAmount, }; API.write(WRITE_COMMANDS.SPLIT_BILL_AND_OPEN_REPORT, parameters, onyxData); diff --git a/src/pages/iou/request/step/IOURequestStepAmount.tsx b/src/pages/iou/request/step/IOURequestStepAmount.tsx index 18dc951f949b..4ae2784f61b1 100644 --- a/src/pages/iou/request/step/IOURequestStepAmount.tsx +++ b/src/pages/iou/request/step/IOURequestStepAmount.tsx @@ -39,9 +39,6 @@ type IOURequestStepAmountOnyxProps = { /** Whether the confirmation step should be skipped */ skipConfirmation: OnyxEntry; - /** The draft transaction object being modified in Onyx */ - draftTransaction: OnyxEntry; - /** Personal details of all users */ personalDetails: OnyxEntry; @@ -67,7 +64,6 @@ function IOURequestStepAmount({ currentUserPersonalDetails, splitDraftTransaction, skipConfirmation, - draftTransaction, }: IOURequestStepAmountProps) { const {translate} = useLocalize(); const textInput = useRef(null); @@ -78,8 +74,9 @@ function IOURequestStepAmount({ const isEditing = action === CONST.IOU.ACTION.EDIT; const isSplitBill = iouType === CONST.IOU.TYPE.SPLIT; const isEditingSplitBill = isEditing && isSplitBill; - const {amount: transactionAmount} = ReportUtils.getTransactionDetails(isEditingSplitBill && !isEmptyObject(splitDraftTransaction) ? splitDraftTransaction : transaction) ?? {amount: 0}; - const {currency: originalCurrency} = ReportUtils.getTransactionDetails(isEditing ? draftTransaction : transaction) ?? {currency: CONST.CURRENCY.USD}; + const currentTransaction = isEditingSplitBill && !isEmptyObject(splitDraftTransaction) ? splitDraftTransaction : transaction; + const {amount: transactionAmount} = ReportUtils.getTransactionDetails(currentTransaction) ?? {amount: 0}; + const {currency: originalCurrency} = ReportUtils.getTransactionDetails(currentTransaction) ?? {currency: CONST.CURRENCY.USD}; const currency = CurrencyUtils.isValidCurrencyCode(selectedCurrency) ? selectedCurrency : originalCurrency; // For quick button actions, we'll skip the confirmation page unless the report is archived or this is a workspace request, as @@ -269,25 +266,25 @@ function IOURequestStepAmount({ } // If the value hasn't changed, don't request to save changes on the server and just close the modal - const transactionCurrency = TransactionUtils.getCurrency(transaction); - if (newAmount === TransactionUtils.getAmount(transaction) && currency === transactionCurrency) { + const transactionCurrency = TransactionUtils.getCurrency(currentTransaction); + if (newAmount === TransactionUtils.getAmount(currentTransaction) && currency === transactionCurrency) { Navigation.dismissModal(); return; } + // If currency has changed, then we get the default tax rate based on currency, otherwise we use the current tax rate selected in transaction, if we have it. + const transactionTaxCode = ReportUtils.getTransactionDetails(currentTransaction)?.taxCode; + const defaultTaxCode = TransactionUtils.getDefaultTaxCode(policy, currentTransaction, currency) ?? ''; + const taxCode = (currency !== transactionCurrency ? defaultTaxCode : transactionTaxCode) ?? defaultTaxCode; + const taxPercentage = TransactionUtils.getTaxValue(policy, currentTransaction, taxCode) ?? ''; + const taxAmount = CurrencyUtils.convertToBackendAmount(TransactionUtils.calculateTaxAmount(taxPercentage, newAmount)); + if (isSplitBill) { - IOU.setDraftSplitTransaction(transactionID, {amount: newAmount, currency}); + IOU.setDraftSplitTransaction(transactionID, {amount: newAmount, currency, taxCode, taxAmount}); Navigation.goBack(backTo); return; } - // If currency has changed, then we get the default tax rate based on currency, otherwise we use the current tax rate selected in transaction, if we have it. - const transactionTaxCode = transaction?.taxCode ?? ''; - const defaultTaxCode = TransactionUtils.getDefaultTaxCode(policy, transaction, currency) ?? ''; - const taxCode = (currency !== transactionCurrency ? defaultTaxCode : transactionTaxCode) ?? defaultTaxCode; - const taxPercentage = TransactionUtils.getTaxValue(policy, transaction, taxCode) ?? ''; - const taxAmount = CurrencyUtils.convertToBackendAmount(TransactionUtils.calculateTaxAmount(taxPercentage, newAmount)); - IOU.updateMoneyRequestAmountAndCurrency({transactionID, transactionThreadReportID: reportID, currency, amount: newAmount, taxAmount, policy, taxCode}); Navigation.dismissModal(); }; @@ -326,12 +323,6 @@ const IOURequestStepAmountWithOnyx = withOnyx { - const transactionID = route.params.transactionID ?? 0; - return `${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}`; - }, - }, skipConfirmation: { key: ({route}) => { const transactionID = route.params.transactionID ?? 0; diff --git a/src/pages/iou/request/step/IOURequestStepConfirmation.tsx b/src/pages/iou/request/step/IOURequestStepConfirmation.tsx index b4eb9f1082ed..08b95a326d48 100644 --- a/src/pages/iou/request/step/IOURequestStepConfirmation.tsx +++ b/src/pages/iou/request/step/IOURequestStepConfirmation.tsx @@ -325,7 +325,9 @@ function IOURequestStepConfirmation({ const participantsWithAmount = Object.keys(transaction.splitShares ?? {}) .filter((accountID: string): boolean => (transaction?.splitShares?.[Number(accountID)]?.amount ?? 0) > 0) .map((accountID) => Number(accountID)); - splitParticipants = selectedParticipants.filter((participant) => participantsWithAmount.includes(participant.accountID ?? -1)); + splitParticipants = selectedParticipants.filter((participant) => + participantsWithAmount.includes(participant.isPolicyExpenseChat ? participant?.ownerAccountID ?? -1 : participant.accountID ?? -1), + ); } const trimmedComment = (transaction?.comment.comment ?? '').trim(); @@ -375,6 +377,8 @@ function IOURequestStepConfirmation({ iouRequestType: transaction.iouRequestType, splitShares: transaction.splitShares, splitPayerAccountIDs: transaction.splitPayerAccountIDs ?? [], + taxCode: transactionTaxCode, + taxAmount: transactionTaxAmount, }); } return; @@ -398,6 +402,8 @@ function IOURequestStepConfirmation({ iouRequestType: transaction.iouRequestType, splitShares: transaction.splitShares, splitPayerAccountIDs: transaction.splitPayerAccountIDs, + taxCode: transactionTaxCode, + taxAmount: transactionTaxAmount, }); } return; @@ -493,6 +499,8 @@ function IOURequestStepConfirmation({ policy, policyTags, policyCategories, + transactionTaxAmount, + transactionTaxCode, ], ); diff --git a/src/pages/iou/request/step/IOURequestStepParticipants.tsx b/src/pages/iou/request/step/IOURequestStepParticipants.tsx index e0be24db5c85..89c6a4043b28 100644 --- a/src/pages/iou/request/step/IOURequestStepParticipants.tsx +++ b/src/pages/iou/request/step/IOURequestStepParticipants.tsx @@ -41,7 +41,9 @@ function IOURequestStepParticipants({ const participants = transaction?.participants; const {translate} = useLocalize(); const styles = useThemeStyles(); - const selectedReportID = useRef(reportID); + + // We need to set selectedReportID if user has navigated back from confirmation page and navigates to confirmation page with already selected participant + const selectedReportID = useRef(participants?.length === 1 ? participants[0]?.reportID ?? reportID : reportID); const numberOfParticipants = useRef(participants?.length ?? 0); const iouRequestType = TransactionUtils.getRequestType(transaction); const isSplitRequest = iouType === CONST.IOU.TYPE.SPLIT; diff --git a/src/pages/iou/request/step/IOURequestStepTaxAmountPage.tsx b/src/pages/iou/request/step/IOURequestStepTaxAmountPage.tsx index 27c2808669df..fd589b6361f0 100644 --- a/src/pages/iou/request/step/IOURequestStepTaxAmountPage.tsx +++ b/src/pages/iou/request/step/IOURequestStepTaxAmountPage.tsx @@ -16,6 +16,7 @@ import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; import type {Policy, PolicyCategories, PolicyTagList, Transaction} from '@src/types/onyx'; +import {isEmptyObject} from '@src/types/utils/EmptyObject'; import StepScreenWrapper from './StepScreenWrapper'; import withFullTransactionOrNotFound from './withFullTransactionOrNotFound'; import type {WithWritableReportOrNotFoundProps} from './withWritableReportOrNotFound'; @@ -27,6 +28,9 @@ type IOURequestStepTaxAmountPageOnyxProps = { /** Collection of tag list on a policy */ policyTags: OnyxEntry; + + /** The draft transaction that holds data to be persisted on the current transaction */ + splitDraftTransaction: OnyxEntry; }; type IOURequestStepTaxAmountPageProps = IOURequestStepTaxAmountPageOnyxProps & @@ -58,14 +62,16 @@ function IOURequestStepTaxAmountPage({ policy, policyTags, policyCategories, + splitDraftTransaction, }: IOURequestStepTaxAmountPageProps) { const {translate} = useLocalize(); const textInput = useRef(); const isEditing = action === CONST.IOU.ACTION.EDIT; + const isEditingSplitBill = isEditing && iouType === CONST.IOU.TYPE.SPLIT; const focusTimeoutRef = useRef(); - const transactionDetails = ReportUtils.getTransactionDetails(transaction); + const transactionDetails = ReportUtils.getTransactionDetails(isEditingSplitBill && !isEmptyObject(splitDraftTransaction) ? splitDraftTransaction : transaction); const currency = CurrencyUtils.isValidCurrencyCode(selectedCurrency) ? selectedCurrency : transactionDetails?.currency; useFocusEffect( @@ -102,19 +108,25 @@ function IOURequestStepTaxAmountPage({ }; const updateTaxAmount = (currentAmount: CurrentMoney) => { - const amountInSmallestCurrencyUnits = CurrencyUtils.convertToBackendAmount(Number.parseFloat(currentAmount.amount)); + const taxAmountInSmallestCurrencyUnits = CurrencyUtils.convertToBackendAmount(Number.parseFloat(currentAmount.amount)); + + if (isEditingSplitBill) { + IOU.setDraftSplitTransaction(transactionID, {taxAmount: taxAmountInSmallestCurrencyUnits}); + navigateBack(); + return; + } if (isEditing) { - if (amountInSmallestCurrencyUnits === TransactionUtils.getTaxAmount(transaction, false)) { + if (taxAmountInSmallestCurrencyUnits === TransactionUtils.getTaxAmount(transaction, false)) { navigateBack(); return; } - IOU.updateMoneyRequestTaxAmount(transactionID, report?.reportID ?? '', amountInSmallestCurrencyUnits, policy, policyTags, policyCategories); + IOU.updateMoneyRequestTaxAmount(transactionID, report?.reportID ?? '', taxAmountInSmallestCurrencyUnits, policy, policyTags, policyCategories); navigateBack(); return; } - IOU.setMoneyRequestTaxAmount(transactionID, amountInSmallestCurrencyUnits, true); + IOU.setMoneyRequestTaxAmount(transactionID, taxAmountInSmallestCurrencyUnits, true); // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing IOU.setMoneyRequestCurrency(transactionID, currency || CONST.CURRENCY.USD); @@ -164,6 +176,12 @@ function IOURequestStepTaxAmountPage({ IOURequestStepTaxAmountPage.displayName = 'IOURequestStepTaxAmountPage'; const IOURequestStepTaxAmountPageWithOnyx = withOnyx({ + splitDraftTransaction: { + key: ({route}) => { + const transactionID = route?.params.transactionID ?? 0; + return `${ONYXKEYS.COLLECTION.SPLIT_TRANSACTION_DRAFT}${transactionID}`; + }, + }, policy: { key: ({report}) => `${ONYXKEYS.COLLECTION.POLICY}${report ? report.policyID : '0'}`, }, diff --git a/src/pages/iou/request/step/IOURequestStepTaxRatePage.tsx b/src/pages/iou/request/step/IOURequestStepTaxRatePage.tsx index 9376aa65e4e6..d048cae15089 100644 --- a/src/pages/iou/request/step/IOURequestStepTaxRatePage.tsx +++ b/src/pages/iou/request/step/IOURequestStepTaxRatePage.tsx @@ -12,10 +12,11 @@ import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type SCREENS from '@src/SCREENS'; import type {Policy, PolicyCategories, PolicyTagList, Transaction} from '@src/types/onyx'; +import {isEmptyObject} from '@src/types/utils/EmptyObject'; import StepScreenWrapper from './StepScreenWrapper'; import withFullTransactionOrNotFound from './withFullTransactionOrNotFound'; -import type {WithWritableReportOrNotFoundProps} from './withWritableReportOrNotFound'; import withWritableReportOrNotFound from './withWritableReportOrNotFound'; +import type {WithWritableReportOrNotFoundProps} from './withWritableReportOrNotFound'; type IOURequestStepTaxRatePageOnyxProps = { policy: OnyxEntry; @@ -23,6 +24,9 @@ type IOURequestStepTaxRatePageOnyxProps = { /** Collection of tag list on a policy */ policyTags: OnyxEntry; + + /** The draft transaction that holds data to be persisted on the current transaction */ + splitDraftTransaction: OnyxEntry; }; type IOURequestStepTaxRatePageProps = IOURequestStepTaxRatePageOnyxProps & @@ -40,41 +44,53 @@ function getTaxAmount(policy: OnyxEntry, transaction: OnyxEntry { Navigation.goBack(backTo); }; - const taxRateTitle = TransactionUtils.getTaxName(policy, transaction); + const taxRateTitle = TransactionUtils.getTaxName(policy, currentTransaction); const updateTaxRates = (taxes: TaxRatesOption) => { - if (!transaction || !taxes.code || !taxRates) { + if (!currentTransaction || !taxes.code || !taxRates) { Navigation.goBack(); return; } - const taxAmount = getTaxAmount(policy, transaction, taxes?.code, TransactionUtils.getAmount(transaction, false, true)); + const taxAmount = getTaxAmount(policy, currentTransaction, taxes.code, TransactionUtils.getAmount(currentTransaction, false, true)); + + if (isEditingSplitBill) { + IOU.setDraftSplitTransaction(currentTransaction.transactionID, { + taxAmount: CurrencyUtils.convertToBackendAmount(taxAmount ?? 0), + taxCode: taxes.code, + }); + navigateBack(); + return; + } if (isEditing) { const newTaxCode = taxes.code; - if (newTaxCode === TransactionUtils.getTaxCode(transaction)) { + if (newTaxCode === TransactionUtils.getTaxCode(currentTransaction)) { navigateBack(); return; } IOU.updateMoneyRequestTaxRate({ - transactionID: transaction?.transactionID ?? '', + transactionID: currentTransaction?.transactionID ?? '', optimisticReportActionID: report?.reportID ?? '', taxCode: newTaxCode, taxAmount: CurrencyUtils.convertToBackendAmount(taxAmount ?? 0), @@ -91,8 +107,8 @@ function IOURequestStepTaxRatePage({ return; } const amountInSmallestCurrencyUnits = CurrencyUtils.convertToBackendAmount(taxAmount); - IOU.setMoneyRequestTaxRate(transaction?.transactionID, taxes?.code ?? ''); - IOU.setMoneyRequestTaxAmount(transaction.transactionID, amountInSmallestCurrencyUnits, true); + IOU.setMoneyRequestTaxRate(currentTransaction?.transactionID, taxes?.code ?? ''); + IOU.setMoneyRequestTaxAmount(currentTransaction.transactionID, amountInSmallestCurrencyUnits, true); navigateBack(); }; @@ -107,7 +123,7 @@ function IOURequestStepTaxRatePage({ @@ -118,6 +134,12 @@ function IOURequestStepTaxRatePage({ IOURequestStepTaxRatePage.displayName = 'IOURequestStepTaxRatePage'; const IOURequestStepTaxRatePageWithOnyx = withOnyx({ + splitDraftTransaction: { + key: ({route}) => { + const transactionID = route.params.transactionID ?? 0; + return `${ONYXKEYS.COLLECTION.SPLIT_TRANSACTION_DRAFT}${transactionID}`; + }, + }, policy: { key: ({report}) => `${ONYXKEYS.COLLECTION.POLICY}${report ? report.policyID : '0'}`, }, diff --git a/src/types/onyx/IOU.ts b/src/types/onyx/IOU.ts index 8b0ca935f4b3..68e8836e149c 100644 --- a/src/types/onyx/IOU.ts +++ b/src/types/onyx/IOU.ts @@ -25,6 +25,7 @@ type Participant = { isSelfDM?: boolean; isSender?: boolean; iouType?: string; + ownerAccountID?: number; }; type Split = { @@ -41,6 +42,7 @@ type Split = { reportPreviewReportActionID?: string; transactionThreadReportID?: string; createdReportActionIDForThread?: string; + taxAmount?: number; }; type IOU = { From c008c45de0cb516cb898e50f1697a0f3295ab640 Mon Sep 17 00:00:00 2001 From: Monil Bhavsar Date: Wed, 29 May 2024 16:36:23 +0530 Subject: [PATCH 2/9] Fix duplicate tax rates being rendered --- src/components/TaxPicker.tsx | 23 ++++++++++++++++--- .../step/IOURequestStepTaxRatePage.tsx | 1 + 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/src/components/TaxPicker.tsx b/src/components/TaxPicker.tsx index 3ff2bc6a9cfb..e50f3443c063 100644 --- a/src/components/TaxPicker.tsx +++ b/src/components/TaxPicker.tsx @@ -13,6 +13,8 @@ import ONYXKEYS from '@src/ONYXKEYS'; import type {Policy, Transaction} from '@src/types/onyx'; import SelectionList from './SelectionList'; import RadioListItem from './SelectionList/RadioListItem'; +import {isEmptyObject} from "@src/types/utils/EmptyObject"; +import {ValueOf} from "type-fest"; type TaxPickerOnyxProps = { /** The policy which the user has access to and which the report is tied to */ @@ -20,6 +22,9 @@ type TaxPickerOnyxProps = { /** All the data for the transaction */ transaction: OnyxEntry; + + /** The draft transaction that holds data to be persisted on the current split transaction */ + splitDraftTransaction: OnyxEntry; }; type TaxPickerProps = TaxPickerOnyxProps & { @@ -46,13 +51,20 @@ type TaxPickerProps = TaxPickerOnyxProps & { /** The action to take */ // eslint-disable-next-line react/no-unused-prop-types action?: IOUAction; + + /** The type of IOU */ + iouType?: ValueOf; }; -function TaxPicker({selectedTaxRate = '', policy, transaction, insets, onSubmit}: TaxPickerProps) { +function TaxPicker({selectedTaxRate = '', policy, transaction, insets, onSubmit, action, splitDraftTransaction, iouType}: TaxPickerProps) { const StyleUtils = useStyleUtils(); const {translate} = useLocalize(); const [searchValue, setSearchValue] = useState(''); + const isEditing = action === CONST.IOU.ACTION.EDIT; + const isEditingSplitBill = isEditing && iouType === CONST.IOU.TYPE.SPLIT; + const currentTransaction = isEditingSplitBill && !isEmptyObject(splitDraftTransaction) ? splitDraftTransaction : transaction; + const taxRates = policy?.taxRates; const taxRatesCount = TransactionUtils.getEnabledTaxRateCount(taxRates?.taxes ?? {}); const isTaxRatesCountBelowThreshold = taxRatesCount < CONST.TAX_RATES_LIST_THRESHOLD; @@ -74,8 +86,8 @@ function TaxPicker({selectedTaxRate = '', policy, transaction, insets, onSubmit} }, [selectedTaxRate]); const sections = useMemo( - () => OptionsListUtils.getTaxRatesSection(policy, selectedOptions as OptionsListUtils.Tax[], searchValue, transaction), - [searchValue, selectedOptions, policy, transaction], + () => OptionsListUtils.getTaxRatesSection(policy, selectedOptions as OptionsListUtils.Tax[], searchValue, currentTransaction), + [searchValue, selectedOptions, policy, currentTransaction], ); const headerMessage = OptionsListUtils.getHeaderMessageForNonUserList(sections[0].data.length > 0, searchValue); @@ -112,4 +124,9 @@ export default withOnyx({ return `${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`; }, }, + splitDraftTransaction: { + key: ({transactionID}) => { + return `${ONYXKEYS.COLLECTION.SPLIT_TRANSACTION_DRAFT}${transactionID}`; + }, + }, })(TaxPicker); diff --git a/src/pages/iou/request/step/IOURequestStepTaxRatePage.tsx b/src/pages/iou/request/step/IOURequestStepTaxRatePage.tsx index d048cae15089..17e42115edcd 100644 --- a/src/pages/iou/request/step/IOURequestStepTaxRatePage.tsx +++ b/src/pages/iou/request/step/IOURequestStepTaxRatePage.tsx @@ -126,6 +126,7 @@ function IOURequestStepTaxRatePage({ transactionID={currentTransaction?.transactionID} onSubmit={updateTaxRates} action={action} + iouType={iouType} /> ); From 47b72b719fe8ba59cbfee55dbf1b4576cde695b0 Mon Sep 17 00:00:00 2001 From: Monil Bhavsar Date: Wed, 29 May 2024 16:46:08 +0530 Subject: [PATCH 3/9] Use correct transaction and fix allowing tax amount to zero --- src/pages/iou/request/step/IOURequestStepTaxAmountPage.tsx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/pages/iou/request/step/IOURequestStepTaxAmountPage.tsx b/src/pages/iou/request/step/IOURequestStepTaxAmountPage.tsx index fd589b6361f0..5f3cdf990e02 100644 --- a/src/pages/iou/request/step/IOURequestStepTaxAmountPage.tsx +++ b/src/pages/iou/request/step/IOURequestStepTaxAmountPage.tsx @@ -71,7 +71,8 @@ function IOURequestStepTaxAmountPage({ const focusTimeoutRef = useRef(); - const transactionDetails = ReportUtils.getTransactionDetails(isEditingSplitBill && !isEmptyObject(splitDraftTransaction) ? splitDraftTransaction : transaction); + const currentTransaction = isEditingSplitBill && !isEmptyObject(splitDraftTransaction) ? splitDraftTransaction : transaction; + const transactionDetails = ReportUtils.getTransactionDetails(currentTransaction); const currency = CurrencyUtils.isValidCurrencyCode(selectedCurrency) ? selectedCurrency : transactionDetails?.currency; useFocusEffect( @@ -117,7 +118,7 @@ function IOURequestStepTaxAmountPage({ } if (isEditing) { - if (taxAmountInSmallestCurrencyUnits === TransactionUtils.getTaxAmount(transaction, false)) { + if (taxAmountInSmallestCurrencyUnits === TransactionUtils.getTaxAmount(currentTransaction, false)) { navigateBack(); return; } @@ -163,7 +164,7 @@ function IOURequestStepTaxAmountPage({ isEditing={Boolean(backTo || isEditing)} currency={currency} amount={Math.abs(transactionDetails?.taxAmount ?? 0)} - taxAmount={getTaxAmount(transaction, policy, currency, Boolean(backTo || isEditing))} + taxAmount={getTaxAmount(currentTransaction, policy, currency, Boolean(backTo || isEditing))} ref={(e) => (textInput.current = e)} onCurrencyButtonPress={navigateToCurrencySelectionPage} onSubmitButtonPress={updateTaxAmount} From fa8bc7ef46bbbf4c03005ac9cdec84a9e5b62a19 Mon Sep 17 00:00:00 2001 From: Monil Bhavsar Date: Wed, 29 May 2024 18:02:22 +0530 Subject: [PATCH 4/9] Use draft transaction for currency --- src/pages/iou/request/step/IOURequestStepAmount.tsx | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/pages/iou/request/step/IOURequestStepAmount.tsx b/src/pages/iou/request/step/IOURequestStepAmount.tsx index 4ae2784f61b1..1613823b444d 100644 --- a/src/pages/iou/request/step/IOURequestStepAmount.tsx +++ b/src/pages/iou/request/step/IOURequestStepAmount.tsx @@ -36,6 +36,9 @@ type IOURequestStepAmountOnyxProps = { /** The draft transaction that holds data to be persisted on the current transaction */ splitDraftTransaction: OnyxEntry; + /** The draft transaction object being modified in Onyx */ + draftTransaction: OnyxEntry; + /** Whether the confirmation step should be skipped */ skipConfirmation: OnyxEntry; @@ -63,6 +66,7 @@ function IOURequestStepAmount({ personalDetails, currentUserPersonalDetails, splitDraftTransaction, + draftTransaction, skipConfirmation, }: IOURequestStepAmountProps) { const {translate} = useLocalize(); @@ -76,7 +80,7 @@ function IOURequestStepAmount({ const isEditingSplitBill = isEditing && isSplitBill; const currentTransaction = isEditingSplitBill && !isEmptyObject(splitDraftTransaction) ? splitDraftTransaction : transaction; const {amount: transactionAmount} = ReportUtils.getTransactionDetails(currentTransaction) ?? {amount: 0}; - const {currency: originalCurrency} = ReportUtils.getTransactionDetails(currentTransaction) ?? {currency: CONST.CURRENCY.USD}; + const {currency: originalCurrency} = ReportUtils.getTransactionDetails(isEditing ? draftTransaction : transaction) ?? {currency: CONST.CURRENCY.USD}; const currency = CurrencyUtils.isValidCurrencyCode(selectedCurrency) ? selectedCurrency : originalCurrency; // For quick button actions, we'll skip the confirmation page unless the report is archived or this is a workspace request, as @@ -323,6 +327,12 @@ const IOURequestStepAmountWithOnyx = withOnyx { + const transactionID = route.params.transactionID ?? 0; + return `${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}`; + }, + }, skipConfirmation: { key: ({route}) => { const transactionID = route.params.transactionID ?? 0; From e18c0c1b5b38d1d9f90d24501e248f48d3cc2dce Mon Sep 17 00:00:00 2001 From: Monil Bhavsar Date: Wed, 29 May 2024 18:38:57 +0530 Subject: [PATCH 5/9] Fix lint --- src/components/TaxPicker.tsx | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/components/TaxPicker.tsx b/src/components/TaxPicker.tsx index e50f3443c063..4ee24a7f28d1 100644 --- a/src/components/TaxPicker.tsx +++ b/src/components/TaxPicker.tsx @@ -11,10 +11,10 @@ import CONST from '@src/CONST'; import type {IOUAction} from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type {Policy, Transaction} from '@src/types/onyx'; +import type {ValueOf} from "type-fest"; +import {isEmptyObject} from "@src/types/utils/EmptyObject"; import SelectionList from './SelectionList'; import RadioListItem from './SelectionList/RadioListItem'; -import {isEmptyObject} from "@src/types/utils/EmptyObject"; -import {ValueOf} from "type-fest"; type TaxPickerOnyxProps = { /** The policy which the user has access to and which the report is tied to */ @@ -125,8 +125,6 @@ export default withOnyx({ }, }, splitDraftTransaction: { - key: ({transactionID}) => { - return `${ONYXKEYS.COLLECTION.SPLIT_TRANSACTION_DRAFT}${transactionID}`; - }, + key: ({transactionID}) => `${ONYXKEYS.COLLECTION.SPLIT_TRANSACTION_DRAFT}${transactionID}`, }, })(TaxPicker); From 2114fec6c91e01857bf7132f1f17175c7c88b1e2 Mon Sep 17 00:00:00 2001 From: Monil Bhavsar Date: Wed, 29 May 2024 19:32:58 +0530 Subject: [PATCH 6/9] Pass tax params to start and complete split bill requests --- src/components/TaxPicker.tsx | 4 ++-- .../API/parameters/CompleteSplitBillParams.ts | 2 ++ .../API/parameters/StartSplitBillParams.ts | 2 ++ src/libs/actions/IOU.ts | 20 +++++++++++++++---- .../step/IOURequestStepConfirmation.tsx | 2 ++ .../step/IOURequestStepScan/index.native.tsx | 9 +++++++++ .../request/step/IOURequestStepScan/index.tsx | 9 +++++++++ 7 files changed, 42 insertions(+), 6 deletions(-) diff --git a/src/components/TaxPicker.tsx b/src/components/TaxPicker.tsx index 4ee24a7f28d1..fc29da30941d 100644 --- a/src/components/TaxPicker.tsx +++ b/src/components/TaxPicker.tsx @@ -2,6 +2,7 @@ import React, {useMemo, useState} from 'react'; import {withOnyx} from 'react-native-onyx'; import type {OnyxEntry} from 'react-native-onyx'; import type {EdgeInsets} from 'react-native-safe-area-context'; +import type {ValueOf} from 'type-fest'; import useLocalize from '@hooks/useLocalize'; import useStyleUtils from '@hooks/useStyleUtils'; import * as IOUUtils from '@libs/IOUUtils'; @@ -11,8 +12,7 @@ import CONST from '@src/CONST'; import type {IOUAction} from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type {Policy, Transaction} from '@src/types/onyx'; -import type {ValueOf} from "type-fest"; -import {isEmptyObject} from "@src/types/utils/EmptyObject"; +import {isEmptyObject} from '@src/types/utils/EmptyObject'; import SelectionList from './SelectionList'; import RadioListItem from './SelectionList/RadioListItem'; diff --git a/src/libs/API/parameters/CompleteSplitBillParams.ts b/src/libs/API/parameters/CompleteSplitBillParams.ts index 50054ba6fd10..a1731d32fcc4 100644 --- a/src/libs/API/parameters/CompleteSplitBillParams.ts +++ b/src/libs/API/parameters/CompleteSplitBillParams.ts @@ -8,6 +8,8 @@ type CompleteSplitBillParams = { category?: string; tag?: string; splits: string; + taxCode?: string; + taxAmount?: number; }; export default CompleteSplitBillParams; diff --git a/src/libs/API/parameters/StartSplitBillParams.ts b/src/libs/API/parameters/StartSplitBillParams.ts index ce7a310f14e2..499073c88de9 100644 --- a/src/libs/API/parameters/StartSplitBillParams.ts +++ b/src/libs/API/parameters/StartSplitBillParams.ts @@ -14,6 +14,8 @@ type StartSplitBillParams = { createdReportActionID?: string; billable: boolean; chatType?: string; + taxCode?: string; + taxAmount?: number; }; export default StartSplitBillParams; diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 1a1ad7e3c7e7..5d034bdf0a26 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -4310,6 +4310,8 @@ type StartSplitBilActionParams = { category: string | undefined; tag: string | undefined; currency: string; + taxCode: string; + taxAmount: number; }; /** Used exclusively for starting a split expense request that contains a receipt, the split request will be completed once the receipt is scanned @@ -4328,6 +4330,8 @@ function startSplitBill({ category = '', tag = '', currency, + taxCode = '', + taxAmount = 0, }: StartSplitBilActionParams) { const currentUserEmailForIOUSplit = PhoneNumber.addSMSDomainIfPhoneNumber(currentUserLogin); const participantAccountIDs = participants.map((participant) => Number(participant.accountID)); @@ -4352,8 +4356,8 @@ function startSplitBill({ undefined, category, tag, - undefined, - undefined, + taxCode, + taxAmount, billable, ); @@ -4595,6 +4599,8 @@ function startSplitBill({ billable, ...(existingSplitChatReport ? {} : {createdReportActionID: splitChatCreatedReportAction.reportActionID}), chatType: splitChatReport?.chatType, + taxCode, + taxAmount, }; API.write(WRITE_COMMANDS.START_SPLIT_BILL, parameters, {optimisticData, successData, failureData}); @@ -4679,9 +4685,11 @@ function completeSplitBill(chatReportID: string, reportAction: OnyxTypes.ReportA const splitParticipants: Split[] = updatedTransaction?.comment.splits ?? []; const amount = updatedTransaction?.modifiedAmount; const currency = updatedTransaction?.modifiedCurrency; + console.debug(updatedTransaction); // Exclude the current user when calculating the split amount, `calculateAmount` takes it into account const splitAmount = IOUUtils.calculateAmount(splitParticipants.length - 1, amount ?? 0, currency ?? '', false); + const splitTaxAmount = IOUUtils.calculateAmount(splitParticipants.length - 1, updatedTransaction?.taxAmount ?? 0, currency ?? '', false); const splits: Split[] = [{email: currentUserEmailForIOUSplit}]; splitParticipants.forEach((participant) => { @@ -4745,8 +4753,8 @@ function completeSplitBill(chatReportID: string, reportAction: OnyxTypes.ReportA undefined, updatedTransaction?.category, updatedTransaction?.tag, - undefined, - undefined, + updatedTransaction?.taxCode, + isPolicyExpenseChat ? -splitTaxAmount : splitAmount, updatedTransaction?.billable, ); @@ -4820,6 +4828,8 @@ function completeSplitBill(chatReportID: string, reportAction: OnyxTypes.ReportA comment: transactionComment, category: transactionCategory, tag: transactionTag, + taxCode: transactionTaxCode, + taxAmount: transactionTaxAmount, } = ReportUtils.getTransactionDetails(updatedTransaction) ?? {}; const parameters: CompleteSplitBillParams = { @@ -4832,6 +4842,8 @@ function completeSplitBill(chatReportID: string, reportAction: OnyxTypes.ReportA category: transactionCategory, tag: transactionTag, splits: JSON.stringify(splits), + taxCode: transactionTaxCode, + taxAmount: transactionTaxAmount, }; API.write(WRITE_COMMANDS.COMPLETE_SPLIT_BILL, parameters, {optimisticData, successData, failureData}); diff --git a/src/pages/iou/request/step/IOURequestStepConfirmation.tsx b/src/pages/iou/request/step/IOURequestStepConfirmation.tsx index 08b95a326d48..11f9892e490d 100644 --- a/src/pages/iou/request/step/IOURequestStepConfirmation.tsx +++ b/src/pages/iou/request/step/IOURequestStepConfirmation.tsx @@ -352,6 +352,8 @@ function IOURequestStepConfirmation({ category: transaction.category, tag: transaction.tag, currency: transaction.currency, + taxCode: transactionTaxCode, + taxAmount: transactionTaxAmount, }); } return; diff --git a/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx b/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx index b844aa29f670..51dfc20eb430 100644 --- a/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx +++ b/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx @@ -27,6 +27,7 @@ import Log from '@libs/Log'; import Navigation from '@libs/Navigation/Navigation'; import * as OptionsListUtils from '@libs/OptionsListUtils'; import * as ReportUtils from '@libs/ReportUtils'; +import * as TransactionUtils from '@libs/TransactionUtils'; import StepScreenWrapper from '@pages/iou/request/step/StepScreenWrapper'; import withFullTransactionOrNotFound from '@pages/iou/request/step/withFullTransactionOrNotFound'; import withWritableReportOrNotFound from '@pages/iou/request/step/withWritableReportOrNotFound'; @@ -63,6 +64,10 @@ function IOURequestStepScan({ const [cameraPermissionStatus, setCameraPermissionStatus] = useState(null); const [didCapturePhoto, setDidCapturePhoto] = useState(false); + const defaultTaxCode = TransactionUtils.getDefaultTaxCode(policy, transaction); + const transactionTaxCode = (transaction?.taxCode ? transaction?.taxCode : defaultTaxCode) ?? ''; + const transactionTaxAmount = transaction?.taxAmount ?? 0; + // For quick button actions, we'll skip the confirmation page unless the report is archived or this is a workspace // request and the workspace requires a category or a tag const shouldSkipConfirmation: boolean = useMemo(() => { @@ -243,6 +248,8 @@ function IOURequestStepScan({ category: '', tag: '', currency: transaction?.currency ?? 'USD', + taxCode: transactionTaxCode, + taxAmount: transactionTaxAmount, }); return; } @@ -353,6 +360,8 @@ function IOURequestStepScan({ navigateToConfirmationPage, navigateToParticipantPage, policy, + transactionTaxCode, + transactionTaxAmount, ], ); diff --git a/src/pages/iou/request/step/IOURequestStepScan/index.tsx b/src/pages/iou/request/step/IOURequestStepScan/index.tsx index ede79e009a49..f79c4eb7cd14 100644 --- a/src/pages/iou/request/step/IOURequestStepScan/index.tsx +++ b/src/pages/iou/request/step/IOURequestStepScan/index.tsx @@ -28,6 +28,7 @@ import Log from '@libs/Log'; import Navigation from '@libs/Navigation/Navigation'; import * as OptionsListUtils from '@libs/OptionsListUtils'; import * as ReportUtils from '@libs/ReportUtils'; +import * as TransactionUtils from '@libs/TransactionUtils'; import ReceiptDropUI from '@pages/iou/ReceiptDropUI'; import StepScreenDragAndDropWrapper from '@pages/iou/request/step/StepScreenDragAndDropWrapper'; import withFullTransactionOrNotFound from '@pages/iou/request/step/withFullTransactionOrNotFound'; @@ -79,6 +80,10 @@ function IOURequestStepScan({ const tabIndex = 1; const isTabActive = useTabNavigatorFocus({tabIndex}); + const defaultTaxCode = TransactionUtils.getDefaultTaxCode(policy, transaction); + const transactionTaxCode = (transaction?.taxCode ? transaction?.taxCode : defaultTaxCode) ?? ''; + const transactionTaxAmount = transaction?.taxAmount ?? 0; + // For quick button actions, we'll skip the confirmation page unless the report is archived or this is a workspace // request and the workspace requires a category or a tag const shouldSkipConfirmation: boolean = useMemo(() => { @@ -281,6 +286,8 @@ function IOURequestStepScan({ category: '', tag: '', currency: transaction?.currency ?? 'USD', + taxCode: transactionTaxCode, + taxAmount: transactionTaxAmount, }); return; } @@ -391,6 +398,8 @@ function IOURequestStepScan({ navigateToConfirmationPage, navigateToParticipantPage, policy, + transactionTaxCode, + transactionTaxAmount, ], ); From 49cbba5b89915012b787521c2dd1ac282e8cdd7c Mon Sep 17 00:00:00 2001 From: Monil Bhavsar Date: Mon, 3 Jun 2024 20:32:40 +0530 Subject: [PATCH 7/9] Reset tax amount on first step and update hook --- src/components/MoneyRequestConfirmationList.tsx | 10 +--------- src/pages/iou/request/step/IOURequestStepAmount.tsx | 4 ++++ 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/src/components/MoneyRequestConfirmationList.tsx b/src/components/MoneyRequestConfirmationList.tsx index 703b2f453d73..b41d2091d401 100755 --- a/src/components/MoneyRequestConfirmationList.tsx +++ b/src/components/MoneyRequestConfirmationList.tsx @@ -340,15 +340,7 @@ function MoneyRequestConfirmationList({ const isCategoryRequired = !!policy?.requiresCategory; useEffect(() => { - if (!shouldShowTax) { - return; - } - setTaxAmountInDraft(transaction, policy); - // eslint-disable-next-line react-hooks/exhaustive-deps -- we want to call this function when component is mounted - }, []); - - useEffect(() => { - if (!shouldShowTax || prevTaxAmount !== transaction?.taxAmount || (prevAmount === transaction?.amount && prevCurrency === transaction?.currency)) { + if (!shouldShowTax || (transaction?.taxAmount !== undefined && prevAmount === transaction?.amount && prevCurrency === transaction?.currency)) { return; } setTaxAmountInDraft(transaction, policy); diff --git a/src/pages/iou/request/step/IOURequestStepAmount.tsx b/src/pages/iou/request/step/IOURequestStepAmount.tsx index 1613823b444d..87a103feec34 100644 --- a/src/pages/iou/request/step/IOURequestStepAmount.tsx +++ b/src/pages/iou/request/step/IOURequestStepAmount.tsx @@ -166,6 +166,10 @@ function IOURequestStepAmount({ // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing IOU.setMoneyRequestAmount(transactionID, amountInSmallestCurrencyUnits, currency || CONST.CURRENCY.USD); + // Initially when we're creating money request, we donot know the participant and hence if the request is with workspace with tax tracking enabled + // So, we reset the taxAmount here and calculate it in the MoneyRequestConfirmationList + IOU.setMoneyRequestTaxAmount(transactionID, null, true); + if (backTo) { Navigation.goBack(backTo); return; From 029c4dc7370d6e0904a53714a2b55b88416d2ec5 Mon Sep 17 00:00:00 2001 From: Monil Bhavsar Date: Mon, 3 Jun 2024 21:16:49 +0530 Subject: [PATCH 8/9] Update function definition and fix lint and typecheck --- src/components/MoneyRequestConfirmationList.tsx | 5 ++--- src/libs/actions/IOU.ts | 4 ++-- src/pages/iou/request/step/IOURequestStepAmount.tsx | 6 +++--- src/pages/iou/request/step/IOURequestStepDistanceRate.tsx | 2 +- src/pages/iou/request/step/IOURequestStepTaxAmountPage.tsx | 2 +- src/pages/iou/request/step/IOURequestStepTaxRatePage.tsx | 2 +- 6 files changed, 10 insertions(+), 11 deletions(-) diff --git a/src/components/MoneyRequestConfirmationList.tsx b/src/components/MoneyRequestConfirmationList.tsx index b41d2091d401..867c47c8238c 100755 --- a/src/components/MoneyRequestConfirmationList.tsx +++ b/src/components/MoneyRequestConfirmationList.tsx @@ -186,7 +186,7 @@ const setTaxAmountInDraft = (transaction: OnyxEntry, poli const taxPercentage = TransactionUtils.getTaxValue(policy, transaction, transaction?.taxCode ?? defaultTaxCode) ?? ''; const taxAmount = TransactionUtils.calculateTaxAmount(taxPercentage, transaction?.amount ?? 0); const taxAmountInSmallestCurrencyUnits = CurrencyUtils.convertToBackendAmount(Number.parseFloat(taxAmount.toString())); - IOU.setMoneyRequestTaxAmount(transaction?.transactionID ?? '', taxAmountInSmallestCurrencyUnits, true); + IOU.setMoneyRequestTaxAmount(transaction?.transactionID ?? '', taxAmountInSmallestCurrencyUnits); }; function MoneyRequestConfirmationList({ @@ -308,7 +308,6 @@ function MoneyRequestConfirmationList({ ); const formattedTaxAmount = CurrencyUtils.convertToDisplayString(transaction?.taxAmount, iouCurrencyCode); const taxRateTitle = TransactionUtils.getTaxName(policy, transaction); - const prevTaxAmount = usePrevious(transaction?.taxAmount); const prevAmount = usePrevious(transaction?.amount); const prevCurrency = usePrevious(transaction?.currency); @@ -344,7 +343,7 @@ function MoneyRequestConfirmationList({ return; } setTaxAmountInDraft(transaction, policy); - }, [policy, shouldShowTax, prevTaxAmount, prevAmount, prevCurrency, transaction]); + }, [policy, shouldShowTax, prevAmount, prevCurrency, transaction]); useEffect(() => { if (customUnitRateID || !canUseP2PDistanceRequests) { diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 7738da668e41..4e741d729d21 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -6624,8 +6624,8 @@ function setMoneyRequestTaxRate(transactionID: string, taxCode: string) { Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}`, {taxCode}); } -function setMoneyRequestTaxAmount(transactionID: string, taxAmount: number, isDraft: boolean) { - Onyx.merge(`${isDraft ? ONYXKEYS.COLLECTION.TRANSACTION_DRAFT : ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, {taxAmount}); +function setMoneyRequestTaxAmount(transactionID: string, taxAmount: number | null) { + Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}`, {taxAmount}); } function setShownHoldUseExplanation() { diff --git a/src/pages/iou/request/step/IOURequestStepAmount.tsx b/src/pages/iou/request/step/IOURequestStepAmount.tsx index 87a103feec34..cf5fdb913618 100644 --- a/src/pages/iou/request/step/IOURequestStepAmount.tsx +++ b/src/pages/iou/request/step/IOURequestStepAmount.tsx @@ -166,9 +166,9 @@ function IOURequestStepAmount({ // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing IOU.setMoneyRequestAmount(transactionID, amountInSmallestCurrencyUnits, currency || CONST.CURRENCY.USD); - // Initially when we're creating money request, we donot know the participant and hence if the request is with workspace with tax tracking enabled - // So, we reset the taxAmount here and calculate it in the MoneyRequestConfirmationList - IOU.setMoneyRequestTaxAmount(transactionID, null, true); + // Initially when we're creating money request, we do not know the participant and hence if the request is with workspace with tax tracking enabled + // So, we reset the taxAmount here and calculate it in the hook in MoneyRequestConfirmationList component + IOU.setMoneyRequestTaxAmount(transactionID, null); if (backTo) { Navigation.goBack(backTo); diff --git a/src/pages/iou/request/step/IOURequestStepDistanceRate.tsx b/src/pages/iou/request/step/IOURequestStepDistanceRate.tsx index 80ddd6567de8..17ab088246dc 100644 --- a/src/pages/iou/request/step/IOURequestStepDistanceRate.tsx +++ b/src/pages/iou/request/step/IOURequestStepDistanceRate.tsx @@ -84,7 +84,7 @@ function IOURequestStepDistanceRate({ const taxableAmount = -1 * transaction.amount * taxClaimablePercentage; const taxPercentage = TransactionUtils.getTaxValue(policy, transaction, taxRateExternalID) ?? ''; const taxAmount = CurrencyUtils.convertToBackendAmount(TransactionUtils.calculateTaxAmount(taxPercentage, taxableAmount)); - IOU.setMoneyRequestTaxAmount(transactionID, taxAmount, true); + IOU.setMoneyRequestTaxAmount(transactionID, taxAmount); IOU.setMoneyRequestTaxRate(transactionID, taxRateExternalID); } IOU.updateDistanceRequestRate(transactionID, customUnitRateID, policy?.id ?? ''); diff --git a/src/pages/iou/request/step/IOURequestStepTaxAmountPage.tsx b/src/pages/iou/request/step/IOURequestStepTaxAmountPage.tsx index 5f3cdf990e02..69430131c28e 100644 --- a/src/pages/iou/request/step/IOURequestStepTaxAmountPage.tsx +++ b/src/pages/iou/request/step/IOURequestStepTaxAmountPage.tsx @@ -127,7 +127,7 @@ function IOURequestStepTaxAmountPage({ return; } - IOU.setMoneyRequestTaxAmount(transactionID, taxAmountInSmallestCurrencyUnits, true); + IOU.setMoneyRequestTaxAmount(transactionID, taxAmountInSmallestCurrencyUnits); // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing IOU.setMoneyRequestCurrency(transactionID, currency || CONST.CURRENCY.USD); diff --git a/src/pages/iou/request/step/IOURequestStepTaxRatePage.tsx b/src/pages/iou/request/step/IOURequestStepTaxRatePage.tsx index 17e42115edcd..a237b47777b2 100644 --- a/src/pages/iou/request/step/IOURequestStepTaxRatePage.tsx +++ b/src/pages/iou/request/step/IOURequestStepTaxRatePage.tsx @@ -108,7 +108,7 @@ function IOURequestStepTaxRatePage({ } const amountInSmallestCurrencyUnits = CurrencyUtils.convertToBackendAmount(taxAmount); IOU.setMoneyRequestTaxRate(currentTransaction?.transactionID, taxes?.code ?? ''); - IOU.setMoneyRequestTaxAmount(currentTransaction.transactionID, amountInSmallestCurrencyUnits, true); + IOU.setMoneyRequestTaxAmount(currentTransaction.transactionID, amountInSmallestCurrencyUnits); navigateBack(); }; From 62169f63ee1463f8b1da282e112a0798bdecbc13 Mon Sep 17 00:00:00 2001 From: Monil Bhavsar Date: Wed, 5 Jun 2024 21:10:09 +0530 Subject: [PATCH 9/9] Revert some unnecessary changes --- .../MoneyRequestConfirmationList.tsx | 60 ++++++++++--------- .../iou/request/step/IOURequestStepAmount.tsx | 6 +- 2 files changed, 34 insertions(+), 32 deletions(-) diff --git a/src/components/MoneyRequestConfirmationList.tsx b/src/components/MoneyRequestConfirmationList.tsx index c0956ba74e8c..d8ed1edfa152 100755 --- a/src/components/MoneyRequestConfirmationList.tsx +++ b/src/components/MoneyRequestConfirmationList.tsx @@ -238,6 +238,16 @@ function MoneyRequestConfirmationList({ const transactionID = transaction?.transactionID ?? ''; const customUnitRateID = TransactionUtils.getRateID(transaction) ?? ''; + useEffect(() => { + if (customUnitRateID || !canUseP2PDistanceRequests) { + return; + } + if (!customUnitRateID) { + const rateID = lastSelectedDistanceRates?.[policy?.id ?? ''] ?? defaultMileageRate?.customUnitRateID ?? ''; + IOU.setCustomUnitRateID(transactionID, rateID); + } + }, [defaultMileageRate, customUnitRateID, lastSelectedDistanceRates, policy?.id, canUseP2PDistanceRequests, transactionID]); + const policyCurrency = policy?.outputCurrency ?? PolicyUtils.getPersonalPolicy()?.outputCurrency ?? CONST.CURRENCY.USD; const mileageRate = TransactionUtils.isCustomUnitRateIDForP2P(transaction) @@ -298,8 +308,9 @@ function MoneyRequestConfirmationList({ ); const formattedTaxAmount = CurrencyUtils.convertToDisplayString(transaction?.taxAmount, iouCurrencyCode); const taxRateTitle = TransactionUtils.getTaxName(policy, transaction); - const prevAmount = usePrevious(transaction?.amount); - const prevCurrency = usePrevious(transaction?.currency); + + const previousTransactionAmount = usePrevious(transaction?.amount); + const previousTransactionCurrency = usePrevious(transaction?.currency); const isFocused = useIsFocused(); const [formError, debouncedFormError, setFormError] = useDebouncedState(''); @@ -329,33 +340,6 @@ function MoneyRequestConfirmationList({ const isCategoryRequired = !!policy?.requiresCategory; - useEffect(() => { - if (!shouldShowTax || (transaction?.taxAmount !== undefined && prevAmount === transaction?.amount && prevCurrency === transaction?.currency)) { - return; - } - - let taxAmount; - if (isDistanceRequest) { - taxAmount = DistanceRequestUtils.calculateTaxAmount(policy, transaction, TransactionUtils.getRateID(transaction) ?? ''); - } else { - const defaultTaxCode = TransactionUtils.getDefaultTaxCode(policy, transaction) ?? ''; - const taxPercentage = TransactionUtils.getTaxValue(policy, transaction, transaction?.taxCode ?? defaultTaxCode) ?? ''; - taxAmount = TransactionUtils.calculateTaxAmount(taxPercentage, transaction?.amount ?? 0); - } - const taxAmountInSmallestCurrencyUnits = CurrencyUtils.convertToBackendAmount(Number.parseFloat(taxAmount.toString())); - IOU.setMoneyRequestTaxAmount(transaction?.transactionID ?? '', taxAmountInSmallestCurrencyUnits); - }, [policy, shouldShowTax, prevAmount, prevCurrency, transaction, isDistanceRequest]); - - useEffect(() => { - if (customUnitRateID || !canUseP2PDistanceRequests) { - return; - } - if (!customUnitRateID) { - const rateID = lastSelectedDistanceRates?.[policy?.id ?? ''] ?? defaultMileageRate?.customUnitRateID ?? ''; - IOU.setCustomUnitRateID(transactionID, rateID); - } - }, [defaultMileageRate, customUnitRateID, lastSelectedDistanceRates, policy?.id, canUseP2PDistanceRequests, transactionID]); - useEffect(() => { if (shouldDisplayFieldError && didConfirmSplit) { setFormError('iou.error.genericSmartscanFailureMessage'); @@ -381,6 +365,24 @@ function MoneyRequestConfirmationList({ IOU.setMoneyRequestAmount(transactionID, amount, currency ?? ''); }, [shouldCalculateDistanceAmount, distance, rate, unit, transactionID, currency]); + // Calculate and set tax amount in transaction draft + useEffect(() => { + if (!shouldShowTax || (transaction?.taxAmount !== undefined && previousTransactionAmount === transaction?.amount && previousTransactionCurrency === transaction?.currency)) { + return; + } + + let taxAmount; + if (isDistanceRequest) { + taxAmount = DistanceRequestUtils.calculateTaxAmount(policy, transaction, TransactionUtils.getRateID(transaction) ?? ''); + } else { + const defaultTaxCode = TransactionUtils.getDefaultTaxCode(policy, transaction) ?? ''; + const taxPercentage = TransactionUtils.getTaxValue(policy, transaction, transaction?.taxCode ?? defaultTaxCode) ?? ''; + taxAmount = TransactionUtils.calculateTaxAmount(taxPercentage, transaction?.amount ?? 0); + } + const taxAmountInSmallestCurrencyUnits = CurrencyUtils.convertToBackendAmount(Number.parseFloat(taxAmount.toString())); + IOU.setMoneyRequestTaxAmount(transaction?.transactionID ?? '', taxAmountInSmallestCurrencyUnits); + }, [policy, shouldShowTax, previousTransactionAmount, previousTransactionCurrency, transaction, isDistanceRequest]); + // If completing a split expense fails, set didConfirm to false to allow the user to edit the fields again if (isEditingSplitBill && didConfirm) { setDidConfirm(false); diff --git a/src/pages/iou/request/step/IOURequestStepAmount.tsx b/src/pages/iou/request/step/IOURequestStepAmount.tsx index 2e697d87b467..44e7cc58d10c 100644 --- a/src/pages/iou/request/step/IOURequestStepAmount.tsx +++ b/src/pages/iou/request/step/IOURequestStepAmount.tsx @@ -36,12 +36,12 @@ type IOURequestStepAmountOnyxProps = { /** The draft transaction that holds data to be persisted on the current transaction */ splitDraftTransaction: OnyxEntry; - /** The draft transaction object being modified in Onyx */ - draftTransaction: OnyxEntry; - /** Whether the confirmation step should be skipped */ skipConfirmation: OnyxEntry; + /** The draft transaction object being modified in Onyx */ + draftTransaction: OnyxEntry; + /** Personal details of all users */ personalDetails: OnyxEntry;