Skip to content

Commit 7cae68e

Browse files
committed
Merge branch 'main' into snyk-fix-d4b5669ca76951e1fd2fab3e4130c806
2 parents 4c3eef3 + 485a092 commit 7cae68e

16 files changed

Lines changed: 314 additions & 68 deletions

src/libs/Formula.ts

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
import type {OnyxEntry} from 'react-native-onyx';
22
import type {ValueOf} from 'type-fest';
33
import CONST from '@src/CONST';
4-
import type Policy from '@src/types/onyx/Policy';
5-
import type Report from '@src/types/onyx/Report';
4+
import type {Policy, Report, Transaction} from '@src/types/onyx';
65
import {getCurrencySymbol} from './CurrencyUtils';
76
import {getAllReportActions} from './ReportActionsUtils';
87
import {getReportTransactions} from './ReportUtils';
@@ -25,6 +24,7 @@ type FormulaPart = {
2524
type FormulaContext = {
2625
report: Report;
2726
policy: OnyxEntry<Policy>;
27+
transaction?: Transaction;
2828
};
2929

3030
const FORMULA_PART_TYPES = {
@@ -244,7 +244,7 @@ function computeReportPart(part: FormulaPart, context: FormulaContext): string {
244244
case 'type':
245245
return formatType(report.type);
246246
case 'startdate':
247-
return formatDate(getOldestTransactionDate(report.reportID), format);
247+
return formatDate(getOldestTransactionDate(report.reportID, context), format);
248248
case 'total':
249249
return formatAmount(report.total, getCurrencySymbol(report.currency ?? '') ?? report.currency);
250250
case 'currency':
@@ -468,7 +468,7 @@ function formatType(type: string | undefined): string {
468468
/**
469469
* Get the date of the oldest transaction for a given report
470470
*/
471-
function getOldestTransactionDate(reportID: string): string | undefined {
471+
function getOldestTransactionDate(reportID: string, context?: FormulaContext): string | undefined {
472472
if (!reportID) {
473473
return undefined;
474474
}
@@ -481,14 +481,17 @@ function getOldestTransactionDate(reportID: string): string | undefined {
481481
let oldestDate: string | undefined;
482482

483483
transactions.forEach((transaction) => {
484-
const created = getCreated(transaction);
484+
// Use updated transaction data if available and matches this transaction
485+
const currentTransaction = context?.transaction && transaction.transactionID === context.transaction.transactionID ? context.transaction : transaction;
486+
487+
const created = getCreated(currentTransaction);
485488
if (!created) {
486489
return;
487490
}
488491
if (oldestDate && created >= oldestDate) {
489492
return;
490493
}
491-
if (isPartialTransaction(transaction)) {
494+
if (isPartialTransaction(currentTransaction)) {
492495
return;
493496
}
494497
oldestDate = created;

src/libs/OptimisticReportNames.ts

Lines changed: 79 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import Onyx from 'react-native-onyx';
33
import CONST from '@src/CONST';
44
import ONYXKEYS from '@src/ONYXKEYS';
55
import type {OnyxKey} from '@src/ONYXKEYS';
6+
import type {Transaction} from '@src/types/onyx';
67
import type Policy from '@src/types/onyx/Policy';
78
import type Report from '@src/types/onyx/Report';
89
import Timing from './actions/Timing';
@@ -18,13 +19,16 @@ import {getTitleReportField, isArchivedReport} from './ReportUtils';
1819
/**
1920
* Get the object type from an Onyx key
2021
*/
21-
function determineObjectTypeByKey(key: string): 'report' | 'policy' | 'unknown' {
22+
function determineObjectTypeByKey(key: string): 'report' | 'policy' | 'transaction' | 'unknown' {
2223
if (key.startsWith(ONYXKEYS.COLLECTION.REPORT)) {
2324
return 'report';
2425
}
2526
if (key.startsWith(ONYXKEYS.COLLECTION.POLICY)) {
2627
return 'policy';
2728
}
29+
if (key.startsWith(ONYXKEYS.COLLECTION.TRANSACTION)) {
30+
return 'transaction';
31+
}
2832
return 'unknown';
2933
}
3034

@@ -67,6 +71,13 @@ function getPolicyByID(policyID: string | undefined, allPolicies: Record<string,
6771
return allPolicies[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`];
6872
}
6973

74+
/**
75+
* Get transaction by ID from the transactions collection
76+
*/
77+
function getTransactionByID(transactionID: string, allTransactions: Record<string, Transaction>): Transaction | undefined {
78+
return allTransactions[`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`];
79+
}
80+
7081
/**
7182
* Get all reports associated with a policy ID
7283
*/
@@ -100,6 +111,24 @@ function getReportsByPolicyID(policyID: string, allReports: Record<string, Repor
100111
});
101112
}
102113

114+
/**
115+
* Get the report associated with a transaction ID
116+
*/
117+
function getReportByTransactionID(transactionID: string, context: UpdateContext): Report | undefined {
118+
if (!transactionID) {
119+
return undefined;
120+
}
121+
122+
const transaction = getTransactionByID(transactionID, context.allTransactions);
123+
124+
if (!transaction?.reportID) {
125+
return undefined;
126+
}
127+
128+
// Get the report using the transaction's reportID from context
129+
return getReportByID(transaction.reportID, context.allReports);
130+
}
131+
103132
/**
104133
* Generate the Onyx key for a report
105134
*/
@@ -180,13 +209,18 @@ function computeReportNameIfNeeded(report: Report | undefined, incomingUpdate: O
180209
const formula = titleField.defaultValue;
181210
const formulaParts = parse(formula);
182211

212+
let transaction: Transaction | undefined;
213+
if (updateType === 'transaction') {
214+
transaction = getTransactionByID((incomingUpdate.value as Transaction).transactionID, context.allTransactions);
215+
}
216+
183217
// Check if any formula part might be affected by this update
184218
const isAffected = formulaParts.some((part) => {
185219
if (part.type === FORMULA_PART_TYPES.REPORT) {
186220
// Checking if the formula part is affected in this manner works, but it could certainly be more precise.
187221
// For example, a policy update only affects the part if the formula in the policy changed, or if the report part references a field on the policy.
188222
// However, if we run into performance problems, this would be a good place to optimize.
189-
return updateType === 'report' || updateType === 'policy';
223+
return updateType === 'report' || updateType === 'transaction' || updateType === 'policy';
190224
}
191225
if (part.type === FORMULA_PART_TYPES.FIELD) {
192226
return updateType === 'report';
@@ -206,10 +240,13 @@ function computeReportNameIfNeeded(report: Report | undefined, incomingUpdate: O
206240

207241
const updatedPolicy = updateType === 'policy' && targetReport.policyID === getPolicyIDFromKey(incomingUpdate.key) ? {...(policy ?? {}), ...(incomingUpdate.value as Policy)} : policy;
208242

243+
const updatedTransaction = updateType === 'transaction' ? {...(transaction ?? {}), ...(incomingUpdate.value as Transaction)} : undefined;
244+
209245
// Compute the new name
210246
const formulaContext: FormulaContext = {
211247
report: updatedReport,
212248
policy: updatedPolicy,
249+
transaction: updatedTransaction,
213250
};
214251

215252
const newName = compute(formula, formulaContext);
@@ -260,7 +297,6 @@ function updateOptimisticReportNamesFromUpdates(updates: OnyxUpdate[], context:
260297

261298
for (const update of updates) {
262299
const objectType = determineObjectTypeByKey(update.key);
263-
let affectedReports: Report[] = [];
264300

265301
switch (objectType) {
266302
case 'report': {
@@ -284,26 +320,50 @@ function updateOptimisticReportNamesFromUpdates(updates: OnyxUpdate[], context:
284320

285321
case 'policy': {
286322
const policyID = getPolicyIDFromKey(update.key);
287-
affectedReports = getReportsByPolicyID(policyID, allReports, context);
323+
const affectedReports = getReportsByPolicyID(policyID, allReports, context);
324+
for (const report of affectedReports) {
325+
const reportNameUpdate = computeReportNameIfNeeded(report, update, context);
326+
327+
if (reportNameUpdate) {
328+
additionalUpdates.push({
329+
key: getReportKey(report.reportID),
330+
onyxMethod: Onyx.METHOD.MERGE,
331+
value: {
332+
reportName: reportNameUpdate,
333+
},
334+
});
335+
}
336+
}
288337
break;
289338
}
290339

291-
default:
292-
continue;
293-
}
340+
case 'transaction': {
341+
let report: Report | undefined;
342+
const transactionUpdate = update.value as Partial<Transaction>;
343+
if (transactionUpdate.reportID) {
344+
report = getReportByID(transactionUpdate.reportID, allReports);
345+
} else {
346+
report = getReportByTransactionID(getTransactionIDFromKey(update.key), context);
347+
}
294348

295-
for (const report of affectedReports) {
296-
const reportNameUpdate = computeReportNameIfNeeded(report, update, context);
297-
298-
if (reportNameUpdate) {
299-
additionalUpdates.push({
300-
key: getReportKey(report.reportID),
301-
onyxMethod: Onyx.METHOD.MERGE,
302-
value: {
303-
reportName: reportNameUpdate,
304-
},
305-
});
349+
if (report) {
350+
const reportNameUpdate = computeReportNameIfNeeded(report, update, context);
351+
352+
if (reportNameUpdate) {
353+
additionalUpdates.push({
354+
key: getReportKey(report.reportID),
355+
onyxMethod: Onyx.METHOD.MERGE,
356+
value: {
357+
reportName: reportNameUpdate,
358+
},
359+
});
360+
}
361+
}
362+
break;
306363
}
364+
365+
default:
366+
continue;
307367
}
308368
}
309369

@@ -326,5 +386,5 @@ function createUpdateContext(): Promise<UpdateContext> {
326386
return getUpdateContextAsync();
327387
}
328388

329-
export {updateOptimisticReportNamesFromUpdates, computeReportNameIfNeeded, createUpdateContext, shouldComputeReportName};
389+
export {updateOptimisticReportNamesFromUpdates, computeReportNameIfNeeded, createUpdateContext, shouldComputeReportName, getReportByTransactionID};
330390
export type {UpdateContext};

src/libs/OptimisticReportNamesConnectionManager.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import type {OnyxEntry} from 'react-native-onyx';
22
import Onyx from 'react-native-onyx';
33
import ONYXKEYS from '@src/ONYXKEYS';
4+
import type {Transaction} from '@src/types/onyx';
45
import type Beta from '@src/types/onyx/Beta';
56
import type Policy from '@src/types/onyx/Policy';
67
import type Report from '@src/types/onyx/Report';
@@ -11,15 +12,17 @@ type UpdateContext = {
1112
allReports: Record<string, Report>;
1213
allPolicies: Record<string, Policy>;
1314
allReportNameValuePairs: Record<string, ReportNameValuePairs>;
15+
allTransactions: Record<string, Transaction>;
1416
};
1517

1618
let betas: OnyxEntry<Beta[]>;
1719
let allReports: Record<string, Report>;
1820
let allPolicies: Record<string, Policy>;
1921
let allReportNameValuePairs: Record<string, ReportNameValuePairs>;
22+
let allTransactions: Record<string, Transaction>;
2023
let isInitialized = false;
2124
let connectionsInitializedCount = 0;
22-
const totalConnections = 4;
25+
const totalConnections = 5;
2326
let initializationPromise: Promise<void> | null = null;
2427

2528
/**
@@ -89,6 +92,16 @@ function initialize(): Promise<void> {
8992
checkAndMarkInitialized();
9093
},
9194
});
95+
96+
// Connect to all TRANSACTIONS
97+
Onyx.connectWithoutView({
98+
key: ONYXKEYS.COLLECTION.TRANSACTION,
99+
waitForCollectionCallback: true,
100+
callback: (val) => {
101+
allTransactions = (val as Record<string, Transaction>) ?? {};
102+
checkAndMarkInitialized();
103+
},
104+
});
92105
});
93106

94107
return initializationPromise;
@@ -108,6 +121,7 @@ function getUpdateContext(): UpdateContext {
108121
allReports: allReports ?? {},
109122
allPolicies: allPolicies ?? {},
110123
allReportNameValuePairs: allReportNameValuePairs ?? {},
124+
allTransactions: allTransactions ?? {},
111125
};
112126
}
113127

src/libs/PolicyUtils.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -247,7 +247,7 @@ function getPolicyBrickRoadIndicatorStatus(policy: OnyxEntry<Policy>, isConnecti
247247
return undefined;
248248
}
249249

250-
function getPolicyRole(policy: OnyxInputOrEntry<Policy> | SearchPolicy, currentUserLogin: string | undefined) {
250+
function getPolicyRole(policy: OnyxInputOrEntry<Policy> | SearchPolicy, currentUserLogin: string | undefined): string | undefined {
251251
if (policy?.role) {
252252
return policy.role;
253253
}
@@ -280,8 +280,11 @@ function shouldShowPolicy(policy: OnyxEntry<Policy>, shouldShowPendingDeletePoli
280280
);
281281
}
282282

283-
function isPolicyMember(currentUserLogin: string | undefined, policyID: string | undefined): boolean {
284-
return !!currentUserLogin && !!policyID && !!allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`]?.employeeList?.[currentUserLogin];
283+
/**
284+
* Checks if a specific user is a member of the policy.
285+
*/
286+
function isPolicyMember(policy: OnyxEntry<Policy>, userLogin: string | undefined): boolean {
287+
return !!policy && !!userLogin && !!policy.employeeList?.[userLogin];
285288
}
286289

287290
function isPolicyMemberWithoutPendingDelete(currentUserLogin: string | undefined, policy: OnyxEntry<Policy>): boolean {

src/libs/ReportSecondaryActionUtils.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ function isSplitAction(report: Report, reportTransactions: Transaction[], policy
111111
const isOpenReport = isOpenReportUtils(report);
112112
const isPolicyExpenseChat = !!policy?.isPolicyExpenseChatEnabled;
113113
const currentUserEmail = getCurrentUserEmail();
114-
const userIsPolicyMember = isPolicyMember(currentUserEmail, report.policyID);
114+
const userIsPolicyMember = isPolicyMember(policy, currentUserEmail);
115115

116116
if (!(userIsPolicyMember && isPolicyExpenseChat)) {
117117
return false;

src/libs/ReportUtils.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11117,7 +11117,7 @@ function isWorkspaceEligibleForReportChange(newPolicy: OnyxEntry<Policy>, report
1111711117
if (isIOUReport(report)) {
1111811118
return isPaidGroupPolicyPolicyUtils(newPolicy) && isWorkspacePayer(managerLogin ?? '', newPolicy);
1111911119
}
11120-
return isPaidGroupPolicyPolicyUtils(newPolicy) && (isPolicyMember(submitterEmail, newPolicy?.id) || isPolicyAdmin(newPolicy?.id, policies));
11120+
return isPaidGroupPolicyPolicyUtils(newPolicy) && (isPolicyMember(newPolicy, submitterEmail) || isPolicyAdmin(newPolicy?.id, policies));
1112111121
}
1112211122

1112311123
function getApprovalChain(policy: OnyxEntry<Policy>, expenseReport: OnyxEntry<Report>): string[] {

src/libs/TransactionUtils/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1021,7 +1021,7 @@ function shouldShowBrokenConnectionViolationForMultipleTransactions(
10211021
*/
10221022
function shouldShowViolation(iouReport: OnyxEntry<Report>, policy: OnyxEntry<Policy>, violationName: ViolationName, shouldShowRterForSettledReport = true): boolean {
10231023
const isSubmitter = isCurrentUserSubmitter(iouReport);
1024-
const isPolicyMember = isPolicyMemberPolicyUtils(currentUserEmail, policy?.id);
1024+
const isPolicyMember = isPolicyMemberPolicyUtils(policy, currentUserEmail);
10251025
const isReportOpen = isOpenExpenseReport(iouReport);
10261026

10271027
if (violationName === CONST.VIOLATIONS.AUTO_REPORTED_REJECTED_EXPENSE) {

0 commit comments

Comments
 (0)