diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.js index 10cca88979d9..fb4e9f02f1b6 100644 --- a/src/libs/actions/IOU.js +++ b/src/libs/actions/IOU.js @@ -22,6 +22,7 @@ import * as ReportActionsUtils from '@libs/ReportActionsUtils'; import * as ReportUtils from '@libs/ReportUtils'; import * as TransactionUtils from '@libs/TransactionUtils'; import * as UserUtils from '@libs/UserUtils'; +import ViolationsUtils from '@libs/ViolationsUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; @@ -306,6 +307,27 @@ function getReceiptError(receipt, filename, isScanRequest = true) { : ErrorUtils.getMicroSecondOnyxErrorObject({error: CONST.IOU.RECEIPT_ERROR, source: receipt.source, filename}); } +/** + * Builds the Onyx data for a money request. + * + * @param {Object} chatReport + * @param {Object} iouReport + * @param {Object} transaction + * @param {Object} chatCreatedAction + * @param {Object} iouCreatedAction + * @param {Object} iouAction + * @param {Object} optimisticPersonalDetailListAction + * @param {Object} reportPreviewAction + * @param {Array} optimisticPolicyRecentlyUsedCategories + * @param {Array} optimisticPolicyRecentlyUsedTags + * @param {boolean} isNewChatReport + * @param {boolean} isNewIOUReport + * @param {Object} policy - May be undefined, an empty object, or an object matching the Policy type (src/types/onyx/Policy.ts) + * @param {Array} policyTags + * @param {Array} policyCategories + * @param {Boolean} hasOutstandingChildRequest + * @returns {Array} - An array containing the optimistic data, success data, and failure data. + */ function buildOnyxDataForMoneyRequest( chatReport, iouReport, @@ -319,6 +341,9 @@ function buildOnyxDataForMoneyRequest( optimisticPolicyRecentlyUsedTags, isNewChatReport, isNewIOUReport, + policy, + policyTags, + policyCategories, hasOutstandingChildRequest = false, ) { const isScanRequest = TransactionUtils.isScanRequest(transaction); @@ -556,6 +581,22 @@ function buildOnyxDataForMoneyRequest( }, ]; + // Policy won't be set for P2P cases for which we don't need to compute violations + if (!policy || !policy.id) { + return [optimisticData, successData, failureData]; + } + + const violationsOnyxData = ViolationsUtils.getViolationsOnyxData(transaction, [], policy.requiresTag, policyTags, policy.requiresCategory, policyCategories); + + if (violationsOnyxData) { + optimisticData.push(violationsOnyxData); + failureData.push({ + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${transaction.transactionID}`, + value: [], + }); + } + return [optimisticData, successData, failureData]; } @@ -577,6 +618,9 @@ function buildOnyxDataForMoneyRequest( * @param {String} [category] * @param {String} [tag] * @param {Boolean} [billable] + * @param {Object} [policy] + * @param {Object} [policyTags] + * @param {Object} [policyCategories] * @returns {Object} data * @returns {String} data.payerEmail * @returns {Object} data.iouReport @@ -606,6 +650,9 @@ function getMoneyRequestInformation( category = undefined, tag = undefined, billable = undefined, + policy = undefined, + policyTags = undefined, + policyCategories = undefined, ) { const payerEmail = OptionsListUtils.addSMSDomainIfPhoneNumber(participant.login); const payerAccountID = Number(participant.accountID); @@ -639,7 +686,6 @@ function getMoneyRequestInformation( let needsToBeManuallySubmitted = false; let isFromPaidPolicy = false; if (isPolicyExpenseChat) { - const policy = ReportUtils.getPolicy(chatReport.policyID); isFromPaidPolicy = PolicyUtils.isPaidGroupPolicy(policy); // If the scheduled submit is turned off on the policy, user needs to manually submit the report which is indicated by GBR in LHN @@ -774,6 +820,9 @@ function getMoneyRequestInformation( optimisticPolicyRecentlyUsedTags, isNewChatReport, isNewIOUReport, + policy, + policyTags, + policyCategories, hasOutstandingChildRequest, ); @@ -808,9 +857,12 @@ function getMoneyRequestInformation( * @param {String} currency * @param {String} merchant * @param {Boolean} [billable] - * @param {Obejct} validWaypoints + * @param {Object} validWaypoints + * @param {Object} policy - May be undefined, an empty object, or an object matching the Policy type (src/types/onyx/Policy.ts) + * @param {Array} policyTags + * @param {Array} policyCategories */ -function createDistanceRequest(report, participant, comment, created, category, tag, amount, currency, merchant, billable, validWaypoints) { +function createDistanceRequest(report, participant, comment, created, category, tag, amount, currency, merchant, billable, validWaypoints, policy, policyTags, policyCategories) { // 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); const currentChatReport = isMoneyRequestReport ? ReportUtils.getReport(report.chatReportID) : report; @@ -834,6 +886,9 @@ function createDistanceRequest(report, participant, comment, created, category, category, tag, billable, + policy, + policyTags, + policyCategories, ); API.write( 'CreateDistanceRequest', @@ -1104,6 +1159,9 @@ function updateDistanceRequest(transactionID, transactionThreadReportID, transac * @param {String} [taxCode] * @param {Number} [taxAmount] * @param {Boolean} [billable] + * @param {Object} [policy] + * @param {Object} [policyTags] + * @param {Object} [policyCategories] */ function requestMoney( report, @@ -1121,12 +1179,33 @@ function requestMoney( taxCode = '', taxAmount = 0, billable = undefined, + policy = undefined, + policyTags = undefined, + policyCategories = undefined, ) { // If the report is iou or expense report, we should get the linked chat report to be passed to the getMoneyRequestInformation function const isMoneyRequestReport = ReportUtils.isMoneyRequestReport(report); const currentChatReport = isMoneyRequestReport ? ReportUtils.getReport(report.chatReportID) : report; const {payerAccountID, payerEmail, iouReport, chatReport, transaction, iouAction, createdChatReportActionID, createdIOUReportActionID, reportPreviewAction, onyxData} = - getMoneyRequestInformation(currentChatReport, participant, comment, amount, currency, created, merchant, payeeAccountID, payeeEmail, receipt, undefined, category, tag, billable); + getMoneyRequestInformation( + currentChatReport, + participant, + comment, + amount, + currency, + created, + merchant, + payeeAccountID, + payeeEmail, + receipt, + undefined, + category, + tag, + billable, + policy, + policyTags, + policyCategories, + ); const activeReportID = isMoneyRequestReport ? report.reportID : chatReport.reportID; API.write( diff --git a/src/pages/iou/request/step/IOURequestStepConfirmation.js b/src/pages/iou/request/step/IOURequestStepConfirmation.js index 893c735aac3b..d41442edd670 100644 --- a/src/pages/iou/request/step/IOURequestStepConfirmation.js +++ b/src/pages/iou/request/step/IOURequestStepConfirmation.js @@ -1,12 +1,15 @@ import lodashGet from 'lodash/get'; +import PropTypes from 'prop-types'; import React, {useCallback, useEffect, useMemo, useState} from 'react'; import {View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; import _ from 'underscore'; +import categoryPropTypes from '@components/categoryPropTypes'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import * as Expensicons from '@components/Icon/Expensicons'; import MoneyRequestConfirmationList from '@components/MoneyTemporaryForRefactorRequestConfirmationList'; import ScreenWrapper from '@components/ScreenWrapper'; +import tagPropTypes from '@components/tagPropTypes'; import transactionPropTypes from '@components/transactionPropTypes'; import withCurrentUserPersonalDetails, {withCurrentUserPersonalDetailsDefaultProps, withCurrentUserPersonalDetailsPropTypes} from '@components/withCurrentUserPersonalDetails'; import useLocalize from '@hooks/useLocalize'; @@ -46,6 +49,12 @@ const propTypes = { /** The policy of the report */ ...policyPropTypes, + /** The tag configuration of the report's policy */ + policyTags: tagPropTypes, + + /** The category configuration of the report's policy */ + policyCategories: PropTypes.objectOf(categoryPropTypes), + /** The full IOU report */ report: reportPropTypes, @@ -55,6 +64,8 @@ const propTypes = { const defaultProps = { personalDetails: {}, policy: {}, + policyCategories: {}, + policyTags: {}, report: {}, transaction: {}, ...withCurrentUserPersonalDetailsDefaultProps, @@ -63,6 +74,8 @@ function IOURequestStepConfirmation({ currentUserPersonalDetails, personalDetails, policy, + policyTags, + policyCategories, report, route: { params: {iouType, reportID, transactionID}, @@ -164,9 +177,12 @@ function IOURequestStepConfirmation({ transactionTaxCode, transactionTaxAmount, transaction.billable, + policy, + policyTags, + policyCategories, ); }, - [report, transaction, transactionTaxCode, transactionTaxAmount, currentUserPersonalDetails.login, currentUserPersonalDetails.accountID], + [report, transaction, transactionTaxCode, transactionTaxAmount, currentUserPersonalDetails.login, currentUserPersonalDetails.accountID, policy, policyTags, policyCategories], ); /** @@ -187,9 +203,12 @@ function IOURequestStepConfirmation({ transaction.merchant, transaction.billable, TransactionUtils.getValidWaypoints(transaction.comment.waypoints, true), + policy, + policyTags, + policyCategories, ); }, - [report, transaction], + [policy, policyCategories, policyTags, report, transaction], ); const createTransaction = useCallback( @@ -375,7 +394,13 @@ export default compose( // eslint-disable-next-line rulesdir/no-multiple-onyx-in-file withOnyx({ policy: { - key: ({report}) => `${ONYXKEYS.COLLECTION.POLICY}${lodashGet(report, 'policyID', '0')}`, + 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'}`, }, }), )(IOURequestStepConfirmation); diff --git a/src/pages/iou/steps/MoneyRequestConfirmPage.js b/src/pages/iou/steps/MoneyRequestConfirmPage.js index 3476b2304875..1738ac78df47 100644 --- a/src/pages/iou/steps/MoneyRequestConfirmPage.js +++ b/src/pages/iou/steps/MoneyRequestConfirmPage.js @@ -4,11 +4,13 @@ import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react'; import {View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; import _ from 'underscore'; +import categoryPropTypes from '@components/categoryPropTypes'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import * as Expensicons from '@components/Icon/Expensicons'; import MoneyRequestConfirmationList from '@components/MoneyRequestConfirmationList'; import {usePersonalDetails} from '@components/OnyxProvider'; import ScreenWrapper from '@components/ScreenWrapper'; +import tagPropTypes from '@components/tagPropTypes'; import withCurrentUserPersonalDetails, {withCurrentUserPersonalDetailsDefaultProps, withCurrentUserPersonalDetailsPropTypes} from '@components/withCurrentUserPersonalDetails'; import withLocalize from '@components/withLocalize'; import useInitialValue from '@hooks/useInitialValue'; @@ -23,6 +25,7 @@ import * as OptionsListUtils from '@libs/OptionsListUtils'; import * as ReportUtils from '@libs/ReportUtils'; import {iouDefaultProps, iouPropTypes} from '@pages/iou/propTypes'; import reportPropTypes from '@pages/reportPropTypes'; +import {policyDefaultProps, policyPropTypes} from '@pages/workspace/withPolicy'; import * as IOU from '@userActions/IOU'; import * as Policy from '@userActions/Policy'; import CONST from '@src/CONST'; @@ -47,12 +50,22 @@ const propTypes = { /** Holds data related to Money Request view state, rather than the underlying Money Request data. */ iou: iouPropTypes, + /** The policy of the current request */ + policy: policyPropTypes, + + policyTags: tagPropTypes, + + policyCategories: PropTypes.objectOf(categoryPropTypes), + ...withCurrentUserPersonalDetailsPropTypes, }; const defaultProps = { report: {}, + policyCategories: {}, + policyTags: {}, iou: iouDefaultProps, + policy: policyDefaultProps, ...withCurrentUserPersonalDetailsDefaultProps, }; @@ -163,6 +176,9 @@ function MoneyRequestConfirmPage(props) { props.iou.category, props.iou.tag, props.iou.billable, + props.policy, + props.policyTags, + props.policyCategories, ); }, [ @@ -176,6 +192,9 @@ function MoneyRequestConfirmPage(props) { props.iou.category, props.iou.tag, props.iou.billable, + props.policy, + props.policyTags, + props.policyCategories, ], ); @@ -197,9 +216,25 @@ function MoneyRequestConfirmPage(props) { props.iou.currency, props.iou.merchant, props.iou.billable, + props.policy, + props.policyTags, + props.policyCategories, ); }, - [props.report, props.iou.created, props.iou.transactionID, props.iou.category, props.iou.tag, props.iou.amount, props.iou.currency, props.iou.merchant, props.iou.billable], + [ + props.report, + props.iou.created, + props.iou.transactionID, + props.iou.category, + props.iou.tag, + props.iou.amount, + props.iou.currency, + props.iou.merchant, + props.iou.billable, + props.policy, + props.policyTags, + props.policyCategories, + ], ); const createTransaction = useCallback( @@ -424,10 +459,15 @@ export default compose( key: `${ONYXKEYS.COLLECTION.SELECTED_TAB}${CONST.TAB.RECEIPT_TAB_ID}`, }, }), - // 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'}`, + }, + policyTags: { + key: ({report}) => `${ONYXKEYS.COLLECTION.POLICY_TAGS}${report ? report.policyID : '0'}`, + }, }), )(MoneyRequestConfirmPage); diff --git a/src/pages/workspace/withPolicy.tsx b/src/pages/workspace/withPolicy.tsx index 90eda3651a04..ec38b61fb0dc 100644 --- a/src/pages/workspace/withPolicy.tsx +++ b/src/pages/workspace/withPolicy.tsx @@ -54,6 +54,18 @@ const policyPropTypes = { * } */ errorFields: PropTypes.objectOf(PropTypes.objectOf(PropTypes.string)), + + /** Whether or not the policy requires tags */ + requiresTag: PropTypes.bool, + + /** Whether or not the policy requires categories */ + requiresCategory: PropTypes.bool, + + /** Whether or not the policy has multiple tag lists */ + hasMultipleTagLists: PropTypes.bool, + + /** Whether or not the policy has tax tracking enabled */ + isTaxTrackingEnabled: PropTypes.bool, }), /** The employee list of this policy */