diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 3f5b2f9cdbd1..0a50b6549bb0 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -6677,6 +6677,11 @@ function populateOptimisticReportFormula(formula: string, report: OptimisticExpe const createdDate = report.lastVisibleActionCreated ? new Date(report.lastVisibleActionCreated) : undefined; + const totalAmount = report.total !== undefined && !Number.isNaN(report.total) ? Math.abs(report.total) : 0; + const nonReimbursableTotal = + 'nonReimbursableTotal' in report && report.nonReimbursableTotal !== undefined && !Number.isNaN(report.nonReimbursableTotal) ? Math.abs(report.nonReimbursableTotal) : 0; + const reimbursableAmount = totalAmount - nonReimbursableTotal; + const result = formula // We don't translate because the server response is always in English .replaceAll(/\{report:type\}/gi, 'Expense Report') @@ -6684,6 +6689,7 @@ function populateOptimisticReportFormula(formula: string, report: OptimisticExpe .replaceAll(/\{report:enddate\}/gi, createdDate ? format(createdDate, CONST.DATE.FNS_FORMAT_STRING) : '') .replaceAll(/\{report:id\}/gi, getBase62ReportID(Number(report.reportID))) .replaceAll(/\{report:total\}/gi, report.total !== undefined && !Number.isNaN(report.total) ? convertToDisplayString(Math.abs(report.total), report.currency).toString() : '') + .replaceAll(/\{report:reimbursable\}/gi, report.total !== undefined && !Number.isNaN(report.total) ? convertToDisplayString(reimbursableAmount, report.currency).toString() : '') .replaceAll(/\{report:currency\}/gi, report.currency ?? '') .replaceAll(/\{report:policyname\}/gi, policy?.name ?? '') .replaceAll(/\{report:workspacename\}/gi, policy?.name ?? '') diff --git a/src/libs/actions/IOU/index.ts b/src/libs/actions/IOU/index.ts index 086c8b635334..89ac19a03e81 100644 --- a/src/libs/actions/IOU/index.ts +++ b/src/libs/actions/IOU/index.ts @@ -186,6 +186,7 @@ import { isSettled, isTestTransactionReport, isTrackExpenseReport, + populateOptimisticReportFormula, prepareOnboardingOnyxData, shouldCreateNewMoneyRequestReport as shouldCreateNewMoneyRequestReportReportUtils, shouldEnableNegative, @@ -2991,6 +2992,22 @@ function getDeleteTrackExpenseInformation( return {parameters, optimisticData, successData, failureData, shouldDeleteTransactionThread, chatReport}; } +/** + * Recalculates the report name using the policy's custom title formula. + * This is needed when report totals change (e.g., adding expenses or changing reimbursable status) + * to ensure the report title reflects the updated values like {report:reimbursable}. + */ +function recalculateOptimisticReportName(iouReport: OnyxTypes.Report, policy: OnyxEntry): string | undefined { + if (!policy?.fieldList?.[CONST.POLICY.FIELDS.FIELD_LIST_TITLE]) { + return undefined; + } + const titleFormula = policy.fieldList[CONST.POLICY.FIELDS.FIELD_LIST_TITLE]?.defaultValue ?? ''; + if (!titleFormula) { + return undefined; + } + return populateOptimisticReportFormula(titleFormula, iouReport as Parameters[1], policy); +} + /** * Gathers all the data needed to submit an expense. It attempts to find existing reports, iouReports, and receipts. If it doesn't find them, then * it creates optimistic versions of them and uses those instead @@ -3133,6 +3150,12 @@ function getMoneyRequestInformation(moneyRequestInformation: MoneyRequestInforma iouReport.nonReimbursableTotal = (iouReport.nonReimbursableTotal ?? 0) - amount; } } + + // Recalculate reportName to reflect updated totals + const updatedReportName = recalculateOptimisticReportName(iouReport, policy); + if (updatedReportName) { + iouReport.reportName = updatedReportName; + } } if (typeof iouReport.unheldTotal === 'number') { // Use newReportTotal in scenarios where the total is based on more than just the current transaction amount, and we need to override it manually @@ -4248,6 +4271,12 @@ function getUpdateMoneyRequestParams(params: GetUpdateMoneyRequestParamsType): U updatedMoneyRequestReport.unheldNonReimbursableTotal += updatedTransaction.reimbursable ? -updatedTransaction.amount : updatedTransaction.amount; } } + + // Recalculate reportName after all totals are updated + const updatedReportName = recalculateOptimisticReportName(updatedMoneyRequestReport, policy); + if (updatedReportName) { + updatedMoneyRequestReport.reportName = updatedReportName; + } } else { updatedMoneyRequestReport = updateIOUOwnerAndTotal( iouReport,