@zeniai/client-epic-state 5.0.80 → 5.0.81-beta0ND

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (222) hide show
  1. package/lib/commonStateTypes/cacheOverrideFetchReducer.d.ts +17 -0
  2. package/lib/commonStateTypes/cacheOverrideFetchReducer.js +20 -0
  3. package/lib/commonStateTypes/filterPrimitives.d.ts +23 -0
  4. package/lib/commonStateTypes/filterPrimitives.js +21 -0
  5. package/lib/commonStateTypes/pusherActions.d.ts +11 -0
  6. package/lib/commonStateTypes/pusherActions.js +6 -0
  7. package/lib/coreEpics.js +1 -1
  8. package/lib/entity/account/accountSelector.d.ts +27 -1
  9. package/lib/entity/account/accountSelector.js +47 -1
  10. package/lib/entity/account/accountState.d.ts +6 -1
  11. package/lib/entity/account/accountState.js +11 -4
  12. package/lib/entity/account/subAccountSelector.d.ts +1 -1
  13. package/lib/entity/aiAccountantCustomer/aiAccountantCustomerPayload.js +1 -3
  14. package/lib/entity/aiAccountantCustomer/aiAccountantCustomerReducer.js +1 -2
  15. package/lib/entity/aiAccountantCustomer/aiAccountantCustomerSelector.d.ts +1 -1
  16. package/lib/entity/chargeCard/chargeCardReducer.d.ts +19 -2
  17. package/lib/entity/chargeCard/chargeCardReducer.js +35 -2
  18. package/lib/entity/chargeCardTransaction/chargeCardTransactionReducer.d.ts +14 -1
  19. package/lib/entity/chargeCardTransaction/chargeCardTransactionReducer.js +36 -2
  20. package/lib/entity/class/classSelector.d.ts +35 -0
  21. package/lib/entity/class/classSelector.js +50 -0
  22. package/lib/entity/paymentAccount/paymentAccountSelector.d.ts +1 -0
  23. package/lib/entity/paymentAccount/paymentAccountSelector.js +4 -0
  24. package/lib/entity/sectionProjectView/sectionProjectViewReducer.d.ts +1 -1
  25. package/lib/entity/sectionProjectView/sectionProjectViewSelector.js +1 -1
  26. package/lib/entity/tenant/SessionManager.js +6 -3
  27. package/lib/entity/tenant/clearAllEpic.d.ts +2 -2
  28. package/lib/entity/tenant/clearAllEpic.js +2 -2
  29. package/lib/entity/tenant/epic/deleteConnectionEpic.d.ts +1 -1
  30. package/lib/entity/tenant/epic/initEmailConnectOAuthEpic.d.ts +21 -0
  31. package/lib/entity/tenant/epic/initEmailConnectOAuthEpic.js +54 -0
  32. package/lib/entity/tenant/epic/saveAPIKeyConnectionEpic.d.ts +1 -1
  33. package/lib/entity/tenant/epic/saveAPIKeyConnectionEpic.js +6 -2
  34. package/lib/entity/tenant/epic/saveConnectorCredentialsEpic.d.ts +3 -3
  35. package/lib/entity/tenant/epic/saveOAuthConnectionEpic.d.ts +1 -1
  36. package/lib/entity/tenant/epic/saveOAuthConnectionEpic.js +6 -2
  37. package/lib/entity/tenant/tenantPayload.d.ts +1 -0
  38. package/lib/entity/tenant/tenantReducer.d.ts +38 -25
  39. package/lib/entity/tenant/tenantReducer.js +71 -6
  40. package/lib/entity/tenant/tenantState.d.ts +1 -0
  41. package/lib/entity/transaction/payloadTypes/transactionLinePayload.js +3 -1
  42. package/lib/epic.d.ts +13 -11
  43. package/lib/epic.js +15 -12
  44. package/lib/esm/commonStateTypes/cacheOverrideFetchReducer.js +16 -0
  45. package/lib/esm/commonStateTypes/filterPrimitives.js +20 -0
  46. package/lib/esm/commonStateTypes/pusherActions.js +3 -0
  47. package/lib/esm/coreEpics.js +1 -1
  48. package/lib/esm/entity/account/accountSelector.js +46 -1
  49. package/lib/esm/entity/account/accountState.js +7 -1
  50. package/lib/esm/entity/aiAccountantCustomer/aiAccountantCustomerPayload.js +1 -3
  51. package/lib/esm/entity/aiAccountantCustomer/aiAccountantCustomerReducer.js +1 -2
  52. package/lib/esm/entity/chargeCard/chargeCardReducer.js +34 -1
  53. package/lib/esm/entity/chargeCardTransaction/chargeCardTransactionReducer.js +35 -1
  54. package/lib/esm/entity/class/classSelector.js +48 -0
  55. package/lib/esm/entity/paymentAccount/paymentAccountSelector.js +3 -0
  56. package/lib/esm/entity/sectionProjectView/sectionProjectViewSelector.js +1 -1
  57. package/lib/esm/entity/tenant/SessionManager.js +4 -1
  58. package/lib/esm/entity/tenant/clearAllEpic.js +2 -2
  59. package/lib/esm/entity/tenant/epic/initEmailConnectOAuthEpic.js +50 -0
  60. package/lib/esm/entity/tenant/epic/saveAPIKeyConnectionEpic.js +6 -2
  61. package/lib/esm/entity/tenant/epic/saveOAuthConnectionEpic.js +6 -2
  62. package/lib/esm/entity/tenant/tenantReducer.js +69 -4
  63. package/lib/esm/entity/transaction/payloadTypes/transactionLinePayload.js +3 -1
  64. package/lib/esm/epic.js +15 -12
  65. package/lib/esm/index.js +38 -25
  66. package/lib/esm/reducer.js +2 -2
  67. package/lib/esm/view/aiAccountantView/epics/fetchAiAccountantCustomersEpic.js +2 -5
  68. package/lib/esm/view/aiAccountantView/epics/fetchAiAccountantJobsEpic.js +2 -5
  69. package/lib/esm/view/aiAccountantView/epics/triggerAiAccountantJobEpic.js +4 -1
  70. package/lib/esm/view/companyHealthMetricView/companyHealthMetricViewSelector.js +1 -1
  71. package/lib/esm/view/companyTaskManagerView/companyTaskManagerViewSelector.js +1 -1
  72. package/lib/esm/view/companyTaskManagerView/epics/fetchTaskManagerMetricsEpic.js +1 -1
  73. package/lib/esm/view/companyView/helpers/cockpitHelpers.js +1 -1
  74. package/lib/esm/view/companyView/selector/companyManagementViewSelector.js +1 -1
  75. package/lib/esm/view/companyView/selector/companyPortfolioViewSelector.js +1 -1
  76. package/lib/esm/view/creditAgentView/creditAgentViewReducer.js +1 -16
  77. package/lib/esm/view/expenseAutomationView/epics/missingReceipts/fetchBulkUploadBatchesEpic.js +2 -2
  78. package/lib/esm/view/expenseAutomationView/epics/missingReceipts/fetchCompletedTransactionsEpic.js +2 -1
  79. package/lib/esm/view/expenseAutomationView/epics/missingReceipts/restoreBulkUploadAutomatchingOnMountEpic.js +8 -6
  80. package/lib/esm/view/expenseAutomationView/epics/missingReceipts/searchTransactionsForManualMatchEpic.js +7 -1
  81. package/lib/esm/view/expenseAutomationView/epics/transactionCategorization/fetchTransactionCategorizationEpic.js +11 -1
  82. package/lib/esm/view/expenseAutomationView/helpers/transactionCategorizationLocalDataHelper.js +21 -1
  83. package/lib/esm/view/expenseAutomationView/reducers/missingReceiptsViewReducer.js +2 -1
  84. package/lib/esm/view/expenseAutomationView/reducers/transactionsViewReducer.js +253 -14
  85. package/lib/esm/view/expenseAutomationView/selectors/transactionCategorizationSelector.js +147 -13
  86. package/lib/esm/view/expenseAutomationView/transactionFilterHelpers.js +258 -0
  87. package/lib/esm/view/expenseAutomationView/types/completedSubTab.js +17 -0
  88. package/lib/esm/view/expenseAutomationView/types/missingReceiptsViewState.js +0 -2
  89. package/lib/esm/view/financeStatement/financeStatementSelector.js +1 -1
  90. package/lib/esm/view/onboardingView/customerView/onboardingCustomerViewReducer.js +61 -50
  91. package/lib/esm/view/recommendation/fetchEntityRecommendationsForLineUpdateEpic.js +87 -0
  92. package/lib/esm/view/recommendation/recommendationHelper.js +29 -0
  93. package/lib/esm/view/recommendation/recommendationReducer.js +26 -1
  94. package/lib/esm/view/spendManagement/chargeCards/cashbackDetail/cashbackDetailReducer.js +3 -2
  95. package/lib/esm/view/spendManagement/chargeCards/chargeCardConfig/chargeCardConfigReducer.js +3 -2
  96. package/lib/esm/view/spendManagement/chargeCards/chargeCardDetail/chargeCardDetailReducer.js +55 -18
  97. package/lib/esm/view/spendManagement/chargeCards/chargeCardDetail/fetchChargeCardDetailEpic.js +16 -2
  98. package/lib/esm/view/spendManagement/chargeCards/chargeCardDetail/fetchChargeCardDetailPageEpic.js +5 -4
  99. package/lib/esm/view/spendManagement/chargeCards/chargeCardList/chargeCardListReducer.js +26 -9
  100. package/lib/esm/view/spendManagement/chargeCards/chargeCardList/fetchChargeCardListPageEpic.js +9 -6
  101. package/lib/esm/view/spendManagement/chargeCards/chargeCardPaymentHistory/chargeCardPaymentHistoryReducer.js +3 -2
  102. package/lib/esm/view/spendManagement/chargeCards/chargeCardPaymentHistory/fetchChargeCardPaymentPageEpic.js +1 -1
  103. package/lib/esm/view/spendManagement/chargeCards/chargeCardRepaymentDetail/chargeCardRepaymentDetailReducer.js +7 -3
  104. package/lib/esm/view/spendManagement/commonSetup/epic/setup/parseUploadedKybDocumentEpic.js +49 -0
  105. package/lib/esm/view/spendManagement/commonSetup/epic/setup/parseUploadedKycDocumentEpic.js +58 -0
  106. package/lib/esm/view/spendManagement/commonSetup/kycKybAutofillActions.js +12 -0
  107. package/lib/esm/view/spendManagement/commonSetup/kycKybParseMapper.js +205 -0
  108. package/lib/esm/view/spendManagement/commonSetup/setupViewReducer.js +71 -52
  109. package/lib/esm/view/spendManagement/commonSetup/setupViewSelector.js +2 -0
  110. package/lib/esm/view/spendManagement/commonSetup/types/kycKybAutofill.js +28 -0
  111. package/lib/esm/view/spendManagement/spendManagementFilterHelpers.js +4 -2
  112. package/lib/esm/view/subscriptionView/subscriptionViewSelector.js +1 -1
  113. package/lib/esm/view/taskManager/cannedResponsesView/epics/fetchCannedResponsesEpic.js +1 -1
  114. package/lib/esm/view/taskManager/cannedResponsesView/epics/saveCannedResponseEpic.js +1 -1
  115. package/lib/esm/view/taskManager/taskDetailView/epics/unsnoozeTaskEpic.js +1 -1
  116. package/lib/esm/view/taskManager/taskListView/taskListReducer.js +1 -1
  117. package/lib/esm/view/transactionDetail/transactionDetailReducer.js +121 -6
  118. package/lib/esm/view/transactionDetail/transactionDetailSelector.js +29 -1
  119. package/lib/index.d.ts +39 -28
  120. package/lib/index.js +97 -60
  121. package/lib/reducer.d.ts +2 -2
  122. package/lib/reducer.js +2 -2
  123. package/lib/view/aiAccountantView/aiAccountantViewPayload.d.ts +2 -2
  124. package/lib/view/aiAccountantView/aiAccountantViewReducer.d.ts +1 -1
  125. package/lib/view/aiAccountantView/epics/fetchAiAccountantCustomersEpic.js +2 -5
  126. package/lib/view/aiAccountantView/epics/fetchAiAccountantJobsEpic.js +2 -5
  127. package/lib/view/aiAccountantView/epics/triggerAiAccountantJobEpic.js +4 -1
  128. package/lib/view/companyHealthMetricView/companyHealthMetricViewSelector.js +1 -1
  129. package/lib/view/companyTaskManagerView/companyTaskManagerViewSelector.d.ts +1 -1
  130. package/lib/view/companyTaskManagerView/companyTaskManagerViewSelector.js +1 -1
  131. package/lib/view/companyView/helpers/cockpitHelpers.js +1 -1
  132. package/lib/view/companyView/selector/companyManagementViewSelector.js +1 -1
  133. package/lib/view/companyView/selector/companyPortfolioViewSelector.js +1 -1
  134. package/lib/view/companyView/types/cockpitTypes.d.ts +1 -1
  135. package/lib/view/creditAgentView/creditAgentViewPayload.d.ts +0 -3
  136. package/lib/view/creditAgentView/creditAgentViewReducer.d.ts +1 -1
  137. package/lib/view/creditAgentView/creditAgentViewReducer.js +2 -17
  138. package/lib/view/creditAgentView/creditAgentViewState.d.ts +0 -5
  139. package/lib/view/expenseAutomationView/epics/missingReceipts/fetchBulkUploadBatchesEpic.js +2 -2
  140. package/lib/view/expenseAutomationView/epics/missingReceipts/fetchCompletedTransactionsEpic.js +2 -1
  141. package/lib/view/expenseAutomationView/epics/missingReceipts/restoreBulkUploadAutomatchingOnMountEpic.js +8 -6
  142. package/lib/view/expenseAutomationView/epics/missingReceipts/searchTransactionsForManualMatchEpic.js +7 -1
  143. package/lib/view/expenseAutomationView/epics/transactionCategorization/fetchTransactionCategorizationEpic.d.ts +2 -2
  144. package/lib/view/expenseAutomationView/epics/transactionCategorization/fetchTransactionCategorizationEpic.js +10 -0
  145. package/lib/view/expenseAutomationView/helpers/transactionCategorizationLocalDataHelper.d.ts +2 -1
  146. package/lib/view/expenseAutomationView/helpers/transactionCategorizationLocalDataHelper.js +23 -2
  147. package/lib/view/expenseAutomationView/payload/transactionCategorizationPayload.d.ts +21 -0
  148. package/lib/view/expenseAutomationView/reducers/missingReceiptsViewReducer.d.ts +2 -1
  149. package/lib/view/expenseAutomationView/reducers/missingReceiptsViewReducer.js +2 -1
  150. package/lib/view/expenseAutomationView/reducers/transactionsViewReducer.d.ts +28 -5
  151. package/lib/view/expenseAutomationView/reducers/transactionsViewReducer.js +253 -14
  152. package/lib/view/expenseAutomationView/selectorTypes/missingReceiptsSelectorTypes.d.ts +2 -1
  153. package/lib/view/expenseAutomationView/selectorTypes/transactionsViewSelectorTypes.d.ts +16 -0
  154. package/lib/view/expenseAutomationView/selectors/transactionCategorizationSelector.d.ts +7 -1
  155. package/lib/view/expenseAutomationView/selectors/transactionCategorizationSelector.js +150 -13
  156. package/lib/view/expenseAutomationView/transactionFilterHelpers.d.ts +66 -0
  157. package/lib/view/expenseAutomationView/transactionFilterHelpers.js +262 -0
  158. package/lib/view/expenseAutomationView/types/completedSubTab.d.ts +10 -0
  159. package/lib/view/expenseAutomationView/types/completedSubTab.js +21 -0
  160. package/lib/view/expenseAutomationView/types/missingReceiptsViewState.d.ts +1 -2
  161. package/lib/view/expenseAutomationView/types/missingReceiptsViewState.js +1 -4
  162. package/lib/view/expenseAutomationView/types/transactionsViewState.d.ts +44 -2
  163. package/lib/view/financeStatement/financeStatementSelector.js +1 -1
  164. package/lib/view/onboardingView/customerView/onboardingCustomerViewReducer.d.ts +25 -2
  165. package/lib/view/onboardingView/customerView/onboardingCustomerViewReducer.js +62 -51
  166. package/lib/view/onboardingView/customerView/onboardingCustomerViewState.d.ts +10 -0
  167. package/lib/view/profitAndLossProjectView/profitAndLossProjectViewSelectorTypes.d.ts +1 -1
  168. package/lib/view/recommendation/fetchEntityRecommendationsForLineUpdateEpic.d.ts +9 -0
  169. package/lib/view/recommendation/fetchEntityRecommendationsForLineUpdateEpic.js +91 -0
  170. package/lib/view/recommendation/recommendationHelper.d.ts +9 -0
  171. package/lib/view/recommendation/recommendationHelper.js +31 -1
  172. package/lib/view/recommendation/recommendationReducer.d.ts +20 -1
  173. package/lib/view/recommendation/recommendationReducer.js +27 -2
  174. package/lib/view/spendManagement/billPay/billList/billListState.d.ts +1 -1
  175. package/lib/view/spendManagement/chargeCards/cashbackDetail/cashbackDetailReducer.d.ts +3 -1
  176. package/lib/view/spendManagement/chargeCards/cashbackDetail/cashbackDetailReducer.js +3 -2
  177. package/lib/view/spendManagement/chargeCards/chargeCardConfig/chargeCardConfigReducer.d.ts +3 -1
  178. package/lib/view/spendManagement/chargeCards/chargeCardConfig/chargeCardConfigReducer.js +3 -2
  179. package/lib/view/spendManagement/chargeCards/chargeCardDetail/chargeCardDetailReducer.js +55 -18
  180. package/lib/view/spendManagement/chargeCards/chargeCardDetail/fetchChargeCardDetailEpic.js +16 -2
  181. package/lib/view/spendManagement/chargeCards/chargeCardDetail/fetchChargeCardDetailPageEpic.js +5 -4
  182. package/lib/view/spendManagement/chargeCards/chargeCardList/chargeCardListReducer.d.ts +18 -3
  183. package/lib/view/spendManagement/chargeCards/chargeCardList/chargeCardListReducer.js +27 -10
  184. package/lib/view/spendManagement/chargeCards/chargeCardList/fetchChargeCardListPageEpic.js +9 -6
  185. package/lib/view/spendManagement/chargeCards/chargeCardPaymentHistory/chargeCardPaymentHistory.d.ts +1 -1
  186. package/lib/view/spendManagement/chargeCards/chargeCardPaymentHistory/chargeCardPaymentHistoryReducer.d.ts +3 -1
  187. package/lib/view/spendManagement/chargeCards/chargeCardPaymentHistory/chargeCardPaymentHistoryReducer.js +3 -2
  188. package/lib/view/spendManagement/chargeCards/chargeCardPaymentHistory/fetchChargeCardPaymentPageEpic.js +1 -1
  189. package/lib/view/spendManagement/chargeCards/chargeCardRepaymentDetail/chargeCardRepaymentDetailReducer.d.ts +3 -1
  190. package/lib/view/spendManagement/chargeCards/chargeCardRepaymentDetail/chargeCardRepaymentDetailReducer.js +7 -3
  191. package/lib/view/spendManagement/commonSetup/epic/setup/parseUploadedKybDocumentEpic.d.ts +8 -0
  192. package/lib/view/spendManagement/commonSetup/epic/setup/parseUploadedKybDocumentEpic.js +53 -0
  193. package/lib/view/spendManagement/commonSetup/epic/setup/parseUploadedKycDocumentEpic.d.ts +8 -0
  194. package/lib/view/spendManagement/commonSetup/epic/setup/parseUploadedKycDocumentEpic.js +62 -0
  195. package/lib/view/spendManagement/commonSetup/kycKybAutofillActions.d.ts +27 -0
  196. package/lib/view/spendManagement/commonSetup/kycKybAutofillActions.js +15 -0
  197. package/lib/view/spendManagement/commonSetup/kycKybParseMapper.d.ts +24 -0
  198. package/lib/view/spendManagement/commonSetup/kycKybParseMapper.js +213 -0
  199. package/lib/view/spendManagement/commonSetup/setupViewReducer.d.ts +24 -2
  200. package/lib/view/spendManagement/commonSetup/setupViewReducer.js +72 -53
  201. package/lib/view/spendManagement/commonSetup/setupViewSelector.d.ts +4 -0
  202. package/lib/view/spendManagement/commonSetup/setupViewSelector.js +2 -0
  203. package/lib/view/spendManagement/commonSetup/setupViewState.d.ts +12 -0
  204. package/lib/view/spendManagement/commonSetup/types/kycKybAutofill.d.ts +125 -0
  205. package/lib/view/spendManagement/commonSetup/types/kycKybAutofill.js +32 -0
  206. package/lib/view/spendManagement/reimbursement/remiListView/remiListState.d.ts +1 -1
  207. package/lib/view/spendManagement/spendManagementFilterHelpers.d.ts +12 -3
  208. package/lib/view/spendManagement/spendManagementFilterHelpers.js +4 -2
  209. package/lib/view/subscriptionView/subscriptionViewSelector.js +1 -1
  210. package/lib/view/taskManager/taskListView/taskList.d.ts +1 -1
  211. package/lib/view/taskManager/taskListView/taskListReducer.js +1 -1
  212. package/lib/view/transactionDetail/transactionDetailReducer.d.ts +11 -1
  213. package/lib/view/transactionDetail/transactionDetailReducer.js +122 -7
  214. package/lib/view/transactionDetail/transactionDetailSelector.d.ts +2 -0
  215. package/lib/view/transactionDetail/transactionDetailSelector.js +31 -2
  216. package/lib/view/transactionDetail/transactionDetailTypes.d.ts +1 -0
  217. package/lib/view/zeniOAuthView/zeniOAuthSelector.d.ts +1 -1
  218. package/package.json +10 -4
  219. package/lib/esm/view/creditAgentView/epics/fetchCreditAgentAccessEpic.js +0 -13
  220. package/lib/tsconfig.typecheck.tsbuildinfo +0 -1
  221. package/lib/view/creditAgentView/epics/fetchCreditAgentAccessEpic.d.ts +0 -11
  222. package/lib/view/creditAgentView/epics/fetchCreditAgentAccessEpic.js +0 -17
