@zeniai/client-epic-state 4.19.83 → 4.19.84-betaVR1

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 (113) hide show
  1. package/lib/entity/account/accountPayload.d.ts +2 -0
  2. package/lib/entity/account/accountPayload.js +2 -0
  3. package/lib/entity/account/accountState.d.ts +2 -0
  4. package/lib/entity/company/companyPayload.d.ts +13 -0
  5. package/lib/entity/company/companyPayload.js +21 -0
  6. package/lib/entity/company/companyStateTypes.d.ts +13 -0
  7. package/lib/entity/snackbar/snackbarTypes.d.ts +1 -1
  8. package/lib/entity/snackbar/snackbarTypes.js +1 -0
  9. package/lib/epic.d.ts +12 -1
  10. package/lib/epic.js +12 -1
  11. package/lib/esm/entity/account/accountPayload.js +2 -0
  12. package/lib/esm/entity/chargeCardTransaction/chargeCardTransactionPayload.js +2 -2
  13. package/lib/esm/entity/company/companyPayload.js +21 -0
  14. package/lib/esm/entity/snackbar/snackbarTypes.js +1 -0
  15. package/lib/esm/epic.js +12 -1
  16. package/lib/esm/index.js +12 -8
  17. package/lib/esm/view/companyView/epic/companyPassport/updateCompanyDetailsEpic.js +6 -0
  18. package/lib/esm/view/companyView/types/companyPassport/companyPassportLocalData.js +13 -0
  19. package/lib/esm/view/companyView/types/companyPassportViewPayload.js +55 -12
  20. package/lib/esm/view/expenseAutomationView/epics/missingReceipts/bulkUploadReceiptsEpic.js +24 -0
  21. package/lib/esm/view/expenseAutomationView/epics/missingReceipts/confirmBulkUploadMatchEpic.js +49 -0
  22. package/lib/esm/view/expenseAutomationView/epics/missingReceipts/fetchBulkUploadBatchDetailsEpic.js +40 -0
  23. package/lib/esm/view/expenseAutomationView/epics/missingReceipts/fetchBulkUploadBatchesEpic.js +47 -0
  24. package/lib/esm/view/expenseAutomationView/epics/missingReceipts/fetchCompletedTransactionsEpic.js +61 -0
  25. package/lib/esm/view/expenseAutomationView/epics/missingReceipts/fetchMoreBatchDetailsEpic.js +34 -0
  26. package/lib/esm/view/expenseAutomationView/epics/missingReceipts/fetchMultipleBatchDetailsEpic.js +57 -0
  27. package/lib/esm/view/expenseAutomationView/epics/missingReceipts/searchTransactionsForManualMatchEpic.js +68 -0
  28. package/lib/esm/view/expenseAutomationView/epics/missingReceipts/uploadMissingReceiptSuccessEpic.js +7 -5
  29. package/lib/esm/view/expenseAutomationView/epics/missingReceipts/watchBulkUploadBatchStatusEpic.js +42 -0
  30. package/lib/esm/view/expenseAutomationView/epics/transactionCategorization/triggerReviewTabRefetchEpic.js +2 -3
  31. package/lib/esm/view/expenseAutomationView/epics/transactionCategorization/uploadTransactionReceiptSuccessEpic.js +24 -0
  32. package/lib/esm/view/expenseAutomationView/helpers/transactionCategorizationLocalDataHelper.js +2 -2
  33. package/lib/esm/view/expenseAutomationView/payload/missingReceiptsPayload.js +147 -1
  34. package/lib/esm/view/expenseAutomationView/reducers/missingReceiptsViewReducer.js +401 -1
  35. package/lib/esm/view/expenseAutomationView/reducers/transactionsViewReducer.js +20 -1
  36. package/lib/esm/view/expenseAutomationView/selectors/missingReceiptsSelector.js +224 -1
  37. package/lib/esm/view/expenseAutomationView/selectors/transactionCategorizationSelector.js +2 -1
  38. package/lib/esm/view/expenseAutomationView/types/transactionsViewState.js +2 -1
  39. package/lib/esm/view/onboardingView/customerView/onboardingCustomerViewSelector.js +22 -4
  40. package/lib/esm/view/spendManagement/chargeCards/chargeCardPaymentHistory/chargeCardPaymentHistoryDownloadSelector.js +2 -2
  41. package/lib/esm/view/spendManagement/chargeCards/chargeCardPaymentHistory/chargeCardPaymentHistorySelector.js +2 -2
  42. package/lib/esm/view/spendManagement/chargeCards/chargeCardRepaymentDetail/chargeCardRepaymentDetailSelector.js +4 -1
  43. package/lib/esm/view/spendManagement/chargeCards/chargeCardRepaymentDetail/fetchChargeCardRepaymentDetailEpic.js +2 -1
  44. package/lib/esm/view/spendManagement/chargeCards/chargeCardRepaymentDetail/initiateChargeCardRepaymentEpic.js +1 -1
  45. package/lib/esm/view/spendManagement/commonSetup/epic/setup/updateBusinessVerificationDetailsEpic.js +67 -1
  46. package/lib/esm/view/spendManagement/commonSetup/epic/setup/updateSetupViewLocalStoreDataEpic.js +16 -2
  47. package/lib/esm/view/spendManagement/commonSetup/setupViewSelector.js +15 -1
  48. package/lib/esm/view/spendManagement/commonSetup/types/businessVerification.js +148 -1
  49. package/lib/esm/view/taskManager/taskDetailView/taskDetailSelector.js +1 -1
  50. package/lib/esm/zeniAPI.js +0 -2
  51. package/lib/index.d.ts +14 -9
  52. package/lib/index.js +87 -44
  53. package/lib/view/companyView/epic/companyPassport/updateCompanyDetailsEpic.js +6 -0
  54. package/lib/view/companyView/types/companyPassport/companyDetailsLocalData.d.ts +13 -0
  55. package/lib/view/companyView/types/companyPassport/companyPassportLocalData.js +13 -0
  56. package/lib/view/companyView/types/companyPassportViewPayload.d.ts +1 -1
  57. package/lib/view/companyView/types/companyPassportViewPayload.js +55 -12
  58. package/lib/view/expenseAutomationView/epics/missingReceipts/bulkUploadReceiptsEpic.d.ts +15 -0
  59. package/lib/view/expenseAutomationView/epics/missingReceipts/bulkUploadReceiptsEpic.js +28 -0
  60. package/lib/view/expenseAutomationView/epics/missingReceipts/confirmBulkUploadMatchEpic.d.ts +7 -0
  61. package/lib/view/expenseAutomationView/epics/missingReceipts/confirmBulkUploadMatchEpic.js +53 -0
  62. package/lib/view/expenseAutomationView/epics/missingReceipts/fetchBulkUploadBatchDetailsEpic.d.ts +8 -0
  63. package/lib/view/expenseAutomationView/epics/missingReceipts/fetchBulkUploadBatchDetailsEpic.js +44 -0
  64. package/lib/view/expenseAutomationView/epics/missingReceipts/fetchBulkUploadBatchesEpic.d.ts +7 -0
  65. package/lib/view/expenseAutomationView/epics/missingReceipts/fetchBulkUploadBatchesEpic.js +51 -0
  66. package/lib/view/expenseAutomationView/epics/missingReceipts/fetchCompletedTransactionsEpic.d.ts +8 -0
  67. package/lib/view/expenseAutomationView/epics/missingReceipts/fetchCompletedTransactionsEpic.js +65 -0
  68. package/lib/view/expenseAutomationView/epics/missingReceipts/fetchMoreBatchDetailsEpic.d.ts +8 -0
  69. package/lib/view/expenseAutomationView/epics/missingReceipts/fetchMoreBatchDetailsEpic.js +38 -0
  70. package/lib/view/expenseAutomationView/epics/missingReceipts/fetchMultipleBatchDetailsEpic.d.ts +9 -0
  71. package/lib/view/expenseAutomationView/epics/missingReceipts/fetchMultipleBatchDetailsEpic.js +62 -0
  72. package/lib/view/expenseAutomationView/epics/missingReceipts/searchTransactionsForManualMatchEpic.d.ts +14 -0
  73. package/lib/view/expenseAutomationView/epics/missingReceipts/searchTransactionsForManualMatchEpic.js +72 -0
  74. package/lib/view/expenseAutomationView/epics/missingReceipts/uploadMissingReceiptSuccessEpic.js +7 -5
  75. package/lib/view/expenseAutomationView/epics/missingReceipts/watchBulkUploadBatchStatusEpic.d.ts +13 -0
  76. package/lib/view/expenseAutomationView/epics/missingReceipts/watchBulkUploadBatchStatusEpic.js +47 -0
  77. package/lib/view/expenseAutomationView/epics/transactionCategorization/triggerReviewTabRefetchEpic.js +2 -3
  78. package/lib/view/expenseAutomationView/epics/transactionCategorization/uploadTransactionReceiptSuccessEpic.d.ts +7 -0
  79. package/lib/view/expenseAutomationView/epics/transactionCategorization/uploadTransactionReceiptSuccessEpic.js +28 -0
  80. package/lib/view/expenseAutomationView/helpers/transactionCategorizationLocalDataHelper.d.ts +1 -1
  81. package/lib/view/expenseAutomationView/helpers/transactionCategorizationLocalDataHelper.js +2 -2
  82. package/lib/view/expenseAutomationView/payload/missingReceiptsPayload.d.ts +107 -0
  83. package/lib/view/expenseAutomationView/payload/missingReceiptsPayload.js +158 -0
  84. package/lib/view/expenseAutomationView/reducers/missingReceiptsViewReducer.d.ts +70 -3
  85. package/lib/view/expenseAutomationView/reducers/missingReceiptsViewReducer.js +403 -2
  86. package/lib/view/expenseAutomationView/reducers/transactionsViewReducer.d.ts +11 -3
  87. package/lib/view/expenseAutomationView/reducers/transactionsViewReducer.js +21 -2
  88. package/lib/view/expenseAutomationView/selectorTypes/missingReceiptsSelectorTypes.d.ts +69 -1
  89. package/lib/view/expenseAutomationView/selectorTypes/transactionsViewSelectorTypes.d.ts +1 -0
  90. package/lib/view/expenseAutomationView/selectors/missingReceiptsSelector.d.ts +1 -1
  91. package/lib/view/expenseAutomationView/selectors/missingReceiptsSelector.js +223 -0
  92. package/lib/view/expenseAutomationView/selectors/transactionCategorizationSelector.js +2 -1
  93. package/lib/view/expenseAutomationView/types/missingReceiptsViewState.d.ts +124 -0
  94. package/lib/view/expenseAutomationView/types/transactionsViewState.d.ts +2 -1
  95. package/lib/view/expenseAutomationView/types/transactionsViewState.js +2 -1
  96. package/lib/view/onboardingView/customerView/onboardingCustomerViewSelector.d.ts +1 -0
  97. package/lib/view/onboardingView/customerView/onboardingCustomerViewSelector.js +22 -3
  98. package/lib/view/spendManagement/chargeCards/chargeCardPaymentHistory/chargeCardPaymentHistoryDownloadSelector.js +2 -2
  99. package/lib/view/spendManagement/chargeCards/chargeCardPaymentHistory/chargeCardPaymentHistorySelector.js +2 -2
  100. package/lib/view/spendManagement/chargeCards/chargeCardRepaymentDetail/chargeCardRepaymentDetailSelector.js +4 -1
  101. package/lib/view/spendManagement/chargeCards/chargeCardRepaymentDetail/fetchChargeCardRepaymentDetailEpic.js +2 -1
  102. package/lib/view/spendManagement/chargeCards/chargeCardRepaymentDetail/initiateChargeCardRepaymentEpic.d.ts +1 -1
  103. package/lib/view/spendManagement/chargeCards/chargeCardRepaymentDetail/initiateChargeCardRepaymentEpic.js +1 -1
  104. package/lib/view/spendManagement/commonSetup/epic/setup/updateBusinessVerificationDetailsEpic.js +67 -1
  105. package/lib/view/spendManagement/commonSetup/epic/setup/updateSetupViewLocalStoreDataEpic.js +16 -2
  106. package/lib/view/spendManagement/commonSetup/setupViewSelector.d.ts +14 -0
  107. package/lib/view/spendManagement/commonSetup/setupViewSelector.js +15 -1
  108. package/lib/view/spendManagement/commonSetup/setupViewState.d.ts +14 -0
  109. package/lib/view/spendManagement/commonSetup/types/businessVerification.d.ts +43 -0
  110. package/lib/view/spendManagement/commonSetup/types/businessVerification.js +150 -0
  111. package/lib/view/taskManager/taskDetailView/taskDetailSelector.js +1 -1
  112. package/lib/zeniAPI.js +0 -2
  113. package/package.json +2 -2
