Skip to content

Commit 967b234

Browse files
authored
Merge pull request Expensify#65030 from Expensify/cmartins-fixApproveForPendingTransactions
Fix approve for pending transactions
2 parents ea6f6a0 + 4099d99 commit 967b234

6 files changed

Lines changed: 95 additions & 1 deletion

src/libs/ReportPreviewActionUtils.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,10 @@ function canApprove(report: Report, violations: OnyxCollection<TransactionViolat
106106
return false;
107107
}
108108

109+
if (!!transactions && transactions?.length > 0 && transactions.every((transaction) => isPending(transaction))) {
110+
return false;
111+
}
112+
109113
const isPreventSelfApprovalEnabled = policy?.preventSelfApproval;
110114
const isReportSubmitter = isCurrentUserSubmitter(report.reportID);
111115

src/libs/ReportPrimaryActionUtils.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,10 @@ function isApproveAction(report: Report, reportTransactions: Transaction[], poli
127127
return false;
128128
}
129129

130+
if (reportTransactions.length > 0 && reportTransactions.every((transaction) => isPending(transaction))) {
131+
return false;
132+
}
133+
130134
const isPreventSelfApprovalEnabled = policy?.preventSelfApproval;
131135
const isReportSubmitter = isCurrentUserSubmitter(report.reportID);
132136

src/libs/ReportSecondaryActionUtils.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,10 @@ function isApproveAction(report: Report, reportTransactions: Transaction[], viol
195195
return true;
196196
}
197197

198+
if (reportTransactions.length > 0 && reportTransactions.every((transaction) => isPending(transaction))) {
199+
return false;
200+
}
201+
198202
const transactionIDs = reportTransactions.map((t) => t.transactionID);
199203

200204
const hasAllPendingRTERViolations = allHavePendingRTERViolation(reportTransactions, violations);

tests/actions/ReportPreviewActionUtilsTest.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,35 @@ describe('getReportPreviewAction', () => {
181181

182182
expect(getReportPreviewAction(VIOLATIONS, report, policy, [transaction], false)).toBe(CONST.REPORT.REPORT_PREVIEW_ACTIONS.VIEW);
183183
});
184+
185+
it('should return false for report with pending expenses', async () => {
186+
const report = {
187+
...createRandomReport(REPORT_ID),
188+
type: CONST.REPORT.TYPE.EXPENSE,
189+
ownerAccountID: CURRENT_USER_ACCOUNT_ID,
190+
stateNum: CONST.REPORT.STATE_NUM.SUBMITTED,
191+
statusNum: CONST.REPORT.STATUS_NUM.SUBMITTED,
192+
managerID: CURRENT_USER_ACCOUNT_ID,
193+
isWaitingOnBankAccount: false,
194+
};
195+
196+
const policy = createRandomPolicy(0);
197+
policy.type = CONST.POLICY.TYPE.CORPORATE;
198+
policy.approver = CURRENT_USER_EMAIL;
199+
policy.approvalMode = CONST.POLICY.APPROVAL_MODE.BASIC;
200+
policy.preventSelfApproval = false;
201+
202+
await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${REPORT_ID}`, report);
203+
const transaction = {
204+
reportID: `${REPORT_ID}`,
205+
status: CONST.TRANSACTION.STATUS.PENDING,
206+
amount: 10,
207+
merchant: 'Merchant',
208+
date: '2025-01-01',
209+
} as unknown as Transaction;
210+
211+
expect(getReportPreviewAction(VIOLATIONS, report, policy, [transaction], false)).toBe(CONST.REPORT.REPORT_PREVIEW_ACTIONS.VIEW);
212+
});
184213
});
185214

186215
it("canApprove should return true for the current report manager regardless of whether they're in the current approval workflow", async () => {

tests/unit/ReportPrimaryActionUtilsTest.ts

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,32 @@ describe('getPrimaryAction', () => {
151151
expect(getReportPrimaryAction({report, chatReport, reportTransactions: [transaction], violations: {}, policy: policy as Policy})).toBe('');
152152
});
153153

154-
it('should return PAY for submitted invoice report if paid as personal', async () => {
154+
it('should return empty for report being processed but transactions are pending', async () => {
155+
const report = {
156+
reportID: REPORT_ID,
157+
type: CONST.REPORT.TYPE.EXPENSE,
158+
ownerAccountID: CURRENT_USER_ACCOUNT_ID,
159+
stateNum: CONST.REPORT.STATE_NUM.SUBMITTED,
160+
statusNum: CONST.REPORT.STATUS_NUM.SUBMITTED,
161+
managerID: CURRENT_USER_ACCOUNT_ID,
162+
} as unknown as Report;
163+
await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${REPORT_ID}`, report);
164+
const policy = {
165+
approver: CURRENT_USER_EMAIL,
166+
approvalMode: CONST.POLICY.APPROVAL_MODE.BASIC,
167+
};
168+
const transaction = {
169+
reportID: `${REPORT_ID}`,
170+
status: CONST.TRANSACTION.STATUS.PENDING,
171+
amount: 10,
172+
merchant: 'Merchant',
173+
date: '2025-01-01',
174+
} as unknown as Transaction;
175+
176+
expect(getReportPrimaryAction({report, chatReport, reportTransactions: [transaction], violations: {}, policy: policy as Policy})).toBe('');
177+
});
178+
179+
it('should return PAY for submitted invoice report if paid as personal', async () => {
155180
const report = {
156181
reportID: REPORT_ID,
157182
type: CONST.REPORT.TYPE.INVOICE,

tests/unit/ReportSecondaryActionUtilsTest.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,34 @@ describe('getSecondaryAction', () => {
178178
expect(result.includes(CONST.REPORT.SECONDARY_ACTIONS.APPROVE)).toBe(true);
179179
});
180180

181+
it('does not include APPROVE option for approver and report with only pending transactions', async () => {
182+
const report = {
183+
reportID: REPORT_ID,
184+
type: CONST.REPORT.TYPE.EXPENSE,
185+
ownerAccountID: EMPLOYEE_ACCOUNT_ID,
186+
stateNum: CONST.REPORT.STATE_NUM.SUBMITTED,
187+
statusNum: CONST.REPORT.STATUS_NUM.SUBMITTED,
188+
managerID: EMPLOYEE_ACCOUNT_ID,
189+
} as unknown as Report;
190+
const policy = {
191+
approver: EMPLOYEE_EMAIL,
192+
} as unknown as Policy;
193+
const TRANSACTION_ID = 'TRANSACTION_ID';
194+
const transaction = {
195+
transactionID: TRANSACTION_ID,
196+
status: CONST.TRANSACTION.STATUS.PENDING,
197+
amount: 10,
198+
merchant: 'Merchant',
199+
date: '2025-01-01',
200+
} as unknown as Transaction;
201+
202+
await Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION}${TRANSACTION_ID}`, transaction);
203+
await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${REPORT_ID}`, report);
204+
205+
const result = getSecondaryReportActions({report, chatReport, reportTransactions: [transaction], violations: {}, policy});
206+
expect(result.includes(CONST.REPORT.SECONDARY_ACTIONS.APPROVE)).toBe(false);
207+
});
208+
181209
it('includes APPROVE option for report with RTER violations when it is submitted', () => {
182210
const report = {
183211
reportID: REPORT_ID,

0 commit comments

Comments
 (0)