@@ -0,0 +1,205 @@
1
+ /**
2
+ * Pure mappers from ExtendAI parser payloads to our form-side local-data shapes.
3
+ *
4
+ * Each mapper filters out fields below KYC_KYB_AUTOFILL_MIN_CONFIDENCE and returns
5
+ * both the merged values and the list of field names that were autofilled so the
6
+ * UI can show the ✦ AI badge next to each.
7
+ */
8
+ import { date } from '../../../zeniDayJS';
9
+ import { toZeniUrl } from '../../../zeniUrl';
10
+ import { KYC_KYB_AUTOFILL_MIN_CONFIDENCE, } from './types/kycKybAutofill';
11
+ const emptyResult = () => ({
12
+ values: {},
13
+ autoFilledFieldNames: [],
14
+ });
15
+ const isConfident = (field) => {
16
+ if (field == null) {
17
+ return false;
18
+ }
19
+ if (field.value == null) {
20
+ return false;
21
+ }
22
+ return field.confidence >= KYC_KYB_AUTOFILL_MIN_CONFIDENCE;
23
+ };
24
+ const assignAutofilledValue = (result, key, value) => {
25
+ if (value == null) {
26
+ return;
27
+ }
28
+ if (typeof value === 'string' && value.trim() === '') {
29
+ return;
30
+ }
31
+ result.values[key] = value;
32
+ result.autoFilledFieldNames.push(key);
33
+ };
34
+ const tryParseDate = (rawDate) => {
35
+ const parsedDate = date(rawDate);
36
+ if (parsedDate.isValid()) {
37
+ return parsedDate;
38
+ }
39
+ return undefined;
40
+ };
41
+ const normalizeNationality = (rawNationality, countries) => {
42
+ const needle = rawNationality.trim().toLowerCase();
43
+ if (needle === '') {
44
+ return undefined;
45
+ }
46
+ // Exact ISO-2 / ISO-3 match.
47
+ const isoMatch = countries.find((country) => country.countryCode.toLowerCase() === needle);
48
+ if (isoMatch != null) {
49
+ return isoMatch.countryCode;
50
+ }
51
+ // Common nationality adjectives map to country names (e.g. "American" → "United States").
52
+ const adjectiveToCountry = {
53
+ american: 'united states',
54
+ british: 'united kingdom',
55
+ indian: 'india',
56
+ canadian: 'canada',
57
+ australian: 'australia',
58
+ german: 'germany',
59
+ french: 'france',
60
+ chinese: 'china',
61
+ japanese: 'japan',
62
+ mexican: 'mexico',
63
+ brazilian: 'brazil',
64
+ };
65
+ const resolvedName = adjectiveToCountry[needle] ?? needle;
66
+ const nameMatch = countries.find((country) => country.countryName.toLowerCase() === resolvedName);
67
+ return nameMatch?.countryCode;
68
+ };
69
+ const splitFullName = (fullName) => {
70
+ const parts = fullName.trim().split(/\s+/);
71
+ if (parts.length === 1) {
72
+ return { firstName: parts[0], lastName: '' };
73
+ }
74
+ const lastName = parts[parts.length - 1];
75
+ const firstName = parts.slice(0, -1).join(' ');
76
+ return { firstName, lastName };
77
+ };
78
+ /** Passport → Company Officer. */
79
+ export const mapPassportToOfficer = (parsed, allNationalityCountries) => {
80
+ const result = emptyResult();
81
+ if (isConfident(parsed.first_name)) {
82
+ assignAutofilledValue(result, 'firstName', parsed.first_name.value);
83
+ }
84
+ if (isConfident(parsed.last_name)) {
85
+ assignAutofilledValue(result, 'lastName', parsed.last_name.value);
86
+ }
87
+ if (isConfident(parsed.date_of_birth)) {
88
+ const parsedDate = tryParseDate(parsed.date_of_birth.value);
89
+ if (parsedDate != null) {
90
+ assignAutofilledValue(result, 'birthday', parsedDate);
91
+ }
92
+ }
93
+ if (isConfident(parsed.nationality)) {
94
+ const isoCode = normalizeNationality(parsed.nationality.value, allNationalityCountries);
95
+ if (isoCode != null) {
96
+ assignAutofilledValue(result, 'nationalityCountryCode', isoCode);
97
+ }
98
+ }
99
+ return result;
100
+ };
101
+ /** Driving License → Company Officer. */
102
+ export const mapDrivingLicenseToOfficer = (parsed) => {
103
+ const result = emptyResult();
104
+ if (isConfident(parsed.first_name)) {
105
+ assignAutofilledValue(result, 'firstName', parsed.first_name.value);
106
+ }
107
+ if (isConfident(parsed.last_name)) {
108
+ assignAutofilledValue(result, 'lastName', parsed.last_name.value);
109
+ }
110
+ if (isConfident(parsed.date_of_birth)) {
111
+ const parsedDate = tryParseDate(parsed.date_of_birth.value);
112
+ if (parsedDate != null) {
113
+ assignAutofilledValue(result, 'birthday', parsedDate);
114
+ }
115
+ }
116
+ // address_* fields are mapped at a higher layer (addressView reducer) — kept here as values
117
+ // only, the consuming epic dispatches the address payload separately.
118
+ return result;
119
+ };
120
+ /** SSN Card → Company Officer (name only — SSN value not yet returned by parser). */
121
+ export const mapSsnCardToOfficer = (parsed) => {
122
+ const result = emptyResult();
123
+ if (isConfident(parsed.full_name)) {
124
+ const { firstName, lastName } = splitFullName(parsed.full_name.value);
125
+ if (firstName !== '') {
126
+ assignAutofilledValue(result, 'firstName', firstName);
127
+ }
128
+ if (lastName !== '') {
129
+ assignAutofilledValue(result, 'lastName', lastName);
130
+ }
131
+ }
132
+ return result;
133
+ };
134
+ /** Certificate of Incorporation → Company Details. */
135
+ export const mapCoiToCompanyDetails = (parsed) => {
136
+ const result = emptyResult();
137
+ if (isConfident(parsed.company_legal_name)) {
138
+ assignAutofilledValue(result, 'companyLegalName', parsed.company_legal_name.value);
139
+ }
140
+ if (isConfident(parsed.ein)) {
141
+ assignAutofilledValue(result, 'taxIdOrEIN', parsed.ein.value);
142
+ }
143
+ if (isConfident(parsed.phone_number)) {
144
+ assignAutofilledValue(result, 'phone', parsed.phone_number.value);
145
+ }
146
+ if (isConfident(parsed.product_description)) {
147
+ assignAutofilledValue(result, 'companyDescription', parsed.product_description.value);
148
+ }
149
+ if (isConfident(parsed.website)) {
150
+ try {
151
+ assignAutofilledValue(result, 'website', toZeniUrl(parsed.website.value));
152
+ }
153
+ catch {
154
+ // ignore — malformed URL string from OCR
155
+ }
156
+ }
157
+ if (isConfident(parsed.industry)) {
158
+ assignAutofilledValue(result, 'companyIndustryType', parsed.industry.value);
159
+ }
160
+ if (isConfident(parsed.sub_industry)) {
161
+ assignAutofilledValue(result, 'companySubIndustry', parsed.sub_industry.value);
162
+ }
163
+ if (isConfident(parsed.type_of_incorporation)) {
164
+ assignAutofilledValue(result, 'typeOfIncorporation', parsed.type_of_incorporation.value);
165
+ }
166
+ if (isConfident(parsed.date_of_incorporation)) {
167
+ const parsedDate = tryParseDate(parsed.date_of_incorporation.value);
168
+ if (parsedDate != null) {
169
+ assignAutofilledValue(result, 'incDate', parsedDate);
170
+ }
171
+ }
172
+ if (isConfident(parsed.state_of_incorporation)) {
173
+ assignAutofilledValue(result, 'stateOfIncorporation', parsed.state_of_incorporation.value);
174
+ }
175
+ if (isConfident(parsed.countries_of_operations)) {
176
+ assignAutofilledValue(result, 'countriesOfOperations', parsed.countries_of_operations.value);
177
+ }
178
+ if (isConfident(parsed.source_of_funds)) {
179
+ assignAutofilledValue(result, 'sourceOfFunds', parsed.source_of_funds.value);
180
+ }
181
+ if (isConfident(parsed.transaction_volume_expectations)) {
182
+ assignAutofilledValue(result, 'transactionVolume', parsed.transaction_volume_expectations.value);
183
+ }
184
+ if (isConfident(parsed.purpose_of_account)) {
185
+ assignAutofilledValue(result, 'purposeOfAccount', parsed.purpose_of_account.value);
186
+ }
187
+ if (isConfident(parsed.regulated_status)) {
188
+ assignAutofilledValue(result, 'regulatedStatus', parsed.regulated_status.value);
189
+ }
190
+ if (isConfident(parsed.us_nexus)) {
191
+ assignAutofilledValue(result, 'usNexus', parsed.us_nexus.value);
192
+ }
193
+ return result;
194
+ };
195
+ /** Tax EIN letter → Company Details. */
196
+ export const mapTaxEinToCompanyDetails = (parsed) => {
197
+ const result = emptyResult();
198
+ if (isConfident(parsed.legal_business_name)) {
199
+ assignAutofilledValue(result, 'companyLegalName', parsed.legal_business_name.value);
200
+ }
201
+ if (isConfident(parsed.ein)) {
202
+ assignAutofilledValue(result, 'taxIdOrEIN', parsed.ein.value);
203
+ }
204
+ return result;
205
+ };
@@ -3,60 +3,26 @@ const initialFetchStatus = {
3
3
  fetchState: 'Not-Started',
4
4
  error: undefined,
5
5
  };
