diff --git a/src/CONST.ts b/src/CONST.ts
index 4d7049271e78..7b1d941bb5ed 100755
--- a/src/CONST.ts
+++ b/src/CONST.ts
@@ -3717,6 +3717,7 @@ const CONST = {
TAG: 'tag',
TAX_RATE: 'taxRate',
TAX_AMOUNT: 'taxAmount',
+ REIMBURSABLE: 'reimbursable',
REPORT: 'report',
},
FOOTER: {
diff --git a/src/components/MoneyRequestConfirmationList.tsx b/src/components/MoneyRequestConfirmationList.tsx
index 99f5da95537c..06d59c8c4c3f 100755
--- a/src/components/MoneyRequestConfirmationList.tsx
+++ b/src/components/MoneyRequestConfirmationList.tsx
@@ -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;
@@ -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});
@@ -1089,6 +1097,8 @@ function MoneyRequestConfirmationList({
unit={unit}
onPDFLoadError={onPDFLoadError}
onPDFPassword={onPDFPassword}
+ iouIsReimbursable={iouIsReimbursable}
+ onToggleReimbursable={onToggleReimbursable}
isReceiptEditable={isReceiptEditable}
/>
);
diff --git a/src/components/MoneyRequestConfirmationListFooter.tsx b/src/components/MoneyRequestConfirmationListFooter.tsx
index 15b5cf32d0bd..cc6401dde660 100644
--- a/src/components/MoneyRequestConfirmationListFooter.tsx
+++ b/src/components/MoneyRequestConfirmationListFooter.tsx
@@ -25,6 +25,7 @@ import {
getTaxAmount,
getTaxName,
isAmountMissing,
+ isCardTransaction,
isCreatedMissing,
isFetchingWaypointsFromServer,
shouldShowAttendees as shouldShowAttendeesTransactionUtils,
@@ -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({
@@ -246,6 +253,8 @@ function MoneyRequestConfirmationListFooter({
unit,
onPDFLoadError,
onPDFPassword,
+ iouIsReimbursable,
+ onToggleReimbursable,
isReceiptEditable = false,
}: MoneyRequestConfirmationListFooterProps) {
const styles = useThemeStyles();
@@ -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
@@ -636,6 +646,25 @@ function MoneyRequestConfirmationListFooter({
shouldShow: shouldShowAttendees,
isSupplementary: true,
},
+ {
+ item: (
+
+ onToggleReimbursable?.(isOn)}
+ isActive={iouIsReimbursable}
+ disabled={isReadOnly}
+ wrapperStyle={styles.flex1}
+ />
+
+ ),
+ shouldShow: shouldShowReimbursable,
+ isSupplementary: true,
+ },
{
item: (
shouldShowAttendeesTransactionUtils(iouType, policy), [iouType, policy]);
const shouldShowTax = isTaxTrackingEnabled(isPolicyExpenseChat, policy, isDistanceRequest, isPerDiemRequest);
@@ -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}`;
@@ -800,6 +817,19 @@ function MoneyRequestView({report, shouldShowAnimatedBackground, readonly = fals
/>
)}
+ {shouldShowReimbursable && (
+
+
+ {Str.UCFirst(translate('iou.reimbursable'))}
+
+
+
+ )}
{/* Note: "Billable" toggle and "View trip details" should be always the last two items */}
{shouldShowBillable && (
diff --git a/src/libs/API/parameters/CreateDistanceRequestParams.ts b/src/libs/API/parameters/CreateDistanceRequestParams.ts
index 73f08deb6c4f..738393903c36 100644
--- a/src/libs/API/parameters/CreateDistanceRequestParams.ts
+++ b/src/libs/API/parameters/CreateDistanceRequestParams.ts
@@ -15,6 +15,7 @@ type CreateDistanceRequestParams = {
taxCode?: string;
taxAmount?: number;
billable?: boolean;
+ reimbursable?: boolean;
transactionThreadReportID?: string;
createdReportActionIDForThread?: string;
payerEmail?: string;
diff --git a/src/libs/API/parameters/CreatePerDiemRequestParams.ts b/src/libs/API/parameters/CreatePerDiemRequestParams.ts
index 480acd9ca23d..85ff52eadaf1 100644
--- a/src/libs/API/parameters/CreatePerDiemRequestParams.ts
+++ b/src/libs/API/parameters/CreatePerDiemRequestParams.ts
@@ -20,6 +20,7 @@ type CreatePerDiemRequestParams = {
transactionThreadReportID: string;
createdReportActionIDForThread: string | undefined;
billable?: boolean;
+ reimbursable?: boolean;
attendees?: string;
};
diff --git a/src/libs/API/parameters/RequestMoneyParams.ts b/src/libs/API/parameters/RequestMoneyParams.ts
index 74a435600cd2..d059aec8e597 100644
--- a/src/libs/API/parameters/RequestMoneyParams.ts
+++ b/src/libs/API/parameters/RequestMoneyParams.ts
@@ -27,7 +27,7 @@ type RequestMoneyParams = {
receiptGpsPoints?: string;
transactionThreadReportID: string;
createdReportActionIDForThread: string | undefined;
- reimbursible?: boolean;
+ reimbursable?: boolean;
description?: string;
attendees?: string;
};
diff --git a/src/libs/API/parameters/SplitBillParams.ts b/src/libs/API/parameters/SplitBillParams.ts
index 3fda11b9ca98..bc99bf176e2d 100644
--- a/src/libs/API/parameters/SplitBillParams.ts
+++ b/src/libs/API/parameters/SplitBillParams.ts
@@ -9,6 +9,7 @@ type SplitBillParams = {
category: string;
tag: string;
billable: boolean;
+ reimbursable: boolean;
transactionID: string;
reportActionID: string;
createdReportActionID?: string;
diff --git a/src/libs/API/parameters/StartSplitBillParams.ts b/src/libs/API/parameters/StartSplitBillParams.ts
index 10f1029a0fba..031d75aca525 100644
--- a/src/libs/API/parameters/StartSplitBillParams.ts
+++ b/src/libs/API/parameters/StartSplitBillParams.ts
@@ -13,6 +13,7 @@ type StartSplitBillParams = {
isFromGroupDM: boolean;
createdReportActionID?: string;
billable: boolean;
+ reimbursable: boolean;
chatType?: string;
taxCode?: string;
taxAmount?: number;
diff --git a/src/libs/API/types.ts b/src/libs/API/types.ts
index 950c15dc3af9..bd38bc2e77f8 100644
--- a/src/libs/API/types.ts
+++ b/src/libs/API/types.ts
@@ -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',
@@ -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;
diff --git a/src/libs/ModifiedExpenseMessage.ts b/src/libs/ModifiedExpenseMessage.ts
index c4a9028118dc..0f438053f807 100644
--- a/src/libs/ModifiedExpenseMessage.ts
+++ b/src/libs/ModifiedExpenseMessage.ts
@@ -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.
*/
@@ -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,
diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts
index 11882161be61..65ed4ac9d293 100644
--- a/src/libs/ReportUtils.ts
+++ b/src/libs/ReportUtils.ts
@@ -660,6 +660,7 @@ type TransactionDetails = {
customUnitRateID?: string;
comment: string;
category: string;
+ reimbursable: boolean;
billable: boolean;
tag: string;
mccGroup?: ValueOf;
@@ -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),
@@ -3837,6 +3839,7 @@ function canEditFieldOfMoneyRequest(reportAction: OnyxInputOrEntry
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,
];
@@ -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();
diff --git a/src/libs/TransactionUtils/index.ts b/src/libs/TransactionUtils/index.ts
index beba9b35c280..6b031ddb7320 100644
--- a/src/libs/TransactionUtils/index.ts
+++ b/src/libs/TransactionUtils/index.ts
@@ -486,6 +486,10 @@ function getUpdatedTransaction({
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;
}
@@ -526,6 +530,7 @@ function getUpdatedTransaction({
...(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}),
@@ -684,7 +689,7 @@ function getFormattedAttendees(modifiedAttendees?: Attendee[], attendees?: Atten
/**
* Return the reimbursable value. Defaults to true to match BE logic.
*/
-function getReimbursable(transaction: Transaction): boolean {
+function getReimbursable(transaction: OnyxInputOrEntry): boolean {
return transaction?.reimbursable ?? true;
}
diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts
index 0fdb551ae682..35405a7d9672 100644
--- a/src/libs/actions/IOU.ts
+++ b/src/libs/actions/IOU.ts
@@ -234,6 +234,7 @@ type BaseTransactionParams = {
taxCode?: string;
taxAmount?: number;
billable?: boolean;
+ reimbursable?: boolean;
customUnitRateID?: string;
};
@@ -251,6 +252,7 @@ type MoneyRequestInformation = {
createdReportActionIDForThread: string | undefined;
onyxData: OnyxData;
billable?: boolean;
+ reimbursable?: boolean;
};
type TrackExpenseInformation = {
@@ -404,7 +406,6 @@ type RequestMoneyInformation = {
policyParams?: BasePolicyParams;
gpsPoints?: GPSPoint;
action?: IOUAction;
- reimbursible?: boolean;
transactionParams: RequestMoneyTransactionParams;
isRetry?: boolean;
shouldHandleNavigation?: boolean;
@@ -605,6 +606,7 @@ type StartSplitBilActionParams = {
receipt: Receipt;
existingSplitChatReportID?: string;
billable?: boolean;
+ reimbursable?: boolean;
category: string | undefined;
tag: string | undefined;
currency: string;
@@ -1016,6 +1018,10 @@ function setMoneyRequestBillable(transactionID: string, billable: boolean) {
Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}`, {billable});
}
+function setMoneyRequestReimbursable(transactionID: string, reimbursable: boolean) {
+ Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}`, {reimbursable});
+}
+
function setMoneyRequestParticipants(transactionID: string, participants: Participant[] = [], isTestTransaction = false) {
// We should change the reportID and isFromGlobalCreate of the test transaction since this flow can start inside an existing report
return Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}`, {
@@ -3342,7 +3348,7 @@ function getPerDiemExpenseInformation(perDiemExpenseInformation: PerDiemExpenseI
const {parentChatReport, transactionParams, participantParams, policyParams = {}, moneyRequestReportID = ''} = perDiemExpenseInformation;
const {payeeAccountID = userAccountID, payeeEmail = currentUserEmail, participant} = participantParams;
const {policy, policyCategories, policyTagList} = policyParams;
- const {comment = '', currency, created, category, tag, customUnit, billable, attendees} = transactionParams;
+ const {comment = '', currency, created, category, tag, customUnit, billable, attendees, reimbursable} = transactionParams;
const amount = computePerDiemExpenseAmount(customUnit);
const merchant = computePerDiemExpenseMerchant(customUnit, policy);
@@ -3420,6 +3426,7 @@ function getPerDiemExpenseInformation(perDiemExpenseInformation: PerDiemExpenseI
tag,
customUnit,
billable,
+ reimbursable,
pendingFields: {subRates: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD},
attendees,
},
@@ -3537,6 +3544,7 @@ function getPerDiemExpenseInformation(perDiemExpenseInformation: PerDiemExpenseI
failureData,
},
billable,
+ reimbursable,
};
}
@@ -4389,6 +4397,24 @@ function updateMoneyRequestBillable(
API.write(WRITE_COMMANDS.UPDATE_MONEY_REQUEST_BILLABLE, params, onyxData);
}
+function updateMoneyRequestReimbursable(
+ transactionID: string | undefined,
+ transactionThreadReportID: string | undefined,
+ value: boolean,
+ policy: OnyxEntry,
+ policyTagList: OnyxEntry,
+ policyCategories: OnyxEntry,
+) {
+ if (!transactionID || !transactionThreadReportID) {
+ return;
+ }
+ const transactionChanges: TransactionChanges = {
+ reimbursable: value,
+ };
+ const {params, onyxData} = getUpdateMoneyRequestParams(transactionID, transactionThreadReportID, transactionChanges, policy, policyTagList, policyCategories);
+ API.write(WRITE_COMMANDS.UPDATE_MONEY_REQUEST_REIMBURSABLE, params, onyxData);
+}
+
/** Updates the merchant field of an expense */
function updateMoneyRequestMerchant(
transactionID: string,
@@ -4714,6 +4740,7 @@ type ConvertTrackedWorkspaceParams = {
receipt: Receipt | undefined;
waypoints?: string;
customUnitRateID?: string;
+ reimbursable?: boolean;
};
type AddTrackedExpenseToPolicyParam = {
@@ -4999,7 +5026,7 @@ function shareTrackedExpense(trackedExpenseParams: TrackedExpenseParams) {
* Submit expense to another user
*/
function requestMoney(requestMoneyInformation: RequestMoneyInformation) {
- const {report, participantParams, policyParams = {}, transactionParams, gpsPoints, action, reimbursible, shouldHandleNavigation = true, backToReport} = requestMoneyInformation;
+ const {report, participantParams, policyParams = {}, transactionParams, gpsPoints, action, shouldHandleNavigation = true, backToReport} = requestMoneyInformation;
const {payeeAccountID} = participantParams;
const parsedComment = getParsedComment(transactionParams.comment ?? '');
transactionParams.comment = parsedComment;
@@ -5014,6 +5041,7 @@ function requestMoney(requestMoneyInformation: RequestMoneyInformation) {
taxCode = '',
taxAmount = 0,
billable,
+ reimbursable,
created,
attendees,
actionableWhisperReportActionID,
@@ -5093,6 +5121,7 @@ function requestMoney(requestMoneyInformation: RequestMoneyInformation) {
policyID: chatReport.policyID,
waypoints: sanitizedWaypoints,
customUnitRateID,
+ reimbursable,
}
: undefined;
convertTrackedExpenseToRequest({
@@ -5155,7 +5184,7 @@ function requestMoney(requestMoneyInformation: RequestMoneyInformation) {
receiptGpsPoints: gpsPoints ? JSON.stringify(gpsPoints) : undefined,
transactionThreadReportID,
createdReportActionIDForThread,
- reimbursible,
+ reimbursable,
description: parsedComment,
attendees: attendees ? JSON.stringify(attendees) : undefined,
};
@@ -5217,6 +5246,7 @@ function submitPerDiemExpense(submitPerDiemExpenseInformation: PerDiemExpenseInf
createdReportActionIDForThread,
onyxData,
billable,
+ reimbursable,
} = getPerDiemExpenseInformation({
parentChatReport: currentChatReport,
participantParams,
@@ -5248,6 +5278,7 @@ function submitPerDiemExpense(submitPerDiemExpenseInformation: PerDiemExpenseInf
transactionThreadReportID,
createdReportActionIDForThread,
billable,
+ reimbursable,
attendees: attendees ? JSON.stringify(attendees) : undefined,
};
@@ -6115,6 +6146,7 @@ type SplitBillActionsParams = {
category?: string;
tag?: string;
billable?: boolean;
+ reimbursable?: boolean;
iouRequestType?: IOURequestType;
existingSplitChatReportID?: string;
splitShares?: SplitShares;
@@ -6140,6 +6172,7 @@ function splitBill({
category = '',
tag = '',
billable = false,
+ reimbursable = false,
iouRequestType = CONST.IOU.REQUEST_TYPE.MANUAL,
existingSplitChatReportID,
splitShares = {},
@@ -6163,6 +6196,7 @@ function splitBill({
tag,
splitShares,
billable,
+ reimbursable,
iouRequestType,
taxCode,
taxAmount,
@@ -6180,6 +6214,7 @@ function splitBill({
created,
tag,
billable,
+ reimbursable,
transactionID: splitData.transactionID,
reportActionID: splitData.reportActionID,
createdReportActionID: splitData.createdReportActionID,
@@ -6214,6 +6249,7 @@ function splitBillAndOpenReport({
category = '',
tag = '',
billable = false,
+ reimbursable = false,
iouRequestType = CONST.IOU.REQUEST_TYPE.MANUAL,
splitShares = {},
splitPayerAccountIDs = [],
@@ -6237,6 +6273,7 @@ function splitBillAndOpenReport({
tag,
splitShares,
billable,
+ reimbursable,
iouRequestType,
taxCode,
taxAmount,
@@ -6254,6 +6291,7 @@ function splitBillAndOpenReport({
category,
tag,
billable,
+ reimbursable,
transactionID: splitData.transactionID,
reportActionID: splitData.reportActionID,
createdReportActionID: splitData.createdReportActionID,
@@ -6285,6 +6323,7 @@ function startSplitBill({
receipt,
existingSplitChatReportID,
billable = false,
+ reimbursable = false,
category = '',
tag = '',
currency,
@@ -6314,6 +6353,7 @@ function startSplitBill({
taxCode,
taxAmount,
billable,
+ reimbursable,
filename,
},
});
@@ -6449,6 +6489,7 @@ function startSplitBill({
receipt: receiptObject,
existingSplitChatReportID,
billable,
+ reimbursable,
category,
tag,
currency,
@@ -6598,6 +6639,7 @@ function startSplitBill({
currency,
isFromGroupDM: !existingSplitChatReport,
billable,
+ reimbursable,
...(existingSplitChatReport ? {} : {createdReportActionID: splitChatCreatedReportAction.reportActionID}),
chatType: splitChatReport?.chatType,
taxCode,
@@ -6912,7 +6954,23 @@ function createDistanceRequest(distanceRequestInformation: CreateDistanceRequest
const {policy, policyCategories, policyTagList} = policyParams;
const parsedComment = getParsedComment(transactionParams.comment);
transactionParams.comment = parsedComment;
- const {amount, comment, currency, created, category, tag, taxAmount, taxCode, merchant, billable, validWaypoints, customUnitRateID = '', splitShares = {}, attendees} = transactionParams;
+ const {
+ amount,
+ comment,
+ currency,
+ created,
+ category,
+ tag,
+ taxAmount,
+ taxCode,
+ merchant,
+ billable,
+ reimbursable,
+ validWaypoints,
+ customUnitRateID = '',
+ splitShares = {},
+ attendees,
+ } = transactionParams;
// 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 = isMoneyRequestReportReportUtils(report);
@@ -6971,6 +7029,7 @@ function createDistanceRequest(distanceRequestInformation: CreateDistanceRequest
taxCode,
taxAmount,
billable,
+ reimbursable,
splits: JSON.stringify(splits),
chatType: splitData.chatType,
description: parsedComment,
@@ -7016,6 +7075,7 @@ function createDistanceRequest(distanceRequestInformation: CreateDistanceRequest
taxCode,
taxAmount,
billable,
+ reimbursable,
attendees,
},
});
@@ -7038,6 +7098,7 @@ function createDistanceRequest(distanceRequestInformation: CreateDistanceRequest
taxCode,
taxAmount,
billable,
+ reimbursable,
transactionThreadReportID,
createdReportActionIDForThread,
payerEmail,
@@ -11011,6 +11072,7 @@ export {
unholdRequest,
updateMoneyRequestAttendees,
updateMoneyRequestAmountAndCurrency,
+ updateMoneyRequestReimbursable,
updateMoneyRequestBillable,
updateMoneyRequestCategory,
updateMoneyRequestDate,
@@ -11030,5 +11092,6 @@ export {
canSubmitReport,
submitPerDiemExpense,
calculateDiffAmount,
+ setMoneyRequestReimbursable,
};
export type {GPSPoint as GpsPoint, IOURequestType, StartSplitBilActionParams, CreateTrackExpenseParams, RequestMoneyInformation, ReplaceReceipt};
diff --git a/src/libs/actions/Policy/Policy.ts b/src/libs/actions/Policy/Policy.ts
index 5faebc8c7323..a1db72bcc3dc 100644
--- a/src/libs/actions/Policy/Policy.ts
+++ b/src/libs/actions/Policy/Policy.ts
@@ -1760,7 +1760,8 @@ function createDraftInitialWorkspace(policyOwnerEmail = '', policyName = '', pol
},
areWorkflowsEnabled: shouldEnableWorkflowsByDefault,
defaultBillable: false,
- disabledFields: {defaultBillable: true},
+ defaultReimbursable: true,
+ disabledFields: {defaultBillable: true, reimbursable: false},
requiresCategory: true,
},
},
@@ -1867,7 +1868,8 @@ function buildPolicyData(
type: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD,
},
defaultBillable: false,
- disabledFields: {defaultBillable: true},
+ defaultReimbursable: true,
+ disabledFields: {defaultBillable: true, reimbursable: false},
avatarURL: file?.uri,
originalFileName: file?.name,
...optimisticMccGroupData.optimisticData,
diff --git a/src/pages/iou/request/step/IOURequestStepConfirmation.tsx b/src/pages/iou/request/step/IOURequestStepConfirmation.tsx
index 55a015e4d9e6..5e982980079c 100644
--- a/src/pages/iou/request/step/IOURequestStepConfirmation.tsx
+++ b/src/pages/iou/request/step/IOURequestStepConfirmation.tsx
@@ -47,6 +47,7 @@ import {
setMoneyRequestBillable,
setMoneyRequestCategory,
setMoneyRequestReceipt,
+ setMoneyRequestReimbursable,
splitBill,
splitBillAndOpenReport,
startMoneyRequest,
@@ -235,6 +236,13 @@ function IOURequestStepConfirmation({
});
}, [transactionIDs, defaultBillable]);
+ const defaultReimbursable = !!policy?.defaultReimbursable;
+ useEffect(() => {
+ transactionIDs.forEach((transactionID) => {
+ setMoneyRequestReimbursable(transactionID, defaultReimbursable);
+ });
+ }, [transactionIDs, defaultReimbursable]);
+
useEffect(() => {
// Exit early if the transaction is still loading
if (isLoadingTransaction) {
@@ -475,6 +483,7 @@ function IOURequestStepConfirmation({
taxCode: transactionTaxCode,
taxAmount: transactionTaxAmount,
billable: item.billable,
+ reimbursable: item.reimbursable,
actionableWhisperReportActionID: item.actionableWhisperReportActionID,
linkedTrackedExpenseReportAction: item.linkedTrackedExpenseReportAction,
linkedTrackedExpenseReportID: item.linkedTrackedExpenseReportID,
@@ -533,6 +542,7 @@ function IOURequestStepConfirmation({
tag: transaction.tag,
customUnit: transaction.comment?.customUnit,
billable: transaction.billable,
+ reimbursable: transaction.reimbursable,
attendees: transaction.comment?.attendees,
},
});
@@ -639,6 +649,7 @@ function IOURequestStepConfirmation({
splitShares: transaction.splitShares,
validWaypoints: getValidWaypoints(transaction.comment?.waypoints, true),
billable: transaction.billable,
+ reimbursable: transaction.reimbursable,
attendees: transaction.comment?.attendees,
},
backToReport,
@@ -704,6 +715,7 @@ function IOURequestStepConfirmation({
receipt: currentTransactionReceiptFile,
existingSplitChatReportID: report?.reportID,
billable: transaction.billable,
+ reimbursable: transaction.reimbursable,
category: transaction.category,
tag: transaction.tag,
currency: transaction.currency,
@@ -731,6 +743,7 @@ function IOURequestStepConfirmation({
tag: transaction.tag,
existingSplitChatReportID: report?.reportID,
billable: transaction.billable,
+ reimbursable: transaction.reimbursable,
iouRequestType: transaction.iouRequestType,
splitShares: transaction.splitShares,
splitPayerAccountIDs: transaction.splitPayerAccountIDs ?? [],
@@ -756,6 +769,7 @@ function IOURequestStepConfirmation({
category: transaction.category,
tag: transaction.tag,
billable: !!transaction.billable,
+ reimbursable: !!transaction.reimbursable,
iouRequestType: transaction.iouRequestType,
splitShares: transaction.splitShares,
splitPayerAccountIDs: transaction.splitPayerAccountIDs,
@@ -919,6 +933,13 @@ function IOURequestStepConfirmation({
[currentTransactionID],
);
+ const setReimbursable = useCallback(
+ (reimbursable: boolean) => {
+ setMoneyRequestReimbursable(currentTransactionID, reimbursable);
+ },
+ [currentTransactionID],
+ );
+
// This loading indicator is shown because the transaction originalCurrency is being updated later than the component mounts.
// To prevent the component from rendering with the wrong currency, we show a loading indicator until the correct currency is set.
const isLoading = !!transaction?.originalCurrency;
@@ -1082,6 +1103,8 @@ function IOURequestStepConfirmation({
shouldPlaySound={iouType === CONST.IOU.TYPE.PAY}
isConfirmed={isConfirmed}
isConfirming={isConfirming}
+ iouIsReimbursable={transaction?.reimbursable}
+ onToggleReimbursable={setReimbursable}
isReceiptEditable
/>
diff --git a/src/pages/iou/request/step/IOURequestStepDistance.tsx b/src/pages/iou/request/step/IOURequestStepDistance.tsx
index 3c39fee66263..bb523775fca2 100644
--- a/src/pages/iou/request/step/IOURequestStepDistance.tsx
+++ b/src/pages/iou/request/step/IOURequestStepDistance.tsx
@@ -353,6 +353,7 @@ function IOURequestStepDistance({
currency: transaction?.currency ?? 'USD',
merchant: translate('iou.fieldPending'),
billable: !!policy?.defaultBillable,
+ reimbursable: !!policy?.defaultReimbursable,
validWaypoints: getValidWaypoints(waypoints, true),
customUnitRateID: DistanceRequestUtils.getCustomUnitRateID(report.reportID),
splitShares: transaction?.splitShares,
diff --git a/src/types/onyx/Policy.ts b/src/types/onyx/Policy.ts
index eadece5d418a..08d5877c86e0 100644
--- a/src/types/onyx/Policy.ts
+++ b/src/types/onyx/Policy.ts
@@ -1740,6 +1740,9 @@ type Policy = OnyxCommon.OnyxValueWithOfflineFeedback<
/** Whether transactions should be billable by default */
defaultBillable?: boolean;
+ /** Whether transactions should be reimbursable by default */
+ defaultReimbursable?: boolean;
+
/** The workspace description */
description?: string;