From 2af05150f9f3e4746e5871e0caa221990b1615c5 Mon Sep 17 00:00:00 2001
From: nkdengineer <nkdengineer@outlook.com>
Date: Tue, 22 Oct 2024 01:23:28 +0700
Subject: [PATCH 1/9] update getSubmitToAccountID with category and tag
 approver

---
 src/libs/Permissions.ts |  1 +
 src/libs/PolicyUtils.ts | 32 ++++++++++++++++++++++++++++----
 src/libs/ReportUtils.ts | 11 ++++++-----
 src/libs/actions/IOU.ts |  9 ++++-----
 4 files changed, 39 insertions(+), 14 deletions(-)

diff --git a/src/libs/Permissions.ts b/src/libs/Permissions.ts
index 24de2e612208..47fa860af63c 100644
--- a/src/libs/Permissions.ts
+++ b/src/libs/Permissions.ts
@@ -4,6 +4,7 @@ import type {IOUType} from '@src/CONST';
 import type Beta from '@src/types/onyx/Beta';
 
 function canUseAllBetas(betas: OnyxEntry<Beta[]>): boolean {
+    return true;
     return !!betas?.includes(CONST.BETAS.ALL);
 }
 
diff --git a/src/libs/PolicyUtils.ts b/src/libs/PolicyUtils.ts
index 3e0ae7cba291..5f703bc27879 100644
--- a/src/libs/PolicyUtils.ts
+++ b/src/libs/PolicyUtils.ts
@@ -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 {
@@ -31,10 +31,13 @@ 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 {getTransactionDetails} from './ReportUtils';
+import {getAllReportTransactions} from './TransactionUtils';
 
 type MemberEmailsToAccountIDs = Record<string, number>;
 
@@ -514,10 +517,31 @@ function getDefaultApprover(policy: OnyxEntry<Policy>): string {
 /**
  * Returns the accountID to whom the given employeeAccountID 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));
+
+    for (let i = 0; i < allTransactions.length; i++) {
+        const {tag = '', category = ''} = getTransactionDetails(allTransactions.at(i)) ?? {};
+        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;
@@ -531,8 +555,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) ?? '';
 }
 
diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts
index 1ef60b626ac7..306ef147cbd9 100644
--- a/src/libs/ReportUtils.ts
+++ b/src/libs/ReportUtils.ts
@@ -3081,7 +3081,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;
     }
 
@@ -4591,7 +4591,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;
     }
@@ -7875,7 +7875,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);
 }
@@ -8256,15 +8256,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);
diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts
index 0d9a4887bd1d..94e29948e266 100644
--- a/src/libs/actions/IOU.ts
+++ b/src/libs/actions/IOU.ts
@@ -7080,10 +7080,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;
@@ -7111,7 +7110,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;
@@ -7472,7 +7471,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,
     };
 

From c93c19da35dd88874dfb14aef9d795f811c6747a Mon Sep 17 00:00:00 2001
From: nkdengineer <nkdengineer@outlook.com>
Date: Tue, 22 Oct 2024 01:38:11 +0700
Subject: [PATCH 2/9] revert change

---
 src/libs/Permissions.ts | 1 -
 1 file changed, 1 deletion(-)

diff --git a/src/libs/Permissions.ts b/src/libs/Permissions.ts
index 47fa860af63c..24de2e612208 100644
--- a/src/libs/Permissions.ts
+++ b/src/libs/Permissions.ts
@@ -4,7 +4,6 @@ import type {IOUType} from '@src/CONST';
 import type Beta from '@src/types/onyx/Beta';
 
 function canUseAllBetas(betas: OnyxEntry<Beta[]>): boolean {
-    return true;
     return !!betas?.includes(CONST.BETAS.ALL);
 }
 

From 7d36a2bdfe2ec300a2a3f2ef2e4714a2b0bae97b Mon Sep 17 00:00:00 2001
From: nkdengineer <nkdengineer@outlook.com>
Date: Tue, 22 Oct 2024 02:02:31 +0700
Subject: [PATCH 3/9] fix lint

---
 ios/Podfile.lock        | 31 +++++++++----------------------
 src/libs/PolicyUtils.ts |  7 ++++---
 2 files changed, 13 insertions(+), 25 deletions(-)

diff --git a/ios/Podfile.lock b/ios/Podfile.lock
index 1242ab7a5a39..68266651661a 100644
--- a/ios/Podfile.lock
+++ b/ios/Podfile.lock
@@ -1606,29 +1606,16 @@ PODS:
     - ReactCommon/turbomodule/bridging
     - ReactCommon/turbomodule/core
     - Yoga
-  - react-native-config (1.5.3):
-    - react-native-config/App (= 1.5.3)
-  - react-native-config/App (1.5.3):
-    - DoubleConversion
-    - glog
-    - hermes-engine
-    - RCT-Folly (= 2024.01.01.00)
+  - react-native-config (1.5.0):
+    - react-native-config/App (= 1.5.0)
+  - react-native-config/App (1.5.0):
+    - RCT-Folly
     - RCTRequired
     - RCTTypeSafety
-    - React-Core
-    - React-debug
-    - React-Fabric
-    - React-featureflags
-    - React-graphics
-    - React-ImageManager
-    - React-NativeModulesApple
+    - React
+    - React-Codegen
     - React-RCTFabric
-    - React-rendererdebug
-    - React-utils
-    - ReactCodegen
-    - ReactCommon/turbomodule/bridging
     - ReactCommon/turbomodule/core
-    - Yoga
   - react-native-document-picker (9.3.1):
     - DoubleConversion
     - glog
@@ -1700,7 +1687,7 @@ PODS:
     - ReactCommon/turbomodule/bridging
     - ReactCommon/turbomodule/core
     - Yoga
-  - react-native-keyboard-controller (1.14.1):
+  - react-native-keyboard-controller (1.14.0):
     - DoubleConversion
     - glog
     - hermes-engine
@@ -3186,12 +3173,12 @@ SPEC CHECKSUMS:
   react-native-airship: e10f6823d8da49bbcb2db4bdb16ff954188afccc
   react-native-blob-util: 221c61c98ae507b758472ac4d2d489119d1a6c44
   react-native-cameraroll: 478a0c1fcdd39f08f6ac272b7ed06e92b2c7c129
-  react-native-config: 742a9e0a378a78d0eaff1fb3477d8c0ae222eb51
+  react-native-config: 5ce986133b07fc258828b20b9506de0e683efc1c
   react-native-document-picker: e9d83c149bdd72dc01cf8dcb8df0389c6bd5fddb
   react-native-geolocation: b9bd12beaf0ebca61a01514517ca8455bd26fa06
   react-native-image-picker: f8a13ff106bcc7eb00c71ce11fdc36aac2a44440
   react-native-key-command: aae312752fcdfaa2240be9a015fc41ce54087546
-  react-native-keyboard-controller: 902c07f41a415b632583b384427a71770a8b02a3
+  react-native-keyboard-controller: 17d5830f2bd6c6cad44682eb2cc13f9078eff985
   react-native-launch-arguments: 5f41e0abf88a15e3c5309b8875d6fd5ac43df49d
   react-native-netinfo: fb5112b1fa754975485884ae85a3fb6a684f49d5
   react-native-pager-view: 94195f1bf32e7f78359fa20057c97e632364a08b
diff --git a/src/libs/PolicyUtils.ts b/src/libs/PolicyUtils.ts
index 5f703bc27879..e49fc2ae41d4 100644
--- a/src/libs/PolicyUtils.ts
+++ b/src/libs/PolicyUtils.ts
@@ -36,8 +36,7 @@ import * as Localize from './Localize';
 import Navigation from './Navigation/Navigation';
 import * as NetworkStore from './Network/NetworkStore';
 import {getAccountIDsByLogins, getLoginsByAccountIDs, getPersonalDetailByEmail} from './PersonalDetailsUtils';
-import {getTransactionDetails} from './ReportUtils';
-import {getAllReportTransactions} from './TransactionUtils';
+import {getAllReportTransactions, getCategory, getTag} from './TransactionUtils';
 
 type MemberEmailsToAccountIDs = Record<string, number>;
 
@@ -527,7 +526,9 @@ function getSubmitToAccountID(policy: OnyxEntry<Policy>, expenseReport: OnyxEntr
     const allTransactions = getAllReportTransactions(expenseReport?.reportID).sort((transA, transB) => (transA.created < transB.created ? -1 : 1));
 
     for (let i = 0; i < allTransactions.length; i++) {
-        const {tag = '', category = ''} = getTransactionDetails(allTransactions.at(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;

From 3682b9f4fd89b755fcb9a9383f0bbd62b2014bc9 Mon Sep 17 00:00:00 2001
From: nkdengineer <nkdengineer@outlook.com>
Date: Tue, 22 Oct 2024 02:04:04 +0700
Subject: [PATCH 4/9] revert pod file

---
 ios/Podfile.lock | 31 ++++++++++++++++++++++---------
 1 file changed, 22 insertions(+), 9 deletions(-)

diff --git a/ios/Podfile.lock b/ios/Podfile.lock
index 68266651661a..1242ab7a5a39 100644
--- a/ios/Podfile.lock
+++ b/ios/Podfile.lock
@@ -1606,16 +1606,29 @@ PODS:
     - ReactCommon/turbomodule/bridging
     - ReactCommon/turbomodule/core
     - Yoga
-  - react-native-config (1.5.0):
-    - react-native-config/App (= 1.5.0)
-  - react-native-config/App (1.5.0):
-    - RCT-Folly
+  - react-native-config (1.5.3):
+    - react-native-config/App (= 1.5.3)
+  - react-native-config/App (1.5.3):
+    - DoubleConversion
+    - glog
+    - hermes-engine
+    - RCT-Folly (= 2024.01.01.00)
     - RCTRequired
     - RCTTypeSafety
-    - React
-    - React-Codegen
+    - React-Core
+    - React-debug
+    - React-Fabric
+    - React-featureflags
+    - React-graphics
+    - React-ImageManager
+    - React-NativeModulesApple
     - React-RCTFabric
+    - React-rendererdebug
+    - React-utils
+    - ReactCodegen
+    - ReactCommon/turbomodule/bridging
     - ReactCommon/turbomodule/core
+    - Yoga
   - react-native-document-picker (9.3.1):
     - DoubleConversion
     - glog
@@ -1687,7 +1700,7 @@ PODS:
     - ReactCommon/turbomodule/bridging
     - ReactCommon/turbomodule/core
     - Yoga
-  - react-native-keyboard-controller (1.14.0):
+  - react-native-keyboard-controller (1.14.1):
     - DoubleConversion
     - glog
     - hermes-engine
@@ -3173,12 +3186,12 @@ SPEC CHECKSUMS:
   react-native-airship: e10f6823d8da49bbcb2db4bdb16ff954188afccc
   react-native-blob-util: 221c61c98ae507b758472ac4d2d489119d1a6c44
   react-native-cameraroll: 478a0c1fcdd39f08f6ac272b7ed06e92b2c7c129
-  react-native-config: 5ce986133b07fc258828b20b9506de0e683efc1c
+  react-native-config: 742a9e0a378a78d0eaff1fb3477d8c0ae222eb51
   react-native-document-picker: e9d83c149bdd72dc01cf8dcb8df0389c6bd5fddb
   react-native-geolocation: b9bd12beaf0ebca61a01514517ca8455bd26fa06
   react-native-image-picker: f8a13ff106bcc7eb00c71ce11fdc36aac2a44440
   react-native-key-command: aae312752fcdfaa2240be9a015fc41ce54087546
-  react-native-keyboard-controller: 17d5830f2bd6c6cad44682eb2cc13f9078eff985
+  react-native-keyboard-controller: 902c07f41a415b632583b384427a71770a8b02a3
   react-native-launch-arguments: 5f41e0abf88a15e3c5309b8875d6fd5ac43df49d
   react-native-netinfo: fb5112b1fa754975485884ae85a3fb6a684f49d5
   react-native-pager-view: 94195f1bf32e7f78359fa20057c97e632364a08b

From b75c05605b0db964dba8a8b3f88e83ca854aa190 Mon Sep 17 00:00:00 2001
From: nkdengineer <161821005+nkdengineer@users.noreply.github.com>
Date: Fri, 25 Oct 2024 02:17:26 +0700
Subject: [PATCH 5/9] Update src/libs/PolicyUtils.ts

Co-authored-by: Alex Beaman <dabeamanator@gmail.com>
---
 src/libs/PolicyUtils.ts | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/src/libs/PolicyUtils.ts b/src/libs/PolicyUtils.ts
index 1547c4ddda02..35fb0d6b69eb 100644
--- a/src/libs/PolicyUtils.ts
+++ b/src/libs/PolicyUtils.ts
@@ -531,6 +531,8 @@ function getSubmitToAccountID(policy: OnyxEntry<Policy>, expenseReport: OnyxEntr
     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);

From de7e6a67957292ffbc1171c65c347b572c6a10f1 Mon Sep 17 00:00:00 2001
From: nkdengineer <nkdengineer@outlook.com>
Date: Fri, 25 Oct 2024 02:24:36 +0700
Subject: [PATCH 6/9] update comment

---
 src/libs/PolicyUtils.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/libs/PolicyUtils.ts b/src/libs/PolicyUtils.ts
index 35fb0d6b69eb..2e7efd6afc71 100644
--- a/src/libs/PolicyUtils.ts
+++ b/src/libs/PolicyUtils.ts
@@ -520,7 +520,7 @@ 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>, expenseReport: OnyxEntry<Report>): number {
     const employeeAccountID = expenseReport?.ownerAccountID ?? -1;

From 4dbb1a42c10838a35c649bbff05fe71e393ddec3 Mon Sep 17 00:00:00 2001
From: nkdengineer <nkdengineer@outlook.com>
Date: Sat, 26 Oct 2024 00:33:29 +0700
Subject: [PATCH 7/9] fix test

---
 src/libs/PolicyUtils.ts | 41 ++++++++++++++++++++++-------------------
 1 file changed, 22 insertions(+), 19 deletions(-)

diff --git a/src/libs/PolicyUtils.ts b/src/libs/PolicyUtils.ts
index 2e7efd6afc71..d353ebcd1df4 100644
--- a/src/libs/PolicyUtils.ts
+++ b/src/libs/PolicyUtils.ts
@@ -37,6 +37,7 @@ import Navigation from './Navigation/Navigation';
 import * as NetworkStore from './Network/NetworkStore';
 import {getAccountIDsByLogins, getLoginsByAccountIDs, getPersonalDetailByEmail} from './PersonalDetailsUtils';
 import {getAllReportTransactions, getCategory, getTag} from './TransactionUtils';
+import { isExpenseReport } from './ReportUtils';
 
 type MemberEmailsToAccountIDs = Record<string, number>;
 
@@ -527,28 +528,30 @@ function getSubmitToAccountID(policy: OnyxEntry<Policy>, expenseReport: OnyxEntr
     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 (isExpenseReport(expenseReport)) {
+        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 && getTagApproverRule(policy?.id ?? '-1', tag)?.approver) {
+                tagApprover = getTagApproverRule(policy?.id ?? '-1', tag)?.approver;
+            }
         }
-    }
 
-    if (tagApprover) {
-        return getAccountIDsByLogins([tagApprover]).at(0) ?? -1;
+        if (tagApprover) {
+            return getAccountIDsByLogins([tagApprover]).at(0) ?? -1;
+        }
     }
 
     // For policy using the optional or basic workflow, the manager is the policy default approver.

From 9a789f34db26e28bdf4eb81d445e37a44c27f074 Mon Sep 17 00:00:00 2001
From: nkdengineer <nkdengineer@outlook.com>
Date: Sat, 26 Oct 2024 00:45:20 +0700
Subject: [PATCH 8/9] fix test

---
 src/libs/PolicyUtils.ts | 2 +-
 src/libs/ReportUtils.ts | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/libs/PolicyUtils.ts b/src/libs/PolicyUtils.ts
index d353ebcd1df4..96ca8c503eca 100644
--- a/src/libs/PolicyUtils.ts
+++ b/src/libs/PolicyUtils.ts
@@ -36,8 +36,8 @@ import * as Localize from './Localize';
 import Navigation from './Navigation/Navigation';
 import * as NetworkStore from './Network/NetworkStore';
 import {getAccountIDsByLogins, getLoginsByAccountIDs, getPersonalDetailByEmail} from './PersonalDetailsUtils';
+import {isExpenseReport} from './ReportUtils';
 import {getAllReportTransactions, getCategory, getTag} from './TransactionUtils';
-import { isExpenseReport } from './ReportUtils';
 
 type MemberEmailsToAccountIDs = Record<string, number>;
 
diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts
index 2a8546a2d915..298ac5721217 100644
--- a/src/libs/ReportUtils.ts
+++ b/src/libs/ReportUtils.ts
@@ -1295,7 +1295,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;
 }

From 62c0db1b506e4819600e40a0d7bdf1c4faab4021 Mon Sep 17 00:00:00 2001
From: nkdengineer <nkdengineer@outlook.com>
Date: Sat, 26 Oct 2024 00:47:56 +0700
Subject: [PATCH 9/9] fix cycle dependen

---
 src/libs/PolicyUtils.ts | 41 +++++++++++++++++++----------------------
 1 file changed, 19 insertions(+), 22 deletions(-)

diff --git a/src/libs/PolicyUtils.ts b/src/libs/PolicyUtils.ts
index 96ca8c503eca..2e7efd6afc71 100644
--- a/src/libs/PolicyUtils.ts
+++ b/src/libs/PolicyUtils.ts
@@ -36,7 +36,6 @@ import * as Localize from './Localize';
 import Navigation from './Navigation/Navigation';
 import * as NetworkStore from './Network/NetworkStore';
 import {getAccountIDsByLogins, getLoginsByAccountIDs, getPersonalDetailByEmail} from './PersonalDetailsUtils';
-import {isExpenseReport} from './ReportUtils';
 import {getAllReportTransactions, getCategory, getTag} from './TransactionUtils';
 
 type MemberEmailsToAccountIDs = Record<string, number>;
@@ -528,32 +527,30 @@ function getSubmitToAccountID(policy: OnyxEntry<Policy>, expenseReport: OnyxEntr
     const employeeLogin = getLoginsByAccountIDs([employeeAccountID]).at(0) ?? '';
     const defaultApprover = getDefaultApprover(policy);
 
-    if (isExpenseReport(expenseReport)) {
-        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;
-            }
+    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) {
-            return getAccountIDsByLogins([tagApprover]).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;
