Skip to content

Commit 135e6d1

Browse files
authored
Merge pull request Expensify#88230 from KJ21-ENG/kj/86927-lhn-rbr-modified-amount
fix: exclude modifiedAmount NOTICE from LHN RBR for submitted reports
2 parents 7e807bf + 2aa26cc commit 135e6d1

2 files changed

Lines changed: 198 additions & 3 deletions

File tree

src/libs/ReportUtils.ts

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9132,6 +9132,8 @@ function getViolatingReportIDForRBRInLHN(report: OnyxEntry<Report>, transactionV
91329132
return false;
91339133
}
91349134

9135+
const excludedNoticeNamesForLHN = isProcessingReport(potentialReport) ? [CONST.VIOLATIONS.MODIFIED_AMOUNT] : [];
9136+
91359137
return (
91369138
!isInvoiceReport(potentialReport) &&
91379139
ViolationsUtils.hasVisibleViolationsForUser(
@@ -9162,21 +9164,39 @@ function getViolatingReportIDForRBRInLHN(report: OnyxEntry<Report>, transactionV
91629164
potentialReport,
91639165
policy,
91649166
) ||
9165-
hasNoticeTypeViolations(
9166-
potentialReport.reportID,
9167+
hasNoticeTypeViolationsForRBRInLHN(
91679168
transactionViolations,
91689169
deprecatedCurrentUserAccountID ?? CONST.DEFAULT_NUMBER_ID,
91699170
deprecatedCurrentUserEmail ?? '',
9170-
true,
91719171
transactions,
91729172
potentialReport,
91739173
policy,
9174+
excludedNoticeNamesForLHN,
91749175
))
91759176
);
91769177
});
91779178
return violatingReport ? violatingReport.reportID : null;
91789179
}
91799180

9181+
function hasNoticeTypeViolationsForRBRInLHN(
9182+
transactionViolations: OnyxCollection<TransactionViolation[]>,
9183+
currentUserAccountIDParam: number,
9184+
currentUserEmailParam: string,
9185+
reportTransactions: Transaction[],
9186+
report: OnyxEntry<Report>,
9187+
policy: OnyxEntry<Policy>,
9188+
excludedViolationNames: string[],
9189+
): boolean {
9190+
return reportTransactions.some((transaction) => {
9191+
const rawViolations = transactionViolations?.[`${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${transaction?.transactionID}`];
9192+
if (!rawViolations?.length) {
9193+
return false;
9194+
}
9195+
const filteredViolations = excludedViolationNames.length > 0 ? rawViolations.filter((violation) => !excludedViolationNames.includes(violation.name)) : rawViolations;
9196+
return hasNoticeTypeViolation(transaction, filteredViolations, currentUserEmailParam, currentUserAccountIDParam, report, policy, true);
9197+
});
9198+
}
9199+
91809200
/**
91819201
* Checks to see if a report contains a violation
91829202
*/

