@zeniai/client-epic-state 4.19.37-betaVR1 → 4.19.37-betaVR11

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.

Potentially problematic release.


This version of @zeniai/client-epic-state might be problematic. Click here for more details.

Files changed (44) hide show
  1. package/lib/entity/snackbar/snackbarTypes.d.ts +1 -1
  2. package/lib/entity/snackbar/snackbarTypes.js +1 -0
  3. package/lib/epic.d.ts +3 -1
  4. package/lib/epic.js +3 -1
  5. package/lib/esm/entity/snackbar/snackbarTypes.js +1 -0
  6. package/lib/esm/epic.js +3 -1
  7. package/lib/esm/index.js +2 -2
  8. package/lib/esm/view/expenseAutomationView/epics/missingReceipts/bulkUploadReceiptsEpic.js +4 -8
  9. package/lib/esm/view/expenseAutomationView/epics/missingReceipts/confirmBulkUploadMatchEpic.js +37 -13
  10. package/lib/esm/view/expenseAutomationView/epics/missingReceipts/fetchBulkUploadBatchDetailsEpic.js +3 -3
  11. package/lib/esm/view/expenseAutomationView/epics/missingReceipts/fetchBulkUploadBatchesEpic.js +41 -11
  12. package/lib/esm/view/expenseAutomationView/epics/missingReceipts/fetchCompletedTransactionsEpic.js +23 -14
  13. package/lib/esm/view/expenseAutomationView/epics/missingReceipts/fetchMoreBatchDetailsEpic.js +31 -0
  14. package/lib/esm/view/expenseAutomationView/epics/missingReceipts/fetchMultipleBatchDetailsEpic.js +39 -0
  15. package/lib/esm/view/expenseAutomationView/epics/missingReceipts/watchBulkUploadBatchStatusEpic.js +35 -27
  16. package/lib/esm/view/expenseAutomationView/payload/missingReceiptsPayload.js +12 -80
  17. package/lib/esm/view/expenseAutomationView/reducers/missingReceiptsViewReducer.js +79 -44
  18. package/lib/esm/view/expenseAutomationView/selectors/missingReceiptsSelector.js +25 -5
  19. package/lib/index.d.ts +3 -3
  20. package/lib/index.js +35 -32
  21. package/lib/view/expenseAutomationView/epics/missingReceipts/bulkUploadReceiptsEpic.d.ts +2 -5
  22. package/lib/view/expenseAutomationView/epics/missingReceipts/bulkUploadReceiptsEpic.js +3 -7
  23. package/lib/view/expenseAutomationView/epics/missingReceipts/confirmBulkUploadMatchEpic.d.ts +2 -1
  24. package/lib/view/expenseAutomationView/epics/missingReceipts/confirmBulkUploadMatchEpic.js +36 -12
  25. package/lib/view/expenseAutomationView/epics/missingReceipts/fetchBulkUploadBatchDetailsEpic.js +3 -3
  26. package/lib/view/expenseAutomationView/epics/missingReceipts/fetchBulkUploadBatchesEpic.d.ts +3 -2
  27. package/lib/view/expenseAutomationView/epics/missingReceipts/fetchBulkUploadBatchesEpic.js +40 -10
  28. package/lib/view/expenseAutomationView/epics/missingReceipts/fetchCompletedTransactionsEpic.js +22 -13
  29. package/lib/view/expenseAutomationView/epics/missingReceipts/fetchMoreBatchDetailsEpic.d.ts +8 -0
  30. package/lib/view/expenseAutomationView/epics/missingReceipts/fetchMoreBatchDetailsEpic.js +35 -0
  31. package/lib/view/expenseAutomationView/epics/missingReceipts/fetchMultipleBatchDetailsEpic.d.ts +9 -0
  32. package/lib/view/expenseAutomationView/epics/missingReceipts/fetchMultipleBatchDetailsEpic.js +44 -0
  33. package/lib/view/expenseAutomationView/epics/missingReceipts/watchBulkUploadBatchStatusEpic.d.ts +3 -10
  34. package/lib/view/expenseAutomationView/epics/missingReceipts/watchBulkUploadBatchStatusEpic.js +33 -25
  35. package/lib/view/expenseAutomationView/helpers/transactionCategorizationLocalDataHelper.d.ts +1 -1
  36. package/lib/view/expenseAutomationView/payload/missingReceiptsPayload.d.ts +19 -84
  37. package/lib/view/expenseAutomationView/payload/missingReceiptsPayload.js +12 -82
  38. package/lib/view/expenseAutomationView/reducers/missingReceiptsViewReducer.d.ts +15 -5
  39. package/lib/view/expenseAutomationView/reducers/missingReceiptsViewReducer.js +80 -45
  40. package/lib/view/expenseAutomationView/selectorTypes/missingReceiptsSelectorTypes.d.ts +5 -1
  41. package/lib/view/expenseAutomationView/selectors/missingReceiptsSelector.js +25 -5
  42. package/lib/view/expenseAutomationView/types/missingReceiptsViewState.d.ts +7 -3
  43. package/package.json +2 -2
  44. package/lib/tsconfig.typecheck.tsbuildinfo +0 -1
