Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
70296be
add pending workflow update helper
lakchote May 1, 2026
bb79e9a
add submit manager selection helper
lakchote May 1, 2026
e4a74d5
fix report submit workflow handling
lakchote May 1, 2026
7b4fbf5
fix search submit manager selection
lakchote May 1, 2026
aa8a19b
block primary submit during workflow update
lakchote May 1, 2026
ff3c775
block secondary submit during workflow update
lakchote May 1, 2026
8dd96eb
add tests for submit approver rca
lakchote May 1, 2026
12a48d4
add test for pending workflow helper
lakchote May 1, 2026
bec1382
add test for pending secondary submit
lakchote May 1, 2026
284ac99
Merge main into lucien/fix-submit-approver-rca
lakchote May 4, 2026
0c53f56
fix `hasPendingReportRetract()` helper
lakchote May 4, 2026
deec968
fix retract pending state in workflow
lakchote May 4, 2026
4711caf
fix submit manager fallback
lakchote May 4, 2026
077a8b8
fix search submit pending guard
lakchote May 4, 2026
2b03eb1
fix primary submit pending guard
lakchote May 4, 2026
8137ce6
fix secondary submit pending guard
lakchote May 4, 2026
22b99b6
update `hasPendingReportRetract()` test
lakchote May 4, 2026
e94d3f1
add primary submit pending tests
lakchote May 4, 2026
6a3c4cb
update secondary submit pending tests
lakchote May 4, 2026
548c24a
add submit workflow pending tests
lakchote May 4, 2026
086e1a2
Merge main into lucien/fix-submit-approver-rca
lakchote May 5, 2026
dc0329f
remove pending retract helper
lakchote May 5, 2026
06aa6a3
allow workflow submit after retract
lakchote May 5, 2026
36fa662
allow search submit after retract
lakchote May 5, 2026
0fd8770
allow primary submit after retract
lakchote May 5, 2026
efc375d
allow secondary submit after retract
lakchote May 5, 2026
64a2394
update workflow retract submit tests
lakchote May 5, 2026
bb80645
update primary retract submit test
lakchote May 5, 2026
6abbfc5
update secondary retract submit test
lakchote May 5, 2026
75ec71e
remove pending retract helper test
lakchote May 5, 2026
3bf9b88
fix workspace submit manager state
lakchote May 5, 2026
da3beb5
add test for workspace submit manager
lakchote May 5, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 27 additions & 1 deletion src/libs/PolicyUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ import {formatMemberForList} from './OptionsListUtils';
import type {MemberForList} from './OptionsListUtils';
import {getAccountIDsByLogins, getLoginByAccountID, getLoginsByAccountIDs, getPersonalDetailByEmail} from './PersonalDetailsUtils';
import {getAllSortedTransactions, getCategory, getTag, getTagArrayFromName} from './TransactionUtils';
import {isPublicDomain} from './ValidationUtils';
import {isPublicDomain, isValidAccountRoute} from './ValidationUtils';

type MemberEmailsToAccountIDs = Record<string, number>;

Expand Down Expand Up @@ -1242,6 +1242,31 @@ function getSubmitToAccountID(policy: OnyxEntry<Policy>, expenseReport: OnyxEntr
return getManagerAccountID(policy, expenseReport);
}

function getSubmitReportManagerAccountID(policy: OnyxEntry<Policy>, expenseReport: OnyxEntry<Report>): number | undefined {
const ownerAccountID = expenseReport?.ownerAccountID ?? CONST.DEFAULT_NUMBER_ID;
const existingManagerID = expenseReport?.managerID;
const approvalRules = policy?.rules?.approvalRules;
const ruleApprover = !isSubmitAndClose(policy) && approvalRules?.length ? getFirstRuleApprover(approvalRules, expenseReport) : '';
const submitToAccountID = ruleApprover ? (getAccountIDsByLogins([ruleApprover]).at(0) ?? -1) : getManagerAccountID(policy, expenseReport);
const isValidSubmitToAccountID = isValidAccountRoute(submitToAccountID);
const isValidExistingManagerID = isValidAccountRoute(existingManagerID ?? CONST.DEFAULT_NUMBER_ID) && existingManagerID !== ownerAccountID;
const employeeLogin = getLoginByAccountID(ownerAccountID) ?? '';
const hasReliablePolicyRoute =
([CONST.POLICY.APPROVAL_MODE.OPTIONAL, CONST.POLICY.APPROVAL_MODE.BASIC] as Array<ValueOf<typeof CONST.POLICY.APPROVAL_MODE>>).includes(getApprovalWorkflow(policy)) ||
!!ruleApprover ||
!!policy?.employeeList?.[employeeLogin];

if (hasReliablePolicyRoute && isValidSubmitToAccountID) {
return submitToAccountID;
}

if (!hasReliablePolicyRoute && isValidExistingManagerID) {
return existingManagerID;
}

return isValidSubmitToAccountID ? submitToAccountID : existingManagerID;
}

