Skip to content

Commit df35ada

Browse files
authored
Merge pull request Expensify#89889 from Expensify/claude-alwaysGreenSubmitBadge
Always show green Submit badge/button when report is ready to submit
2 parents d65ec27 + b98fa40 commit df35ada

5 files changed

Lines changed: 102 additions & 21 deletions

File tree

src/components/ReportActionItem/MoneyRequestReportPreview/ReportPreviewActionButton.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,6 @@ function ReportPreviewActionButton({
146146
return (
147147
<SubmitActionButton
148148
iouReportID={iouReportID}
149-
chatReportID={chatReportID}
150149
isSubmittingAnimationRunning={isSubmittingAnimationRunning}
151150
stopAnimation={stopAnimation}
152151
startSubmittingAnimation={startSubmittingAnimation}

src/components/ReportActionItem/MoneyRequestReportPreview/SubmitActionButton.tsx

Lines changed: 3 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import useNetwork from '@hooks/useNetwork';
88
import useOnyx from '@hooks/useOnyx';
99
import usePermissions from '@hooks/usePermissions';
1010
import useReportTransactionsCollection from '@hooks/useReportTransactionsCollection';
11-
import {canSubmitAndIsAwaitingForCurrentUser, hasViolations as hasViolationsReportUtils} from '@libs/ReportUtils';
11+
import {hasViolations as hasViolationsReportUtils} from '@libs/ReportUtils';
1212
import {hasAnyPendingRTERViolation as hasAnyPendingRTERViolationTransactionUtils} from '@libs/TransactionUtils';
1313
import {submitReport} from '@userActions/IOU/ReportWorkflow';
1414
import {markPendingRTERTransactionsAsCash} from '@userActions/Transaction';
@@ -18,21 +18,19 @@ import type {Transaction} from '@src/types/onyx';
1818

1919
type SubmitActionButtonProps = {
2020
iouReportID: string | undefined;
21-
chatReportID: string | undefined;
2221
isSubmittingAnimationRunning: boolean;
2322
stopAnimation: () => void;
2423
startSubmittingAnimation: () => void;
2524
};
2625

27-
function SubmitActionButton({iouReportID, chatReportID, isSubmittingAnimationRunning, stopAnimation, startSubmittingAnimation}: SubmitActionButtonProps) {
26+
function SubmitActionButton({iouReportID, isSubmittingAnimationRunning, stopAnimation, startSubmittingAnimation}: SubmitActionButtonProps) {
2827
const {translate} = useLocalize();
2928
const currentUserDetails = useCurrentUserPersonalDetails();
3029
const currentUserAccountID = currentUserDetails.accountID;
3130
const currentUserEmail = currentUserDetails.email ?? '';
3231
const {isBetaEnabled} = usePermissions();
3332

3433
const [iouReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${iouReportID}`);
35-
const [chatReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${chatReportID}`);
3634
const [policy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${iouReport?.policyID}`);
3735
const [userBillingGracePeriodEnds] = useOnyx(ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_USER_BILLING_GRACE_PERIOD_END);
3836
const [iouReportNextStep] = useOnyx(`${ONYXKEYS.COLLECTION.NEXT_STEP}${iouReportID}`);
@@ -57,20 +55,9 @@ function SubmitActionButton({iouReportID, chatReportID, isSubmittingAnimationRun
5755

5856
const confirmPendingRTERAndProceed = useConfirmPendingRTERAndProceed(hasAnyPendingRTERViolation, handleMarkPendingRTERTransactionsAsCash);
5957

60-
const isWaitingForSubmissionFromCurrentUser = canSubmitAndIsAwaitingForCurrentUser(
61-
iouReport,
62-
chatReport,
63-
policy,
64-
transactions,
65-
transactionViolations,
66-
currentUserEmail,
67-
currentUserAccountID,
68-
reportActions,
69-
);
70-
7158
return (
7259
<AnimatedSubmitButton
73-
success={isWaitingForSubmissionFromCurrentUser}
60+
success
7461
text={translate('common.submit')}
7562
onPress={() => {
7663
confirmPendingRTERAndProceed(() => {

src/libs/ReportPreviewActionUtils.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,6 @@ function canSubmit(
3939
const isExpense = isExpenseReport(report);
4040
const isSubmitter = isCurrentUserSubmitter(report);
4141
const isOpen = isOpenReport(report);
42-
const isManager = report.managerID === currentUserAccountID;
43-
const isAdmin = policy?.role === CONST.POLICY.ROLE.ADMIN;
4442

4543
if (!!transactions && transactions?.length > 0 && transactions.every((transaction) => isPending(transaction))) {
4644
return false;
@@ -62,7 +60,7 @@ function canSubmit(
6260
return false;
6361
}
6462

65-
return isExpense && (isSubmitter || isManager || isAdmin) && isOpen && !isAnyReceiptBeingScanned && !!transactions && transactions.length > 0;
63+
return isExpense && isSubmitter && isOpen && !isAnyReceiptBeingScanned && !!transactions && transactions.length > 0;
6664
}
6765

6866
function canApprove(report: Report, currentUserAccountID: number, reportMetadata: OnyxEntry<ReportMetadata>, policy?: Policy, transactions?: Transaction[]) {

src/libs/actions/OnyxDerived/configs/reportAttributes.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -296,8 +296,14 @@ export default createOnyxDerivedValueConfig({
296296
!!isReportArchived,
297297
reports,
298298
);
299+
300+
// When the report is ready to submit, always show the green Submit badge
301+
// regardless of violations — the user can submit without fix.
302+
const willShowGreenSubmit = requiresAttention && actionGreenBadge === CONST.REPORT.ACTION_BADGE.SUBMIT;
303+
299304
// if report has errors or violations, show red dot
300-
if (reasonAndReportAction) {
305+
// Also skip setting ERROR when we'll show the green Submit badge — let the user submit without fix.
306+
if (reasonAndReportAction && !willShowGreenSubmit) {
301307
needsParentChatErrorPropagation = true;
302308

303309
// RBR/Fix mirrors GBR's access rule: only show on the child when the user can't already

tests/actions/ReportPreviewActionUtilsTest.ts

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,97 @@ describe('getReportPreviewAction', () => {
130130
).toBe(CONST.REPORT.REPORT_PREVIEW_ACTIONS.SUBMIT);
131131
});
132132

133+
it('canSubmit should return false for manager who is not the submitter', async () => {
134+
const MANAGER_ACCOUNT_ID = 2;
135+
const MANAGER_EMAIL = 'manager@mail.com';
136+
const report: Report = {
137+
...createRandomReport(REPORT_ID, undefined),
138+
type: CONST.REPORT.TYPE.EXPENSE,
139+
ownerAccountID: CURRENT_USER_ACCOUNT_ID,
140+
managerID: MANAGER_ACCOUNT_ID,
141+
stateNum: CONST.REPORT.STATE_NUM.OPEN,
142+
statusNum: CONST.REPORT.STATUS_NUM.OPEN,
143+
isWaitingOnBankAccount: false,
144+
};
145+
146+
const policy = createRandomPolicy(0);
147+
policy.autoReportingFrequency = CONST.POLICY.AUTO_REPORTING_FREQUENCIES.IMMEDIATE;
148+
policy.type = CONST.POLICY.TYPE.CORPORATE;
149+
if (policy.harvesting) {
150+
policy.harvesting.enabled = false;
151+
}
152+
await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${REPORT_ID}`, report);
153+
await Onyx.merge(ONYXKEYS.SESSION, {email: MANAGER_EMAIL, accountID: MANAGER_ACCOUNT_ID});
154+
const transaction = {
155+
reportID: `${REPORT_ID}`,
156+
amount: 100,
157+
merchant: 'Test Merchant',
158+
created: '2025-01-01',
159+
} as unknown as Transaction;
160+
161+
const {result: isReportArchived} = renderHook(() => useReportIsArchived(report?.parentReportID));
162+
await waitForBatchedUpdatesWithAct();
163+
164+
expect(
165+
getReportPreviewAction({
166+
isReportArchived: isReportArchived.current,
167+
currentUserAccountID: MANAGER_ACCOUNT_ID,
168+
currentUserLogin: MANAGER_EMAIL,
169+
report,
170+
policy,
171+
transactions: [transaction],
172+
bankAccountList: {},
173+
reportMetadata: undefined,
174+
}),
175+
).not.toBe(CONST.REPORT.REPORT_PREVIEW_ACTIONS.SUBMIT);
176+
});
177+
178+
it('canSubmit should return false for admin who is not the submitter', async () => {
179+
const ADMIN_ACCOUNT_ID = 3;
180+
const ADMIN_EMAIL = 'admin@mail.com';
181+
const report: Report = {
182+
...createRandomReport(REPORT_ID, undefined),
183+
type: CONST.REPORT.TYPE.EXPENSE,
184+
ownerAccountID: CURRENT_USER_ACCOUNT_ID,
185+
managerID: 2,
186+
stateNum: CONST.REPORT.STATE_NUM.OPEN,
187+
statusNum: CONST.REPORT.STATUS_NUM.OPEN,
188+
isWaitingOnBankAccount: false,
189+
};
190+
191+
const policy = createRandomPolicy(0);
192+
policy.autoReportingFrequency = CONST.POLICY.AUTO_REPORTING_FREQUENCIES.IMMEDIATE;
193+
policy.type = CONST.POLICY.TYPE.CORPORATE;
194+
policy.role = CONST.POLICY.ROLE.ADMIN;
195+
if (policy.harvesting) {
196+
policy.harvesting.enabled = false;
197+
}
198+
await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${REPORT_ID}`, report);
199+
await Onyx.merge(ONYXKEYS.SESSION, {email: ADMIN_EMAIL, accountID: ADMIN_ACCOUNT_ID});
200+
const transaction = {
201+
reportID: `${REPORT_ID}`,
202+
amount: 100,
203+
merchant: 'Test Merchant',
204+
created: '2025-01-01',
205+
} as unknown as Transaction;
206+
207+
const {result: isReportArchived} = renderHook(() => useReportIsArchived(report?.parentReportID));
208+
await waitForBatchedUpdatesWithAct();
209+
210+
expect(
211+
getReportPreviewAction({
212+
isReportArchived: isReportArchived.current,
213+
currentUserAccountID: ADMIN_ACCOUNT_ID,
214+
currentUserLogin: ADMIN_EMAIL,
215+
report,
216+
policy,
217+
transactions: [transaction],
218+
bankAccountList: {},
219+
reportMetadata: undefined,
220+
}),
221+
).not.toBe(CONST.REPORT.REPORT_PREVIEW_ACTIONS.SUBMIT);
222+
});
223+
133224
it('canSubmit should return true for open report in instant submit policy with no approvers', async () => {
134225
const report: Report = {
135226
...createRandomReport(REPORT_ID, undefined),

0 commit comments

Comments
 (0)