@@ -1,8 +1,10 @@
1
1
  import orderBy from 'lodash/orderBy';
2
2
  import { toMonthYearPeriodId } from '../../../commonStateTypes/timePeriod';
3
3
  import { getCurrentTenant } from '../../../entity/tenant/tenantSelector';
4
- import { getSupportedTransactionsByIds } from '../../../entity/transaction/transactionSelector';
4
+ import { getSupportedTransactionById, getSupportedTransactionsByIds, } from '../../../entity/transaction/transactionSelector';
5
5
  import { getAllSteps } from '../selectorTypes/expenseAutomationViewSelectorTypes';
6
+ import { isBatchListStatusCompleted } from '../payload/missingReceiptsPayload';
7
+ import { getCompletedTransactionsCacheKey } from '../reducers/missingReceiptsViewReducer';
6
8
  export function getExpenseAutomationMissingReceiptsView(state) {
7
9
  const { expenseAutomationMissingReceiptsViewState, expenseAutomationViewState, transactionState, monthEndCloseChecksState, } = state;
8
10
  const currentTenant = getCurrentTenant(state);
@@ -47,6 +49,110 @@ export function getExpenseAutomationMissingReceiptsView(state) {
47
49
  const completionStatus = missingReceiptsCompletionStatus != null
48
50
  ? missingReceiptsCompletionStatus
49
51
  : 'incomplete';
52
+ const { bulkUpload, pendingMissingReceiptsTabNavigation, } = expenseAutomationMissingReceiptsViewState;
53
+ const currentBatchStatus = bulkUpload.currentBatchId != null
54
+ ? bulkUpload.batchStatusById[bulkUpload.currentBatchId]
55
+ : undefined;
56
+ const batchList = monthYearPeriodId != null
57
+ ? bulkUpload.batchListByPeriod[monthYearPeriodId] ?? []
58
+ : [];
59
+ /**
60
+ * Primary "Unmatched" batch = first completed batch in platform list order, preferring the
61
+ * first batch that still has un_matched/no_match work when any completed batch has such work.
62
+ * Batches without details yet: wait if the list row still reports unmatched work; otherwise skip
63
+ * so we do not block the whole tab while other rows are not fetched yet.
64
+ */
65
+ const primaryBatchIdFromDetailsOrder = getPrimaryBatchIdFromBatchListOrder(batchList, bulkUpload.batchDetailsById, bulkUpload.failedBatchDetailIds);
66
+ /** During upload/matching, the in-flight batch may not appear in batchList yet — prefer currentBatchId. */
67
+ const unmatchedSectionBatchId = bulkUpload.phase === 'matching' || bulkUpload.phase === 'uploading'
68
+ ? bulkUpload.currentBatchId ?? primaryBatchIdFromDetailsOrder
69
+ : primaryBatchIdFromDetailsOrder;
70
+ const completedBatchesInApiOrder = batchList.filter((b) => isBatchListStatusCompleted(b.status));
71
+ const currentBatchDetails = unmatchedSectionBatchId != null
72
+ ? bulkUpload.batchDetailsById[unmatchedSectionBatchId]
73
+ : undefined;
74
+ const resolvedFiles = currentBatchDetails?.files.map((file) => resolveBatchFile(file, currentBatchDetails.batchId, transactionState));
75
+ const failedIds = new Set(bulkUpload.failedBatchDetailIds);
76
+ const hasMoreBatchDetails = completedBatchesInApiOrder.some((b) => bulkUpload.batchDetailsById[b.batchId] == null &&
77
+ !failedIds.has(b.batchId));
78
+ const getSortValue = (file) => {
79
+ const t = file.matchedTransaction;
80
+ if (t == null) {
81
+ return null;
82
+ }
83
+ switch (bulkUpload.sortKey) {
84
+ case 'vendor':
85
+ return (t.vendorName ?? t.description)?.toLowerCase() ?? null;
86
+ case 'category': {
87
+ const line = t.lines?.[0];
88
+ return line?.account?.accountName?.toLowerCase() ?? null;
89
+ }
90
+ case 'class': {
91
+ const line = t.lines?.[0];
92
+ return line?.class?.className?.toLowerCase() ?? null;
93
+ }
94
+ case 'date':
95
+ return t.date.valueOf();
96
+ case 'amount':
97
+ return t.amount.amount;
98
+ default:
99
+ return null;
100
+ }
101
+ };
102
+ const bulkUploadSortDirection = bulkUpload.sortOrder === 'ascending' ? 'asc' : 'desc';
103
+ const matchedFiles = orderBy(resolvedFiles?.filter((f) => f.status === 'matched') ?? [], getSortValue, bulkUploadSortDirection);
104
+ const unmatchedFiles = orderBy(resolvedFiles?.filter((f) => f.status === 'un_matched' || f.status === 'no_match') ?? [], getSortValue, bulkUploadSortDirection);
105
+ const progress = currentBatchStatus != null
106
+ ? {
107
+ ...currentBatchStatus.progress,
108
+ percentage: currentBatchStatus.progress.total > 0
109
+ ? Math.round((currentBatchStatus.progress.processed /
110
+ currentBatchStatus.progress.total) *
111
+ 100)
112
+ : 0,
113
+ }
114
+ : { total: 0, processed: 0, percentage: 0 };
115
+ const batchListFetchState = bulkUpload.batchListFetchState;
116
+ /**
117
+ * When primary batch is not resolved yet, still aggregate unmatched files from every batch
118
+ * that has details loaded (otherwise past is empty and receipts vanish from the UI).
119
+ */
120
+ const pastBatches = unmatchedSectionBatchId != null
121
+ ? batchList.filter((b) => b.batchId !== unmatchedSectionBatchId)
122
+ : batchList;
123
+ const pastUnmatchedFiles = pastBatches.flatMap((b) => {
124
+ const details = bulkUpload.batchDetailsById[b.batchId];
125
+ if (details == null) {
126
+ return [];
127
+ }
128
+ return details.files
129
+ .filter((f) => f.status === 'un_matched' || f.status === 'no_match')
130
+ .map((f) => resolveBatchFile(f, b.batchId, transactionState));
131
+ });
132
+ const completedTransactionsCacheKey = monthYearPeriodId != null
133
+ ? getCompletedTransactionsCacheKey(monthYearPeriodId, bulkUpload.sortKey, bulkUpload.sortOrder, bulkUpload.completedSubTab)
134
+ : undefined;
135
+ const completedTransactionsForPeriod = completedTransactionsCacheKey != null
136
+ ? bulkUpload.completedTransactionsByPeriod[completedTransactionsCacheKey]
137
+ : undefined;
138
+ const completedTransactionIds = completedTransactionsForPeriod?.transactionIds ?? [];
139
+ const resolvedCompletedTransactions = getSupportedTransactionsByIds(transactionState, completedTransactionIds);
140
+ const resolvedBatchDetails = currentBatchDetails != null && resolvedFiles != null
141
+ ? {
142
+ batchId: currentBatchDetails.batchId,
143
+ createdAt: currentBatchDetails.createdAt,
144
+ files: resolvedFiles,
145
+ status: currentBatchDetails.status,
146
+ summary: currentBatchDetails.summary,
147
+ }
148
+ : undefined;
149
+ const manualSearchResults = bulkUpload.manualSearch.results.map((r) => {
150
+ const t = getSupportedTransactionById(transactionState, r.transactionId);
151
+ return {
152
+ ...r,
153
+ vendorLogo: r.vendorLogo ?? t?.logo?.href,
154
+ };
155
+ });
50
156
  return {
51
157
  fetchState,
52
158
  error,
@@ -55,5 +161,122 @@ export function getExpenseAutomationMissingReceiptsView(state) {
55
161
  refreshStatus,
56
162
  transactions: sortedTransactions,
57
163
  completionStatus,
164
+ pendingMissingReceiptsTabNavigation: pendingMissingReceiptsTabNavigation ?? null,
165
+ bulkUpload: {
166
+ phase: bulkUpload.phase,
167
+ uploadStatus: bulkUpload.uploadStatus,
168
+ batchStatus: currentBatchStatus,
169
+ batchDetails: resolvedBatchDetails,
170
+ completedSubTab: bulkUpload.completedSubTab,
171
+ completedTransactions: {
172
+ fetchState: completedTransactionsForPeriod?.fetchState ?? {
173
+ fetchState: 'Not-Started',
174
+ error: undefined,
175
+ },
176
+ nextPageToken: completedTransactionsForPeriod?.nextPageToken ?? null,
177
+ totalCount: completedTransactionsForPeriod?.totalCount ?? 0,
178
+ transactions: resolvedCompletedTransactions,
179
+ },
180
+ matchedFiles,
181
+ unmatchedFiles,
182
+ progress,
183
+ currentResultsTab: bulkUpload.currentResultsTab,
184
+ sortKey: bulkUpload.sortKey,
185
+ sortOrder: bulkUpload.sortOrder,
186
+ batchList,
187
+ batchListFetchState,
188
+ pastBatches,
189
+ pastUnmatchedFiles,
190
+ confirmMatchStatus: bulkUpload.confirmMatchStatus,
191
+ manualSearchUiResetKey: bulkUpload.manualSearchUiResetKey,
192
+ hasMoreBatchDetails,
193
+ batchDetailsPaginationState: bulkUpload.batchDetailsPaginationState,
194
+ manualSearch: {
195
+ ...bulkUpload.manualSearch,
196
+ results: manualSearchResults,
197
+ },
198
+ uploadProgress: bulkUpload.uploadProgress,
199
+ },
200
+ };
201
+ }
202
+ function batchDetailsHasUnmatchedWork(details) {
203
+ return details.files.some((f) => f.status === 'un_matched' || f.status === 'no_match');
204
+ }
205
+ /** List row summary before batch details load — aligns with unmatched / possible-match work. */
206
+ function batchListItemHasUnmatchedWork(batch) {
207
+ return batch.noMatchCount > 0 || batch.possibleMatchesCount > 0;
208
+ }
209
+ /**
210
+ * `batchList` must match platform order (same sequence as the bulk-upload batches API). Picks the
211
+ * primary batch for the top "Unmatched" section. Skips failed detail fetches.
212
+ *
213
+ * If details are not loaded for an earlier list row but that row reports no unmatched work, we
214
+ * skip it so later rows with loaded details can be primary (avoids an empty tab when the first
215
+ * fetch page only loaded later batches). If the list reports unmatched work but details are still
216
+ * loading, we wait (undefined) so primary order stays correct once details arrive.
217
+ */
218
+ function getPrimaryBatchIdFromBatchListOrder(batchListInPlatformOrder, batchDetailsById, failedBatchDetailIds) {
219
+ const failedIds = new Set(failedBatchDetailIds);
220
+ const anyCompletedBatchHasUnmatchedWork = batchListInPlatformOrder.some((b) => {
221
+ if (failedIds.has(b.batchId)) {
222
+ return false;
223
+ }
224
+ const d = batchDetailsById[b.batchId];
225
+ if (d != null &&
226
+ d.status === 'completed' &&
227
+ batchDetailsHasUnmatchedWork(d)) {
228
+ return true;
229
+ }
230
+ return d == null && batchListItemHasUnmatchedWork(b);
231
+ });
232
+ for (const batch of batchListInPlatformOrder) {
233
+ if (failedIds.has(batch.batchId)) {
234
+ continue;
235
+ }
236
+ const details = batchDetailsById[batch.batchId];
237
+ if (details == null) {
238
+ if (batchListItemHasUnmatchedWork(batch)) {
239
+ return undefined;
240
+ }
241
+ continue;
242
+ }
243
+ if (details.status !== 'completed') {
244
+ continue;
245
+ }
246
+ if (anyCompletedBatchHasUnmatchedWork) {
247
+ if (batchDetailsHasUnmatchedWork(details)) {
248
+ return batch.batchId;
249
+ }
250
+ continue;
251
+ }
252
+ return batch.batchId;
253
+ }
254
+ return undefined;
255
+ }
256
+ function resolveBatchFile(file, batchId, transactionState) {
257
+ const matchedTransaction = file.matchedTransactionId != null
258
+ ? getSupportedTransactionById(transactionState, file.matchedTransactionId)
259
+ : undefined;
260
+ const candidates = file.candidates
261
+ ?.map((ref) => {
262
+ const transaction = getSupportedTransactionById(transactionState, ref.transactionId);
263
+ return transaction != null
264
+ ? { transaction, matchScore: ref.matchScore }
265
+ : undefined;
266
+ })
267
+ .filter((c) => c != null);
268
+ return {
269
+ attachmentId: file.attachmentId,
270
+ batchId,
271
+ fileDownloadUrl: file.fileDownloadUrl,
272
+ fileId: file.fileId,
273
+ filename: file.filename,
274
+ filePreviewUrl: file.filePreviewUrl,
275
+ status: file.status,
276
+ matchedTransaction,
277
+ candidates: candidates != null && candidates.length > 0
278
+ ? candidates
279
+ : undefined,
280
+ matchSource: file.matchSource,
58
281
  };
59
282
  }
@@ -10,7 +10,7 @@ import { getAllSteps } from '../selectorTypes/expenseAutomationViewSelectorTypes
10
10
  export function getExpenseAutomationTransactionView(state) {
11
11
  const { accountState, classState, classListState, accountListState, expenseAutomationTransactionsViewState, expenseAutomationViewState, transactionState, monthEndCloseChecksState, } = state;
12
12
  const { selectedTransactionCategorizationTab } = expenseAutomationTransactionsViewState;
13
- const { uiState, refreshStatus, saveStatus, markAsNotMiscategorizedStatus, transactionIdsBySelectedPeriod, transactionReviewLocalDataById, selectedCheckBoxTransactionIds, transactionIdsWithUnsavedData, selectedTransactionId, selectedTransactionLineId, } = expenseAutomationTransactionsViewState.transactionCategorizationView[selectedTransactionCategorizationTab];
13
+ const { uiState, refreshStatus, saveStatus, markAsNotMiscategorizedStatus, transactionIdsBySelectedPeriod, transactionReviewLocalDataById, selectedCheckBoxTransactionIds, transactionIdsWithUnsavedData, uploadReceiptStatusById, selectedTransactionId, selectedTransactionLineId, } = expenseAutomationTransactionsViewState.transactionCategorizationView[selectedTransactionCategorizationTab];
14
14
  const uncategorizedIncomeExpense = getUncategorizedAccounts(accountState, accountListState, 'accountList');
15
15
  const accountList = getAccountList(accountState, accountListState, 'accountList');
16
16
  const classList = getClassList(classState, classListState);
@@ -94,6 +94,7 @@ export function getExpenseAutomationTransactionView(state) {
94
94
  completionStatus,
95
95
  totalCountByTab,
96
96
  fetchStateByTransactionTabs: fetchStateByTab,
97
+ uploadReceiptStatusById,
97
98
  selectedTransactionId,
98
99
  selectedTransactionLineId,
99
100
  };
@@ -4,10 +4,11 @@ const TRANSACTIONS_SORT_KEYS = [
4
4
  'vendor',
5
5
  'amount',
6
6
  'transaction_type',
7
+ 'payment_account',
7
8
  'date',
8
9
  'category',
9
10
  'class',
10
- 'memo',
11
+ 'memo_and_receipt',
11
12
  ];
12
13
  export const toTransactionsSortKey = (v) => stringToUnion(v, TRANSACTIONS_SORT_KEYS);
13
14
  export const TRANSACTIONS_TABS = ['review', 'autoCategorized'];
@@ -218,17 +218,17 @@ export const onboardingStepsData = (isValidConnection, productSettings, accounti
218
218
  requiredStepsData: requiredStepsData.map((step) => toOnboardingStepType(step)),
219
219
  };
220
220
  };
221
- function isCompanyDetailsSavePending(fileState, company, primaryContact, companyDetailsLocalData) {
221
+ export function isCompanyDetailsSavePending(fileState, company, primaryContact, companyDetailsLocalData) {
222
222
  if (company == null || companyDetailsLocalData == null) {
223
223
  return false;
224
224
  }
225
225
  const { companyInfo, incInfo, taxInfo, questionaire } = company.company.company;
226
- const { companyDescription, companyLegalName, companyUrl, phone } = companyInfo;
227
- const { fileIds, companyIncDate: incDate, companyIncType } = incInfo;
226
+ const { companyDescription, companyLegalName, companyUrl, phone, companySourceOfFunds, companySourceOfFundsDescription, countriesOfOperations, transactionVolume, transactionVolumeDescription, purposeOfAccount, purposeOfAccountDescription, regulatedStatus, regulatedStatusDescription, usNexus, usNexusTypes, } = companyInfo;
227
+ const { fileIds, companyIncDate: incDate, companyIncState, companyIncType, companySubIndustry, } = incInfo;
228
228
  const { companyIndustry } = questionaire;
229
229
  const { taxId } = taxInfo;
230
230
  const companyFileIds = getFilesByFileIds(fileState, fileIds).map((file) => file.fileId);
231
- const { companyDescription: companyDescriptionLocalData, companyIndustry: companyIndustryLocalData, companyLegalName: companyLegalNameLocalData, taxIdOrEIN: taxIdLocalData, incDate: incDateLocalData, phone: companyPhoneLocalData, website: companyUrlLocalData, companyFiles: companyFilesLocalData, typeOfIncorporation: companyIncTypeLocalData, primaryContactLocalData, } = companyDetailsLocalData;
231
+ const { companyDescription: companyDescriptionLocalData, companyIndustry: companyIndustryLocalData, companyLegalName: companyLegalNameLocalData, taxIdOrEIN: taxIdLocalData, incDate: incDateLocalData, phone: companyPhoneLocalData, website: companyUrlLocalData, companyFiles: companyFilesLocalData, stateOfIncorporation: stateOfIncorporationLocalData, typeOfIncorporation: companyIncTypeLocalData, companySourceOfFunds: companySourceOfFundsLocalData, companySourceOfFundsDescription: companySourceOfFundsDescriptionLocalData, countriesOfOperations: countriesOfOperationsLocalData, companySubIndustry: companySubIndustryLocalData, transactionVolume: transactionVolumeLocalData, transactionVolumeDescription: transactionVolumeDescriptionLocalData, purposeOfAccount: purposeOfAccountLocalData, purposeOfAccountDescription: purposeOfAccountDescriptionLocalData, regulatedStatus: regulatedStatusLocalData, regulatedStatusDescription: regulatedStatusDescriptionLocalData, usNexus: usNexusLocalData, usNexusTypes: usNexusTypesLocalData, primaryContactLocalData, } = companyDetailsLocalData;
232
232
  const fileIdsLocalData = companyFilesLocalData.map((file) => file.fileId);
233
233
  if (primaryContact != null) {
234
234
  const { firstName, lastName, email } = primaryContact;
@@ -238,6 +238,10 @@ function isCompanyDetailsSavePending(fileState, company, primaryContact, company
238
238
  return true;
239
239
  }
240
240
  }
241
+ const countriesOfOperationsMatch = JSON.stringify(countriesOfOperations ?? []) ===
242
+ JSON.stringify(countriesOfOperationsLocalData ?? []);
243
+ const usNexusTypesMatch = JSON.stringify(usNexusTypes ?? []) ===
244
+ JSON.stringify(usNexusTypesLocalData ?? []);
241
245
  return (companyDescription != companyDescriptionLocalData ||
242
246
  companyIndustry != companyIndustryLocalData ||
243
247
  companyLegalName != companyLegalNameLocalData ||
@@ -245,7 +249,21 @@ function isCompanyDetailsSavePending(fileState, company, primaryContact, company
245
249
  incDate?.toString() != incDateLocalData?.toString() ||
246
250
  phone != companyPhoneLocalData ||
247
251
  companyUrl?.toString() != companyUrlLocalData?.toString() ||
252
+ companyIncState != stateOfIncorporationLocalData ||
253
+ companySourceOfFunds != companySourceOfFundsLocalData ||
254
+ companySourceOfFundsDescription !=
255
+ companySourceOfFundsDescriptionLocalData ||
256
+ !countriesOfOperationsMatch ||
248
257
  companyIncType != companyIncTypeLocalData ||
258
+ transactionVolume != transactionVolumeLocalData ||
259
+ transactionVolumeDescription != transactionVolumeDescriptionLocalData ||
260
+ purposeOfAccount != purposeOfAccountLocalData ||
261
+ purposeOfAccountDescription != purposeOfAccountDescriptionLocalData ||
262
+ regulatedStatus != regulatedStatusLocalData ||
263
+ regulatedStatusDescription != regulatedStatusDescriptionLocalData ||
264
+ usNexus != usNexusLocalData ||
265
+ !usNexusTypesMatch ||
266
+ companySubIndustry != companySubIndustryLocalData ||
249
267
  !(fileIdsLocalData.length === companyFileIds.length &&
250
268
  companyFileIds.every((fileId) => fileIdsLocalData.includes(fileId))));
251
269
  }
@@ -2,8 +2,8 @@ import { getChargeCardRepaymentByIds } from '../../../../entity/chargeCardRepaym
2
2
  import { getUsersByUserIds } from '../../../../entity/user/userSelector';
3
3
  import { applyPaymentHistoryFiltersSearchAndSort } from './chargeCardPaymentHistoryHelpers';
4
4
  export const getChargeCardPaymentHistoryDownloadReport = (state) => {
5
- const { chargeCardPaymentHistoryState, chargeCardRepaymentState, userState, } = state;
6
- const { filters, repaymentHistoryIds, searchText, uiState, fetchState, error, } = chargeCardPaymentHistoryState;
5
+ const { chargeCardPaymentHistoryState, chargeCardRepaymentState, userState } = state;
6
+ const { filters, repaymentHistoryIds, searchText, uiState, fetchState, error } = chargeCardPaymentHistoryState;
7
7
  const repaymentHistory = getChargeCardRepaymentByIds(chargeCardRepaymentState, repaymentHistoryIds);
8
8
  const repaymentHistoryUserIds = Array.from(new Set(repaymentHistory.map((r) => r.paidById)));
9
9
  const repaymentHistoryUsers = getUsersByUserIds(userState, repaymentHistoryUserIds);
@@ -1,9 +1,9 @@
1
1
  import { reduceAllFetchState } from '../../../../commonStateTypes/reduceFetchState';
2
2
  import { getChargeCardRepaymentByIds } from '../../../../entity/chargeCardRepayment/chargeCardRepaymentSelector';
3
- import { getUsersByUserIds } from '../../../../entity/user/userSelector';
4
- import { getAllFundingAccounts, mapDepositAccToFundingAccount, mapPaymentAccToFundingAccount, } from '../../commonSetup/setupViewSelector';
5
3
  import { getDepositAccountByDepositAccountId } from '../../../../entity/depositAccount/depositAccountSelector';
6
4
  import { getPaymentAccountByPaymentAccountId } from '../../../../entity/paymentAccount/paymentAccountSelector';
5
+ import { getUsersByUserIds } from '../../../../entity/user/userSelector';
6
+ import { getAllFundingAccounts, mapDepositAccToFundingAccount, mapPaymentAccToFundingAccount, } from '../../commonSetup/setupViewSelector';
7
7
  import { getAllDepositAccounts } from '../../zeniAccounts/depositAccountList/depositAccountListSelector';
8
8
  import { getAllPaymentAccounts } from '../../zeniAccounts/paymentAccountList/paymentAccountListSelector';
9
9
  import { applyPaymentHistoryFiltersSearchAndSort } from './chargeCardPaymentHistoryHelpers';
@@ -15,7 +15,10 @@ export const getChargeCardRepaymentDetail = (state) => {
15
15
  else {
16
16
  const depositFundingFromLimit = Object.values(depositAccountState.depositAccountLimitForChargeCardByID ?? {}).map(mapDepositAccountWithLimitToFundingAccount);
17
17
  const paymentFundingAccounts = getAllFundingAccounts([], paymentAccountsView.paymentAccounts, paymentAccountListState.paymentAccountBalanceFetchStatus);
18
- transferFromAccounts = [...depositFundingFromLimit, ...paymentFundingAccounts];
18
+ transferFromAccounts = [
19
+ ...depositFundingFromLimit,
20
+ ...paymentFundingAccounts,
21
+ ];
19
22
  }
20
23
  }
21
24
  const reducedFetchState = reduceAnyCompletedFetchState([
@@ -7,7 +7,8 @@ export const fetchChargeCardRepaymentDetailEpic = (actions$, state$) => actions$
7
7
  const fetchChargeCardRepaymentDetailActions = [];
8
8
  const state = state$.value;
9
9
  const depositAccounts = state.depositAccountListState;
10
- const willFetchDeposit = depositAccounts.fetchState !== 'In-Progress' && depositAccounts.fetchState !== 'Completed';
10
+ const willFetchDeposit = depositAccounts.fetchState !== 'In-Progress' &&
11
+ depositAccounts.fetchState !== 'Completed';
11
12
  if (willFetchDeposit) {
12
13
  fetchChargeCardRepaymentDetailActions.push(fetchDepositAccountList());
13
14
  }
@@ -9,8 +9,8 @@ import { getAllFundingAccounts } from '../../commonSetup/setupViewSelector';
9
9
  import { accountTypeSubConfigCodeToAccType } from '../../helpers';
10
10
  import { getAllDepositAccounts } from '../../zeniAccounts/depositAccountList/depositAccountListSelector';
11
11
  import { getAllPaymentAccounts } from '../../zeniAccounts/paymentAccountList/paymentAccountListSelector';
12
- import { addRepaymentToChargeCardPaymentHistory } from '../chargeCardPaymentHistory/chargeCardPaymentHistoryReducer';
13
12
  import { updateCreditAccountRepaymentAmountAndDate } from '../chargeCardList/chargeCardListReducer';
13
+ import { addRepaymentToChargeCardPaymentHistory } from '../chargeCardPaymentHistory/chargeCardPaymentHistoryReducer';
14
14
  import { initiateChargeCardRepayment, updateChargeCardRepaymentStatus, } from './chargeCardRepaymentDetailReducer';
15
15
  export const initiateChargeCardRepaymentEpic = (actions$, state$, zeniAPI) => actions$.pipe(filter(initiateChargeCardRepayment.match), switchMap((action) => {
16
16
  const state = state$.value;
@@ -115,8 +115,28 @@ export const updateBusinessVerificationDetailsEpic = (actions$, state$, zeniAPI)
115
115
  return from([]);
116
116
  }
117
117
  }));
118
+ /** US Nexus API keys. Form may submit these or display labels; we send keys to API. */
119
+ const US_NEXUS_KEYS = new Set([
120
+ 'employees',
121
+ 'customers',
122
+ 'physical_office',
123
+ 'banking_relationships',
124
+ ]);
125
+ /** Maps display label → API key for US nexus types (English labels from strings). */
126
+ const US_NEXUS_LABEL_TO_KEY = {
127
+ Employees: 'employees',
128
+ Customers: 'customers',
129
+ 'Physical office or facility': 'physical_office',
130
+ 'Banking relationships': 'banking_relationships',
131
+ };
132
+ function toUsNexusTypeKey(value) {
133
+ if (US_NEXUS_KEYS.has(value)) {
134
+ return value;
135
+ }
136
+ return US_NEXUS_LABEL_TO_KEY[value] ?? value;
137
+ }
118
138
  export const toBusinessVerificationCompanyDataPayload = (companyLocalData, sendForVerification, companyAddressData, registeredAddressData, setupViewType) => {
119
- const { companyDescription, companyLegalName, companyIndustry, website, incDate, taxIdOrEIN, phone, syncToken, fileIds, typeOfIncorporation, sourceOfFunds, } = companyLocalData;
139
+ const { companyDescription, companyLegalName, companyIndustry, companyIndustryType, companySubIndustry, countriesOfOperations, companySourceOfFunds, companySourceOfFundsDescription, transactionVolume, transactionVolumeDescription, purposeOfAccount, purposeOfAccountDescription, regulatedStatus, regulatedStatusDescription, usNexus, usNexusTypes, website, incDate, taxIdOrEIN, phone, syncToken, fileIds, typeOfIncorporation, stateOfIncorporation, sourceOfFunds, } = companyLocalData;
120
140
  const company = {
121
141
  is_ready_for_verification: sendForVerification,
122
142
  file_ids: fileIds,
@@ -167,9 +187,55 @@ export const toBusinessVerificationCompanyDataPayload = (companyLocalData, sendF
167
187
  if (typeOfIncorporation != null) {
168
188
  company['company_inc_type'] = typeOfIncorporation;
169
189
  }
190
+ if (stateOfIncorporation != null) {
191
+ company['company_inc_state'] = stateOfIncorporation;
192
+ }
170
193
  if (companyIndustry != null) {
171
194
  company['company_industry'] = companyIndustry;
172
195
  }
196
+ Object.assign(company, {
197
+ ...(companyIndustryType != null && {
198
+ company_industry_type: companyIndustryType,
199
+ }),
200
+ ...(companySubIndustry != null && {
201
+ company_industry_sub_type: companySubIndustry,
202
+ }),
203
+ ...(countriesOfOperations != null &&
204
+ countriesOfOperations.length > 0 && {
205
+ company_countries_of_operations: countriesOfOperations,
206
+ }),
207
+ ...(companySourceOfFunds != null && {
208
+ company_source_of_funds: companySourceOfFunds,
209
+ }),
210
+ ...(companySourceOfFundsDescription != null && {
211
+ company_source_of_funds_desc: companySourceOfFundsDescription,
212
+ }),
213
+ ...(transactionVolume != null && {
214
+ company_transaction_vol_expectations: transactionVolume,
215
+ }),
216
+ ...(transactionVolumeDescription != null && {
217
+ company_transaction_vol_expectations_desc: transactionVolumeDescription,
218
+ }),
219
+ ...(purposeOfAccount != null && {
220
+ company_purpose_of_account: purposeOfAccount,
221
+ }),
222
+ ...(purposeOfAccountDescription != null && {
223
+ company_purpose_of_account_desc: purposeOfAccountDescription,
224
+ }),
225
+ ...(regulatedStatus != null && {
226
+ company_regulated_states: regulatedStatus === 'yes',
227
+ }),
228
+ ...(regulatedStatusDescription != null && {
229
+ company_regulated_states_desc: regulatedStatusDescription,
230
+ }),
231
+ ...(usNexus != null && {
232
+ company_us_nexus: usNexus === 'yes',
233
+ }),
234
+ ...(usNexusTypes != null &&
235
+ usNexusTypes.length > 0 && {
236
+ company_us_nexus_desc: usNexusTypes.map(toUsNexusTypeKey),
237
+ }),
238
+ });
173
239
  if (setupViewType === 'zeni_treasury' && sourceOfFunds != null) {
174
240
  company['source_of_funds'] = sourceOfFunds;
175
241
  }
@@ -43,16 +43,21 @@ export const updateSetupViewLocalStoreDataEpic = (actions$, state$) => actions$.
43
43
  */
44
44
  export function toSetupViewLocalData(company, users, userRoles, primaryContact) {
45
45
  const { companyInfo, incInfo, taxInfo, questionaire, companyBillPayInfo } = company;
46
- const { companyId, companyDescription, companyLegalName, companyUrl, phone, syncToken, sourceOfFunds, } = companyInfo;
46
+ const { companyId, companyDescription, companyLegalName, companyUrl, phone, syncToken, sourceOfFunds, companySourceOfFunds: incCompanySourceOfFunds, companySourceOfFundsDescription, countriesOfOperations, transactionVolume, transactionVolumeDescription, purposeOfAccount, purposeOfAccountDescription, regulatedStatus, regulatedStatusDescription, usNexus, usNexusTypes, } = companyInfo;
47
47
  const { usersSelectedForVerification: usersSelected } = companyBillPayInfo;
48
- const { fileIds, companyIncDate: incDate, companyIncType } = incInfo;
48
+ const { fileIds, companyIncDate: incDate, companyIncType, companyIncState, companySubIndustry, companyIndustryType, } = incInfo;
49
49
  const { companyIndustry } = questionaire;
50
50
  const { taxId } = taxInfo;
51
51
  const companyDetails = {
52
52
  companyId,
53
53
  companyDescription,
54
54
  companyIndustry,
55
+ companyIndustryType,
55
56
  companyLegalName,
57
+ companySubIndustry,
58
+ companySourceOfFunds: incCompanySourceOfFunds,
59
+ companySourceOfFundsDescription,
60
+ countriesOfOperations,
56
61
  taxIdOrEIN: taxId,
57
62
  incDate,
58
63
  phone,
@@ -61,6 +66,15 @@ export function toSetupViewLocalData(company, users, userRoles, primaryContact)
61
66
  syncToken,
62
67
  typeOfIncorporation: companyIncType,
63
68
  sourceOfFunds,
69
+ stateOfIncorporation: companyIncState,
70
+ transactionVolume,
71
+ transactionVolumeDescription,
72
+ purposeOfAccount,
73
+ purposeOfAccountDescription,
74
+ regulatedStatus,
75
+ regulatedStatusDescription,
76
+ usNexus,
77
+ usNexusTypes,
64
78
  };
65
79
  let primaryContactDetails = undefined;
66
80
  if (primaryContact != null) {
@@ -109,7 +109,7 @@ export const getCompanyAndIdentityDetails = (state, companyId, companyAndIdentit
109
109
  if (companyDetails.localData != null) {
110
110
  const deleteFileStatusById = {};
111
111
  const updateFileStatusById = {};
112
- const { syncToken, companyDescription, companyLegalName, website, companyIndustry, phone, taxIdOrEIN, incDate, fileIds, typeOfIncorporation, sourceOfFunds, } = companyDetails.localData;
112
+ const { syncToken, companyDescription, companyLegalName, website, companyIndustry, companyIndustryType, companySubIndustry, companySourceOfFunds, companySourceOfFundsDescription, countriesOfOperations, phone, taxIdOrEIN, incDate, fileIds, typeOfIncorporation, sourceOfFunds, transactionVolume, transactionVolumeDescription, purposeOfAccount, purposeOfAccountDescription, regulatedStatus, regulatedStatusDescription, stateOfIncorporation, usNexus, usNexusTypes, } = companyDetails.localData;
113
113
  fileIds.forEach((fileId) => {
114
114
  deleteFileStatusById[fileId] = getFileDeleteStatusById(state, fileId);
115
115
  });
@@ -123,6 +123,11 @@ export const getCompanyAndIdentityDetails = (state, companyId, companyAndIdentit
123
123
  companyDescription,
124
124
  companyLegalName,
125
125
  companyIndustry,
126
+ companyIndustryType,
127
+ companySubIndustry,
128
+ companySourceOfFunds,
129
+ companySourceOfFundsDescription,
130
+ countriesOfOperations,
126
131
  website,
127
132
  phone,
128
133
  taxIdOrEIN,
@@ -136,6 +141,15 @@ export const getCompanyAndIdentityDetails = (state, companyId, companyAndIdentit
136
141
  primaryContactLocalData,
137
142
  typeOfIncorporation,
138
143
  sourceOfFunds,
144
+ transactionVolume,
145
+ transactionVolumeDescription,
146
+ purposeOfAccount,
147
+ purposeOfAccountDescription,
148
+ regulatedStatus,
149
+ regulatedStatusDescription,
150
+ stateOfIncorporation,
151
+ usNexus,
152
+ usNexusTypes,
139
153
  };
140
154
  }
141
155
  const companyOfficerLocalData = {