6
+ const emptyOfficerSlot = {
7
+ autoFilledFields: [],
8
+ localData: undefined,
9
+ sendOtpStatus: initialFetchStatus,
10
+ otpverificationStatus: initialFetchStatus,
11
+ };
6
12
  export const initialState = {
7
13
  updateStatus: { fetchState: 'Not-Started', error: undefined },
8
- companyDetails: {},
14
+ companyDetails: { autoFilledFields: [] },
9
15
  companyOfficerUpdateStatus: {
10
- Officer_1: {
11
- localData: undefined,
12
- sendOtpStatus: initialFetchStatus,
13
- otpverificationStatus: initialFetchStatus,
14
- },
15
- Officer_2: {
16
- localData: undefined,
17
- sendOtpStatus: initialFetchStatus,
18
- otpverificationStatus: initialFetchStatus,
19
- },
20
- Officer_3: {
21
- localData: undefined,
22
- sendOtpStatus: initialFetchStatus,
23
- otpverificationStatus: initialFetchStatus,
24
- },
25
- Officer_4: {
26
- localData: undefined,
27
- sendOtpStatus: initialFetchStatus,
28
- otpverificationStatus: initialFetchStatus,
29
- },
30
- Officer_5: {
31
- localData: undefined,
32
- sendOtpStatus: initialFetchStatus,
33
- otpverificationStatus: initialFetchStatus,
34
- },
35
- Officer_6: {
36
- localData: undefined,
37
- sendOtpStatus: initialFetchStatus,
38
- otpverificationStatus: initialFetchStatus,
39
- },
40
- Officer_7: {
41
- localData: undefined,
42
- sendOtpStatus: initialFetchStatus,
43
- otpverificationStatus: initialFetchStatus,
44
- },
45
- Officer_8: {
46
- localData: undefined,
47
- sendOtpStatus: initialFetchStatus,
48
- otpverificationStatus: initialFetchStatus,
49
- },
50
- Officer_9: {
51
- localData: undefined,
52
- sendOtpStatus: initialFetchStatus,
53
- otpverificationStatus: initialFetchStatus,
54
- },
55
- Officer_10: {
56
- localData: undefined,
57
- sendOtpStatus: initialFetchStatus,
58
- otpverificationStatus: initialFetchStatus,
59
- },
16
+ Officer_1: { ...emptyOfficerSlot },
17
+ Officer_2: { ...emptyOfficerSlot },
18
+ Officer_3: { ...emptyOfficerSlot },
19
+ Officer_4: { ...emptyOfficerSlot },
20
+ Officer_5: { ...emptyOfficerSlot },
21
+ Officer_6: { ...emptyOfficerSlot },
22
+ Officer_7: { ...emptyOfficerSlot },
23
+ Officer_8: { ...emptyOfficerSlot },
24
+ Officer_9: { ...emptyOfficerSlot },
25
+ Officer_10: { ...emptyOfficerSlot },
60
26
  },
61
27
  primaryContactDetails: {},
62
28
  };