function getManagerAccountEmail(policy: OnyxEntry<Policy>, expenseReport: OnyxEntry<Report>): string {
const managerAccountID = getManagerAccountID(policy, expenseReport);
return getLoginsByAccountIDs([managerAccountID]).at(0) ?? '';
Expand Down Expand Up @@ -2257,6 +2282,7 @@ export {
getDefaultChatEnabledPolicy,
getForwardsToAccount,
getSubmitToAccountID,
getSubmitReportManagerAccountID,
getAllTaxRatesNamesAndKeys as getAllTaxRates,
getAllTaxRatesNamesAndValues,
getTagNamesFromTagsLists,
Expand Down
15 changes: 7 additions & 8 deletions src/libs/actions/IOU/ReportWorkflow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import {getMicroSecondOnyxErrorWithTranslationKey} from '@libs/ErrorUtils';
import Navigation from '@libs/Navigation/Navigation';
import {getIsOffline} from '@libs/NetworkState';
import {buildNextStepNew, buildOptimisticNextStep} from '@libs/NextStepUtils';
import {arePaymentsEnabled, getSubmitToAccountID, hasDynamicExternalWorkflow, isPaidGroupPolicy, isPolicyAdmin, isSubmitAndClose} from '@libs/PolicyUtils';
import {arePaymentsEnabled, getSubmitReportManagerAccountID, hasDynamicExternalWorkflow, isPaidGroupPolicy, isPolicyAdmin, isSubmitAndClose} from '@libs/PolicyUtils';
import {getAllReportActions, getReportActionHtml, getReportActionText, hasPendingDEWApprove, isCreatedAction, isDeletedAction} from '@libs/ReportActionsUtils';
import {
buildOptimisticApprovedReportAction,
Expand Down Expand Up @@ -1258,10 +1258,11 @@ function submitReport({
return;
}

const parentReport = getReportOrDraftReport(expenseReport.parentReportID);
const isCurrentUserManager = currentUserAccountIDParam === expenseReport.managerID;
const isSubmitAndClosePolicy = isSubmitAndClose(policy);
const adminAccountID = policy?.role === CONST.POLICY.ROLE.ADMIN ? currentUserAccountIDParam : undefined;
const parentReport = getReportOrDraftReport(expenseReport.parentReportID);
const managerID = getSubmitReportManagerAccountID(policy, expenseReport);
const isCurrentUserManager = currentUserAccountIDParam === managerID;
const optimisticSubmittedReportAction = buildOptimisticSubmittedReportAction(
expenseReport?.total ?? 0,
expenseReport.currency ?? '',
Expand Down Expand Up @@ -1300,9 +1301,6 @@ function submitReport({
isASAPSubmitBetaEnabled,
isUnapprove: true,
});
const submitToAccountID = getSubmitToAccountID(policy, expenseReport);
const managerID = submitToAccountID > 0 ? submitToAccountID : expenseReport.managerID;

const optimisticData: Array<
OnyxUpdate<typeof ONYXKEYS.COLLECTION.REPORT_ACTIONS | typeof ONYXKEYS.COLLECTION.REPORT | typeof ONYXKEYS.COLLECTION.NEXT_STEP | typeof ONYXKEYS.COLLECTION.REPORT_METADATA>
> = [];
Expand Down Expand Up @@ -1440,8 +1438,9 @@ function submitReport({
onyxMethod: Onyx.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.REPORT}${expenseReport.reportID}`,
value: {
statusNum: CONST.REPORT.STATUS_NUM.OPEN,
stateNum: CONST.REPORT.STATE_NUM.OPEN,
statusNum: expenseReport.statusNum,
stateNum: expenseReport.stateNum,
managerID: expenseReport.managerID,
...(isDEWPolicy
? {}
: {
Expand Down
8 changes: 4 additions & 4 deletions src/libs/actions/Search.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import type {SearchFullscreenNavigatorParamList} from '@libs/Navigation/types';
import enhanceParameters from '@libs/Network/enhanceParameters';
import {rand64} from '@libs/NumberUtils';
import {getActivePaymentType} from '@libs/PaymentUtils';
import {getSubmitToAccountID, getValidConnectedIntegration, isDelayedSubmissionEnabled} from '@libs/PolicyUtils';
import {getSubmitReportManagerAccountID, getValidConnectedIntegration, isDelayedSubmissionEnabled} from '@libs/PolicyUtils';
import type {OptimisticExportIntegrationAction} from '@libs/ReportUtils';
import {
buildOptimisticExportIntegrationAction,
Expand Down Expand Up @@ -615,6 +615,7 @@ function search({
}

function submitMoneyRequestOnSearch(hash: number, reportList: Report[], policy: Policy[], currentSearchKey?: SearchKey) {
const firstReport = (reportList.at(0) ?? {}) as Report;
const optimisticData: Array<OnyxUpdate<typeof ONYXKEYS.COLLECTION.RAM_ONLY_REPORT_LOADING_STATE>> = [
{
onyxMethod: Onyx.METHOD.MERGE_COLLECTION,
Expand Down Expand Up @@ -657,10 +658,9 @@ function submitMoneyRequestOnSearch(hash: number, reportList: Report[], policy:
},
];

const report = (reportList.at(0) ?? {}) as Report;
const parameters: SubmitReportParams = {
reportID: report.reportID,
managerAccountID: getSubmitToAccountID(policy.at(0), report) ?? report?.managerID,
reportID: firstReport.reportID,
managerAccountID: getSubmitReportManagerAccountID(policy.at(0), firstReport),
reportActionID: rand64(),
};

Expand Down
Loading
Loading