@@ -1,16 +1,46 @@
1
1
  import { of } from 'rxjs';
2
- import { catchError, filter, mergeMap } from 'rxjs/operators';
2
+ import { catchError, filter, mergeMap, switchMap } from 'rxjs/operators';
3
+ import { convertToPeriod, toMonthYearPeriodId, } from '../../../../commonStateTypes/timePeriod';
4
+ import { getCurrentTenant } from '../../../../entity/tenant/tenantSelector';
3
5
  import { createZeniAPIStatus, isSuccessResponse, } from '../../../../responsePayload';
4
6
  import { toBatchListItem, } from '../../payload/missingReceiptsPayload';
5
7
  import { fetchBulkUploadBatches, fetchBulkUploadBatchesFailure, fetchBulkUploadBatchesSuccess, } from '../../reducers/missingReceiptsViewReducer';
6
- export const fetchBulkUploadBatchesEpic = (actions$, _state$, zeniAPI) => actions$.pipe(filter(fetchBulkUploadBatches.match), mergeMap(() => zeniAPI
7
- .getJSON(`${zeniAPI.apiEndPoints.accountMicroServiceBaseUrl}/1.0/receipts/batches?days=30`)
8
- .pipe(mergeMap((response) => {
9
- if (isSuccessResponse(response) && response.data != null) {
10
- return of(fetchBulkUploadBatchesSuccess(response.data.batches.map(toBatchListItem)));
8
+ export const fetchBulkUploadBatchesEpic = (actions$, state$, zeniAPI) => actions$.pipe(filter(fetchBulkUploadBatches.match), switchMap((action) => {
9
+ const { cacheOverride } = action.payload;
10
+ const state = state$.value;
11
+ const { expenseAutomationViewState: { selectedPeriodByTenantId }, expenseAutomationMissingReceiptsViewState: { bulkUpload }, } = state;
12
+ const currentTenant = getCurrentTenant(state);
13
+ const selectedPeriod = selectedPeriodByTenantId[currentTenant.tenantId];
14
+ const periodId = toMonthYearPeriodId(selectedPeriod);
15
+ const existingBatches = bulkUpload.batchListByPeriod[periodId] ?? [];
16
+ if (cacheOverride !== true && existingBatches.length > 0) {
17
+ return of(fetchBulkUploadBatchesSuccess({
18
+ batchList: existingBatches,
19
+ selectedPeriod,
20
+ }));
11
21
  }
12
- return of(fetchBulkUploadBatchesFailure({ error: response.status }));
13
- }), catchError((error) => of(fetchBulkUploadBatchesFailure({
14
- error: createZeniAPIStatus('Unexpected Error', 'Fetch bulk upload batches errored out: ' +
15
- JSON.stringify(error)),
16
- }))))));
22
+ const period = convertToPeriod(selectedPeriod);
23
+ const queryParam = {
24
+ start_date: period.start,
25
+ end_date: period.end,
26
+ sort_order: 'desc',
27
+ };
28
+ return zeniAPI
29
+ .getJSON(`${zeniAPI.apiEndPoints.communicationAgentMicroServiceBaseUrl}/1.0/batches?query=${encodeURIComponent(JSON.stringify(queryParam))}`)
30
+ .pipe(mergeMap((response) => {
31
+ if (isSuccessResponse(response) && response.data != null) {
32
+ return of(fetchBulkUploadBatchesSuccess({
33
+ batchList: response.data.batches.map(toBatchListItem),
34
+ selectedPeriod,
35
+ }));
36
+ }
37
+ return of(fetchBulkUploadBatchesFailure({
38
+ error: response.status,
39
+ selectedPeriod,
40
+ }));
41
+ }), catchError((error) => of(fetchBulkUploadBatchesFailure({
42
+ error: createZeniAPIStatus('Unexpected Error', 'Fetch bulk upload batches errored out: ' +
43
+ JSON.stringify(error)),
44
+ selectedPeriod,
45
+ }))));
46
+ }));
@@ -1,34 +1,43 @@
1
1
  import { from, of } from 'rxjs';
2
2
  import { catchError, filter, mergeMap, switchMap } from 'rxjs/operators';
3
- import { convertToPeriod } from '../../../../commonStateTypes/timePeriod';
3
+ import { convertToPeriod, toMonthYearPeriodId, } from '../../../../commonStateTypes/timePeriod';
4
+ import { getCurrentTenant } from '../../../../entity/tenant/tenantSelector';
4
5
  import { updateTransactions } from '../../../../entity/transaction/transactionReducer';
5
6
  import { createZeniAPIStatus, isSuccessResponse, } from '../../../../responsePayload';
6
- import { completedTransactionPayloadToSupportedTransactionPayload, } from '../../payload/missingReceiptsPayload';
7
7
  import { fetchCompletedTransactions, fetchCompletedTransactionsFailure, fetchCompletedTransactionsSuccess, } from '../../reducers/missingReceiptsViewReducer';
8
8
  export const fetchCompletedTransactionsEpic = (actions$, state$, zeniAPI) => actions$.pipe(filter(fetchCompletedTransactions.match), switchMap((action) => {
9
9
  const state = state$.value;
10
- const { expenseAutomationMissingReceiptsViewState: { bulkUpload }, } = state;
11
- const { selectedPeriod } = action.payload;
12
- const queryPayload = {};
13
- if (bulkUpload.completedSubTab !== 'all') {
14
- queryPayload.match_type = bulkUpload.completedSubTab;
10
+ const { expenseAutomationViewState: { selectedPeriodByTenantId }, expenseAutomationMissingReceiptsViewState: { bulkUpload }, } = state;
11
+ const currentTenant = getCurrentTenant(state);
12
+ const selectedPeriod = selectedPeriodByTenantId[currentTenant.tenantId];
13
+ const periodId = toMonthYearPeriodId(selectedPeriod);
14
+ const isInitialLoad = action.payload.pageToken == null;
15
+ const existingData = bulkUpload.completedTransactionsByPeriod[periodId];
16
+ if (isInitialLoad && existingData != null && existingData.transactionIds.length > 0) {
17
+ return from([]);
15
18
  }
16
19
  const period = convertToPeriod(selectedPeriod);
17
- queryPayload.start_date = period.start;
18
- queryPayload.end_date = period.end;
19
- if (action.payload.pageToken != null) {
20
- queryPayload.page_token = action.payload.pageToken;
20
+ const queryPayload = {
21
+ start_date: period.start,
22
+ end_date: period.end,
23
+ sort_by: bulkUpload.sortKey,
24
+ sort_order: bulkUpload.sortOrder === 'ascending' ? 'asc' : 'desc',
25
+ page_token: action.payload.pageToken ?? null,
26
+ page_size: 25,
27
+ };
28
+ if (bulkUpload.completedSubTab !== 'all') {
29
+ queryPayload.match_type = bulkUpload.completedSubTab;
21
30
  }
22
31
  const encodedQuery = encodeURIComponent(JSON.stringify(queryPayload));
23
32
  return zeniAPI
24
- .getJSON(`${zeniAPI.apiEndPoints.accountMicroServiceBaseUrl}/1.0/receipt-agent/transactions/completed?query=${encodedQuery}`)
33
+ .getJSON(`${zeniAPI.apiEndPoints.communicationAgentMicroServiceBaseUrl}/1.0/receipt-agent/transactions/completed?query=${encodedQuery}`)
25
34
  .pipe(mergeMap((response) => {
26
35
  if (isSuccessResponse(response) && response.data != null) {
27
- const transactionPayloads = response.data.transactions.map(completedTransactionPayloadToSupportedTransactionPayload);
28
36
  const transactionIds = response.data.transactions.map((t) => t.transaction_id);
29
37
  const updateActions = [];
30
- updateActions.push(updateTransactions(transactionPayloads, (value) => value.transaction_id));
38
+ updateActions.push(updateTransactions(response.data.transactions, (value) => value.transaction_id));
31
39
  updateActions.push(fetchCompletedTransactionsSuccess({
40
+ isInitialLoad,
32
41
  transactionIds,
33
42
  nextPageToken: response.data.next_page_token,
34
43
  totalCount: response.data.total_count,
@@ -0,0 +1,31 @@
1
+ import { concat, of } from 'rxjs';
2
+ import { catchError, filter, mergeMap } from 'rxjs/operators';
3
+ import { toMonthYearPeriodId } from '../../../../commonStateTypes/timePeriod';
4
+ import { getCurrentTenant } from '../../../../entity/tenant/tenantSelector';
5
+ import { createZeniAPIStatus } from '../../../../responsePayload';
6
+ import { fetchMoreBatchDetails, fetchMoreBatchDetailsComplete, fetchMoreBatchDetailsFailure, } from '../../reducers/missingReceiptsViewReducer';
7
+ import { fetchBatchDetailsByIds } from './fetchMultipleBatchDetailsEpic';
8
+ const MORE_BATCH_PAGE_SIZE = 4;
9
+ export const fetchMoreBatchDetailsEpic = (actions$, state$, zeniAPI) => actions$.pipe(filter(fetchMoreBatchDetails.match), mergeMap(() => {
10
+ const state = state$.value;
11
+ const { expenseAutomationViewState: { selectedPeriodByTenantId }, expenseAutomationMissingReceiptsViewState: { bulkUpload }, } = state;
12
+ const currentTenant = getCurrentTenant(state);
13
+ const selectedPeriod = selectedPeriodByTenantId[currentTenant.tenantId];
14
+ if (selectedPeriod == null) {
15
+ return of(fetchMoreBatchDetailsComplete());
16
+ }
17
+ const periodId = toMonthYearPeriodId(selectedPeriod);
18
+ const batchList = bulkUpload.batchListByPeriod[periodId] ?? [];
19
+ const unfetchedBatchIds = batchList
20
+ .filter((batch) => batch.status === 'completed' &&
21
+ bulkUpload.batchDetailsById[batch.batchId] == null)
22
+ .map((batch) => batch.batchId);
23
+ if (unfetchedBatchIds.length === 0) {
24
+ return of(fetchMoreBatchDetailsComplete());
25
+ }
26
+ const nextPage = unfetchedBatchIds.slice(0, MORE_BATCH_PAGE_SIZE);
27
+ return concat(fetchBatchDetailsByIds(nextPage, zeniAPI), of(fetchMoreBatchDetailsComplete())).pipe(catchError((error) => of(fetchMoreBatchDetailsFailure({
28
+ error: createZeniAPIStatus('Unexpected Error', 'Fetch more batch details errored out: ' +
29
+ JSON.stringify(error)),
30
+ }))));
31
+ }));
@@ -0,0 +1,39 @@
1
+ import { EMPTY, from } from 'rxjs';
2
+ import { catchError, filter, mergeMap } from 'rxjs/operators';
3
+ import { updateTransactions } from '../../../../entity/transaction/transactionReducer';
4
+ import { createZeniAPIStatus, isSuccessResponse, } from '../../../../responsePayload';
5
+ import { extractTransactionPayloadsFromBatchFiles, toBatchDetails, } from '../../payload/missingReceiptsPayload';
6
+ import { fetchBulkUploadBatchesSuccess, storeBatchDetails, } from '../../reducers/missingReceiptsViewReducer';
7
+ const INITIAL_BATCH_PAGE_SIZE = 4;
8
+ export function fetchBatchDetailsByIds(batchIds, zeniAPI) {
9
+ if (batchIds.length === 0) {
10
+ return EMPTY;
11
+ }
12
+ return from(batchIds).pipe(mergeMap((batchId) => zeniAPI
13
+ .getJSON(`${zeniAPI.apiEndPoints.communicationAgentMicroServiceBaseUrl}/1.0/batches/${batchId}`)
14
+ .pipe(mergeMap((response) => {
15
+ if (isSuccessResponse(response) && response.data != null) {
16
+ const actions = [];
17
+ const transactionPayloads = extractTransactionPayloadsFromBatchFiles(response.data.files);
18
+ if (transactionPayloads.length > 0) {
19
+ actions.push(updateTransactions(transactionPayloads, (value) => value.transaction_id));
20
+ }
21
+ actions.push(storeBatchDetails(toBatchDetails(response.data, zeniAPI.apiEndPoints.fileMicroServiceBaseUrl)));
22
+ return from(actions);
23
+ }
24
+ return EMPTY;
25
+ }), catchError((error) => {
26
+ console.error(`Failed to fetch batch details for ${batchId}:`, createZeniAPIStatus('Unexpected Error', 'Fetch batch details errored out: ' +
27
+ JSON.stringify(error)));
28
+ return EMPTY;
29
+ })), 5));
30
+ }
31
+ export const fetchMultipleBatchDetailsEpic = (actions$, state$, zeniAPI) => actions$.pipe(filter(fetchBulkUploadBatchesSuccess.match), mergeMap((action) => {
32
+ const batchList = action.payload.batchList;
33
+ const { batchDetailsById } = state$.value.expenseAutomationMissingReceiptsViewState.bulkUpload;
34
+ const completedBatchIds = batchList
35
+ .filter((batch) => batch.status === 'completed' &&
36
+ batchDetailsById[batch.batchId] == null)
37
+ .map((batch) => batch.batchId);
38
+ return fetchBatchDetailsByIds(completedBatchIds.slice(0, INITIAL_BATCH_PAGE_SIZE), zeniAPI);
39
+ }));
@@ -1,39 +1,47 @@
1
1
  import { of, timer } from 'rxjs';
2
2
  import { catchError, filter, mergeMap, switchMap, takeUntil, takeWhile, } from 'rxjs/operators';
3
+ import { convertToPeriod } from '../../../../commonStateTypes/timePeriod';
4
+ import { getCurrentTenant } from '../../../../entity/tenant/tenantSelector';
3
5
  import { isSuccessResponse } from '../../../../responsePayload';
4
- import { toBatchStatus, } from '../../payload/missingReceiptsPayload';
5
- import { bulkUploadReceiptsSuccess, clearBulkUpload, fetchBulkUploadBatchDetails, pusherBatchStatusUpdate, updateBulkUploadBatchStatus, } from '../../reducers/missingReceiptsViewReducer';
6
- /**
7
- * Watches batch status using two strategies:
8
- * 1. Pusher (primary): Reacts to `pusherBatchStatusUpdate` dispatched by the UI layer
9
- * when a Pusher event arrives. Immediately fetches full details on completion.
10
- * 2. Polling (fallback): Starts a relaxed 10s poll after upload success as a safety net
11
- * in case the Pusher event is missed. Stops on completion or `clearBulkUpload`.
12
- */
13
- // Pusher-driven: when Pusher delivers a completed status, fetch details
6
+ import { toBatchListItem, } from '../../payload/missingReceiptsPayload';
7
+ import { bulkUploadReceiptsSuccess, clearBulkUpload, fetchBulkUploadBatchDetails, fetchBulkUploadBatchesSuccess, pusherBatchStatusUpdate, } from '../../reducers/missingReceiptsViewReducer';
14
8
  export const pusherBatchStatusCompletionEpic = (actions$) => actions$.pipe(filter(pusherBatchStatusUpdate.match), filter((action) => action.payload.status === 'completed'), mergeMap(() => of(fetchBulkUploadBatchDetails())));
15
- // Polling fallback: relaxed interval as safety net
16
9
  const FALLBACK_POLL_INTERVAL_MS = 10000;
17
- export const pollBulkUploadBatchStatusEpic = (actions$, _state$, zeniAPI) => actions$.pipe(filter(bulkUploadReceiptsSuccess.match), switchMap((action) => {
10
+ export const pollBulkUploadBatchStatusEpic = (actions$, state$, zeniAPI) => actions$.pipe(filter(bulkUploadReceiptsSuccess.match), switchMap((action) => {
18
11
  const { batchId } = action.payload;
19
12
  return timer(0, FALLBACK_POLL_INTERVAL_MS).pipe(takeUntil(actions$.pipe(filter((a) => clearBulkUpload.match(a) ||
20
13
  (pusherBatchStatusUpdate.match(a) &&
21
- a.payload.status === 'completed')))), switchMap(() => zeniAPI
22
- .getJSON(`${zeniAPI.apiEndPoints.accountMicroServiceBaseUrl}/1.0/receipts/batches/${batchId}/status`)
23
- .pipe(mergeMap((response) => {
24
- if (isSuccessResponse(response) && response.data != null) {
25
- const batchStatus = toBatchStatus(response.data);
26
- const actions = [
27
- updateBulkUploadBatchStatus(batchStatus),
28
- ];
29
- if (batchStatus.status === 'completed') {
30
- actions.push(fetchBulkUploadBatchDetails());
14
+ a.payload.status === 'completed')))), switchMap(() => {
15
+ const state = state$.value;
16
+ const { expenseAutomationViewState: { selectedPeriodByTenantId }, } = state;
17
+ const currentTenant = getCurrentTenant(state);
18
+ const selectedPeriod = selectedPeriodByTenantId[currentTenant.tenantId];
19
+ const period = convertToPeriod(selectedPeriod);
20
+ const queryParam = {
21
+ start_date: period.start,
22
+ end_date: period.end,
23
+ sort_order: 'desc',
24
+ };
25
+ return zeniAPI
26
+ .getJSON(`${zeniAPI.apiEndPoints.communicationAgentMicroServiceBaseUrl}/1.0/batches?query=${encodeURIComponent(JSON.stringify(queryParam))}`)
27
+ .pipe(mergeMap((response) => {
28
+ if (isSuccessResponse(response) && response.data != null) {
29
+ const batchList = response.data.batches.map(toBatchListItem);
30
+ const currentBatch = batchList.find((b) => b.batchId === batchId);
31
+ if (currentBatch?.status === 'completed') {
32
+ return [
33
+ fetchBulkUploadBatchesSuccess({
34
+ batchList,
35
+ selectedPeriod,
36
+ }),
37
+ fetchBulkUploadBatchDetails(),
38
+ ];
39
+ }
31
40
  }
32
- return actions;
33
- }
34
- return [];
35
- }), catchError(() => of()))), takeWhile((action) => {
36
- if (fetchBulkUploadBatchDetails.match(action)) {
41
+ return [];
42
+ }), catchError(() => of()));
43
+ }), takeWhile((emittedAction) => {
44
+ if (fetchBulkUploadBatchDetails.match(emittedAction)) {
37
45
  return false;
38
46
  }
39
47
  return true;
@@ -1,14 +1,19 @@
1
+ import { toURL } from '../../../commonPayloadTypes/urlPayload';
1
2
  // -- Converter functions --
2
- export function toBatchFile(payload) {
3
+ export function toBatchFile(payload, filesEndPoint) {
3
4
  return {
4
5
  attachmentId: payload.attachment_id,
5
6
  candidates: payload.candidates?.map((c) => ({
6
7
  transactionId: c.transaction_id,
7
8
  matchScore: c.match_score,
8
9
  })),
10
+ fileDownloadUrl: payload.file_id != null && filesEndPoint != null
11
+ ? `${filesEndPoint}/1.0/files/${payload.file_id}?query=${encodeURIComponent(JSON.stringify({ download_file: true }))}`
12
+ : null,
9
13
  fileId: payload.file_id,
10
14
  filePreviewUrl: payload.file_preview_url,
11
15
  filename: payload.filename,
16
+ matchSource: payload.match_source,
12
17
  matchedTransactionId: payload.matched_transaction?.transaction_id,
13
18
  status: payload.status,
14
19
  };
@@ -39,10 +44,12 @@ export function extractTransactionPayloadsFromBatchFiles(files) {
39
44
  function matchedTransactionPayloadToSupportedTransactionPayload(payload) {
40
45
  return {
41
46
  transaction_id: payload.transaction_id,
42
- transaction_date: payload.transaction_date,
47
+ transaction_type: payload.transaction_type ?? 'expense',
48
+ transaction_date: payload.transaction_date ?? '',
43
49
  currency_code: payload.currency_code,
44
50
  total_amount: payload.amount,
45
51
  vendor_name: payload.vendor_name,
52
+ logo: payload.vendor_logo == null ? undefined : toURL(payload.vendor_logo),
46
53
  };
47
54
  }
48
55
  export function toBatchSummary(payload) {
@@ -54,18 +61,11 @@ export function toBatchSummary(payload) {
54
61
  total: payload.total,
55
62
  };
56
63
  }
57
- export function toBatchStatus(payload) {
58
- return {
59
- batchId: payload.batch_id,
60
- progress: payload.progress,
61
- status: payload.status,
62
- };
63
- }
64
- export function toBatchDetails(payload) {
64
+ export function toBatchDetails(payload, filesEndPoint) {
65
65
  return {
66
66
  batchId: payload.batch_id,
67
67
  createdAt: payload.created_at,
68
- files: payload.files.map(toBatchFile),
68
+ files: payload.files.map((f) => toBatchFile(f, filesEndPoint)),
69
69
  status: payload.status,
70
70
  summary: toBatchSummary(payload.summary),
71
71
  };
@@ -85,7 +85,7 @@ export function toBatchListItem(payload) {
85
85
  export function toManualSearchResult(payload) {
86
86
  return {
87
87
  amount: payload.amount,
88
- currency: payload.currency,
88
+ currencyCode: payload.currency,
89
89
  entityId: payload.entity_id,
90
90
  memo: payload.memo,
91
91
  transactionDate: payload.transaction_date,
@@ -94,71 +94,3 @@ export function toManualSearchResult(payload) {
94
94
  vendorName: payload.vendor_name,
95
95
  };
96
96
  }
97
- function completedTransactionAttachmentToAttachmentPayload(payload) {
98
- return {
99
- attachment_create_time: payload.attachment_create_time ?? '',
100
- attachment_id: payload.attachment_id,
101
- attachment_update_time: payload.attachment_update_time ?? '',
102
- content_type: payload.content_type,
103
- file_checksum: null,
104
- file_download_url: payload.file_download_url,
105
- file_name: payload.file_name,
106
- file_size: payload.file_size,
107
- };
108
- }
109
- function completedTransactionLineToLinePayload(payload) {
110
- return {
111
- amount: payload.amount,
112
- line_description: payload.line_description,
113
- line_detail_type: payload.line_detail_type,
114
- line_id: payload.line_id,
115
- categorization_status: payload.categorization_status,
116
- line_detail: {
117
- account: {
118
- account_id: payload.line_detail.account?.account_id ?? '',
119
- account_name: payload.line_detail.account?.account_name ?? '',
120
- labels: [],
121
- coa_id: payload.line_detail.account?.coa_id,
122
- qbo_id: payload.line_detail.account?.qbo_id,
123
- },
124
- class: {
125
- accounting_class_id: payload.line_detail.class?.accounting_class_id ?? '',
126
- accounting_class_name: payload.line_detail.class?.accounting_class_name ?? '',
127
- accounting_class_number: payload.line_detail.class?.accounting_class_number,
128
- fully_qualified_accounting_class_name: payload.line_detail.class?.fully_qualified_accounting_class_name,
129
- qbo_id: payload.line_detail.class?.qbo_id,
130
- },
131
- line_detail_type: payload.line_detail_type,
132
- customer: payload.line_detail.customer != null
133
- ? {
134
- customer_id: payload.line_detail.customer.customer_id,
135
- customer_name: payload.line_detail.customer.customer_name,
136
- qbo_id: payload.line_detail.customer.qbo_id,
137
- }
138
- : undefined,
139
- vendor: payload.line_detail.vendor != null
140
- ? {
141
- vendor_id: payload.line_detail.vendor.vendor_id,
142
- vendor_name: payload.line_detail.vendor.vendor_name,
143
- }
144
- : undefined,
145
- },
146
- };
147
- }
148
- export function completedTransactionPayloadToSupportedTransactionPayload(payload) {
149
- return {
150
- transaction_id: payload.transaction_id,
151
- transaction_type: payload.transaction_type,
152
- transaction_date: payload.transaction_date,
153
- transaction_type_name: payload.transaction_type_name,
154
- currency_code: payload.currency_code,
155
- currency_symbol: payload.currency_symbol,
156
- total_amount: payload.total_amount,
157
- vendor_id: payload.vendor_id,
158
- vendor_name: payload.vendor_name,
159
- account_id: payload.account_id,
160
- account_name: payload.account_name,
161
- attachments: payload.attachments.map(completedTransactionAttachmentToAttachmentPayload),
162
- lines: payload.lines.map(completedTransactionLineToLinePayload),
163
- };
164
- }
@@ -2,7 +2,8 @@ import { createSlice } from '@reduxjs/toolkit';
2
2
  import { toMonthYearPeriodId } from '../../../commonStateTypes/timePeriod';
3
3
  export const initialBulkUploadState = {
4
4
  batchDetailsById: {},
5
- batchList: [],
5
+ batchDetailsPaginationState: { fetchState: 'Not-Started', error: undefined },
6
+ batchListByPeriod: {},
6
7
  batchListFetchState: { fetchState: 'Not-Started', error: undefined },
7
8
  batchStatusById: {},
8
9
  completedSubTab: 'all',
@@ -175,10 +176,6 @@ const expenseAutomationMissingReceiptsView = createSlice({
175
176
  };
176
177
  draft.bulkUpload.phase = 'idle';
177
178
  },
178
- updateBulkUploadBatchStatus(draft, action) {
179
- const batchStatus = action.payload;
180
- draft.bulkUpload.batchStatusById[batchStatus.batchId] = batchStatus;
181
- },
182
179
  pusherBatchStatusUpdate(draft, action) {
183
180
  const batchStatus = action.payload;
184
181
  draft.bulkUpload.batchStatusById[batchStatus.batchId] = batchStatus;
@@ -196,14 +193,44 @@ const expenseAutomationMissingReceiptsView = createSlice({
196
193
  fetchBulkUploadBatchDetailsFailure(draft, _action) {
197
194
  draft.bulkUpload.phase = 'completed';
198
195
  },
199
- fetchBulkUploadBatches(draft) {
200
- draft.bulkUpload.batchListFetchState = {
196
+ storeBatchDetails(draft, action) {
197
+ const details = action.payload;
198
+ draft.bulkUpload.batchDetailsById[details.batchId] = details;
199
+ },
200
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
201
+ fetchMoreBatchDetails(_draft) {
202
+ _draft.bulkUpload.batchDetailsPaginationState = {
201
203
  fetchState: 'In-Progress',
202
204
  error: undefined,
203
205
  };
204
206
  },
207
+ fetchMoreBatchDetailsComplete(draft) {
208
+ draft.bulkUpload.batchDetailsPaginationState = {
209
+ fetchState: 'Completed',
210
+ error: undefined,
211
+ };
212
+ },
213
+ fetchMoreBatchDetailsFailure(draft, action) {
214
+ draft.bulkUpload.batchDetailsPaginationState = {
215
+ fetchState: 'Error',
216
+ error: action.payload.error,
217
+ };
218
+ },
219
+ fetchBulkUploadBatches: {
220
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
221
+ reducer(draft, _action) {
222
+ draft.bulkUpload.batchListFetchState = {
223
+ fetchState: 'In-Progress',
224
+ error: undefined,
225
+ };
226
+ },
227
+ prepare(cacheOverride = false) {
228
+ return { payload: { cacheOverride } };
229
+ },
230
+ },
205
231
  fetchBulkUploadBatchesSuccess(draft, action) {
206
- draft.bulkUpload.batchList = action.payload;
232
+ const periodId = toMonthYearPeriodId(action.payload.selectedPeriod);
233
+ draft.bulkUpload.batchListByPeriod[periodId] = action.payload.batchList;
207
234
  draft.bulkUpload.batchListFetchState = {
208
235
  fetchState: 'Completed',
209
236
  error: undefined,
@@ -223,8 +250,8 @@ const expenseAutomationMissingReceiptsView = createSlice({
223
250
  error: undefined,
224
251
  };
225
252
  },
226
- prepare(batchId, attachmentId, transactionId) {
227
- return { payload: { batchId, attachmentId, transactionId } };
253
+ prepare(batchId, attachmentId, transactionId, transactionType, fileName) {
254
+ return { payload: { batchId, attachmentId, transactionId, transactionType, fileName } };
228
255
  },
229
256
  },
230
257
  confirmBulkUploadMatchSuccess(draft, action) {
@@ -237,12 +264,18 @@ const expenseAutomationMissingReceiptsView = createSlice({
237
264
  if (details != null) {
238
265
  const file = details.files.find((f) => f.attachmentId === attachmentId);
239
266
  if (file != null) {
267
+ const originalStatus = file.status;
240
268
  file.status = 'matched';
241
269
  file.matchedTransactionId = transactionId;
242
270
  file.candidates = undefined;
271
+ details.summary.matched += 1;
272
+ if (originalStatus === 'no_match') {
273
+ details.summary.noMatch = Math.max(0, details.summary.noMatch - 1);
274
+ }
275
+ else {
276
+ details.summary.possibleMatches = Math.max(0, details.summary.possibleMatches - 1);
277
+ }
243
278
  }
244
- details.summary.matched += 1;
245
- details.summary.possibleMatches = Math.max(0, details.summary.possibleMatches - 1);
246
279
  }
247
280
  },
248
281
  confirmBulkUploadMatchFailure(draft, action) {
@@ -297,43 +330,28 @@ const expenseAutomationMissingReceiptsView = createSlice({
297
330
  // -- Completed Transactions Actions --
298
331
  fetchCompletedTransactions: {
299
332
  reducer(draft, action) {
300
- const periodId = toMonthYearPeriodId(action.payload.selectedPeriod);
301
- const existing = draft.bulkUpload.completedTransactionsByPeriod[periodId];
302
- if (action.payload.pageToken == null) {
303
- draft.bulkUpload.completedTransactionsByPeriod[periodId] = {
304
- fetchState: { fetchState: 'In-Progress', error: undefined },
305
- nextPageToken: null,
306
- totalCount: 0,
307
- transactionIds: [],
308
- };
309
- }
310
- else if (existing != null) {
311
- existing.fetchState = { fetchState: 'In-Progress', error: undefined };
312
- }
313
- else {
314
- draft.bulkUpload.completedTransactionsByPeriod[periodId] = {
315
- fetchState: { fetchState: 'In-Progress', error: undefined },
316
- nextPageToken: null,
317
- totalCount: 0,
318
- transactionIds: [],
319
- };
333
+ const { pageToken } = action.payload;
334
+ if (pageToken != null) {
335
+ const periods = draft.bulkUpload.completedTransactionsByPeriod;
336
+ for (const entry of Object.values(periods)) {
337
+ if (entry.nextPageToken === pageToken) {
338
+ entry.fetchState = {
339
+ fetchState: 'In-Progress',
340
+ error: undefined,
341
+ };
342
+ break;
343
+ }
344
+ }
320
345
  }
321
346
  },
322
- prepare(selectedPeriod, pageToken) {
323
- return { payload: { selectedPeriod, pageToken } };
347
+ prepare(pageToken) {
348
+ return { payload: { pageToken } };
324
349
  },
325
350
  },
326
351
  fetchCompletedTransactionsSuccess(draft, action) {
327
- const { transactionIds, nextPageToken, totalCount, selectedPeriod } = action.payload;
352
+ const { transactionIds, nextPageToken, totalCount, selectedPeriod, isInitialLoad } = action.payload;
328
353
  const periodId = toMonthYearPeriodId(selectedPeriod);
329
- const existing = draft.bulkUpload.completedTransactionsByPeriod[periodId];
330
- if (existing != null) {
331
- existing.transactionIds.push(...transactionIds);
332
- existing.nextPageToken = nextPageToken;
333
- existing.totalCount = totalCount;
334
- existing.fetchState = { fetchState: 'Completed', error: undefined };
335
- }
336
- else {
354
+ if (isInitialLoad) {
337
355
  draft.bulkUpload.completedTransactionsByPeriod[periodId] = {
338
356
  transactionIds,
339
357
  nextPageToken,
@@ -341,6 +359,23 @@ const expenseAutomationMissingReceiptsView = createSlice({
341
359
  fetchState: { fetchState: 'Completed', error: undefined },
342
360
  };
343
361
  }
362
+ else {
363
+ const existing = draft.bulkUpload.completedTransactionsByPeriod[periodId];
364
+ if (existing != null) {
365
+ existing.transactionIds.push(...transactionIds);
366
+ existing.nextPageToken = nextPageToken;
367
+ existing.totalCount = totalCount;
368
+ existing.fetchState = { fetchState: 'Completed', error: undefined };
369
+ }
370
+ else {
371
+ draft.bulkUpload.completedTransactionsByPeriod[periodId] = {
372
+ transactionIds,
373
+ nextPageToken,
374
+ totalCount,
375
+ fetchState: { fetchState: 'Completed', error: undefined },
376
+ };
377
+ }
378
+ }
344
379
  },
345
380
  fetchCompletedTransactionsFailure(draft, action) {
346
381
  const periodId = toMonthYearPeriodId(action.payload.selectedPeriod);
@@ -364,7 +399,7 @@ const expenseAutomationMissingReceiptsView = createSlice({
364
399
  });
365
400
  export const { fetchMissingReceipts, fetchMissingReceiptsSuccess, fetchMissingReceiptsFailure, uploadMissingReceiptSuccess, updateMissingReceiptUploadState, updateMissingReceiptsUIState, markMissingReceiptAsDone, clearExpenseAutomationMissingReceiptsView,
366
401
  // Bulk Upload
367
- bulkUploadReceipts, updateBulkUploadProgress, bulkUploadReceiptsSuccess, bulkUploadReceiptsFailure, updateBulkUploadBatchStatus, pusherBatchStatusUpdate, fetchBulkUploadBatchDetails, fetchBulkUploadBatchDetailsSuccess, fetchBulkUploadBatchDetailsFailure, fetchBulkUploadBatches, fetchBulkUploadBatchesSuccess, fetchBulkUploadBatchesFailure, confirmBulkUploadMatch, confirmBulkUploadMatchSuccess, confirmBulkUploadMatchFailure, setBulkUploadResultsTab, setBulkUploadCompletedSubTab, setBulkUploadSortConfig, clearBulkUpload, searchTransactionsForManualMatch, searchTransactionsForManualMatchSuccess, searchTransactionsForManualMatchFailure, clearManualSearchResults,
402
+ bulkUploadReceipts, updateBulkUploadProgress, bulkUploadReceiptsSuccess, bulkUploadReceiptsFailure, pusherBatchStatusUpdate, fetchBulkUploadBatchDetails, fetchBulkUploadBatchDetailsSuccess, fetchBulkUploadBatchDetailsFailure, storeBatchDetails, fetchMoreBatchDetails, fetchMoreBatchDetailsComplete, fetchMoreBatchDetailsFailure, fetchBulkUploadBatches, fetchBulkUploadBatchesSuccess, fetchBulkUploadBatchesFailure, confirmBulkUploadMatch, confirmBulkUploadMatchSuccess, confirmBulkUploadMatchFailure, setBulkUploadResultsTab, setBulkUploadCompletedSubTab, setBulkUploadSortConfig, clearBulkUpload, searchTransactionsForManualMatch, searchTransactionsForManualMatchSuccess, searchTransactionsForManualMatchFailure, clearManualSearchResults,
368
403
  // Completed Transactions
369
404
  fetchCompletedTransactions, fetchCompletedTransactionsSuccess, fetchCompletedTransactionsFailure, } = expenseAutomationMissingReceiptsView.actions;
370
405
  export default expenseAutomationMissingReceiptsView.reducer;