diff --git a/src/components/MoneyRequestConfirmationList.tsx b/src/components/MoneyRequestConfirmationList.tsx index 0d47ad461173..d8ed1edfa152 100755 --- a/src/components/MoneyRequestConfirmationList.tsx +++ b/src/components/MoneyRequestConfirmationList.tsx @@ -179,15 +179,6 @@ type MoneyRequestConfirmationListProps = MoneyRequestConfirmationListOnyxProps & type MoneyRequestConfirmationListItem = Participant | ReportUtils.OptionData; -const getTaxAmount = (transaction: OnyxEntry, policy: OnyxEntry, isDistanceRequest: boolean) => { - if (isDistanceRequest) { - return DistanceRequestUtils.calculateTaxAmount(policy, transaction, TransactionUtils.getRateID(transaction) ?? ''); - } - const defaultTaxCode = TransactionUtils.getDefaultTaxCode(policy, transaction) ?? ''; - const taxPercentage = TransactionUtils.getTaxValue(policy, transaction, transaction?.taxCode ?? defaultTaxCode) ?? ''; - return TransactionUtils.calculateTaxAmount(taxPercentage, transaction?.amount ?? 0); -}; - function MoneyRequestConfirmationList({ transaction = null, onSendMoney, @@ -376,15 +367,21 @@ function MoneyRequestConfirmationList({ // Calculate and set tax amount in transaction draft useEffect(() => { - const taxAmount = getTaxAmount(transaction, policy, isDistanceRequest).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); + if (!shouldShowTax || (transaction?.taxAmount !== undefined && previousTransactionAmount === transaction?.amount && previousTransactionCurrency === transaction?.currency)) { + return; } - IOU.setMoneyRequestTaxAmount(transactionID, amountInSmallestCurrencyUnits, true); - }, [policy, transaction, transactionID, previousTransactionAmount, previousTransactionCurrency, isDistanceRequest]); + 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) { diff --git a/src/components/TaxPicker.tsx b/src/components/TaxPicker.tsx index 3ff2bc6a9cfb..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,6 +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 {isEmptyObject} from '@src/types/utils/EmptyObject'; import SelectionList from './SelectionList'; import RadioListItem from './SelectionList/RadioListItem'; @@ -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,7 @@ export default withOnyx({ return `${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`; }, }, + splitDraftTransaction: { + key: ({transactionID}) => `${ONYXKEYS.COLLECTION.SPLIT_TRANSACTION_DRAFT}${transactionID}`, + }, })(TaxPicker); 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/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/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 c4ce7ec25de2..6694d6bce70c 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -3787,6 +3787,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)); @@ -3808,8 +3810,8 @@ function createSplitsAndOnyxData( undefined, category, tag, - undefined, - undefined, + taxCode, + taxAmount, billable, ); @@ -3964,14 +3966,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. @@ -4041,8 +4045,8 @@ function createSplitsAndOnyxData( undefined, category, tag, - undefined, - undefined, + taxCode, + ReportUtils.isExpenseReport(oneOnOneIOUReport) ? -splitTaxAmount : splitTaxAmount, billable, ); @@ -4133,6 +4137,7 @@ function createSplitsAndOnyxData( reportPreviewReportActionID: oneOnOneReportPreviewAction.reportActionID, transactionThreadReportID: optimisticTransactionThread.reportID, createdReportActionIDForThread: optimisticCreatedActionForTransactionThread.reportActionID, + taxAmount: splitTaxAmount, }; splits.push(individualSplit); @@ -4186,6 +4191,8 @@ type SplitBillActionsParams = { existingSplitChatReportID?: string; splitShares?: SplitShares; splitPayerAccountIDs?: number[]; + taxCode?: string; + taxAmount?: number; }; /** @@ -4208,6 +4215,8 @@ function splitBill({ existingSplitChatReportID = '', splitShares = {}, splitPayerAccountIDs = [], + taxCode = '', + taxAmount = 0, }: SplitBillActionsParams) { const currentCreated = DateUtils.enrichMoneyRequestTimestamp(created); const {splitData, splits, onyxData} = createSplitsAndOnyxData( @@ -4225,6 +4234,8 @@ function splitBill({ existingSplitChatReportID, billable, iouRequestType, + taxCode, + taxAmount, ); const parameters: SplitBillParams = { @@ -4244,6 +4255,8 @@ function splitBill({ policyID: splitData.policyID, chatType: splitData.chatType, splitPayerAccountIDs, + taxCode, + taxAmount, }; API.write(WRITE_COMMANDS.SPLIT_BILL, parameters, onyxData); @@ -4270,6 +4283,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( @@ -4287,6 +4302,8 @@ function splitBillAndOpenReport({ '', billable, iouRequestType, + taxCode, + taxAmount, ); const parameters: SplitBillParams = { @@ -4306,6 +4323,8 @@ function splitBillAndOpenReport({ policyID: splitData.policyID, chatType: splitData.chatType, splitPayerAccountIDs, + taxCode, + taxAmount, }; API.write(WRITE_COMMANDS.SPLIT_BILL_AND_OPEN_REPORT, parameters, onyxData); @@ -4325,6 +4344,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 @@ -4343,6 +4364,8 @@ function startSplitBill({ category = '', tag = '', currency, + taxCode = '', + taxAmount = 0, }: StartSplitBilActionParams) { const currentUserEmailForIOUSplit = PhoneNumber.addSMSDomainIfPhoneNumber(currentUserLogin); const participantAccountIDs = participants.map((participant) => Number(participant.accountID)); @@ -4367,8 +4390,8 @@ function startSplitBill({ undefined, category, tag, - undefined, - undefined, + taxCode, + taxAmount, billable, ); @@ -4610,6 +4633,8 @@ function startSplitBill({ billable, ...(existingSplitChatReport ? {} : {createdReportActionID: splitChatCreatedReportAction.reportActionID}), chatType: splitChatReport?.chatType, + taxCode, + taxAmount, }; API.write(WRITE_COMMANDS.START_SPLIT_BILL, parameters, {optimisticData, successData, failureData}); @@ -4694,9 +4719,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) => { @@ -4760,8 +4787,8 @@ function completeSplitBill(chatReportID: string, reportAction: OnyxTypes.ReportA undefined, updatedTransaction?.category, updatedTransaction?.tag, - undefined, - undefined, + updatedTransaction?.taxCode, + isPolicyExpenseChat ? -splitTaxAmount : splitAmount, updatedTransaction?.billable, ); @@ -4835,6 +4862,8 @@ function completeSplitBill(chatReportID: string, reportAction: OnyxTypes.ReportA comment: transactionComment, category: transactionCategory, tag: transactionTag, + taxCode: transactionTaxCode, + taxAmount: transactionTaxAmount, } = ReportUtils.getTransactionDetails(updatedTransaction) ?? {}; const parameters: CompleteSplitBillParams = { @@ -4847,6 +4876,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}); @@ -6608,8 +6639,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 e74ed42454aa..44e7cc58d10c 100644 --- a/src/pages/iou/request/step/IOURequestStepAmount.tsx +++ b/src/pages/iou/request/step/IOURequestStepAmount.tsx @@ -82,7 +82,8 @@ 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 currentTransaction = isEditingSplitBill && !isEmptyObject(splitDraftTransaction) ? splitDraftTransaction : transaction; + const {amount: transactionAmount} = ReportUtils.getTransactionDetails(currentTransaction) ?? {amount: 0}; const {currency: originalCurrency} = ReportUtils.getTransactionDetails(isEditing ? draftTransaction : transaction) ?? {currency: CONST.CURRENCY.USD}; const currency = CurrencyUtils.isValidCurrencyCode(selectedCurrency) ? selectedCurrency : originalCurrency; @@ -169,6 +170,10 @@ function IOURequestStepAmount({ // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing IOU.setMoneyRequestAmount(transactionID, amountInSmallestCurrencyUnits, currency || CONST.CURRENCY.USD, shouldKeepUserInput); + // 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); return; @@ -273,25 +278,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(); }; diff --git a/src/pages/iou/request/step/IOURequestStepConfirmation.tsx b/src/pages/iou/request/step/IOURequestStepConfirmation.tsx index 939589598701..dd95e4577442 100644 --- a/src/pages/iou/request/step/IOURequestStepConfirmation.tsx +++ b/src/pages/iou/request/step/IOURequestStepConfirmation.tsx @@ -326,7 +326,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(); @@ -351,6 +353,8 @@ function IOURequestStepConfirmation({ category: transaction.category, tag: transaction.tag, currency: transaction.currency, + taxCode: transactionTaxCode, + taxAmount: transactionTaxAmount, }); } return; @@ -376,6 +380,8 @@ function IOURequestStepConfirmation({ iouRequestType: transaction.iouRequestType, splitShares: transaction.splitShares, splitPayerAccountIDs: transaction.splitPayerAccountIDs ?? [], + taxCode: transactionTaxCode, + taxAmount: transactionTaxAmount, }); } return; @@ -399,6 +405,8 @@ function IOURequestStepConfirmation({ iouRequestType: transaction.iouRequestType, splitShares: transaction.splitShares, splitPayerAccountIDs: transaction.splitPayerAccountIDs, + taxCode: transactionTaxCode, + taxAmount: transactionTaxAmount, }); } return; @@ -494,6 +502,8 @@ function IOURequestStepConfirmation({ policy, policyTags, policyCategories, + transactionTaxAmount, + transactionTaxCode, ], ); diff --git a/src/pages/iou/request/step/IOURequestStepDistanceRate.tsx b/src/pages/iou/request/step/IOURequestStepDistanceRate.tsx index 9aa769113b9c..dda4c862e5fd 100644 --- a/src/pages/iou/request/step/IOURequestStepDistanceRate.tsx +++ b/src/pages/iou/request/step/IOURequestStepDistanceRate.tsx @@ -82,7 +82,7 @@ function IOURequestStepDistanceRate({ const policyCustomUnitRate = policy?.customUnits[customUnitID].rates[customUnitRateID]; const taxRateExternalID = policyCustomUnitRate.attributes?.taxRateExternalID ?? ''; const taxAmount = CurrencyUtils.convertToBackendAmount(DistanceRequestUtils.calculateTaxAmount(policy, transaction, customUnitRateID)); - 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/IOURequestStepParticipants.tsx b/src/pages/iou/request/step/IOURequestStepParticipants.tsx index 189e204e810b..7b592b657c84 100644 --- a/src/pages/iou/request/step/IOURequestStepParticipants.tsx +++ b/src/pages/iou/request/step/IOURequestStepParticipants.tsx @@ -43,7 +43,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/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 0e74b7c392ae..2dddae7512c2 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, ], ); diff --git a/src/pages/iou/request/step/IOURequestStepTaxAmountPage.tsx b/src/pages/iou/request/step/IOURequestStepTaxAmountPage.tsx index 27c2808669df..69430131c28e 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,17 @@ 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 currentTransaction = isEditingSplitBill && !isEmptyObject(splitDraftTransaction) ? splitDraftTransaction : transaction; + const transactionDetails = ReportUtils.getTransactionDetails(currentTransaction); const currency = CurrencyUtils.isValidCurrencyCode(selectedCurrency) ? selectedCurrency : transactionDetails?.currency; useFocusEffect( @@ -102,19 +109,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(currentTransaction, 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); // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing IOU.setMoneyRequestCurrency(transactionID, currency || CONST.CURRENCY.USD); @@ -151,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} @@ -164,6 +177,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..a237b47777b2 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); navigateBack(); }; @@ -107,9 +123,10 @@ function IOURequestStepTaxRatePage({ ); @@ -118,6 +135,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 = {