@@ -297,7 +263,60 @@ const setupView = createSlice({
297
263
  clearSetupView(draft) {
298
264
  Object.assign(draft, initialState);
299
265
  },
266
+ /**
267
+ * Merge AI-parsed officer fields into the selected officer's localData and
268
+ * track which field names were autofilled so the form can show the ✦ badge.
269
+ */
270
+ applyKycDocumentAutofillForSetup: {
271
+ reducer(draft, action) {
272
+ const { officerType, values, autoFilledFieldNames } = action.payload;
273
+ const slot = draft.companyOfficerUpdateStatus[officerType];
274
+ slot.localData = Object.assign({}, slot.localData, values);
275
+ slot.autoFilledFields = Array.from(new Set([...slot.autoFilledFields, ...autoFilledFieldNames]));
276
+ },
277
+ prepare(payload) {
278
+ return { payload };
279
+ },
280
+ },
281
+ /**
282
+ * Merge AI-parsed company fields into companyDetails.localData and track
283
+ * which field names were autofilled.
284
+ */
285
+ applyKybDocumentAutofillForSetup: {
286
+ reducer(draft, action) {
287
+ const { values, autoFilledFieldNames } = action.payload;
288
+ draft.companyDetails.localData = Object.assign({}, draft.companyDetails.localData, values);
289
+ draft.companyDetails.autoFilledFields = Array.from(new Set([
290
+ ...draft.companyDetails.autoFilledFields,
291
+ ...autoFilledFieldNames,
292
+ ]));
293
+ },
294
+ prepare(payload) {
295
+ return { payload };
296
+ },
297
+ },
298
+ /**
299
+ * Wipe AI-autofill badges. If `officerType` is provided, clears only that
300
+ * officer; otherwise clears company details too.
301
+ */
302
+ clearKycKybAutofillForSetup: {
303
+ reducer(draft, action) {
304
+ const { officerType } = action.payload;
305
+ if (officerType != null) {
306
+ draft.companyOfficerUpdateStatus[officerType].autoFilledFields = [];
307
+ }
308
+ else {
309
+ draft.companyDetails.autoFilledFields = [];
310
+ Object.keys(draft.companyOfficerUpdateStatus).forEach((key) => {
311
+ draft.companyOfficerUpdateStatus[key].autoFilledFields = [];
312
+ });
313
+ }
314
+ },
315
+ prepare(payload = {}) {
316
+ return { payload };
317
+ },
318
+ },
300
319
  },
301
320
  });
