From f0d6aaa5fdfc7c4cdc6af1af8b95dcd0f40059a4 Mon Sep 17 00:00:00 2001 From: Monil Bhavsar Date: Tue, 14 May 2024 18:19:22 +0530 Subject: [PATCH 1/5] Set tax details for track expense on policy expense chat --- .../step/IOURequestStepParticipants.tsx | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/src/pages/iou/request/step/IOURequestStepParticipants.tsx b/src/pages/iou/request/step/IOURequestStepParticipants.tsx index 45e644607d349..22d5dcedd4711 100644 --- a/src/pages/iou/request/step/IOURequestStepParticipants.tsx +++ b/src/pages/iou/request/step/IOURequestStepParticipants.tsx @@ -20,6 +20,9 @@ import type {WithFullTransactionOrNotFoundProps} from './withFullTransactionOrNo import withFullTransactionOrNotFound from './withFullTransactionOrNotFound'; import type {WithWritableReportOrNotFoundProps} from './withWritableReportOrNotFound'; import withWritableReportOrNotFound from './withWritableReportOrNotFound'; +import policy from "@src/types/onyx/Policy"; +import * as CurrencyUtils from "@libs/CurrencyUtils"; +import {getPolicy, isTaxTrackingEnabled} from "@libs/PolicyUtils"; type IOURequestStepParticipantsOnyxProps = { /** Whether the confirmation step should be skipped */ @@ -96,12 +99,41 @@ function IOURequestStepParticipants({ return; } + // If user is categorizing tracked expense with respective workspace on policy expense chat then update tax details on draft transaction + setTaxDetailsOnCategorizing(val); + // When a participant is selected, the reportID needs to be saved because that's the reportID that will be used in the confirmation step. selectedReportID.current = val[0]?.reportID ?? reportID; }, [reportID, transactionID], ); + const setTaxDetailsOnCategorizing = useCallback( + (participants: Participant[]) => { + const isCategorizing = action === CONST.IOU.ACTION.CATEGORIZE; + if (!isCategorizing) { + return; + } + + const isPolicyExpenseChat = participants.length === 1 && participants[0]?.isPolicyExpenseChat; + if (!isPolicyExpenseChat) { + return; + } + + const policyID = participants[0].policyID; + const policy= getPolicy(policyID); + if (!isTaxTrackingEnabled(isPolicyExpenseChat, policy)) { + return; + } + const taxCode = TransactionUtils.getDefaultTaxCode(policy, transaction) ?? ''; + const taxPercentage = TransactionUtils.getTaxValue(policy, transaction, taxCode) ?? ''; + const taxAmount = CurrencyUtils.convertToBackendAmount(TransactionUtils.calculateTaxAmount(taxPercentage, transaction?.amount ?? 0)); + IOU.setMoneyRequestTaxRate(transactionID, taxCode, true); + IOU.setMoneyRequestTaxAmount(transactionID, taxAmount, true); + }, + [action] + ); + const goToNextStep = useCallback(() => { const isCategorizing = action === CONST.IOU.ACTION.CATEGORIZE; From 0bd0536a0c5a386284b312a90b6782cd1d5294f9 Mon Sep 17 00:00:00 2001 From: Monil Bhavsar Date: Tue, 14 May 2024 18:20:18 +0530 Subject: [PATCH 2/5] Set tax rate on a draft transaction --- src/libs/actions/IOU.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 708060c481314..6a95ebd156385 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -6517,8 +6517,8 @@ function setMoneyRequestParticipantsFromReport(transactionID: string, report: On return participants; } -function setMoneyRequestTaxRate(transactionID: string, taxCode: string) { - Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}`, {taxCode}); +function setMoneyRequestTaxRate(transactionID: string, taxCode: string, isDraft: boolean) { + Onyx.merge(`${isDraft ? ONYXKEYS.COLLECTION.TRANSACTION_DRAFT : ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, {taxCode}); } function setMoneyRequestTaxAmount(transactionID: string, taxAmount: number, isDraft: boolean) { From f65039ba02d0e0380da4a408773d0e53a528ea5c Mon Sep 17 00:00:00 2001 From: Monil Bhavsar Date: Tue, 14 May 2024 18:21:04 +0530 Subject: [PATCH 3/5] Udpate function definition and fix typescript check --- src/libs/PolicyUtils.ts | 2 +- src/libs/TransactionUtils.ts | 5 +++-- src/pages/iou/request/step/IOURequestStepTaxRatePage.tsx | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/libs/PolicyUtils.ts b/src/libs/PolicyUtils.ts index c234a61bdbd91..7672be56d1020 100644 --- a/src/libs/PolicyUtils.ts +++ b/src/libs/PolicyUtils.ts @@ -265,7 +265,7 @@ function isPaidGroupPolicy(policy: OnyxEntry | EmptyObject): boolean { return policy?.type === CONST.POLICY.TYPE.TEAM || policy?.type === CONST.POLICY.TYPE.CORPORATE; } -function isTaxTrackingEnabled(isPolicyExpenseChat: boolean, policy: OnyxEntry): boolean { +function isTaxTrackingEnabled(isPolicyExpenseChat: boolean, policy: OnyxEntry | EmptyObject): boolean { return (isPolicyExpenseChat && (policy?.tax?.trackingEnabled ?? policy?.isTaxTrackingEnabled)) ?? false; } diff --git a/src/libs/TransactionUtils.ts b/src/libs/TransactionUtils.ts index aa9a874049242..c1a9f628b0ab4 100644 --- a/src/libs/TransactionUtils.ts +++ b/src/libs/TransactionUtils.ts @@ -13,6 +13,7 @@ import DateUtils from './DateUtils'; import * as Localize from './Localize'; import * as NumberUtils from './NumberUtils'; import {getCleanedTagName} from './PolicyUtils'; +import type {EmptyObject} from '@src/types/utils/EmptyObject'; let allTransactions: OnyxCollection = {}; @@ -658,7 +659,7 @@ function getRateID(transaction: OnyxEntry): string | undefined { * Gets the tax code based on selected currency. * Returns policy default tax rate if transaction is in policy default currency, otherwise returns foreign default tax rate */ -function getDefaultTaxCode(policy: OnyxEntry, transaction: OnyxEntry, currency?: string | undefined) { +function getDefaultTaxCode(policy: OnyxEntry | EmptyObject, transaction: OnyxEntry, currency?: string | undefined) { const defaultExternalID = policy?.taxRates?.defaultExternalID; const foreignTaxDefault = policy?.taxRates?.foreignTaxDefault; return policy?.outputCurrency === (currency ?? getCurrency(transaction)) ? defaultExternalID : foreignTaxDefault; @@ -691,7 +692,7 @@ function transformedTaxRates(policy: OnyxEntry | undefined, transaction? /** * Gets the tax value of a selected tax */ -function getTaxValue(policy: OnyxEntry, transaction: OnyxEntry, taxCode: string) { +function getTaxValue(policy: OnyxEntry | EmptyObject, transaction: OnyxEntry, taxCode: string) { return Object.values(transformedTaxRates(policy, transaction)).find((taxRate) => taxRate.code === taxCode)?.value; } diff --git a/src/pages/iou/request/step/IOURequestStepTaxRatePage.tsx b/src/pages/iou/request/step/IOURequestStepTaxRatePage.tsx index 9376aa65e4e66..66bb49156b4b7 100644 --- a/src/pages/iou/request/step/IOURequestStepTaxRatePage.tsx +++ b/src/pages/iou/request/step/IOURequestStepTaxRatePage.tsx @@ -91,7 +91,7 @@ function IOURequestStepTaxRatePage({ return; } const amountInSmallestCurrencyUnits = CurrencyUtils.convertToBackendAmount(taxAmount); - IOU.setMoneyRequestTaxRate(transaction?.transactionID, taxes?.code ?? ''); + IOU.setMoneyRequestTaxRate(transaction?.transactionID, taxes?.code ?? '', true); IOU.setMoneyRequestTaxAmount(transaction.transactionID, amountInSmallestCurrencyUnits, true); navigateBack(); From a1fded9addc2af778457304df4aade947b3f8abf Mon Sep 17 00:00:00 2001 From: Monil Bhavsar Date: Tue, 14 May 2024 18:49:11 +0530 Subject: [PATCH 4/5] Fix lint errors --- src/libs/TransactionUtils.ts | 2 +- .../step/IOURequestStepParticipants.tsx | 56 +++++++++---------- 2 files changed, 28 insertions(+), 30 deletions(-) diff --git a/src/libs/TransactionUtils.ts b/src/libs/TransactionUtils.ts index c1a9f628b0ab4..8e9297dbf1941 100644 --- a/src/libs/TransactionUtils.ts +++ b/src/libs/TransactionUtils.ts @@ -6,6 +6,7 @@ import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type {Policy, RecentWaypoint, Report, TaxRate, TaxRates, Transaction, TransactionViolation} from '@src/types/onyx'; import type {Comment, Receipt, TransactionChanges, TransactionPendingFieldsKey, Waypoint, WaypointCollection} from '@src/types/onyx/Transaction'; +import type {EmptyObject} from '@src/types/utils/EmptyObject'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; import type {IOURequestType} from './actions/IOU'; import {isCorporateCard, isExpensifyCard} from './CardUtils'; @@ -13,7 +14,6 @@ import DateUtils from './DateUtils'; import * as Localize from './Localize'; import * as NumberUtils from './NumberUtils'; import {getCleanedTagName} from './PolicyUtils'; -import type {EmptyObject} from '@src/types/utils/EmptyObject'; let allTransactions: OnyxCollection = {}; diff --git a/src/pages/iou/request/step/IOURequestStepParticipants.tsx b/src/pages/iou/request/step/IOURequestStepParticipants.tsx index 22d5dcedd4711..d645f53e866a1 100644 --- a/src/pages/iou/request/step/IOURequestStepParticipants.tsx +++ b/src/pages/iou/request/step/IOURequestStepParticipants.tsx @@ -15,14 +15,13 @@ import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; import type {Participant} from '@src/types/onyx/IOU'; +import * as CurrencyUtils from "@libs/CurrencyUtils"; +import {getPolicy, isTaxTrackingEnabled} from "@libs/PolicyUtils"; import StepScreenWrapper from './StepScreenWrapper'; import type {WithFullTransactionOrNotFoundProps} from './withFullTransactionOrNotFound'; import withFullTransactionOrNotFound from './withFullTransactionOrNotFound'; import type {WithWritableReportOrNotFoundProps} from './withWritableReportOrNotFound'; import withWritableReportOrNotFound from './withWritableReportOrNotFound'; -import policy from "@src/types/onyx/Policy"; -import * as CurrencyUtils from "@libs/CurrencyUtils"; -import {getPolicy, isTaxTrackingEnabled} from "@libs/PolicyUtils"; type IOURequestStepParticipantsOnyxProps = { /** Whether the confirmation step should be skipped */ @@ -84,6 +83,31 @@ function IOURequestStepParticipants({ IOU.navigateToStartStepIfScanFileCannotBeRead(receiptFilename ?? '', receiptPath ?? '', () => {}, iouRequestType, iouType, transactionID, reportID, receiptType ?? ''); }, [receiptType, receiptPath, receiptFilename, iouRequestType, iouType, transactionID, reportID, action]); + const setTaxDetailsOnCategorizing = useCallback( + (participantsArray: Participant[]) => { + if (action !== CONST.IOU.ACTION.CATEGORIZE) { + return; + } + + const isPolicyExpenseChat = participantsArray.length === 1 && participantsArray[0]?.isPolicyExpenseChat; + if (!isPolicyExpenseChat) { + return; + } + + const policyID = participantsArray[0].policyID; + const policy = getPolicy(policyID); + if (!isTaxTrackingEnabled(isPolicyExpenseChat, policy)) { + return; + } + const taxCode = TransactionUtils.getDefaultTaxCode(policy, transaction) ?? ''; + const taxPercentage = TransactionUtils.getTaxValue(policy, transaction, taxCode) ?? ''; + const taxAmount = CurrencyUtils.convertToBackendAmount(TransactionUtils.calculateTaxAmount(taxPercentage, transaction?.amount ?? 0)); + IOU.setMoneyRequestTaxRate(transactionID, taxCode, true); + IOU.setMoneyRequestTaxAmount(transactionID, taxAmount, true); + }, + [action, transaction, transactionID] + ); + const addParticipant = useCallback( (val: Participant[]) => { IOU.setMoneyRequestParticipants(transactionID, val); @@ -108,32 +132,6 @@ function IOURequestStepParticipants({ [reportID, transactionID], ); - const setTaxDetailsOnCategorizing = useCallback( - (participants: Participant[]) => { - const isCategorizing = action === CONST.IOU.ACTION.CATEGORIZE; - if (!isCategorizing) { - return; - } - - const isPolicyExpenseChat = participants.length === 1 && participants[0]?.isPolicyExpenseChat; - if (!isPolicyExpenseChat) { - return; - } - - const policyID = participants[0].policyID; - const policy= getPolicy(policyID); - if (!isTaxTrackingEnabled(isPolicyExpenseChat, policy)) { - return; - } - const taxCode = TransactionUtils.getDefaultTaxCode(policy, transaction) ?? ''; - const taxPercentage = TransactionUtils.getTaxValue(policy, transaction, taxCode) ?? ''; - const taxAmount = CurrencyUtils.convertToBackendAmount(TransactionUtils.calculateTaxAmount(taxPercentage, transaction?.amount ?? 0)); - IOU.setMoneyRequestTaxRate(transactionID, taxCode, true); - IOU.setMoneyRequestTaxAmount(transactionID, taxAmount, true); - }, - [action] - ); - const goToNextStep = useCallback(() => { const isCategorizing = action === CONST.IOU.ACTION.CATEGORIZE; From 833d95550b170f04ebfb116275c051d9d6d0a2aa Mon Sep 17 00:00:00 2001 From: Monil Bhavsar Date: Tue, 14 May 2024 19:14:56 +0530 Subject: [PATCH 5/5] Fix typescript checks --- src/libs/OptionsListUtils.ts | 6 +++--- src/libs/TransactionUtils.ts | 18 +++++++++++++----- .../step/IOURequestStepParticipants.tsx | 8 ++++---- 3 files changed, 20 insertions(+), 12 deletions(-) diff --git a/src/libs/OptionsListUtils.ts b/src/libs/OptionsListUtils.ts index f1bdac4a24948..ec4fe88a9ef08 100644 --- a/src/libs/OptionsListUtils.ts +++ b/src/libs/OptionsListUtils.ts @@ -165,7 +165,7 @@ type GetOptionsConfig = { includeSelectedOptions?: boolean; includeTaxRates?: boolean; taxRates?: TaxRatesWithDefault; - policy?: OnyxEntry; + policy?: OnyxEntry | EmptyObject; transaction?: OnyxEntry; includePolicyReportFieldOptions?: boolean; policyReportFieldOptions?: string[]; @@ -1383,7 +1383,7 @@ function getTaxRatesOptions(taxRates: Array>): TaxRatesOption[] /** * Builds the section list for tax rates */ -function getTaxRatesSection(policy: OnyxEntry | undefined, selectedOptions: Tax[], searchInputValue: string, transaction?: OnyxEntry): TaxSection[] { +function getTaxRatesSection(policy: OnyxEntry | EmptyObject, selectedOptions: Tax[], searchInputValue: string, transaction?: OnyxEntry): TaxSection[] { const policyRatesSections = []; const taxes = TransactionUtils.transformedTaxRates(policy, transaction); @@ -1705,7 +1705,7 @@ function getOptions( } if (includeTaxRates) { - const taxRatesOptions = getTaxRatesSection(policy, selectedOptions as Tax[], searchInputValue, transaction); + const taxRatesOptions = getTaxRatesSection(policy ?? {}, selectedOptions as Tax[], searchInputValue, transaction); return { recentReports: [], diff --git a/src/libs/TransactionUtils.ts b/src/libs/TransactionUtils.ts index 8e9297dbf1941..bb38f36808846 100644 --- a/src/libs/TransactionUtils.ts +++ b/src/libs/TransactionUtils.ts @@ -668,10 +668,9 @@ function getDefaultTaxCode(policy: OnyxEntry | EmptyObject, transaction: /** * Transforms tax rates to a new object format - to add codes and new name with concatenated name and value. * - * @param policy - The policy which the user has access to and which the report is tied to. - * @returns The transformed tax rates object.g + * @returns The transformed tax rates object */ -function transformedTaxRates(policy: OnyxEntry | undefined, transaction?: OnyxEntry): Record { +function transformedTaxRates(policy: OnyxEntry | EmptyObject, transaction?: OnyxEntry): Record { const taxRates = policy?.taxRates; const defaultExternalID = taxRates?.defaultExternalID; @@ -685,8 +684,17 @@ function transformedTaxRates(policy: OnyxEntry | undefined, transaction? const getModifiedName = (data: TaxRate, code: string) => `${data.name} (${data.value})${defaultTaxCode() === code ? ` ${CONST.DOT_SEPARATOR} ${Localize.translateLocal('common.default')}` : ''}`; - const taxes = Object.fromEntries(Object.entries(taxRates?.taxes ?? {}).map(([code, data]) => [code, {...data, code, modifiedName: getModifiedName(data, code), name: data.name}])); - return taxes; + return Object.fromEntries( + Object.entries(taxRates?.taxes ?? {}).map(([code, data]) => [ + code, + { + ...data, + code, + modifiedName: getModifiedName(data, code), + name: data.name, + }, + ]), + ); } /** diff --git a/src/pages/iou/request/step/IOURequestStepParticipants.tsx b/src/pages/iou/request/step/IOURequestStepParticipants.tsx index d645f53e866a1..a428949587bc0 100644 --- a/src/pages/iou/request/step/IOURequestStepParticipants.tsx +++ b/src/pages/iou/request/step/IOURequestStepParticipants.tsx @@ -4,9 +4,11 @@ import {withOnyx} from 'react-native-onyx'; import FormHelpMessage from '@components/FormHelpMessage'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; +import * as CurrencyUtils from '@libs/CurrencyUtils'; import DistanceRequestUtils from '@libs/DistanceRequestUtils'; import * as IOUUtils from '@libs/IOUUtils'; import Navigation from '@libs/Navigation/Navigation'; +import {getPolicy, isTaxTrackingEnabled} from '@libs/PolicyUtils'; import * as TransactionUtils from '@libs/TransactionUtils'; import MoneyRequestParticipantsSelector from '@pages/iou/request/MoneyRequestParticipantsSelector'; import * as IOU from '@userActions/IOU'; @@ -15,8 +17,6 @@ import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; import type {Participant} from '@src/types/onyx/IOU'; -import * as CurrencyUtils from "@libs/CurrencyUtils"; -import {getPolicy, isTaxTrackingEnabled} from "@libs/PolicyUtils"; import StepScreenWrapper from './StepScreenWrapper'; import type {WithFullTransactionOrNotFoundProps} from './withFullTransactionOrNotFound'; import withFullTransactionOrNotFound from './withFullTransactionOrNotFound'; @@ -105,7 +105,7 @@ function IOURequestStepParticipants({ IOU.setMoneyRequestTaxRate(transactionID, taxCode, true); IOU.setMoneyRequestTaxAmount(transactionID, taxAmount, true); }, - [action, transaction, transactionID] + [action, transaction, transactionID], ); const addParticipant = useCallback( @@ -129,7 +129,7 @@ function IOURequestStepParticipants({ // When a participant is selected, the reportID needs to be saved because that's the reportID that will be used in the confirmation step. selectedReportID.current = val[0]?.reportID ?? reportID; }, - [reportID, transactionID], + [reportID, transactionID, setTaxDetailsOnCategorizing], ); const goToNextStep = useCallback(() => {