Skip to content

Commit

Permalink
Merge pull request #27950 from BeeMargarida/feat/26793-edit-money-req…
Browse files Browse the repository at this point in the history
…uest-tag

#26793: Edit a tag in a money request
  • Loading branch information
amyevans authored Sep 25, 2023
2 parents 41e1da3 + 02c218f commit f0c9bbe
Show file tree
Hide file tree
Showing 13 changed files with 252 additions and 41 deletions.
1 change: 1 addition & 0 deletions src/CONST.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1359,6 +1359,7 @@ const CONST = {
MERCHANT: 'merchant',
CATEGORY: 'category',
RECEIPT: 'receipt',
TAG: 'tag',
},
FOOTER: {
EXPENSE_MANAGEMENT_URL: `${USE_EXPENSIFY_URL}/expense-management`,
Expand Down
19 changes: 7 additions & 12 deletions src/components/MoneyRequestConfirmationList.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import transactionPropTypes from './transactionPropTypes';
import DistanceRequestUtils from '../libs/DistanceRequestUtils';
import * as IOU from '../libs/actions/IOU';
import * as TransactionUtils from '../libs/TransactionUtils';
import * as PolicyUtils from '../libs/PolicyUtils';

const propTypes = {
/** Callback to inform parent modal of success */
Expand Down Expand Up @@ -142,13 +143,7 @@ const propTypes = {
policyCategories: PropTypes.objectOf(categoryPropTypes),

/** Collection of tags attached to a policy */
policyTags: PropTypes.objectOf(
PropTypes.shape({
name: PropTypes.string,
required: PropTypes.bool,
tags: PropTypes.objectOf(tagPropTypes),
}),
),
policyTags: tagPropTypes,
};

const defaultProps = {
Expand Down Expand Up @@ -202,12 +197,12 @@ function MoneyRequestConfirmationList(props) {
const shouldShowCategories = isPolicyExpenseChat && Permissions.canUseCategories(props.betas) && OptionsListUtils.hasEnabledOptions(_.values(props.policyCategories));

// Fetches the first tag list of the policy
const tagListKey = _.first(_.keys(props.policyTags));
const tagList = lodashGet(props.policyTags, [tagListKey, 'tags'], []);
const tagListName = lodashGet(props.policyTags, [tagListKey, 'name'], '');
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 && _.any(tagList, (tag) => tag.enabled);
const shouldShowTags = isPolicyExpenseChat && canUseTags && OptionsListUtils.hasEnabledOptions(_.values(policyTagList));

// A flag for showing the billable field
const shouldShowBillable = canUseTags && !lodashGet(props.policy, 'disabledFields.defaultBillable', true);
Expand Down Expand Up @@ -541,7 +536,7 @@ function MoneyRequestConfirmationList(props) {
<MenuItemWithTopDescription
shouldShowRightIcon={!props.isReadOnly}
title={props.iouTag}
description={tagListName || translate('common.tag')}
description={policyTagListName}
onPress={() => Navigation.navigate(ROUTES.MONEY_REQUEST_TAG.getRoute(props.iouType, props.reportID))}
style={[styles.moneyRequestMenuItem, styles.mb2]}
disabled={didConfirm || props.isReadOnly}
Expand Down
32 changes: 30 additions & 2 deletions src/components/ReportActionItem/MoneyRequestView.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import * as ReportUtils from '../../libs/ReportUtils';
import * as OptionsListUtils from '../../libs/OptionsListUtils';
import * as ReportActionsUtils from '../../libs/ReportActionsUtils';
import * as StyleUtils from '../../styles/StyleUtils';
import * as PolicyUtils from '../../libs/PolicyUtils';
import CONST from '../../CONST';
import * as Expensicons from '../Icon/Expensicons';
import iouReportPropTypes from '../../pages/iouReportPropTypes';
Expand All @@ -32,6 +33,7 @@ import * as TransactionUtils from '../../libs/TransactionUtils';
import OfflineWithFeedback from '../OfflineWithFeedback';
import categoryPropTypes from '../categoryPropTypes';
import SpacerView from '../SpacerView';
import tagPropTypes from '../tagPropTypes';

const propTypes = {
/** The report currently being looked at */
Expand All @@ -53,6 +55,9 @@ const propTypes = {
/** The transaction associated with the transactionThread */
transaction: transactionPropTypes,

/** Collection of tags attached to a policy */
policyTags: tagPropTypes,

...withCurrentUserPersonalDetailsPropTypes,
};

Expand All @@ -65,9 +70,10 @@ const defaultProps = {
currency: CONST.CURRENCY.USD,
comment: {comment: ''},
},
policyTags: {},
};

function MoneyRequestView({betas, report, parentReport, policyCategories, shouldShowHorizontalRule, transaction}) {
function MoneyRequestView({report, betas, parentReport, policyCategories, shouldShowHorizontalRule, transaction, policyTags}) {
const {isSmallScreenWidth} = useWindowDimensions();
const {translate} = useLocalize();

Expand All @@ -80,6 +86,7 @@ function MoneyRequestView({betas, report, parentReport, policyCategories, should
comment: transactionDescription,
merchant: transactionMerchant,
category: transactionCategory,
tag: transactionTag,
} = ReportUtils.getTransactionDetails(transaction);
const isEmptyMerchant =
transactionMerchant === '' || transactionMerchant === CONST.TRANSACTION.UNKNOWN_MERCHANT || transactionMerchant === CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT;
Expand All @@ -89,8 +96,14 @@ function MoneyRequestView({betas, report, parentReport, policyCategories, should
const canEdit = ReportUtils.canEditMoneyRequest(parentReportAction);
// A flag for verifying that the current report is a sub-report of a workspace chat
const isPolicyExpenseChat = useMemo(() => ReportUtils.isPolicyExpenseChat(ReportUtils.getRootParentReport(report)), [report]);
// A flag for showing categories

// Fetches only the first tag, for now
const policyTag = PolicyUtils.getTag(policyTags);
const policyTagsList = lodashGet(policyTag, 'tags', {});

// Flags for showing categories and tags
const shouldShowCategory = isPolicyExpenseChat && Permissions.canUseCategories(betas) && (transactionCategory || OptionsListUtils.hasEnabledOptions(lodashValues(policyCategories)));
const shouldShowTag = isPolicyExpenseChat && Permissions.canUseTags(betas) && (transactionTag || OptionsListUtils.hasEnabledOptions(lodashValues(policyTagsList)));

let description = `${translate('iou.amount')}${translate('iou.cash')}`;
if (isSettled) {
Expand Down Expand Up @@ -200,6 +213,18 @@ function MoneyRequestView({betas, report, parentReport, policyCategories, should
/>
</OfflineWithFeedback>
)}
{shouldShowTag && (
<OfflineWithFeedback pendingAction={lodashGet(transaction, 'pendingFields.tag') || lodashGet(transaction, 'pendingAction')}>
<MenuItemWithTopDescription
description={lodashGet(policyTag, 'name', translate('common.tag'))}
title={transactionTag}
interactive={canEdit}
shouldShowRightIcon={canEdit}
titleStyle={styles.flex1}
onPress={() => Navigation.navigate(ROUTES.EDIT_REQUEST.getRoute(report.reportID, CONST.EDIT_REQUEST_FIELD.TAG))}
/>
</OfflineWithFeedback>
)}
<SpacerView
shouldShow={shouldShowHorizontalRule}
style={[shouldShowHorizontalRule ? styles.reportHorizontalRule : {}]}
Expand Down Expand Up @@ -237,5 +262,8 @@ export default compose(
return `${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`;
},
},
policyTags: {
key: ({report}) => `${ONYXKEYS.COLLECTION.POLICY_TAGS}${report.policyID}`,
},
}),
)(MoneyRequestView);
3 changes: 2 additions & 1 deletion src/components/TagPicker/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import ONYXKEYS from '../../ONYXKEYS';
import styles from '../../styles/styles';
import useLocalize from '../../hooks/useLocalize';
import * as OptionsListUtils from '../../libs/OptionsListUtils';
import * as PolicyUtils from '../../libs/PolicyUtils';
import OptionsSelector from '../OptionsSelector';
import {propTypes, defaultProps} from './tagPickerPropTypes';

Expand All @@ -15,7 +16,7 @@ function TagPicker({selectedTag, tag, policyTags, policyRecentlyUsedTags, onSubm
const [searchValue, setSearchValue] = useState('');

const policyRecentlyUsedTagsList = lodashGet(policyRecentlyUsedTags, tag, []);
const policyTagList = lodashGet(policyTags, [tag, 'tags'], {});
const policyTagList = PolicyUtils.getTagList(policyTags, tag);
const policyTagsCount = _.size(_.filter(policyTagList, (policyTag) => policyTag.enabled));
const isTagsCountBelowThreshold = policyTagsCount < CONST.TAG_LIST_THRESHOLD;

Expand Down
7 changes: 1 addition & 6 deletions src/components/TagPicker/tagPickerPropTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,7 @@ const propTypes = {

/* Onyx Props */
/** Collection of tags attached to a policy */
policyTags: PropTypes.objectOf(
PropTypes.shape({
name: PropTypes.string,
tags: PropTypes.objectOf(tagPropTypes),
}),
),
policyTags: tagPropTypes,

/** List of recently used tags */
policyRecentlyUsedTags: PropTypes.objectOf(PropTypes.arrayOf(PropTypes.string)),
Expand Down
10 changes: 9 additions & 1 deletion src/components/tagPropTypes.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import PropTypes from 'prop-types';

export default PropTypes.shape({
const tagListPropTypes = PropTypes.shape({
/** Name of a tag */
name: PropTypes.string.isRequired,

Expand All @@ -10,3 +10,11 @@ export default PropTypes.shape({
/** "General Ledger code" that corresponds to this tag in an accounting system. Similar to an ID. */
'GL Code': PropTypes.string,
});

export default PropTypes.objectOf(
PropTypes.shape({
name: PropTypes.string,
required: PropTypes.bool,
tags: PropTypes.objectOf(tagListPropTypes),
}),
);
53 changes: 53 additions & 0 deletions src/libs/PolicyUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,56 @@ function getIneligibleInvitees(policyMembers, personalDetails) {
return memberEmailsToExclude;
}

/**
* Gets the tag from policy tags, defaults to the first if no key is provided.
*
* @param {Object} policyTags
* @param {String} [tagKey]
* @returns {Object}
*/
function getTag(policyTags, tagKey) {
if (_.isEmpty(policyTags)) {
return {};
}

const policyTagKey = tagKey || _.first(_.keys(policyTags));

return lodashGet(policyTags, policyTagKey, {});
}

/**
* Gets the first tag name from policy tags.
*
* @param {Object} policyTags
* @returns {String}
*/
function getTagListName(policyTags) {
if (_.isEmpty(policyTags)) {
return '';
}

const policyTagKeys = _.keys(policyTags) || [];

return lodashGet(policyTags, [_.first(policyTagKeys), 'name'], '');
}

/**
* Gets the tags of a policy for a specific key. Defaults to the first tag if no key is provided.
*
* @param {Object} policyTags
* @param {String} [tagKey]
* @returns {String}
*/
function getTagList(policyTags, tagKey) {
if (_.isEmpty(policyTags)) {
return {};
}

const policyTagKey = tagKey || _.first(_.keys(policyTags));

return lodashGet(policyTags, [policyTagKey, 'tags'], {});
}

/**
* @param {Object} policy
* @returns {Boolean}
Expand All @@ -226,5 +276,8 @@ export {
isPolicyAdmin,
getMemberAccountIDsForWorkspace,
getIneligibleInvitees,
getTag,
getTagListName,
getTagList,
isPendingDeletePolicy,
};
11 changes: 11 additions & 0 deletions src/libs/ReportUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -1341,6 +1341,7 @@ function getTransactionDetails(transaction) {
comment: TransactionUtils.getDescription(transaction),
merchant: TransactionUtils.getMerchant(transaction),
category: TransactionUtils.getCategory(transaction),
tag: TransactionUtils.getTag(transaction),
};
}

Expand Down Expand Up @@ -1592,6 +1593,11 @@ function getModifiedExpenseMessage(reportAction) {
if (hasModifiedCategory) {
return getProperSchemaForModifiedExpenseMessage(reportActionOriginalMessage.category, reportActionOriginalMessage.oldCategory, Localize.translateLocal('common.category'), true);
}

const hasModifiedTag = _.has(reportActionOriginalMessage, 'oldTag') && _.has(reportActionOriginalMessage, 'tag');
if (hasModifiedTag) {
return getProperSchemaForModifiedExpenseMessage(reportActionOriginalMessage.tag, reportActionOriginalMessage.oldTag, Localize.translateLocal('common.tag'), true);
}
}

/**
Expand Down Expand Up @@ -1637,6 +1643,11 @@ function getModifiedExpenseOriginalMessage(oldTransaction, transactionChanges, i
originalMessage.category = transactionChanges.category;
}

if (_.has(transactionChanges, 'tag')) {
originalMessage.oldTag = TransactionUtils.getTag(oldTransaction);
originalMessage.tag = transactionChanges.tag;
}

return originalMessage;
}

Expand Down
16 changes: 16 additions & 0 deletions src/libs/TransactionUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,10 @@ function getUpdatedTransaction(transaction, transactionChanges, isFromExpenseRep
updatedTransaction.category = transactionChanges.category;
}

if (_.has(transactionChanges, 'tag')) {
updatedTransaction.tag = transactionChanges.tag;
}

if (shouldStopSmartscan && _.has(transaction, 'receipt') && !_.isEmpty(transaction.receipt) && lodashGet(transaction, 'receipt.state') !== CONST.IOU.RECEIPT_STATE.OPEN) {
updatedTransaction.receipt.state = CONST.IOU.RECEIPT_STATE.OPEN;
}
Expand All @@ -162,6 +166,7 @@ function getUpdatedTransaction(transaction, transactionChanges, isFromExpenseRep
...(_.has(transactionChanges, 'currency') && {currency: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE}),
...(_.has(transactionChanges, 'merchant') && {merchant: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE}),
...(_.has(transactionChanges, 'category') && {category: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE}),
...(_.has(transactionChanges, 'tag') && {tag: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE}),
};

return updatedTransaction;
Expand Down Expand Up @@ -253,6 +258,16 @@ function getCategory(transaction) {
return lodashGet(transaction, 'category', '');
}

/**
* Return the tag from the transaction. This "tag" field has no "modified" complement.
*
* @param {Object} transaction
* @return {String}
*/
function getTag(transaction) {
return lodashGet(transaction, 'tag', '');
}

/**
* Return the created field from the transaction, return the modifiedCreated if present.
*
Expand Down Expand Up @@ -399,6 +414,7 @@ export {
getMerchant,
getCreated,
getCategory,
getTag,
getLinkedTransaction,
getAllReportTransactions,
hasReceipt,
Expand Down
23 changes: 22 additions & 1 deletion src/libs/actions/IOU.js
Original file line number Diff line number Diff line change
Expand Up @@ -1136,6 +1136,17 @@ function editMoneyRequest(transactionID, transactionThreadReportID, transactionC
updatedChatReport.lastMessageHtml = messageText;
}

const optimisticPolicyRecentlyUsedTags = {};
if (_.has(transactionChanges, 'tag')) {
const tagListName = transactionChanges.tagListName;
const recentlyUsedPolicyTags = allRecentlyUsedTags[`${ONYXKEYS.COLLECTION.POLICY_RECENTLY_USED_TAGS}${iouReport.policyID}`];

if (recentlyUsedPolicyTags) {
const uniquePolicyRecentlyUsedTags = _.filter(recentlyUsedPolicyTags[tagListName], (recentlyUsedPolicyTag) => recentlyUsedPolicyTag !== transactionChanges.tag);
optimisticPolicyRecentlyUsedTags[tagListName] = [transactionChanges.tag, ...uniquePolicyRecentlyUsedTags];
}
}

// STEP 4: Compose the optimistic data
const currentTime = DateUtils.getDBTime();
const optimisticData = [
Expand Down Expand Up @@ -1171,6 +1182,14 @@ function editMoneyRequest(transactionID, transactionThreadReportID, transactionC
},
];

if (!_.isEmpty(optimisticPolicyRecentlyUsedTags)) {
optimisticData.push({
onyxMethod: Onyx.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.POLICY_RECENTLY_USED_TAGS}${iouReport.policyID}`,
value: optimisticPolicyRecentlyUsedTags,
});
}

const successData = [
{
onyxMethod: Onyx.METHOD.MERGE,
Expand All @@ -1190,6 +1209,7 @@ function editMoneyRequest(transactionID, transactionThreadReportID, transactionC
currency: null,
merchant: null,
category: null,
tag: null,
},
},
},
Expand Down Expand Up @@ -1236,7 +1256,7 @@ function editMoneyRequest(transactionID, transactionThreadReportID, transactionC
];

// STEP 6: Call the API endpoint
const {created, amount, currency, comment, merchant, category} = ReportUtils.getTransactionDetails(updatedTransaction);
const {created, amount, currency, comment, merchant, category, tag} = ReportUtils.getTransactionDetails(updatedTransaction);
API.write(
'EditMoneyRequest',
{
Expand All @@ -1248,6 +1268,7 @@ function editMoneyRequest(transactionID, transactionThreadReportID, transactionC
comment,
merchant,
category,
tag,
},
{optimisticData, successData, failureData},
);
Expand Down
Loading

0 comments on commit f0c9bbe

Please sign in to comment.