Skip to content

Commit c8abc59

Browse files
authored
Merge pull request Expensify#65779 from ganzz4/fix/64547
fix: Error displayed after Approver A unapproves a final approved report
2 parents 64e83b2 + 4444ec4 commit c8abc59

2 files changed

Lines changed: 116 additions & 2 deletions

File tree

src/libs/ReportSecondaryActionUtils.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -222,12 +222,18 @@ function isUnapproveAction(report: Report, policy?: Policy): boolean {
222222
const isReportApproved = isReportApprovedUtils({report});
223223
const isReportSettled = isSettled(report);
224224
const isPaymentProcessing = report.isWaitingOnBankAccount && report.statusNum === CONST.REPORT.STATUS_NUM.APPROVED;
225+
const isAdmin = policy?.role === CONST.POLICY.ROLE.ADMIN;
226+
const isManager = report.managerID === getCurrentUserAccountID();
225227

226-
if (isReportSettled || isPaymentProcessing) {
228+
if (isReportSettled || !isExpenseReport || !isReportApproved || isPaymentProcessing) {
227229
return false;
228230
}
229231

230-
return isExpenseReport && isReportApprover && isReportApproved;
232+
if (report.statusNum === CONST.REPORT.STATUS_NUM.APPROVED) {
233+
return isManager || isAdmin;
234+
}
235+
236+
return isReportApprover;
231237
}
232238

233239
function isCancelPaymentAction(report: Report, reportTransactions: Transaction[], policy?: Policy): boolean {

tests/unit/ReportSecondaryActionUtilsTest.ts

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -375,13 +375,121 @@ describe('getSecondaryAction', () => {
375375
ownerAccountID: EMPLOYEE_ACCOUNT_ID,
376376
stateNum: CONST.REPORT.STATE_NUM.APPROVED,
377377
statusNum: CONST.REPORT.STATUS_NUM.APPROVED,
378+
managerID: EMPLOYEE_ACCOUNT_ID,
378379
} as unknown as Report;
379380
const policy = {approver: EMPLOYEE_EMAIL} as unknown as Policy;
380381

381382
const result = getSecondaryReportActions({report, chatReport, reportTransactions: [], violations: {}, policy});
382383
expect(result.includes(CONST.REPORT.SECONDARY_ACTIONS.UNAPPROVE)).toBe(true);
383384
});
384385

386+
it('includes UNAPPROVE option for admin on finally approved report', () => {
387+
const report = {
388+
reportID: REPORT_ID,
389+
type: CONST.REPORT.TYPE.EXPENSE,
390+
ownerAccountID: EMPLOYEE_ACCOUNT_ID,
391+
stateNum: CONST.REPORT.STATE_NUM.APPROVED,
392+
statusNum: CONST.REPORT.STATUS_NUM.APPROVED,
393+
managerID: MANAGER_ACCOUNT_ID,
394+
} as unknown as Report;
395+
const policy = {
396+
approver: APPROVER_EMAIL,
397+
role: CONST.POLICY.ROLE.ADMIN,
398+
} as unknown as Policy;
399+
400+
const result = getSecondaryReportActions({report, chatReport, reportTransactions: [], violations: {}, policy});
401+
expect(result.includes(CONST.REPORT.SECONDARY_ACTIONS.UNAPPROVE)).toBe(true);
402+
});
403+
404+
it('includes UNAPPROVE option for manager on finally approved report', () => {
405+
const report = {
406+
reportID: REPORT_ID,
407+
type: CONST.REPORT.TYPE.EXPENSE,
408+
ownerAccountID: EMPLOYEE_ACCOUNT_ID,
409+
stateNum: CONST.REPORT.STATE_NUM.APPROVED,
410+
statusNum: CONST.REPORT.STATUS_NUM.APPROVED,
411+
managerID: EMPLOYEE_ACCOUNT_ID,
412+
} as unknown as Report;
413+
const policy = {
414+
approver: APPROVER_EMAIL,
415+
} as unknown as Policy;
416+
417+
const result = getSecondaryReportActions({report, chatReport, reportTransactions: [], violations: {}, policy});
418+
expect(result.includes(CONST.REPORT.SECONDARY_ACTIONS.UNAPPROVE)).toBe(true);
419+
});
420+
421+
it('does not include UNAPPROVE option for non-admin, non-manager on finally approved report', () => {
422+
const report = {
423+
reportID: REPORT_ID,
424+
type: CONST.REPORT.TYPE.EXPENSE,
425+
ownerAccountID: EMPLOYEE_ACCOUNT_ID,
426+
stateNum: CONST.REPORT.STATE_NUM.APPROVED,
427+
statusNum: CONST.REPORT.STATUS_NUM.APPROVED,
428+
managerID: MANAGER_ACCOUNT_ID,
429+
} as unknown as Report;
430+
const policy = {
431+
approver: APPROVER_EMAIL,
432+
} as unknown as Policy;
433+
434+
const result = getSecondaryReportActions({report, chatReport, reportTransactions: [], violations: {}, policy});
435+
expect(result.includes(CONST.REPORT.SECONDARY_ACTIONS.UNAPPROVE)).toBe(false);
436+
});
437+
438+
it('does not include UNAPPROVE option for non-approved report', () => {
439+
const report = {
440+
reportID: REPORT_ID,
441+
type: CONST.REPORT.TYPE.EXPENSE,
442+
ownerAccountID: EMPLOYEE_ACCOUNT_ID,
443+
stateNum: CONST.REPORT.STATE_NUM.SUBMITTED,
444+
statusNum: CONST.REPORT.STATUS_NUM.SUBMITTED,
445+
managerID: EMPLOYEE_ACCOUNT_ID,
446+
} as unknown as Report;
447+
const policy = {
448+
approver: EMPLOYEE_EMAIL,
449+
role: CONST.POLICY.ROLE.ADMIN,
450+
} as unknown as Policy;
451+
452+
const result = getSecondaryReportActions({report, chatReport, reportTransactions: [], violations: {}, policy});
453+
expect(result.includes(CONST.REPORT.SECONDARY_ACTIONS.UNAPPROVE)).toBe(false);
454+
});
455+
456+
it('does not include UNAPPROVE option for settled report', () => {
457+
const report = {
458+
reportID: REPORT_ID,
459+
type: CONST.REPORT.TYPE.EXPENSE,
460+
ownerAccountID: EMPLOYEE_ACCOUNT_ID,
461+
stateNum: CONST.REPORT.STATE_NUM.APPROVED,
462+
statusNum: CONST.REPORT.STATUS_NUM.REIMBURSED,
463+
managerID: EMPLOYEE_ACCOUNT_ID,
464+
} as unknown as Report;
465+
const policy = {
466+
approver: EMPLOYEE_EMAIL,
467+
role: CONST.POLICY.ROLE.ADMIN,
468+
} as unknown as Policy;
469+
470+
const result = getSecondaryReportActions({report, chatReport, reportTransactions: [], violations: {}, policy});
471+
expect(result.includes(CONST.REPORT.SECONDARY_ACTIONS.UNAPPROVE)).toBe(false);
472+
});
473+
474+
it('does not include UNAPPROVE option for payment processing report', () => {
475+
const report = {
476+
reportID: REPORT_ID,
477+
type: CONST.REPORT.TYPE.EXPENSE,
478+
ownerAccountID: EMPLOYEE_ACCOUNT_ID,
479+
stateNum: CONST.REPORT.STATE_NUM.APPROVED,
480+
statusNum: CONST.REPORT.STATUS_NUM.APPROVED,
481+
managerID: EMPLOYEE_ACCOUNT_ID,
482+
isWaitingOnBankAccount: true,
483+
} as unknown as Report;
484+
const policy = {
485+
approver: EMPLOYEE_EMAIL,
486+
role: CONST.POLICY.ROLE.ADMIN,
487+
} as unknown as Policy;
488+
489+
const result = getSecondaryReportActions({report, chatReport, reportTransactions: [], violations: {}, policy});
490+
expect(result.includes(CONST.REPORT.SECONDARY_ACTIONS.UNAPPROVE)).toBe(false);
491+
});
492+
385493
it('includes CANCEL_PAYMENT option for report paid elsewhere', () => {
386494
const report = {
387495
reportID: REPORT_ID,

0 commit comments

Comments
 (0)