From c6111876d7a99b59db83cd2e8fe48403c2708b91 Mon Sep 17 00:00:00 2001 From: Monil Bhavsar Date: Mon, 15 Apr 2024 19:43:16 +0530 Subject: [PATCH 01/12] Pass taxCode and taxAmount params for split bill request --- src/libs/API/parameters/SplitBillParams.ts | 2 ++ src/libs/actions/IOU.ts | 13 ++++++++++++- .../iou/request/step/IOURequestStepConfirmation.tsx | 4 ++++ 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/libs/API/parameters/SplitBillParams.ts b/src/libs/API/parameters/SplitBillParams.ts index 310923093d5e..0f121da76025 100644 --- a/src/libs/API/parameters/SplitBillParams.ts +++ b/src/libs/API/parameters/SplitBillParams.ts @@ -14,6 +14,8 @@ type SplitBillParams = { createdReportActionID?: string; policyID: string | undefined; chatType: string | undefined; + taxCode: string; + taxAmount: number; }; export default SplitBillParams; diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index cd0264ddb6ea..8601b37b979a 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -2543,6 +2543,7 @@ function createSplitsAndOnyxData( existingSplitChatReportID = '', billable = false, iouRequestType: IOURequestType = CONST.IOU.REQUEST_TYPE.MANUAL, + taxAmount: number, ): SplitsAndOnyxData { const currentUserEmailForIOUSplit = PhoneNumber.addSMSDomainIfPhoneNumber(currentUserLogin); const participantAccountIDs = participants.map((participant) => Number(participant.accountID)); @@ -2709,7 +2710,8 @@ function createSplitsAndOnyxData( // Loop through participants creating individual chats, iouReports and reportActionIDs as needed const splitAmount = IOUUtils.calculateAmount(participants.length, amount, currency, false); - const splits: Split[] = [{email: currentUserEmailForIOUSplit, accountID: currentUserAccountID, amount: IOUUtils.calculateAmount(participants.length, amount, currency, true)}]; + const splitTaxAmount = IOUUtils.calculateAmount(participants.length, taxAmount, currency, false); + const splits: Split[] = [{email: currentUserEmailForIOUSplit, accountID: currentUserAccountID, amount: IOUUtils.calculateAmount(participants.length, amount, currency, true), taxAmount: IOUUtils.calculateAmount(participants.length, taxAmount, currency, true)}]; const hasMultipleParticipants = participants.length > 1; participants.forEach((participant) => { @@ -2863,6 +2865,7 @@ function createSplitsAndOnyxData( reportPreviewReportActionID: oneOnOneReportPreviewAction.reportActionID, transactionThreadReportID: optimisticTransactionThread.reportID, createdReportActionIDForThread: optimisticCreatedActionForTransactionThread.reportActionID, + taxAmount: splitTaxAmount, }; splits.push(individualSplit); @@ -2904,6 +2907,8 @@ type SplitBillActionsParams = { billable?: boolean; iouRequestType?: IOURequestType; existingSplitChatReportID?: string; + taxCode?: string; + taxAmount?: number; }; /** @@ -2924,7 +2929,10 @@ function splitBill({ billable = false, iouRequestType = CONST.IOU.REQUEST_TYPE.MANUAL, existingSplitChatReportID = '', + taxCode = '', + taxAmount = 0, }: SplitBillActionsParams) { + console.debug(taxCode); const currentCreated = DateUtils.enrichMoneyRequestTimestamp(created); const {splitData, splits, onyxData} = createSplitsAndOnyxData( participants, @@ -2940,6 +2948,7 @@ function splitBill({ existingSplitChatReportID, billable, iouRequestType, + taxAmount, ); const parameters: SplitBillParams = { @@ -2958,6 +2967,8 @@ function splitBill({ createdReportActionID: splitData.createdReportActionID, policyID: splitData.policyID, chatType: splitData.chatType, + taxCode, + taxAmount, }; API.write(WRITE_COMMANDS.SPLIT_BILL, parameters, onyxData); diff --git a/src/pages/iou/request/step/IOURequestStepConfirmation.tsx b/src/pages/iou/request/step/IOURequestStepConfirmation.tsx index 83f831708799..1e1ab4da3092 100644 --- a/src/pages/iou/request/step/IOURequestStepConfirmation.tsx +++ b/src/pages/iou/request/step/IOURequestStepConfirmation.tsx @@ -307,6 +307,8 @@ function IOURequestStepConfirmation({ existingSplitChatReportID: report?.reportID, billable: transaction.billable, iouRequestType: transaction.iouRequestType, + taxCode: transaction.taxCode, + taxAmount: transaction.taxAmount, }); } return; @@ -328,6 +330,8 @@ function IOURequestStepConfirmation({ tag: transaction.tag, billable: !!transaction.billable, iouRequestType: transaction.iouRequestType, + taxCode: transaction.taxCode, + taxAmount: transaction.taxAmount, }); } return; From 477b381560e2119ed6f4b1ebfc33f744e57c64de Mon Sep 17 00:00:00 2001 From: Monil Bhavsar Date: Wed, 15 May 2024 18:01:33 +0530 Subject: [PATCH 02/12] Split amount based on new logic and update optimistic transaction --- src/libs/actions/IOU.ts | 22 ++++++++++++++----- .../step/IOURequestStepConfirmation.tsx | 8 +++---- src/types/onyx/IOU.ts | 1 + 3 files changed, 21 insertions(+), 10 deletions(-) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 3e78117e2b71..3347e617220f 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -3726,6 +3726,7 @@ function createSplitsAndOnyxData( existingSplitChatReportID = '', billable = false, iouRequestType: IOURequestType = CONST.IOU.REQUEST_TYPE.MANUAL, + taxCode: string, taxAmount: number, ): SplitsAndOnyxData { const currentUserEmailForIOUSplit = PhoneNumber.addSMSDomainIfPhoneNumber(currentUserLogin); @@ -3748,8 +3749,8 @@ function createSplitsAndOnyxData( undefined, category, tag, - undefined, - undefined, + taxCode, + taxAmount, billable, ); @@ -3904,14 +3905,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 splitTaxAmount = IOUUtils.calculateAmount(participants.length, taxAmount, currency, false); - const splits: Split[] = [{email: currentUserEmailForIOUSplit, accountID: currentUserAccountID, amount: currentUserAmount}]; + const currentUserTaxAmount = IOUUtils.calculateAmount(participants.length, taxAmount, currency, true); + + 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. @@ -3981,8 +3984,8 @@ function createSplitsAndOnyxData( undefined, category, tag, - undefined, - undefined, + taxCode, + splitTaxAmount, billable, ); @@ -4172,6 +4175,7 @@ function splitBill({ existingSplitChatReportID, billable, iouRequestType, + taxCode, taxAmount, ); @@ -4220,6 +4224,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( @@ -4237,6 +4243,8 @@ function splitBillAndOpenReport({ '', billable, iouRequestType, + taxCode, + taxAmount, ); const parameters: SplitBillParams = { @@ -4256,6 +4264,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/IOURequestStepConfirmation.tsx b/src/pages/iou/request/step/IOURequestStepConfirmation.tsx index 050403e8f79f..e0e5f8b45fec 100644 --- a/src/pages/iou/request/step/IOURequestStepConfirmation.tsx +++ b/src/pages/iou/request/step/IOURequestStepConfirmation.tsx @@ -375,8 +375,8 @@ function IOURequestStepConfirmation({ iouRequestType: transaction.iouRequestType, splitShares: transaction.splitShares, splitPayerAccountIDs: transaction.splitPayerAccountIDs ?? [], - taxCode: transaction.taxCode, - taxAmount: transaction.taxAmount, + taxCode: transactionTaxCode, + taxAmount: transactionTaxAmount, }); } return; @@ -400,8 +400,8 @@ function IOURequestStepConfirmation({ iouRequestType: transaction.iouRequestType, splitShares: transaction.splitShares, splitPayerAccountIDs: transaction.splitPayerAccountIDs, - taxCode: transaction.taxCode, - taxAmount: transaction.taxAmount, + taxCode: transactionTaxCode, + taxAmount: transactionTaxAmount, }); } return; diff --git a/src/types/onyx/IOU.ts b/src/types/onyx/IOU.ts index 8b0ca935f4b3..3eb65f29776a 100644 --- a/src/types/onyx/IOU.ts +++ b/src/types/onyx/IOU.ts @@ -41,6 +41,7 @@ type Split = { reportPreviewReportActionID?: string; transactionThreadReportID?: string; createdReportActionIDForThread?: string; + taxAmount?: number; }; type IOU = { From ec4847690017620986b5c823aa568dc225bf2811 Mon Sep 17 00:00:00 2001 From: Monil Bhavsar Date: Wed, 15 May 2024 19:30:32 +0530 Subject: [PATCH 03/12] Fix lint --- src/libs/actions/IOU.ts | 5 ++--- src/pages/iou/request/step/IOURequestStepConfirmation.tsx | 2 ++ 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 3347e617220f..f8e8bc26a954 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -3726,8 +3726,8 @@ function createSplitsAndOnyxData( existingSplitChatReportID = '', billable = false, iouRequestType: IOURequestType = CONST.IOU.REQUEST_TYPE.MANUAL, - taxCode: string, - taxAmount: number, + taxCode = '', + taxAmount = 0, ): SplitsAndOnyxData { const currentUserEmailForIOUSplit = PhoneNumber.addSMSDomainIfPhoneNumber(currentUserLogin); const participantAccountIDs = participants.map((participant) => Number(participant.accountID)); @@ -4158,7 +4158,6 @@ function splitBill({ taxCode = '', taxAmount = 0, }: SplitBillActionsParams) { - console.debug(taxCode); const currentCreated = DateUtils.enrichMoneyRequestTimestamp(created); const {splitData, splits, onyxData} = createSplitsAndOnyxData( participants, diff --git a/src/pages/iou/request/step/IOURequestStepConfirmation.tsx b/src/pages/iou/request/step/IOURequestStepConfirmation.tsx index e0e5f8b45fec..013d93061fb0 100644 --- a/src/pages/iou/request/step/IOURequestStepConfirmation.tsx +++ b/src/pages/iou/request/step/IOURequestStepConfirmation.tsx @@ -497,6 +497,8 @@ function IOURequestStepConfirmation({ policy, policyTags, policyCategories, + transactionTaxAmount, + transactionTaxCode, ], ); From de501561616a7ab21b39c22076b04a8dc9fd77ce Mon Sep 17 00:00:00 2001 From: Monil Bhavsar Date: Thu, 16 May 2024 17:00:48 +0530 Subject: [PATCH 04/12] Fix bug when selectedReportID and hence policy is undefined on confirmation page --- src/pages/iou/request/step/IOURequestStepParticipants.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) 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; From c667482ecc6dfbfd57d757f8dfbe61ef5b730945 Mon Sep 17 00:00:00 2001 From: Monil Bhavsar Date: Tue, 21 May 2024 19:51:52 +0530 Subject: [PATCH 05/12] Fix taxAmount not being calculated --- src/components/MoneyRequestConfirmationList.tsx | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/src/components/MoneyRequestConfirmationList.tsx b/src/components/MoneyRequestConfirmationList.tsx index 33f1258e60c7..779126eed3b9 100755 --- a/src/components/MoneyRequestConfirmationList.tsx +++ b/src/components/MoneyRequestConfirmationList.tsx @@ -372,14 +372,9 @@ function MoneyRequestConfirmationList({ // 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]); + const taxAmountInSmallestCurrencyUnits = CurrencyUtils.convertToBackendAmount(Number.parseFloat(taxAmount)); + IOU.setMoneyRequestTaxAmount(transactionID, taxAmountInSmallestCurrencyUnits, true); + }, [policy, transaction, transactionID]); // If completing a split expense fails, set didConfirm to false to allow the user to edit the fields again if (isEditingSplitBill && didConfirm) { From cdfb3adcd2f002064a806c9cdc9471e2355cbb9f Mon Sep 17 00:00:00 2001 From: Monil Bhavsar Date: Tue, 21 May 2024 21:02:15 +0530 Subject: [PATCH 06/12] Handle editing split bill case --- .../request/step/IOURequestStepTaxAmountPage.tsx | 15 +++++++++++---- .../request/step/IOURequestStepTaxRatePage.tsx | 14 ++++++++++++-- 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/src/pages/iou/request/step/IOURequestStepTaxAmountPage.tsx b/src/pages/iou/request/step/IOURequestStepTaxAmountPage.tsx index 94f8e2be2f6c..39e86d0a1317 100644 --- a/src/pages/iou/request/step/IOURequestStepTaxAmountPage.tsx +++ b/src/pages/iou/request/step/IOURequestStepTaxAmountPage.tsx @@ -62,6 +62,7 @@ function IOURequestStepTaxAmountPage({ const {translate} = useLocalize(); const textInput = useRef(); const isEditing = action === CONST.IOU.ACTION.EDIT; + const isEditingSplitBill = isEditing && iouType === CONST.IOU.TYPE.SPLIT; const focusTimeoutRef = useRef(); @@ -102,19 +103,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); diff --git a/src/pages/iou/request/step/IOURequestStepTaxRatePage.tsx b/src/pages/iou/request/step/IOURequestStepTaxRatePage.tsx index 9376aa65e4e6..5e8fc99ecfb4 100644 --- a/src/pages/iou/request/step/IOURequestStepTaxRatePage.tsx +++ b/src/pages/iou/request/step/IOURequestStepTaxRatePage.tsx @@ -40,7 +40,7 @@ function getTaxAmount(policy: OnyxEntry, transaction: OnyxEntry { @@ -65,7 +66,16 @@ function IOURequestStepTaxRatePage({ return; } - const taxAmount = getTaxAmount(policy, transaction, taxes?.code, TransactionUtils.getAmount(transaction, false, true)); + const taxAmount = getTaxAmount(policy, transaction, taxes.code, TransactionUtils.getAmount(transaction, false, true)); + + if (isEditingSplitBill) { + IOU.setDraftSplitTransaction(transaction.transactionID, { + taxAmount: CurrencyUtils.convertToBackendAmount(taxAmount ?? 0), + taxCode: taxes.code, + }); + navigateBack(); + return; + } if (isEditing) { const newTaxCode = taxes.code; From 78bf98a517bb68e0044fb5811935ca882c323e64 Mon Sep 17 00:00:00 2001 From: Monil Bhavsar Date: Wed, 22 May 2024 00:19:07 +0530 Subject: [PATCH 07/12] Handle scan split requests optimistically --- .../iou/request/step/IOURequestStepAmount.tsx | 36 ++++++++----------- .../step/IOURequestStepTaxAmountPage.tsx | 13 ++++++- .../step/IOURequestStepTaxRatePage.tsx | 30 +++++++++++----- 3 files changed, 47 insertions(+), 32 deletions(-) diff --git a/src/pages/iou/request/step/IOURequestStepAmount.tsx b/src/pages/iou/request/step/IOURequestStepAmount.tsx index 18dc951f949b..0609c7d081dc 100644 --- a/src/pages/iou/request/step/IOURequestStepAmount.tsx +++ b/src/pages/iou/request/step/IOURequestStepAmount.tsx @@ -26,6 +26,7 @@ import StepScreenWrapper from './StepScreenWrapper'; import withFullTransactionOrNotFound from './withFullTransactionOrNotFound'; import type {WithWritableReportOrNotFoundProps} from './withWritableReportOrNotFound'; import withWritableReportOrNotFound from './withWritableReportOrNotFound'; +import lodashIsEmpty from "lodash/isEmpty"; type AmountParams = { amount: string; @@ -39,9 +40,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 +65,6 @@ function IOURequestStepAmount({ currentUserPersonalDetails, splitDraftTransaction, skipConfirmation, - draftTransaction, }: IOURequestStepAmountProps) { const {translate} = useLocalize(); const textInput = useRef(null); @@ -78,8 +75,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 +267,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 +324,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/IOURequestStepTaxAmountPage.tsx b/src/pages/iou/request/step/IOURequestStepTaxAmountPage.tsx index 39e86d0a1317..a35d7979cc4a 100644 --- a/src/pages/iou/request/step/IOURequestStepTaxAmountPage.tsx +++ b/src/pages/iou/request/step/IOURequestStepTaxAmountPage.tsx @@ -20,6 +20,7 @@ import StepScreenWrapper from './StepScreenWrapper'; import withFullTransactionOrNotFound from './withFullTransactionOrNotFound'; import type {WithWritableReportOrNotFoundProps} from './withWritableReportOrNotFound'; import withWritableReportOrNotFound from './withWritableReportOrNotFound'; +import {isEmptyObject} from "@src/types/utils/EmptyObject"; type IOURequestStepTaxAmountPageOnyxProps = { policy: OnyxEntry; @@ -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,6 +62,7 @@ function IOURequestStepTaxAmountPage({ policy, policyTags, policyCategories, + splitDraftTransaction, }: IOURequestStepTaxAmountPageProps) { const {translate} = useLocalize(); const textInput = useRef(); @@ -66,7 +71,7 @@ function IOURequestStepTaxAmountPage({ 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( @@ -171,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 5e8fc99ecfb4..ec42a1553cfb 100644 --- a/src/pages/iou/request/step/IOURequestStepTaxRatePage.tsx +++ b/src/pages/iou/request/step/IOURequestStepTaxRatePage.tsx @@ -16,6 +16,7 @@ import StepScreenWrapper from './StepScreenWrapper'; import withFullTransactionOrNotFound from './withFullTransactionOrNotFound'; import type {WithWritableReportOrNotFoundProps} from './withWritableReportOrNotFound'; import withWritableReportOrNotFound from './withWritableReportOrNotFound'; +import {isEmptyObject} from "@src/types/utils/EmptyObject"; 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 & @@ -47,29 +51,31 @@ function IOURequestStepTaxRatePage({ report, policyCategories, policyTags, + splitDraftTransaction, }: IOURequestStepTaxRatePageProps) { const {translate} = useLocalize(); 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 navigateBack = () => { 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(transaction.transactionID, { + IOU.setDraftSplitTransaction(currentTransaction.transactionID, { taxAmount: CurrencyUtils.convertToBackendAmount(taxAmount ?? 0), taxCode: taxes.code, }); @@ -79,12 +85,12 @@ function IOURequestStepTaxRatePage({ 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), @@ -101,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(); }; @@ -117,7 +123,7 @@ function IOURequestStepTaxRatePage({ @@ -128,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'}`, }, From 776856c27218d81a03d6ff12b2954d88322f3622 Mon Sep 17 00:00:00 2001 From: Monil Bhavsar Date: Wed, 22 May 2024 00:25:47 +0530 Subject: [PATCH 08/12] Fix lint --- src/components/MoneyRequestConfirmationList.tsx | 3 --- src/pages/iou/request/step/IOURequestStepAmount.tsx | 1 - src/pages/iou/request/step/IOURequestStepTaxAmountPage.tsx | 2 +- src/pages/iou/request/step/IOURequestStepTaxRatePage.tsx | 4 ++-- 4 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/components/MoneyRequestConfirmationList.tsx b/src/components/MoneyRequestConfirmationList.tsx index 779126eed3b9..18cf28e171c0 100755 --- a/src/components/MoneyRequestConfirmationList.tsx +++ b/src/components/MoneyRequestConfirmationList.tsx @@ -315,9 +315,6 @@ 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 isFocused = useIsFocused(); const [formError, debouncedFormError, setFormError] = useDebouncedState(''); diff --git a/src/pages/iou/request/step/IOURequestStepAmount.tsx b/src/pages/iou/request/step/IOURequestStepAmount.tsx index 0609c7d081dc..4ae2784f61b1 100644 --- a/src/pages/iou/request/step/IOURequestStepAmount.tsx +++ b/src/pages/iou/request/step/IOURequestStepAmount.tsx @@ -26,7 +26,6 @@ import StepScreenWrapper from './StepScreenWrapper'; import withFullTransactionOrNotFound from './withFullTransactionOrNotFound'; import type {WithWritableReportOrNotFoundProps} from './withWritableReportOrNotFound'; import withWritableReportOrNotFound from './withWritableReportOrNotFound'; -import lodashIsEmpty from "lodash/isEmpty"; type AmountParams = { amount: string; diff --git a/src/pages/iou/request/step/IOURequestStepTaxAmountPage.tsx b/src/pages/iou/request/step/IOURequestStepTaxAmountPage.tsx index a35d7979cc4a..be266c455ec1 100644 --- a/src/pages/iou/request/step/IOURequestStepTaxAmountPage.tsx +++ b/src/pages/iou/request/step/IOURequestStepTaxAmountPage.tsx @@ -16,11 +16,11 @@ 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'; import withWritableReportOrNotFound from './withWritableReportOrNotFound'; -import {isEmptyObject} from "@src/types/utils/EmptyObject"; type IOURequestStepTaxAmountPageOnyxProps = { policy: OnyxEntry; diff --git a/src/pages/iou/request/step/IOURequestStepTaxRatePage.tsx b/src/pages/iou/request/step/IOURequestStepTaxRatePage.tsx index ec42a1553cfb..d048cae15089 100644 --- a/src/pages/iou/request/step/IOURequestStepTaxRatePage.tsx +++ b/src/pages/iou/request/step/IOURequestStepTaxRatePage.tsx @@ -12,11 +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 {isEmptyObject} from "@src/types/utils/EmptyObject"; +import type {WithWritableReportOrNotFoundProps} from './withWritableReportOrNotFound'; type IOURequestStepTaxRatePageOnyxProps = { policy: OnyxEntry; From 6dd21cbcaf4ce7a06894b2c6c5957cdf5ee70b36 Mon Sep 17 00:00:00 2001 From: Monil Bhavsar Date: Thu, 23 May 2024 00:31:09 +0530 Subject: [PATCH 09/12] Check for ownerAccountID in case of split with policy --- src/pages/iou/request/step/IOURequestStepConfirmation.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/iou/request/step/IOURequestStepConfirmation.tsx b/src/pages/iou/request/step/IOURequestStepConfirmation.tsx index 013d93061fb0..d889bc69f0fb 100644 --- a/src/pages/iou/request/step/IOURequestStepConfirmation.tsx +++ b/src/pages/iou/request/step/IOURequestStepConfirmation.tsx @@ -325,7 +325,7 @@ 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(); From 787c520289d4b8c52c3ff5bbd63acaf0720577f8 Mon Sep 17 00:00:00 2001 From: Monil Bhavsar Date: Thu, 23 May 2024 16:50:31 +0530 Subject: [PATCH 10/12] Fix taxAmount is negative optimistically --- src/libs/actions/IOU.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index f8e8bc26a954..91da96b94ed3 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -3985,7 +3985,7 @@ function createSplitsAndOnyxData( category, tag, taxCode, - splitTaxAmount, + ReportUtils.isExpenseReport(oneOnOneIOUReport) ? -splitTaxAmount : splitTaxAmount, billable, ); From d136b4a4da22b26da099c4bc74166f51b224e59c Mon Sep 17 00:00:00 2001 From: Monil Bhavsar Date: Thu, 23 May 2024 16:56:06 +0530 Subject: [PATCH 11/12] Fix lint and typecheck --- src/pages/iou/request/step/IOURequestStepConfirmation.tsx | 4 +++- src/types/onyx/IOU.ts | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/pages/iou/request/step/IOURequestStepConfirmation.tsx b/src/pages/iou/request/step/IOURequestStepConfirmation.tsx index d889bc69f0fb..d0cdf956e07d 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.isPolicyExpenseChat ? participant?.ownerAccountID ?? -1 : participant.accountID ?? -1)); + splitParticipants = selectedParticipants.filter((participant) => + participantsWithAmount.includes(participant.isPolicyExpenseChat ? participant?.ownerAccountID ?? -1 : participant.accountID ?? -1), + ); } const trimmedComment = (transaction?.comment.comment ?? '').trim(); diff --git a/src/types/onyx/IOU.ts b/src/types/onyx/IOU.ts index 3eb65f29776a..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 = { From c671c7a080a35e331fbfc6476895e64bb2aebc28 Mon Sep 17 00:00:00 2001 From: Monil Bhavsar Date: Thu, 23 May 2024 20:03:45 +0530 Subject: [PATCH 12/12] Fix bug when updating taxAmount and refactor --- .../MoneyRequestConfirmationList.tsx | 55 ++++++++++++------- 1 file changed, 35 insertions(+), 20 deletions(-) diff --git a/src/components/MoneyRequestConfirmationList.tsx b/src/components/MoneyRequestConfirmationList.tsx index 18cf28e171c0..41ab893ecc7d 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,6 +308,9 @@ 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); const isFocused = useIsFocused(); const [formError, debouncedFormError, setFormError] = useDebouncedState(''); @@ -342,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'); @@ -366,13 +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 taxAmountInSmallestCurrencyUnits = CurrencyUtils.convertToBackendAmount(Number.parseFloat(taxAmount)); - IOU.setMoneyRequestTaxAmount(transactionID, taxAmountInSmallestCurrencyUnits, true); - }, [policy, transaction, transactionID]); - // If completing a split expense fails, set didConfirm to false to allow the user to edit the fields again if (isEditingSplitBill && didConfirm) { setDidConfirm(false);