tests/unit/ReportUtilsTest.ts

Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12827,6 +12827,181 @@ describe('ReportUtils', () => {
1282712827

1282812828
await Onyx.clear();
1282912829
});
12830+
12831+
it('should return null for a processing (submitted) expense report whose only violation is a modifiedAmount NOTICE', async () => {
12832+
await Onyx.clear();
12833+
12834+
const policyID = 'policy-rbr-modified-amount-processing';
12835+
const chatReportID = 'chat-rbr-modified-amount-processing';
12836+
const expenseReportID = 'expense-rbr-modified-amount-processing';
12837+
const transactionID = 'transaction-rbr-modified-amount-processing';
12838+
12839+
const policyData: Policy = {
12840+
id: policyID,
12841+
name: 'Modified Amount Processing Workspace',
12842+
type: CONST.POLICY.TYPE.TEAM,
12843+
role: CONST.POLICY.ROLE.ADMIN,
12844+
outputCurrency: CONST.CURRENCY.USD,
12845+
reimbursementChoice: CONST.POLICY.REIMBURSEMENT_CHOICES.REIMBURSEMENT_YES,
12846+
approvalMode: CONST.POLICY.APPROVAL_MODE.BASIC,
12847+
employeeList: {
12848+
[currentUserEmail]: {
12849+
role: CONST.POLICY.ROLE.ADMIN,
12850+
},
12851+
},
12852+
owner: currentUserEmail,
12853+
isPolicyExpenseChatEnabled: true,
12854+
};
12855+
12856+
const chatReport: Report = {
12857+
...createPolicyExpenseChat(820),
12858+
reportID: chatReportID,
12859+
ownerAccountID: currentUserAccountID,
12860+
policyID,
12861+
iouReportID: expenseReportID,
12862+
};
12863+
12864+
const expenseReport: Report = {
12865+
...createExpenseReport(821),
12866+
reportID: expenseReportID,
12867+
chatReportID,
12868+
ownerAccountID: currentUserAccountID,
12869+
managerID: 42,
12870+
policyID,
12871+
type: CONST.REPORT.TYPE.EXPENSE,
12872+
currency: CONST.CURRENCY.USD,
12873+
total: 5000,
12874+
stateNum: CONST.REPORT.STATE_NUM.SUBMITTED,
12875+
statusNum: CONST.REPORT.STATUS_NUM.SUBMITTED,
12876+
};
12877+
12878+
const baseTransaction = createRandomTransaction(820);
12879+
const transaction: Transaction = {
12880+
...baseTransaction,
12881+
transactionID,
12882+
reportID: expenseReportID,
12883+
amount: 5000,
12884+
currency: CONST.CURRENCY.USD,
12885+
status: CONST.TRANSACTION.STATUS.POSTED,
12886+
reimbursable: true,
12887+
};
12888+
12889+
const transactionViolationsKey = `${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${transactionID}` as OnyxKey;
12890+
const transactionViolationsCollection: OnyxCollection<TransactionViolation[]> = {
12891+
[transactionViolationsKey]: [
12892+
{
12893+
name: CONST.VIOLATIONS.MODIFIED_AMOUNT,
12894+
type: CONST.VIOLATION_TYPES.NOTICE,
12895+
showInReview: true,
12896+
},
12897+
],
12898+
};
12899+
12900+
await Onyx.merge(ONYXKEYS.SESSION, {accountID: currentUserAccountID, email: currentUserEmail});
12901+
await waitForBatchedUpdates();
12902+
12903+
await Promise.all([
12904+
Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, policyData),
12905+
Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${chatReport.reportID}`, chatReport),
12906+
Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${expenseReport.reportID}`, expenseReport),
12907+
Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION}${transaction.transactionID}`, transaction),
12908+
Onyx.merge(transactionViolationsKey, transactionViolationsCollection[transactionViolationsKey]),
12909+
]);
12910+
await waitForBatchedUpdates();
12911+
12912+
const result = getViolatingReportIDForRBRInLHN(chatReport, transactionViolationsCollection);
12913+
expect(result).toBeNull();
12914+
12915+
await Onyx.clear();
12916+
});
12917+
12918+
it('should still surface RBR for an open expense report whose only violation is a modifiedAmount NOTICE', async () => {
12919+
await Onyx.clear();
12920+
12921+
const policyID = 'policy-rbr-modified-amount-open';
12922+
const chatReportID = 'chat-rbr-modified-amount-open';
12923+
const expenseReportID = 'expense-rbr-modified-amount-open';
12924+
const transactionID = 'transaction-rbr-modified-amount-open';
12925+
12926+
const policyData: Policy = {
12927+
id: policyID,
12928+
name: 'Modified Amount Open Workspace',
12929+
type: CONST.POLICY.TYPE.TEAM,
12930+
role: CONST.POLICY.ROLE.ADMIN,
12931+
outputCurrency: CONST.CURRENCY.USD,
12932+
reimbursementChoice: CONST.POLICY.REIMBURSEMENT_CHOICES.REIMBURSEMENT_YES,
12933+
approvalMode: CONST.POLICY.APPROVAL_MODE.BASIC,
12934+
employeeList: {
12935+
[currentUserEmail]: {
12936+
role: CONST.POLICY.ROLE.ADMIN,
12937+
},
12938+
},
12939+
owner: currentUserEmail,
12940+
isPolicyExpenseChatEnabled: true,
12941+
};
12942+
12943+
const chatReport: Report = {
12944+
...createPolicyExpenseChat(822),
12945+
reportID: chatReportID,
12946+
ownerAccountID: currentUserAccountID,
12947+
policyID,
12948+
iouReportID: expenseReportID,
12949+
hasOutstandingChildRequest: true,
12950+
};
12951+
12952+
const expenseReport: Report = {
12953+
...createExpenseReport(823),
12954+
reportID: expenseReportID,
12955+
chatReportID,
12956+
ownerAccountID: currentUserAccountID,
12957+
managerID: 42,
12958+
policyID,
12959+
type: CONST.REPORT.TYPE.EXPENSE,
12960+
currency: CONST.CURRENCY.USD,
12961+
total: 5000,
12962+
stateNum: CONST.REPORT.STATE_NUM.OPEN,
12963+
statusNum: CONST.REPORT.STATUS_NUM.OPEN,
12964+
};
12965+
12966+
const baseTransaction = createRandomTransaction(822);
12967+
const transaction: Transaction = {
12968+
...baseTransaction,
12969+
transactionID,
12970+
reportID: expenseReportID,
12971+
amount: 5000,
12972+
currency: CONST.CURRENCY.USD,
12973+
status: CONST.TRANSACTION.STATUS.POSTED,
12974+
reimbursable: true,
12975+
};
12976+
12977+
const transactionViolationsKey = `${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${transactionID}` as OnyxKey;
12978+
const transactionViolationsCollection: OnyxCollection<TransactionViolation[]> = {
12979+
[transactionViolationsKey]: [
12980+
{
12981+
name: CONST.VIOLATIONS.MODIFIED_AMOUNT,
12982+
type: CONST.VIOLATION_TYPES.NOTICE,
12983+
showInReview: true,
12984+
},
12985+
],
12986+
};
12987+
12988+
await Onyx.merge(ONYXKEYS.SESSION, {accountID: currentUserAccountID, email: currentUserEmail});
12989+
await waitForBatchedUpdates();
12990+
12991+
await Promise.all([
12992+
Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, policyData),
12993+
Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${chatReport.reportID}`, chatReport),
12994+
Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${expenseReport.reportID}`, expenseReport),
12995+
Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION}${transaction.transactionID}`, transaction),
12996+
Onyx.merge(transactionViolationsKey, transactionViolationsCollection[transactionViolationsKey]),
12997+
]);
12998+
await waitForBatchedUpdates();
12999+
13000+
const result = getViolatingReportIDForRBRInLHN(chatReport, transactionViolationsCollection);
13001+
expect(result).toBe(expenseReportID);
13002+
13003+
await Onyx.clear();
13004+
});
1283013005
});
1283113006

1283213007
it('should surface a GBR for admin with held expenses requiring approval or payment and avoid showing an RBR', async () => {

0 commit comments

Comments
 (0)