From 3d85737a06294ebc2231d54c18031fda9570f5eb Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Wed, 6 Dec 2023 10:07:07 +0100 Subject: [PATCH 01/78] initial input screens --- src/ROUTES.ts | 8 ++ .../MoneyRequestConfirmationList.js | 36 +++++ .../AppNavigator/ModalStackNavigators.js | 2 + src/libs/Navigation/linkingConfig.ts | 2 + src/libs/Navigation/types.ts | 8 ++ src/libs/actions/IOU.js | 10 ++ .../iou/steps/IOURequestStepTaxAmountPage.js | 123 ++++++++++++++++++ .../iou/steps/IOURequestStepTaxRatePage.js | 59 +++++++++ 8 files changed, 248 insertions(+) create mode 100644 src/pages/iou/steps/IOURequestStepTaxAmountPage.js create mode 100644 src/pages/iou/steps/IOURequestStepTaxRatePage.js diff --git a/src/ROUTES.ts b/src/ROUTES.ts index a3aa28c44609..6c68617307a5 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -283,6 +283,14 @@ const ROUTES = { route: ':iouType/new/merchant/:reportID?', getRoute: (iouType: string, reportID = '') => `${iouType}/new/merchant/${reportID}` as const, }, + MONEY_REQUEST_TAX_RATE: { + route: ':iouType/new/taxRate/:reportID?', + getRoute: (iouType: string, reportID = '') => `${iouType}/new/taxRate/${reportID}` as const, + }, + MONEY_REQUEST_TAX_AMOUNT: { + route: ':iouType/new/taxAmount/:reportID?', + getRoute: (iouType: string, reportID = '') => `${iouType}/new/taxAmount/${reportID}` as const, + }, MONEY_REQUEST_WAYPOINT: { route: ':iouType/new/waypoint/:waypointIndex', getRoute: (iouType: string, waypointIndex: number) => `${iouType}/new/waypoint/${waypointIndex}` as const, diff --git a/src/components/MoneyRequestConfirmationList.js b/src/components/MoneyRequestConfirmationList.js index 1b4967a9c54c..22f733526e2a 100755 --- a/src/components/MoneyRequestConfirmationList.js +++ b/src/components/MoneyRequestConfirmationList.js @@ -241,6 +241,12 @@ function MoneyRequestConfirmationList(props) { // A flag for showing the tags field const shouldShowTags = props.isPolicyExpenseChat && (props.iouTag || OptionsListUtils.hasEnabledOptions(_.values(policyTagList))); + // A flag for showing tax rate + const shouldShowTaxRate = true; + + // A flag for showing tax rate + const shouldShowTaxAmount = true; + // A flag for showing the billable field const shouldShowBillable = !lodashGet(props.policy, 'disabledFields.defaultBillable', true); @@ -721,6 +727,36 @@ function MoneyRequestConfirmationList(props) { /> )} + {shouldShowTaxRate && ( + Navigation.navigate(ROUTES.MONEY_REQUEST_TAX_RATE.getRoute(props.iouType, props.reportID))} + disabled={didConfirm} + interactive={!props.isReadOnly} + brickRoadIndicator='' + error='' + /> + )} + + {shouldShowTaxAmount && ( + Navigation.navigate(ROUTES.MONEY_REQUEST_TAX_AMOUNT.getRoute(props.iouType, props.reportID))} + disabled={didConfirm} + interactive={!props.isReadOnly} + brickRoadIndicator='' + error='' + /> + )} + {shouldShowBillable && ( {translate('common.billable')} diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators.js b/src/libs/Navigation/AppNavigator/ModalStackNavigators.js index be803e62a98b..bb78b4f0d5b5 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators.js +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators.js @@ -46,6 +46,8 @@ function createModalStackNavigator(screens) { const MoneyRequestModalStackNavigator = createModalStackNavigator({ Money_Request: () => require('../../../pages/iou/MoneyRequestSelectorPage').default, Money_Request_Amount: () => require('../../../pages/iou/steps/NewRequestAmountPage').default, + Money_Request_Tax_Rate: () => require('../../../pages/iou/steps/IOURequestStepTaxRatePage').default, + Money_Request_Tax_Amount: () => require('../../../pages/iou/steps/IOURequestStepTaxAmountPage').default, Money_Request_Participants: () => require('../../../pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsPage').default, Money_Request_Confirmation: () => require('../../../pages/iou/steps/MoneyRequestConfirmPage').default, Money_Request_Currency: () => require('../../../pages/iou/IOUCurrencySelection').default, diff --git a/src/libs/Navigation/linkingConfig.ts b/src/libs/Navigation/linkingConfig.ts index 92a04778b9a6..97be3c2b68a7 100644 --- a/src/libs/Navigation/linkingConfig.ts +++ b/src/libs/Navigation/linkingConfig.ts @@ -379,6 +379,8 @@ const linkingConfig: LinkingOptions = { }, }, Money_Request_Amount: ROUTES.MONEY_REQUEST_AMOUNT.route, + Money_Request_Tax_Rate: ROUTES.MONEY_REQUEST_TAX_RATE.route, + Money_Request_Tax_Amount: ROUTES.MONEY_REQUEST_TAX_AMOUNT.route, Money_Request_Participants: ROUTES.MONEY_REQUEST_PARTICIPANTS.route, Money_Request_Confirmation: ROUTES.MONEY_REQUEST_CONFIRMATION.route, Money_Request_Date: ROUTES.MONEY_REQUEST_DATE.route, diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index 41df21d8e237..00cad1983ea0 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -211,6 +211,14 @@ type MoneyRequestNavigatorParamList = { iouType: string; reportID: string; }; + Money_Request_Tax_Rate: { + iouType: string; + reportID: string; + }; + Money_Request_Tax_Amount: { + iouType: string; + reportID: string; + }; Money_Request_Merchant: { iouType: string; reportID: string; diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.js index d9de984ad12c..017942177aee 100644 --- a/src/libs/actions/IOU.js +++ b/src/libs/actions/IOU.js @@ -2875,6 +2875,14 @@ function resetMoneyRequestTag() { Onyx.merge(ONYXKEYS.IOU, {tag: ''}); } +function setMoneyRequestTaxRate(transactionID, taxRate) { + Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}`, {taxRate}); +} + +function setMoneyRequestTaxAmount(transactionID, taxAmount) { + Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}`, {taxAmount}); +} + /** * @param {Boolean} billable */ @@ -2996,6 +3004,8 @@ export { resetMoneyRequestCategory, setMoneyRequestTag, resetMoneyRequestTag, + setMoneyRequestTaxRate, + setMoneyRequestTaxAmount, setMoneyRequestBillable, setMoneyRequestParticipants, setMoneyRequestReceipt, diff --git a/src/pages/iou/steps/IOURequestStepTaxAmountPage.js b/src/pages/iou/steps/IOURequestStepTaxAmountPage.js new file mode 100644 index 000000000000..c12aada5d859 --- /dev/null +++ b/src/pages/iou/steps/IOURequestStepTaxAmountPage.js @@ -0,0 +1,123 @@ +import {useFocusEffect} from '@react-navigation/native'; +import lodashGet from 'lodash/get'; +import PropTypes from 'prop-types'; +import React, {useCallback, useRef} from 'react'; +import {View} from 'react-native'; +import {withOnyx} from 'react-native-onyx'; +import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView'; +import HeaderWithBackButton from '@components/HeaderWithBackButton'; +import ScreenWrapper from '@components/ScreenWrapper'; +import * as CurrencyUtils from '@libs/CurrencyUtils'; +import * as IOUUtils from '@libs/IOUUtils'; +import Navigation from '@libs/Navigation/Navigation'; +import {iouDefaultProps, iouPropTypes} from '@pages/iou/propTypes'; +import useThemeStyles from '@styles/useThemeStyles'; +import * as IOU from '@userActions/IOU'; +import CONST from '@src/CONST'; +import ONYXKEYS from '@src/ONYXKEYS'; +import ROUTES from '@src/ROUTES'; +import MoneyRequestAmountForm from './MoneyRequestAmountForm'; + +const propTypes = { + /** React Navigation route */ + route: PropTypes.shape({ + /** Params from the route */ + params: PropTypes.shape({ + /** The type of IOU report, i.e. bill, request, send */ + iouType: PropTypes.string, + + /** The report ID of the IOU */ + reportID: PropTypes.string, + + /** Selected currency from IOUCurrencySelection */ + currency: PropTypes.string, + }), + }).isRequired, + + /** Holds data related to Money Request view state, rather than the underlying Money Request data. */ + iou: iouPropTypes, +}; + +const defaultProps = { + iou: iouDefaultProps, +}; + +function IOURequestStepTaxAmountPage({route, iou}) { + const styles = useThemeStyles(); + const textInput = useRef(null); + const isEditing = Navigation.getActiveRoute().includes('taxAmount'); + + const iouType = lodashGet(route, 'params.iouType', ''); + const reportID = lodashGet(route, 'params.reportID', ''); + const currentCurrency = lodashGet(route, 'params.currency', ''); + const currency = CurrencyUtils.isValidCurrencyCode(currentCurrency) ? currentCurrency : iou.currency; + + const focusTimeoutRef = useRef(null); + useFocusEffect( + useCallback(() => { + focusTimeoutRef.current = setTimeout(() => textInput.current && textInput.current.focus(), CONST.ANIMATED_TRANSITION); + return () => { + if (!focusTimeoutRef.current) { + return; + } + clearTimeout(focusTimeoutRef.current); + }; + }, []), + ); + + const navigateBack = () => { + Navigation.goBack(isEditing ? ROUTES.MONEY_REQUEST_CONFIRMATION.getRoute(iouType, reportID) : ROUTES.HOME); + }; + + const navigateToCurrencySelectionPage = () => { + // If the money request being created is a distance request, don't allow the user to choose the currency. + // Only USD is allowed for distance requests. + // Remove query from the route and encode it. + const activeRoute = encodeURIComponent(Navigation.getActiveRouteWithoutParams()); + Navigation.navigate(ROUTES.MONEY_REQUEST_CURRENCY.getRoute(iouType, reportID, currency, activeRoute)); + }; + + const updateTaxAmount = (currentAmount) => { + const amountInSmallestCurrencyUnits = CurrencyUtils.convertToBackendAmount(Number.parseFloat(currentAmount)); + IOU.setMoneyRequestTaxAmount(amountInSmallestCurrencyUnits); + navigateBack(); + }; + + const content = ( + (textInput.current = e)} + onCurrencyButtonPress={navigateToCurrencySelectionPage} + onSubmitButtonPress={updateTaxAmount} + /> + ); + + return ( + + {({safeAreaPaddingBottomStyle}) => ( + + + + {content} + + + )} + + ); +} + +IOURequestStepTaxAmountPage.propTypes = propTypes; +IOURequestStepTaxAmountPage.defaultProps = defaultProps; +IOURequestStepTaxAmountPage.displayName = 'IOURequestStepTaxAmountPage'; +export default withOnyx({ + iou: {key: ONYXKEYS.IOU}, +})(IOURequestStepTaxAmountPage); diff --git a/src/pages/iou/steps/IOURequestStepTaxRatePage.js b/src/pages/iou/steps/IOURequestStepTaxRatePage.js new file mode 100644 index 000000000000..58128e643f61 --- /dev/null +++ b/src/pages/iou/steps/IOURequestStepTaxRatePage.js @@ -0,0 +1,59 @@ +import lodashGet from 'lodash/get'; +import PropTypes from 'prop-types'; +import React from 'react'; +import HeaderWithBackButton from '@components/HeaderWithBackButton'; +import ScreenWrapper from '@components/ScreenWrapper'; +import Navigation from '@libs/Navigation/Navigation'; +import ROUTES from '@src/ROUTES'; + +const propTypes = { + /** Route from navigation */ + route: PropTypes.shape({ + /** Params from the route */ + params: PropTypes.shape({ + /** The type of IOU report, i.e. bill, request, send */ + iouType: PropTypes.string, + + /** The report ID of the IOU */ + reportID: PropTypes.string, + + /** Which field we are editing */ + field: PropTypes.string, + + /** reportID for the "transaction thread" */ + threadReportID: PropTypes.string, + }), + }).isRequired, +}; + +const defaultProps = {}; + +function IOURequestStepTaxRatePage({route}) { + const iouType = lodashGet(route, 'params.iouType', ''); + const reportID = lodashGet(route, 'params.reportID', ''); + + function navigateBack() { + Navigation.goBack(ROUTES.MONEY_REQUEST_CONFIRMATION.getRoute(iouType, reportID)); + } + + return ( + + <> + navigateBack()} + /> + + + ); +} + +IOURequestStepTaxRatePage.propTypes = propTypes; +IOURequestStepTaxRatePage.defaultProps = defaultProps; +IOURequestStepTaxRatePage.displayName = 'IOURequestStepTaxRatePage'; + +export default IOURequestStepTaxRatePage; From 20efbfc0412abd1b5b4e98e996a229583eddc2cb Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Wed, 6 Dec 2023 10:19:10 +0100 Subject: [PATCH 02/78] fix lint --- src/components/MoneyRequestConfirmationList.js | 16 ++++++++-------- .../iou/steps/IOURequestStepTaxAmountPage.js | 2 +- src/pages/iou/steps/IOURequestStepTaxRatePage.js | 2 +- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/components/MoneyRequestConfirmationList.js b/src/components/MoneyRequestConfirmationList.js index 22f733526e2a..8dd71def21e7 100755 --- a/src/components/MoneyRequestConfirmationList.js +++ b/src/components/MoneyRequestConfirmationList.js @@ -730,30 +730,30 @@ function MoneyRequestConfirmationList(props) { {shouldShowTaxRate && ( Navigation.navigate(ROUTES.MONEY_REQUEST_TAX_RATE.getRoute(props.iouType, props.reportID))} disabled={didConfirm} interactive={!props.isReadOnly} - brickRoadIndicator='' - error='' + brickRoadIndicator="" + error="" /> )} {shouldShowTaxAmount && ( Navigation.navigate(ROUTES.MONEY_REQUEST_TAX_AMOUNT.getRoute(props.iouType, props.reportID))} disabled={didConfirm} interactive={!props.isReadOnly} - brickRoadIndicator='' - error='' + brickRoadIndicator="" + error="" /> )} diff --git a/src/pages/iou/steps/IOURequestStepTaxAmountPage.js b/src/pages/iou/steps/IOURequestStepTaxAmountPage.js index c12aada5d859..ff57674f7889 100644 --- a/src/pages/iou/steps/IOURequestStepTaxAmountPage.js +++ b/src/pages/iou/steps/IOURequestStepTaxAmountPage.js @@ -104,7 +104,7 @@ function IOURequestStepTaxAmountPage({route, iou}) { {content} diff --git a/src/pages/iou/steps/IOURequestStepTaxRatePage.js b/src/pages/iou/steps/IOURequestStepTaxRatePage.js index 58128e643f61..739275b73fb7 100644 --- a/src/pages/iou/steps/IOURequestStepTaxRatePage.js +++ b/src/pages/iou/steps/IOURequestStepTaxRatePage.js @@ -44,7 +44,7 @@ function IOURequestStepTaxRatePage({route}) { > <> navigateBack()} /> From 71b49b9e0eb166a1586729fc20eb9aacd538842d Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Thu, 7 Dec 2023 23:46:25 +0100 Subject: [PATCH 03/78] check policy expense chat: a flag for showing tax rate --- src/components/MoneyRequestConfirmationList.js | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/components/MoneyRequestConfirmationList.js b/src/components/MoneyRequestConfirmationList.js index 8dd71def21e7..6abad7c28058 100755 --- a/src/components/MoneyRequestConfirmationList.js +++ b/src/components/MoneyRequestConfirmationList.js @@ -242,10 +242,7 @@ function MoneyRequestConfirmationList(props) { const shouldShowTags = props.isPolicyExpenseChat && (props.iouTag || OptionsListUtils.hasEnabledOptions(_.values(policyTagList))); // A flag for showing tax rate - const shouldShowTaxRate = true; - - // A flag for showing tax rate - const shouldShowTaxAmount = true; + const shouldShowTax = props.isPolicyExpenseChat && props.policy.isTaxTrackingEnabled; // A flag for showing the billable field const shouldShowBillable = !lodashGet(props.policy, 'disabledFields.defaultBillable', true); @@ -727,7 +724,7 @@ function MoneyRequestConfirmationList(props) { /> )} - {shouldShowTaxRate && ( + {shouldShowTax && ( )} - {shouldShowTaxAmount && ( + {shouldShowTax && ( Date: Thu, 7 Dec 2023 23:52:11 +0100 Subject: [PATCH 04/78] add policy tax rates in tax rate page --- src/ONYXKEYS.ts | 2 ++ .../iou/steps/IOURequestStepTaxRatePage.js | 33 +++++++++++++++---- 2 files changed, 28 insertions(+), 7 deletions(-) diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts index 5576eb64736d..e4344cd2095f 100755 --- a/src/ONYXKEYS.ts +++ b/src/ONYXKEYS.ts @@ -247,6 +247,8 @@ const ONYXKEYS = { POLICY_CATEGORIES: 'policyCategories_', POLICY_RECENTLY_USED_CATEGORIES: 'policyRecentlyUsedCategories_', POLICY_TAGS: 'policyTags_', + POLICY_TAX_RATE: 'policyTaxRates_', + POLICY_RECENTLY_USED_TAGS: 'policyRecentlyUsedTags_', WORKSPACE_INVITE_MEMBERS_DRAFT: 'workspaceInviteMembersDraft_', WORKSPACE_INVITE_MESSAGE_DRAFT: 'workspaceInviteMessageDraft_', diff --git a/src/pages/iou/steps/IOURequestStepTaxRatePage.js b/src/pages/iou/steps/IOURequestStepTaxRatePage.js index 739275b73fb7..8a5fc56339f6 100644 --- a/src/pages/iou/steps/IOURequestStepTaxRatePage.js +++ b/src/pages/iou/steps/IOURequestStepTaxRatePage.js @@ -1,9 +1,13 @@ import lodashGet from 'lodash/get'; import PropTypes from 'prop-types'; import React from 'react'; +import {withOnyx} from 'react-native-onyx'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import ScreenWrapper from '@components/ScreenWrapper'; +import compose from '@libs/compose'; import Navigation from '@libs/Navigation/Navigation'; +import * as IOU from '@userActions/IOU'; +import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; const propTypes = { @@ -16,12 +20,6 @@ const propTypes = { /** The report ID of the IOU */ reportID: PropTypes.string, - - /** Which field we are editing */ - field: PropTypes.string, - - /** reportID for the "transaction thread" */ - threadReportID: PropTypes.string, }), }).isRequired, }; @@ -56,4 +54,25 @@ IOURequestStepTaxRatePage.propTypes = propTypes; IOURequestStepTaxRatePage.defaultProps = defaultProps; IOURequestStepTaxRatePage.displayName = 'IOURequestStepTaxRatePage'; -export default IOURequestStepTaxRatePage; +export default compose( + withOnyx({ + iou: { + key: ONYXKEYS.IOU, + }, + }), + // eslint-disable-next-line rulesdir/no-multiple-onyx-in-file + withOnyx({ + report: { + key: ({route, iou}) => { + const reportID = IOU.getIOUReportID(iou, route); + + return `${ONYXKEYS.COLLECTION.REPORT}${reportID}`; + }, + }, + }), + withOnyx({ + policyTaxRates: { + key: ({report}) => `${ONYXKEYS.COLLECTION.POLICY_TAX_RATE}${report ? report.policyID : '0'}`, + }, + }), +)(IOURequestStepTaxRatePage); From c6835007095796093ac8b0809093fc240ad1e884 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Fri, 8 Dec 2023 03:27:33 +0100 Subject: [PATCH 05/78] add tax rates list threshold --- src/CONST.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/CONST.ts b/src/CONST.ts index 13b79179f431..c95bd6fce27c 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -2778,6 +2778,7 @@ const CONST = { PARENT_CHILD_SEPARATOR: ': ', CATEGORY_LIST_THRESHOLD: 8, TAG_LIST_THRESHOLD: 8, + TAX_RATES_LIST_THRESHOLD: 8, DEMO_PAGES: { SAASTR: 'SaaStrDemoSetup', SBE: 'SbeDemoSetup', From c14bbe3d41131800f01ce7ef892a7eda11f57042 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Fri, 8 Dec 2023 03:29:07 +0100 Subject: [PATCH 06/78] add tax rates sections in options utils --- src/libs/OptionsListUtils.js | 74 ++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/src/libs/OptionsListUtils.js b/src/libs/OptionsListUtils.js index 820339ebc6c4..b8bf1270ee84 100644 --- a/src/libs/OptionsListUtils.js +++ b/src/libs/OptionsListUtils.js @@ -1046,6 +1046,55 @@ function getTagListSections(rawTags, recentlyUsedTags, selectedOptions, searchIn return tagSections; } +function sortTaxRates(taxRates) { + const sortedtaxRates = _.chain(taxRates) + .values() + .sortBy((taxRates) => taxRates.name) + .value(); + + return sortedtaxRates; +} + +function getTaxRatesOptions(taxRates) { + return _.map(taxRates, (taxRate) => ({ + text: `${taxRate.name} (${taxRate.value})`, + keyForList: taxRate.name, + searchText: taxRate.name, + tooltipText: taxRate.name, + isDisabled: false, + })); +} + +function getTaxRatesSection(policyTaxRates, selectedOptions) { + const policyRatesSections = []; + + const sortedTaxRates = sortTaxRates(policyTaxRates.taxes); + const numberOfTaxRates = _.size(sortedTaxRates); + let indexOffset = 0; + + if (numberOfTaxRates === 0 && selectedOptions.length > 0) { + categorySections.push({ + // "Selected" section + title: '', + shouldShow: false, + indexOffset, + data: getCategoryOptionTree(getTaxRatesOptions), + }); + + return policyRatesSections; + } + + policyRatesSections.push({ + // "All" section when items amount more than the threshold + title: Localize.translateLocal('common.all'), + shouldShow: true, + indexOffset, + data: getTaxRatesOptions(sortedTaxRates), + }); + + return policyRatesSections; +} + /** * Build the options * @@ -1087,6 +1136,8 @@ function getOptions( recentlyUsedTags = [], canInviteUser = true, includeSelectedOptions = false, + includePolicyTaxRates, + policyTaxRates }, ) { if (includeCategories) { @@ -1099,6 +1150,7 @@ function getOptions( currentUserOption: null, categoryOptions, tagOptions: [], + policyTaxRatesOptions: [] }; } @@ -1112,6 +1164,21 @@ function getOptions( currentUserOption: null, categoryOptions: [], tagOptions, + policyTaxRatesOptions: [] + }; + } + + if (includePolicyTaxRates) { + const policyTaxRatesOptions = getTaxRatesSection(policyTaxRates, selectedOptions, searchInputValue, maxRecentReportsToShow); + + return { + recentReports: [], + personalDetails: [], + userToInvite: null, + currentUserOption: null, + categoryOptions: [], + tagOptions: [], + policyTaxRatesOptions }; } @@ -1123,6 +1190,7 @@ function getOptions( currentUserOption: null, categoryOptions: [], tagOptions: [], + policyTaxRatesOptions: [] }; } @@ -1389,6 +1457,7 @@ function getOptions( currentUserOption, categoryOptions: [], tagOptions: [], + policyTaxRatesOptions: [] }; } @@ -1478,6 +1547,7 @@ function getIOUConfirmationOptionsFromParticipants(participants, amountText) { * @param {Array} [recentlyUsedTags] * @param {boolean} [canInviteUser] * @param {boolean} [includeSelectedOptions] + * @param {Object} [policyTaxRates] * @returns {Object} */ function getFilteredOptions( @@ -1497,6 +1567,8 @@ function getFilteredOptions( recentlyUsedTags = [], canInviteUser = true, includeSelectedOptions = false, + includePolicyTaxRates = false, + policyTaxRates = {}, ) { return getOptions(reports, personalDetails, { betas, @@ -1516,6 +1588,8 @@ function getFilteredOptions( recentlyUsedTags, canInviteUser, includeSelectedOptions, + includePolicyTaxRates, + policyTaxRates, }); } From 88d352dee9dde099d43bacc415a8688e4d698559 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Fri, 8 Dec 2023 03:29:52 +0100 Subject: [PATCH 07/78] add tax propTypes --- src/components/taxPropTypes.js | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 src/components/taxPropTypes.js diff --git a/src/components/taxPropTypes.js b/src/components/taxPropTypes.js new file mode 100644 index 000000000000..ff5a45518c28 --- /dev/null +++ b/src/components/taxPropTypes.js @@ -0,0 +1,28 @@ +import PropTypes from 'prop-types'; + +const taxPropTypes = PropTypes.shape({ + /** Name of a tax */ + name: PropTypes.string.isRequired, + + /** value of a tax */ + value: PropTypes.string.isRequired, +}); + +export default PropTypes.objectOf( + PropTypes.shape({ + /** Defualt name of taxes */ + name: PropTypes.string.isRequired, + + /** Defualt external ID of taxes */ + defaultExternalID: PropTypes.string.isRequired, + + /** Default value of taxes */ + defaultValue: PropTypes.string.isRequired, + + /** Default Foreign Tax ID */ + foreignTaxDefault: PropTypes.string.isRequired, + + /** List of Taxes names and values */ + taxes: PropTypes.objectOf(taxPropTypes), + }), +); From 8b26228dbcc697da536d70e251338bc70c669274 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Fri, 8 Dec 2023 03:30:17 +0100 Subject: [PATCH 08/78] add tax pickerPropTypes --- .../TaxPicker/taxPickerPropTypes.js | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 src/components/TaxPicker/taxPickerPropTypes.js diff --git a/src/components/TaxPicker/taxPickerPropTypes.js b/src/components/TaxPicker/taxPickerPropTypes.js new file mode 100644 index 000000000000..12757e3d614b --- /dev/null +++ b/src/components/TaxPicker/taxPickerPropTypes.js @@ -0,0 +1,22 @@ +import PropTypes from 'prop-types'; +import taxPropTypes from '@components/taxPropTypes'; + +const propTypes = { + /** The selected tax rate of an expense */ + selectedTaxRate: PropTypes.string, + + /* Onyx Props */ + /** Collection of tax rates attached to a policy */ + policyTaxRates: taxPropTypes, + + + /** Callback to fire when a tax is pressed */ + onSubmit: PropTypes.func.isRequired, +}; + +const defaultProps = { + selectedTaxRate: '', + policyTaxRates: {}, +}; + +export {propTypes, defaultProps}; From e4be96d7aa7303eb23f831b0dc8226cd3ebc5273 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Fri, 8 Dec 2023 03:30:40 +0100 Subject: [PATCH 09/78] add tax picker --- src/components/TaxPicker/index.js | 79 +++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 src/components/TaxPicker/index.js diff --git a/src/components/TaxPicker/index.js b/src/components/TaxPicker/index.js new file mode 100644 index 000000000000..cbd8a84e9370 --- /dev/null +++ b/src/components/TaxPicker/index.js @@ -0,0 +1,79 @@ +import lodashGet from 'lodash/get'; +import React, {useMemo, useState} from 'react'; +import _ from 'underscore'; +import OptionsSelector from '@components/OptionsSelector'; +import useLocalize from '@hooks/useLocalize'; +import * as OptionsListUtils from '@libs/OptionsListUtils'; +import useThemeStyles from '@styles/useThemeStyles'; +import {defaultProps, propTypes} from './taxPickerPropTypes'; + +function TaxPicker({selectedTaxRate, policyTaxRates, onSubmit}) { + const styles = useThemeStyles(); + const {translate} = useLocalize(); + const [searchValue, setSearchValue] = useState(''); + + const selectedOptions = useMemo(() => { + if (!selectedTaxRate) { + return []; + } + + return [ + { + name: selectedTaxRate, + enabled: true, + accountID: null, + }, + ]; + }, [selectedTaxRate]); + + const sections = useMemo(() => { + const {policyTaxRatesOptions} = OptionsListUtils.getFilteredOptions( + {}, // reports {} + {}, // personalDetails {} + [], // betas [] + searchValue, // searchValue string + selectedOptions, // selectedOptions any[] + [], // excludedLogins any[] + false, // includeOwnedWorkspaceChats boolean + false, // includeP2P boolean + false, // includeCategories boolean + {}, // categories {} + [], // recentlyUsedCategories string[] + false, // includeTags boolean + {}, // tags {} + [], // recentlyUsedTags string[] + false, // canInviteUser boolean + false, // includeSelectedOptions + true, // includePolicyTaxRates boolean + policyTaxRates // policyTaxRates {} + ); + return policyTaxRatesOptions; + }, [policyTaxRates, searchValue, selectedOptions]); + + const selectedOptionKey = lodashGet(_.filter(lodashGet(sections, '[0].data', []), (taxRate) => taxRate.searchText === selectedTaxRate)[0], 'keyForList'); + + return ( + + ); +} + +TaxPicker.displayName = 'TaxPicker'; +TaxPicker.propTypes = propTypes; +TaxPicker.defaultProps = defaultProps; + +export default TaxPicker; From 11a469136f0fad9e2434919c9eebf90cf45ea8dc Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Fri, 8 Dec 2023 03:31:24 +0100 Subject: [PATCH 10/78] use TaxPicker in tax rates page --- src/pages/iou/steps/IOURequestStepTaxRatePage.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/pages/iou/steps/IOURequestStepTaxRatePage.js b/src/pages/iou/steps/IOURequestStepTaxRatePage.js index 8a5fc56339f6..a11377fa2d62 100644 --- a/src/pages/iou/steps/IOURequestStepTaxRatePage.js +++ b/src/pages/iou/steps/IOURequestStepTaxRatePage.js @@ -4,6 +4,7 @@ import React from 'react'; import {withOnyx} from 'react-native-onyx'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import ScreenWrapper from '@components/ScreenWrapper'; +import TaxPicker from '@components/TaxPicker'; import compose from '@libs/compose'; import Navigation from '@libs/Navigation/Navigation'; import * as IOU from '@userActions/IOU'; @@ -26,7 +27,7 @@ const propTypes = { const defaultProps = {}; -function IOURequestStepTaxRatePage({route}) { +function IOURequestStepTaxRatePage({route, policyTaxRates}) { const iouType = lodashGet(route, 'params.iouType', ''); const reportID = lodashGet(route, 'params.reportID', ''); @@ -45,6 +46,11 @@ function IOURequestStepTaxRatePage({route}) { title="Tax Rate" onBackButtonPress={() => navigateBack()} /> + {}} + /> ); From df81cf5d687ae1e6021f47a4f0e46012d3ea91f9 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Fri, 8 Dec 2023 03:44:50 +0100 Subject: [PATCH 11/78] fix lint --- src/components/TaxPicker/index.js | 2 +- .../TaxPicker/taxPickerPropTypes.js | 1 - src/components/taxPropTypes.js | 6 +++--- src/libs/OptionsListUtils.js | 21 ++++++++++--------- .../iou/steps/IOURequestStepTaxRatePage.js | 10 ++++++--- 5 files changed, 22 insertions(+), 18 deletions(-) diff --git a/src/components/TaxPicker/index.js b/src/components/TaxPicker/index.js index cbd8a84e9370..befe5530d891 100644 --- a/src/components/TaxPicker/index.js +++ b/src/components/TaxPicker/index.js @@ -45,7 +45,7 @@ function TaxPicker({selectedTaxRate, policyTaxRates, onSubmit}) { false, // canInviteUser boolean false, // includeSelectedOptions true, // includePolicyTaxRates boolean - policyTaxRates // policyTaxRates {} + policyTaxRates, // policyTaxRates {} ); return policyTaxRatesOptions; }, [policyTaxRates, searchValue, selectedOptions]); diff --git a/src/components/TaxPicker/taxPickerPropTypes.js b/src/components/TaxPicker/taxPickerPropTypes.js index 12757e3d614b..d0cd8e896493 100644 --- a/src/components/TaxPicker/taxPickerPropTypes.js +++ b/src/components/TaxPicker/taxPickerPropTypes.js @@ -9,7 +9,6 @@ const propTypes = { /** Collection of tax rates attached to a policy */ policyTaxRates: taxPropTypes, - /** Callback to fire when a tax is pressed */ onSubmit: PropTypes.func.isRequired, }; diff --git a/src/components/taxPropTypes.js b/src/components/taxPropTypes.js index ff5a45518c28..91f220b92395 100644 --- a/src/components/taxPropTypes.js +++ b/src/components/taxPropTypes.js @@ -4,8 +4,8 @@ const taxPropTypes = PropTypes.shape({ /** Name of a tax */ name: PropTypes.string.isRequired, - /** value of a tax */ - value: PropTypes.string.isRequired, + /** value of a tax */ + value: PropTypes.string.isRequired, }); export default PropTypes.objectOf( @@ -16,7 +16,7 @@ export default PropTypes.objectOf( /** Defualt external ID of taxes */ defaultExternalID: PropTypes.string.isRequired, - /** Default value of taxes */ + /** Default value of taxes */ defaultValue: PropTypes.string.isRequired, /** Default Foreign Tax ID */ diff --git a/src/libs/OptionsListUtils.js b/src/libs/OptionsListUtils.js index b8bf1270ee84..c062924234ce 100644 --- a/src/libs/OptionsListUtils.js +++ b/src/libs/OptionsListUtils.js @@ -1049,7 +1049,7 @@ function getTagListSections(rawTags, recentlyUsedTags, selectedOptions, searchIn function sortTaxRates(taxRates) { const sortedtaxRates = _.chain(taxRates) .values() - .sortBy((taxRates) => taxRates.name) + .sortBy((taxRate) => taxRate.name) .value(); return sortedtaxRates; @@ -1057,7 +1057,7 @@ function sortTaxRates(taxRates) { function getTaxRatesOptions(taxRates) { return _.map(taxRates, (taxRate) => ({ - text: `${taxRate.name} (${taxRate.value})`, + text: `${taxRate.name} (${taxRate.value})`, keyForList: taxRate.name, searchText: taxRate.name, tooltipText: taxRate.name, @@ -1070,10 +1070,10 @@ function getTaxRatesSection(policyTaxRates, selectedOptions) { const sortedTaxRates = sortTaxRates(policyTaxRates.taxes); const numberOfTaxRates = _.size(sortedTaxRates); - let indexOffset = 0; + const indexOffset = 0; if (numberOfTaxRates === 0 && selectedOptions.length > 0) { - categorySections.push({ + policyRatesSections.push({ // "Selected" section title: '', shouldShow: false, @@ -1137,7 +1137,7 @@ function getOptions( canInviteUser = true, includeSelectedOptions = false, includePolicyTaxRates, - policyTaxRates + policyTaxRates, }, ) { if (includeCategories) { @@ -1150,7 +1150,7 @@ function getOptions( currentUserOption: null, categoryOptions, tagOptions: [], - policyTaxRatesOptions: [] + policyTaxRatesOptions: [], }; } @@ -1164,7 +1164,7 @@ function getOptions( currentUserOption: null, categoryOptions: [], tagOptions, - policyTaxRatesOptions: [] + policyTaxRatesOptions: [], }; } @@ -1178,7 +1178,7 @@ function getOptions( currentUserOption: null, categoryOptions: [], tagOptions: [], - policyTaxRatesOptions + policyTaxRatesOptions, }; } @@ -1190,7 +1190,7 @@ function getOptions( currentUserOption: null, categoryOptions: [], tagOptions: [], - policyTaxRatesOptions: [] + policyTaxRatesOptions: [], }; } @@ -1457,7 +1457,7 @@ function getOptions( currentUserOption, categoryOptions: [], tagOptions: [], - policyTaxRatesOptions: [] + policyTaxRatesOptions: [], }; } @@ -1547,6 +1547,7 @@ function getIOUConfirmationOptionsFromParticipants(participants, amountText) { * @param {Array} [recentlyUsedTags] * @param {boolean} [canInviteUser] * @param {boolean} [includeSelectedOptions] + * @param {boolean} [includePolicyTaxRates] * @param {Object} [policyTaxRates] * @returns {Object} */ diff --git a/src/pages/iou/steps/IOURequestStepTaxRatePage.js b/src/pages/iou/steps/IOURequestStepTaxRatePage.js index a11377fa2d62..50dc64b735a4 100644 --- a/src/pages/iou/steps/IOURequestStepTaxRatePage.js +++ b/src/pages/iou/steps/IOURequestStepTaxRatePage.js @@ -5,6 +5,7 @@ import {withOnyx} from 'react-native-onyx'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import ScreenWrapper from '@components/ScreenWrapper'; import TaxPicker from '@components/TaxPicker'; +import taxPropTypes from '@components/taxPropTypes'; import compose from '@libs/compose'; import Navigation from '@libs/Navigation/Navigation'; import * as IOU from '@userActions/IOU'; @@ -23,9 +24,12 @@ const propTypes = { reportID: PropTypes.string, }), }).isRequired, + policyTaxRates: taxPropTypes, }; -const defaultProps = {}; +const defaultProps = { + policyTaxRates: {}, +}; function IOURequestStepTaxRatePage({route, policyTaxRates}) { const iouType = lodashGet(route, 'params.iouType', ''); @@ -47,9 +51,9 @@ function IOURequestStepTaxRatePage({route, policyTaxRates}) { onBackButtonPress={() => navigateBack()} /> {}} + onSubmit={() => {}} /> From c34095ec90909a056c62222c1fb34c149a85cefc Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Fri, 8 Dec 2023 10:55:00 +0100 Subject: [PATCH 12/78] remove whitespace --- src/ONYXKEYS.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts index e4344cd2095f..14f1b5d34852 100755 --- a/src/ONYXKEYS.ts +++ b/src/ONYXKEYS.ts @@ -248,7 +248,6 @@ const ONYXKEYS = { POLICY_RECENTLY_USED_CATEGORIES: 'policyRecentlyUsedCategories_', POLICY_TAGS: 'policyTags_', POLICY_TAX_RATE: 'policyTaxRates_', - POLICY_RECENTLY_USED_TAGS: 'policyRecentlyUsedTags_', WORKSPACE_INVITE_MEMBERS_DRAFT: 'workspaceInviteMembersDraft_', WORKSPACE_INVITE_MESSAGE_DRAFT: 'workspaceInviteMessageDraft_', From 2f78afd65e0e3ec8244197e6bebfdcc2f2af2a8d Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Sun, 10 Dec 2023 02:26:02 +0100 Subject: [PATCH 13/78] add default amount to avoid Nan --- src/libs/CurrencyUtils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/CurrencyUtils.ts b/src/libs/CurrencyUtils.ts index 4829ce115592..b8f5b434cc4a 100644 --- a/src/libs/CurrencyUtils.ts +++ b/src/libs/CurrencyUtils.ts @@ -99,7 +99,7 @@ function convertToFrontendAmount(amountAsInt: number): number { * @param currency - IOU currency * @param shouldFallbackToTbd - whether to return 'TBD' instead of a falsy value (e.g. 0.00) */ -function convertToDisplayString(amountInCents: number, currency: string = CONST.CURRENCY.USD, shouldFallbackToTbd = false): string { +function convertToDisplayString(amountInCents = 0, currency: string = CONST.CURRENCY.USD, shouldFallbackToTbd = false): string { if (shouldFallbackToTbd && !amountInCents) { return Localize.translateLocal('common.tbd'); } From 8098ae1dfad5315d7c0434121dc0da4da5cb989a Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Sun, 10 Dec 2023 02:27:43 +0100 Subject: [PATCH 14/78] add default title for tax rate and format amount --- .../MoneyRequestConfirmationList.js | 24 +++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/src/components/MoneyRequestConfirmationList.js b/src/components/MoneyRequestConfirmationList.js index 6abad7c28058..0d0ce3b92e56 100755 --- a/src/components/MoneyRequestConfirmationList.js +++ b/src/components/MoneyRequestConfirmationList.js @@ -166,6 +166,11 @@ const propTypes = { /** Holds data related to Money Request view state, rather than the underlying Money Request data. */ iou: iouPropTypes, + + transactionsDraft: PropTypes.shape({ + taxRate: PropTypes.string, + taxAmount: PropTypes.number, + }), }; const defaultProps = { @@ -200,6 +205,10 @@ const defaultProps = { shouldShowSmartScanFields: true, isPolicyExpenseChat: false, iou: iouDefaultProps, + transactionsDraft: { + taxRate: null, + taxAmount: null, + }, }; function MoneyRequestConfirmationList(props) { @@ -209,7 +218,7 @@ function MoneyRequestConfirmationList(props) { // Prop functions pass props itself as a "this" value to the function which means they change every time props change. const {onSendMoney, onConfirm, onSelectParticipant} = props; const {translate, toLocaleDigit} = useLocalize(); - const transaction = props.isEditingSplitBill ? props.draftTransaction || props.transaction : props.transaction; + const transaction = props.isEditingSplitBill ? props.splitTransactionDraft || props.transaction : props.transaction; const {canUseViolations} = usePermissions(); const isTypeRequest = props.iouType === CONST.IOU.TYPE.REQUEST; @@ -255,6 +264,7 @@ function MoneyRequestConfirmationList(props) { shouldCalculateDistanceAmount ? DistanceRequestUtils.getDistanceRequestAmount(distance, unit, rate) : props.iouAmount, props.isDistanceRequest ? currency : props.iouCurrencyCode, ); + const formattedTaxAmount = CurrencyUtils.convertToDisplayString(props.transactionsDraft.taxAmount, props.iouCurrencyCode); const isFocused = useIsFocused(); const [formError, setFormError] = useState(''); @@ -727,7 +737,7 @@ function MoneyRequestConfirmationList(props) { {shouldShowTax && ( `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, selector: DistanceRequestUtils.getDefaultMileageRate, }, - draftTransaction: { + splitTransactionDraft: { key: ({transactionID}) => `${ONYXKEYS.COLLECTION.SPLIT_TRANSACTION_DRAFT}${transactionID}`, }, transaction: { key: ({transactionID}) => `${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, }, + transactionsDraft: { + key: ({transactionID}) => `${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}`, + }, policy: { key: ({policyID}) => `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, }, + policyTaxRates: { + key: ({policyID}) => `${ONYXKEYS.COLLECTION.POLICY_TAX_RATE}${policyID}`, + }, iou: { key: ONYXKEYS.IOU, }, From 11ab5300460b5884b82ed0bda1d9f4f76cfa5c78 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Sun, 10 Dec 2023 02:28:55 +0100 Subject: [PATCH 15/78] set tax rate and tax amount to onyx --- .../iou/steps/IOURequestStepTaxAmountPage.js | 30 ++++++++++++++----- .../iou/steps/IOURequestStepTaxRatePage.js | 29 ++++++++++++++++-- 2 files changed, 49 insertions(+), 10 deletions(-) diff --git a/src/pages/iou/steps/IOURequestStepTaxAmountPage.js b/src/pages/iou/steps/IOURequestStepTaxAmountPage.js index ff57674f7889..43d776dc3670 100644 --- a/src/pages/iou/steps/IOURequestStepTaxAmountPage.js +++ b/src/pages/iou/steps/IOURequestStepTaxAmountPage.js @@ -7,6 +7,7 @@ import {withOnyx} from 'react-native-onyx'; import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import ScreenWrapper from '@components/ScreenWrapper'; +import compose from '@libs/compose'; import * as CurrencyUtils from '@libs/CurrencyUtils'; import * as IOUUtils from '@libs/IOUUtils'; import Navigation from '@libs/Navigation/Navigation'; @@ -36,13 +37,20 @@ const propTypes = { /** Holds data related to Money Request view state, rather than the underlying Money Request data. */ iou: iouPropTypes, + + transactionsDraft: PropTypes.shape({ + taxAmount: PropTypes.number, + }), }; const defaultProps = { iou: iouDefaultProps, + transactionsDraft: { + taxAmount: null, + }, }; -function IOURequestStepTaxAmountPage({route, iou}) { +function IOURequestStepTaxAmountPage({route, iou, transactionsDraft}) { const styles = useThemeStyles(); const textInput = useRef(null); const isEditing = Navigation.getActiveRoute().includes('taxAmount'); @@ -79,15 +87,16 @@ function IOURequestStepTaxAmountPage({route, iou}) { const updateTaxAmount = (currentAmount) => { const amountInSmallestCurrencyUnits = CurrencyUtils.convertToBackendAmount(Number.parseFloat(currentAmount)); - IOU.setMoneyRequestTaxAmount(amountInSmallestCurrencyUnits); - navigateBack(); + IOU.setMoneyRequestTaxAmount(iou.transactionID, amountInSmallestCurrencyUnits); + IOU.setMoneyRequestCurrency(currency); + Navigation.goBack(ROUTES.MONEY_REQUEST_CONFIRMATION.getRoute(iouType, reportID)); }; const content = ( (textInput.current = e)} onCurrencyButtonPress={navigateToCurrencySelectionPage} onSubmitButtonPress={updateTaxAmount} @@ -118,6 +127,13 @@ function IOURequestStepTaxAmountPage({route, iou}) { IOURequestStepTaxAmountPage.propTypes = propTypes; IOURequestStepTaxAmountPage.defaultProps = defaultProps; IOURequestStepTaxAmountPage.displayName = 'IOURequestStepTaxAmountPage'; -export default withOnyx({ - iou: {key: ONYXKEYS.IOU}, -})(IOURequestStepTaxAmountPage); +export default compose( + withOnyx({ + iou: {key: ONYXKEYS.IOU}, + }), + withOnyx({ + transactionsDraft: { + key: ({iou}) => `${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${iou.transactionID}`, + }, + }), +)(IOURequestStepTaxAmountPage); diff --git a/src/pages/iou/steps/IOURequestStepTaxRatePage.js b/src/pages/iou/steps/IOURequestStepTaxRatePage.js index 50dc64b735a4..4d0e6bda659d 100644 --- a/src/pages/iou/steps/IOURequestStepTaxRatePage.js +++ b/src/pages/iou/steps/IOURequestStepTaxRatePage.js @@ -8,6 +8,7 @@ import TaxPicker from '@components/TaxPicker'; import taxPropTypes from '@components/taxPropTypes'; import compose from '@libs/compose'; import Navigation from '@libs/Navigation/Navigation'; +import {iouDefaultProps, iouPropTypes} from '@pages/iou/propTypes'; import * as IOU from '@userActions/IOU'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; @@ -25,13 +26,24 @@ const propTypes = { }), }).isRequired, policyTaxRates: taxPropTypes, + + /** Holds data related to Money Request view state, rather than the underlying Money Request data. */ + iou: iouPropTypes, + + transactionsDraft: PropTypes.shape({ + taxRate: PropTypes.string, + }), }; const defaultProps = { policyTaxRates: {}, + iou: iouDefaultProps, + transactionsDraft: { + taxRate: null, + }, }; -function IOURequestStepTaxRatePage({route, policyTaxRates}) { +function IOURequestStepTaxRatePage({route, iou, policyTaxRates, transactionsDraft}) { const iouType = lodashGet(route, 'params.iouType', ''); const reportID = lodashGet(route, 'params.reportID', ''); @@ -39,6 +51,12 @@ function IOURequestStepTaxRatePage({route, policyTaxRates}) { Navigation.goBack(ROUTES.MONEY_REQUEST_CONFIRMATION.getRoute(iouType, reportID)); } + const updateTaxRates = (taxes) => { + IOU.setMoneyRequestTaxRate(iou.transactionID, taxes.text); + + Navigation.goBack(ROUTES.MONEY_REQUEST_CONFIRMATION.getRoute(iouType, reportID)); + }; + return ( navigateBack()} /> {}} + onSubmit={updateTaxRates} /> @@ -85,4 +103,9 @@ export default compose( key: ({report}) => `${ONYXKEYS.COLLECTION.POLICY_TAX_RATE}${report ? report.policyID : '0'}`, }, }), + withOnyx({ + transactionsDraft: { + key: ({iou}) => `${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${iou.transactionID}`, + }, + }), )(IOURequestStepTaxRatePage); From bebace8ac01a95eb994d2c316cfc3898d241bf41 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Wed, 13 Dec 2023 06:10:08 +0100 Subject: [PATCH 16/78] add defaulttax title --- src/components/MoneyRequestConfirmationList.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/components/MoneyRequestConfirmationList.js b/src/components/MoneyRequestConfirmationList.js index 0d0ce3b92e56..d82dfc358197 100755 --- a/src/components/MoneyRequestConfirmationList.js +++ b/src/components/MoneyRequestConfirmationList.js @@ -349,6 +349,9 @@ function MoneyRequestConfirmationList(props) { const canModifyParticipants = !props.isReadOnly && props.canModifyParticipants && props.hasMultipleParticipants; const shouldDisablePaidBySection = canModifyParticipants; + const defaulTaxKey = props.policyTaxRates.defaultExternalID; + const defaultTaxName = props.policyTaxRates.taxes[defaulTaxKey].name; + const optionSelectorSections = useMemo(() => { const sections = []; const unselectedParticipants = _.filter(props.selectedParticipants, (participant) => !participant.selected); @@ -737,7 +740,7 @@ function MoneyRequestConfirmationList(props) { {shouldShowTax && ( Date: Wed, 13 Dec 2023 06:14:19 +0100 Subject: [PATCH 17/78] update tax rate section --- src/libs/OptionsListUtils.js | 86 ++++++++++++++++++++++++++++++++---- 1 file changed, 78 insertions(+), 8 deletions(-) diff --git a/src/libs/OptionsListUtils.js b/src/libs/OptionsListUtils.js index c062924234ce..22d053347b27 100644 --- a/src/libs/OptionsListUtils.js +++ b/src/libs/OptionsListUtils.js @@ -639,6 +639,18 @@ function getEnabledCategoriesCount(options) { return _.filter(options, (option) => option.enabled).length; } +/** + * Calculates count of all tax enabled options + * + * @param {Object[]} options - an initial strings array + * @param {Boolean} options[].isDisabled - a flag to enable/disable option in a list + * @param {String} options[].name - a name of an option + * @returns {Number} + */ +function getEnabledTaxRateCount(options) { + return _.filter(options, (option) => !option.isDisabled).length; +} + /** * Verifies that there is at least one enabled option * @@ -1057,39 +1069,96 @@ function sortTaxRates(taxRates) { function getTaxRatesOptions(taxRates) { return _.map(taxRates, (taxRate) => ({ - text: `${taxRate.name} (${taxRate.value})`, + text: taxRate.name, keyForList: taxRate.name, searchText: taxRate.name, tooltipText: taxRate.name, - isDisabled: false, + isDisabled: taxRate.isDisabled, })); } -function getTaxRatesSection(policyTaxRates, selectedOptions) { +function getTaxRatesSection(policyTaxRates, selectedOptions, searchInputValue) { const policyRatesSections = []; const sortedTaxRates = sortTaxRates(policyTaxRates.taxes); - const numberOfTaxRates = _.size(sortedTaxRates); - const indexOffset = 0; + const enabledTaxRates = _.filter(sortedTaxRates, (taxRate) => !taxRate.isDisabled); + const numberOfTaxRates = _.size(enabledTaxRates); + + let indexOffset = 0; + // If all tax rates are disabled but there's a previously selected tag, show only the selected tag if (numberOfTaxRates === 0 && selectedOptions.length > 0) { + const selectedTaxRateOptions = _.map(selectedOptions, (option) => ({ + name: option.name, + // Should be marked as enabled to be able to be de-selected + isDisabled: false, + })); policyRatesSections.push({ // "Selected" section title: '', shouldShow: false, indexOffset, - data: getCategoryOptionTree(getTaxRatesOptions), + data: getTaxRatesOptions(selectedTaxRateOptions), }); return policyRatesSections; } + if (!_.isEmpty(searchInputValue)) { + const searchTaxRates = _.filter(enabledTaxRates, (taxRate) => taxRate.name.toLowerCase().includes(searchInputValue.toLowerCase())); + + policyRatesSections.push({ + // "Search" section + title: '', + shouldShow: true, + indexOffset, + data: getTaxRatesOptions(searchTaxRates), + }); + + return policyRatesSections; + } + + if (numberOfTaxRates < CONST.TAX_RATES_LIST_THRESHOLD) { + policyRatesSections.push({ + // "All" section when items amount less than the threshold + title: '', + shouldShow: false, + indexOffset, + data: getTaxRatesOptions(enabledTaxRates), + }); + + return policyRatesSections; + } + + const selectedOptionNames = _.map(selectedOptions, (selectedOption) => selectedOption.name); + const filteredTaxRates = _.filter(enabledTaxRates, (taxRate) => !_.includes(selectedOptionNames, taxRate.name)); + + if (!_.isEmpty(selectedOptions)) { + const selectedTaxRatesOptions = _.map(selectedOptions, (option) => { + const taxRateObject = _.find(policyTaxRates.taxes, (taxRate) => taxRate.name === option.name); + return { + name: option.name, + enabled: Boolean(taxRateObject && !taxRateObject.isDisabled), + }; + }); + + policyRatesSections.push({ + // "Selected" section + title: '', + shouldShow: true, + indexOffset, + data: getTaxRatesOptions(selectedTaxRatesOptions), + }); + + indexOffset += selectedOptions.length; + } + policyRatesSections.push({ // "All" section when items amount more than the threshold - title: Localize.translateLocal('common.all'), + title: '', shouldShow: true, indexOffset, - data: getTaxRatesOptions(sortedTaxRates), + data: getTaxRatesOptions(filteredTaxRates), }); return policyRatesSections; @@ -1829,6 +1898,7 @@ export { shouldOptionShowTooltip, getLastMessageTextForReport, getEnabledCategoriesCount, + getEnabledTaxRateCount, hasEnabledOptions, sortCategories, getCategoryOptionTree, From 2abfc7c3a1b1b75f104535c6a61d262e041cfc3d Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Wed, 13 Dec 2023 06:30:13 +0100 Subject: [PATCH 18/78] update tax Amount based on tax rate --- .../iou/steps/IOURequestStepTaxRatePage.js | 37 +++++++++++++------ 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/src/pages/iou/steps/IOURequestStepTaxRatePage.js b/src/pages/iou/steps/IOURequestStepTaxRatePage.js index 4d0e6bda659d..0c9d16aed540 100644 --- a/src/pages/iou/steps/IOURequestStepTaxRatePage.js +++ b/src/pages/iou/steps/IOURequestStepTaxRatePage.js @@ -2,11 +2,13 @@ import lodashGet from 'lodash/get'; import PropTypes from 'prop-types'; import React from 'react'; import {withOnyx} from 'react-native-onyx'; +import _ from 'underscore'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import ScreenWrapper from '@components/ScreenWrapper'; import TaxPicker from '@components/TaxPicker'; import taxPropTypes from '@components/taxPropTypes'; import compose from '@libs/compose'; +import * as CurrencyUtils from '@libs/CurrencyUtils'; import Navigation from '@libs/Navigation/Navigation'; import {iouDefaultProps, iouPropTypes} from '@pages/iou/propTypes'; import * as IOU from '@userActions/IOU'; @@ -43,6 +45,13 @@ const defaultProps = { }, }; +// this is the formulae to calculate tax +const calculateAmount = (taxRates, selectedTaxRate, amount) => { + const percentage = _.find(taxRates, (taxRate) => taxRate.name === selectedTaxRate).value; + const divisor = percentage.slice(0, -1) / 100 + 1; // slice to remove % at the end; converts "10%" to "10" + return parseInt(Math.round(amount - amount / divisor), 10) / 100; // returns The expense amount of transaction +}; + function IOURequestStepTaxRatePage({route, iou, policyTaxRates, transactionsDraft}) { const iouType = lodashGet(route, 'params.iouType', ''); const reportID = lodashGet(route, 'params.reportID', ''); @@ -52,7 +61,10 @@ function IOURequestStepTaxRatePage({route, iou, policyTaxRates, transactionsDraf } const updateTaxRates = (taxes) => { + calculateAmount(policyTaxRates.taxes, taxes.text, iou.amount); + const amountInSmallestCurrencyUnits = CurrencyUtils.convertToBackendAmount(Number.parseFloat(calculateAmount)); IOU.setMoneyRequestTaxRate(iou.transactionID, taxes.text); + IOU.setMoneyRequestTaxAmount(iou.transactionID, amountInSmallestCurrencyUnits); Navigation.goBack(ROUTES.MONEY_REQUEST_CONFIRMATION.getRoute(iouType, reportID)); }; @@ -63,17 +75,20 @@ function IOURequestStepTaxRatePage({route, iou, policyTaxRates, transactionsDraf shouldEnableMaxHeight testID={IOURequestStepTaxRatePage.displayName} > - <> - navigateBack()} - /> - - + {({insets}) => ( + <> + navigateBack()} + /> + + + )} ); } From ce50e7cd2f4d2339f14a32104e87f1b4346582ad Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Wed, 13 Dec 2023 06:31:55 +0100 Subject: [PATCH 19/78] should show search input if threshold is more than 8 --- src/components/TaxPicker/index.js | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/components/TaxPicker/index.js b/src/components/TaxPicker/index.js index befe5530d891..92858ca37928 100644 --- a/src/components/TaxPicker/index.js +++ b/src/components/TaxPicker/index.js @@ -4,14 +4,21 @@ import _ from 'underscore'; import OptionsSelector from '@components/OptionsSelector'; import useLocalize from '@hooks/useLocalize'; import * as OptionsListUtils from '@libs/OptionsListUtils'; +import * as StyleUtils from '@styles/StyleUtils'; import useThemeStyles from '@styles/useThemeStyles'; +import CONST from '@src/CONST'; import {defaultProps, propTypes} from './taxPickerPropTypes'; -function TaxPicker({selectedTaxRate, policyTaxRates, onSubmit}) { +function TaxPicker({selectedTaxRate, policyTaxRates, insets, onSubmit}) { const styles = useThemeStyles(); const {translate} = useLocalize(); const [searchValue, setSearchValue] = useState(''); + const policyTaxRatesCount = OptionsListUtils.getEnabledTaxRateCount(policyTaxRates.taxes); + const isTaxRatesCountBelowThreshold = policyTaxRatesCount < CONST.TAX_RATES_LIST_THRESHOLD; + + const shouldShowTextInput = !isTaxRatesCountBelowThreshold; + const selectedOptions = useMemo(() => { if (!selectedTaxRate) { return []; @@ -54,6 +61,7 @@ function TaxPicker({selectedTaxRate, policyTaxRates, onSubmit}) { return ( From 9e12ece66efadad9de6323b9d764b9d198630bfe Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Wed, 13 Dec 2023 06:32:56 +0100 Subject: [PATCH 20/78] should show error if current amount is greater than iou amount --- src/pages/iou/steps/MoneyRequestAmountForm.js | 21 ++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/src/pages/iou/steps/MoneyRequestAmountForm.js b/src/pages/iou/steps/MoneyRequestAmountForm.js index 2150af0d1040..59726809caaa 100644 --- a/src/pages/iou/steps/MoneyRequestAmountForm.js +++ b/src/pages/iou/steps/MoneyRequestAmountForm.js @@ -2,6 +2,7 @@ import lodashGet from 'lodash/get'; import PropTypes from 'prop-types'; import React, {useCallback, useEffect, useRef, useState} from 'react'; import {ScrollView, View} from 'react-native'; +import {withOnyx} from 'react-native-onyx'; import _ from 'underscore'; import BigNumberPad from '@components/BigNumberPad'; import Button from '@components/Button'; @@ -15,8 +16,10 @@ import * as CurrencyUtils from '@libs/CurrencyUtils'; import * as DeviceCapabilities from '@libs/DeviceCapabilities'; import getOperatingSystem from '@libs/getOperatingSystem'; import * as MoneyRequestUtils from '@libs/MoneyRequestUtils'; +import {iouDefaultProps, iouPropTypes} from '@pages/iou/propTypes'; import useThemeStyles from '@styles/useThemeStyles'; import CONST from '@src/CONST'; +import ONYXKEYS from '@src/ONYXKEYS'; const propTypes = { /** IOU amount saved in Onyx */ @@ -39,9 +42,13 @@ const propTypes = { /** The current tab we have navigated to in the request modal. String that corresponds to the request type. */ selectedTab: PropTypes.oneOf([CONST.TAB.DISTANCE, CONST.TAB.MANUAL, CONST.TAB.SCAN]), + + /** Holds data related to Money Request view state, rather than the underlying Money Request data. */ + iou: iouPropTypes, }; const defaultProps = { + iou: iouDefaultProps, amount: 0, currency: CONST.CURRENCY.USD, forwardedRef: null, @@ -63,12 +70,13 @@ const getNewSelection = (oldSelection, prevLength, newLength) => { }; const isAmountInvalid = (amount) => !amount.length || parseFloat(amount) < 0.01; +const isTaxAmountInvalid = (currentAmount, amount) => amount > 0 && currentAmount > CurrencyUtils.convertToFrontendAmount(amount); const AMOUNT_VIEW_ID = 'amountView'; const NUM_PAD_CONTAINER_VIEW_ID = 'numPadContainerView'; const NUM_PAD_VIEW_ID = 'numPadView'; -function MoneyRequestAmountForm({amount, currency, isEditing, forwardedRef, onCurrencyButtonPress, onSubmitButtonPress, selectedTab}) { +function MoneyRequestAmountForm({iou, amount, currency, isEditing, forwardedRef, onCurrencyButtonPress, onSubmitButtonPress, selectedTab}) { const styles = useThemeStyles(); const {isExtraSmallScreenHeight} = useWindowDimensions(); const {translate, toLocaleDigit, numberFormat} = useLocalize(); @@ -223,13 +231,18 @@ function MoneyRequestAmountForm({amount, currency, isEditing, forwardedRef, onCu return; } + if (isTaxAmountInvalid(currentAmount, iou.amount)) { + setFormError('iou.error.invalidAmount'); + return; + } + // Update display amount string post-edit to ensure consistency with backend amount // Reference: https://github.com/Expensify/App/issues/30505 const backendAmount = CurrencyUtils.convertToBackendAmount(Number.parseFloat(currentAmount)); initializeAmount(backendAmount); onSubmitButtonPress(currentAmount); - }, [onSubmitButtonPress, currentAmount, initializeAmount]); + }, [onSubmitButtonPress, currentAmount, iou.amount, initializeAmount]); /** * Input handler to check for a forward-delete key (or keyboard shortcut) press. @@ -335,4 +348,6 @@ const MoneyRequestAmountFormWithRef = React.forwardRef((props, ref) => ( MoneyRequestAmountFormWithRef.displayName = 'MoneyRequestAmountFormWithRef'; -export default MoneyRequestAmountFormWithRef; +export default withOnyx({ + iou: {key: ONYXKEYS.IOU}, +})(MoneyRequestAmountFormWithRef); From e5f6e17621e2d2f1c25e857b907c892b18b37190 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Wed, 13 Dec 2023 06:36:45 +0100 Subject: [PATCH 21/78] update comment --- src/libs/OptionsListUtils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/OptionsListUtils.js b/src/libs/OptionsListUtils.js index 22d053347b27..e8cace7a625a 100644 --- a/src/libs/OptionsListUtils.js +++ b/src/libs/OptionsListUtils.js @@ -1086,7 +1086,7 @@ function getTaxRatesSection(policyTaxRates, selectedOptions, searchInputValue) { let indexOffset = 0; - // If all tax rates are disabled but there's a previously selected tag, show only the selected tag + // If all tax are disabled but there's a previously selected tag, show only the selected tag if (numberOfTaxRates === 0 && selectedOptions.length > 0) { const selectedTaxRateOptions = _.map(selectedOptions, (option) => ({ name: option.name, From d9fa49d85ae49de277cdeef580a31e2a69fb9e45 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Wed, 13 Dec 2023 06:40:38 +0100 Subject: [PATCH 22/78] set taxAmount in Onyx --- src/pages/iou/steps/IOURequestStepTaxRatePage.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/iou/steps/IOURequestStepTaxRatePage.js b/src/pages/iou/steps/IOURequestStepTaxRatePage.js index 0c9d16aed540..5caeb37438b5 100644 --- a/src/pages/iou/steps/IOURequestStepTaxRatePage.js +++ b/src/pages/iou/steps/IOURequestStepTaxRatePage.js @@ -61,8 +61,8 @@ function IOURequestStepTaxRatePage({route, iou, policyTaxRates, transactionsDraf } const updateTaxRates = (taxes) => { - calculateAmount(policyTaxRates.taxes, taxes.text, iou.amount); - const amountInSmallestCurrencyUnits = CurrencyUtils.convertToBackendAmount(Number.parseFloat(calculateAmount)); + const taxAmount = calculateAmount(policyTaxRates.taxes, taxes.text, iou.amount); + const amountInSmallestCurrencyUnits = CurrencyUtils.convertToBackendAmount(Number.parseFloat(taxAmount)); IOU.setMoneyRequestTaxRate(iou.transactionID, taxes.text); IOU.setMoneyRequestTaxAmount(iou.transactionID, amountInSmallestCurrencyUnits); From 98db78be8d3146213478f9bad0eb5bbe649f5419 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Wed, 13 Dec 2023 10:23:06 +0100 Subject: [PATCH 23/78] add tax translation strings --- src/languages/en.ts | 3 +++ src/languages/es.ts | 3 +++ 2 files changed, 6 insertions(+) diff --git a/src/languages/en.ts b/src/languages/en.ts index 8f772f1260bb..adec0bafad40 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -528,6 +528,8 @@ export default { }, iou: { amount: 'Amount', + taxAmount: 'Tax amount', + taxRate: 'Tax rate', approve: 'Approve', approved: 'Approved', cash: 'Cash', @@ -596,6 +598,7 @@ export default { categorySelection: 'Select a category to add additional organization to your money.', error: { invalidAmount: 'Please enter a valid amount before continuing.', + invalidTaxAmount: ({amount}: RequestAmountParams) => `Maximum tax amount is ${amount}`, invalidSplit: 'Split amounts do not equal total amount', other: 'Unexpected error, please try again later', genericCreateFailureMessage: 'Unexpected error requesting money, please try again later', diff --git a/src/languages/es.ts b/src/languages/es.ts index 3887891299df..1b3a68621fb1 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -520,6 +520,8 @@ export default { }, iou: { amount: 'Importe', + taxAmount: 'Tax amount', + taxRate: 'Tax rate', approve: 'Aprobar', approved: 'Aprobado', cash: 'Efectivo', @@ -590,6 +592,7 @@ export default { categorySelection: 'Seleccione una categoría para organizar mejor tu dinero.', error: { invalidAmount: 'Por favor ingresa un monto válido antes de continuar.', + invalidTaxAmount: ({amount}: RequestAmountParams) => `El monto máximo del impuesto es ${amount}`, invalidSplit: 'La suma de las partes no equivale al monto total', other: 'Error inesperado, por favor inténtalo más tarde', genericCreateFailureMessage: 'Error inesperado solicitando dinero, Por favor, inténtalo más tarde', From e28ae1b81cb82a7b43e0f2cddfb0b1f17e3e9eb9 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Wed, 13 Dec 2023 10:29:24 +0100 Subject: [PATCH 24/78] format tax amount and use translation for invalid tax amount --- src/pages/iou/steps/MoneyRequestAmountForm.js | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/pages/iou/steps/MoneyRequestAmountForm.js b/src/pages/iou/steps/MoneyRequestAmountForm.js index 59726809caaa..970d559f10d8 100644 --- a/src/pages/iou/steps/MoneyRequestAmountForm.js +++ b/src/pages/iou/steps/MoneyRequestAmountForm.js @@ -70,7 +70,7 @@ const getNewSelection = (oldSelection, prevLength, newLength) => { }; const isAmountInvalid = (amount) => !amount.length || parseFloat(amount) < 0.01; -const isTaxAmountInvalid = (currentAmount, amount) => amount > 0 && currentAmount > CurrencyUtils.convertToFrontendAmount(amount); +const isTaxAmountInvalid = (currentAmount, amount, isEditing) => isEditing && currentAmount > CurrencyUtils.convertToFrontendAmount(amount); const AMOUNT_VIEW_ID = 'amountView'; const NUM_PAD_CONTAINER_VIEW_ID = 'numPadContainerView'; @@ -97,6 +97,8 @@ function MoneyRequestAmountForm({iou, amount, currency, isEditing, forwardedRef, const forwardDeletePressedRef = useRef(false); + const formattedTaxAmount = CurrencyUtils.convertToDisplayString(iou.amount, iou.currency); + /** * Event occurs when a user presses a mouse button over an DOM element. * @@ -227,12 +229,12 @@ function MoneyRequestAmountForm({iou, amount, currency, isEditing, forwardedRef, */ const submitAndNavigateToNextPage = useCallback(() => { if (isAmountInvalid(currentAmount)) { - setFormError('iou.error.invalidAmount'); + setFormError(translate('iou.error.invalidAmount')); return; } - if (isTaxAmountInvalid(currentAmount, iou.amount)) { - setFormError('iou.error.invalidAmount'); + if (isTaxAmountInvalid(currentAmount, iou.amount, isEditing)) { + setFormError(translate('iou.error.invalidTaxAmount', {amount: formattedTaxAmount})); return; } @@ -242,7 +244,7 @@ function MoneyRequestAmountForm({iou, amount, currency, isEditing, forwardedRef, initializeAmount(backendAmount); onSubmitButtonPress(currentAmount); - }, [onSubmitButtonPress, currentAmount, iou.amount, initializeAmount]); + }, [onSubmitButtonPress, currentAmount, iou.amount, isEditing, formattedTaxAmount, translate, initializeAmount]); /** * Input handler to check for a forward-delete key (or keyboard shortcut) press. @@ -303,7 +305,7 @@ function MoneyRequestAmountForm({iou, amount, currency, isEditing, forwardedRef, )} From 781952a9fabcc4a9384c9e986f4527a6af62f89c Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Wed, 13 Dec 2023 10:30:15 +0100 Subject: [PATCH 25/78] translate titles --- src/pages/iou/steps/IOURequestStepTaxAmountPage.js | 4 +++- src/pages/iou/steps/IOURequestStepTaxRatePage.js | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/pages/iou/steps/IOURequestStepTaxAmountPage.js b/src/pages/iou/steps/IOURequestStepTaxAmountPage.js index 43d776dc3670..6f8e8e9583b7 100644 --- a/src/pages/iou/steps/IOURequestStepTaxAmountPage.js +++ b/src/pages/iou/steps/IOURequestStepTaxAmountPage.js @@ -7,6 +7,7 @@ import {withOnyx} from 'react-native-onyx'; import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import ScreenWrapper from '@components/ScreenWrapper'; +import useLocalize from '@hooks/useLocalize'; import compose from '@libs/compose'; import * as CurrencyUtils from '@libs/CurrencyUtils'; import * as IOUUtils from '@libs/IOUUtils'; @@ -51,6 +52,7 @@ const defaultProps = { }; function IOURequestStepTaxAmountPage({route, iou, transactionsDraft}) { + const {translate} = useLocalize(); const styles = useThemeStyles(); const textInput = useRef(null); const isEditing = Navigation.getActiveRoute().includes('taxAmount'); @@ -113,7 +115,7 @@ function IOURequestStepTaxAmountPage({route, iou, transactionsDraft}) { {content} diff --git a/src/pages/iou/steps/IOURequestStepTaxRatePage.js b/src/pages/iou/steps/IOURequestStepTaxRatePage.js index 5caeb37438b5..1cf12cf56880 100644 --- a/src/pages/iou/steps/IOURequestStepTaxRatePage.js +++ b/src/pages/iou/steps/IOURequestStepTaxRatePage.js @@ -7,6 +7,7 @@ import HeaderWithBackButton from '@components/HeaderWithBackButton'; import ScreenWrapper from '@components/ScreenWrapper'; import TaxPicker from '@components/TaxPicker'; import taxPropTypes from '@components/taxPropTypes'; +import useLocalize from '@hooks/useLocalize'; import compose from '@libs/compose'; import * as CurrencyUtils from '@libs/CurrencyUtils'; import Navigation from '@libs/Navigation/Navigation'; @@ -53,6 +54,7 @@ const calculateAmount = (taxRates, selectedTaxRate, amount) => { }; function IOURequestStepTaxRatePage({route, iou, policyTaxRates, transactionsDraft}) { + const {translate} = useLocalize(); const iouType = lodashGet(route, 'params.iouType', ''); const reportID = lodashGet(route, 'params.reportID', ''); @@ -78,7 +80,7 @@ function IOURequestStepTaxRatePage({route, iou, policyTaxRates, transactionsDraf {({insets}) => ( <> navigateBack()} /> Date: Wed, 13 Dec 2023 11:42:48 +0100 Subject: [PATCH 26/78] update merge conflict --- src/SCREENS.ts | 2 ++ src/components/TaxPicker/index.js | 3 ++- src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx | 2 ++ src/libs/Navigation/linkingConfig.ts | 2 +- src/pages/iou/steps/MoneyRequestAmountForm.js | 4 ++-- 5 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/SCREENS.ts b/src/SCREENS.ts index 921f57953482..2c5b4a8d0046 100644 --- a/src/SCREENS.ts +++ b/src/SCREENS.ts @@ -129,6 +129,8 @@ const SCREENS = { STEP_WAYPOINT: 'Money_Request_Step_Waypoint', ROOT: 'Money_Request', AMOUNT: 'Money_Request_Amount', + TAX_AMOUNT: 'Money_Request_Tax_Amount', + TAX_RATE: 'Money_Request_Tax_Rate', PARTICIPANTS: 'Money_Request_Participants', CONFIRMATION: 'Money_Request_Confirmation', CURRENCY: 'Money_Request_Currency', diff --git a/src/components/TaxPicker/index.js b/src/components/TaxPicker/index.js index 92858ca37928..743010bddf4d 100644 --- a/src/components/TaxPicker/index.js +++ b/src/components/TaxPicker/index.js @@ -4,13 +4,14 @@ import _ from 'underscore'; import OptionsSelector from '@components/OptionsSelector'; import useLocalize from '@hooks/useLocalize'; import * as OptionsListUtils from '@libs/OptionsListUtils'; -import * as StyleUtils from '@styles/StyleUtils'; +import useStyleUtils from '@styles/useStyleUtils'; import useThemeStyles from '@styles/useThemeStyles'; import CONST from '@src/CONST'; import {defaultProps, propTypes} from './taxPickerPropTypes'; function TaxPicker({selectedTaxRate, policyTaxRates, insets, onSubmit}) { const styles = useThemeStyles(); + const StyleUtils = useStyleUtils(); const {translate} = useLocalize(); const [searchValue, setSearchValue] = useState(''); diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx b/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx index 29449f52ecd6..5ac9c0853b7c 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx @@ -88,6 +88,8 @@ const MoneyRequestModalStackNavigator = createModalStackNavigator require('../../../pages/iou/request/step/IOURequestStepWaypoint').default as React.ComponentType, [SCREENS.MONEY_REQUEST.ROOT]: () => require('../../../pages/iou/MoneyRequestSelectorPage').default as React.ComponentType, [SCREENS.MONEY_REQUEST.AMOUNT]: () => require('../../../pages/iou/steps/NewRequestAmountPage').default as React.ComponentType, + [SCREENS.MONEY_REQUEST.TAX_AMOUNT]: () => require('../../../pages/iou/steps/IOURequestStepTaxAmountPage').default as React.ComponentType, + [SCREENS.MONEY_REQUEST.TAX_RATE]: () => require('../../../pages/iou/steps/IOURequestStepTaxRatePage').default as React.ComponentType, [SCREENS.MONEY_REQUEST.PARTICIPANTS]: () => require('../../../pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsPage').default as React.ComponentType, [SCREENS.MONEY_REQUEST.CONFIRMATION]: () => require('../../../pages/iou/steps/MoneyRequestConfirmPage').default as React.ComponentType, [SCREENS.MONEY_REQUEST.CURRENCY]: () => require('../../../pages/iou/IOUCurrencySelection').default as React.ComponentType, diff --git a/src/libs/Navigation/linkingConfig.ts b/src/libs/Navigation/linkingConfig.ts index 6de0afe58b52..1879e2cb9c8e 100644 --- a/src/libs/Navigation/linkingConfig.ts +++ b/src/libs/Navigation/linkingConfig.ts @@ -418,7 +418,7 @@ const linkingConfig: LinkingOptions = { }, [SCREENS.MONEY_REQUEST.AMOUNT]: ROUTES.MONEY_REQUEST_AMOUNT.route, [SCREENS.MONEY_REQUEST.TAX_AMOUNT]: ROUTES.MONEY_REQUEST_TAX_AMOUNT.route, - [SCREENS.MONEY_REQUEST.TAX_RATE]: ROUTES.MONEY_REQUEST_TAX_RATE.route, + [SCREENS.MONEY_REQUEST.TAX_RATE]: ROUTES.MONEY_REQUEST_TAX_RATE.route, [SCREENS.MONEY_REQUEST.PARTICIPANTS]: ROUTES.MONEY_REQUEST_PARTICIPANTS.route, [SCREENS.MONEY_REQUEST.CONFIRMATION]: ROUTES.MONEY_REQUEST_CONFIRMATION.route, [SCREENS.MONEY_REQUEST.DATE]: ROUTES.MONEY_REQUEST_DATE.route, diff --git a/src/pages/iou/steps/MoneyRequestAmountForm.js b/src/pages/iou/steps/MoneyRequestAmountForm.js index 9345c25c0294..d5b6ae6442b3 100644 --- a/src/pages/iou/steps/MoneyRequestAmountForm.js +++ b/src/pages/iou/steps/MoneyRequestAmountForm.js @@ -43,8 +43,8 @@ const propTypes = { /** The current tab we have navigated to in the request modal. String that corresponds to the request type. */ selectedTab: PropTypes.oneOf([CONST.TAB_REQUEST.DISTANCE, CONST.TAB_REQUEST.MANUAL, CONST.TAB_REQUEST.SCAN]), - /** Holds data related to Money Request view state, rather than the underlying Money Request data. */ - iou: iouPropTypes, + /** Holds data related to Money Request view state, rather than the underlying Money Request data. */ + iou: iouPropTypes, }; const defaultProps = { From 62e98c1ad82c91593a5e20e8ddc847e49318d3c5 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Wed, 13 Dec 2023 13:14:04 +0100 Subject: [PATCH 27/78] translate tax amount and rate --- src/components/MoneyRequestConfirmationList.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/MoneyRequestConfirmationList.js b/src/components/MoneyRequestConfirmationList.js index fc0da151526b..c302c9f9b6d8 100755 --- a/src/components/MoneyRequestConfirmationList.js +++ b/src/components/MoneyRequestConfirmationList.js @@ -757,7 +757,7 @@ function MoneyRequestConfirmationList(props) { Navigation.navigate(ROUTES.MONEY_REQUEST_TAX_RATE.getRoute(props.iouType, props.reportID))} @@ -772,7 +772,7 @@ function MoneyRequestConfirmationList(props) { Navigation.navigate(ROUTES.MONEY_REQUEST_TAX_AMOUNT.getRoute(props.iouType, props.reportID))} From ff9ca23fbf3b58779f5fca044ff5b82bad2f97c4 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Thu, 14 Dec 2023 17:29:18 +0100 Subject: [PATCH 28/78] refactor tax amount and rate pages --- .../step}/IOURequestStepTaxAmountPage.js | 68 ++++++++++++++----- .../step}/IOURequestStepTaxRatePage.js | 55 ++++++++------- 2 files changed, 79 insertions(+), 44 deletions(-) rename src/pages/iou/{steps => request/step}/IOURequestStepTaxAmountPage.js (62%) rename src/pages/iou/{steps => request/step}/IOURequestStepTaxRatePage.js (75%) diff --git a/src/pages/iou/steps/IOURequestStepTaxAmountPage.js b/src/pages/iou/request/step/IOURequestStepTaxAmountPage.js similarity index 62% rename from src/pages/iou/steps/IOURequestStepTaxAmountPage.js rename to src/pages/iou/request/step/IOURequestStepTaxAmountPage.js index 6f8e8e9583b7..61588155273a 100644 --- a/src/pages/iou/steps/IOURequestStepTaxAmountPage.js +++ b/src/pages/iou/request/step/IOURequestStepTaxAmountPage.js @@ -7,18 +7,22 @@ import {withOnyx} from 'react-native-onyx'; import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import ScreenWrapper from '@components/ScreenWrapper'; +import transactionPropTypes from '@components/transactionPropTypes'; import useLocalize from '@hooks/useLocalize'; +import useThemeStyles from '@hooks/useThemeStyles'; import compose from '@libs/compose'; import * as CurrencyUtils from '@libs/CurrencyUtils'; import * as IOUUtils from '@libs/IOUUtils'; import Navigation from '@libs/Navigation/Navigation'; import {iouDefaultProps, iouPropTypes} from '@pages/iou/propTypes'; -import useThemeStyles from '@styles/useThemeStyles'; +import MoneyRequestAmountForm from '@pages/iou/steps/MoneyRequestAmountForm'; +import reportPropTypes from '@pages/reportPropTypes'; import * as IOU from '@userActions/IOU'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; -import MoneyRequestAmountForm from './MoneyRequestAmountForm'; +import withFullTransactionOrNotFound from './withFullTransactionOrNotFound'; +import withWritableReportOrNotFound from './withWritableReportOrNotFound'; const propTypes = { /** React Navigation route */ @@ -42,6 +46,13 @@ const propTypes = { transactionsDraft: PropTypes.shape({ taxAmount: PropTypes.number, }), + + /* Onyx Props */ + /** The report that the transaction belongs to */ + report: reportPropTypes, + + /** The transaction object being modified in Onyx */ + transaction: transactionPropTypes, }; const defaultProps = { @@ -49,18 +60,26 @@ const defaultProps = { transactionsDraft: { taxAmount: null, }, + report: {}, + transaction: {}, }; -function IOURequestStepTaxAmountPage({route, iou, transactionsDraft}) { +function IOURequestStepTaxAmountPage({ + route: { + params: {iouType, reportID, transactionID, backTo, currency: selectedCurrency}, + }, + iou, + transactionsDraft, + transaction, + transaction: {currency: originalCurrency}, + report, +}) { const {translate} = useLocalize(); const styles = useThemeStyles(); const textInput = useRef(null); const isEditing = Navigation.getActiveRoute().includes('taxAmount'); - const iouType = lodashGet(route, 'params.iouType', ''); - const reportID = lodashGet(route, 'params.reportID', ''); - const currentCurrency = lodashGet(route, 'params.currency', ''); - const currency = CurrencyUtils.isValidCurrencyCode(currentCurrency) ? currentCurrency : iou.currency; + const currency = selectedCurrency || originalCurrency; const focusTimeoutRef = useRef(null); useFocusEffect( @@ -83,21 +102,39 @@ function IOURequestStepTaxAmountPage({route, iou, transactionsDraft}) { // If the money request being created is a distance request, don't allow the user to choose the currency. // Only USD is allowed for distance requests. // Remove query from the route and encode it. - const activeRoute = encodeURIComponent(Navigation.getActiveRouteWithoutParams()); - Navigation.navigate(ROUTES.MONEY_REQUEST_CURRENCY.getRoute(iouType, reportID, currency, activeRoute)); + Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_CURRENCY.getRoute(iouType, transactionID, reportID, backTo ? 'confirm' : '', Navigation.getActiveRouteWithoutParams())); }; const updateTaxAmount = (currentAmount) => { const amountInSmallestCurrencyUnits = CurrencyUtils.convertToBackendAmount(Number.parseFloat(currentAmount)); - IOU.setMoneyRequestTaxAmount(iou.transactionID, amountInSmallestCurrencyUnits); - IOU.setMoneyRequestCurrency(currency); - Navigation.goBack(ROUTES.MONEY_REQUEST_CONFIRMATION.getRoute(iouType, reportID)); + IOU.setMoneyRequestTaxAmount(transactionID, amountInSmallestCurrencyUnits); + + IOU.setMoneyRequestAmount_temporaryForRefactor(transactionID, amountInSmallestCurrencyUnits, currency || CONST.CURRENCY.USD); + + if (backTo) { + Navigation.goBack(backTo); + return; + } + + // If a reportID exists in the report object, it's because the user started this flow from using the + button in the composer + // inside a report. In this case, the participants can be automatically assigned from the report and the user can skip the participants step and go straight + // to the confirm step. + if (report.reportID) { + IOU.setMoneyRequestParticipantsFromReport(transactionID, report); + Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_CONFIRMATION.getRoute(iouType, transactionID, reportID)); + return; + } + + // If there was no reportID, then that means the user started this flow from the global + menu + // and an optimistic reportID was generated. In that case, the next step is to select the participants for this request. + Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_PARTICIPANTS.getRoute(iouType, transactionID, reportID)); }; const content = ( (textInput.current = e)} onCurrencyButtonPress={navigateToCurrencySelectionPage} @@ -130,12 +167,11 @@ IOURequestStepTaxAmountPage.propTypes = propTypes; IOURequestStepTaxAmountPage.defaultProps = defaultProps; IOURequestStepTaxAmountPage.displayName = 'IOURequestStepTaxAmountPage'; export default compose( - withOnyx({ - iou: {key: ONYXKEYS.IOU}, - }), + withWritableReportOrNotFound, + withFullTransactionOrNotFound, withOnyx({ transactionsDraft: { - key: ({iou}) => `${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${iou.transactionID}`, + key: ({transaction}) => `${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transaction.transactionID}`, }, }), )(IOURequestStepTaxAmountPage); diff --git a/src/pages/iou/steps/IOURequestStepTaxRatePage.js b/src/pages/iou/request/step/IOURequestStepTaxRatePage.js similarity index 75% rename from src/pages/iou/steps/IOURequestStepTaxRatePage.js rename to src/pages/iou/request/step/IOURequestStepTaxRatePage.js index 1cf12cf56880..fe7f1f77ba65 100644 --- a/src/pages/iou/steps/IOURequestStepTaxRatePage.js +++ b/src/pages/iou/request/step/IOURequestStepTaxRatePage.js @@ -1,4 +1,3 @@ -import lodashGet from 'lodash/get'; import PropTypes from 'prop-types'; import React from 'react'; import {withOnyx} from 'react-native-onyx'; @@ -7,14 +6,17 @@ import HeaderWithBackButton from '@components/HeaderWithBackButton'; import ScreenWrapper from '@components/ScreenWrapper'; import TaxPicker from '@components/TaxPicker'; import taxPropTypes from '@components/taxPropTypes'; +import transactionPropTypes from '@components/transactionPropTypes'; import useLocalize from '@hooks/useLocalize'; import compose from '@libs/compose'; import * as CurrencyUtils from '@libs/CurrencyUtils'; import Navigation from '@libs/Navigation/Navigation'; -import {iouDefaultProps, iouPropTypes} from '@pages/iou/propTypes'; +import reportPropTypes from '@pages/reportPropTypes'; import * as IOU from '@userActions/IOU'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; +import withFullTransactionOrNotFound from './withFullTransactionOrNotFound'; +import withWritableReportOrNotFound from './withWritableReportOrNotFound'; const propTypes = { /** Route from navigation */ @@ -30,20 +32,25 @@ const propTypes = { }).isRequired, policyTaxRates: taxPropTypes, - /** Holds data related to Money Request view state, rather than the underlying Money Request data. */ - iou: iouPropTypes, - transactionsDraft: PropTypes.shape({ taxRate: PropTypes.string, }), + + /* Onyx Props */ + /** The report that the transaction belongs to */ + report: reportPropTypes, + + /** The transaction object being modified in Onyx */ + transaction: transactionPropTypes, }; const defaultProps = { + report: {}, policyTaxRates: {}, - iou: iouDefaultProps, transactionsDraft: { taxRate: null, }, + transaction: {}, }; // this is the formulae to calculate tax @@ -53,20 +60,25 @@ const calculateAmount = (taxRates, selectedTaxRate, amount) => { return parseInt(Math.round(amount - amount / divisor), 10) / 100; // returns The expense amount of transaction }; -function IOURequestStepTaxRatePage({route, iou, policyTaxRates, transactionsDraft}) { +function IOURequestStepTaxRatePage({ + route: { + params: {iouType, reportID}, + }, + policyTaxRates, + transactionsDraft, + transaction, +}) { const {translate} = useLocalize(); - const iouType = lodashGet(route, 'params.iouType', ''); - const reportID = lodashGet(route, 'params.reportID', ''); function navigateBack() { Navigation.goBack(ROUTES.MONEY_REQUEST_CONFIRMATION.getRoute(iouType, reportID)); } const updateTaxRates = (taxes) => { - const taxAmount = calculateAmount(policyTaxRates.taxes, taxes.text, iou.amount); + const taxAmount = calculateAmount(policyTaxRates.taxes, taxes.text, transaction.amount); const amountInSmallestCurrencyUnits = CurrencyUtils.convertToBackendAmount(Number.parseFloat(taxAmount)); - IOU.setMoneyRequestTaxRate(iou.transactionID, taxes.text); - IOU.setMoneyRequestTaxAmount(iou.transactionID, amountInSmallestCurrencyUnits); + IOU.setMoneyRequestTaxRate(transaction.transactionID, taxes.text); + IOU.setMoneyRequestTaxAmount(transaction.transactionID, amountInSmallestCurrencyUnits); Navigation.goBack(ROUTES.MONEY_REQUEST_CONFIRMATION.getRoute(iouType, reportID)); }; @@ -100,21 +112,8 @@ IOURequestStepTaxRatePage.defaultProps = defaultProps; IOURequestStepTaxRatePage.displayName = 'IOURequestStepTaxRatePage'; export default compose( - withOnyx({ - iou: { - key: ONYXKEYS.IOU, - }, - }), - // eslint-disable-next-line rulesdir/no-multiple-onyx-in-file - withOnyx({ - report: { - key: ({route, iou}) => { - const reportID = IOU.getIOUReportID(iou, route); - - return `${ONYXKEYS.COLLECTION.REPORT}${reportID}`; - }, - }, - }), + withWritableReportOrNotFound, + withFullTransactionOrNotFound, withOnyx({ policyTaxRates: { key: ({report}) => `${ONYXKEYS.COLLECTION.POLICY_TAX_RATE}${report ? report.policyID : '0'}`, @@ -122,7 +121,7 @@ export default compose( }), withOnyx({ transactionsDraft: { - key: ({iou}) => `${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${iou.transactionID}`, + key: ({transaction}) => `${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transaction.transactionID}`, }, }), )(IOURequestStepTaxRatePage); From b692144da5a33bee9fe3c3633425fda4f240ab40 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Thu, 14 Dec 2023 17:29:58 +0100 Subject: [PATCH 29/78] refactor tax amount and rate routes and navigation --- src/ROUTES.ts | 18 ++++++++++-------- src/SCREENS.ts | 4 ++-- .../AppNavigator/ModalStackNavigators.tsx | 4 ++-- src/libs/Navigation/linkingConfig.ts | 4 ++-- src/libs/Navigation/types.ts | 8 ++++++-- 5 files changed, 22 insertions(+), 16 deletions(-) diff --git a/src/ROUTES.ts b/src/ROUTES.ts index 0e9051852e93..637f861cc2cc 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -286,14 +286,6 @@ const ROUTES = { route: ':iouType/new/merchant/:reportID?', getRoute: (iouType: string, reportID = '') => `${iouType}/new/merchant/${reportID}` as const, }, - MONEY_REQUEST_TAX_RATE: { - route: ':iouType/new/taxRate/:reportID?', - getRoute: (iouType: string, reportID = '') => `${iouType}/new/taxRate/${reportID}` as const, - }, - MONEY_REQUEST_TAX_AMOUNT: { - route: ':iouType/new/taxAmount/:reportID?', - getRoute: (iouType: string, reportID = '') => `${iouType}/new/taxAmount/${reportID}` as const, - }, MONEY_REQUEST_WAYPOINT: { route: ':iouType/new/waypoint/:waypointIndex', getRoute: (iouType: string, waypointIndex: number) => `${iouType}/new/waypoint/${waypointIndex}` as const, @@ -330,6 +322,16 @@ const ROUTES = { getRoute: (iouType: ValueOf, transactionID: string, reportID: string, backTo = '') => getUrlWithBackToParam(`create/${iouType}/amount/${transactionID}/${reportID}/`, backTo), }, + MONEY_REQUEST_STEP_TAX_RATE: { + route: 'create/:iouType/taxRate/:transactionID/:reportID?', + getRoute: (iouType: ValueOf, transactionID: string, reportID: string, backTo: string) => + getUrlWithBackToParam(`create/${iouType}/taxRate/${transactionID}/${reportID}`, backTo), + }, + MONEY_REQUEST_STEP_TAX_AMOUNT: { + route: 'create/:iouType/taxAmount/:transactionID/:reportID?', + getRoute: (iouType: ValueOf, transactionID: string, reportID: string, backTo: string) => + getUrlWithBackToParam(`create/${iouType}/taxAmount/${transactionID}/${reportID}`, backTo), + }, MONEY_REQUEST_STEP_CATEGORY: { route: 'create/:iouType/category/:transactionID/:reportID/', getRoute: (iouType: ValueOf, transactionID: string, reportID: string, backTo = '') => diff --git a/src/SCREENS.ts b/src/SCREENS.ts index f79a291ccd30..4f97ebadad36 100644 --- a/src/SCREENS.ts +++ b/src/SCREENS.ts @@ -129,10 +129,10 @@ const SCREENS = { STEP_SCAN: 'Money_Request_Step_Scan', STEP_TAG: 'Money_Request_Step_Tag', STEP_WAYPOINT: 'Money_Request_Step_Waypoint', + STEP_TAX_AMOUNT: 'Money_Request_Step_Tax_Amount', + STEP_TAX_RATE: 'Money_Request_Step_Tax_Rate', ROOT: 'Money_Request', AMOUNT: 'Money_Request_Amount', - TAX_AMOUNT: 'Money_Request_Tax_Amount', - TAX_RATE: 'Money_Request_Tax_Rate', PARTICIPANTS: 'Money_Request_Participants', CONFIRMATION: 'Money_Request_Confirmation', CURRENCY: 'Money_Request_Currency', diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx b/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx index 761ce2298af2..5771b77dab3b 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx @@ -76,6 +76,8 @@ const MoneyRequestModalStackNavigator = createModalStackNavigator require('../../../pages/iou/request/IOURequestStartPage').default as React.ComponentType, [SCREENS.MONEY_REQUEST.STEP_CONFIRMATION]: () => require('../../../pages/iou/request/step/IOURequestStepConfirmation').default as React.ComponentType, [SCREENS.MONEY_REQUEST.STEP_AMOUNT]: () => require('../../../pages/iou/request/step/IOURequestStepAmount').default as React.ComponentType, + [SCREENS.MONEY_REQUEST.STEP_TAX_AMOUNT]: () => require('../../../pages/iou/request/step/IOURequestStepTaxAmountPage').default as React.ComponentType, + [SCREENS.MONEY_REQUEST.STEP_TAX_RATE]: () => require('../../../pages/iou/request/step/IOURequestStepTaxRatePage').default as React.ComponentType, [SCREENS.MONEY_REQUEST.STEP_CATEGORY]: () => require('../../../pages/iou/request/step/IOURequestStepCategory').default as React.ComponentType, [SCREENS.MONEY_REQUEST.STEP_CURRENCY]: () => require('../../../pages/iou/request/step/IOURequestStepCurrency').default as React.ComponentType, [SCREENS.MONEY_REQUEST.STEP_DATE]: () => require('../../../pages/iou/request/step/IOURequestStepDate').default as React.ComponentType, @@ -88,8 +90,6 @@ const MoneyRequestModalStackNavigator = createModalStackNavigator require('../../../pages/iou/request/step/IOURequestStepWaypoint').default as React.ComponentType, [SCREENS.MONEY_REQUEST.ROOT]: () => require('../../../pages/iou/MoneyRequestSelectorPage').default as React.ComponentType, [SCREENS.MONEY_REQUEST.AMOUNT]: () => require('../../../pages/iou/steps/NewRequestAmountPage').default as React.ComponentType, - [SCREENS.MONEY_REQUEST.TAX_AMOUNT]: () => require('../../../pages/iou/steps/IOURequestStepTaxAmountPage').default as React.ComponentType, - [SCREENS.MONEY_REQUEST.TAX_RATE]: () => require('../../../pages/iou/steps/IOURequestStepTaxRatePage').default as React.ComponentType, [SCREENS.MONEY_REQUEST.PARTICIPANTS]: () => require('../../../pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsPage').default as React.ComponentType, [SCREENS.MONEY_REQUEST.CONFIRMATION]: () => require('../../../pages/iou/steps/MoneyRequestConfirmPage').default as React.ComponentType, [SCREENS.MONEY_REQUEST.CURRENCY]: () => require('../../../pages/iou/IOUCurrencySelection').default as React.ComponentType, diff --git a/src/libs/Navigation/linkingConfig.ts b/src/libs/Navigation/linkingConfig.ts index 802a871016ee..2aa8c40f9780 100644 --- a/src/libs/Navigation/linkingConfig.ts +++ b/src/libs/Navigation/linkingConfig.ts @@ -422,8 +422,8 @@ const linkingConfig: LinkingOptions = { }, }, [SCREENS.MONEY_REQUEST.AMOUNT]: ROUTES.MONEY_REQUEST_AMOUNT.route, - [SCREENS.MONEY_REQUEST.TAX_AMOUNT]: ROUTES.MONEY_REQUEST_TAX_AMOUNT.route, - [SCREENS.MONEY_REQUEST.TAX_RATE]: ROUTES.MONEY_REQUEST_TAX_RATE.route, + [SCREENS.MONEY_REQUEST.STEP_TAX_AMOUNT]: ROUTES.MONEY_REQUEST_STEP_TAX_AMOUNT.route, + [SCREENS.MONEY_REQUEST.STEP_TAX_RATE]: ROUTES.MONEY_REQUEST_STEP_TAX_RATE.route, [SCREENS.MONEY_REQUEST.PARTICIPANTS]: ROUTES.MONEY_REQUEST_PARTICIPANTS.route, [SCREENS.MONEY_REQUEST.CONFIRMATION]: ROUTES.MONEY_REQUEST_CONFIRMATION.route, [SCREENS.MONEY_REQUEST.DATE]: ROUTES.MONEY_REQUEST_DATE.route, diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index 5fdf992768ce..c86bab623419 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -214,13 +214,17 @@ type MoneyRequestNavigatorParamList = { iouType: string; reportID: string; }; - [SCREENS.MONEY_REQUEST.TAX_AMOUNT]: { + [SCREENS.MONEY_REQUEST.STEP_TAX_AMOUNT]: { iouType: string; + transactionID: string; reportID: string; + backTo: string; }; - [SCREENS.MONEY_REQUEST.TAX_RATE]: { + [SCREENS.MONEY_REQUEST.STEP_TAX_RATE]: { iouType: string; + transactionID: string; reportID: string; + backTo: string; }; [SCREENS.MONEY_REQUEST.MERCHANT]: { iouType: string; From 0f4fb66c3b62349117f541c231be90a27906b465 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Thu, 14 Dec 2023 17:30:47 +0100 Subject: [PATCH 30/78] update tax picker --- src/components/TaxPicker/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/TaxPicker/index.js b/src/components/TaxPicker/index.js index 743010bddf4d..c027ab7c9331 100644 --- a/src/components/TaxPicker/index.js +++ b/src/components/TaxPicker/index.js @@ -3,9 +3,9 @@ import React, {useMemo, useState} from 'react'; import _ from 'underscore'; import OptionsSelector from '@components/OptionsSelector'; import useLocalize from '@hooks/useLocalize'; +import useStyleUtils from '@hooks/useStyleUtils'; +import useThemeStyles from '@hooks/useThemeStyles'; import * as OptionsListUtils from '@libs/OptionsListUtils'; -import useStyleUtils from '@styles/useStyleUtils'; -import useThemeStyles from '@styles/useThemeStyles'; import CONST from '@src/CONST'; import {defaultProps, propTypes} from './taxPickerPropTypes'; From 4341e90f43f2115f681f5db23721e4b33d4fd0e8 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Thu, 14 Dec 2023 17:31:34 +0100 Subject: [PATCH 31/78] refactor invalid tax amount error --- src/pages/iou/steps/MoneyRequestAmountForm.js | 23 ++++++++----------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/src/pages/iou/steps/MoneyRequestAmountForm.js b/src/pages/iou/steps/MoneyRequestAmountForm.js index 8851177182db..d14d903963ae 100644 --- a/src/pages/iou/steps/MoneyRequestAmountForm.js +++ b/src/pages/iou/steps/MoneyRequestAmountForm.js @@ -17,9 +17,8 @@ import * as CurrencyUtils from '@libs/CurrencyUtils'; import * as DeviceCapabilities from '@libs/DeviceCapabilities'; import getOperatingSystem from '@libs/getOperatingSystem'; import * as MoneyRequestUtils from '@libs/MoneyRequestUtils'; -import {iouDefaultProps, iouPropTypes} from '@pages/iou/propTypes'; +import Navigation from '@libs/Navigation/Navigation'; import CONST from '@src/CONST'; -import ONYXKEYS from '@src/ONYXKEYS'; const propTypes = { /** IOU amount saved in Onyx */ @@ -43,13 +42,12 @@ const propTypes = { /** The current tab we have navigated to in the request modal. String that corresponds to the request type. */ selectedTab: PropTypes.oneOf([CONST.TAB_REQUEST.DISTANCE, CONST.TAB_REQUEST.MANUAL, CONST.TAB_REQUEST.SCAN]), - /** Holds data related to Money Request view state, rather than the underlying Money Request data. */ - iou: iouPropTypes, + transactionAmount: PropTypes.string, }; const defaultProps = { - iou: iouDefaultProps, amount: 0, + transactionAmount: 0, currency: CONST.CURRENCY.USD, forwardedRef: null, isEditing: false, @@ -70,18 +68,19 @@ const getNewSelection = (oldSelection, prevLength, newLength) => { }; const isAmountInvalid = (amount) => !amount.length || parseFloat(amount) < 0.01; -const isTaxAmountInvalid = (currentAmount, amount, isEditing) => isEditing && currentAmount > CurrencyUtils.convertToFrontendAmount(amount); +const isTaxAmountInvalid = (currentAmount, amount, isTaxAmountForm) => isTaxAmountForm && currentAmount > CurrencyUtils.convertToFrontendAmount(amount); const AMOUNT_VIEW_ID = 'amountView'; const NUM_PAD_CONTAINER_VIEW_ID = 'numPadContainerView'; const NUM_PAD_VIEW_ID = 'numPadView'; -function MoneyRequestAmountForm({iou, amount, currency, isEditing, forwardedRef, onCurrencyButtonPress, onSubmitButtonPress, selectedTab}) { +function MoneyRequestAmountForm({amount, transactionAmount, currency, isEditing, forwardedRef, onCurrencyButtonPress, onSubmitButtonPress, selectedTab}) { const styles = useThemeStyles(); const {isExtraSmallScreenHeight} = useWindowDimensions(); const {translate, toLocaleDigit, numberFormat} = useLocalize(); const textInput = useRef(null); + const isTaxAmountForm = Navigation.getActiveRoute().includes('taxAmount'); const decimals = CurrencyUtils.getCurrencyDecimals(currency); const selectedAmountAsString = amount ? CurrencyUtils.convertToFrontendAmount(amount).toString() : ''; @@ -97,7 +96,7 @@ function MoneyRequestAmountForm({iou, amount, currency, isEditing, forwardedRef, const forwardDeletePressedRef = useRef(false); - const formattedTaxAmount = CurrencyUtils.convertToDisplayString(iou.amount, iou.currency); + const formattedTaxAmount = CurrencyUtils.convertToDisplayString(transactionAmount, currency); /** * Event occurs when a user presses a mouse button over an DOM element. @@ -233,7 +232,7 @@ function MoneyRequestAmountForm({iou, amount, currency, isEditing, forwardedRef, return; } - if (isTaxAmountInvalid(currentAmount, iou.amount, isEditing)) { + if (isTaxAmountInvalid(currentAmount, transactionAmount, isTaxAmountForm)) { setFormError(translate('iou.error.invalidTaxAmount', {amount: formattedTaxAmount})); return; } @@ -244,7 +243,7 @@ function MoneyRequestAmountForm({iou, amount, currency, isEditing, forwardedRef, initializeAmount(backendAmount); onSubmitButtonPress(currentAmount); - }, [onSubmitButtonPress, currentAmount, iou.amount, isEditing, formattedTaxAmount, translate, initializeAmount]); + }, [onSubmitButtonPress, currentAmount, isTaxAmountForm, formattedTaxAmount, translate, initializeAmount]); /** * Input handler to check for a forward-delete key (or keyboard shortcut) press. @@ -350,6 +349,4 @@ const MoneyRequestAmountFormWithRef = React.forwardRef((props, ref) => ( MoneyRequestAmountFormWithRef.displayName = 'MoneyRequestAmountFormWithRef'; -export default withOnyx({ - iou: {key: ONYXKEYS.IOU}, -})(MoneyRequestAmountFormWithRef); +export default MoneyRequestAmountFormWithRef; From c6d902c762553b011d45cacef756030a268ba686 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Thu, 14 Dec 2023 17:32:43 +0100 Subject: [PATCH 32/78] sync Temp refactor request confirmation list with tax rate and amount page --- .../MoneyRequestConfirmationList.js | 18 +++--- ...oraryForRefactorRequestConfirmationList.js | 58 ++++++++++++++++++- 2 files changed, 68 insertions(+), 8 deletions(-) diff --git a/src/components/MoneyRequestConfirmationList.js b/src/components/MoneyRequestConfirmationList.js index 15410c4597e9..fd96c2781fd3 100755 --- a/src/components/MoneyRequestConfirmationList.js +++ b/src/components/MoneyRequestConfirmationList.js @@ -760,11 +760,13 @@ function MoneyRequestConfirmationList(props) { description={translate('iou.taxRate')} style={[styles.moneyRequestMenuItem]} titleStyle={styles.flex1} - onPress={() => Navigation.navigate(ROUTES.MONEY_REQUEST_TAX_RATE.getRoute(props.iouType, props.reportID))} + onPress={() => + Navigation.navigate( + ROUTES.MONEY_REQUEST_STEP_TAX_RATE.getRoute(props.iouType, props.transaction.transactionID, props.reportID, Navigation.getActiveRouteWithoutParams()), + ) + } disabled={didConfirm} interactive={!props.isReadOnly} - brickRoadIndicator="" - error="" /> )} @@ -775,11 +777,13 @@ function MoneyRequestConfirmationList(props) { description={translate('iou.taxAmount')} style={[styles.moneyRequestMenuItem]} titleStyle={styles.flex1} - onPress={() => Navigation.navigate(ROUTES.MONEY_REQUEST_TAX_AMOUNT.getRoute(props.iouType, props.reportID))} + onPress={() => + Navigation.navigate( + ROUTES.MONEY_REQUEST_STEP_TAX_AMOUNT.getRoute(props.iouType, props.transaction.transactionID, props.reportID, Navigation.getActiveRouteWithoutParams()), + ) + } disabled={didConfirm} interactive={!props.isReadOnly} - brickRoadIndicator="" - error="" /> )} @@ -823,7 +827,7 @@ export default compose( key: ({transactionID}) => `${ONYXKEYS.COLLECTION.SPLIT_TRANSACTION_DRAFT}${transactionID}`, }, transactionsDraft: { - key: ({transactionID}) => `${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}`, + key: ({transaction}) => `${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transaction.transactionID}`, }, policy: { key: ({policyID}) => `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, diff --git a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js index 5c7ea712e772..e082edc30589 100755 --- a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js +++ b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js @@ -161,6 +161,11 @@ const propTypes = { /** Transaction that represents the money request */ transaction: transactionPropTypes, + + transactionsDraft: PropTypes.shape({ + taxRate: PropTypes.string, + taxAmount: PropTypes.number, + }), }; const defaultProps = { @@ -194,6 +199,13 @@ const defaultProps = { isScanRequest: false, shouldShowSmartScanFields: true, isPolicyExpenseChat: false, + transactionsDraft: { + taxRate: null, + taxAmount: null, + }, + policyTaxRates: { + defaultExternalID: '', + }, }; function MoneyTemporaryForRefactorRequestConfirmationList({ @@ -235,7 +247,10 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ session: {accountID}, shouldShowSmartScanFields, transaction, + transactionsDraft, + policyTaxRates, }) { + console.log('transactions ', transaction); const theme = useTheme(); const styles = useThemeStyles(); const {translate, toLocaleDigit} = useLocalize(); @@ -271,6 +286,9 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ // A flag for showing the tags field const shouldShowTags = isPolicyExpenseChat && OptionsListUtils.hasEnabledOptions(_.values(policyTagList)); + // A flag for showing tax rate + const shouldShowTax = isPolicyExpenseChat && policy.isTaxTrackingEnabled; + // A flag for showing the billable field const shouldShowBillable = !lodashGet(policy, 'disabledFields.defaultBillable', true); @@ -282,7 +300,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ shouldCalculateDistanceAmount ? DistanceRequestUtils.getDistanceRequestAmount(distance, unit, rate) : iouAmount, isDistanceRequest ? currency : iouCurrencyCode, ); - + const formattedTaxAmount = CurrencyUtils.convertToDisplayString(transactionsDraft.taxAmount, iouCurrencyCode); const isFocused = useIsFocused(); const [formError, setFormError] = useState(''); @@ -366,6 +384,9 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ }, [isReadOnly, canModifyParticipants, hasMultipleParticipants]); const shouldDisablePaidBySection = userCanModifyParticipants.current; + const defaulTaxKey = policyTaxRates.defaultExternalID; + const defaultTaxName = policyTaxRates.taxes[defaulTaxKey].name; + const optionSelectorSections = useMemo(() => { const sections = []; const unselectedParticipants = _.filter(pickedParticipants, (participant) => !participant.selected); @@ -738,6 +759,35 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ interactive={!isReadOnly} /> )} + {shouldShowTax && ( + + Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_TAX_RATE.getRoute(iouType, transaction.transactionID, reportID, Navigation.getActiveRouteWithoutParams())) + } + disabled={didConfirm} + interactive={!isReadOnly} + /> + )} + + {shouldShowTax && ( + + Navigation.navigate(ROUTES.MONEY_REQUEST_STEP_TAX_AMOUNT.getRoute(iouType, transaction.transactionID, reportID, Navigation.getActiveRouteWithoutParams())) + } + disabled={didConfirm} + interactive={!isReadOnly} + /> + )} {shouldShowBillable && ( {translate('common.billable')} @@ -774,8 +824,14 @@ export default compose( key: ({policyID}) => `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, selector: DistanceRequestUtils.getDefaultMileageRate, }, + transactionsDraft: { + key: ({transaction}) => `${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transaction.transactionID}`, + }, policy: { key: ({policyID}) => `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, }, + policyTaxRates: { + key: ({policyID}) => `${ONYXKEYS.COLLECTION.POLICY_TAX_RATE}${policyID}`, + }, }), )(MoneyTemporaryForRefactorRequestConfirmationList); From ac4092eebb50abf2880975bdcec2c9fe8af64796 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Thu, 14 Dec 2023 17:42:45 +0100 Subject: [PATCH 33/78] fix lint --- ...oraryForRefactorRequestConfirmationList.js | 1 - .../step/IOURequestStepTaxAmountPage.js | 26 +++---------------- .../request/step/IOURequestStepTaxRatePage.js | 6 ----- src/pages/iou/steps/MoneyRequestAmountForm.js | 1 - 4 files changed, 4 insertions(+), 30 deletions(-) diff --git a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js index e082edc30589..c711f95cb259 100755 --- a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js +++ b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js @@ -250,7 +250,6 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ transactionsDraft, policyTaxRates, }) { - console.log('transactions ', transaction); const theme = useTheme(); const styles = useThemeStyles(); const {translate, toLocaleDigit} = useLocalize(); diff --git a/src/pages/iou/request/step/IOURequestStepTaxAmountPage.js b/src/pages/iou/request/step/IOURequestStepTaxAmountPage.js index 61588155273a..047aaf9b9f90 100644 --- a/src/pages/iou/request/step/IOURequestStepTaxAmountPage.js +++ b/src/pages/iou/request/step/IOURequestStepTaxAmountPage.js @@ -1,5 +1,4 @@ import {useFocusEffect} from '@react-navigation/native'; -import lodashGet from 'lodash/get'; import PropTypes from 'prop-types'; import React, {useCallback, useRef} from 'react'; import {View} from 'react-native'; @@ -14,34 +13,19 @@ import compose from '@libs/compose'; import * as CurrencyUtils from '@libs/CurrencyUtils'; import * as IOUUtils from '@libs/IOUUtils'; import Navigation from '@libs/Navigation/Navigation'; -import {iouDefaultProps, iouPropTypes} from '@pages/iou/propTypes'; import MoneyRequestAmountForm from '@pages/iou/steps/MoneyRequestAmountForm'; import reportPropTypes from '@pages/reportPropTypes'; import * as IOU from '@userActions/IOU'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; +import IOURequestStepRoutePropTypes from './IOURequestStepRoutePropTypes'; import withFullTransactionOrNotFound from './withFullTransactionOrNotFound'; import withWritableReportOrNotFound from './withWritableReportOrNotFound'; const propTypes = { - /** React Navigation route */ - route: PropTypes.shape({ - /** Params from the route */ - params: PropTypes.shape({ - /** The type of IOU report, i.e. bill, request, send */ - iouType: PropTypes.string, - - /** The report ID of the IOU */ - reportID: PropTypes.string, - - /** Selected currency from IOUCurrencySelection */ - currency: PropTypes.string, - }), - }).isRequired, - - /** Holds data related to Money Request view state, rather than the underlying Money Request data. */ - iou: iouPropTypes, + /** Navigation route context info provided by react navigation */ + route: IOURequestStepRoutePropTypes.isRequired, transactionsDraft: PropTypes.shape({ taxAmount: PropTypes.number, @@ -56,7 +40,6 @@ const propTypes = { }; const defaultProps = { - iou: iouDefaultProps, transactionsDraft: { taxAmount: null, }, @@ -68,7 +51,6 @@ function IOURequestStepTaxAmountPage({ route: { params: {iouType, reportID, transactionID, backTo, currency: selectedCurrency}, }, - iou, transactionsDraft, transaction, transaction: {currency: originalCurrency}, @@ -110,7 +92,7 @@ function IOURequestStepTaxAmountPage({ IOU.setMoneyRequestTaxAmount(transactionID, amountInSmallestCurrencyUnits); IOU.setMoneyRequestAmount_temporaryForRefactor(transactionID, amountInSmallestCurrencyUnits, currency || CONST.CURRENCY.USD); - + if (backTo) { Navigation.goBack(backTo); return; diff --git a/src/pages/iou/request/step/IOURequestStepTaxRatePage.js b/src/pages/iou/request/step/IOURequestStepTaxRatePage.js index fe7f1f77ba65..7392adbc9168 100644 --- a/src/pages/iou/request/step/IOURequestStepTaxRatePage.js +++ b/src/pages/iou/request/step/IOURequestStepTaxRatePage.js @@ -11,7 +11,6 @@ import useLocalize from '@hooks/useLocalize'; import compose from '@libs/compose'; import * as CurrencyUtils from '@libs/CurrencyUtils'; import Navigation from '@libs/Navigation/Navigation'; -import reportPropTypes from '@pages/reportPropTypes'; import * as IOU from '@userActions/IOU'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; @@ -36,16 +35,11 @@ const propTypes = { taxRate: PropTypes.string, }), - /* Onyx Props */ - /** The report that the transaction belongs to */ - report: reportPropTypes, - /** The transaction object being modified in Onyx */ transaction: transactionPropTypes, }; const defaultProps = { - report: {}, policyTaxRates: {}, transactionsDraft: { taxRate: null, diff --git a/src/pages/iou/steps/MoneyRequestAmountForm.js b/src/pages/iou/steps/MoneyRequestAmountForm.js index d14d903963ae..0917a05b6ee8 100644 --- a/src/pages/iou/steps/MoneyRequestAmountForm.js +++ b/src/pages/iou/steps/MoneyRequestAmountForm.js @@ -2,7 +2,6 @@ import lodashGet from 'lodash/get'; import PropTypes from 'prop-types'; import React, {useCallback, useEffect, useRef, useState} from 'react'; import {ScrollView, View} from 'react-native'; -import {withOnyx} from 'react-native-onyx'; import _ from 'underscore'; import BigNumberPad from '@components/BigNumberPad'; import Button from '@components/Button'; From f0f8ca66186a6e985d6c6f3177e534b62f6bafd7 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Fri, 15 Dec 2023 10:11:54 +0100 Subject: [PATCH 34/78] update tax proptypes --- src/components/taxPropTypes.js | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/src/components/taxPropTypes.js b/src/components/taxPropTypes.js index 91f220b92395..425a637cd168 100644 --- a/src/components/taxPropTypes.js +++ b/src/components/taxPropTypes.js @@ -6,23 +6,24 @@ const taxPropTypes = PropTypes.shape({ /** value of a tax */ value: PropTypes.string.isRequired, + + /** if tax is disabled */ + isDisabled: PropTypes.bool, }); -export default PropTypes.objectOf( - PropTypes.shape({ - /** Defualt name of taxes */ - name: PropTypes.string.isRequired, +export default PropTypes.shape({ + /** Defualt name of taxes */ + name: PropTypes.string.isRequired, - /** Defualt external ID of taxes */ - defaultExternalID: PropTypes.string.isRequired, + /** Defualt external ID of taxes */ + defaultExternalID: PropTypes.string, - /** Default value of taxes */ - defaultValue: PropTypes.string.isRequired, + /** Default value of taxes */ + defaultValue: PropTypes.string, - /** Default Foreign Tax ID */ - foreignTaxDefault: PropTypes.string.isRequired, + /** Default Foreign Tax ID */ + foreignTaxDefault: PropTypes.string, - /** List of Taxes names and values */ - taxes: PropTypes.objectOf(taxPropTypes), - }), -); + /** List of Taxes names and values */ + taxes: PropTypes.objectOf(taxPropTypes), +}); From 3ca564afee18aecf62ae60ccd470143679d3f2cc Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Fri, 15 Dec 2023 10:12:20 +0100 Subject: [PATCH 35/78] add proptypes for transactions draft --- src/components/transactionsDraftPropTypes.js | 37 ++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 src/components/transactionsDraftPropTypes.js diff --git a/src/components/transactionsDraftPropTypes.js b/src/components/transactionsDraftPropTypes.js new file mode 100644 index 000000000000..ca14c4537aa7 --- /dev/null +++ b/src/components/transactionsDraftPropTypes.js @@ -0,0 +1,37 @@ +import PropTypes from 'prop-types'; + +const dataPropTypes = PropTypes.shape({ + name: PropTypes.string.isRequired, + value: PropTypes.string.isRequired, + code: PropTypes.string, +}); + +const taxRatePropTypes = PropTypes.shape({ + text: PropTypes.string.isRequired, + keyForList: PropTypes.string.isRequired, + searchText: PropTypes.string.isRequired, + tooltipText: PropTypes.string.isRequired, + isDisabled: PropTypes.bool, + data: dataPropTypes, +}); + +const transactionsDraftPropTypes = PropTypes.shape({ + taxRate: taxRatePropTypes, + taxAmount: PropTypes.number, +}); + +const taxRateDefaultProps = { + text: '', + keyForList: '', + searchText: '', + tooltipText: '', + isDisabled: false, + data: {}, +}; + +const transactionsDraftDefaultProps = { + taxRate: taxRateDefaultProps, + taxAmount: 0, +}; + +export {transactionsDraftPropTypes, transactionsDraftDefaultProps}; From 9383f8ad0f7cfdc50670de900d420f02e667fb3d Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Fri, 15 Dec 2023 10:13:22 +0100 Subject: [PATCH 36/78] transform taxes to a new object with code --- src/libs/OptionsListUtils.js | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/libs/OptionsListUtils.js b/src/libs/OptionsListUtils.js index 3067cd25e79e..4bf2199c941e 100644 --- a/src/libs/OptionsListUtils.js +++ b/src/libs/OptionsListUtils.js @@ -1071,13 +1071,22 @@ function getTaxRatesOptions(taxRates) { searchText: taxRate.name, tooltipText: taxRate.name, isDisabled: taxRate.isDisabled, + data: taxRate, })); } function getTaxRatesSection(policyTaxRates, selectedOptions, searchInputValue) { const policyRatesSections = []; - const sortedTaxRates = sortTaxRates(policyTaxRates.taxes); + // transform tax rates so we could have key as 'code' in tax rate + const taxes = Object.fromEntries( + Object.entries(policyTaxRates.taxes).map(([code, data]) => [ + code, + { ...data, code } + ]) + ); + + const sortedTaxRates = sortTaxRates(taxes); const enabledTaxRates = _.filter(sortedTaxRates, (taxRate) => !taxRate.isDisabled); const numberOfTaxRates = _.size(enabledTaxRates); @@ -1132,7 +1141,8 @@ function getTaxRatesSection(policyTaxRates, selectedOptions, searchInputValue) { if (!_.isEmpty(selectedOptions)) { const selectedTaxRatesOptions = _.map(selectedOptions, (option) => { - const taxRateObject = _.find(policyTaxRates.taxes, (taxRate) => taxRate.name === option.name); + const taxRateObject = _.find(taxes, (taxRate) => taxRate.name === option.name); + return { name: option.name, enabled: Boolean(taxRateObject && !taxRateObject.isDisabled), From 6a0d3a804faa76c6075aec4daac124efc1142e88 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Fri, 15 Dec 2023 10:14:23 +0100 Subject: [PATCH 37/78] use proptypes --- ...eyTemporaryForRefactorRequestConfirmationList.js | 13 ++++--------- .../iou/request/step/IOURequestStepTaxRatePage.js | 13 +++++-------- src/pages/iou/steps/MoneyRequestAmountForm.js | 4 ++-- 3 files changed, 11 insertions(+), 19 deletions(-) diff --git a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js index c711f95cb259..ff9414430945 100755 --- a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js +++ b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js @@ -40,6 +40,7 @@ import Switch from './Switch'; import tagPropTypes from './tagPropTypes'; import Text from './Text'; import transactionPropTypes from './transactionPropTypes'; +import {transactionsDraftDefaultProps, transactionsDraftPropTypes} from './transactionsDraftPropTypes'; import withCurrentUserPersonalDetails, {withCurrentUserPersonalDetailsDefaultProps, withCurrentUserPersonalDetailsPropTypes} from './withCurrentUserPersonalDetails'; const propTypes = { @@ -162,10 +163,7 @@ const propTypes = { /** Transaction that represents the money request */ transaction: transactionPropTypes, - transactionsDraft: PropTypes.shape({ - taxRate: PropTypes.string, - taxAmount: PropTypes.number, - }), + transactionsDraft: transactionsDraftPropTypes, }; const defaultProps = { @@ -199,10 +197,7 @@ const defaultProps = { isScanRequest: false, shouldShowSmartScanFields: true, isPolicyExpenseChat: false, - transactionsDraft: { - taxRate: null, - taxAmount: null, - }, + transactionsDraft: transactionsDraftDefaultProps, policyTaxRates: { defaultExternalID: '', }, @@ -761,7 +756,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ {shouldShowTax && ( { const taxAmount = calculateAmount(policyTaxRates.taxes, taxes.text, transaction.amount); const amountInSmallestCurrencyUnits = CurrencyUtils.convertToBackendAmount(Number.parseFloat(taxAmount)); - IOU.setMoneyRequestTaxRate(transaction.transactionID, taxes.text); + IOU.setMoneyRequestTaxRate(transaction.transactionID, taxes); IOU.setMoneyRequestTaxAmount(transaction.transactionID, amountInSmallestCurrencyUnits); Navigation.goBack(ROUTES.MONEY_REQUEST_CONFIRMATION.getRoute(iouType, reportID)); @@ -90,7 +87,7 @@ function IOURequestStepTaxRatePage({ onBackButtonPress={() => navigateBack()} /> Date: Fri, 15 Dec 2023 10:21:42 +0100 Subject: [PATCH 38/78] fix lint --- src/libs/OptionsListUtils.js | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/libs/OptionsListUtils.js b/src/libs/OptionsListUtils.js index 4bf2199c941e..4932aefb532e 100644 --- a/src/libs/OptionsListUtils.js +++ b/src/libs/OptionsListUtils.js @@ -1079,12 +1079,7 @@ function getTaxRatesSection(policyTaxRates, selectedOptions, searchInputValue) { const policyRatesSections = []; // transform tax rates so we could have key as 'code' in tax rate - const taxes = Object.fromEntries( - Object.entries(policyTaxRates.taxes).map(([code, data]) => [ - code, - { ...data, code } - ]) - ); + const taxes = Object.fromEntries(Object.entries(policyTaxRates.taxes).map(([code, data]) => [code, {...data, code}])); const sortedTaxRates = sortTaxRates(taxes); const enabledTaxRates = _.filter(sortedTaxRates, (taxRate) => !taxRate.isDisabled); From 9112347cc01294733623e1ca3e16a0920321d845 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Sat, 16 Dec 2023 23:49:30 +0100 Subject: [PATCH 39/78] update translations --- src/languages/en.ts | 1 + src/languages/es.ts | 7 ++++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/languages/en.ts b/src/languages/en.ts index 170bf0bb0151..de420886896a 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -279,6 +279,7 @@ export default { required: 'Required', showing: 'Showing', of: 'of', + default: 'Default', }, location: { useCurrent: 'Use current location', diff --git a/src/languages/es.ts b/src/languages/es.ts index af56e1057bbb..72718e7b44e5 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -269,6 +269,7 @@ export default { required: 'Obligatorio', showing: 'Mostrando', of: 'de', + default: 'Predeterminado', }, location: { useCurrent: 'Usar ubicación actual', @@ -528,8 +529,8 @@ export default { }, iou: { amount: 'Importe', - taxAmount: 'Tax amount', - taxRate: 'Tax rate', + taxAmount: 'Importe del impuesto', + taxRate: 'Tasa de impuesto', approve: 'Aprobar', approved: 'Aprobado', cash: 'Efectivo', @@ -604,7 +605,7 @@ export default { error: { invalidCategoryLength: 'El largo de la categoría escogida excede el máximo permitido (255). Por favor escoge otra categoría o acorta la categoría primero.', invalidAmount: 'Por favor ingresa un monto válido antes de continuar.', - invalidTaxAmount: ({amount}: RequestAmountParams) => `El monto máximo del impuesto es ${amount}`, + invalidTaxAmount: ({amount}: RequestAmountParams) => `El importe máximo del impuesto es ${amount}`, invalidSplit: 'La suma de las partes no equivale al monto total', other: 'Error inesperado, por favor inténtalo más tarde', genericCreateFailureMessage: 'Error inesperado solicitando dinero, Por favor, inténtalo más tarde', From 88c001e26056352ef66ef0996cf5f55882e58e2d Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Sat, 16 Dec 2023 23:52:28 +0100 Subject: [PATCH 40/78] transform tax rates --- src/libs/OptionsListUtils.js | 13 ++++++++++--- .../iou/request/step/IOURequestStepTaxRatePage.js | 5 +++-- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/libs/OptionsListUtils.js b/src/libs/OptionsListUtils.js index 4932aefb532e..107b1eef1a18 100644 --- a/src/libs/OptionsListUtils.js +++ b/src/libs/OptionsListUtils.js @@ -1055,6 +1055,13 @@ function getTagListSections(rawTags, recentlyUsedTags, selectedOptions, searchIn return tagSections; } +function transformedTaxRates(policyTaxRates) { + const defaulTaxKey = policyTaxRates.defaultExternalID; + const getName = (data, code) => `${data.name} (${data.value})${defaulTaxKey === code ? ` • ${Localize.translateLocal('common.default')}` : ''}`; + const taxes = Object.fromEntries(_.map(Object.entries(policyTaxRates.taxes), ([code, data]) => [code, {...data, code, name: getName(data, code), actualName: data.name}])); + return taxes; +} + function sortTaxRates(taxRates) { const sortedtaxRates = _.chain(taxRates) .values() @@ -1067,7 +1074,7 @@ function sortTaxRates(taxRates) { function getTaxRatesOptions(taxRates) { return _.map(taxRates, (taxRate) => ({ text: taxRate.name, - keyForList: taxRate.name, + keyForList: taxRate.code, searchText: taxRate.name, tooltipText: taxRate.name, isDisabled: taxRate.isDisabled, @@ -1078,8 +1085,7 @@ function getTaxRatesOptions(taxRates) { function getTaxRatesSection(policyTaxRates, selectedOptions, searchInputValue) { const policyRatesSections = []; - // transform tax rates so we could have key as 'code' in tax rate - const taxes = Object.fromEntries(Object.entries(policyTaxRates.taxes).map(([code, data]) => [code, {...data, code}])); + const taxes = transformedTaxRates(policyTaxRates); const sortedTaxRates = sortTaxRates(taxes); const enabledTaxRates = _.filter(sortedTaxRates, (taxRate) => !taxRate.isDisabled); @@ -1911,4 +1917,5 @@ export { getIndentedOptionTree, formatMemberForList, formatSectionsFromSearchTerm, + transformedTaxRates, }; diff --git a/src/pages/iou/request/step/IOURequestStepTaxRatePage.js b/src/pages/iou/request/step/IOURequestStepTaxRatePage.js index fe6407870f5b..0493a816e802 100644 --- a/src/pages/iou/request/step/IOURequestStepTaxRatePage.js +++ b/src/pages/iou/request/step/IOURequestStepTaxRatePage.js @@ -12,6 +12,7 @@ import useLocalize from '@hooks/useLocalize'; import compose from '@libs/compose'; import * as CurrencyUtils from '@libs/CurrencyUtils'; import Navigation from '@libs/Navigation/Navigation'; +import * as OptionsListUtils from '@libs/OptionsListUtils'; import * as IOU from '@userActions/IOU'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; @@ -46,7 +47,7 @@ const defaultProps = { // this is the formulae to calculate tax const calculateAmount = (taxRates, selectedTaxRate, amount) => { - const percentage = _.find(taxRates, (taxRate) => taxRate.name === selectedTaxRate).value; + const percentage = _.find(OptionsListUtils.transformedTaxRates(taxRates), (taxRate) => taxRate.name === selectedTaxRate).value; const divisor = percentage.slice(0, -1) / 100 + 1; // slice to remove % at the end; converts "10%" to "10" return parseInt(Math.round(amount - amount / divisor), 10) / 100; // returns The expense amount of transaction }; @@ -66,7 +67,7 @@ function IOURequestStepTaxRatePage({ } const updateTaxRates = (taxes) => { - const taxAmount = calculateAmount(policyTaxRates.taxes, taxes.text, transaction.amount); + const taxAmount = calculateAmount(policyTaxRates, taxes.text, transaction.amount); const amountInSmallestCurrencyUnits = CurrencyUtils.convertToBackendAmount(Number.parseFloat(taxAmount)); IOU.setMoneyRequestTaxRate(transaction.transactionID, taxes); IOU.setMoneyRequestTaxAmount(transaction.transactionID, amountInSmallestCurrencyUnits); From b4d60557bd4e14e3557170ee397a9d470ffb4918 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Sat, 16 Dec 2023 23:55:16 +0100 Subject: [PATCH 41/78] update invalid tax amount error --- .../iou/request/step/IOURequestStepTaxAmountPage.js | 4 +--- src/pages/iou/steps/MoneyRequestAmountForm.js | 11 ++++------- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/src/pages/iou/request/step/IOURequestStepTaxAmountPage.js b/src/pages/iou/request/step/IOURequestStepTaxAmountPage.js index 047aaf9b9f90..003150b20a61 100644 --- a/src/pages/iou/request/step/IOURequestStepTaxAmountPage.js +++ b/src/pages/iou/request/step/IOURequestStepTaxAmountPage.js @@ -52,7 +52,6 @@ function IOURequestStepTaxAmountPage({ params: {iouType, reportID, transactionID, backTo, currency: selectedCurrency}, }, transactionsDraft, - transaction, transaction: {currency: originalCurrency}, report, }) { @@ -91,7 +90,7 @@ function IOURequestStepTaxAmountPage({ const amountInSmallestCurrencyUnits = CurrencyUtils.convertToBackendAmount(Number.parseFloat(currentAmount)); IOU.setMoneyRequestTaxAmount(transactionID, amountInSmallestCurrencyUnits); - IOU.setMoneyRequestAmount_temporaryForRefactor(transactionID, amountInSmallestCurrencyUnits, currency || CONST.CURRENCY.USD); + IOU.setMoneyRequestCurrency_temporaryForRefactor(transactionID, currency || CONST.CURRENCY.USD); if (backTo) { Navigation.goBack(backTo); @@ -116,7 +115,6 @@ function IOURequestStepTaxAmountPage({ (textInput.current = e)} onCurrencyButtonPress={navigateToCurrencySelectionPage} diff --git a/src/pages/iou/steps/MoneyRequestAmountForm.js b/src/pages/iou/steps/MoneyRequestAmountForm.js index f94e14e977c8..a8b6a34cb6e7 100644 --- a/src/pages/iou/steps/MoneyRequestAmountForm.js +++ b/src/pages/iou/steps/MoneyRequestAmountForm.js @@ -40,13 +40,10 @@ const propTypes = { /** The current tab we have navigated to in the request modal. String that corresponds to the request type. */ selectedTab: PropTypes.oneOf([CONST.TAB_REQUEST.DISTANCE, CONST.TAB_REQUEST.MANUAL, CONST.TAB_REQUEST.SCAN]), - - transactionAmount: PropTypes.number, }; const defaultProps = { amount: 0, - transactionAmount: 0, currency: CONST.CURRENCY.USD, forwardedRef: null, isEditing: false, @@ -73,7 +70,7 @@ const AMOUNT_VIEW_ID = 'amountView'; const NUM_PAD_CONTAINER_VIEW_ID = 'numPadContainerView'; const NUM_PAD_VIEW_ID = 'numPadView'; -function MoneyRequestAmountForm({amount, transactionAmount, currency, isEditing, forwardedRef, onCurrencyButtonPress, onSubmitButtonPress, selectedTab}) { +function MoneyRequestAmountForm({amount, currency, isEditing, forwardedRef, onCurrencyButtonPress, onSubmitButtonPress, selectedTab}) { const styles = useThemeStyles(); const {isExtraSmallScreenHeight} = useWindowDimensions(); const {translate, toLocaleDigit, numberFormat} = useLocalize(); @@ -95,7 +92,7 @@ function MoneyRequestAmountForm({amount, transactionAmount, currency, isEditing, const forwardDeletePressedRef = useRef(false); - const formattedTaxAmount = CurrencyUtils.convertToDisplayString(transactionAmount, currency); + const formattedTaxAmount = CurrencyUtils.convertToDisplayString(amount, currency); /** * Event occurs when a user presses a mouse button over an DOM element. @@ -231,7 +228,7 @@ function MoneyRequestAmountForm({amount, transactionAmount, currency, isEditing, return; } - if (isTaxAmountInvalid(currentAmount, transactionAmount, isTaxAmountForm)) { + if (isTaxAmountInvalid(currentAmount, amount, isTaxAmountForm)) { setFormError(translate('iou.error.invalidTaxAmount', {amount: formattedTaxAmount})); return; } @@ -242,7 +239,7 @@ function MoneyRequestAmountForm({amount, transactionAmount, currency, isEditing, initializeAmount(backendAmount); onSubmitButtonPress(currentAmount); - }, [onSubmitButtonPress, currentAmount, transactionAmount, isTaxAmountForm, formattedTaxAmount, translate, initializeAmount]); + }, [onSubmitButtonPress, currentAmount, amount, isTaxAmountForm, formattedTaxAmount, translate, initializeAmount]); /** * Input handler to check for a forward-delete key (or keyboard shortcut) press. From 86edc81fd1b26b53fccf7076eca88d7560c4d27e Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Sat, 16 Dec 2023 23:55:49 +0100 Subject: [PATCH 42/78] update amount defaults --- src/components/MoneyRequestConfirmationList.js | 3 +-- .../MoneyTemporaryForRefactorRequestConfirmationList.js | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/components/MoneyRequestConfirmationList.js b/src/components/MoneyRequestConfirmationList.js index 309c52dd1a04..687101adcb43 100755 --- a/src/components/MoneyRequestConfirmationList.js +++ b/src/components/MoneyRequestConfirmationList.js @@ -349,8 +349,7 @@ function MoneyRequestConfirmationList(props) { const canModifyParticipants = !props.isReadOnly && props.canModifyParticipants && props.hasMultipleParticipants; const shouldDisablePaidBySection = canModifyParticipants; - const defaulTaxKey = props.policyTaxRates.defaultExternalID; - const defaultTaxName = props.policyTaxRates.taxes[defaulTaxKey].name; + const defaultTaxName = `${props.policyTaxRates.name} • ${translate('common.default')}`; const optionSelectorSections = useMemo(() => { const sections = []; diff --git a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js index ff9414430945..710a781537b8 100755 --- a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js +++ b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js @@ -378,8 +378,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ }, [isReadOnly, canModifyParticipants, hasMultipleParticipants]); const shouldDisablePaidBySection = userCanModifyParticipants.current; - const defaulTaxKey = policyTaxRates.defaultExternalID; - const defaultTaxName = policyTaxRates.taxes[defaulTaxKey].name; + const defaultTaxName = `${policyTaxRates.name} • ${translate('common.default')}`; const optionSelectorSections = useMemo(() => { const sections = []; From b59dcaef429e73f43dd09b11a6448895cd79c06d Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Sun, 17 Dec 2023 00:58:43 +0100 Subject: [PATCH 43/78] add JSDOC and comments --- .../MoneyRequestConfirmationList.js | 18 +++++++----- ...oraryForRefactorRequestConfirmationList.js | 10 +++++-- src/libs/OptionsListUtils.js | 29 +++++++++++++++++-- src/libs/actions/IOU.js | 8 +++++ .../step/IOURequestStepTaxAmountPage.js | 11 +++---- .../request/step/IOURequestStepTaxRatePage.js | 4 +++ 6 files changed, 60 insertions(+), 20 deletions(-) diff --git a/src/components/MoneyRequestConfirmationList.js b/src/components/MoneyRequestConfirmationList.js index 687101adcb43..8dd764f45698 100755 --- a/src/components/MoneyRequestConfirmationList.js +++ b/src/components/MoneyRequestConfirmationList.js @@ -40,8 +40,10 @@ import SettlementButton from './SettlementButton'; import ShowMoreButton from './ShowMoreButton'; import Switch from './Switch'; import tagPropTypes from './tagPropTypes'; +import taxPropTypes from './taxPropTypes'; import Text from './Text'; import transactionPropTypes from './transactionPropTypes'; +import {transactionsDraftDefaultProps, transactionsDraftPropTypes} from './transactionsDraftPropTypes'; import withCurrentUserPersonalDetails, {withCurrentUserPersonalDetailsDefaultProps, withCurrentUserPersonalDetailsPropTypes} from './withCurrentUserPersonalDetails'; const propTypes = { @@ -164,13 +166,15 @@ const propTypes = { /** Collection of tags attached to a policy */ policyTags: tagPropTypes, + /* Onyx Props */ + /** Collection of tax rates attached to a policy */ + policyTaxRates: taxPropTypes, + /** Holds data related to Money Request view state, rather than the underlying Money Request data. */ iou: iouPropTypes, - transactionsDraft: PropTypes.shape({ - taxRate: PropTypes.string, - taxAmount: PropTypes.number, - }), + /** holds data for selected tax rates and tax amount */ + transactionsDraft: transactionsDraftPropTypes, }; const defaultProps = { @@ -205,10 +209,8 @@ const defaultProps = { shouldShowSmartScanFields: true, isPolicyExpenseChat: false, iou: iouDefaultProps, - transactionsDraft: { - taxRate: null, - taxAmount: null, - }, + transactionsDraft: transactionsDraftDefaultProps, + policyTaxRates: {}, }; function MoneyRequestConfirmationList(props) { diff --git a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js index 710a781537b8..eec5474263ca 100755 --- a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js +++ b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js @@ -38,6 +38,7 @@ import OptionsSelector from './OptionsSelector'; import SettlementButton from './SettlementButton'; import Switch from './Switch'; import tagPropTypes from './tagPropTypes'; +import taxPropTypes from './taxPropTypes'; import Text from './Text'; import transactionPropTypes from './transactionPropTypes'; import {transactionsDraftDefaultProps, transactionsDraftPropTypes} from './transactionsDraftPropTypes'; @@ -160,9 +161,14 @@ const propTypes = { /** Collection of tags attached to a policy */ policyTags: tagPropTypes, + /* Onyx Props */ + /** Collection of tax rates attached to a policy */ + policyTaxRates: taxPropTypes, + /** Transaction that represents the money request */ transaction: transactionPropTypes, + /** holds data for selected tax rates and tax amount */ transactionsDraft: transactionsDraftPropTypes, }; @@ -198,9 +204,7 @@ const defaultProps = { shouldShowSmartScanFields: true, isPolicyExpenseChat: false, transactionsDraft: transactionsDraftDefaultProps, - policyTaxRates: { - defaultExternalID: '', - }, + policyTaxRates: {}, }; function MoneyTemporaryForRefactorRequestConfirmationList({ diff --git a/src/libs/OptionsListUtils.js b/src/libs/OptionsListUtils.js index 107b1eef1a18..84732952023e 100644 --- a/src/libs/OptionsListUtils.js +++ b/src/libs/OptionsListUtils.js @@ -647,7 +647,6 @@ function getEnabledCategoriesCount(options) { * * @param {Object[]} options - an initial strings array * @param {Boolean} options[].isDisabled - a flag to enable/disable option in a list - * @param {String} options[].name - a name of an option * @returns {Number} */ function getEnabledTaxRateCount(options) { @@ -1055,6 +1054,12 @@ function getTagListSections(rawTags, recentlyUsedTags, selectedOptions, searchIn return tagSections; } +/** + * transforms tax rates to a new object - to add codes and new name with concatenated name and value. + * + * @param {Object} policyTaxRates + * @returns {Array} + */ function transformedTaxRates(policyTaxRates) { const defaulTaxKey = policyTaxRates.defaultExternalID; const getName = (data, code) => `${data.name} (${data.value})${defaulTaxKey === code ? ` • ${Localize.translateLocal('common.default')}` : ''}`; @@ -1062,6 +1067,12 @@ function transformedTaxRates(policyTaxRates) { return taxes; } +/** + * Sorts tax rates alphabetically by name. + * + * @param {Object} taxRates + * @returns {Array} + */ function sortTaxRates(taxRates) { const sortedtaxRates = _.chain(taxRates) .values() @@ -1071,6 +1082,12 @@ function sortTaxRates(taxRates) { return sortedtaxRates; } +/** + * Builds the options for taxRates + * + * @param {Object[]} taxRates - an initial object array + * @returns {Array} + */ function getTaxRatesOptions(taxRates) { return _.map(taxRates, (taxRate) => ({ text: taxRate.name, @@ -1082,6 +1099,14 @@ function getTaxRatesOptions(taxRates) { })); } +/** + * Build the section list for tax rates + * + * @param {Object} policyTaxRates + * @param {Object[]} selectedOptions + * @param {String} searchInputValue + * @returns {Array} + */ function getTaxRatesSection(policyTaxRates, selectedOptions, searchInputValue) { const policyRatesSections = []; @@ -1246,7 +1271,7 @@ function getOptions( } if (includePolicyTaxRates) { - const policyTaxRatesOptions = getTaxRatesSection(policyTaxRates, selectedOptions, searchInputValue, maxRecentReportsToShow); + const policyTaxRatesOptions = getTaxRatesSection(policyTaxRates, selectedOptions, searchInputValue); return { recentReports: [], diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.js index 714bad5ed22b..eace0465ac52 100644 --- a/src/libs/actions/IOU.js +++ b/src/libs/actions/IOU.js @@ -3209,10 +3209,18 @@ function resetMoneyRequestTag() { Onyx.merge(ONYXKEYS.IOU, {tag: ''}); } +/** + * @param {String} transactionID + * @param {Object} taxRate + */ function setMoneyRequestTaxRate(transactionID, taxRate) { Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}`, {taxRate}); } +/** + * @param {String} transactionID + * @param {Number} taxAmount + */ function setMoneyRequestTaxAmount(transactionID, taxAmount) { Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}`, {taxAmount}); } diff --git a/src/pages/iou/request/step/IOURequestStepTaxAmountPage.js b/src/pages/iou/request/step/IOURequestStepTaxAmountPage.js index 003150b20a61..2a331a1c7a17 100644 --- a/src/pages/iou/request/step/IOURequestStepTaxAmountPage.js +++ b/src/pages/iou/request/step/IOURequestStepTaxAmountPage.js @@ -1,5 +1,4 @@ import {useFocusEffect} from '@react-navigation/native'; -import PropTypes from 'prop-types'; import React, {useCallback, useRef} from 'react'; import {View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; @@ -7,6 +6,7 @@ import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView import HeaderWithBackButton from '@components/HeaderWithBackButton'; import ScreenWrapper from '@components/ScreenWrapper'; import transactionPropTypes from '@components/transactionPropTypes'; +import {transactionsDraftDefaultProps, transactionsDraftPropTypes} from '@components/transactionsDraftPropTypes'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; import compose from '@libs/compose'; @@ -27,9 +27,8 @@ const propTypes = { /** Navigation route context info provided by react navigation */ route: IOURequestStepRoutePropTypes.isRequired, - transactionsDraft: PropTypes.shape({ - taxAmount: PropTypes.number, - }), + /** holds data for selected tax rates and tax amount */ + transactionsDraft: transactionsDraftPropTypes, /* Onyx Props */ /** The report that the transaction belongs to */ @@ -40,9 +39,7 @@ const propTypes = { }; const defaultProps = { - transactionsDraft: { - taxAmount: null, - }, + transactionsDraft: transactionsDraftDefaultProps, report: {}, transaction: {}, }; diff --git a/src/pages/iou/request/step/IOURequestStepTaxRatePage.js b/src/pages/iou/request/step/IOURequestStepTaxRatePage.js index 0493a816e802..d0104ba272fb 100644 --- a/src/pages/iou/request/step/IOURequestStepTaxRatePage.js +++ b/src/pages/iou/request/step/IOURequestStepTaxRatePage.js @@ -31,8 +31,12 @@ const propTypes = { reportID: PropTypes.string, }), }).isRequired, + + /* Onyx Props */ + /** Collection of tax rates attached to a policy */ policyTaxRates: taxPropTypes, + /** holds data for selected tax rates and tax amount */ transactionsDraft: transactionsDraftPropTypes, /** The transaction object being modified in Onyx */ From 8ed66c7abe64cda724dff864fc27db0d702a5db2 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Sun, 17 Dec 2023 02:24:28 +0100 Subject: [PATCH 44/78] send taxCode and taxAmount to RequestMoney API --- src/components/TaxPicker/index.js | 36 +++++++++---------- src/libs/actions/IOU.js | 6 ++++ .../step/IOURequestStepConfirmation.js | 6 +++- 3 files changed, 29 insertions(+), 19 deletions(-) diff --git a/src/components/TaxPicker/index.js b/src/components/TaxPicker/index.js index c027ab7c9331..500e8109116d 100644 --- a/src/components/TaxPicker/index.js +++ b/src/components/TaxPicker/index.js @@ -36,24 +36,24 @@ function TaxPicker({selectedTaxRate, policyTaxRates, insets, onSubmit}) { const sections = useMemo(() => { const {policyTaxRatesOptions} = OptionsListUtils.getFilteredOptions( - {}, // reports {} - {}, // personalDetails {} - [], // betas [] - searchValue, // searchValue string - selectedOptions, // selectedOptions any[] - [], // excludedLogins any[] - false, // includeOwnedWorkspaceChats boolean - false, // includeP2P boolean - false, // includeCategories boolean - {}, // categories {} - [], // recentlyUsedCategories string[] - false, // includeTags boolean - {}, // tags {} - [], // recentlyUsedTags string[] - false, // canInviteUser boolean - false, // includeSelectedOptions - true, // includePolicyTaxRates boolean - policyTaxRates, // policyTaxRates {} + {}, + {}, + [], + searchValue, + selectedOptions, + [], + false, + false, + false, + {}, + [], + false, + {}, + [], + false, + false, + true, + policyTaxRates, ); return policyTaxRatesOptions; }, [policyTaxRates, searchValue, selectedOptions]); diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.js index eace0465ac52..3a97df5bca05 100644 --- a/src/libs/actions/IOU.js +++ b/src/libs/actions/IOU.js @@ -1053,6 +1053,8 @@ function updateDistanceRequest(transactionID, transactionThreadReportID, transac * @param {Object} [receipt] * @param {String} [category] * @param {String} [tag] + * @param {String} [taxCode] + * @param {Number} [taxAmount] * @param {Boolean} [billable] */ function requestMoney( @@ -1068,6 +1070,8 @@ function requestMoney( receipt = undefined, category = undefined, tag = undefined, + taxCode = '', + taxAmount = 0, billable = undefined, ) { // If the report is iou or expense report, we should get the linked chat report to be passed to the getMoneyRequestInformation function @@ -1098,6 +1102,8 @@ function requestMoney( receiptState: lodashGet(receipt, 'state'), category, tag, + taxCode, + taxAmount, billable, }, onyxData, diff --git a/src/pages/iou/request/step/IOURequestStepConfirmation.js b/src/pages/iou/request/step/IOURequestStepConfirmation.js index 1f458ef37f01..998a0da5ff92 100644 --- a/src/pages/iou/request/step/IOURequestStepConfirmation.js +++ b/src/pages/iou/request/step/IOURequestStepConfirmation.js @@ -76,6 +76,8 @@ function IOURequestStepConfirmation({ const [receiptFile, setReceiptFile] = useState(); const receiptFilename = lodashGet(transaction, 'filename'); const receiptPath = lodashGet(transaction, 'receipt.source'); + const transactionTaxCode = transaction.taxRate && transaction.taxRate.keyForList; + const transactionTaxAmount = transaction.taxAmount; const requestType = TransactionUtils.getRequestType(transaction); const headerTitle = iouType === CONST.IOU.TYPE.SPLIT ? translate('iou.split') : translate(TransactionUtils.getHeaderTitleTranslationKey(transaction)); const participants = useMemo( @@ -162,10 +164,12 @@ function IOURequestStepConfirmation({ receiptObj, transaction.category, transaction.tag, + transactionTaxCode, + transactionTaxAmount, transaction.billable, ); }, - [report, transaction, currentUserPersonalDetails.login, currentUserPersonalDetails.accountID], + [report, transaction, transactionTaxCode, transactionTaxAmount, currentUserPersonalDetails.login, currentUserPersonalDetails.accountID], ); /** From a2696d35b84b8bd50339ef64677524b2eb61e855 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Sun, 17 Dec 2023 23:04:14 +0100 Subject: [PATCH 45/78] use transactions from onyx instead of transactionsDraft --- .../MoneyRequestConfirmationList.js | 12 ++--------- ...oraryForRefactorRequestConfirmationList.js | 13 ++---------- .../step/IOURequestStepTaxAmountPage.js | 21 +++---------------- .../request/step/IOURequestStepTaxRatePage.js | 13 +----------- 4 files changed, 8 insertions(+), 51 deletions(-) diff --git a/src/components/MoneyRequestConfirmationList.js b/src/components/MoneyRequestConfirmationList.js index 8dd764f45698..d3e207cb1fe2 100755 --- a/src/components/MoneyRequestConfirmationList.js +++ b/src/components/MoneyRequestConfirmationList.js @@ -43,7 +43,6 @@ import tagPropTypes from './tagPropTypes'; import taxPropTypes from './taxPropTypes'; import Text from './Text'; import transactionPropTypes from './transactionPropTypes'; -import {transactionsDraftDefaultProps, transactionsDraftPropTypes} from './transactionsDraftPropTypes'; import withCurrentUserPersonalDetails, {withCurrentUserPersonalDetailsDefaultProps, withCurrentUserPersonalDetailsPropTypes} from './withCurrentUserPersonalDetails'; const propTypes = { @@ -172,9 +171,6 @@ const propTypes = { /** Holds data related to Money Request view state, rather than the underlying Money Request data. */ iou: iouPropTypes, - - /** holds data for selected tax rates and tax amount */ - transactionsDraft: transactionsDraftPropTypes, }; const defaultProps = { @@ -209,7 +205,6 @@ const defaultProps = { shouldShowSmartScanFields: true, isPolicyExpenseChat: false, iou: iouDefaultProps, - transactionsDraft: transactionsDraftDefaultProps, policyTaxRates: {}, }; @@ -266,7 +261,7 @@ function MoneyRequestConfirmationList(props) { shouldCalculateDistanceAmount ? DistanceRequestUtils.getDistanceRequestAmount(distance, unit, rate) : props.iouAmount, props.isDistanceRequest ? currency : props.iouCurrencyCode, ); - const formattedTaxAmount = CurrencyUtils.convertToDisplayString(props.transactionsDraft.taxAmount, props.iouCurrencyCode); + const formattedTaxAmount = CurrencyUtils.convertToDisplayString(props.transaction.taxAmount, props.iouCurrencyCode); const isFocused = useIsFocused(); const [formError, setFormError] = useState(''); @@ -758,7 +753,7 @@ function MoneyRequestConfirmationList(props) { {shouldShowTax && ( `${ONYXKEYS.COLLECTION.SPLIT_TRANSACTION_DRAFT}${transactionID}`, }, - transactionsDraft: { - key: ({transaction}) => `${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transaction.transactionID}`, - }, policy: { key: ({policyID}) => `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, }, diff --git a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js index eec5474263ca..83c0291fa17d 100755 --- a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js +++ b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js @@ -41,7 +41,6 @@ import tagPropTypes from './tagPropTypes'; import taxPropTypes from './taxPropTypes'; import Text from './Text'; import transactionPropTypes from './transactionPropTypes'; -import {transactionsDraftDefaultProps, transactionsDraftPropTypes} from './transactionsDraftPropTypes'; import withCurrentUserPersonalDetails, {withCurrentUserPersonalDetailsDefaultProps, withCurrentUserPersonalDetailsPropTypes} from './withCurrentUserPersonalDetails'; const propTypes = { @@ -167,9 +166,6 @@ const propTypes = { /** Transaction that represents the money request */ transaction: transactionPropTypes, - - /** holds data for selected tax rates and tax amount */ - transactionsDraft: transactionsDraftPropTypes, }; const defaultProps = { @@ -203,7 +199,6 @@ const defaultProps = { isScanRequest: false, shouldShowSmartScanFields: true, isPolicyExpenseChat: false, - transactionsDraft: transactionsDraftDefaultProps, policyTaxRates: {}, }; @@ -246,7 +241,6 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ session: {accountID}, shouldShowSmartScanFields, transaction, - transactionsDraft, policyTaxRates, }) { const theme = useTheme(); @@ -298,7 +292,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ shouldCalculateDistanceAmount ? DistanceRequestUtils.getDistanceRequestAmount(distance, unit, rate) : iouAmount, isDistanceRequest ? currency : iouCurrencyCode, ); - const formattedTaxAmount = CurrencyUtils.convertToDisplayString(transactionsDraft.taxAmount, iouCurrencyCode); + const formattedTaxAmount = CurrencyUtils.convertToDisplayString(transaction.taxAmount, iouCurrencyCode); const isFocused = useIsFocused(); const [formError, setFormError] = useState(''); @@ -759,7 +753,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ {shouldShowTax && ( `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, selector: DistanceRequestUtils.getDefaultMileageRate, }, - transactionsDraft: { - key: ({transaction}) => `${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transaction.transactionID}`, - }, policy: { key: ({policyID}) => `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, }, diff --git a/src/pages/iou/request/step/IOURequestStepTaxAmountPage.js b/src/pages/iou/request/step/IOURequestStepTaxAmountPage.js index 2a331a1c7a17..88ae474bc791 100644 --- a/src/pages/iou/request/step/IOURequestStepTaxAmountPage.js +++ b/src/pages/iou/request/step/IOURequestStepTaxAmountPage.js @@ -1,12 +1,10 @@ import {useFocusEffect} from '@react-navigation/native'; import React, {useCallback, useRef} from 'react'; import {View} from 'react-native'; -import {withOnyx} from 'react-native-onyx'; import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import ScreenWrapper from '@components/ScreenWrapper'; import transactionPropTypes from '@components/transactionPropTypes'; -import {transactionsDraftDefaultProps, transactionsDraftPropTypes} from '@components/transactionsDraftPropTypes'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; import compose from '@libs/compose'; @@ -17,7 +15,6 @@ import MoneyRequestAmountForm from '@pages/iou/steps/MoneyRequestAmountForm'; import reportPropTypes from '@pages/reportPropTypes'; import * as IOU from '@userActions/IOU'; import CONST from '@src/CONST'; -import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import IOURequestStepRoutePropTypes from './IOURequestStepRoutePropTypes'; import withFullTransactionOrNotFound from './withFullTransactionOrNotFound'; @@ -27,9 +24,6 @@ const propTypes = { /** Navigation route context info provided by react navigation */ route: IOURequestStepRoutePropTypes.isRequired, - /** holds data for selected tax rates and tax amount */ - transactionsDraft: transactionsDraftPropTypes, - /* Onyx Props */ /** The report that the transaction belongs to */ report: reportPropTypes, @@ -39,7 +33,6 @@ const propTypes = { }; const defaultProps = { - transactionsDraft: transactionsDraftDefaultProps, report: {}, transaction: {}, }; @@ -48,7 +41,7 @@ function IOURequestStepTaxAmountPage({ route: { params: {iouType, reportID, transactionID, backTo, currency: selectedCurrency}, }, - transactionsDraft, + transaction, transaction: {currency: originalCurrency}, report, }) { @@ -112,7 +105,7 @@ function IOURequestStepTaxAmountPage({ (textInput.current = e)} onCurrencyButtonPress={navigateToCurrencySelectionPage} onSubmitButtonPress={updateTaxAmount} @@ -143,12 +136,4 @@ function IOURequestStepTaxAmountPage({ IOURequestStepTaxAmountPage.propTypes = propTypes; IOURequestStepTaxAmountPage.defaultProps = defaultProps; IOURequestStepTaxAmountPage.displayName = 'IOURequestStepTaxAmountPage'; -export default compose( - withWritableReportOrNotFound, - withFullTransactionOrNotFound, - withOnyx({ - transactionsDraft: { - key: ({transaction}) => `${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transaction.transactionID}`, - }, - }), -)(IOURequestStepTaxAmountPage); +export default compose(withWritableReportOrNotFound, withFullTransactionOrNotFound)(IOURequestStepTaxAmountPage); diff --git a/src/pages/iou/request/step/IOURequestStepTaxRatePage.js b/src/pages/iou/request/step/IOURequestStepTaxRatePage.js index d0104ba272fb..d9db2a81c8f5 100644 --- a/src/pages/iou/request/step/IOURequestStepTaxRatePage.js +++ b/src/pages/iou/request/step/IOURequestStepTaxRatePage.js @@ -7,7 +7,6 @@ import ScreenWrapper from '@components/ScreenWrapper'; import TaxPicker from '@components/TaxPicker'; import taxPropTypes from '@components/taxPropTypes'; import transactionPropTypes from '@components/transactionPropTypes'; -import {transactionsDraftDefaultProps, transactionsDraftPropTypes} from '@components/transactionsDraftPropTypes'; import useLocalize from '@hooks/useLocalize'; import compose from '@libs/compose'; import * as CurrencyUtils from '@libs/CurrencyUtils'; @@ -36,16 +35,12 @@ const propTypes = { /** Collection of tax rates attached to a policy */ policyTaxRates: taxPropTypes, - /** holds data for selected tax rates and tax amount */ - transactionsDraft: transactionsDraftPropTypes, - /** The transaction object being modified in Onyx */ transaction: transactionPropTypes, }; const defaultProps = { policyTaxRates: {}, - transactionsDraft: transactionsDraftDefaultProps, transaction: {}, }; @@ -61,7 +56,6 @@ function IOURequestStepTaxRatePage({ params: {iouType, reportID}, }, policyTaxRates, - transactionsDraft, transaction, }) { const {translate} = useLocalize(); @@ -92,7 +86,7 @@ function IOURequestStepTaxRatePage({ onBackButtonPress={() => navigateBack()} /> `${ONYXKEYS.COLLECTION.POLICY_TAX_RATE}${report ? report.policyID : '0'}`, }, }), - withOnyx({ - transactionsDraft: { - key: ({transaction}) => `${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transaction.transactionID}`, - }, - }), )(IOURequestStepTaxRatePage); From 25e128c801d6fbba1a7b517bcae54659a3762854 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Mon, 18 Dec 2023 11:50:48 +0100 Subject: [PATCH 46/78] Update src/pages/iou/request/step/IOURequestStepTaxAmountPage.js Co-authored-by: Michael (Mykhailo) Kravchenko --- src/pages/iou/request/step/IOURequestStepTaxAmountPage.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/pages/iou/request/step/IOURequestStepTaxAmountPage.js b/src/pages/iou/request/step/IOURequestStepTaxAmountPage.js index 88ae474bc791..6b7e57350b4c 100644 --- a/src/pages/iou/request/step/IOURequestStepTaxAmountPage.js +++ b/src/pages/iou/request/step/IOURequestStepTaxAmountPage.js @@ -53,6 +53,7 @@ function IOURequestStepTaxAmountPage({ const currency = selectedCurrency || originalCurrency; const focusTimeoutRef = useRef(null); + useFocusEffect( useCallback(() => { focusTimeoutRef.current = setTimeout(() => textInput.current && textInput.current.focus(), CONST.ANIMATED_TRANSITION); From 42f7fcfcbac1cbe792bd875b8b787b6cd0f37ddc Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Mon, 18 Dec 2023 11:51:04 +0100 Subject: [PATCH 47/78] Update src/libs/OptionsListUtils.js Co-authored-by: Michael (Mykhailo) Kravchenko --- src/libs/OptionsListUtils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/OptionsListUtils.js b/src/libs/OptionsListUtils.js index 84732952023e..767c78affb22 100644 --- a/src/libs/OptionsListUtils.js +++ b/src/libs/OptionsListUtils.js @@ -1100,7 +1100,7 @@ function getTaxRatesOptions(taxRates) { } /** - * Build the section list for tax rates + * Builds the section list for tax rates * * @param {Object} policyTaxRates * @param {Object[]} selectedOptions From 15b45af5688b039fee392661ef05e812d5bcdd7f Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Mon, 18 Dec 2023 11:51:20 +0100 Subject: [PATCH 48/78] Update src/libs/OptionsListUtils.js Co-authored-by: Michael (Mykhailo) Kravchenko --- src/libs/OptionsListUtils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/OptionsListUtils.js b/src/libs/OptionsListUtils.js index 767c78affb22..26f36de94a2d 100644 --- a/src/libs/OptionsListUtils.js +++ b/src/libs/OptionsListUtils.js @@ -1055,7 +1055,7 @@ function getTagListSections(rawTags, recentlyUsedTags, selectedOptions, searchIn } /** - * transforms tax rates to a new object - to add codes and new name with concatenated name and value. + * Transforms tax rates to a new object - to add codes and new name with concatenated name and value. * * @param {Object} policyTaxRates * @returns {Array} From bd72c6ce1e91ed6b6ed0e4cee28b218169da29fb Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Mon, 18 Dec 2023 11:51:42 +0100 Subject: [PATCH 49/78] Update src/components/TaxPicker/taxPickerPropTypes.js Co-authored-by: Michael (Mykhailo) Kravchenko --- src/components/TaxPicker/taxPickerPropTypes.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/TaxPicker/taxPickerPropTypes.js b/src/components/TaxPicker/taxPickerPropTypes.js index d0cd8e896493..765549aabb58 100644 --- a/src/components/TaxPicker/taxPickerPropTypes.js +++ b/src/components/TaxPicker/taxPickerPropTypes.js @@ -5,12 +5,12 @@ const propTypes = { /** The selected tax rate of an expense */ selectedTaxRate: PropTypes.string, + /** Callback to fire when a tax is pressed */ + onSubmit: PropTypes.func.isRequired, + /* Onyx Props */ /** Collection of tax rates attached to a policy */ policyTaxRates: taxPropTypes, - - /** Callback to fire when a tax is pressed */ - onSubmit: PropTypes.func.isRequired, }; const defaultProps = { From 8a1e906f3883ad29f19bd4a4c913898ea5ce497b Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Mon, 18 Dec 2023 11:51:53 +0100 Subject: [PATCH 50/78] Update src/components/taxPropTypes.js Co-authored-by: Michael (Mykhailo) Kravchenko --- src/components/taxPropTypes.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/taxPropTypes.js b/src/components/taxPropTypes.js index 425a637cd168..93b019e8f445 100644 --- a/src/components/taxPropTypes.js +++ b/src/components/taxPropTypes.js @@ -4,10 +4,10 @@ const taxPropTypes = PropTypes.shape({ /** Name of a tax */ name: PropTypes.string.isRequired, - /** value of a tax */ + /** The value of a tax */ value: PropTypes.string.isRequired, - /** if tax is disabled */ + /** Whether the tax is disabled */ isDisabled: PropTypes.bool, }); From bedc6ef75f58e137b2030441393234c1a70adf24 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Mon, 18 Dec 2023 11:52:02 +0100 Subject: [PATCH 51/78] Update src/pages/iou/request/step/IOURequestStepTaxAmountPage.js Co-authored-by: Michael (Mykhailo) Kravchenko --- src/pages/iou/request/step/IOURequestStepTaxAmountPage.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/pages/iou/request/step/IOURequestStepTaxAmountPage.js b/src/pages/iou/request/step/IOURequestStepTaxAmountPage.js index 6b7e57350b4c..17739cea66f3 100644 --- a/src/pages/iou/request/step/IOURequestStepTaxAmountPage.js +++ b/src/pages/iou/request/step/IOURequestStepTaxAmountPage.js @@ -137,4 +137,5 @@ function IOURequestStepTaxAmountPage({ IOURequestStepTaxAmountPage.propTypes = propTypes; IOURequestStepTaxAmountPage.defaultProps = defaultProps; IOURequestStepTaxAmountPage.displayName = 'IOURequestStepTaxAmountPage'; + export default compose(withWritableReportOrNotFound, withFullTransactionOrNotFound)(IOURequestStepTaxAmountPage); From a456030470a4556462616b191aa98081bc96258f Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Mon, 18 Dec 2023 12:28:40 +0100 Subject: [PATCH 52/78] fix lint --- src/components/TaxPicker/taxPickerPropTypes.js | 2 +- src/pages/iou/request/step/IOURequestStepTaxAmountPage.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/TaxPicker/taxPickerPropTypes.js b/src/components/TaxPicker/taxPickerPropTypes.js index 765549aabb58..289b4e19aaa4 100644 --- a/src/components/TaxPicker/taxPickerPropTypes.js +++ b/src/components/TaxPicker/taxPickerPropTypes.js @@ -7,7 +7,7 @@ const propTypes = { /** Callback to fire when a tax is pressed */ onSubmit: PropTypes.func.isRequired, - + /* Onyx Props */ /** Collection of tax rates attached to a policy */ policyTaxRates: taxPropTypes, diff --git a/src/pages/iou/request/step/IOURequestStepTaxAmountPage.js b/src/pages/iou/request/step/IOURequestStepTaxAmountPage.js index 17739cea66f3..889e93444272 100644 --- a/src/pages/iou/request/step/IOURequestStepTaxAmountPage.js +++ b/src/pages/iou/request/step/IOURequestStepTaxAmountPage.js @@ -53,7 +53,7 @@ function IOURequestStepTaxAmountPage({ const currency = selectedCurrency || originalCurrency; const focusTimeoutRef = useRef(null); - + useFocusEffect( useCallback(() => { focusTimeoutRef.current = setTimeout(() => textInput.current && textInput.current.focus(), CONST.ANIMATED_TRANSITION); From 15a7954d556184e56044ee13b477a943bc3cedac Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Wed, 20 Dec 2023 01:55:44 +0100 Subject: [PATCH 53/78] Update src/components/MoneyRequestConfirmationList.js Co-authored-by: Monil Bhavsar --- src/components/MoneyRequestConfirmationList.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/MoneyRequestConfirmationList.js b/src/components/MoneyRequestConfirmationList.js index d3e207cb1fe2..2b956510049d 100755 --- a/src/components/MoneyRequestConfirmationList.js +++ b/src/components/MoneyRequestConfirmationList.js @@ -247,7 +247,7 @@ function MoneyRequestConfirmationList(props) { // A flag for showing the tags field const shouldShowTags = props.isPolicyExpenseChat && (props.iouTag || OptionsListUtils.hasEnabledOptions(_.values(policyTagList))); - // A flag for showing tax rate + // A flag for showing tax fields - tax rate and tax amount const shouldShowTax = props.isPolicyExpenseChat && props.policy.isTaxTrackingEnabled; // A flag for showing the billable field From 41edcb876c032f23fdef0227d642c01df11356a2 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Wed, 20 Dec 2023 01:56:00 +0100 Subject: [PATCH 54/78] Update src/components/taxPropTypes.js Co-authored-by: Monil Bhavsar --- src/components/taxPropTypes.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/taxPropTypes.js b/src/components/taxPropTypes.js index 93b019e8f445..507e8805456a 100644 --- a/src/components/taxPropTypes.js +++ b/src/components/taxPropTypes.js @@ -21,7 +21,7 @@ export default PropTypes.shape({ /** Default value of taxes */ defaultValue: PropTypes.string, - /** Default Foreign Tax ID */ + /** Default foreign policy tax ID */ foreignTaxDefault: PropTypes.string, /** List of Taxes names and values */ From 8e78e34572b1be04b3495c1e82d9d3351d6ca524 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Wed, 20 Dec 2023 01:56:14 +0100 Subject: [PATCH 55/78] Update src/components/taxPropTypes.js Co-authored-by: Monil Bhavsar --- src/components/taxPropTypes.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/taxPropTypes.js b/src/components/taxPropTypes.js index 507e8805456a..e3eac4178a39 100644 --- a/src/components/taxPropTypes.js +++ b/src/components/taxPropTypes.js @@ -15,7 +15,7 @@ export default PropTypes.shape({ /** Defualt name of taxes */ name: PropTypes.string.isRequired, - /** Defualt external ID of taxes */ + /** Default policy tax ID */ defaultExternalID: PropTypes.string, /** Default value of taxes */ From 8dfb843e2ce6797c40784b9939f488a9d120a80a Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Wed, 20 Dec 2023 01:56:24 +0100 Subject: [PATCH 56/78] Update src/components/taxPropTypes.js Co-authored-by: Monil Bhavsar --- src/components/taxPropTypes.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/taxPropTypes.js b/src/components/taxPropTypes.js index e3eac4178a39..0456023c84aa 100644 --- a/src/components/taxPropTypes.js +++ b/src/components/taxPropTypes.js @@ -12,7 +12,7 @@ const taxPropTypes = PropTypes.shape({ }); export default PropTypes.shape({ - /** Defualt name of taxes */ + /** Name of the tax */ name: PropTypes.string.isRequired, /** Default policy tax ID */ From 0f52c39d2a02521e77c1cc6dadcbc283b1a37822 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Wed, 20 Dec 2023 01:56:39 +0100 Subject: [PATCH 57/78] Update src/components/taxPropTypes.js Co-authored-by: Monil Bhavsar --- src/components/taxPropTypes.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/taxPropTypes.js b/src/components/taxPropTypes.js index 0456023c84aa..d0656fef0433 100644 --- a/src/components/taxPropTypes.js +++ b/src/components/taxPropTypes.js @@ -24,6 +24,6 @@ export default PropTypes.shape({ /** Default foreign policy tax ID */ foreignTaxDefault: PropTypes.string, - /** List of Taxes names and values */ + /** List of tax names and values */ taxes: PropTypes.objectOf(taxPropTypes), }); From 16319b9d01f5e68e1b2c4b57c44d461158f3ba76 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Wed, 20 Dec 2023 01:59:39 +0100 Subject: [PATCH 58/78] Update src/libs/OptionsListUtils.js Co-authored-by: Monil Bhavsar --- src/libs/OptionsListUtils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/OptionsListUtils.js b/src/libs/OptionsListUtils.js index 26f36de94a2d..43ad4802c37d 100644 --- a/src/libs/OptionsListUtils.js +++ b/src/libs/OptionsListUtils.js @@ -1187,7 +1187,7 @@ function getTaxRatesSection(policyTaxRates, selectedOptions, searchInputValue) { } policyRatesSections.push({ - // "All" section when items amount more than the threshold + // "All" section when number of items are more than the threshold title: '', shouldShow: true, indexOffset, From a7be0dccfd386e6cf348370e8a4aff88af0328b1 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Wed, 20 Dec 2023 02:00:23 +0100 Subject: [PATCH 59/78] Update src/libs/OptionsListUtils.js Co-authored-by: Ali Toshmatov <59907218+alitoshmatov@users.noreply.github.com> --- src/libs/OptionsListUtils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/OptionsListUtils.js b/src/libs/OptionsListUtils.js index 43ad4802c37d..43e828dec535 100644 --- a/src/libs/OptionsListUtils.js +++ b/src/libs/OptionsListUtils.js @@ -1061,7 +1061,7 @@ function getTagListSections(rawTags, recentlyUsedTags, selectedOptions, searchIn * @returns {Array} */ function transformedTaxRates(policyTaxRates) { - const defaulTaxKey = policyTaxRates.defaultExternalID; + const defaultTaxKey = policyTaxRates.defaultExternalID; const getName = (data, code) => `${data.name} (${data.value})${defaulTaxKey === code ? ` • ${Localize.translateLocal('common.default')}` : ''}`; const taxes = Object.fromEntries(_.map(Object.entries(policyTaxRates.taxes), ([code, data]) => [code, {...data, code, name: getName(data, code), actualName: data.name}])); return taxes; From 1404d7c9e602b7c772c70bb7d6d9a1549fbe2751 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Wed, 20 Dec 2023 02:00:34 +0100 Subject: [PATCH 60/78] Update src/libs/OptionsListUtils.js Co-authored-by: Ali Toshmatov <59907218+alitoshmatov@users.noreply.github.com> --- src/libs/OptionsListUtils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/OptionsListUtils.js b/src/libs/OptionsListUtils.js index 43e828dec535..f53919cab64a 100644 --- a/src/libs/OptionsListUtils.js +++ b/src/libs/OptionsListUtils.js @@ -1062,7 +1062,7 @@ function getTagListSections(rawTags, recentlyUsedTags, selectedOptions, searchIn */ function transformedTaxRates(policyTaxRates) { const defaultTaxKey = policyTaxRates.defaultExternalID; - const getName = (data, code) => `${data.name} (${data.value})${defaulTaxKey === code ? ` • ${Localize.translateLocal('common.default')}` : ''}`; + const getName = (data, code) => `${data.name} (${data.value})${defaultTaxKey === code ? ` • ${Localize.translateLocal('common.default')}` : ''}`; const taxes = Object.fromEntries(_.map(Object.entries(policyTaxRates.taxes), ([code, data]) => [code, {...data, code, name: getName(data, code), actualName: data.name}])); return taxes; } From 4e0a034dd92a7e573dc3a302fa0ba0f56c09c069 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Wed, 20 Dec 2023 04:37:36 +0100 Subject: [PATCH 61/78] test getFilteredOptions for taxRate --- tests/unit/OptionsListUtilsTest.js | 144 ++++++++++++++++++++++++++++- 1 file changed, 143 insertions(+), 1 deletion(-) diff --git a/tests/unit/OptionsListUtilsTest.js b/tests/unit/OptionsListUtilsTest.js index 45ddf44b244a..cbf7f066195d 100644 --- a/tests/unit/OptionsListUtilsTest.js +++ b/tests/unit/OptionsListUtilsTest.js @@ -7,7 +7,7 @@ import * as ReportUtils from '../../src/libs/ReportUtils'; import ONYXKEYS from '../../src/ONYXKEYS'; import waitForBatchedUpdates from '../utils/waitForBatchedUpdates'; -describe('OptionsListUtils', () => { +fdescribe('OptionsListUtils', () => { // Given a set of reports with both single participants and multiple participants some pinned and some not const REPORTS = { 1: { @@ -2063,6 +2063,148 @@ describe('OptionsListUtils', () => { expect(OptionsListUtils.sortCategories(categoriesIncorrectOrdering3)).toStrictEqual(result3); }); + it('getFilteredOptions() for taxRate', () => { + const search = 'rate'; + const emptySearch = ''; + const wrongSearch = 'bla bla'; + + const policyTaxRatesWithDefault = { + name: 'Tax', + defaultExternalID: 'CODE1', + defaultValue: '0%', + foreignTaxDefault: 'CODE1', + taxes: { + CODE2: { + name: 'Tax rate 2', + value: '3%', + }, + CODE3: { + name: 'Tax option 3', + value: '5%', + }, + CODE1: { + name: 'Tax exempt 1', + value: '0%', + }, + }, + }; + + const resultList = [ + { + title: '', + shouldShow: false, + indexOffset: 0, + // data sorted alphabetically by name + data: [ + { + // Adds 'Default' title to default tax. + // Adds value to tax name for more description. + text: 'Tax exempt 1 (0%) • Default', + keyForList: 'CODE1', + searchText: 'Tax exempt 1 (0%) • Default', + tooltipText: 'Tax exempt 1 (0%) • Default', + isDisabled: undefined, + // creates a data option. + data: { + actualName: 'Tax exempt 1', + code: 'CODE1', + name: 'Tax exempt 1 (0%) • Default', + value: '0%', + }, + }, + { + text: 'Tax option 3 (5%)', + keyForList: 'CODE3', + searchText: 'Tax option 3 (5%)', + tooltipText: 'Tax option 3 (5%)', + isDisabled: undefined, + data: { + actualName: 'Tax option 3', + code: 'CODE3', + name: 'Tax option 3 (5%)', + value: '5%', + }, + }, + { + text: 'Tax rate 2 (3%)', + keyForList: 'CODE2', + searchText: 'Tax rate 2 (3%)', + tooltipText: 'Tax rate 2 (3%)', + isDisabled: undefined, + data: { + actualName: 'Tax rate 2', + code: 'CODE2', + name: 'Tax rate 2 (3%)', + value: '3%', + }, + }, + ], + }, + ]; + + const searchResultList = [ + { + title: '', + shouldShow: true, + indexOffset: 0, + // data sorted alphabetically by name + data: [ + { + text: 'Tax rate 2 (3%)', + keyForList: 'CODE2', + searchText: 'Tax rate 2 (3%)', + tooltipText: 'Tax rate 2 (3%)', + isDisabled: undefined, + data: { + actualName: 'Tax rate 2', + code: 'CODE2', + name: 'Tax rate 2 (3%)', + value: '3%', + }, + }, + ], + }, + ]; + + const wrongSearchResultList = [ + { + title: '', + shouldShow: true, + indexOffset: 0, + data: [], + }, + ]; + + const result = OptionsListUtils.getFilteredOptions({}, {}, [], emptySearch, [], [], false, false, false, {}, [], false, {}, [], false, false, true, policyTaxRatesWithDefault); + + expect(result.policyTaxRatesOptions).toStrictEqual(resultList); + + const searchResult = OptionsListUtils.getFilteredOptions({}, {}, [], search, [], [], false, false, false, {}, [], false, {}, [], false, false, true, policyTaxRatesWithDefault); + expect(searchResult.policyTaxRatesOptions).toStrictEqual(searchResultList); + + const wrongSearchResult = OptionsListUtils.getFilteredOptions( + {}, + {}, + [], + wrongSearch, + [], + [], + false, + false, + false, + {}, + [], + false, + {}, + [], + false, + false, + true, + policyTaxRatesWithDefault, + ); + expect(wrongSearchResult.policyTaxRatesOptions).toStrictEqual(wrongSearchResultList); + }); + it('formatMemberForList()', () => { const formattedMembers = _.map(PERSONAL_DETAILS, (personalDetail, key) => OptionsListUtils.formatMemberForList(personalDetail, {isSelected: key === '1'})); From c168e740ec8400e667b91b127223057034b67ec0 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Thu, 21 Dec 2023 02:15:22 +0100 Subject: [PATCH 62/78] Update tests/unit/OptionsListUtilsTest.js Co-authored-by: Monil Bhavsar --- tests/unit/OptionsListUtilsTest.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/OptionsListUtilsTest.js b/tests/unit/OptionsListUtilsTest.js index cbf7f066195d..387800242ece 100644 --- a/tests/unit/OptionsListUtilsTest.js +++ b/tests/unit/OptionsListUtilsTest.js @@ -7,7 +7,7 @@ import * as ReportUtils from '../../src/libs/ReportUtils'; import ONYXKEYS from '../../src/ONYXKEYS'; import waitForBatchedUpdates from '../utils/waitForBatchedUpdates'; -fdescribe('OptionsListUtils', () => { +describe('OptionsListUtils', () => { // Given a set of reports with both single participants and multiple participants some pinned and some not const REPORTS = { 1: { From 5ec8788cd785f990c0b932eb0ef2a444aa8a6363 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Thu, 21 Dec 2023 08:10:01 +0100 Subject: [PATCH 63/78] Update src/pages/iou/request/step/IOURequestStepTaxRatePage.js Co-authored-by: Monil Bhavsar --- src/pages/iou/request/step/IOURequestStepTaxRatePage.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/pages/iou/request/step/IOURequestStepTaxRatePage.js b/src/pages/iou/request/step/IOURequestStepTaxRatePage.js index d9db2a81c8f5..86eb0ad1e9e1 100644 --- a/src/pages/iou/request/step/IOURequestStepTaxRatePage.js +++ b/src/pages/iou/request/step/IOURequestStepTaxRatePage.js @@ -44,7 +44,6 @@ const defaultProps = { transaction: {}, }; -// this is the formulae to calculate tax const calculateAmount = (taxRates, selectedTaxRate, amount) => { const percentage = _.find(OptionsListUtils.transformedTaxRates(taxRates), (taxRate) => taxRate.name === selectedTaxRate).value; const divisor = percentage.slice(0, -1) / 100 + 1; // slice to remove % at the end; converts "10%" to "10" From a3ac0b6f577dd9a5c5d69584cb5ae853b3f426d0 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Thu, 21 Dec 2023 08:10:28 +0100 Subject: [PATCH 64/78] Update src/pages/iou/request/step/IOURequestStepTaxRatePage.js Co-authored-by: Monil Bhavsar --- src/pages/iou/request/step/IOURequestStepTaxRatePage.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/iou/request/step/IOURequestStepTaxRatePage.js b/src/pages/iou/request/step/IOURequestStepTaxRatePage.js index 86eb0ad1e9e1..10ce786e1995 100644 --- a/src/pages/iou/request/step/IOURequestStepTaxRatePage.js +++ b/src/pages/iou/request/step/IOURequestStepTaxRatePage.js @@ -47,7 +47,7 @@ const defaultProps = { const calculateAmount = (taxRates, selectedTaxRate, amount) => { const percentage = _.find(OptionsListUtils.transformedTaxRates(taxRates), (taxRate) => taxRate.name === selectedTaxRate).value; const divisor = percentage.slice(0, -1) / 100 + 1; // slice to remove % at the end; converts "10%" to "10" - return parseInt(Math.round(amount - amount / divisor), 10) / 100; // returns The expense amount of transaction + return parseInt(Math.round(amount - (amount / divisor)), 10) / 100; }; function IOURequestStepTaxRatePage({ From bea71c4cd0d56d19988f37141b0991f75e1d2f9c Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Thu, 21 Dec 2023 08:24:16 +0100 Subject: [PATCH 65/78] Update src/pages/iou/request/step/IOURequestStepTaxRatePage.js Co-authored-by: Monil Bhavsar --- src/pages/iou/request/step/IOURequestStepTaxRatePage.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/iou/request/step/IOURequestStepTaxRatePage.js b/src/pages/iou/request/step/IOURequestStepTaxRatePage.js index 10ce786e1995..76e600b92001 100644 --- a/src/pages/iou/request/step/IOURequestStepTaxRatePage.js +++ b/src/pages/iou/request/step/IOURequestStepTaxRatePage.js @@ -46,7 +46,7 @@ const defaultProps = { const calculateAmount = (taxRates, selectedTaxRate, amount) => { const percentage = _.find(OptionsListUtils.transformedTaxRates(taxRates), (taxRate) => taxRate.name === selectedTaxRate).value; - const divisor = percentage.slice(0, -1) / 100 + 1; // slice to remove % at the end; converts "10%" to "10" + const divisor = percentage.slice(0, -1) / 100 + 1; return parseInt(Math.round(amount - (amount / divisor)), 10) / 100; }; From 70fe7a19b465c1a320b176679ea662c7b074ed24 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Thu, 21 Dec 2023 08:40:00 +0100 Subject: [PATCH 66/78] it should be calculateTaxAmount --- src/pages/iou/request/step/IOURequestStepTaxRatePage.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/iou/request/step/IOURequestStepTaxRatePage.js b/src/pages/iou/request/step/IOURequestStepTaxRatePage.js index d9db2a81c8f5..9ae7db05a61b 100644 --- a/src/pages/iou/request/step/IOURequestStepTaxRatePage.js +++ b/src/pages/iou/request/step/IOURequestStepTaxRatePage.js @@ -45,7 +45,7 @@ const defaultProps = { }; // this is the formulae to calculate tax -const calculateAmount = (taxRates, selectedTaxRate, amount) => { +const calculateTaxAmount = (taxRates, selectedTaxRate, amount) => { const percentage = _.find(OptionsListUtils.transformedTaxRates(taxRates), (taxRate) => taxRate.name === selectedTaxRate).value; const divisor = percentage.slice(0, -1) / 100 + 1; // slice to remove % at the end; converts "10%" to "10" return parseInt(Math.round(amount - amount / divisor), 10) / 100; // returns The expense amount of transaction @@ -65,7 +65,7 @@ function IOURequestStepTaxRatePage({ } const updateTaxRates = (taxes) => { - const taxAmount = calculateAmount(policyTaxRates, taxes.text, transaction.amount); + const taxAmount = calculateTaxAmount(policyTaxRates, taxes.text, transaction.amount); const amountInSmallestCurrencyUnits = CurrencyUtils.convertToBackendAmount(Number.parseFloat(taxAmount)); IOU.setMoneyRequestTaxRate(transaction.transactionID, taxes); IOU.setMoneyRequestTaxAmount(transaction.transactionID, amountInSmallestCurrencyUnits); From 679193cd29d2c9e2f4dcff67a11de3b77e465a70 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Thu, 21 Dec 2023 08:40:23 +0100 Subject: [PATCH 67/78] define policyTaxRates --- src/libs/OptionsListUtils.js | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/src/libs/OptionsListUtils.js b/src/libs/OptionsListUtils.js index f53919cab64a..eff7e4d5ae6f 100644 --- a/src/libs/OptionsListUtils.js +++ b/src/libs/OptionsListUtils.js @@ -1055,10 +1055,28 @@ function getTagListSections(rawTags, recentlyUsedTags, selectedOptions, searchIn } /** - * Transforms tax rates to a new object - to add codes and new name with concatenated name and value. + * Represents the original tax rates object. * - * @param {Object} policyTaxRates - * @returns {Array} + * @typedef {Object} PolicyTaxRates + * @property {Object.} taxes - Object containing tax rates with their codes. + */ + +/** + * Represents the data for a single tax rate. + * + * @typedef {Object} TaxData + * @property {string} name - The name of the tax rate. + * @property {string} value - The value of the tax rate. + * @property {string} code - The code associated with the tax rate. + * @property {string} actualName - The actual name of the tax rate. + * @property {boolean} [isDisabled] - Indicates if the tax rate is disabled. + */ + + /** + * Transforms tax rates to a new object format - to add codes and new name with concatenated name and value. + * + * @param {PolicyTaxRates} policyTaxRates - The original tax rates object. + * @returns {Array} The transformed tax rates object. */ function transformedTaxRates(policyTaxRates) { const defaultTaxKey = policyTaxRates.defaultExternalID; From b07509d6124c91cbfe5b8888be98a1f912efd9b6 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Thu, 21 Dec 2023 08:49:49 +0100 Subject: [PATCH 68/78] fix lint --- src/libs/OptionsListUtils.js | 8 ++++---- src/pages/iou/request/step/IOURequestStepTaxRatePage.js | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/libs/OptionsListUtils.js b/src/libs/OptionsListUtils.js index eff7e4d5ae6f..15601ef1490b 100644 --- a/src/libs/OptionsListUtils.js +++ b/src/libs/OptionsListUtils.js @@ -1063,7 +1063,7 @@ function getTagListSections(rawTags, recentlyUsedTags, selectedOptions, searchIn /** * Represents the data for a single tax rate. - * + * * @typedef {Object} TaxData * @property {string} name - The name of the tax rate. * @property {string} value - The value of the tax rate. @@ -1071,12 +1071,12 @@ function getTagListSections(rawTags, recentlyUsedTags, selectedOptions, searchIn * @property {string} actualName - The actual name of the tax rate. * @property {boolean} [isDisabled] - Indicates if the tax rate is disabled. */ - - /** + +/** * Transforms tax rates to a new object format - to add codes and new name with concatenated name and value. * * @param {PolicyTaxRates} policyTaxRates - The original tax rates object. - * @returns {Array} The transformed tax rates object. + * @returns {Object.} The transformed tax rates object. */ function transformedTaxRates(policyTaxRates) { const defaultTaxKey = policyTaxRates.defaultExternalID; diff --git a/src/pages/iou/request/step/IOURequestStepTaxRatePage.js b/src/pages/iou/request/step/IOURequestStepTaxRatePage.js index ed17d0cf09eb..e105bf0899f9 100644 --- a/src/pages/iou/request/step/IOURequestStepTaxRatePage.js +++ b/src/pages/iou/request/step/IOURequestStepTaxRatePage.js @@ -48,7 +48,7 @@ const defaultProps = { const calculateTaxAmount = (taxRates, selectedTaxRate, amount) => { const percentage = _.find(OptionsListUtils.transformedTaxRates(taxRates), (taxRate) => taxRate.name === selectedTaxRate).value; const divisor = percentage.slice(0, -1) / 100 + 1; - return parseInt(Math.round(amount - (amount / divisor)), 10) / 100; + return parseInt(Math.round(amount - amount / divisor), 10) / 100; }; function IOURequestStepTaxRatePage({ From 31359399c6d08e6f16cb2b0b236faef2a23fc1ea Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Wed, 27 Dec 2023 11:13:51 +0100 Subject: [PATCH 69/78] fix review comments --- src/components/TaxPicker/index.js | 3 +- src/libs/OptionsListUtils.js | 40 +++++-------------- src/libs/TransactionUtils.ts | 18 +++++++++ .../iou/request/step/IOURequestStepAmount.js | 13 ++++++ .../request/step/IOURequestStepTaxRatePage.js | 11 +++-- src/pages/iou/steps/MoneyRequestAmountForm.js | 2 +- src/types/onyx/PolicyTaxRates.ts | 14 +++++++ 7 files changed, 63 insertions(+), 38 deletions(-) create mode 100644 src/types/onyx/PolicyTaxRates.ts diff --git a/src/components/TaxPicker/index.js b/src/components/TaxPicker/index.js index 500e8109116d..f25a1b84bf64 100644 --- a/src/components/TaxPicker/index.js +++ b/src/components/TaxPicker/index.js @@ -6,6 +6,7 @@ import useLocalize from '@hooks/useLocalize'; import useStyleUtils from '@hooks/useStyleUtils'; import useThemeStyles from '@hooks/useThemeStyles'; import * as OptionsListUtils from '@libs/OptionsListUtils'; +import * as TransactionUtils from '@libs/TransactionUtils'; import CONST from '@src/CONST'; import {defaultProps, propTypes} from './taxPickerPropTypes'; @@ -15,7 +16,7 @@ function TaxPicker({selectedTaxRate, policyTaxRates, insets, onSubmit}) { const {translate} = useLocalize(); const [searchValue, setSearchValue] = useState(''); - const policyTaxRatesCount = OptionsListUtils.getEnabledTaxRateCount(policyTaxRates.taxes); + const policyTaxRatesCount = TransactionUtils.getEnabledTaxRateCount(policyTaxRates.taxes); const isTaxRatesCountBelowThreshold = policyTaxRatesCount < CONST.TAX_RATES_LIST_THRESHOLD; const shouldShowTextInput = !isTaxRatesCountBelowThreshold; diff --git a/src/libs/OptionsListUtils.js b/src/libs/OptionsListUtils.js index 7d1cfa355f5e..473272a23af9 100644 --- a/src/libs/OptionsListUtils.js +++ b/src/libs/OptionsListUtils.js @@ -643,17 +643,6 @@ function getEnabledCategoriesCount(options) { return _.filter(options, (option) => option.enabled).length; } -/** - * Calculates count of all tax enabled options - * - * @param {Object[]} options - an initial strings array - * @param {Boolean} options[].isDisabled - a flag to enable/disable option in a list - * @returns {Number} - */ -function getEnabledTaxRateCount(options) { - return _.filter(options, (option) => !option.isDisabled).length; -} - /** * Verifies that there is at least one enabled option * @@ -1061,34 +1050,26 @@ function getTagListSections(rawTags, recentlyUsedTags, selectedOptions, searchIn return tagSections; } -/** - * Represents the original tax rates object. - * - * @typedef {Object} PolicyTaxRates - * @property {Object.} taxes - Object containing tax rates with their codes. - */ - /** * Represents the data for a single tax rate. * - * @typedef {Object} TaxData * @property {string} name - The name of the tax rate. * @property {string} value - The value of the tax rate. * @property {string} code - The code associated with the tax rate. - * @property {string} actualName - The actual name of the tax rate. + * @property {string} modifiedName - This contains the tax name and tax value as one name * @property {boolean} [isDisabled] - Indicates if the tax rate is disabled. */ /** * Transforms tax rates to a new object format - to add codes and new name with concatenated name and value. * - * @param {PolicyTaxRates} policyTaxRates - The original tax rates object. - * @returns {Object.} The transformed tax rates object. + * @param {Object} policyTaxRates - The original tax rates object. + * @returns {Object.>} The transformed tax rates object. */ function transformedTaxRates(policyTaxRates) { const defaultTaxKey = policyTaxRates.defaultExternalID; - const getName = (data, code) => `${data.name} (${data.value})${defaultTaxKey === code ? ` • ${Localize.translateLocal('common.default')}` : ''}`; - const taxes = Object.fromEntries(_.map(Object.entries(policyTaxRates.taxes), ([code, data]) => [code, {...data, code, name: getName(data, code), actualName: data.name}])); + const getModifiedName = (data, code) => `${data.name} (${data.value})${defaultTaxKey === code ? ` • ${Localize.translateLocal('common.default')}` : ''}`; + const taxes = Object.fromEntries(_.map(Object.entries(policyTaxRates.taxes), ([code, data]) => [code, {...data, code, modifiedName: getModifiedName(data, code), name: data.name}])); return taxes; } @@ -1115,10 +1096,10 @@ function sortTaxRates(taxRates) { */ function getTaxRatesOptions(taxRates) { return _.map(taxRates, (taxRate) => ({ - text: taxRate.name, + text: taxRate.modifiedName, keyForList: taxRate.code, - searchText: taxRate.name, - tooltipText: taxRate.name, + searchText: taxRate.modifiedName, + tooltipText: taxRate.modifiedName, isDisabled: taxRate.isDisabled, data: taxRate, })); @@ -1162,7 +1143,7 @@ function getTaxRatesSection(policyTaxRates, selectedOptions, searchInputValue) { } if (!_.isEmpty(searchInputValue)) { - const searchTaxRates = _.filter(enabledTaxRates, (taxRate) => taxRate.name.toLowerCase().includes(searchInputValue.toLowerCase())); + const searchTaxRates = _.filter(enabledTaxRates, (taxRate) => taxRate.modifiedName.toLowerCase().includes(searchInputValue.toLowerCase())); policyRatesSections.push({ // "Search" section @@ -1192,7 +1173,7 @@ function getTaxRatesSection(policyTaxRates, selectedOptions, searchInputValue) { if (!_.isEmpty(selectedOptions)) { const selectedTaxRatesOptions = _.map(selectedOptions, (option) => { - const taxRateObject = _.find(taxes, (taxRate) => taxRate.name === option.name); + const taxRateObject = _.find(taxes, (taxRate) => taxRate.modifiedName === option.name); return { name: option.name, @@ -1961,7 +1942,6 @@ export { shouldOptionShowTooltip, getLastMessageTextForReport, getEnabledCategoriesCount, - getEnabledTaxRateCount, hasEnabledOptions, sortCategories, getCategoryOptionTree, diff --git a/src/libs/TransactionUtils.ts b/src/libs/TransactionUtils.ts index 6905a542fa5b..0f40d945c19e 100644 --- a/src/libs/TransactionUtils.ts +++ b/src/libs/TransactionUtils.ts @@ -4,6 +4,7 @@ import {ValueOf} from 'type-fest'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import {RecentWaypoint, ReportAction, Transaction} from '@src/types/onyx'; +import PolicyTaxRate, {PolicyTaxRates} from '@src/types/onyx/PolicyTaxRates'; import {Comment, Receipt, Waypoint, WaypointCollection} from '@src/types/onyx/Transaction'; import {EmptyObject} from '@src/types/utils/EmptyObject'; import {isCorporateCard, isExpensifyCard} from './CardUtils'; @@ -508,8 +509,25 @@ function getRecentTransactions(transactions: Record, size = 2): .slice(0, size); } +/** + * this is the formulae to calculate tax + */ +function calculateTaxAmount(percentage: string, amount: number) { + const divisor = Number(percentage.slice(0, -1)) / 100 + 1; + return Math.round(amount - amount / divisor) / 100; +} + +/** + * Calculates count of all tax enabled options + */ +function getEnabledTaxRateCount(options: PolicyTaxRates) { + return Object.values(options).filter((option: PolicyTaxRate) => !option.isDisabled).length; +} + export { buildOptimisticTransaction, + calculateTaxAmount, + getEnabledTaxRateCount, getUpdatedTransaction, getTransaction, getDescription, diff --git a/src/pages/iou/request/step/IOURequestStepAmount.js b/src/pages/iou/request/step/IOURequestStepAmount.js index b0812271c647..0c3b0e97cf50 100644 --- a/src/pages/iou/request/step/IOURequestStepAmount.js +++ b/src/pages/iou/request/step/IOURequestStepAmount.js @@ -6,6 +6,7 @@ import compose from '@libs/compose'; import * as CurrencyUtils from '@libs/CurrencyUtils'; import Navigation from '@libs/Navigation/Navigation'; import {getRequestType} from '@libs/TransactionUtils'; +import * as TransactionUtils from '@libs/TransactionUtils'; import MoneyRequestAmountForm from '@pages/iou/steps/MoneyRequestAmountForm'; import reportPropTypes from '@pages/reportPropTypes'; import * as IOU from '@userActions/IOU'; @@ -33,6 +34,11 @@ const defaultProps = { transaction: {}, }; +const getTaxAmount = (transaction, amount) => { + const percentage = (transaction.taxRate && transaction.taxRate.data.value) || ''; + return TransactionUtils.calculateTaxAmount(percentage, amount); +}; + function IOURequestStepAmount({ report, route: { @@ -72,6 +78,13 @@ function IOURequestStepAmount({ */ const navigateToNextPage = ({amount}) => { const amountInSmallestCurrencyUnits = CurrencyUtils.convertToBackendAmount(Number.parseFloat(amount)); + + if (backTo) { + const taxAmount = getTaxAmount(transaction, amountInSmallestCurrencyUnits); + const taxAmountInSmallestCurrencyUnits = CurrencyUtils.convertToBackendAmount(Number.parseFloat(taxAmount)); + IOU.setMoneyRequestTaxAmount(transaction.transactionID, taxAmountInSmallestCurrencyUnits); + } + IOU.setMoneyRequestAmount_temporaryForRefactor(transactionID, amountInSmallestCurrencyUnits, currency || CONST.CURRENCY.USD); if (backTo) { diff --git a/src/pages/iou/request/step/IOURequestStepTaxRatePage.js b/src/pages/iou/request/step/IOURequestStepTaxRatePage.js index e105bf0899f9..3312869c46ec 100644 --- a/src/pages/iou/request/step/IOURequestStepTaxRatePage.js +++ b/src/pages/iou/request/step/IOURequestStepTaxRatePage.js @@ -12,6 +12,7 @@ import compose from '@libs/compose'; import * as CurrencyUtils from '@libs/CurrencyUtils'; import Navigation from '@libs/Navigation/Navigation'; import * as OptionsListUtils from '@libs/OptionsListUtils'; +import * as TransactionUtils from '@libs/TransactionUtils'; import * as IOU from '@userActions/IOU'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; @@ -44,11 +45,9 @@ const defaultProps = { transaction: {}, }; -// this is the formulae to calculate tax -const calculateTaxAmount = (taxRates, selectedTaxRate, amount) => { - const percentage = _.find(OptionsListUtils.transformedTaxRates(taxRates), (taxRate) => taxRate.name === selectedTaxRate).value; - const divisor = percentage.slice(0, -1) / 100 + 1; - return parseInt(Math.round(amount - amount / divisor), 10) / 100; +const getTaxAmount = (taxRates, selectedTaxRate, amount) => { + const percentage = _.find(OptionsListUtils.transformedTaxRates(taxRates), (taxRate) => taxRate.modifiedName === selectedTaxRate).value; + return TransactionUtils.calculateTaxAmount(percentage, amount); }; function IOURequestStepTaxRatePage({ @@ -65,7 +64,7 @@ function IOURequestStepTaxRatePage({ } const updateTaxRates = (taxes) => { - const taxAmount = calculateTaxAmount(policyTaxRates, taxes.text, transaction.amount); + const taxAmount = getTaxAmount(policyTaxRates, taxes.text, transaction.amount); const amountInSmallestCurrencyUnits = CurrencyUtils.convertToBackendAmount(Number.parseFloat(taxAmount)); IOU.setMoneyRequestTaxRate(transaction.transactionID, taxes); IOU.setMoneyRequestTaxAmount(transaction.transactionID, amountInSmallestCurrencyUnits); diff --git a/src/pages/iou/steps/MoneyRequestAmountForm.js b/src/pages/iou/steps/MoneyRequestAmountForm.js index 5ffd7d0c87d3..4a901eebe0f0 100644 --- a/src/pages/iou/steps/MoneyRequestAmountForm.js +++ b/src/pages/iou/steps/MoneyRequestAmountForm.js @@ -239,7 +239,7 @@ function MoneyRequestAmountForm({amount, currency, isEditing, forwardedRef, onCu initializeAmount(backendAmount); onSubmitButtonPress({amount: currentAmount, currency}); - }, [onSubmitButtonPress, currentAmount, amount, isTaxAmountForm, formattedTaxAmount, translate, initializeAmount]); + }, [onSubmitButtonPress, currentAmount, amount, currency, isTaxAmountForm, formattedTaxAmount, translate, initializeAmount]); /** * Input handler to check for a forward-delete key (or keyboard shortcut) press. diff --git a/src/types/onyx/PolicyTaxRates.ts b/src/types/onyx/PolicyTaxRates.ts new file mode 100644 index 000000000000..d549b620f51f --- /dev/null +++ b/src/types/onyx/PolicyTaxRates.ts @@ -0,0 +1,14 @@ +type PolicyTaxRate = { + /** Name of a tax */ + name: string; + + /** The value of a tax */ + value: string; + + /** Whether the tax is disabled */ + isDisabled?: boolean; +}; + +type PolicyTaxRates = Record; +export default PolicyTaxRate; +export type {PolicyTaxRates}; From e7f0f16bf5d64e7a0b46af85948a090b5cc132dc Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Wed, 27 Dec 2023 11:30:17 +0100 Subject: [PATCH 70/78] fix test --- tests/unit/OptionsListUtilsTest.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/unit/OptionsListUtilsTest.js b/tests/unit/OptionsListUtilsTest.js index 3860a33e1630..efe7fbca7b14 100644 --- a/tests/unit/OptionsListUtilsTest.js +++ b/tests/unit/OptionsListUtilsTest.js @@ -2030,9 +2030,9 @@ describe('OptionsListUtils', () => { isDisabled: undefined, // creates a data option. data: { - actualName: 'Tax exempt 1', + name: 'Tax exempt 1', code: 'CODE1', - name: 'Tax exempt 1 (0%) • Default', + modifiedName: 'Tax exempt 1 (0%) • Default', value: '0%', }, }, @@ -2043,9 +2043,9 @@ describe('OptionsListUtils', () => { tooltipText: 'Tax option 3 (5%)', isDisabled: undefined, data: { - actualName: 'Tax option 3', + name: 'Tax option 3', code: 'CODE3', - name: 'Tax option 3 (5%)', + modifiedName: 'Tax option 3 (5%)', value: '5%', }, }, @@ -2056,9 +2056,9 @@ describe('OptionsListUtils', () => { tooltipText: 'Tax rate 2 (3%)', isDisabled: undefined, data: { - actualName: 'Tax rate 2', + name: 'Tax rate 2', code: 'CODE2', - name: 'Tax rate 2 (3%)', + modifiedName: 'Tax rate 2 (3%)', value: '3%', }, }, @@ -2080,9 +2080,9 @@ describe('OptionsListUtils', () => { tooltipText: 'Tax rate 2 (3%)', isDisabled: undefined, data: { - actualName: 'Tax rate 2', + name: 'Tax rate 2', code: 'CODE2', - name: 'Tax rate 2 (3%)', + modifiedName: 'Tax rate 2 (3%)', value: '3%', }, }, From 4dadd3b68553b60ce78685e947e855fa323c24dc Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Thu, 28 Dec 2023 18:13:28 +0100 Subject: [PATCH 71/78] calculate default tax on amount request --- .../MoneyRequestConfirmationList.js | 10 ++++--- ...oraryForRefactorRequestConfirmationList.js | 10 ++++--- src/libs/OptionsListUtils.js | 8 +++--- .../iou/request/step/IOURequestStepAmount.js | 27 +++++++++++++++---- .../step/IOURequestStepTaxAmountPage.js | 2 +- .../request/step/IOURequestStepTaxRatePage.js | 6 ++++- 6 files changed, 44 insertions(+), 19 deletions(-) diff --git a/src/components/MoneyRequestConfirmationList.js b/src/components/MoneyRequestConfirmationList.js index 484dfb1cf115..f626facfb497 100755 --- a/src/components/MoneyRequestConfirmationList.js +++ b/src/components/MoneyRequestConfirmationList.js @@ -346,7 +346,9 @@ function MoneyRequestConfirmationList(props) { const canModifyParticipants = !props.isReadOnly && props.canModifyParticipants && props.hasMultipleParticipants; const shouldDisablePaidBySection = canModifyParticipants; - const defaultTaxName = `${props.policyTaxRates.name} • ${translate('common.default')}`; + const defaulTaxKey = props.policyTaxRates.defaultExternalID; + const defaultTaxName = `${props.policyTaxRates.taxes[defaulTaxKey].name} (${props.policyTaxRates.taxes[defaulTaxKey].value}) • ${translate('common.default')}`; + const taxRateTitle = (props.transaction.taxRate && props.transaction.taxRate.text) || defaultTaxName; const optionSelectorSections = useMemo(() => { const sections = []; @@ -752,8 +754,8 @@ function MoneyRequestConfirmationList(props) { {shouldShowTax && ( @@ -770,7 +772,7 @@ function MoneyRequestConfirmationList(props) { diff --git a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js index 75786da9098b..bf7e72f0abea 100755 --- a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js +++ b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js @@ -368,7 +368,9 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ }, [isReadOnly, canModifyParticipants, hasMultipleParticipants]); const shouldDisablePaidBySection = userCanModifyParticipants.current; - const defaultTaxName = `${policyTaxRates.name} • ${translate('common.default')}`; + const defaulTaxKey = policyTaxRates.defaultExternalID; + const defaultTaxName = `${policyTaxRates.taxes[defaulTaxKey].name} (${policyTaxRates.taxes[defaulTaxKey].value}) • ${translate('common.default')}`; + const taxRateTitle = (transaction.taxRate && transaction.taxRate.text) || defaultTaxName; const optionSelectorSections = useMemo(() => { const sections = []; @@ -744,8 +746,8 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ {shouldShowTax && ( @@ -760,7 +762,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ diff --git a/src/libs/OptionsListUtils.js b/src/libs/OptionsListUtils.js index 473272a23af9..e5adcf136cf9 100644 --- a/src/libs/OptionsListUtils.js +++ b/src/libs/OptionsListUtils.js @@ -1127,7 +1127,7 @@ function getTaxRatesSection(policyTaxRates, selectedOptions, searchInputValue) { // If all tax are disabled but there's a previously selected tag, show only the selected tag if (numberOfTaxRates === 0 && selectedOptions.length > 0) { const selectedTaxRateOptions = _.map(selectedOptions, (option) => ({ - name: option.name, + modifiedName: option.name, // Should be marked as enabled to be able to be de-selected isDisabled: false, })); @@ -1169,15 +1169,15 @@ function getTaxRatesSection(policyTaxRates, selectedOptions, searchInputValue) { } const selectedOptionNames = _.map(selectedOptions, (selectedOption) => selectedOption.name); - const filteredTaxRates = _.filter(enabledTaxRates, (taxRate) => !_.includes(selectedOptionNames, taxRate.name)); + const filteredTaxRates = _.filter(enabledTaxRates, (taxRate) => !_.includes(selectedOptionNames, taxRate.modifiedName)); if (!_.isEmpty(selectedOptions)) { const selectedTaxRatesOptions = _.map(selectedOptions, (option) => { const taxRateObject = _.find(taxes, (taxRate) => taxRate.modifiedName === option.name); return { - name: option.name, - enabled: Boolean(taxRateObject && !taxRateObject.isDisabled), + modifiedName: option.name, + isDisabled: Boolean(taxRateObject && taxRateObject.isDisabled), }; }); diff --git a/src/pages/iou/request/step/IOURequestStepAmount.js b/src/pages/iou/request/step/IOURequestStepAmount.js index 0c3b0e97cf50..72fa05bcfd3d 100644 --- a/src/pages/iou/request/step/IOURequestStepAmount.js +++ b/src/pages/iou/request/step/IOURequestStepAmount.js @@ -1,5 +1,7 @@ import {useFocusEffect} from '@react-navigation/native'; import React, {useCallback, useRef} from 'react'; +import {withOnyx} from 'react-native-onyx'; +import taxPropTypes from '@components/taxPropTypes'; import transactionPropTypes from '@components/transactionPropTypes'; import useLocalize from '@hooks/useLocalize'; import compose from '@libs/compose'; @@ -11,6 +13,7 @@ import MoneyRequestAmountForm from '@pages/iou/steps/MoneyRequestAmountForm'; import reportPropTypes from '@pages/reportPropTypes'; import * as IOU from '@userActions/IOU'; import CONST from '@src/CONST'; +import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import IOURequestStepRoutePropTypes from './IOURequestStepRoutePropTypes'; import StepScreenWrapper from './StepScreenWrapper'; @@ -27,15 +30,20 @@ const propTypes = { /** The transaction object being modified in Onyx */ transaction: transactionPropTypes, + + /* Onyx Props */ + /** Collection of tax rates attached to a policy */ + policyTaxRates: taxPropTypes, }; const defaultProps = { report: {}, transaction: {}, + policyTaxRates: {}, }; -const getTaxAmount = (transaction, amount) => { - const percentage = (transaction.taxRate && transaction.taxRate.data.value) || ''; +const getTaxAmount = (transaction, defaultTaxValue, amount) => { + const percentage = transaction.taxRate ? transaction.taxRate.data.value : defaultTaxValue; return TransactionUtils.calculateTaxAmount(percentage, amount); }; @@ -46,6 +54,7 @@ function IOURequestStepAmount({ }, transaction, transaction: {currency: originalCurrency}, + policyTaxRates, }) { const {translate} = useLocalize(); const textInput = useRef(null); @@ -79,8 +88,8 @@ function IOURequestStepAmount({ const navigateToNextPage = ({amount}) => { const amountInSmallestCurrencyUnits = CurrencyUtils.convertToBackendAmount(Number.parseFloat(amount)); - if (backTo) { - const taxAmount = getTaxAmount(transaction, amountInSmallestCurrencyUnits); + if (iouRequestType === CONST.IOU.REQUEST_TYPE.MANUAL || backTo) { + const taxAmount = getTaxAmount(transaction, policyTaxRates.defaultValue, amountInSmallestCurrencyUnits); const taxAmountInSmallestCurrencyUnits = CurrencyUtils.convertToBackendAmount(Number.parseFloat(taxAmount)); IOU.setMoneyRequestTaxAmount(transaction.transactionID, taxAmountInSmallestCurrencyUnits); } @@ -131,4 +140,12 @@ IOURequestStepAmount.propTypes = propTypes; IOURequestStepAmount.defaultProps = defaultProps; IOURequestStepAmount.displayName = 'IOURequestStepAmount'; -export default compose(withWritableReportOrNotFound, withFullTransactionOrNotFound)(IOURequestStepAmount); +export default compose( + withWritableReportOrNotFound, + withFullTransactionOrNotFound, + withOnyx({ + policyTaxRates: { + key: ({report}) => `${ONYXKEYS.COLLECTION.POLICY_TAX_RATE}${report ? report.policyID : '0'}`, + }, + }), +)(IOURequestStepAmount); diff --git a/src/pages/iou/request/step/IOURequestStepTaxAmountPage.js b/src/pages/iou/request/step/IOURequestStepTaxAmountPage.js index 889e93444272..b9915b32187f 100644 --- a/src/pages/iou/request/step/IOURequestStepTaxAmountPage.js +++ b/src/pages/iou/request/step/IOURequestStepTaxAmountPage.js @@ -78,7 +78,7 @@ function IOURequestStepTaxAmountPage({ }; const updateTaxAmount = (currentAmount) => { - const amountInSmallestCurrencyUnits = CurrencyUtils.convertToBackendAmount(Number.parseFloat(currentAmount)); + const amountInSmallestCurrencyUnits = CurrencyUtils.convertToBackendAmount(Number.parseFloat(currentAmount.amount)); IOU.setMoneyRequestTaxAmount(transactionID, amountInSmallestCurrencyUnits); IOU.setMoneyRequestCurrency_temporaryForRefactor(transactionID, currency || CONST.CURRENCY.USD); diff --git a/src/pages/iou/request/step/IOURequestStepTaxRatePage.js b/src/pages/iou/request/step/IOURequestStepTaxRatePage.js index 3312869c46ec..d747e8c7759a 100644 --- a/src/pages/iou/request/step/IOURequestStepTaxRatePage.js +++ b/src/pages/iou/request/step/IOURequestStepTaxRatePage.js @@ -63,6 +63,10 @@ function IOURequestStepTaxRatePage({ Navigation.goBack(ROUTES.MONEY_REQUEST_CONFIRMATION.getRoute(iouType, reportID)); } + const defaulTaxKey = policyTaxRates.defaultExternalID; + const defaultTaxName = `${policyTaxRates.taxes[defaulTaxKey].name} (${policyTaxRates.taxes[defaulTaxKey].value}) • ${translate('common.default')}`; + const selectedTaxRate = (transaction.taxRate && transaction.taxRate.text) || defaultTaxName; + const updateTaxRates = (taxes) => { const taxAmount = getTaxAmount(policyTaxRates, taxes.text, transaction.amount); const amountInSmallestCurrencyUnits = CurrencyUtils.convertToBackendAmount(Number.parseFloat(taxAmount)); @@ -85,7 +89,7 @@ function IOURequestStepTaxRatePage({ onBackButtonPress={() => navigateBack()} /> Date: Fri, 29 Dec 2023 05:45:57 +0100 Subject: [PATCH 72/78] calculate tax amount on request page if only isTaxTrackingEnabled is true --- .../iou/request/step/IOURequestStepAmount.js | 23 ++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/src/pages/iou/request/step/IOURequestStepAmount.js b/src/pages/iou/request/step/IOURequestStepAmount.js index 72fa05bcfd3d..d231ff080906 100644 --- a/src/pages/iou/request/step/IOURequestStepAmount.js +++ b/src/pages/iou/request/step/IOURequestStepAmount.js @@ -1,4 +1,6 @@ import {useFocusEffect} from '@react-navigation/native'; +import lodashGet from 'lodash/get'; +import PropTypes from 'prop-types'; import React, {useCallback, useRef} from 'react'; import {withOnyx} from 'react-native-onyx'; import taxPropTypes from '@components/taxPropTypes'; @@ -7,6 +9,7 @@ import useLocalize from '@hooks/useLocalize'; import compose from '@libs/compose'; import * as CurrencyUtils from '@libs/CurrencyUtils'; import Navigation from '@libs/Navigation/Navigation'; +import * as ReportUtils from '@libs/ReportUtils'; import {getRequestType} from '@libs/TransactionUtils'; import * as TransactionUtils from '@libs/TransactionUtils'; import MoneyRequestAmountForm from '@pages/iou/steps/MoneyRequestAmountForm'; @@ -34,16 +37,23 @@ const propTypes = { /* Onyx Props */ /** Collection of tax rates attached to a policy */ policyTaxRates: taxPropTypes, + + /** The policy of the report */ + policy: PropTypes.shape({ + /** Is Tax tracking Enabled */ + isTaxTrackingEnabled: PropTypes.bool, + }), }; const defaultProps = { report: {}, transaction: {}, policyTaxRates: {}, + policy: {}, }; const getTaxAmount = (transaction, defaultTaxValue, amount) => { - const percentage = transaction.taxRate ? transaction.taxRate.data.value : defaultTaxValue; + const percentage = (transaction.taxRate ? transaction.taxRate.data.value : defaultTaxValue) || ''; return TransactionUtils.calculateTaxAmount(percentage, amount); }; @@ -55,6 +65,7 @@ function IOURequestStepAmount({ transaction, transaction: {currency: originalCurrency}, policyTaxRates, + policy, }) { const {translate} = useLocalize(); const textInput = useRef(null); @@ -62,6 +73,9 @@ function IOURequestStepAmount({ const iouRequestType = getRequestType(transaction); const currency = selectedCurrency || originalCurrency; + const isPolicyExpenseChat = ReportUtils.isPolicyExpenseChat(ReportUtils.getRootParentReport(report)); + const isTaxTrackingEnabled = isPolicyExpenseChat && policy.isTaxTrackingEnabled; + useFocusEffect( useCallback(() => { focusTimeoutRef.current = setTimeout(() => textInput.current && textInput.current.focus(), CONST.ANIMATED_TRANSITION); @@ -88,7 +102,7 @@ function IOURequestStepAmount({ const navigateToNextPage = ({amount}) => { const amountInSmallestCurrencyUnits = CurrencyUtils.convertToBackendAmount(Number.parseFloat(amount)); - if (iouRequestType === CONST.IOU.REQUEST_TYPE.MANUAL || backTo) { + if ((iouRequestType === CONST.IOU.REQUEST_TYPE.MANUAL || backTo) && isTaxTrackingEnabled) { const taxAmount = getTaxAmount(transaction, policyTaxRates.defaultValue, amountInSmallestCurrencyUnits); const taxAmountInSmallestCurrencyUnits = CurrencyUtils.convertToBackendAmount(Number.parseFloat(taxAmount)); IOU.setMoneyRequestTaxAmount(transaction.transactionID, taxAmountInSmallestCurrencyUnits); @@ -145,7 +159,10 @@ export default compose( withFullTransactionOrNotFound, withOnyx({ policyTaxRates: { - key: ({report}) => `${ONYXKEYS.COLLECTION.POLICY_TAX_RATE}${report ? report.policyID : '0'}`, + key: ({report}) => `${ONYXKEYS.COLLECTION.POLICY_TAX_RATE}${lodashGet(report, 'policyID', '0')}`, + }, + policy: { + key: ({report}) => `${ONYXKEYS.COLLECTION.POLICY}${lodashGet(report, 'policyID', '0')}`, }, }), )(IOURequestStepAmount); From 3b304767ce8d65dea9e7b882e0ef836a0210f21e Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Fri, 29 Dec 2023 11:51:20 +0100 Subject: [PATCH 73/78] fix app crash on request money --- .../MoneyTemporaryForRefactorRequestConfirmationList.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js index bf7e72f0abea..91f34899ad05 100755 --- a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js +++ b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js @@ -285,6 +285,10 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ isDistanceRequest ? currency : iouCurrencyCode, ); const formattedTaxAmount = CurrencyUtils.convertToDisplayString(transaction.taxAmount, iouCurrencyCode); + const defaulTaxKey = policyTaxRates.defaultExternalID; + const defaultTaxName = defaulTaxKey && `${policyTaxRates.taxes[defaulTaxKey].name} (${policyTaxRates.taxes[defaulTaxKey].value}) • ${translate('common.default')}`; + const taxRateTitle = (transaction.taxRate && transaction.taxRate.text) || defaultTaxName; + const isFocused = useIsFocused(); const [formError, setFormError] = useState(''); @@ -368,10 +372,6 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ }, [isReadOnly, canModifyParticipants, hasMultipleParticipants]); const shouldDisablePaidBySection = userCanModifyParticipants.current; - const defaulTaxKey = policyTaxRates.defaultExternalID; - const defaultTaxName = `${policyTaxRates.taxes[defaulTaxKey].name} (${policyTaxRates.taxes[defaulTaxKey].value}) • ${translate('common.default')}`; - const taxRateTitle = (transaction.taxRate && transaction.taxRate.text) || defaultTaxName; - const optionSelectorSections = useMemo(() => { const sections = []; const unselectedParticipants = _.filter(pickedParticipants, (participant) => !participant.selected); From 6ac4eb944031f3a860411245d2eb841f2004f84d Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Fri, 29 Dec 2023 12:05:29 +0100 Subject: [PATCH 74/78] Update src/components/MoneyRequestConfirmationList.js Co-authored-by: Monil Bhavsar --- src/components/MoneyRequestConfirmationList.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/MoneyRequestConfirmationList.js b/src/components/MoneyRequestConfirmationList.js index f626facfb497..ca258cb7d94c 100755 --- a/src/components/MoneyRequestConfirmationList.js +++ b/src/components/MoneyRequestConfirmationList.js @@ -346,7 +346,7 @@ function MoneyRequestConfirmationList(props) { const canModifyParticipants = !props.isReadOnly && props.canModifyParticipants && props.hasMultipleParticipants; const shouldDisablePaidBySection = canModifyParticipants; - const defaulTaxKey = props.policyTaxRates.defaultExternalID; + const defaultTaxKey = props.policyTaxRates.defaultExternalID; const defaultTaxName = `${props.policyTaxRates.taxes[defaulTaxKey].name} (${props.policyTaxRates.taxes[defaulTaxKey].value}) • ${translate('common.default')}`; const taxRateTitle = (props.transaction.taxRate && props.transaction.taxRate.text) || defaultTaxName; From 7a1345bf2154c3355cad57c3fcfa51aa5eee5da9 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Fri, 29 Dec 2023 12:51:35 +0100 Subject: [PATCH 75/78] fix typo --- src/components/MoneyRequestConfirmationList.js | 8 ++++---- .../MoneyTemporaryForRefactorRequestConfirmationList.js | 5 +++-- src/pages/iou/request/step/IOURequestStepTaxRatePage.js | 2 +- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/components/MoneyRequestConfirmationList.js b/src/components/MoneyRequestConfirmationList.js index ca258cb7d94c..a3c82cbcd876 100755 --- a/src/components/MoneyRequestConfirmationList.js +++ b/src/components/MoneyRequestConfirmationList.js @@ -263,6 +263,10 @@ function MoneyRequestConfirmationList(props) { ); const formattedTaxAmount = CurrencyUtils.convertToDisplayString(props.transaction.taxAmount, props.iouCurrencyCode); + const defaultTaxKey = props.policyTaxRates.defaultExternalID; + const defaultTaxName = (defaultTaxKey && `${props.policyTaxRates.taxes[defaultTaxKey].name} (${props.policyTaxRates.taxes[defaultTaxKey].value}) • ${translate('common.default')}`) || ''; + const taxRateTitle = (props.transaction.taxRate && props.transaction.taxRate.text) || defaultTaxName; + const isFocused = useIsFocused(); const [formError, setFormError] = useState(''); @@ -346,10 +350,6 @@ function MoneyRequestConfirmationList(props) { const canModifyParticipants = !props.isReadOnly && props.canModifyParticipants && props.hasMultipleParticipants; const shouldDisablePaidBySection = canModifyParticipants; - const defaultTaxKey = props.policyTaxRates.defaultExternalID; - const defaultTaxName = `${props.policyTaxRates.taxes[defaulTaxKey].name} (${props.policyTaxRates.taxes[defaulTaxKey].value}) • ${translate('common.default')}`; - const taxRateTitle = (props.transaction.taxRate && props.transaction.taxRate.text) || defaultTaxName; - const optionSelectorSections = useMemo(() => { const sections = []; const unselectedParticipants = _.filter(props.selectedParticipants, (participant) => !participant.selected); diff --git a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js index 91f34899ad05..a58fbfbf80a6 100755 --- a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js +++ b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js @@ -285,8 +285,9 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ isDistanceRequest ? currency : iouCurrencyCode, ); const formattedTaxAmount = CurrencyUtils.convertToDisplayString(transaction.taxAmount, iouCurrencyCode); - const defaulTaxKey = policyTaxRates.defaultExternalID; - const defaultTaxName = defaulTaxKey && `${policyTaxRates.taxes[defaulTaxKey].name} (${policyTaxRates.taxes[defaulTaxKey].value}) • ${translate('common.default')}`; + + const defaultTaxKey = policyTaxRates.defaultExternalID; + const defaultTaxName = (defaultTaxKey && `${policyTaxRates.taxes[defaultTaxKey].name} (${policyTaxRates.taxes[defaultTaxKey].value}) • ${translate('common.default')}`) || ''; const taxRateTitle = (transaction.taxRate && transaction.taxRate.text) || defaultTaxName; const isFocused = useIsFocused(); diff --git a/src/pages/iou/request/step/IOURequestStepTaxRatePage.js b/src/pages/iou/request/step/IOURequestStepTaxRatePage.js index d747e8c7759a..cf7f9f244b0b 100644 --- a/src/pages/iou/request/step/IOURequestStepTaxRatePage.js +++ b/src/pages/iou/request/step/IOURequestStepTaxRatePage.js @@ -64,7 +64,7 @@ function IOURequestStepTaxRatePage({ } const defaulTaxKey = policyTaxRates.defaultExternalID; - const defaultTaxName = `${policyTaxRates.taxes[defaulTaxKey].name} (${policyTaxRates.taxes[defaulTaxKey].value}) • ${translate('common.default')}`; + const defaultTaxName = (defaulTaxKey && `${policyTaxRates.taxes[defaulTaxKey].name} (${policyTaxRates.taxes[defaulTaxKey].value}) • ${translate('common.default')}`) || ''; const selectedTaxRate = (transaction.taxRate && transaction.taxRate.text) || defaultTaxName; const updateTaxRates = (taxes) => { From e64a73ed13497eedc13e58d20d94047f5a1c9b2e Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Fri, 29 Dec 2023 13:07:32 +0100 Subject: [PATCH 76/78] fix typo --- src/pages/iou/request/step/IOURequestStepTaxRatePage.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/iou/request/step/IOURequestStepTaxRatePage.js b/src/pages/iou/request/step/IOURequestStepTaxRatePage.js index cf7f9f244b0b..bae08cd8cb62 100644 --- a/src/pages/iou/request/step/IOURequestStepTaxRatePage.js +++ b/src/pages/iou/request/step/IOURequestStepTaxRatePage.js @@ -63,8 +63,8 @@ function IOURequestStepTaxRatePage({ Navigation.goBack(ROUTES.MONEY_REQUEST_CONFIRMATION.getRoute(iouType, reportID)); } - const defaulTaxKey = policyTaxRates.defaultExternalID; - const defaultTaxName = (defaulTaxKey && `${policyTaxRates.taxes[defaulTaxKey].name} (${policyTaxRates.taxes[defaulTaxKey].value}) • ${translate('common.default')}`) || ''; + const defaultTaxKey = policyTaxRates.defaultExternalID; + const defaultTaxName = (defaultTaxKey && `${policyTaxRates.taxes[defaultTaxKey].name} (${policyTaxRates.taxes[defaultTaxKey].value}) • ${translate('common.default')}`) || ''; const selectedTaxRate = (transaction.taxRate && transaction.taxRate.text) || defaultTaxName; const updateTaxRates = (taxes) => { From 0d69fac896fec9c9ac850f52a6fc38319f815cd4 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Fri, 29 Dec 2023 13:30:41 +0100 Subject: [PATCH 77/78] fix prop types --- src/components/taxPropTypes.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/taxPropTypes.js b/src/components/taxPropTypes.js index d0656fef0433..98c3a4a75257 100644 --- a/src/components/taxPropTypes.js +++ b/src/components/taxPropTypes.js @@ -2,10 +2,10 @@ import PropTypes from 'prop-types'; const taxPropTypes = PropTypes.shape({ /** Name of a tax */ - name: PropTypes.string.isRequired, + name: PropTypes.string, /** The value of a tax */ - value: PropTypes.string.isRequired, + value: PropTypes.string, /** Whether the tax is disabled */ isDisabled: PropTypes.bool, @@ -13,7 +13,7 @@ const taxPropTypes = PropTypes.shape({ export default PropTypes.shape({ /** Name of the tax */ - name: PropTypes.string.isRequired, + name: PropTypes.string, /** Default policy tax ID */ defaultExternalID: PropTypes.string, From 40cd5a3fa269f5fd9e03c943b954819a31a6ee66 Mon Sep 17 00:00:00 2001 From: Etotaziba Olei Date: Sat, 30 Dec 2023 16:05:58 +0100 Subject: [PATCH 78/78] use default tax amount to check invalidTaxAmont --- .../iou/request/step/IOURequestStepAmount.js | 5 ++-- .../step/IOURequestStepTaxAmountPage.js | 27 ++++++++++++++++++- src/pages/iou/steps/MoneyRequestAmountForm.js | 14 ++++++---- 3 files changed, 37 insertions(+), 9 deletions(-) diff --git a/src/pages/iou/request/step/IOURequestStepAmount.js b/src/pages/iou/request/step/IOURequestStepAmount.js index d231ff080906..84e0ac8533c5 100644 --- a/src/pages/iou/request/step/IOURequestStepAmount.js +++ b/src/pages/iou/request/step/IOURequestStepAmount.js @@ -1,5 +1,4 @@ import {useFocusEffect} from '@react-navigation/native'; -import lodashGet from 'lodash/get'; import PropTypes from 'prop-types'; import React, {useCallback, useRef} from 'react'; import {withOnyx} from 'react-native-onyx'; @@ -159,10 +158,10 @@ export default compose( withFullTransactionOrNotFound, withOnyx({ policyTaxRates: { - key: ({report}) => `${ONYXKEYS.COLLECTION.POLICY_TAX_RATE}${lodashGet(report, 'policyID', '0')}`, + key: ({report}) => `${ONYXKEYS.COLLECTION.POLICY_TAX_RATE}${report ? report.policyID : '0'}`, }, policy: { - key: ({report}) => `${ONYXKEYS.COLLECTION.POLICY}${lodashGet(report, 'policyID', '0')}`, + key: ({report}) => `${ONYXKEYS.COLLECTION.POLICY}${report ? report.policyID : '0'}`, }, }), )(IOURequestStepAmount); diff --git a/src/pages/iou/request/step/IOURequestStepTaxAmountPage.js b/src/pages/iou/request/step/IOURequestStepTaxAmountPage.js index b9915b32187f..8ee3abb56d00 100644 --- a/src/pages/iou/request/step/IOURequestStepTaxAmountPage.js +++ b/src/pages/iou/request/step/IOURequestStepTaxAmountPage.js @@ -1,9 +1,11 @@ import {useFocusEffect} from '@react-navigation/native'; import React, {useCallback, useRef} from 'react'; import {View} from 'react-native'; +import {withOnyx} from 'react-native-onyx'; import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import ScreenWrapper from '@components/ScreenWrapper'; +import taxPropTypes from '@components/taxPropTypes'; import transactionPropTypes from '@components/transactionPropTypes'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; @@ -11,10 +13,12 @@ import compose from '@libs/compose'; import * as CurrencyUtils from '@libs/CurrencyUtils'; import * as IOUUtils from '@libs/IOUUtils'; import Navigation from '@libs/Navigation/Navigation'; +import * as TransactionUtils from '@libs/TransactionUtils'; import MoneyRequestAmountForm from '@pages/iou/steps/MoneyRequestAmountForm'; import reportPropTypes from '@pages/reportPropTypes'; import * as IOU from '@userActions/IOU'; import CONST from '@src/CONST'; +import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import IOURequestStepRoutePropTypes from './IOURequestStepRoutePropTypes'; import withFullTransactionOrNotFound from './withFullTransactionOrNotFound'; @@ -30,11 +34,21 @@ const propTypes = { /** The transaction object being modified in Onyx */ transaction: transactionPropTypes, + + /* Onyx Props */ + /** Collection of tax rates attached to a policy */ + policyTaxRates: taxPropTypes, }; const defaultProps = { report: {}, transaction: {}, + policyTaxRates: {}, +}; + +const getTaxAmount = (transaction, defaultTaxValue) => { + const percentage = (transaction.taxRate ? transaction.taxRate.data.value : defaultTaxValue) || ''; + return CurrencyUtils.convertToBackendAmount(Number.parseFloat(TransactionUtils.calculateTaxAmount(percentage, transaction.amount))); }; function IOURequestStepTaxAmountPage({ @@ -44,6 +58,7 @@ function IOURequestStepTaxAmountPage({ transaction, transaction: {currency: originalCurrency}, report, + policyTaxRates, }) { const {translate} = useLocalize(); const styles = useThemeStyles(); @@ -107,6 +122,8 @@ function IOURequestStepTaxAmountPage({ isEditing={isEditing} currency={currency} amount={transaction.taxAmount} + taxAmount={getTaxAmount(transaction, policyTaxRates.defaultValue)} + transaction={transaction} ref={(e) => (textInput.current = e)} onCurrencyButtonPress={navigateToCurrencySelectionPage} onSubmitButtonPress={updateTaxAmount} @@ -138,4 +155,12 @@ IOURequestStepTaxAmountPage.propTypes = propTypes; IOURequestStepTaxAmountPage.defaultProps = defaultProps; IOURequestStepTaxAmountPage.displayName = 'IOURequestStepTaxAmountPage'; -export default compose(withWritableReportOrNotFound, withFullTransactionOrNotFound)(IOURequestStepTaxAmountPage); +export default compose( + withWritableReportOrNotFound, + withFullTransactionOrNotFound, + withOnyx({ + policyTaxRates: { + key: ({report}) => `${ONYXKEYS.COLLECTION.POLICY_TAX_RATE}${report ? report.policyID : '0'}`, + }, + }), +)(IOURequestStepTaxAmountPage); diff --git a/src/pages/iou/steps/MoneyRequestAmountForm.js b/src/pages/iou/steps/MoneyRequestAmountForm.js index 4a901eebe0f0..536944f4a2d8 100644 --- a/src/pages/iou/steps/MoneyRequestAmountForm.js +++ b/src/pages/iou/steps/MoneyRequestAmountForm.js @@ -23,6 +23,9 @@ const propTypes = { /** IOU amount saved in Onyx */ amount: PropTypes.number, + /** Calculated tax amount based on selected tax rate */ + taxAmount: PropTypes.number, + /** Currency chosen by user or saved in Onyx */ currency: PropTypes.string, @@ -44,6 +47,7 @@ const propTypes = { const defaultProps = { amount: 0, + taxAmount: 0, currency: CONST.CURRENCY.USD, forwardedRef: null, isEditing: false, @@ -64,13 +68,13 @@ const getNewSelection = (oldSelection, prevLength, newLength) => { }; const isAmountInvalid = (amount) => !amount.length || parseFloat(amount) < 0.01; -const isTaxAmountInvalid = (currentAmount, amount, isTaxAmountForm) => isTaxAmountForm && currentAmount > CurrencyUtils.convertToFrontendAmount(amount); +const isTaxAmountInvalid = (currentAmount, taxAmount, isTaxAmountForm) => isTaxAmountForm && currentAmount > CurrencyUtils.convertToFrontendAmount(taxAmount); const AMOUNT_VIEW_ID = 'amountView'; const NUM_PAD_CONTAINER_VIEW_ID = 'numPadContainerView'; const NUM_PAD_VIEW_ID = 'numPadView'; -function MoneyRequestAmountForm({amount, currency, isEditing, forwardedRef, onCurrencyButtonPress, onSubmitButtonPress, selectedTab}) { +function MoneyRequestAmountForm({amount, taxAmount, currency, isEditing, forwardedRef, onCurrencyButtonPress, onSubmitButtonPress, selectedTab}) { const styles = useThemeStyles(); const {isExtraSmallScreenHeight} = useWindowDimensions(); const {translate, toLocaleDigit, numberFormat} = useLocalize(); @@ -92,7 +96,7 @@ function MoneyRequestAmountForm({amount, currency, isEditing, forwardedRef, onCu const forwardDeletePressedRef = useRef(false); - const formattedTaxAmount = CurrencyUtils.convertToDisplayString(amount, currency); + const formattedTaxAmount = CurrencyUtils.convertToDisplayString(taxAmount, currency); /** * Event occurs when a user presses a mouse button over an DOM element. @@ -228,7 +232,7 @@ function MoneyRequestAmountForm({amount, currency, isEditing, forwardedRef, onCu return; } - if (isTaxAmountInvalid(currentAmount, amount, isTaxAmountForm)) { + if (isTaxAmountInvalid(currentAmount, taxAmount, isTaxAmountForm)) { setFormError(translate('iou.error.invalidTaxAmount', {amount: formattedTaxAmount})); return; } @@ -239,7 +243,7 @@ function MoneyRequestAmountForm({amount, currency, isEditing, forwardedRef, onCu initializeAmount(backendAmount); onSubmitButtonPress({amount: currentAmount, currency}); - }, [onSubmitButtonPress, currentAmount, amount, currency, isTaxAmountForm, formattedTaxAmount, translate, initializeAmount]); + }, [onSubmitButtonPress, currentAmount, taxAmount, currency, isTaxAmountForm, formattedTaxAmount, translate, initializeAmount]); /** * Input handler to check for a forward-delete key (or keyboard shortcut) press.