Skip to content

Commit 5a3f4e9

Browse files
Use oldest report action to determine LHN badge priority between Task and IOU
When a chat has both an outstanding task and an IOU action (Submit/Approve/Pay), the badge shown in the LHN should correspond to the oldest report action rather than always showing the Task badge. Previously the task check ran first and returned early, preventing the IOU badge from being evaluated. Co-authored-by: Aimane Chnaif <aimane-chnaif@users.noreply.github.com>
1 parent dd15fd9 commit 5a3f4e9

2 files changed

Lines changed: 136 additions & 20 deletions

File tree

src/libs/ReportUtils.ts

Lines changed: 38 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -4242,22 +4242,7 @@ function getReasonAndReportActionThatRequiresAttention(
42424242
const invoiceReceiverPolicy = invoiceReceiverPolicyID ? getPolicy(invoiceReceiverPolicyID) : undefined;
42434243
const actionTypeForAssigneeToComplete = getActionTypeForAssigneeToComplete(optionOrReport, parentReportAction);
42444244

4245-
if (actionTypeForAssigneeToComplete) {
4246-
const isAssigneeExpenseAction = actionTypeForAssigneeToComplete === CONST.REPORT.ACTION_TYPES_FOR_ASSIGNEE_TO_COMPLETE.EXPENSE;
4247-
const assigneeBadge = isAssigneeExpenseAction
4248-
? getBadgeFromIOUReport(optionOrReport, undefined, policy, optionReportMetadata, invoiceReceiverPolicy, currentUserLogin, currentUserAccountID)
4249-
: CONST.REPORT.ACTION_BADGE.TASK;
4250-
return {
4251-
reason: CONST.REQUIRES_ATTENTION_REASONS.IS_WAITING_FOR_ASSIGNEE_TO_COMPLETE_ACTION,
4252-
reportAction: Object.values(reportActions)
4253-
.filter((action) => action.childType === CONST.REPORT.TYPE.TASK && !isTaskCompleted(action) && action.childManagerAccountID === deprecatedCurrentUserAccountID)
4254-
// eslint-disable-next-line rulesdir/prefer-locale-compare-from-context
4255-
.sort((a, b) => (!a.created || !b.created ? 0 : a.created.localeCompare(b.created)))
4256-
.at(0),
4257-
...(assigneeBadge ? {actionBadge: assigneeBadge} : {}),
4258-
};
4259-
}
4260-
4245+
// Compute IOU candidate upfront so we can compare timestamps with task candidate
42614246
const {reportAction: iouReportActionToApproveOrPay, actionBadge} = getIOUReportActionWithBadge(
42624247
optionOrReport,
42634248
policy,
@@ -4272,11 +4257,44 @@ function getReasonAndReportActionThatRequiresAttention(
42724257

42734258
// Has a child report that is awaiting action (e.g. approve, pay, add bank account) from current user
42744259
const hasStaleChildRequest = isTripRoom(optionOrReport) && (optionOrReport.transactionCount ?? 0) === 0;
4275-
4276-
if (
4260+
const hasValidIOUAction =
42774261
((optionOrReport.hasOutstandingChildRequest === true && !hasStaleChildRequest) || iouReportActionToApproveOrPay?.reportActionID) &&
4278-
(policy?.reimbursementChoice !== CONST.POLICY.REIMBURSEMENT_CHOICES.REIMBURSEMENT_NO || !hasOnlyPendingTransactions)
4279-
) {
4262+
(policy?.reimbursementChoice !== CONST.POLICY.REIMBURSEMENT_CHOICES.REIMBURSEMENT_NO || !hasOnlyPendingTransactions);
4263+
4264+
if (actionTypeForAssigneeToComplete) {
4265+
const isAssigneeExpenseAction = actionTypeForAssigneeToComplete === CONST.REPORT.ACTION_TYPES_FOR_ASSIGNEE_TO_COMPLETE.EXPENSE;
4266+
if (isAssigneeExpenseAction) {
4267+
const assigneeBadge = getBadgeFromIOUReport(optionOrReport, undefined, policy, optionReportMetadata, invoiceReceiverPolicy, currentUserLogin, currentUserAccountID);
4268+
return {
4269+
reason: CONST.REQUIRES_ATTENTION_REASONS.IS_WAITING_FOR_ASSIGNEE_TO_COMPLETE_ACTION,
4270+
...(assigneeBadge ? {actionBadge: assigneeBadge} : {}),
4271+
};
4272+
}
4273+
4274+
// Task badge candidate - compare with IOU candidate and pick the oldest report action
4275+
const oldestTaskAction = Object.values(reportActions)
4276+
.filter((action) => action.childType === CONST.REPORT.TYPE.TASK && !isTaskCompleted(action) && action.childManagerAccountID === deprecatedCurrentUserAccountID)
4277+
// eslint-disable-next-line rulesdir/prefer-locale-compare-from-context
4278+
.sort((a, b) => (!a.created || !b.created ? 0 : a.created.localeCompare(b.created)))
4279+
.at(0);
4280+
4281+
// If there's a valid IOU action that is older than the task, use the IOU badge instead
4282+
if (hasValidIOUAction && iouReportActionToApproveOrPay?.created && oldestTaskAction?.created && iouReportActionToApproveOrPay.created < oldestTaskAction.created) {
4283+
return {
4284+
reason: CONST.REQUIRES_ATTENTION_REASONS.HAS_CHILD_REPORT_AWAITING_ACTION,
4285+
reportAction: iouReportActionToApproveOrPay,
4286+
actionBadge,
4287+
};
4288+
}
4289+
4290+
return {
4291+
reason: CONST.REQUIRES_ATTENTION_REASONS.IS_WAITING_FOR_ASSIGNEE_TO_COMPLETE_ACTION,
4292+
reportAction: oldestTaskAction,
4293+
actionBadge: CONST.REPORT.ACTION_BADGE.TASK,
4294+
};
4295+
}
4296+
4297+
if (hasValidIOUAction) {
42804298
return {
42814299
reason: CONST.REQUIRES_ATTENTION_REASONS.HAS_CHILD_REPORT_AWAITING_ACTION,
42824300
reportAction: iouReportActionToApproveOrPay,

tests/unit/ReportUtilsTest.ts

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9301,6 +9301,104 @@ describe('ReportUtils', () => {
93019301
expect(result?.reportAction?.reportActionID).toBe('current-user-task');
93029302
});
93039303

9304+
it('should return the IOU badge when the IOU action is older than the task action', async () => {
9305+
const iouReportID = 'iou-report-for-priority';
9306+
9307+
const taskAction: ReportAction = {
9308+
reportActionID: 'task-action-newer',
9309+
actionName: CONST.REPORT.ACTIONS.TYPE.CARD_MISSING_ADDRESS,
9310+
childType: CONST.REPORT.TYPE.TASK,
9311+
childReportID: 'task-report-priority',
9312+
childManagerAccountID: currentUserAccountID,
9313+
created: '2024-01-02 00:00:00',
9314+
originalMessage: {
9315+
assigneeAccountID: currentUserAccountID,
9316+
cardID: 99001,
9317+
},
9318+
};
9319+
9320+
const iouAction: ReportAction = {
9321+
reportActionID: 'iou-action-older',
9322+
actionName: CONST.REPORT.ACTIONS.TYPE.REPORT_PREVIEW,
9323+
childType: CONST.REPORT.TYPE.IOU,
9324+
childReportID: iouReportID,
9325+
childManagerAccountID: currentUserAccountID,
9326+
created: '2024-01-01 00:00:00',
9327+
message: [{html: 'iou preview', text: 'iou preview', type: 'COMMENT'}],
9328+
};
9329+
9330+
const workspaceChat = {
9331+
...createPolicyExpenseChat(42001),
9332+
hasOutstandingChildTask: true,
9333+
hasOutstandingChildRequest: true,
9334+
iouReportID,
9335+
};
9336+
9337+
await Onyx.set(`${ONYXKEYS.COLLECTION.REPORT}${workspaceChat.reportID}`, workspaceChat);
9338+
await Onyx.set(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${workspaceChat.reportID}`, {
9339+
[taskAction.reportActionID]: taskAction,
9340+
[iouAction.reportActionID]: iouAction,
9341+
});
9342+
await waitForBatchedUpdates();
9343+
9344+
const {result: isReportArchived} = renderHook(() => useReportIsArchived(workspaceChat.reportID));
9345+
const result = getReasonAndReportActionThatRequiresAttention(workspaceChat, currentUserEmail, currentUserAccountID, undefined, isReportArchived.current);
9346+
9347+
// The IOU action is older, so its badge should take priority over the task badge
9348+
expect(result?.reason).toBe(CONST.REQUIRES_ATTENTION_REASONS.HAS_CHILD_REPORT_AWAITING_ACTION);
9349+
expect(result?.reportAction?.reportActionID).toBe('iou-action-older');
9350+
expect(result?.actionBadge).toBe(CONST.REPORT.ACTION_BADGE.PAY);
9351+
});
9352+
9353+
it('should return the Task badge when the task action is older than the IOU action', async () => {
9354+
const iouReportID = 'iou-report-for-priority-2';
9355+
9356+
const taskAction: ReportAction = {
9357+
reportActionID: 'task-action-older',
9358+
actionName: CONST.REPORT.ACTIONS.TYPE.CARD_MISSING_ADDRESS,
9359+
childType: CONST.REPORT.TYPE.TASK,
9360+
childReportID: 'task-report-priority-2',
9361+
childManagerAccountID: currentUserAccountID,
9362+
created: '2024-01-01 00:00:00',
9363+
originalMessage: {
9364+
assigneeAccountID: currentUserAccountID,
9365+
cardID: 99002,
9366+
},
9367+
};
9368+
9369+
const iouAction: ReportAction = {
9370+
reportActionID: 'iou-action-newer',
9371+
actionName: CONST.REPORT.ACTIONS.TYPE.REPORT_PREVIEW,
9372+
childType: CONST.REPORT.TYPE.IOU,
9373+
childReportID: iouReportID,
9374+
childManagerAccountID: currentUserAccountID,
9375+
created: '2024-01-02 00:00:00',
9376+
message: [{html: 'iou preview', text: 'iou preview', type: 'COMMENT'}],
9377+
};
9378+
9379+
const workspaceChat = {
9380+
...createPolicyExpenseChat(42002),
9381+
hasOutstandingChildTask: true,
9382+
hasOutstandingChildRequest: true,
9383+
iouReportID,
9384+
};
9385+
9386+
await Onyx.set(`${ONYXKEYS.COLLECTION.REPORT}${workspaceChat.reportID}`, workspaceChat);
9387+
await Onyx.set(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${workspaceChat.reportID}`, {
9388+
[taskAction.reportActionID]: taskAction,
9389+
[iouAction.reportActionID]: iouAction,
9390+
});
9391+
await waitForBatchedUpdates();
9392+
9393+
const {result: isReportArchived} = renderHook(() => useReportIsArchived(workspaceChat.reportID));
9394+
const result = getReasonAndReportActionThatRequiresAttention(workspaceChat, currentUserEmail, currentUserAccountID, undefined, isReportArchived.current);
9395+
9396+
// The task action is older, so the task badge should take priority
9397+
expect(result?.reason).toBe(CONST.REQUIRES_ATTENTION_REASONS.IS_WAITING_FOR_ASSIGNEE_TO_COMPLETE_ACTION);
9398+
expect(result?.reportAction?.reportActionID).toBe('task-action-older');
9399+
expect(result?.actionBadge).toBe(CONST.REPORT.ACTION_BADGE.TASK);
9400+
});
9401+
93049402
it('should return the earliest matching report action for invoice rooms with missing bank account', async () => {
93059403
const invoiceRoomID = '50000';
93069404
const olderChildReportID = '50001';

0 commit comments

Comments
 (0)