Skip to content

Commit

Permalink
Merge pull request #27936 from rezkiy37/fix/26506-hide-category-in-sp…
Browse files Browse the repository at this point in the history
…lit-bill

Allow Adding a category to a Split Bill
  • Loading branch information
puneetlath authored Oct 9, 2023
2 parents a545eaa + 585d6a2 commit 068e59e
Show file tree
Hide file tree
Showing 8 changed files with 129 additions and 68 deletions.
11 changes: 6 additions & 5 deletions src/components/MoneyRequestConfirmationList.js
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,9 @@ const propTypes = {
/** Whether the money request is a distance request */
isDistanceRequest: PropTypes.bool,

/** A flag for verifying that the current report is a sub-report of a workspace chat */
isPolicyExpenseChat: PropTypes.bool,

/* Onyx Props */
/** Collection of categories attached to a policy */
policyCategories: PropTypes.objectOf(categoryPropTypes),
Expand Down Expand Up @@ -176,6 +179,7 @@ const defaultProps = {
transaction: {},
mileageRate: {unit: CONST.CUSTOM_UNITS.DISTANCE_UNIT_MILES, rate: 0, currency: 'USD'},
isDistanceRequest: false,
isPolicyExpenseChat: false,
};

function MoneyRequestConfirmationList(props) {
Expand All @@ -193,19 +197,16 @@ function MoneyRequestConfirmationList(props) {
const distance = lodashGet(transaction, 'routes.route0.distance', 0);
const shouldCalculateDistanceAmount = props.isDistanceRequest && props.iouAmount === 0;

// A flag for verifying that the current report is a sub-report of a workspace chat
const isPolicyExpenseChat = useMemo(() => ReportUtils.isPolicyExpenseChat(ReportUtils.getRootParentReport(ReportUtils.getReport(props.reportID))), [props.reportID]);

// A flag for showing the categories field
const shouldShowCategories = isPolicyExpenseChat && Permissions.canUseCategories(props.betas) && OptionsListUtils.hasEnabledOptions(_.values(props.policyCategories));
const shouldShowCategories = props.isPolicyExpenseChat && Permissions.canUseCategories(props.betas) && OptionsListUtils.hasEnabledOptions(_.values(props.policyCategories));

// Fetches the first tag list of the policy
const policyTag = PolicyUtils.getTag(props.policyTags);
const policyTagList = lodashGet(policyTag, 'tags', {});
const policyTagListName = lodashGet(policyTag, 'name', translate('common.tag'));
const canUseTags = Permissions.canUseTags(props.betas);
// A flag for showing the tags field
const shouldShowTags = isPolicyExpenseChat && canUseTags && OptionsListUtils.hasEnabledOptions(_.values(policyTagList));
const shouldShowTags = props.isPolicyExpenseChat && canUseTags && OptionsListUtils.hasEnabledOptions(_.values(policyTagList));

// A flag for showing the billable field
const shouldShowBillable = canUseTags && !lodashGet(props.policy, 'disabledFields.defaultBillable', true);
Expand Down
80 changes: 53 additions & 27 deletions src/libs/actions/IOU.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import * as Report from './Report';
import * as NumberUtils from '../NumberUtils';
import ReceiptGeneric from '../../../assets/images/receipt-generic.png';
import * as LocalePhoneNumber from '../LocalePhoneNumber';
import * as Policy from './Policy';

let allReports;
Onyx.connect({
Expand All @@ -44,13 +45,6 @@ Onyx.connect({
},
});

let allRecentlyUsedCategories = {};
Onyx.connect({
key: ONYXKEYS.COLLECTION.POLICY_RECENTLY_USED_CATEGORIES,
waitForCollectionCallback: true,
callback: (val) => (allRecentlyUsedCategories = val),
});

let allRecentlyUsedTags = {};
Onyx.connect({
key: ONYXKEYS.COLLECTION.POLICY_RECENTLY_USED_TAGS,
Expand Down Expand Up @@ -137,7 +131,7 @@ function buildOnyxDataForMoneyRequest(
iouAction,
optimisticPersonalDetailListAction,
reportPreviewAction,
optimisticRecentlyUsedCategories,
optimisticPolicyRecentlyUsedCategories,
optimisticPolicyRecentlyUsedTags,
isNewChatReport,
isNewIOUReport,
Expand Down Expand Up @@ -189,11 +183,11 @@ function buildOnyxDataForMoneyRequest(
},
];

if (!_.isEmpty(optimisticRecentlyUsedCategories)) {
if (!_.isEmpty(optimisticPolicyRecentlyUsedCategories)) {
optimisticData.push({
onyxMethod: Onyx.METHOD.SET,
key: `${ONYXKEYS.COLLECTION.POLICY_RECENTLY_USED_CATEGORIES}${iouReport.policyID}`,
value: optimisticRecentlyUsedCategories,
value: optimisticPolicyRecentlyUsedCategories,
});
}

Expand Down Expand Up @@ -478,13 +472,7 @@ function getMoneyRequestInformation(
billable,
);

const uniquePolicyRecentlyUsedCategories = allRecentlyUsedCategories
? _.filter(
allRecentlyUsedCategories[`${ONYXKEYS.COLLECTION.POLICY_RECENTLY_USED_CATEGORIES}${iouReport.policyID}`],
(recentlyUsedPolicyCategory) => recentlyUsedPolicyCategory !== category,
)
: [];
const optimisticPolicyRecentlyUsedCategories = [category, ...uniquePolicyRecentlyUsedCategories];
const optimisticPolicyRecentlyUsedCategories = Policy.buildOptimisticPolicyRecentlyUsedCategories(iouReport.policyID, category);

const optimisticPolicyRecentlyUsedTags = {};
const policyTags = allPolicyTags[`${ONYXKEYS.COLLECTION.POLICY_TAGS}${iouReport.policyID}`];
Expand Down Expand Up @@ -882,11 +870,12 @@ function requestMoney(
* @param {Number} amount - always in the smallest unit of the currency
* @param {String} comment
* @param {String} currency
* @param {String} category
* @param {String} existingSplitChatReportID - the report ID where the split bill happens, could be a group chat or a workspace chat
*
* @return {Object}
*/
function createSplitsAndOnyxData(participants, currentUserLogin, currentUserAccountID, amount, comment, currency, existingSplitChatReportID = '') {
function createSplitsAndOnyxData(participants, currentUserLogin, currentUserAccountID, amount, comment, currency, category, existingSplitChatReportID = '') {
const currentUserEmailForIOUSplit = OptionsListUtils.addSMSDomainIfPhoneNumber(currentUserLogin);
const participantAccountIDs = _.map(participants, (participant) => Number(participant.accountID));
const existingSplitChatReport =
Expand All @@ -910,6 +899,10 @@ function createSplitsAndOnyxData(participants, currentUserLogin, currentUserAcco
'',
'',
`${Localize.translateLocal('iou.splitBill')} ${Localize.translateLocal('common.with')} ${formattedParticipants} [${DateUtils.getDBTime().slice(0, 10)}]`,
undefined,
undefined,
undefined,
category,
);

// Note: The created action must be optimistically generated before the IOU action so there's no chance that the created action appears after the IOU action in the chat
Expand Down Expand Up @@ -1036,9 +1029,12 @@ function createSplitsAndOnyxData(participants, currentUserLogin, currentUserAcco

const hasMultipleParticipants = participants.length > 1;
_.each(participants, (participant) => {
// In case the participant is a worskapce, email & accountID should remain undefined and won't be used in the rest of this code
const email = isOwnPolicyExpenseChat ? '' : OptionsListUtils.addSMSDomainIfPhoneNumber(participant.login).toLowerCase();
const accountID = isOwnPolicyExpenseChat ? 0 : Number(participant.accountID);
// In a case when a participant is a workspace, even when a current user is not an owner of the workspace
const isPolicyExpenseChat = ReportUtils.isPolicyExpenseChat(participant);

// In case the participant is a workspace, email & accountID should remain undefined and won't be used in the rest of this code
const email = isOwnPolicyExpenseChat || isPolicyExpenseChat ? '' : OptionsListUtils.addSMSDomainIfPhoneNumber(participant.login).toLowerCase();
const accountID = isOwnPolicyExpenseChat || isPolicyExpenseChat ? 0 : Number(participant.accountID);
if (email === currentUserEmailForIOUSplit) {
return;
}
Expand Down Expand Up @@ -1090,6 +1086,11 @@ function createSplitsAndOnyxData(participants, currentUserLogin, currentUserAcco
'',
CONST.IOU.MONEY_REQUEST_TYPE.SPLIT,
splitTransaction.transactionID,
undefined,
undefined,
undefined,
undefined,
category,
);

// STEP 4: Build optimistic reportActions. We need:
Expand Down Expand Up @@ -1131,6 +1132,12 @@ function createSplitsAndOnyxData(participants, currentUserLogin, currentUserAcco
oneOnOneReportPreviewAction = ReportUtils.buildOptimisticReportPreview(oneOnOneChatReport, oneOnOneIOUReport);
}

// Add category to optimistic policy recently used categories when a participant is a workspace
let optimisticPolicyRecentlyUsedCategories = [];
if (isPolicyExpenseChat) {
optimisticPolicyRecentlyUsedCategories = Policy.buildOptimisticPolicyRecentlyUsedCategories(participant.policyID, category);
}

// STEP 5: Build Onyx Data
const [oneOnOneOptimisticData, oneOnOneSuccessData, oneOnOneFailureData] = buildOnyxDataForMoneyRequest(
oneOnOneChatReport,
Expand All @@ -1141,7 +1148,7 @@ function createSplitsAndOnyxData(participants, currentUserLogin, currentUserAcco
oneOnOneIOUAction,
oneOnOnePersonalDetailListAction,
oneOnOneReportPreviewAction,
[],
optimisticPolicyRecentlyUsedCategories,
{},
isNewOneOnOneChatReport,
shouldCreateNewOneOnOneIOUReport,
Expand Down Expand Up @@ -1191,11 +1198,11 @@ function createSplitsAndOnyxData(participants, currentUserLogin, currentUserAcco
* @param {Number} amount - always in smallest currency unit
* @param {String} comment
* @param {String} currency
* @param {String} category
* @param {String} existingSplitChatReportID - Either a group DM or a workspace chat
*/
function splitBill(participants, currentUserLogin, currentUserAccountID, amount, comment, currency, existingSplitChatReportID = '') {
const {splitData, splits, onyxData} = createSplitsAndOnyxData(participants, currentUserLogin, currentUserAccountID, amount, comment, currency, existingSplitChatReportID);

function splitBill(participants, currentUserLogin, currentUserAccountID, amount, comment, currency, category, existingSplitChatReportID = '') {
const {splitData, splits, onyxData} = createSplitsAndOnyxData(participants, currentUserLogin, currentUserAccountID, amount, comment, currency, category, existingSplitChatReportID);
API.write(
'SplitBill',
{
Expand All @@ -1204,6 +1211,7 @@ function splitBill(participants, currentUserLogin, currentUserAccountID, amount,
splits: JSON.stringify(splits),
currency,
comment,
category,
transactionID: splitData.transactionID,
reportActionID: splitData.reportActionID,
createdReportActionID: splitData.createdReportActionID,
Expand All @@ -1224,9 +1232,10 @@ function splitBill(participants, currentUserLogin, currentUserAccountID, amount,
* @param {Number} amount - always in smallest currency unit
* @param {String} comment
* @param {String} currency
* @param {String} category
*/
function splitBillAndOpenReport(participants, currentUserLogin, currentUserAccountID, amount, comment, currency) {
const {splitData, splits, onyxData} = createSplitsAndOnyxData(participants, currentUserLogin, currentUserAccountID, amount, comment, currency);
function splitBillAndOpenReport(participants, currentUserLogin, currentUserAccountID, amount, comment, currency, category) {
const {splitData, splits, onyxData} = createSplitsAndOnyxData(participants, currentUserLogin, currentUserAccountID, amount, comment, currency, category);

API.write(
'SplitBillAndOpenReport',
Expand All @@ -1236,6 +1245,7 @@ function splitBillAndOpenReport(participants, currentUserLogin, currentUserAccou
splits: JSON.stringify(splits),
currency,
comment,
category,
transactionID: splitData.transactionID,
reportActionID: splitData.reportActionID,
createdReportActionID: splitData.createdReportActionID,
Expand Down Expand Up @@ -2302,6 +2312,21 @@ function navigateToNextPage(iou, iouType, report, path = '') {
Navigation.navigate(ROUTES.MONEY_REQUEST_PARTICIPANTS.getRoute(iouType));
}

/**
* When the money request or split bill creation flow is initialized via FAB, the reportID is not passed as a navigation
* parameter.
* Gets a report id from the first participant of the IOU object stored in Onyx.
* @param {Object} iou
* @param {Array} iou.participants
* @param {Object} route
* @param {Object} route.params
* @param {String} [route.params.reportID]
* @returns {String}
*/
function getIOUReportID(iou, route) {
return lodashGet(route, 'params.reportID') || lodashGet(iou, 'participants.0.reportID', '');
}

function submitReport() {
// Will be implemented in https://github.com/Expensify/App/issues/28763
}
Expand Down Expand Up @@ -2336,5 +2361,6 @@ export {
navigateToNextPage,
updateDistanceRequest,
replaceReceipt,
getIOUReportID,
submitReport,
};
24 changes: 24 additions & 0 deletions src/libs/actions/Policy.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import _ from 'underscore';
import Onyx from 'react-native-onyx';
import lodashGet from 'lodash/get';
import lodashUnion from 'lodash/union';
import {PUBLIC_DOMAINS} from 'expensify-common/lib/CONST';
import Str from 'expensify-common/lib/str';
import {escapeRegExp} from 'lodash';
Expand Down Expand Up @@ -65,6 +66,13 @@ Onyx.connect({
callback: (val) => (allPersonalDetails = val),
});

let allRecentlyUsedCategories = {};
Onyx.connect({
key: ONYXKEYS.COLLECTION.POLICY_RECENTLY_USED_CATEGORIES,
waitForCollectionCallback: true,
callback: (val) => (allRecentlyUsedCategories = val),
});

/**
* Stores in Onyx the policy ID of the last workspace that was accessed by the user
* @param {String|null} policyID
Expand Down Expand Up @@ -1168,6 +1176,21 @@ function clearErrors(policyID) {
hideWorkspaceAlertMessage(policyID);
}

/**
* @param {String} policyID
* @param {String} category
* @returns {Object}
*/
function buildOptimisticPolicyRecentlyUsedCategories(policyID, category) {
if (!policyID || !category) {
return [];
}

const policyRecentlyUsedCategories = lodashGet(allRecentlyUsedCategories, `${ONYXKEYS.COLLECTION.POLICY_RECENTLY_USED_CATEGORIES}${policyID}`, []);

return lodashUnion([category], policyRecentlyUsedCategories);
}

export {
removeMembers,
addMembersToWorkspace,
Expand Down Expand Up @@ -1197,4 +1220,5 @@ export {
setWorkspaceInviteMembersDraft,
clearErrors,
openDraftWorkspaceRequest,
buildOptimisticPolicyRecentlyUsedCategories,
};
26 changes: 18 additions & 8 deletions src/pages/iou/MoneyRequestCategoryPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React from 'react';
import PropTypes from 'prop-types';
import lodashGet from 'lodash/get';
import {withOnyx} from 'react-native-onyx';
import compose from '../../libs/compose';
import ROUTES from '../../ROUTES';
import Navigation from '../../libs/Navigation/Navigation';
import useLocalize from '../../hooks/useLocalize';
Expand Down Expand Up @@ -83,11 +84,20 @@ MoneyRequestCategoryPage.displayName = 'MoneyRequestCategoryPage';
MoneyRequestCategoryPage.propTypes = propTypes;
MoneyRequestCategoryPage.defaultProps = defaultProps;

export default withOnyx({
report: {
key: ({route}) => `${ONYXKEYS.COLLECTION.REPORT}${lodashGet(route, 'params.reportID', '')}`,
},
iou: {
key: ONYXKEYS.IOU,
},
})(MoneyRequestCategoryPage);
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}`;
},
},
}),
)(MoneyRequestCategoryPage);
13 changes: 10 additions & 3 deletions src/pages/iou/MoneyRequestTagPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -98,14 +98,21 @@ MoneyRequestTagPage.defaultProps = defaultProps;

export default compose(
withOnyx({
report: {
key: ({route}) => `${ONYXKEYS.COLLECTION.REPORT}${lodashGet(route, 'params.reportID')}`,
},
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}`;
},
},
}),
// eslint-disable-next-line rulesdir/no-multiple-onyx-in-file
withOnyx({
policyTags: {
key: ({report}) => `${ONYXKEYS.COLLECTION.POLICY_TAGS}${report ? report.policyID : '0'}`,
Expand Down
Loading

0 comments on commit 068e59e

Please sign in to comment.