Skip to content

Commit

Permalink
Merge pull request Expensify#51196 from nkdengineer/fix/51001
Browse files Browse the repository at this point in the history
Update getSubmitToAccountID with category and tag approver
  • Loading branch information
Beamanator authored Nov 11, 2024
2 parents da595d4 + e2c5d24 commit 0275692
Show file tree
Hide file tree
Showing 3 changed files with 43 additions and 16 deletions.
37 changes: 32 additions & 5 deletions src/libs/PolicyUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
import INPUT_IDS from '@src/types/form/NetSuiteCustomFieldForm';
import type {OnyxInputOrEntry, Policy, PolicyCategories, PolicyEmployeeList, PolicyTagLists, PolicyTags, TaxRate} from '@src/types/onyx';
import type {OnyxInputOrEntry, Policy, PolicyCategories, PolicyEmployeeList, PolicyTagLists, PolicyTags, Report, TaxRate} from '@src/types/onyx';
import type {CardFeedData} from '@src/types/onyx/CardFeeds';
import type {ErrorFields, PendingAction, PendingFields} from '@src/types/onyx/OnyxCommon';
import type {
Expand All @@ -31,10 +31,12 @@ import type {
import type PolicyEmployee from '@src/types/onyx/PolicyEmployee';
import {isEmptyObject} from '@src/types/utils/EmptyObject';
import {hasSynchronizationErrorMessage} from './actions/connections';
import {getCategoryApproverRule} from './CategoryUtils';
import * as Localize from './Localize';
import Navigation from './Navigation/Navigation';
import * as NetworkStore from './Network/NetworkStore';
import {getAccountIDsByLogins, getLoginsByAccountIDs, getPersonalDetailByEmail} from './PersonalDetailsUtils';
import {getAllReportTransactions, getCategory, getTag} from './TransactionUtils';

type MemberEmailsToAccountIDs = Record<string, number>;

Expand Down Expand Up @@ -525,12 +527,37 @@ function getDefaultApprover(policy: OnyxEntry<Policy>): string {
}

/**
* Returns the accountID to whom the given employeeAccountID submits reports to in the given Policy.
* Returns the accountID to whom the given expenseReport submits reports to in the given Policy.
*/
function getSubmitToAccountID(policy: OnyxEntry<Policy>, employeeAccountID: number): number {
function getSubmitToAccountID(policy: OnyxEntry<Policy>, expenseReport: OnyxEntry<Report>): number {
const employeeAccountID = expenseReport?.ownerAccountID ?? -1;
const employeeLogin = getLoginsByAccountIDs([employeeAccountID]).at(0) ?? '';
const defaultApprover = getDefaultApprover(policy);

let categoryAppover;
let tagApprover;
const allTransactions = getAllReportTransactions(expenseReport?.reportID).sort((transA, transB) => (transA.created < transB.created ? -1 : 1));

// Before submitting to their `submitsTo` (in a policy on Advanced Approvals), submit to category/tag approvers.
// Category approvers are prioritized, then tag approvers.
for (let i = 0; i < allTransactions.length; i++) {
const transaction = allTransactions.at(i);
const tag = getTag(transaction);
const category = getCategory(transaction);
categoryAppover = getCategoryApproverRule(policy?.rules?.approvalRules ?? [], category)?.approver;
if (categoryAppover) {
return getAccountIDsByLogins([categoryAppover]).at(0) ?? -1;
}

if (!tagApprover && getTagApproverRule(policy?.id ?? '-1', tag)?.approver) {
tagApprover = getTagApproverRule(policy?.id ?? '-1', tag)?.approver;
}
}

if (tagApprover) {
return getAccountIDsByLogins([tagApprover]).at(0) ?? -1;
}

// 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<ValueOf<typeof CONST.POLICY.APPROVAL_MODE>>).includes(getApprovalWorkflow(policy))) {
return getAccountIDsByLogins([defaultApprover]).at(0) ?? -1;
Expand All @@ -544,8 +571,8 @@ function getSubmitToAccountID(policy: OnyxEntry<Policy>, employeeAccountID: numb
return getAccountIDsByLogins([employee.submitsTo ?? defaultApprover]).at(0) ?? -1;
}

function getSubmitToEmail(policy: OnyxEntry<Policy>, employeeAccountID: number): string {
const submitToAccountID = getSubmitToAccountID(policy, employeeAccountID);
function getSubmitToEmail(policy: OnyxEntry<Policy>, expenseReport: OnyxEntry<Report>): string {
const submitToAccountID = getSubmitToAccountID(policy, expenseReport);
return getLoginsByAccountIDs([submitToAccountID]).at(0) ?? '';
}

Expand Down
13 changes: 7 additions & 6 deletions src/libs/ReportUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1311,7 +1311,7 @@ function isAwaitingFirstLevelApproval(report: OnyxEntry<Report>): boolean {
return false;
}

const submitsToAccountID = PolicyUtils.getSubmitToAccountID(getPolicy(report.policyID), report.ownerAccountID ?? -1);
const submitsToAccountID = PolicyUtils.getSubmitToAccountID(getPolicy(report.policyID), report);

return isProcessingReport(report) && submitsToAccountID === report.managerID;
}
Expand Down Expand Up @@ -3163,7 +3163,7 @@ function canEditMoneyRequest(reportAction: OnyxInputOrEntry<ReportAction<typeof
}

if (policy?.type === CONST.POLICY.TYPE.CORPORATE && moneyRequestReport && isSubmitted && isCurrentUserSubmitter(moneyRequestReport.reportID)) {
const isForwarded = PolicyUtils.getSubmitToAccountID(policy, moneyRequestReport.ownerAccountID ?? -1) !== moneyRequestReport.managerID;
const isForwarded = PolicyUtils.getSubmitToAccountID(policy, moneyRequestReport) !== moneyRequestReport.managerID;
return !isForwarded;
}

Expand Down Expand Up @@ -4694,7 +4694,7 @@ function buildOptimisticExpenseReport(
};

// Get the approver/manager for this report to properly display the optimistic data
const submitToAccountID = PolicyUtils.getSubmitToAccountID(policy, payeeAccountID);
const submitToAccountID = PolicyUtils.getSubmitToAccountID(policy, expenseReport);
if (submitToAccountID) {
expenseReport.managerID = submitToAccountID;
}
Expand Down Expand Up @@ -7979,7 +7979,7 @@ function isAllowedToApproveExpenseReport(report: OnyxEntry<Report>, approverAcco

function isAllowedToSubmitDraftExpenseReport(report: OnyxEntry<Report>): boolean {
const policy = getPolicy(report?.policyID);
const submitToAccountID = PolicyUtils.getSubmitToAccountID(policy, report?.ownerAccountID ?? -1);
const submitToAccountID = PolicyUtils.getSubmitToAccountID(policy, report);

return isAllowedToApproveExpenseReport(report, submitToAccountID);
}
Expand Down Expand Up @@ -8375,15 +8375,16 @@ function isExported(reportActions: OnyxEntry<ReportActions>) {
return Object.values(reportActions).some((action) => ReportActionsUtils.isExportIntegrationAction(action));
}

function getApprovalChain(policy: OnyxEntry<Policy>, employeeAccountID: number, reportTotal: number): string[] {
function getApprovalChain(policy: OnyxEntry<Policy>, expenseReport: OnyxEntry<Report>): string[] {
const approvalChain: string[] = [];
const reportTotal = expenseReport?.total ?? 0;

// If the policy is not on advanced approval mode, we should not use the approval chain even if it exists.
if (!PolicyUtils.isControlOnAdvancedApprovalMode(policy)) {
return approvalChain;
}

let nextApproverEmail = PolicyUtils.getSubmitToEmail(policy, employeeAccountID);
let nextApproverEmail = PolicyUtils.getSubmitToEmail(policy, expenseReport);

while (nextApproverEmail && !approvalChain.includes(nextApproverEmail)) {
approvalChain.push(nextApproverEmail);
Expand Down
9 changes: 4 additions & 5 deletions src/libs/actions/IOU.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7167,10 +7167,9 @@ function isLastApprover(approvalChain: string[]): boolean {
}

function getNextApproverAccountID(report: OnyxEntry<OnyxTypes.Report>) {
const ownerAccountID = report?.ownerAccountID ?? -1;
const policy = PolicyUtils.getPolicy(report?.policyID);
const approvalChain = ReportUtils.getApprovalChain(policy, ownerAccountID, report?.total ?? 0);
const submitToAccountID = PolicyUtils.getSubmitToAccountID(policy, ownerAccountID);
const approvalChain = ReportUtils.getApprovalChain(policy, report);
const submitToAccountID = PolicyUtils.getSubmitToAccountID(policy, report);

if (approvalChain.length === 0) {
return submitToAccountID;
Expand Down Expand Up @@ -7198,7 +7197,7 @@ function approveMoneyRequest(expenseReport: OnyxEntry<OnyxTypes.Report>, full?:
}
const optimisticApprovedReportAction = ReportUtils.buildOptimisticApprovedReportAction(total, expenseReport?.currency ?? '', expenseReport?.reportID ?? '-1');

const approvalChain = ReportUtils.getApprovalChain(PolicyUtils.getPolicy(expenseReport?.policyID), expenseReport?.ownerAccountID ?? -1, expenseReport?.total ?? 0);
const approvalChain = ReportUtils.getApprovalChain(PolicyUtils.getPolicy(expenseReport?.policyID), expenseReport);

const predictedNextStatus = isLastApprover(approvalChain) ? CONST.REPORT.STATUS_NUM.APPROVED : CONST.REPORT.STATUS_NUM.SUBMITTED;
const predictedNextState = isLastApprover(approvalChain) ? CONST.REPORT.STATE_NUM.APPROVED : CONST.REPORT.STATE_NUM.SUBMITTED;
Expand Down Expand Up @@ -7558,7 +7557,7 @@ function submitReport(expenseReport: OnyxTypes.Report) {

const parameters: SubmitReportParams = {
reportID: expenseReport.reportID,
managerAccountID: PolicyUtils.getSubmitToAccountID(policy, expenseReport.ownerAccountID ?? -1) ?? expenseReport.managerID,
managerAccountID: PolicyUtils.getSubmitToAccountID(policy, expenseReport) ?? expenseReport.managerID,
reportActionID: optimisticSubmittedReportAction.reportActionID,
};

Expand Down

0 comments on commit 0275692

Please sign in to comment.