302
- export const { enableSetup, enableSetupSuccess, enableSetupFailure, updateBusinessVerificationDetails, updateBusinessVerificationDetailsSuccess, updateBusinessVerificationDetailsFailure, updateSelectedCompanyOfficer, updateSetupViewLocalStoreData, saveSetupViewDataInLocalStore, saveCompnayOfficerPhoneInLocalStore, saveCompnayOfficerAdditionalDocumentsInLocalStore, saveTreasuryAdditionalDocumentsInLocalStore, sendOtp: sendOtpSpendManagementSetUp, resendOtp: resendOtpSpendManagementSetUp, sendOtpSuccess: sendOtpSuccessSpendManagementSetUp, sendOtpFailure: sendOtpFailureSpendManagementSetUp, verifyOtp: verifyOtpSpendManagementSetUp, verifyOtpSuccess: verifyOtpSuccessSpendManagementSetUp, verifyOtpFailure: verifyOtpFailureSpendManagementSetUp, saveIndustryAndIncDateInLocalStore, clearSetupViewDataInLocalStore, clearSetupView, } = setupView.actions;
321
+ export const { enableSetup, enableSetupSuccess, enableSetupFailure, updateBusinessVerificationDetails, updateBusinessVerificationDetailsSuccess, updateBusinessVerificationDetailsFailure, updateSelectedCompanyOfficer, updateSetupViewLocalStoreData, saveSetupViewDataInLocalStore, saveCompnayOfficerPhoneInLocalStore, saveCompnayOfficerAdditionalDocumentsInLocalStore, saveTreasuryAdditionalDocumentsInLocalStore, sendOtp: sendOtpSpendManagementSetUp, resendOtp: resendOtpSpendManagementSetUp, sendOtpSuccess: sendOtpSuccessSpendManagementSetUp, sendOtpFailure: sendOtpFailureSpendManagementSetUp, verifyOtp: verifyOtpSpendManagementSetUp, verifyOtpSuccess: verifyOtpSuccessSpendManagementSetUp, verifyOtpFailure: verifyOtpFailureSpendManagementSetUp, saveIndustryAndIncDateInLocalStore, clearSetupViewDataInLocalStore, clearSetupView, applyKycDocumentAutofillForSetup, applyKybDocumentAutofillForSetup, clearKycKybAutofillForSetup, } = setupView.actions;
303
322
  export default setupView.reducer;
