Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/CONST.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3717,6 +3717,7 @@ const CONST = {
TAG: 'tag',
TAX_RATE: 'taxRate',
TAX_AMOUNT: 'taxAmount',
REIMBURSABLE: 'reimbursable',
REPORT: 'report',
},
FOOTER: {
Expand Down
10 changes: 10 additions & 0 deletions src/components/MoneyRequestConfirmationList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,12 @@ type MoneyRequestConfirmationListProps = {

/** The PDF password callback */
onPDFPassword?: () => void;

/** Function to toggle reimbursable */
onToggleReimbursable?: (isOn: boolean) => void;

/** Flag indicating if the IOU is reimbursable */
iouIsReimbursable?: boolean;
};

type MoneyRequestConfirmationListItem = Participant | OptionData;
Expand Down Expand Up @@ -224,6 +230,8 @@ function MoneyRequestConfirmationList({
isConfirming,
onPDFLoadError,
onPDFPassword,
iouIsReimbursable = true,
onToggleReimbursable,
}: MoneyRequestConfirmationListProps) {
const [policyCategoriesReal] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`, {canBeMissing: true});
const [policyTags] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`, {canBeMissing: true});
Expand Down Expand Up @@ -1089,6 +1097,8 @@ function MoneyRequestConfirmationList({
unit={unit}
onPDFLoadError={onPDFLoadError}
onPDFPassword={onPDFPassword}
iouIsReimbursable={iouIsReimbursable}
onToggleReimbursable={onToggleReimbursable}
isReceiptEditable={isReceiptEditable}
/>
);
Expand Down
29 changes: 29 additions & 0 deletions src/components/MoneyRequestConfirmationListFooter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import {
getTaxAmount,
getTaxName,
isAmountMissing,
isCardTransaction,
isCreatedMissing,
isFetchingWaypointsFromServer,
shouldShowAttendees as shouldShowAttendeesTransactionUtils,
Expand Down Expand Up @@ -196,6 +197,12 @@ type MoneyRequestConfirmationListFooterProps = {

/** The PDF password callback */
onPDFPassword?: () => void;

/** Function to toggle reimbursable */
onToggleReimbursable?: (isOn: boolean) => void;

/** Flag indicating if the IOU is reimbursable */
iouIsReimbursable: boolean;
};

function MoneyRequestConfirmationListFooter({
Expand Down Expand Up @@ -246,6 +253,8 @@ function MoneyRequestConfirmationListFooter({
unit,
onPDFLoadError,
onPDFPassword,
iouIsReimbursable,
onToggleReimbursable,
isReceiptEditable = false,
}: MoneyRequestConfirmationListFooterProps) {
const styles = useThemeStyles();
Expand Down Expand Up @@ -312,6 +321,7 @@ function MoneyRequestConfirmationListFooter({
const canModifyTaxFields = !isReadOnly && !isDistanceRequest && !isPerDiemRequest;
// A flag for showing the billable field
const shouldShowBillable = policy?.disabledFields?.defaultBillable === false;
const shouldShowReimbursable = policy?.disabledFields?.reimbursable === false && !isCardTransaction(transaction);
// Do not hide fields in case of paying someone
const shouldShowAllFields = !!isPerDiemRequest || !!isDistanceRequest || shouldExpandFields || !shouldShowSmartScanFields || isTypeSend || !!isEditingSplitBill;
// Calculate the formatted tax amount based on the transaction's tax amount and the IOU currency code
Expand Down Expand Up @@ -636,6 +646,25 @@ function MoneyRequestConfirmationListFooter({
shouldShow: shouldShowAttendees,
isSupplementary: true,
},
{
item: (
<View
key={Str.UCFirst(translate('iou.reimbursable'))}
style={[styles.flexRow, styles.justifyContentBetween, styles.alignItemsCenter, styles.ml5, styles.mr8, styles.optionRow]}
>
<ToggleSettingOptionRow
switchAccessibilityLabel={Str.UCFirst(translate('iou.reimbursable'))}
title={Str.UCFirst(translate('iou.reimbursable'))}
onToggle={(isOn) => onToggleReimbursable?.(isOn)}
isActive={iouIsReimbursable}
disabled={isReadOnly}
wrapperStyle={styles.flex1}
/>
</View>
),
shouldShow: shouldShowReimbursable,
isSupplementary: true,
},
{
item: (
<View
Expand Down
32 changes: 31 additions & 1 deletion src/components/ReportActionItem/MoneyRequestView.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import {Str} from 'expensify-common';
import mapValues from 'lodash/mapValues';
import React, {useCallback, useMemo, useState} from 'react';
import {View} from 'react-native';
Expand Down Expand Up @@ -51,6 +52,7 @@ import {
getBillable,
getDescription,
getDistanceInMeters,
getReimbursable,
getTagForDisplay,
getTaxName,
hasMissingSmartscanFields,
Expand All @@ -66,7 +68,7 @@ import {
import ViolationsUtils from '@libs/Violations/ViolationsUtils';
import Navigation from '@navigation/Navigation';
import AnimatedEmptyStateBackground from '@pages/home/report/AnimatedEmptyStateBackground';
import {cleanUpMoneyRequest, updateMoneyRequestBillable} from '@userActions/IOU';
import {cleanUpMoneyRequest, updateMoneyRequestBillable, updateMoneyRequestReimbursable} from '@userActions/IOU';
import {navigateToConciergeChatAndDeleteReport} from '@userActions/Report';
import {clearAllRelatedReportActionErrors} from '@userActions/ReportActions';
import {clearError, getLastModifiedExpense, revert} from '@userActions/Transaction';
Expand Down Expand Up @@ -153,6 +155,7 @@ function MoneyRequestView({report, shouldShowAnimatedBackground, readonly = fals
currency: transactionCurrency,
comment: transactionDescription,
merchant: transactionMerchant,
reimbursable: transactionReimbursable,
billable: transactionBillable,
category: transactionCategory,
tag: transactionTag,
Expand Down Expand Up @@ -230,6 +233,9 @@ function MoneyRequestView({report, shouldShowAnimatedBackground, readonly = fals
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
const shouldShowTag = isPolicyExpenseChat && (transactionTag || hasEnabledTags(policyTagLists));
const shouldShowBillable = isPolicyExpenseChat && (!!transactionBillable || !(policy?.disabledFields?.defaultBillable ?? true) || !!updatedTransaction?.billable);
const shouldShowReimbursable =
isPolicyExpenseChat && (!!transactionReimbursable || !(policy?.disabledFields?.reimbursable ?? true) || !!updatedTransaction?.reimbursable) && !isCardTransaction;
const canEditReimbursable = canUserPerformWriteAction && canEditFieldOfMoneyRequest(parentReportAction, CONST.EDIT_REQUEST_FIELD.REIMBURSABLE);
const shouldShowAttendees = useMemo(() => shouldShowAttendeesTransactionUtils(iouType, policy), [iouType, policy]);

const shouldShowTax = isTaxTrackingEnabled(isPolicyExpenseChat, policy, isDistanceRequest, isPerDiemRequest);
Expand Down Expand Up @@ -279,6 +285,17 @@ function MoneyRequestView({report, shouldShowAnimatedBackground, readonly = fals
[transaction, report, policy, policyTagList, policyCategories],
);

const saveReimbursable = useCallback(
(newReimbursable: boolean) => {
// If the value hasn't changed, don't request to save changes on the server and just close the modal
if (newReimbursable === getReimbursable(transaction) || !transaction?.transactionID || !report?.reportID) {
return;
}
updateMoneyRequestReimbursable(transaction.transactionID, report?.reportID, newReimbursable, policy, policyTagList, policyCategories);
},
[transaction, report, policy, policyTagList, policyCategories],
);

if (isCardTransaction) {
if (transactionPostedDate) {
dateDescription += ` ${CONST.DOT_SEPARATOR} ${translate('iou.posted')} ${transactionPostedDate}`;
Expand Down Expand Up @@ -800,6 +817,19 @@ function MoneyRequestView({report, shouldShowAnimatedBackground, readonly = fals
/>
</OfflineWithFeedback>
)}
{shouldShowReimbursable && (
<View style={[styles.flexRow, styles.optionRow, styles.justifyContentBetween, styles.alignItemsCenter, styles.ml5, styles.mr8]}>
<View>
<Text>{Str.UCFirst(translate('iou.reimbursable'))}</Text>
</View>
<Switch
accessibilityLabel={Str.UCFirst(translate('iou.reimbursable'))}
isOn={updatedTransaction?.reimbursable ?? !!transactionReimbursable}
onToggle={saveReimbursable}
disabled={!canEditReimbursable}
/>
</View>
)}
{/* Note: "Billable" toggle and "View trip details" should be always the last two items */}
{shouldShowBillable && (
<View style={[styles.flexRow, styles.optionRow, styles.justifyContentBetween, styles.alignItemsCenter, styles.ml5, styles.mr8]}>
Expand Down
1 change: 1 addition & 0 deletions src/libs/API/parameters/CreateDistanceRequestParams.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ type CreateDistanceRequestParams = {
taxCode?: string;
taxAmount?: number;
billable?: boolean;
reimbursable?: boolean;
transactionThreadReportID?: string;
createdReportActionIDForThread?: string;
payerEmail?: string;
Expand Down
1 change: 1 addition & 0 deletions src/libs/API/parameters/CreatePerDiemRequestParams.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ type CreatePerDiemRequestParams = {
transactionThreadReportID: string;
createdReportActionIDForThread: string | undefined;
billable?: boolean;
reimbursable?: boolean;
attendees?: string;
};

Expand Down
2 changes: 1 addition & 1 deletion src/libs/API/parameters/RequestMoneyParams.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ type RequestMoneyParams = {
receiptGpsPoints?: string;
transactionThreadReportID: string;
createdReportActionIDForThread: string | undefined;
reimbursible?: boolean;
reimbursable?: boolean;
description?: string;
attendees?: string;
};
Expand Down
1 change: 1 addition & 0 deletions src/libs/API/parameters/SplitBillParams.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ type SplitBillParams = {
category: string;
tag: string;
billable: boolean;
reimbursable: boolean;
transactionID: string;
reportActionID: string;
createdReportActionID?: string;
Expand Down
1 change: 1 addition & 0 deletions src/libs/API/parameters/StartSplitBillParams.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ type StartSplitBillParams = {
isFromGroupDM: boolean;
createdReportActionID?: string;
billable: boolean;
reimbursable: boolean;
chatType?: string;
taxCode?: string;
taxAmount?: number;
Expand Down
2 changes: 2 additions & 0 deletions src/libs/API/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,7 @@ const WRITE_COMMANDS = {
COMPLETE_SPLIT_BILL: 'CompleteSplitBill',
UPDATE_MONEY_REQUEST_ATTENDEES: 'UpdateMoneyRequestAttendees',
UPDATE_MONEY_REQUEST_DATE: 'UpdateMoneyRequestDate',
UPDATE_MONEY_REQUEST_REIMBURSABLE: 'UpdateMoneyRequestReimbursable',
UPDATE_MONEY_REQUEST_BILLABLE: 'UpdateMoneyRequestBillable',
UPDATE_MONEY_REQUEST_MERCHANT: 'UpdateMoneyRequestMerchant',
UPDATE_MONEY_REQUEST_TAG: 'UpdateMoneyRequestTag',
Expand Down Expand Up @@ -655,6 +656,7 @@ type WriteCommandParameters = {
[WRITE_COMMANDS.UPDATE_MONEY_REQUEST_ATTENDEES]: Parameters.UpdateMoneyRequestParams;
[WRITE_COMMANDS.UPDATE_MONEY_REQUEST_DATE]: Parameters.UpdateMoneyRequestParams;
[WRITE_COMMANDS.UPDATE_MONEY_REQUEST_MERCHANT]: Parameters.UpdateMoneyRequestParams;
[WRITE_COMMANDS.UPDATE_MONEY_REQUEST_REIMBURSABLE]: Parameters.UpdateMoneyRequestParams;
[WRITE_COMMANDS.UPDATE_MONEY_REQUEST_BILLABLE]: Parameters.UpdateMoneyRequestParams;
[WRITE_COMMANDS.UPDATE_MONEY_REQUEST_TAG]: Parameters.UpdateMoneyRequestParams;
[WRITE_COMMANDS.UPDATE_MONEY_REQUEST_TAX_AMOUNT]: Parameters.UpdateMoneyRequestParams;
Expand Down
11 changes: 2 additions & 9 deletions src/libs/ModifiedExpenseMessage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,6 @@ Onyx.connect({
callback: (value) => (allReports = value),
});

/**
* Utility to get message based on boolean literal value.
*/
function getBooleanLiteralMessage(value: string | undefined, truthyMessage: string, falsyMessage: string): string {
return value === 'true' ? truthyMessage : falsyMessage;
}

/**
* Builds the partial message fragment for a modified field on the expense.
*/
Expand Down Expand Up @@ -334,8 +327,8 @@ function getForReportAction({
const hasModifiedReimbursable = isReportActionOriginalMessageAnObject && 'oldReimbursable' in reportActionOriginalMessage && 'reimbursable' in reportActionOriginalMessage;
if (hasModifiedReimbursable) {
buildMessageFragmentForValue(
getBooleanLiteralMessage(reportActionOriginalMessage?.reimbursable, translateLocal('iou.reimbursable'), translateLocal('iou.nonReimbursable')),
getBooleanLiteralMessage(reportActionOriginalMessage?.oldReimbursable, translateLocal('iou.reimbursable'), translateLocal('iou.nonReimbursable')),
reportActionOriginalMessage?.reimbursable ?? '',
reportActionOriginalMessage?.oldReimbursable ?? '',
translateLocal('iou.expense'),
true,
setFragments,
Expand Down
9 changes: 9 additions & 0 deletions src/libs/ReportUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -660,6 +660,7 @@ type TransactionDetails = {
customUnitRateID?: string;
comment: string;
category: string;
reimbursable: boolean;
billable: boolean;
tag: string;
mccGroup?: ValueOf<typeof CONST.MCC_GROUPS>;
Expand Down Expand Up @@ -3734,6 +3735,7 @@ function getTransactionDetails(
waypoints: getWaypoints(transaction),
customUnitRateID: getRateID(transaction),
category: getCategory(transaction),
reimbursable: getReimbursable(transaction),
billable: getBillable(transaction),
tag: getTag(transaction),
mccGroup: getMCCGroup(transaction),
Expand Down Expand Up @@ -3837,6 +3839,7 @@ function canEditFieldOfMoneyRequest(reportAction: OnyxInputOrEntry<ReportAction>
CONST.EDIT_REQUEST_FIELD.RECEIPT,
CONST.EDIT_REQUEST_FIELD.DISTANCE,
CONST.EDIT_REQUEST_FIELD.DISTANCE_RATE,
CONST.EDIT_REQUEST_FIELD.REIMBURSABLE,
CONST.EDIT_REQUEST_FIELD.REPORT,
];

Expand Down Expand Up @@ -4378,6 +4381,12 @@ function getModifiedExpenseOriginalMessage(
originalMessage.currency = getCurrency(oldTransaction);
}

if ('reimbursable' in transactionChanges) {
const oldReimbursable = getReimbursable(oldTransaction);
originalMessage.oldReimbursable = oldReimbursable ? translateLocal('common.reimbursable').toLowerCase() : translateLocal('iou.nonReimbursable').toLowerCase();
originalMessage.reimbursable = transactionChanges?.reimbursable ? translateLocal('common.reimbursable').toLowerCase() : translateLocal('iou.nonReimbursable').toLowerCase();
}

if ('billable' in transactionChanges) {
const oldBillable = getBillable(oldTransaction);
originalMessage.oldBillable = oldBillable ? translateLocal('common.billable').toLowerCase() : translateLocal('common.nonBillable').toLowerCase();
Expand Down
7 changes: 6 additions & 1 deletion src/libs/TransactionUtils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -486,6 +486,10 @@
updatedTransaction.taxCode = transactionChanges.taxCode;
}

if (Object.hasOwn(transactionChanges, 'reimbursable') && typeof transactionChanges.reimbursable === 'boolean') {
updatedTransaction.reimbursable = transactionChanges.reimbursable;
}

if (Object.hasOwn(transactionChanges, 'billable') && typeof transactionChanges.billable === 'boolean') {
updatedTransaction.billable = transactionChanges.billable;
}
Expand Down Expand Up @@ -526,6 +530,7 @@
...(Object.hasOwn(transactionChanges, 'currency') && {currency: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE}),
...(Object.hasOwn(transactionChanges, 'merchant') && {merchant: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE}),
...(Object.hasOwn(transactionChanges, 'waypoints') && {waypoints: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE}),
...(Object.hasOwn(transactionChanges, 'reimbursable') && {reimbursable: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE}),
...(Object.hasOwn(transactionChanges, 'billable') && {billable: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE}),
...(Object.hasOwn(transactionChanges, 'category') && {category: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE}),
...(Object.hasOwn(transactionChanges, 'tag') && {tag: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE}),
Expand Down Expand Up @@ -684,7 +689,7 @@
/**
* Return the reimbursable value. Defaults to true to match BE logic.
*/
function getReimbursable(transaction: Transaction): boolean {
function getReimbursable(transaction: OnyxInputOrEntry<Transaction>): boolean {
return transaction?.reimbursable ?? true;
}

Expand Down Expand Up @@ -1620,7 +1625,7 @@
hasWarningTypeViolation,
isCustomUnitRateIDForP2P,
getRateID,
getTransaction,

Check failure on line 1628 in src/libs/TransactionUtils/index.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

'getTransaction' is deprecated. Get the data straight from Onyx

Check failure on line 1628 in src/libs/TransactionUtils/index.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

'getTransaction' is deprecated. Get the data straight from Onyx
compareDuplicateTransactionFields,
getTransactionID,
buildNewTransactionAfterReviewingDuplicates,
Expand Down
Loading
Loading