Problem
After approving an expense report, the green Approve action badge in the LHN disappears (correct) but is immediately replaced by a green dot ("greenlight" / GBR indicator). The green dot should not be there — the report is fully actioned. Navigating away and back (which triggers OpenReport) clears the green dot, confirming the optimistic state is wrong and the server state is right.
Reproduction
- Be the approver of an expense report that shows the green Approve badge in the LHN.
- Approve the report.
- Observe the LHN row: the Approve badge is replaced by a spurious green dot.
- Navigate to another report and back (triggers
OpenReport).
- The green dot disappears.
Expected: After approving, the LHN row shows no action badge and no green dot.
Actual: A green dot lingers until OpenReport refetches server data.
Root cause
The LHN green dot for an expense report is driven by the report's own hasOutstandingChildRequest field. In getReasonAndReportActionThatRequiresAttention:
const hasValidIOUAction =
((optionOrReport.hasOutstandingChildRequest === true && !hasStaleChildRequest) || iouReportActionToApproveOrPay?.reportActionID) &&
(policy?.reimbursementChoice !== CONST.POLICY.REIMBURSEMENT_CHOICES.REIMBURSEMENT_NO || !hasOnlyPendingTransactions);
When hasOutstandingChildRequest === true, this returns HAS_CHILD_REPORT_AWAITING_ACTION, which sets brickRoadStatus = INFO (the green dot) in reportAttributes.ts.
The optimistic approve flow in approveMoneyRequest (ReportWorkflow.ts:480-526) correctly updates stateNum, statusNum, managerID, and nextStep on the expense report, and recomputes hasOutstandingChildRequest — but only on the parent chat report:
const updatedExpenseReport = {
...expenseReport,
// ... stateNum, statusNum, managerID, nextStep ... <-- no hasOutstandingChildRequest
};
optimisticData.push({ ...key: REPORT${expenseReport.reportID}, value: updatedExpenseReport });
if (chatReport) {
optimisticData.push({
key: `${ONYXKEYS.COLLECTION.REPORT}${expenseReport.chatReportID}`, // <-- chat report, not expense report
value: { hasOutstandingChildRequest: hasOutstandingChildRequest(chatReport, updatedExpenseReport, ...) },
});
}
So the expense report keeps its stale hasOutstandingChildRequest: true until OpenReport returns fresh server data with false.
Evidence (Onyx for the expense report)
reportID: 8367965920096919, type: expense, stateNum: 2, statusNum: 3, non-reimbursable (total: -8710, nonReimbursableTotal: -8710, reimbursableTotal: 0).
Immediately after optimistic approve (buggy — green dot shows):
"hasOutstandingChildRequest": true,
"nextStep": { "icon": "hourglass", "messageKey": "waitingToApprove" }
After navigating away/back — OpenReport (correct — no green dot):
"hasOutstandingChildRequest": false,
"nextStep": { "icon": "checkmark", "messageKey": "noFurtherAction" }
The single field that toggles the green dot here is hasOutstandingChildRequest. (nextStep does not factor into the green-dot or action-badge computation, though note the optimistic nextStep above also lands as stale waitingToApprove rather than the server's noFurtherAction — the nextStep optimistic value is built by buildOptimisticNextStep and should resolve to noFurtherAction for this reimbursableSpend === 0 case; worth confirming during the fix.)
Recommended fix
In approveMoneyRequest, include a recomputed hasOutstandingChildRequest on the expense report's optimistic merge (updatedExpenseReport), not just on the chat report — so the green dot clears optimistically instead of waiting for OpenReport. Add a matching failureData entry to roll it back if the approve fails.
Investigation details
- Green dot (
brickRoadIndicator === INFO) is read by SidebarUtils.getOptionData from the derived reportAttributes.brickRoadStatus (reportAttributes.ts:299-342), which delegates to getReasonAndReportActionThatRequiresAttention in ReportUtils.ts.
- The green Approve action badge is computed separately by
getBadgeFromIOUReport / canApproveIOU (ReportWorkflow.ts); after approval canApproveIOU returns false, so the badge correctly disappears.
nextStep is not consumed by either indicator.
Reported in Slack.
Upwork Automation - Do Not Edit
Issue Owner
Current Issue Owner: @aimane-chnaif
Problem
After approving an expense report, the green Approve action badge in the LHN disappears (correct) but is immediately replaced by a green dot ("greenlight" / GBR indicator). The green dot should not be there — the report is fully actioned. Navigating away and back (which triggers
OpenReport) clears the green dot, confirming the optimistic state is wrong and the server state is right.Reproduction
OpenReport).Expected: After approving, the LHN row shows no action badge and no green dot.
Actual: A green dot lingers until
OpenReportrefetches server data.Root cause
The LHN green dot for an expense report is driven by the report's own
hasOutstandingChildRequestfield. IngetReasonAndReportActionThatRequiresAttention:When
hasOutstandingChildRequest === true, this returnsHAS_CHILD_REPORT_AWAITING_ACTION, which setsbrickRoadStatus = INFO(the green dot) inreportAttributes.ts.The optimistic approve flow in
approveMoneyRequest(ReportWorkflow.ts:480-526) correctly updatesstateNum,statusNum,managerID, andnextStepon the expense report, and recomputeshasOutstandingChildRequest— but only on the parent chat report:So the expense report keeps its stale
hasOutstandingChildRequest: trueuntilOpenReportreturns fresh server data withfalse.Evidence (Onyx for the expense report)
reportID: 8367965920096919,type: expense,stateNum: 2,statusNum: 3, non-reimbursable (total: -8710,nonReimbursableTotal: -8710,reimbursableTotal: 0).Immediately after optimistic approve (buggy — green dot shows):
After navigating away/back —
OpenReport(correct — no green dot):The single field that toggles the green dot here is
hasOutstandingChildRequest. (nextStepdoes not factor into the green-dot or action-badge computation, though note the optimisticnextStepabove also lands as stalewaitingToApproverather than the server'snoFurtherAction— thenextStepoptimistic value is built bybuildOptimisticNextStepand should resolve tonoFurtherActionfor thisreimbursableSpend === 0case; worth confirming during the fix.)Recommended fix
In
approveMoneyRequest, include a recomputedhasOutstandingChildRequeston the expense report's optimistic merge (updatedExpenseReport), not just on the chat report — so the green dot clears optimistically instead of waiting forOpenReport. Add a matchingfailureDataentry to roll it back if the approve fails.Investigation details
brickRoadIndicator === INFO) is read bySidebarUtils.getOptionDatafrom the derivedreportAttributes.brickRoadStatus(reportAttributes.ts:299-342), which delegates togetReasonAndReportActionThatRequiresAttentioninReportUtils.ts.getBadgeFromIOUReport/canApproveIOU(ReportWorkflow.ts); after approvalcanApproveIOUreturns false, so the badge correctly disappears.nextStepis not consumed by either indicator.Reported in Slack.
Upwork Automation - Do Not Edit
Issue Owner
Current Issue Owner: @aimane-chnaif