diff --git a/src/components/ReportActionItem/MoneyRequestView.tsx b/src/components/ReportActionItem/MoneyRequestView.tsx index df5be02ca64f..3fa6f1872ca3 100644 --- a/src/components/ReportActionItem/MoneyRequestView.tsx +++ b/src/components/ReportActionItem/MoneyRequestView.tsx @@ -152,10 +152,11 @@ function MoneyRequestView({ Navigation.dismissModal(); return; } - IOU.updateMoneyRequestBillable(transaction?.transactionID ?? '', report?.reportID, newBillable); + // @ts-expect-error: the type used across the app for policyTags is not what is returned by Onyx, PolicyTagList represents that, but existing policy tag utils need a refactor to fix this + IOU.updateMoneyRequestBillable(transaction?.transactionID ?? '', report?.reportID, newBillable, policy, policyTags, policyCategories); Navigation.dismissModal(); }, - [transaction, report], + [transaction, report, policy, policyTags, policyCategories], ); if (isCardTransaction) { diff --git a/src/libs/Violations/ViolationsUtils.ts b/src/libs/Violations/ViolationsUtils.ts index 9a05ba4d87ed..992449267934 100644 --- a/src/libs/Violations/ViolationsUtils.ts +++ b/src/libs/Violations/ViolationsUtils.ts @@ -4,7 +4,7 @@ import type {OnyxUpdate} from 'react-native-onyx'; import type {Phrase, PhraseParameters} from '@libs/Localize'; import type {TranslationPaths} from '@src/languages/types'; import ONYXKEYS from '@src/ONYXKEYS'; -import type {PolicyCategories, PolicyTags, Transaction, TransactionViolation} from '@src/types/onyx'; +import type {PolicyCategories, PolicyTagList, Transaction, TransactionViolation} from '@src/types/onyx'; const ViolationsUtils = { /** @@ -12,10 +12,10 @@ const ViolationsUtils = { * violations. */ getViolationsOnyxData( - transaction: Transaction, + updatedTransaction: Transaction, transactionViolations: TransactionViolation[], policyRequiresTags: boolean, - policyTags: PolicyTags, + policyTagList: PolicyTagList, policyRequiresCategories: boolean, policyCategories: PolicyCategories, ): OnyxUpdate { @@ -24,15 +24,16 @@ const ViolationsUtils = { if (policyRequiresCategories) { const hasCategoryOutOfPolicyViolation = transactionViolations.some((violation) => violation.name === 'categoryOutOfPolicy'); const hasMissingCategoryViolation = transactionViolations.some((violation) => violation.name === 'missingCategory'); - const isCategoryInPolicy = !!policyCategories[transaction.category ?? '']?.enabled; + const categoryKey = updatedTransaction.category; + const isCategoryInPolicy = categoryKey ? policyCategories?.[categoryKey]?.enabled : false; // Add 'categoryOutOfPolicy' violation if category is not in policy - if (!hasCategoryOutOfPolicyViolation && transaction.category && !isCategoryInPolicy) { + if (!hasCategoryOutOfPolicyViolation && categoryKey && !isCategoryInPolicy) { newTransactionViolations.push({name: 'categoryOutOfPolicy', type: 'violation', userMessage: ''}); } // Remove 'categoryOutOfPolicy' violation if category is in policy - if (hasCategoryOutOfPolicyViolation && transaction.category && isCategoryInPolicy) { + if (hasCategoryOutOfPolicyViolation && updatedTransaction.category && isCategoryInPolicy) { newTransactionViolations = reject(newTransactionViolations, {name: 'categoryOutOfPolicy'}); } @@ -42,23 +43,25 @@ const ViolationsUtils = { } // Add 'missingCategory' violation if category is required and not set - if (!hasMissingCategoryViolation && policyRequiresCategories && !transaction.category) { + if (!hasMissingCategoryViolation && policyRequiresCategories && !categoryKey) { newTransactionViolations.push({name: 'missingCategory', type: 'violation', userMessage: ''}); } } if (policyRequiresTags) { + const policyTagListName = Object.keys(policyTagList)[0]; + const policyTags = policyTagList[policyTagListName]?.tags; const hasTagOutOfPolicyViolation = transactionViolations.some((violation) => violation.name === 'tagOutOfPolicy'); const hasMissingTagViolation = transactionViolations.some((violation) => violation.name === 'missingTag'); - const isTagInPolicy = !!policyTags[transaction.tag ?? '']?.enabled; + const isTagInPolicy = policyTags ? !!policyTags[updatedTransaction.tag ?? '']?.enabled : false; // Add 'tagOutOfPolicy' violation if tag is not in policy - if (!hasTagOutOfPolicyViolation && transaction.tag && !isTagInPolicy) { + if (!hasTagOutOfPolicyViolation && updatedTransaction.tag && !isTagInPolicy) { newTransactionViolations.push({name: 'tagOutOfPolicy', type: 'violation', userMessage: ''}); } // Remove 'tagOutOfPolicy' violation if tag is in policy - if (hasTagOutOfPolicyViolation && transaction.tag && isTagInPolicy) { + if (hasTagOutOfPolicyViolation && updatedTransaction.tag && isTagInPolicy) { newTransactionViolations = reject(newTransactionViolations, {name: 'tagOutOfPolicy'}); } @@ -68,14 +71,14 @@ const ViolationsUtils = { } // Add 'missingTag violation' if tag is required and not set - if (!hasMissingTagViolation && !transaction.tag && policyRequiresTags) { + if (!hasMissingTagViolation && !updatedTransaction.tag && policyRequiresTags) { newTransactionViolations.push({name: 'missingTag', type: 'violation', userMessage: ''}); } } return { onyxMethod: Onyx.METHOD.SET, - key: `${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${transaction.transactionID}`, + key: `${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${updatedTransaction.transactionID}`, value: newTransactionViolations, }; }, diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 05fd97f494b7..d7ac6f3c9361 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -393,9 +393,9 @@ function buildOnyxDataForMoneyRequest( optimisticPolicyRecentlyUsedTags: OnyxTypes.RecentlyUsedTags, isNewChatReport: boolean, shouldCreateNewMoneyRequestReport: boolean, - policy?: OnyxTypes.Policy | EmptyObject, - policyTags?: OnyxTypes.PolicyTags, - policyCategories?: OnyxTypes.PolicyCategories, + policy?: OnyxEntry, + policyTags?: OnyxEntry, + policyCategories?: OnyxEntry, optimisticNextStep?: OnyxTypes.ReportNextStep | null, needsToBeManuallySubmitted = true, ): [OnyxUpdate[], OnyxUpdate[], OnyxUpdate[]] { @@ -710,9 +710,9 @@ function getMoneyRequestInformation( category: string | undefined, tag: string | undefined, billable: boolean | undefined, - policy: OnyxTypes.Policy | EmptyObject | undefined, - policyTags: OnyxTypes.PolicyTags | undefined, - policyCategories: OnyxTypes.PolicyCategories | undefined, + policy: OnyxEntry | undefined, + policyTags: OnyxEntry | undefined, + policyCategories: OnyxEntry | undefined, payeeAccountID = userAccountID, payeeEmail = currentUserEmail, moneyRequestReportID = '', @@ -927,9 +927,9 @@ function createDistanceRequest( merchant: string, billable: boolean | undefined, validWaypoints: WaypointCollection, - policy: OnyxTypes.Policy | EmptyObject | undefined, - policyTags: OnyxTypes.PolicyTags, - policyCategories: OnyxTypes.PolicyCategories, + policy: OnyxEntry, + policyTags: OnyxEntry, + policyCategories: OnyxEntry, ) { // If the report is an iou or expense report, we should get the linked chat report to be passed to the getMoneyRequestInformation function const isMoneyRequestReport = ReportUtils.isMoneyRequestReport(report); @@ -984,8 +984,13 @@ function createDistanceRequest( } /** + * @param transactionID + * @param transactionThreadReportID * @param transactionChanges * @param [transactionChanges.created] Present when updated the date field + * @param policy May be undefined, an empty object, or an object matching the Policy type (src/types/onyx/Policy.ts) + * @param policyTags + * @param policyCategories * @param onlyIncludeChangedFields * When 'true', then the returned params will only include the transaction details for the fields that were changed. * When `false`, then the returned params will include all the transaction details, regardless of which fields were changed. @@ -995,6 +1000,9 @@ function getUpdateMoneyRequestParams( transactionID: string, transactionThreadReportID: string, transactionChanges: TransactionChanges, + policy: OnyxEntry, + policyTags: OnyxEntry, + policyCategories: OnyxEntry, onlyIncludeChangedFields: boolean, ): UpdateMoneyRequestData { const optimisticData: OnyxUpdate[] = []; @@ -1210,6 +1218,25 @@ function getUpdateMoneyRequestParams( }); } + if (policy?.id && updatedTransaction) { + const currentTransactionViolations = allTransactionViolations[`${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${transactionID}`] ?? []; + optimisticData.push( + ViolationsUtils.getViolationsOnyxData( + updatedTransaction, + currentTransactionViolations, + !!policy.requiresTag, + policyTags ?? {}, + !!policy.requiresCategory, + policyCategories ?? {}, + ), + ); + failureData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${transactionID}`, + value: currentTransactionViolations, + }); + } + return { params, onyxData: {optimisticData, successData, failureData}, @@ -1217,71 +1244,127 @@ function getUpdateMoneyRequestParams( } /** Updates the created date of a money request */ -function updateMoneyRequestDate(transactionID: string, transactionThreadReportID: string, value: string) { +function updateMoneyRequestDate( + transactionID: string, + transactionThreadReportID: string, + value: string, + policy: OnyxEntry, + policyTags: OnyxEntry, + policyCategories: OnyxEntry, +) { const transactionChanges: TransactionChanges = { created: value, }; - const {params, onyxData} = getUpdateMoneyRequestParams(transactionID, transactionThreadReportID, transactionChanges, true); + const {params, onyxData} = getUpdateMoneyRequestParams(transactionID, transactionThreadReportID, transactionChanges, policy, policyTags, policyCategories, true); API.write(WRITE_COMMANDS.UPDATE_MONEY_REQUEST_DATE, params, onyxData); } /** Updates the billable field of a money request */ -function updateMoneyRequestBillable(transactionID: string, transactionThreadReportID: string, value: boolean) { +function updateMoneyRequestBillable( + transactionID: string, + transactionThreadReportID: string, + value: boolean, + policy: OnyxEntry, + policyTags: OnyxEntry, + policyCategories: OnyxEntry, +) { const transactionChanges: TransactionChanges = { billable: value, }; - const {params, onyxData} = getUpdateMoneyRequestParams(transactionID, transactionThreadReportID, transactionChanges, true); + const {params, onyxData} = getUpdateMoneyRequestParams(transactionID, transactionThreadReportID, transactionChanges, policy, policyTags, policyCategories, true); API.write(WRITE_COMMANDS.UPDATE_MONEY_REQUEST_BILLABLE, params, onyxData); } /** Updates the merchant field of a money request */ -function updateMoneyRequestMerchant(transactionID: string, transactionThreadReportID: string, value: string) { +function updateMoneyRequestMerchant( + transactionID: string, + transactionThreadReportID: string, + value: string, + policy: OnyxEntry, + policyTags: OnyxEntry, + policyCategories: OnyxEntry, +) { const transactionChanges: TransactionChanges = { merchant: value, }; - const {params, onyxData} = getUpdateMoneyRequestParams(transactionID, transactionThreadReportID, transactionChanges, true); + const {params, onyxData} = getUpdateMoneyRequestParams(transactionID, transactionThreadReportID, transactionChanges, policy, policyTags, policyCategories, true); API.write(WRITE_COMMANDS.UPDATE_MONEY_REQUEST_MERCHANT, params, onyxData); } /** Updates the tag of a money request */ -function updateMoneyRequestTag(transactionID: string, transactionThreadReportID: string, tag: string) { +function updateMoneyRequestTag( + transactionID: string, + transactionThreadReportID: string, + tag: string, + policy: OnyxEntry, + policyTags: OnyxEntry, + policyCategories: OnyxEntry, +) { const transactionChanges: TransactionChanges = { tag, }; - const {params, onyxData} = getUpdateMoneyRequestParams(transactionID, transactionThreadReportID, transactionChanges, true); + const {params, onyxData} = getUpdateMoneyRequestParams(transactionID, transactionThreadReportID, transactionChanges, policy, policyTags, policyCategories, true); API.write(WRITE_COMMANDS.UPDATE_MONEY_REQUEST_TAG, params, onyxData); } /** Updates the waypoints of a distance money request */ -function updateMoneyRequestDistance(transactionID: string, transactionThreadReportID: string, waypoints: WaypointCollection) { +function updateMoneyRequestDistance( + transactionID: string, + transactionThreadReportID: string, + waypoints: WaypointCollection, + policy: OnyxEntry, + policyTags: OnyxEntry, + policyCategories: OnyxEntry, +) { const transactionChanges: TransactionChanges = { waypoints, }; - const {params, onyxData} = getUpdateMoneyRequestParams(transactionID, transactionThreadReportID, transactionChanges, true); + const {params, onyxData} = getUpdateMoneyRequestParams(transactionID, transactionThreadReportID, transactionChanges, policy, policyTags, policyCategories, true); API.write(WRITE_COMMANDS.UPDATE_MONEY_REQUEST_DISTANCE, params, onyxData); } /** Updates the category of a money request */ -function updateMoneyRequestCategory(transactionID: string, transactionThreadReportID: string, category: string) { +function updateMoneyRequestCategory( + transactionID: string, + transactionThreadReportID: string, + category: string, + policy: OnyxEntry, + policyTags: OnyxEntry, + policyCategories: OnyxEntry, +) { const transactionChanges: TransactionChanges = { category, }; - const {params, onyxData} = getUpdateMoneyRequestParams(transactionID, transactionThreadReportID, transactionChanges, true); + const {params, onyxData} = getUpdateMoneyRequestParams(transactionID, transactionThreadReportID, transactionChanges, policy, policyTags, policyCategories, true); API.write(WRITE_COMMANDS.UPDATE_MONEY_REQUEST_CATEGORY, params, onyxData); } /** Updates the description of a money request */ -function updateMoneyRequestDescription(transactionID: string, transactionThreadReportID: string, comment: string) { +function updateMoneyRequestDescription( + transactionID: string, + transactionThreadReportID: string, + comment: string, + policy: OnyxEntry, + policyTags: OnyxEntry, + policyCategories: OnyxEntry, +) { const transactionChanges: TransactionChanges = { comment, }; - const {params, onyxData} = getUpdateMoneyRequestParams(transactionID, transactionThreadReportID, transactionChanges, true); + const {params, onyxData} = getUpdateMoneyRequestParams(transactionID, transactionThreadReportID, transactionChanges, policy, policyTags, policyCategories, true); API.write(WRITE_COMMANDS.UPDATE_MONEY_REQUEST_DESCRIPTION, params, onyxData); } /** Edits an existing distance request */ -function updateDistanceRequest(transactionID: string, transactionThreadReportID: string, transactionChanges: TransactionChanges) { - const {params, onyxData} = getUpdateMoneyRequestParams(transactionID, transactionThreadReportID, transactionChanges, false); +function updateDistanceRequest( + transactionID: string, + transactionThreadReportID: string, + transactionChanges: TransactionChanges, + policy: OnyxTypes.Policy, + policyTags: OnyxTypes.PolicyTagList, + policyCategories: OnyxTypes.PolicyCategories, +) { + const {params, onyxData} = getUpdateMoneyRequestParams(transactionID, transactionThreadReportID, transactionChanges, policy, policyTags, policyCategories, false); API.write(WRITE_COMMANDS.UPDATE_DISTANCE_REQUEST, params, onyxData); } @@ -1304,9 +1387,9 @@ function requestMoney( taxCode = '', taxAmount = 0, billable?: boolean, - policy = undefined, - policyTags = undefined, - policyCategories = undefined, + policy?: OnyxEntry, + policyTags?: OnyxEntry, + policyCategories?: OnyxEntry, gpsPoints = undefined, ) { // If the report is iou or expense report, we should get the linked chat report to be passed to the getMoneyRequestInformation function @@ -2346,7 +2429,14 @@ function setDraftSplitTransaction(transactionID: string, transactionChanges: Tra Onyx.merge(`${ONYXKEYS.COLLECTION.SPLIT_TRANSACTION_DRAFT}${transactionID}`, updatedTransaction); } -function editRegularMoneyRequest(transactionID: string, transactionThreadReportID: string, transactionChanges: TransactionChanges) { +function editRegularMoneyRequest( + transactionID: string, + transactionThreadReportID: string, + transactionChanges: TransactionChanges, + policy: OnyxTypes.Policy, + policyTags: OnyxTypes.PolicyTagList, + policyCategories: OnyxTypes.PolicyCategories, +) { // STEP 1: Get all collections we're updating const transactionThread = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${transactionThreadReportID}`] ?? null; const transaction = allTransactions[`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`]; @@ -2559,6 +2649,25 @@ function editRegularMoneyRequest(transactionID: string, transactionThreadReportI }, ]; + // Add transaction violations if there is a policy and updated transaaction + if (policy?.id && updatedTransaction) { + const currentTransactionViolations = allTransactionViolations[`${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${transactionID}`] ?? []; + const updatedViolationsOnyxData = ViolationsUtils.getViolationsOnyxData( + updatedTransaction, + currentTransactionViolations, + !!policy.requiresTag, + policyTags, + !!policy.requiresCategory, + policyCategories, + ); + optimisticData.push(updatedViolationsOnyxData); + failureData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${transactionID}`, + value: currentTransactionViolations, + }); + } + // STEP 6: Call the API endpoint const {created, amount, currency, comment, merchant, category, billable, tag} = ReportUtils.getTransactionDetails(updatedTransaction) ?? {}; @@ -2578,21 +2687,36 @@ function editRegularMoneyRequest(transactionID: string, transactionThreadReportI API.write(WRITE_COMMANDS.EDIT_MONEY_REQUEST, parameters, {optimisticData, successData, failureData}); } -function editMoneyRequest(transaction: OnyxTypes.Transaction, transactionThreadReportID: string, transactionChanges: TransactionChanges) { +function editMoneyRequest( + transaction: OnyxTypes.Transaction, + transactionThreadReportID: string, + transactionChanges: TransactionChanges, + policy: OnyxTypes.Policy, + policyTags: OnyxTypes.PolicyTagList, + policyCategories: OnyxTypes.PolicyCategories, +) { if (TransactionUtils.isDistanceRequest(transaction)) { - updateDistanceRequest(transaction.transactionID, transactionThreadReportID, transactionChanges); + updateDistanceRequest(transaction.transactionID, transactionThreadReportID, transactionChanges, policy, policyTags, policyCategories); } else { - editRegularMoneyRequest(transaction.transactionID, transactionThreadReportID, transactionChanges); + editRegularMoneyRequest(transaction.transactionID, transactionThreadReportID, transactionChanges, policy, policyTags, policyCategories); } } /** Updates the amount and currency fields of a money request */ -function updateMoneyRequestAmountAndCurrency(transactionID: string, transactionThreadReportID: string, currency: string, amount: number) { +function updateMoneyRequestAmountAndCurrency( + transactionID: string, + transactionThreadReportID: string, + currency: string, + amount: number, + policy: OnyxEntry, + policyTags: OnyxEntry, + policyCategories: OnyxEntry, +) { const transactionChanges = { amount, currency, }; - const {params, onyxData} = getUpdateMoneyRequestParams(transactionID, transactionThreadReportID, transactionChanges, true); + const {params, onyxData} = getUpdateMoneyRequestParams(transactionID, transactionThreadReportID, transactionChanges, policy, policyTags, policyCategories, true); API.write(WRITE_COMMANDS.UPDATE_MONEY_REQUEST_AMOUNT_AND_CURRENCY, params, onyxData); } diff --git a/src/pages/EditRequestPage.js b/src/pages/EditRequestPage.js index 7e1a9f7d9b7b..777321fc2068 100644 --- a/src/pages/EditRequestPage.js +++ b/src/pages/EditRequestPage.js @@ -28,6 +28,7 @@ import EditRequestReceiptPage from './EditRequestReceiptPage'; import EditRequestTagPage from './EditRequestTagPage'; import reportActionPropTypes from './home/report/reportActionPropTypes'; import reportPropTypes from './reportPropTypes'; +import {policyPropTypes} from './workspace/withPolicy'; const propTypes = { /** Route from navigation */ @@ -46,6 +47,9 @@ const propTypes = { /** The report object for the thread report */ report: reportPropTypes, + /** The policy of the report */ + policy: policyPropTypes.policy, + /** Collection of categories attached to a policy */ policyCategories: PropTypes.objectOf(categoryPropTypes), @@ -61,13 +65,14 @@ const propTypes = { const defaultProps = { report: {}, + policy: {}, policyCategories: {}, policyTags: {}, parentReportActions: {}, transaction: {}, }; -function EditRequestPage({report, route, policyCategories, policyTags, parentReportActions, transaction}) { +function EditRequestPage({report, route, policy, policyCategories, policyTags, parentReportActions, transaction}) { const parentReportActionID = lodashGet(report, 'parentReportActionID', '0'); const parentReportAction = lodashGet(parentReportActions, parentReportActionID, {}); const { @@ -118,10 +123,10 @@ function EditRequestPage({report, route, policyCategories, policyTags, parentRep return; } - IOU.updateMoneyRequestAmountAndCurrency(transaction.transactionID, report.reportID, newCurrency, newAmount); + IOU.updateMoneyRequestAmountAndCurrency(transaction.transactionID, report.reportID, newCurrency, newAmount, policy, policyTags, policyCategories); Navigation.dismissModal(); }, - [transaction, report], + [transaction, report, policy, policyTags, policyCategories], ); const saveCreated = useCallback( @@ -131,10 +136,10 @@ function EditRequestPage({report, route, policyCategories, policyTags, parentRep Navigation.dismissModal(); return; } - IOU.updateMoneyRequestDate(transaction.transactionID, report.reportID, newCreated); + IOU.updateMoneyRequestDate(transaction.transactionID, report.reportID, newCreated, policy, policyTags, policyCategories); Navigation.dismissModal(); }, - [transaction, report], + [transaction, report, policy, policyTags, policyCategories], ); const saveMerchant = useCallback( @@ -149,10 +154,17 @@ function EditRequestPage({report, route, policyCategories, policyTags, parentRep } // An empty newTrimmedMerchant is only possible for the P2P IOU case - IOU.updateMoneyRequestMerchant(transaction.transactionID, report.reportID, newTrimmedMerchant || CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT); + IOU.updateMoneyRequestMerchant( + transaction.transactionID, + report.reportID, + newTrimmedMerchant || CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT, + policy, + policyTags, + policyCategories, + ); Navigation.dismissModal(); }, - [transactionMerchant, transaction, report], + [transactionMerchant, transaction, report, policy, policyTags, policyCategories], ); const saveTag = useCallback( @@ -162,20 +174,20 @@ function EditRequestPage({report, route, policyCategories, policyTags, parentRep // In case the same tag has been selected, reset the tag. updatedTag = ''; } - IOU.updateMoneyRequestTag(transaction.transactionID, report.reportID, updatedTag); + IOU.updateMoneyRequestTag(transaction.transactionID, report.reportID, updatedTag, policy, policyTags, policyCategories); Navigation.dismissModal(); }, - [transactionTag, transaction.transactionID, report.reportID], + [transactionTag, transaction.transactionID, report.reportID, policy, policyTags, policyCategories], ); const saveCategory = useCallback( ({category: newCategory}) => { // In case the same category has been selected, reset the category. const updatedCategory = newCategory === transactionCategory ? '' : newCategory; - IOU.updateMoneyRequestCategory(transaction.transactionID, report.reportID, updatedCategory); + IOU.updateMoneyRequestCategory(transaction.transactionID, report.reportID, updatedCategory, policy, policyTags, policyCategories); Navigation.dismissModal(); }, - [transactionCategory, transaction.transactionID, report.reportID], + [transactionCategory, transaction.transactionID, report.reportID, policy, policyTags, policyCategories], ); if (fieldToEdit === CONST.EDIT_REQUEST_FIELD.DATE) { @@ -274,6 +286,9 @@ export default compose( }), // eslint-disable-next-line rulesdir/no-multiple-onyx-in-file withOnyx({ + policy: { + key: ({report}) => `${ONYXKEYS.COLLECTION.POLICY}${report ? report.policyID : '0'}`, + }, policyCategories: { key: ({report}) => `${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${report ? report.policyID : '0'}`, }, diff --git a/src/pages/iou/request/step/IOURequestStepConfirmation.js b/src/pages/iou/request/step/IOURequestStepConfirmation.js index 98865539091a..bc30a8148d43 100644 --- a/src/pages/iou/request/step/IOURequestStepConfirmation.js +++ b/src/pages/iou/request/step/IOURequestStepConfirmation.js @@ -66,9 +66,9 @@ const propTypes = { }; const defaultProps = { personalDetails: {}, - policy: {}, - policyCategories: {}, - policyTags: {}, + policy: null, + policyCategories: null, + policyTags: null, report: {}, transaction: {}, ...withCurrentUserPersonalDetailsDefaultProps, diff --git a/src/pages/iou/request/step/IOURequestStepDescription.js b/src/pages/iou/request/step/IOURequestStepDescription.js index e6fff0541599..3a50eff13918 100644 --- a/src/pages/iou/request/step/IOURequestStepDescription.js +++ b/src/pages/iou/request/step/IOURequestStepDescription.js @@ -1,11 +1,14 @@ import {useFocusEffect} from '@react-navigation/native'; import lodashGet from 'lodash/get'; import lodashIsEmpty from 'lodash/isEmpty'; +import PropTypes from 'prop-types'; import React, {useCallback, useRef} from 'react'; import {View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; +import categoryPropTypes from '@components/categoryPropTypes'; import FormProvider from '@components/Form/FormProvider'; import InputWrapperWithRef from '@components/Form/InputWrapper'; +import tagPropTypes from '@components/tagPropTypes'; import TextInput from '@components/TextInput'; import transactionPropTypes from '@components/transactionPropTypes'; import useLocalize from '@hooks/useLocalize'; @@ -16,6 +19,7 @@ import updateMultilineInputRange from '@libs/updateMultilineInputRange'; import * as IOU from '@userActions/IOU'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; +import {policyPropTypes} from '@src/pages/workspace/withPolicy'; import IOURequestStepRoutePropTypes from './IOURequestStepRoutePropTypes'; import StepScreenWrapper from './StepScreenWrapper'; import withFullTransactionOrNotFound from './withFullTransactionOrNotFound'; @@ -31,11 +35,23 @@ const propTypes = { /** The draft transaction that holds data to be persisted on the current transaction */ splitDraftTransaction: transactionPropTypes, + + /** The policy of the report */ + policy: policyPropTypes.policy, + + /** Collection of categories attached to a policy */ + policyCategories: PropTypes.objectOf(categoryPropTypes), + + /** Collection of tags attached to a policy */ + policyTags: tagPropTypes, }; const defaultProps = { transaction: {}, splitDraftTransaction: {}, + policy: null, + policyTags: null, + policyCategories: null, }; function IOURequestStepDescription({ @@ -44,6 +60,9 @@ function IOURequestStepDescription({ }, transaction, splitDraftTransaction, + policy, + policyTags, + policyCategories, }) { const styles = useThemeStyles(); const {translate} = useLocalize(); @@ -96,7 +115,7 @@ function IOURequestStepDescription({ IOU.setMoneyRequestDescription(transaction.transactionID, newComment, action === CONST.IOU.ACTION.CREATE); if (action === CONST.IOU.ACTION.EDIT) { - IOU.updateMoneyRequestDescription(transaction.transactionID, reportID, newComment); + IOU.updateMoneyRequestDescription(transaction.transactionID, reportID, newComment, policy, policyTags, policyCategories); } navigateBack(); @@ -156,5 +175,14 @@ export default compose( return `${ONYXKEYS.COLLECTION.SPLIT_TRANSACTION_DRAFT}${transactionID}`; }, }, + policy: { + key: ({report}) => `${ONYXKEYS.COLLECTION.POLICY}${report ? report.policyID : '0'}`, + }, + policyCategories: { + key: ({report}) => `${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${report ? report.policyID : '0'}`, + }, + policyTags: { + key: ({report}) => `${ONYXKEYS.COLLECTION.POLICY_TAGS}${report ? report.policyID : '0'}`, + }, }), )(IOURequestStepDescription); diff --git a/src/pages/iou/request/step/IOURequestStepTag.js b/src/pages/iou/request/step/IOURequestStepTag.js index 47ee8c05ad45..1a26d10552f7 100644 --- a/src/pages/iou/request/step/IOURequestStepTag.js +++ b/src/pages/iou/request/step/IOURequestStepTag.js @@ -1,6 +1,8 @@ +import PropTypes from 'prop-types'; import React from 'react'; import {withOnyx} from 'react-native-onyx'; import _ from 'underscore'; +import categoryPropTypes from '@components/categoryPropTypes'; import TagPicker from '@components/TagPicker'; import tagPropTypes from '@components/tagPropTypes'; import Text from '@components/Text'; @@ -11,6 +13,7 @@ import compose from '@libs/compose'; import Navigation from '@libs/Navigation/Navigation'; import * as PolicyUtils from '@libs/PolicyUtils'; import reportPropTypes from '@pages/reportPropTypes'; +import {policyPropTypes} from '@pages/workspace/withPolicy'; import * as IOU from '@userActions/IOU'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -30,17 +33,27 @@ const propTypes = { /** The report currently being used */ report: reportPropTypes, + /** The policy of the report */ + policy: policyPropTypes.policy, + + /** The category configuration of the report's policy */ + policyCategories: PropTypes.objectOf(categoryPropTypes), + /** Collection of tags attached to a policy */ policyTags: tagPropTypes, }; const defaultProps = { report: {}, - policyTags: {}, + policy: null, + policyTags: null, + policyCategories: null, transaction: {}, }; function IOURequestStepTag({ + policy, + policyCategories, policyTags, report, route: { @@ -74,7 +87,7 @@ function IOURequestStepTag({ return; } if (isEditing) { - IOU.updateMoneyRequestTag(transactionID, report.reportID, updatedTag); + IOU.updateMoneyRequestTag(transactionID, report.reportID, updatedTag, policy, policyTags, policyCategories); Navigation.dismissModal(); return; } @@ -113,6 +126,12 @@ export default compose( withWritableReportOrNotFound, withFullTransactionOrNotFound, withOnyx({ + policy: { + key: ({report}) => `${ONYXKEYS.COLLECTION.POLICY}${report ? report.policyID : '0'}`, + }, + policyCategories: { + key: ({report}) => `${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${report ? report.policyID : '0'}`, + }, policyTags: { key: ({report}) => `${ONYXKEYS.COLLECTION.POLICY_TAGS}${report ? report.policyID : '0'}`, }, diff --git a/src/types/onyx/PolicyTag.ts b/src/types/onyx/PolicyTag.ts index c14ede73b622..ff688419605d 100644 --- a/src/types/onyx/PolicyTag.ts +++ b/src/types/onyx/PolicyTag.ts @@ -15,4 +15,17 @@ type PolicyTag = { type PolicyTags = Record; -export type {PolicyTag, PolicyTags}; +type PolicyTagList = Record< + T, + { + /** Name of the tag list */ + name: T; + + /** Flag that determines if tags are required */ + required: boolean; + + tags: PolicyTags; + } +>; + +export type {PolicyTag, PolicyTags, PolicyTagList}; diff --git a/src/types/onyx/index.ts b/src/types/onyx/index.ts index 4728c8872e75..6ee0d0c96716 100644 --- a/src/types/onyx/index.ts +++ b/src/types/onyx/index.ts @@ -45,7 +45,7 @@ import type {PolicyCategories, PolicyCategory} from './PolicyCategory'; import type {PolicyMembers} from './PolicyMember'; import type PolicyMember from './PolicyMember'; import type {PolicyReportField, PolicyReportFields} from './PolicyReportField'; -import type {PolicyTag, PolicyTags} from './PolicyTag'; +import type {PolicyTag, PolicyTagList, PolicyTags} from './PolicyTag'; import type PrivatePersonalDetails from './PrivatePersonalDetails'; import type RecentlyUsedCategories from './RecentlyUsedCategories'; import type RecentlyUsedReportFields from './RecentlyUsedReportFields'; @@ -123,6 +123,7 @@ export type { PolicyMembers, PolicyTag, PolicyTags, + PolicyTagList, PrivatePersonalDetails, RecentWaypoint, RecentlyUsedCategories, diff --git a/tests/unit/ViolationUtilsTest.js b/tests/unit/ViolationUtilsTest.js index f0b53443831e..2b644c1fc822 100644 --- a/tests/unit/ViolationUtilsTest.js +++ b/tests/unit/ViolationUtilsTest.js @@ -128,7 +128,13 @@ describe('getViolationsOnyxData', () => { describe('policyRequiresTags', () => { beforeEach(() => { policyRequiresTags = true; - policyTags = {Lunch: {enabled: true}, Dinner: {enabled: true}}; + policyTags = { + Tag: { + name: 'Tag', + required: true, + tags: {Lunch: {enabled: true}, Dinner: {enabled: true}}, + }, + }; transaction.tag = 'Lunch'; });