diff --git a/src/libs/PolicyUtils.ts b/src/libs/PolicyUtils.ts index 5d762f52b6ab2..9e267ef1991ec 100644 --- a/src/libs/PolicyUtils.ts +++ b/src/libs/PolicyUtils.ts @@ -48,6 +48,7 @@ import {hasSynchronizationErrorMessage, isConnectionUnverified} from './actions/ import {shouldShowQBOReimbursableExportDestinationAccountError} from './actions/connections/QuickbooksOnline'; import {getCategoryApproverRule} from './CategoryUtils'; import {convertToBackendAmount} from './CurrencyUtils'; +import Log from './Log'; import Navigation from './Navigation/Navigation'; import {isOffline as isOfflineNetworkStore} from './Network/NetworkStore'; import {formatMemberForList} from './OptionsListUtils'; @@ -1012,18 +1013,51 @@ function getManagerAccountID(policy: OnyxEntry, expenseReport: OnyxEntry const employeeAccountID = expenseReport?.ownerAccountID ?? CONST.DEFAULT_NUMBER_ID; const employeeLogin = getLoginsByAccountIDs([employeeAccountID]).at(0) ?? ''; const defaultApprover = getDefaultApprover(policy); + const approvalWorkflow = getApprovalWorkflow(policy); // For policy using the optional or basic workflow, the manager is the policy default approver. - if (([CONST.POLICY.APPROVAL_MODE.OPTIONAL, CONST.POLICY.APPROVAL_MODE.BASIC] as Array>).includes(getApprovalWorkflow(policy))) { - return getAccountIDsByLogins([defaultApprover]).at(0) ?? -1; + if (([CONST.POLICY.APPROVAL_MODE.OPTIONAL, CONST.POLICY.APPROVAL_MODE.BASIC] as Array>).includes(approvalWorkflow)) { + const managerAccountID = getAccountIDsByLogins([defaultApprover]).at(0) ?? -1; + Log.info('[getManagerAccountID] Using default approver for non-advanced workflow', false, { + policyID: policy?.id, + approvalWorkflow, + employeeAccountID, + employeeLogin, + defaultApprover, + policyApprover: policy?.approver, + policyOwner: policy?.owner, + managerAccountID, + }); + return managerAccountID; } const employee = policy?.employeeList?.[employeeLogin]; if (!employee && !defaultApprover) { + Log.info('[getManagerAccountID] No employee found and no default approver', false, { + policyID: policy?.id, + employeeAccountID, + employeeLogin, + employeeListKeys: Object.keys(policy?.employeeList ?? {}), + hasPolicy: !!policy, + }); return -1; } - return getAccountIDsByLogins([employee?.submitsTo ?? defaultApprover]).at(0) ?? -1; + const submitsTo = employee?.submitsTo ?? defaultApprover; + const managerAccountID = getAccountIDsByLogins([submitsTo]).at(0) ?? -1; + Log.info('[getManagerAccountID] Resolved manager for advanced workflow', false, { + policyID: policy?.id, + employeeAccountID, + employeeLogin, + employeeFound: !!employee, + employeeSubmitsTo: employee?.submitsTo, + defaultApprover, + policyApprover: policy?.approver, + policyOwner: policy?.owner, + resolvedSubmitsTo: submitsTo, + managerAccountID, + }); + return managerAccountID; } /** @@ -1037,9 +1071,26 @@ function getSubmitToAccountID(policy: OnyxEntry, expenseReport: OnyxEntr ruleApprovers.shift(); } if (ruleApprovers.length > 0 && !isSubmitAndClose(policy)) { - return getAccountIDsByLogins([ruleApprovers.at(0) ?? '']).at(0) ?? -1; - } - + const ruleApproverAccountID = getAccountIDsByLogins([ruleApprovers.at(0) ?? '']).at(0) ?? -1; + Log.info('[getSubmitToAccountID] Using rule approver', false, { + policyID: policy?.id, + reportID: expenseReport?.reportID, + employeeLogin, + ruleApprovers, + selectedRuleApprover: ruleApprovers.at(0), + ruleApproverAccountID, + }); + return ruleApproverAccountID; + } + + Log.info('[getSubmitToAccountID] No rule approvers, falling through to getManagerAccountID', false, { + policyID: policy?.id, + reportID: expenseReport?.reportID, + employeeLogin, + ruleApproversCount: ruleApprovers.length, + isSubmitAndClosePolicy: isSubmitAndClose(policy), + reportManagerID: expenseReport?.managerID, + }); return getManagerAccountID(policy, expenseReport); } diff --git a/src/libs/actions/IOU/index.ts b/src/libs/actions/IOU/index.ts index 823e6a08861b0..6fb49bb09647e 100644 --- a/src/libs/actions/IOU/index.ts +++ b/src/libs/actions/IOU/index.ts @@ -11107,9 +11107,25 @@ function submitReport( }); } + const submitToAccountID = getSubmitToAccountID(policy, expenseReport); + const managerAccountIDParam = submitToAccountID ?? expenseReport.managerID; + Log.info('[submitReport] Submitting report with approval chain diagnostic', false, { + reportID: expenseReport.reportID, + policyID: expenseReport.policyID, + reportOwnerAccountID: expenseReport.ownerAccountID, + reportExistingManagerID: expenseReport.managerID, + getSubmitToAccountIDResult: submitToAccountID, + managerAccountIDSentToAPI: managerAccountIDParam, + optimisticManagerID: managerID, + approvalChainFromGetApprovalChain: approvalChain, + policyApprovalMode: policy?.approvalMode, + policyOwner: policy?.owner, + policyApprover: policy?.approver, + isDEWPolicy, + }); const parameters: SubmitReportParams = { reportID: expenseReport.reportID, - managerAccountID: getSubmitToAccountID(policy, expenseReport) ?? expenseReport.managerID, + managerAccountID: managerAccountIDParam, reportActionID: optimisticSubmittedReportAction.reportActionID, }; diff --git a/src/libs/actions/Search.ts b/src/libs/actions/Search.ts index edf493de02ba0..8be0a395f651f 100644 --- a/src/libs/actions/Search.ts +++ b/src/libs/actions/Search.ts @@ -16,6 +16,7 @@ import {getCommandURL} from '@libs/ApiUtils'; import {convertToDisplayString} from '@libs/CurrencyUtils'; import {getMicroSecondOnyxErrorWithTranslationKey} from '@libs/ErrorUtils'; import fileDownload from '@libs/fileDownload'; +import Log from '@libs/Log'; import isSearchTopmostFullScreenRoute from '@libs/Navigation/helpers/isSearchTopmostFullScreenRoute'; import Navigation, {navigationRef} from '@libs/Navigation/Navigation'; import type {SearchFullscreenNavigatorParamList} from '@libs/Navigation/types'; @@ -559,9 +560,24 @@ function submitMoneyRequestOnSearch(hash: number, reportList: Report[], policy: ]; const report = (reportList.at(0) ?? {}) as Report; + const policyForReport = policy.at(0); + const submitToAccountID = getSubmitToAccountID(policyForReport, report); + const managerAccountIDParam = submitToAccountID ?? report?.managerID; + Log.info('[submitMoneyRequestOnSearch] Submitting report with approval chain diagnostic', false, { + reportID: report.reportID, + reportOwnerAccountID: report.ownerAccountID, + reportExistingManagerID: report.managerID, + getSubmitToAccountIDResult: submitToAccountID, + managerAccountIDSentToAPI: managerAccountIDParam, + hasPolicyData: !!policyForReport, + policyID: policyForReport?.id, + policyApprovalMode: policyForReport?.approvalMode, + policyOwner: policyForReport?.owner, + policyApprover: policyForReport?.approver, + }); const parameters: SubmitReportParams = { reportID: report.reportID, - managerAccountID: getSubmitToAccountID(policy.at(0), report) ?? report?.managerID, + managerAccountID: managerAccountIDParam, reportActionID: rand64(), };