@@ -119,6 +119,7 @@ export const getCompanyAndIdentityDetails = (state, companyId, companyAndIdentit
119
119
  const companyAddress = addressViewState.newAddressState?.company_address;
120
120
  const registeredCompanyAddress = addressViewState.newAddressState?.company_registered_address;
121
121
  companyDetailsLocalData = {
122
+ autoFilledFields: companyDetails.autoFilledFields,
122
123
  companyId,
123
124
  companyDescription,
124
125
  companyLegalName,
@@ -191,6 +192,7 @@ export const getCompanyAndIdentityDetails = (state, companyId, companyAndIdentit
191
192
  const companyOfficerAddress = addressViewState.newAddressState?.[addressType];
192
193
  companyOfficerLocalData[officerType] = {
193
194
  ...companyOfficer.localData,
195
+ autoFilledFields: companyOfficer.autoFilledFields,
194
196
  userFiles: getFilesByFileIds(fileState, fileIds),
195
197
  additionalFiles: getFilesByFileIds(fileState, additionalSubmittedDocumentsForVerificationFileIds),
196
198
  deleteFileStatusById,
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Shared types for the KYC / KYB document autofill flow that lives inside the
3
+ * commonSetup feature (Setup pages + Onboarding both consume it via the same
4
+ * selector view; there is no separate state slice).
5
+ *
6
+ * Backed by document-communication-agent `POST /1.0/documents/process/sync`,
7
+ * which proxies document-service ExtendAI parsers and returns one of the
8
+ * parser payloads below — each value shaped as `{value, confidence}` per field.
9
+ */
10
+ /** Confidence floor below which we drop an autofill candidate. */
11
+ export const KYC_KYB_AUTOFILL_MIN_CONFIDENCE = 0.5;
12
+ /**
13
+ * Maps the form-side {@link KycSelectDocumentType} (camelCase) to the
14
+ * parser-side {@link KycProvidedDocumentType} (snake_case). Returns `null` for
15
+ * `stateId`, which has no parser today.
16
+ */
17
+ export const toKycProvidedDocumentType = (selectType) => {
18
+ switch (selectType) {
19
+ case 'driverLicense':
20
+ return 'driving_license';
21
+ case 'passport':
22
+ return 'passport';
23
+ case 'ssnCard':
24
+ return 'social_security_card';
25
+ case 'stateId':
26
+ return null;
27
+ }
28
+ };
@@ -8,6 +8,8 @@ import { getActualPaymentDate } from './helpers';
8
8
  import { getApproversOrRejectors as getApproversOrRejectorsForReimbursement, } from './reimbursement/remiListView/remiListSelector';
9
9
  import { ALL_REMI_TABS, getRemiListKey, } from './reimbursement/remiListView/remiListState';
10
10
  //filters items against filter values representing a range. ex: date (from-to), amount(1k<-->5k)
11
+ // Spend Management uses only equal/not_equal — TC amount operators
12
+ // (inBetween, lessThan, greaterThan) live in the TC filter pipeline.
11
13
  const filterItemOnRangeCategoryType = (valueForItem, currentFilter) => {
12
14
  let doesItemFallInValueRange = false;
13
15
  const filterField = currentFilter.field;
@@ -41,7 +43,7 @@ const filterItemOnCategoriesWithSingleValue = (categoryValue, currentFilter, ran
41
43
  return filterItemOnRangeCategoryType(categoryValue, currentFilter);
42
44
  }
43
45
  // if filter category is not rangeType
44
- if (currentFilter.matchingOperator === 'not-equal') {
46
+ if (currentFilter.matchingOperator === 'not_equal') {
45
47
  return currentFilter.values.indexOf(categoryValue.toString()) === -1;
46
48
  }
47
49
  return currentFilter.values.indexOf(categoryValue.toString()) > -1;
@@ -115,7 +117,7 @@ export const applyAdvancedFiltersOnList = (entity, allItems, filters, filteredIt
115
117
  ? getCategoryValueForTaskList(key, item)
116
118
  : getCategoryValueForPaymentHistory(key, item);
117
119
  if (categoryValue == null) {
118
- if (currentFilter.matchingOperator === 'not-equal') {
120
+ if (currentFilter.matchingOperator === 'not_equal') {
119
121
  return true;
120
122
  }
121
123
  return false;
@@ -208,7 +208,7 @@ const applyAdvancedFiltersOnCompaniesList = (allCompanies, filters, filteredComp
208
208
  const key = currentFilter.field;
209
209
  const categoryValue = getCategoryValueForCompany(key, company);
210
210
  if (categoryValue == null) {
211
- if (currentFilter.matchingOperator === 'not-equal') {
211
+ if (currentFilter.matchingOperator === 'not_equal') {
212
212
  return true;
213
213
  }
214
214
  return false;
@@ -1,5 +1,5 @@
1
1
  import { of } from 'rxjs';
2
- import { catchError, filter, switchMap, mergeMap } from 'rxjs/operators';
2
+ import { catchError, filter, mergeMap, switchMap } from 'rxjs/operators';
3
3
  import { createZeniAPIStatus, isSuccessResponse, } from '../../../../responsePayload';
4
4
  import { mapCannedResponsePayloadToCannedResponse, } from '../cannedResponsesPayload';
5
5
  import { fetchCannedResponses, updateCannedResponses, updateCannedResponsesFetchStatus, } from '../cannedResponsesReducer';
@@ -1,5 +1,5 @@
1
1
  import { of } from 'rxjs';
2
- import { catchError, filter, switchMap, mergeMap } from 'rxjs/operators';
2
+ import { catchError, filter, mergeMap, switchMap } from 'rxjs/operators';
3
3
  import { createZeniAPIStatus, isSuccessResponse, } from '../../../../responsePayload';
4
4
  import { fetchCannedResponses, saveCannedResponse, saveCannedResponseFailure, saveCannedResponseSuccess, } from '../cannedResponsesReducer';
5
5
  export const saveCannedResponseEpic = (actions$, _state$, zeniAPI) => actions$.pipe(filter(saveCannedResponse.match), switchMap((action) => {
@@ -4,7 +4,7 @@ import { openSnackbar } from '../../../../entity/snackbar/snackbarReducer';
4
4
  import { updateTasks } from '../../../../entity/task/taskReducer';
5
5
  import { createZeniAPIStatus, isSuccessResponse, } from '../../../../responsePayload';
6
6
  import { fetchTaskList, removeTaskFromList, } from '../../taskListView/taskListReducer';
7
- import { snoozeTaskSuccessOrFailure, unsnoozeTask, } from '../taskDetailReducer';
7
+ import { snoozeTaskSuccessOrFailure, unsnoozeTask } from '../taskDetailReducer';
8
8
  export const unsnoozeTaskEpic = (actions$, _state$, zeniAPI) => actions$.pipe(filter(unsnoozeTask.match), mergeMap((action) => handleUnsnoozeTask(action.payload.taskId, zeniAPI)));
9
9
  const handleUnsnoozeTask = (taskId, zeniAPI) => {
10
10
  const unsnoozeTaskApi$ = zeniAPI.deleteAndGetJSON(`${zeniAPI.apiEndPoints.taskMicroServiceBaseUrl}/1.0/task-manager/tasks/${taskId}/snooze`);
@@ -66,7 +66,7 @@ export const initialState = {
66
66
  categories: [
67
67
  {
68
68
  field: 'status',
69
- matchingOperator: 'not-equal',
69
+ matchingOperator: 'not_equal',
70
70
  values: ['resolved'],
71
71
  valuesCombinationOperator: 'ANY',
72
72
  },
@@ -238,12 +238,34 @@ const transactionDetail = createSlice({
238
238
  saveTransactionDetailLocalData(draft, action) {
239
239
  const key = getTransactionDetailKey(action.payload.transactionId);
240
240
  const detail = recordGet(draft.transactionDetailById, key, undefined);
241
- if (detail != null) {
242
- draft.transactionDetailById[key] = {
243
- ...detail,
244
- transactionDetailLocalData: action.payload.transactionLocalData,
245
- };
241
+ if (detail == null) {
242
+ return;
246
243
  }
244
+ const incoming = action.payload.transactionLocalData;
245
+ const prevById = detail.transactionDetailLocalData.lineItemById;
246
+ // Per-line `categoryClassRecommendationsFetchState` is owned by the
247
+ // recommendation epic/reducers (markInProgress / markCompleted /
248
+ // markFailure / setEntityRecommendationForLineIdsForTransactionDetail).
249
+ // Callers that rebuild `transactionLocalData` from a UI snapshot will
250
+ // typically have an out-of-date copy of this field (e.g. payee-change
251
+ // captures the snapshot before the epic's start arm runs and would
252
+ // otherwise clobber In-Progress back to undefined). Treat the field as
253
+ // sticky: only let the caller override it when they explicitly set it.
254
+ const mergedLineItemById = {};
255
+ Object.entries(incoming.lineItemById ?? {}).forEach(([lineId, line]) => {
256
+ mergedLineItemById[lineId] = {
257
+ ...line,
258
+ categoryClassRecommendationsFetchState: line.categoryClassRecommendationsFetchState ??
259
+ prevById?.[lineId]?.categoryClassRecommendationsFetchState,
260
+ };
261
+ });
262
+ draft.transactionDetailById[key] = {
263
+ ...detail,
264
+ transactionDetailLocalData: {
265
+ ...incoming,
266
+ lineItemById: mergedLineItemById,
267
+ },
268
+ };
247
269
  },
248
270
  saveTransactionDetail(draft, action) {
249
271
  const key = getTransactionDetailKey(action.payload.transactionId);
@@ -365,6 +387,21 @@ const transactionDetail = createSlice({
365
387
  const detail = recordGet(draft.transactionDetailById, key, undefined);
366
388
  if (detail != null) {
367
389
  const newLocalData = setEntityRecommendationForLineIdInLocalData(transaction, detail.transactionDetailLocalData, action.payload.entity, lineIds, uncategorizedIncomeExpense, recommendationWithCOTByLineId);
390
+ // Release the per-line fetch flag for every line this fetch
391
+ // covered so the inline skeleton resolves and the page-level
392
+ // Save button can re-enable.
393
+ lineIds.forEach((lineId) => {
394
+ const lineItem = newLocalData.lineItemById[lineId];
395
+ if (lineItem != null) {
396
+ newLocalData.lineItemById[lineId] = {
397
+ ...lineItem,
398
+ categoryClassRecommendationsFetchState: {
399
+ fetchState: 'Completed',
400
+ error: undefined,
401
+ },
402
+ };
403
+ }
404
+ });
368
405
  draft.transactionDetailById[key] = {
369
406
  ...detail,
370
407
  transactionDetailLocalData: newLocalData,
@@ -385,6 +422,84 @@ const transactionDetail = createSlice({
385
422
  };
386
423
  },
387
424
  },
425
+ // Start arm of the line-update recommendations fetch for the detail
426
+ // page. Flips covered lines to In-Progress; detail page has no
427
+ // checkbox, so no auto-uncheck side effect.
428
+ markCategoryClassRecommendationsInProgressForTransactionDetail: {
429
+ reducer(draft, action) {
430
+ const { transactionId, lineIds } = action.payload;
431
+ const key = getTransactionDetailKey(transactionId);
432
+ const detail = recordGet(draft.transactionDetailById, key, undefined);
433
+ if (detail != null) {
434
+ lineIds.forEach((lineId) => {
435
+ const lineItem = detail.transactionDetailLocalData.lineItemById[lineId];
436
+ if (lineItem != null) {
437
+ detail.transactionDetailLocalData.lineItemById[lineId] = {
438
+ ...lineItem,
439
+ categoryClassRecommendationsFetchState: {
440
+ fetchState: 'In-Progress',
441
+ error: undefined,
442
+ },
443
+ };
444
+ }
445
+ });
446
+ }
447
+ },
448
+ prepare(transactionId, lineIds) {
449
+ return { payload: { transactionId, lineIds } };
450
+ },
451
+ },
452
+ // Completed arm. Used when the HTTP call succeeded but the server
453
+ // returned no per-line recommendations — still need to release the
454
+ // In-Progress flag.
455
+ markCategoryClassRecommendationsCompletedForTransactionDetail: {
456
+ reducer(draft, action) {
457
+ const { transactionId, lineIds } = action.payload;
458
+ const key = getTransactionDetailKey(transactionId);
459
+ const detail = recordGet(draft.transactionDetailById, key, undefined);
460
+ if (detail != null) {
461
+ lineIds.forEach((lineId) => {
462
+ const lineItem = detail.transactionDetailLocalData.lineItemById[lineId];
463
+ if (lineItem != null) {
464
+ detail.transactionDetailLocalData.lineItemById[lineId] = {
465
+ ...lineItem,
466
+ categoryClassRecommendationsFetchState: {
467
+ fetchState: 'Completed',
468
+ error: undefined,
469
+ },
470
+ };
471
+ }
472
+ });
473
+ }
474
+ },
475
+ prepare(transactionId, lineIds) {
476
+ return { payload: { transactionId, lineIds } };
477
+ },
478
+ },
479
+ markCategoryClassRecommendationsFailureForTransactionDetail: {
480
+ reducer(draft, action) {
481
+ const { transactionId, lineIds, error } = action.payload;
482
+ const key = getTransactionDetailKey(transactionId);
483
+ const detail = recordGet(draft.transactionDetailById, key, undefined);
484
+ if (detail != null) {
485
+ lineIds.forEach((lineId) => {
486
+ const lineItem = detail.transactionDetailLocalData.lineItemById[lineId];
487
+ if (lineItem != null) {
488
+ detail.transactionDetailLocalData.lineItemById[lineId] = {
489
+ ...lineItem,
490
+ categoryClassRecommendationsFetchState: {
491
+ fetchState: 'Error',
492
+ error,
493
+ },
494
+ };
495
+ }
496
+ });
497
+ }
498
+ },
499
+ prepare(transactionId, lineIds, error) {
500
+ return { payload: { transactionId, lineIds, error } };
501
+ },
502
+ },
388
503
  removeEntityRecommendationForLineIdForTransactionDetail: {
389
504
  reducer(draft, action) {
390
505
  const { transaction, lineId } = action.payload;
@@ -656,7 +771,7 @@ const transactionDetail = createSlice({
656
771
  },
657
772
  },
658
773
  });
659
- export const { downloadAccountingProviderAttachment, downloadAccountingProviderAttachmentSuccess, downloadAccountingProviderAttachmentFailure, fetchTransactionDetail, updateTransactionDetailFetchState, removeTransactionDetail, clearTransactionDetail, uploadMissingAttachmentSuccess, updateTransactionDetail, updateTransactionDetailSaveStatus, clearTransactionDetailSaveStatus, markTransactionAsNotMiscategorized, deleteTransactionAttachment, deleteTransactionAttachmentFailure, deleteTransactionAttachmentSuccess, updateStatusForTransactionNotMiscategorizedUpdate, updateStatusConsolidatedSaveTransactionDetail, updateTransactionStateIfUpdateActionNotAllowed, saveTransactionDetail, initializeTransactionDetailLocalData, saveTransactionDetailLocalData, clearTransactionDetailLocalData, resetLineItemsCategoryClassInLocalData, setAllLineItemsToCategoryClassInLocalData, setAllLineItemsLocalDataIsUpdateAllChecked, setEntityRecommendationForLineIdsForTransactionDetail, removeEntityRecommendationForLineIdForTransactionDetail, updateSelectedVendor, updateSelectedCustomer, resetVendor, resetSelectedCustomer, updateLocalDataWithTransactionsUpdateWithVendorCheckbox, updateVendorToAllLineItems, resetAllLineItemsToVendor, updateLatestSelectedEntity, updateVendorBulkRecommendationsFetchStatus, setIsVendorUpdateAllChecked, } = transactionDetail.actions;
774
+ export const { downloadAccountingProviderAttachment, downloadAccountingProviderAttachmentSuccess, downloadAccountingProviderAttachmentFailure, fetchTransactionDetail, updateTransactionDetailFetchState, removeTransactionDetail, clearTransactionDetail, uploadMissingAttachmentSuccess, updateTransactionDetail, updateTransactionDetailSaveStatus, clearTransactionDetailSaveStatus, markTransactionAsNotMiscategorized, deleteTransactionAttachment, deleteTransactionAttachmentFailure, deleteTransactionAttachmentSuccess, updateStatusForTransactionNotMiscategorizedUpdate, updateStatusConsolidatedSaveTransactionDetail, updateTransactionStateIfUpdateActionNotAllowed, saveTransactionDetail, initializeTransactionDetailLocalData, saveTransactionDetailLocalData, clearTransactionDetailLocalData, resetLineItemsCategoryClassInLocalData, setAllLineItemsToCategoryClassInLocalData, setAllLineItemsLocalDataIsUpdateAllChecked, setEntityRecommendationForLineIdsForTransactionDetail, markCategoryClassRecommendationsInProgressForTransactionDetail, markCategoryClassRecommendationsCompletedForTransactionDetail, markCategoryClassRecommendationsFailureForTransactionDetail, removeEntityRecommendationForLineIdForTransactionDetail, updateSelectedVendor, updateSelectedCustomer, resetVendor, resetSelectedCustomer, updateLocalDataWithTransactionsUpdateWithVendorCheckbox, updateVendorToAllLineItems, resetAllLineItemsToVendor, updateLatestSelectedEntity, updateVendorBulkRecommendationsFetchStatus, setIsVendorUpdateAllChecked, } = transactionDetail.actions;
660
775
  export default transactionDetail.reducer;
661
776
  function getUpdatedEntityLocalData(detail, updates, entity, resetRecommendations, type) {
662
777
  const key = type === 'customer' ? 'customerUpdates' : 'vendorUpdates';