Skip to content

Commit 89601f2

Browse files
authored
Merge pull request Expensify#80621 from marufsharifi/fix/offline-reimbursable-title-update
Fix report title update when reimbursable expense changes offline
2 parents d7f3594 + 5bc57ea commit 89601f2

3 files changed

Lines changed: 52 additions & 2 deletions

File tree

src/components/ReportActionItem/MoneyRequestView.tsx

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,13 +54,13 @@ import {
5454
isTaxTrackingEnabled,
5555
} from '@libs/PolicyUtils';
5656
import {getOriginalMessage, isMoneyRequestAction} from '@libs/ReportActionsUtils';
57+
import {getReportName} from '@libs/ReportNameUtils';
5758
import {isSplitAction} from '@libs/ReportSecondaryActionUtils';
5859
import {
5960
canEditFieldOfMoneyRequest,
6061
canEditMoneyRequest,
6162
canUserPerformWriteAction as canUserPerformWriteActionReportUtils,
6263
// eslint-disable-next-line @typescript-eslint/no-deprecated
63-
getReportName,
6464
getTransactionDetails,
6565
getTripIDFromTransactionParentReportID,
6666
isExpenseReport,
@@ -109,6 +109,7 @@ import CONST from '@src/CONST';
109109
import type {TranslationPaths} from '@src/languages/types';
110110
import ONYXKEYS from '@src/ONYXKEYS';
111111
import ROUTES from '@src/ROUTES';
112+
import reportsSelector from '@src/selectors/Attributes';
112113
import type * as OnyxTypes from '@src/types/onyx';
113114
import type {TransactionPendingFieldsKey} from '@src/types/onyx/Transaction';
114115
import {isEmptyObject} from '@src/types/utils/EmptyObject';
@@ -179,6 +180,8 @@ function MoneyRequestView({
179180

180181
const {currentSearchResults} = useSearchContext();
181182

183+
const [reportAttributes] = useOnyx(ONYXKEYS.DERIVED.REPORT_ATTRIBUTES, {selector: reportsSelector, canBeMissing: true});
184+
182185
// When this component is used when merging from the search page, we might not have the parent report stored in the main collection
183186
let [parentReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${parentReportID}`, {canBeMissing: true});
184187
parentReport = parentReport ?? currentSearchResults?.data[`${ONYXKEYS.COLLECTION.REPORT}${parentReportID}`];
@@ -785,7 +788,9 @@ function MoneyRequestView({
785788
});
786789

787790
// eslint-disable-next-line @typescript-eslint/no-deprecated
788-
const reportNameToDisplay = isFromMergeTransaction ? (updatedTransaction?.reportName ?? translate('common.none')) : getReportName(parentReport) || parentReport?.reportName;
791+
const reportNameToDisplay = isFromMergeTransaction
792+
? (updatedTransaction?.reportName ?? translate('common.none'))
793+
: getReportName(parentReport, reportAttributes) || parentReport?.reportName;
789794
const shouldShowReport = !!parentReportID || (isFromMergeTransaction && !!reportNameToDisplay);
790795
const reportCopyValue = !canEditReport && reportNameToDisplay !== translate('common.none') ? reportNameToDisplay : undefined;
791796
const shouldShowCategoryAnalyzing = isCategoryBeingAnalyzed(updatedTransaction ?? transaction);

src/libs/ReportUtils.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6534,13 +6534,19 @@ function populateOptimisticReportFormula(formula: string, report: OptimisticExpe
65346534

65356535
const createdDate = report.lastVisibleActionCreated ? new Date(report.lastVisibleActionCreated) : undefined;
65366536

6537+
const totalAmount = report.total !== undefined && !Number.isNaN(report.total) ? Math.abs(report.total) : 0;
6538+
const nonReimbursableTotal =
6539+
'nonReimbursableTotal' in report && report.nonReimbursableTotal !== undefined && !Number.isNaN(report.nonReimbursableTotal) ? Math.abs(report.nonReimbursableTotal) : 0;
6540+
const reimbursableAmount = totalAmount - nonReimbursableTotal;
6541+
65376542
const result = formula
65386543
// We don't translate because the server response is always in English
65396544
.replaceAll(/\{report:type\}/gi, 'Expense Report')
65406545
.replaceAll(/\{report:startdate\}/gi, createdDate ? format(createdDate, CONST.DATE.FNS_FORMAT_STRING) : '')
65416546
.replaceAll(/\{report:enddate\}/gi, createdDate ? format(createdDate, CONST.DATE.FNS_FORMAT_STRING) : '')
65426547
.replaceAll(/\{report:id\}/gi, getBase62ReportID(Number(report.reportID)))
65436548
.replaceAll(/\{report:total\}/gi, report.total !== undefined && !Number.isNaN(report.total) ? convertToDisplayString(Math.abs(report.total), report.currency).toString() : '')
6549+
.replaceAll(/\{report:reimbursable\}/gi, report.total !== undefined && !Number.isNaN(report.total) ? convertToDisplayString(reimbursableAmount, report.currency).toString() : '')
65446550
.replaceAll(/\{report:currency\}/gi, report.currency ?? '')
65456551
.replaceAll(/\{report:policyname\}/gi, policy?.name ?? '')
65466552
.replaceAll(/\{report:workspacename\}/gi, policy?.name ?? '')

src/libs/actions/IOU/index.ts

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,7 @@ import {
186186
isSettled,
187187
isTestTransactionReport,
188188
isTrackExpenseReport,
189+
populateOptimisticReportFormula,
189190
prepareOnboardingOnyxData,
190191
shouldCreateNewMoneyRequestReport as shouldCreateNewMoneyRequestReportReportUtils,
191192
shouldEnableNegative,
@@ -3272,6 +3273,37 @@ function getDeleteTrackExpenseInformation(
32723273
return {parameters, optimisticData, successData, failureData, shouldDeleteTransactionThread, chatReport};
32733274
}
32743275

3276+
/**
3277+
* Recalculates the report name using the policy's custom title formula.
3278+
* This is needed when report totals change (e.g., adding expenses or changing reimbursable status)
3279+
* to ensure the report title reflects the updated values like {report:reimbursable}.
3280+
*/
3281+
function recalculateOptimisticReportName(iouReport: OnyxTypes.Report, policy: OnyxEntry<OnyxTypes.Policy>): string | undefined {
3282+
if (!policy?.fieldList?.[CONST.POLICY.FIELDS.FIELD_LIST_TITLE]) {
3283+
return undefined;
3284+
}
3285+
const titleFormula = policy.fieldList[CONST.POLICY.FIELDS.FIELD_LIST_TITLE]?.defaultValue ?? '';
3286+
if (!titleFormula) {
3287+
return undefined;
3288+
}
3289+
return populateOptimisticReportFormula(titleFormula, iouReport as Parameters<typeof populateOptimisticReportFormula>[1], policy);
3290+
}
3291+
3292+
function maybeUpdateReportNameForFormulaTitle(iouReport: OnyxTypes.Report, policy: OnyxEntry<OnyxTypes.Policy>): OnyxTypes.Report {
3293+
const reportNameValuePairs = allReportNameValuePairs?.[`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${iouReport.reportID}`];
3294+
const titleField = reportNameValuePairs?.expensify_text_title;
3295+
if (titleField?.type !== CONST.REPORT_FIELD_TYPES.FORMULA) {
3296+
return iouReport;
3297+
}
3298+
3299+
const updatedReportName = recalculateOptimisticReportName(iouReport, policy);
3300+
if (!updatedReportName) {
3301+
return iouReport;
3302+
}
3303+
3304+
return {...iouReport, reportName: updatedReportName};
3305+
}
3306+
32753307
/**
32763308
* 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
32773309
* it creates optimistic versions of them and uses those instead
@@ -3436,6 +3468,8 @@ function getMoneyRequestInformation(moneyRequestInformation: MoneyRequestInforma
34363468
iouReport.nonReimbursableTotal = (iouReport.nonReimbursableTotal ?? 0) - amount;
34373469
}
34383470
}
3471+
3472+
iouReport = maybeUpdateReportNameForFormulaTitle(iouReport, policy);
34393473
}
34403474
if (typeof iouReport.unheldTotal === 'number') {
34413475
// Use newReportTotal in scenarios where the total is based on more than just the current transaction amount, and we need to override it manually
@@ -4609,6 +4643,11 @@ function getUpdateMoneyRequestParams(params: GetUpdateMoneyRequestParamsType): U
46094643
updatedMoneyRequestReport.unheldNonReimbursableTotal += updatedTransaction.reimbursable ? -updatedTransaction.amount : updatedTransaction.amount;
46104644
}
46114645
}
4646+
4647+
// Only recalculate reportName when reimbursable status changes and the report uses a formula title
4648+
if ('reimbursable' in transactionChanges) {
4649+
updatedMoneyRequestReport = maybeUpdateReportNameForFormulaTitle(updatedMoneyRequestReport, policy);
4650+
}
46124651
} else {
46134652
updatedMoneyRequestReport = updateIOUOwnerAndTotal(
46144653
iouReport,

0 commit comments

Comments
 (0)