@zeniai/client-epic-state 5.1.52-beta0RD → 5.1.53-betaRD1

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.
@@ -4,11 +4,99 @@ import { toMonthYearPeriodId } from '../../../commonStateTypes/timePeriod';
4
4
  import { openSnackbar } from '../../../entity/snackbar/snackbarReducer';
5
5
  import { getCurrentTenant } from '../../../entity/tenant/tenantSelector';
6
6
  import { removeTransactionCategorization, updateTransactionIsMiscategorized, } from '../../../entity/transaction/transactionReducer';
7
- import { createZeniAPIStatus, isSuccessStatus, } from '../../../responsePayload';
7
+ import { getSupportedTransactionById } from '../../../entity/transaction/transactionSelector';
8
+ import { createZeniAPIStatus, isSuccessResponse, isSuccessStatus, } from '../../../responsePayload';
8
9
  import { updateStatusForTransactionNotMiscategorizedUpdateForCategorization, updateTotalCountForTransactionCategorization, } from '../../expenseAutomationView/reducers/transactionsViewReducer';
9
10
  import { markTransactionAsNotMiscategorized, updateStatusConsolidatedSaveTransactionDetail, updateStatusForTransactionNotMiscategorizedUpdate, } from '../transactionDetailReducer';
10
11
  import { getTransactionDetailKey } from '../transactionDetailState';
11
12
  export const markTransactionAsNotMiscategorizedEpic = (actions$, state$, zeniAPI) => actions$.pipe(filter(markTransactionAsNotMiscategorized.match), mergeMap((action) => {
13
+ if (action.payload.isExpenseAutomationEnabled === true) {
14
+ const transactionKey = getTransactionDetailKey(action.payload.transactionId);
15
+ const transaction = getSupportedTransactionById(state$.value.transactionState, transactionKey);
16
+ const lineIds = (action.payload.lineIds?.length ?? 0) > 0
17
+ ? action.payload.lineIds
18
+ : (transaction?.lines ?? []).map((line) => line.id);
19
+ return zeniAPI
20
+ .putAndGetJSON(`${zeniAPI.apiEndPoints.accountMicroServiceBaseUrl}/1.0/expense-automation/mark-as-reviewed`, {
21
+ transactions_reviewed: [
22
+ {
23
+ transaction_id: action.payload.transactionId.id,
24
+ line_ids: lineIds,
25
+ },
26
+ ],
27
+ })
28
+ .pipe(mergeMap((response) => {
29
+ if (isSuccessResponse(response)) {
30
+ const actions = [];
31
+ actions.push(updateStatusForTransactionNotMiscategorizedUpdate(action.payload.transactionId, 'Completed', undefined));
32
+ const txnId = action.payload.transactionId.id;
33
+ const reviewedFailed = response.data?.transactions_reviewed_failed ?? [];
34
+ const isTransactionFailed = reviewedFailed.some((t) => t.transaction_id === txnId);
35
+ if (!isTransactionFailed) {
36
+ actions.push(updateTransactionIsMiscategorized(transactionKey, false));
37
+ const { expenseAutomationViewState, expenseAutomationTransactionsViewState, } = state$.value;
38
+ const { transactionCategorizationView, selectedTransactionCategorizationTab, } = expenseAutomationTransactionsViewState;
39
+ const currentTenantId = getCurrentTenant(state$.value)?.tenantId;
40
+ const { selectedPeriodByTenantId } = expenseAutomationViewState;
41
+ const selectedPeriod = currentTenantId != null
42
+ ? selectedPeriodByTenantId[currentTenantId]
43
+ : undefined;
44
+ const autoTabLineItems = transactionCategorizationView.autoCategorized
45
+ .transactionReviewLocalDataById[txnId]
46
+ ?.transactionReviewLocalData.tabSpecificLineItems
47
+ ?.autoCategorized ?? [];
48
+ const hasAutoLines = autoTabLineItems.length > 0;
49
+ const { uiState } = transactionCategorizationView[selectedTransactionCategorizationTab];
50
+ const removeIdsFromPeriod = selectedTransactionCategorizationTab === 'review'
51
+ ? [txnId]
52
+ : hasAutoLines
53
+ ? []
54
+ : [txnId];
55
+ if (selectedPeriod != null) {
56
+ actions.push(updateStatusForTransactionNotMiscategorizedUpdateForCategorization(selectedTransactionCategorizationTab, [txnId], removeIdsFromPeriod, selectedPeriod, 'Completed'));
57
+ }
58
+ if (!hasAutoLines) {
59
+ actions.push(removeTransactionCategorization({
60
+ transactionIds: [txnId],
61
+ }));
62
+ }
63
+ if (removeIdsFromPeriod.length > 0 &&
64
+ selectedPeriod != null) {
65
+ const totalCountByPeriod = uiState.totalCount[toMonthYearPeriodId(selectedPeriod)];
66
+ const safeTotalCount = totalCountByPeriod ?? 0;
67
+ const updatedTotalCount = Math.max(0, safeTotalCount - 1);
68
+ actions.push(updateTotalCountForTransactionCategorization({
69
+ selectedPeriod,
70
+ selectedTab: selectedTransactionCategorizationTab,
71
+ totalCount: updatedTotalCount,
72
+ }));
73
+ }
74
+ }
75
+ actions.push(updateStatusConsolidatedSaveTransactionDetail({
76
+ transactionId: action.payload.transactionId,
77
+ fetchState: 'Completed',
78
+ }));
79
+ if (action.payload.transactionSaveUpdates.anyBulkUpdates === true) {
80
+ actions.push(openSnackbar({
81
+ messageSection: 'transactionDetails_updatingPastTransactions',
82
+ messageText: 'success',
83
+ type: 'success',
84
+ }));
85
+ }
86
+ else {
87
+ actions.push(openSnackbar({
88
+ messageSection: 'transactionDetails_transactionUpdated',
89
+ messageText: 'success',
90
+ type: 'success',
91
+ }));
92
+ }
93
+ return from(actions);
94
+ }
95
+ else {
96
+ return of(updateStatusForTransactionNotMiscategorizedUpdate(action.payload.transactionId, 'Error', response.status));
97
+ }
98
+ }), catchError((error) => of(updateStatusForTransactionNotMiscategorizedUpdate(action.payload.transactionId, 'Error', createZeniAPIStatus('Unexpected Error', `Transaction mark as reviewed REST API call errored out ${error}`)))));
99
+ }
12
100
  return zeniAPI
13
101
  .putAndGetJSON(`${zeniAPI.apiEndPoints.accountMicroServiceBaseUrl}/1.0/transactions/${action.payload.transactionId.id}/mark-miscategorized-as-correct`, preparePayload(action.payload.transactionId))
14
102
  .pipe(mergeMap((response) => {
@@ -16,44 +104,7 @@ export const markTransactionAsNotMiscategorizedEpic = (actions$, state$, zeniAPI
16
104
  const actions = [];
17
105
  actions.push(updateStatusForTransactionNotMiscategorizedUpdate(action.payload.transactionId, 'Completed', undefined));
18
106
  const transactionKey = getTransactionDetailKey(action.payload.transactionId);
19
- // Mark this transaction as not miscategorized anymore
20
107
  actions.push(updateTransactionIsMiscategorized(transactionKey, false));
21
- if (action.payload.isExpenseAutomationEnabled === true) {
22
- const { expenseAutomationViewState, expenseAutomationTransactionsViewState, } = state$.value;
23
- const { transactionCategorizationView, selectedTransactionCategorizationTab, } = expenseAutomationTransactionsViewState;
24
- const currentTenant = getCurrentTenant(state$.value);
25
- const { selectedPeriodByTenantId } = expenseAutomationViewState;
26
- const selectedPeriod = selectedPeriodByTenantId[currentTenant.tenantId];
27
- const txnId = action.payload.transactionId.id;
28
- const autoTabLineItems = transactionCategorizationView.autoCategorized
29
- .transactionReviewLocalDataById[txnId]
30
- ?.transactionReviewLocalData.tabSpecificLineItems
31
- .autoCategorized ?? [];
32
- const hasAutoLines = autoTabLineItems.length > 0;
33
- const { uiState } = transactionCategorizationView[selectedTransactionCategorizationTab];
34
- const removeIdsFromPeriod = selectedTransactionCategorizationTab === 'review'
35
- ? [txnId]
36
- : hasAutoLines
37
- ? []
38
- : [txnId];
39
- actions.push(updateStatusForTransactionNotMiscategorizedUpdateForCategorization(selectedTransactionCategorizationTab, [txnId], removeIdsFromPeriod, selectedPeriod, 'Completed'));
40
- if (!hasAutoLines) {
41
- actions.push(removeTransactionCategorization({
42
- transactionIds: [txnId],
43
- }));
44
- }
45
- if (removeIdsFromPeriod.length > 0) {
46
- const totalCountByPeriod = uiState.totalCount[toMonthYearPeriodId(selectedPeriod)];
47
- const updatedTotalCount = totalCountByPeriod === 0
48
- ? totalCountByPeriod
49
- : totalCountByPeriod - 1;
50
- actions.push(updateTotalCountForTransactionCategorization({
51
- selectedPeriod,
52
- selectedTab: selectedTransactionCategorizationTab,
53
- totalCount: updatedTotalCount,
54
- }));
55
- }
56
- }
57
108
  actions.push(updateStatusConsolidatedSaveTransactionDetail({
58
109
  transactionId: action.payload.transactionId,
59
110
  fetchState: 'Completed',
@@ -66,7 +117,6 @@ export const markTransactionAsNotMiscategorizedEpic = (actions$, state$, zeniAPI
66
117
  }));
67
118
  }
68
119
  else {
69
- //Finally everything done for this transaction
70
120
  actions.push(openSnackbar({
71
121
  messageSection: 'transactionDetails_transactionUpdated',
72
122
  messageText: 'success',
@@ -3,6 +3,7 @@ import { from, of } from 'rxjs';
3
3
  import { catchError, filter, mergeMap } from 'rxjs/operators';
4
4
  import { toRecommendationPayload } from '../../../commonPayloadTypes/recommendationPayload';
5
5
  import { openSnackbar } from '../../../entity/snackbar/snackbarReducer';
6
+ import { getIsAccountingClassesEnabled, getIsAccountingProjectsEnabled, } from '../../../entity/tenant/tenantSelector';
6
7
  import { toCustomerTransactionPayload } from '../../../entity/transaction/payloadTypes/customerTransactionPayload';
7
8
  import { toTransferTransactionPayload } from '../../../entity/transaction/payloadTypes/otherTransactionPayload';
8
9
  import { toTransactionID } from '../../../entity/transaction/payloadTypes/transactionIDPayload';
@@ -10,7 +11,7 @@ import { injectCOTFlagsIntoTransactionPayload, toTransactionPayload, } from '../
10
11
  import { toVendorTransactionPayload, toVendorTransactionWithCustomerPayload, } from '../../../entity/transaction/payloadTypes/vendorTransactionPayload';
11
12
  import { isCustomerTransaction, } from '../../../entity/transaction/stateTypes/customerTransaction';
12
13
  import { isTransferTransaction, } from '../../../entity/transaction/stateTypes/otherTransaction';
13
- import { toJEAndDepositTransactionTypeStrict, toVendorTransactionTypeStrict, } from '../../../entity/transaction/stateTypes/transactionType';
14
+ import { toJEAndDepositTransactionTypeStrict } from '../../../entity/transaction/stateTypes/transactionType';
14
15
  import { isVendorTransaction, } from '../../../entity/transaction/stateTypes/vendorTransaction';
15
16
  import toSupportedTransaction from '../../../entity/transaction/toSupportedTransaction';
16
17
  import { getTransactionWithCOT } from '../../../entity/transaction/transactionHelper';
@@ -19,13 +20,12 @@ import { getSupportedTransactionById } from '../../../entity/transaction/transac
19
20
  import { updateVendors } from '../../../entity/vendor/vendorReducer';
20
21
  import { getVendorByVendorId } from '../../../entity/vendor/vendorSelector';
21
22
  import { createZeniAPIStatus, isActionNotAllowed, isSuccessResponse, } from '../../../responsePayload';
22
- import { getIsAccountingClassesEnabled, getIsAccountingProjectsEnabled, } from '../../../entity/tenant/tenantSelector';
23
- import { getUncategorizedAccounts } from '../../accountList/accountListSelector';
23
+ import { getUncategorizedAccounts, } from '../../accountList/accountListSelector';
24
24
  import { getPendingReviewLineIdsFromTransaction } from '../../expenseAutomationView/helpers/transactionCategorizationLocalDataHelper';
25
25
  import { syncTransactionCategorizationFromDetailSave } from '../../expenseAutomationView/reducers/transactionsViewReducer';
26
+ import { isAccountUncategorized } from '../transactionDetailLocalDataHelper';
26
27
  import { initializeTransactionDetailLocalData, markTransactionAsNotMiscategorized, updateStatusConsolidatedSaveTransactionDetail, updateTransactionDetail, updateTransactionDetailSaveStatus, updateTransactionStateIfUpdateActionNotAllowed, } from '../transactionDetailReducer';
27
28
  import { getTransactionDetailKey, } from '../transactionDetailState';
28
- import { isTransactionUncategorized } from './saveTransactionHelper';
29
29
  export const updateTransactionDetailEpic = (actions$, state$, zeniAPI) => actions$.pipe(filter(updateTransactionDetail.match), mergeMap((action) => {
30
30
  const { transactionState, vendorState, transactionDetailState } = state$.value;
31
31
  const isAccountingProjectsEnabled = getIsAccountingProjectsEnabled(state$.value);
@@ -36,24 +36,6 @@ export const updateTransactionDetailEpic = (actions$, state$, zeniAPI) => action
36
36
  if (isSuccessResponse(response) && response.data != null) {
37
37
  const transactionIdResp = toTransactionID(response.data.transaction);
38
38
  const transactionDetailKeyForResponse = getTransactionDetailKey(transactionIdResp);
39
- const tranDetail = recordGet(transactionDetailState.transactionDetailById, transactionDetailKeyForResponse, undefined);
40
- const tranVendorUpdates = tranDetail?.transactionDetailLocalData.vendorUpdates;
41
- let vendorUpdates = undefined;
42
- if (tranVendorUpdates?.qboId != null) {
43
- const vendor = getVendorByVendorId(vendorState, tranVendorUpdates.qboId);
44
- if (vendor != null) {
45
- vendorUpdates = {
46
- id: vendor?.qboId,
47
- name: vendor?.name,
48
- };
49
- }
50
- else if (tranVendorUpdates.name != null) {
51
- vendorUpdates = {
52
- id: tranVendorUpdates.qboId,
53
- name: tranVendorUpdates.name,
54
- };
55
- }
56
- }
57
39
  const actions = [];
58
40
  if (response.data.vendors != null) {
59
41
  actions.push(updateVendors(response.data.vendors, 'merge'));
@@ -66,30 +48,14 @@ export const updateTransactionDetailEpic = (actions$, state$, zeniAPI) => action
66
48
  vendorId: response.data.vendors?.[0]?.vendor_id,
67
49
  }));
68
50
  actions.push(updateTransactionDetailSaveStatus(transactionIdResp, 'Completed'));
69
- let callMarkTransactionAsNotMiscategorized = true;
70
- // NOTE: If tran is vendorType OR JE OR Deposit transaction and there is no vendor chosen on this tranaction while saving,
71
- // then we need to skip mark-miscategorized-as-correct api call
72
- if (toVendorTransactionTypeStrict(response.data.transaction.transaction_type) != null ||
73
- toJEAndDepositTransactionTypeStrict(response.data.transaction.transaction_type) != null) {
74
- const isAccountingClassesEnabled = getIsAccountingClassesEnabled(state$.value);
75
- const isAnyClassMissing = isAccountingClassesEnabled &&
76
- checkIfClassMissingForAnyLineItem(response.data.transaction);
77
- const isVendorOrCustomerMissingInAnyLine = toJEAndDepositTransactionTypeStrict(response.data.transaction.transaction_type) != null &&
78
- checkIfVendorOrCustomerMissingForAnyLineItem(response.data.transaction);
79
- if ((vendorUpdates?.id == null &&
80
- isVendorOrCustomerMissingInAnyLine == true) ||
81
- isAnyClassMissing == true) {
82
- callMarkTransactionAsNotMiscategorized = false;
83
- }
84
- }
85
51
  const uncategorizedIncomeExpense = getUncategorizedAccounts(state$.value.accountState, state$.value.accountListState, 'accountList');
86
52
  const transaction = toSupportedTransaction(response.data.transaction);
87
- const isTransactionUnCategorized = isTransactionUncategorized(transaction, uncategorizedIncomeExpense);
53
+ const isAccountingClassesEnabled = getIsAccountingClassesEnabled(state$.value);
54
+ const eligibleLineIds = getLineIdsEligibleForReview(transaction, uncategorizedIncomeExpense, isAccountingClassesEnabled, action.payload.isExpenseAutomationEnabled);
88
55
  if (action.payload.transactionSaveUpdates.markAsMiscatUpdates ===
89
56
  true &&
90
- callMarkTransactionAsNotMiscategorized === true &&
91
- isTransactionUnCategorized === false) {
92
- actions.push(markTransactionAsNotMiscategorized(transactionIdResp, action.payload.transactionSaveUpdates, action.payload.isExpenseAutomationEnabled));
57
+ eligibleLineIds.length > 0) {
58
+ actions.push(markTransactionAsNotMiscategorized(transactionIdResp, action.payload.transactionSaveUpdates, action.payload.isExpenseAutomationEnabled, eligibleLineIds));
93
59
  }
94
60
  else {
95
61
  actions.push(updateStatusConsolidatedSaveTransactionDetail({
@@ -299,31 +265,58 @@ export const toTransactionUpdatePayload = (updates, detail, cotTransactionTracki
299
265
  // Inject COT flags into the payload
300
266
  return injectCOTFlagsIntoTransactionPayload(basePayload, transaction);
301
267
  };
302
- const checkIfClassMissingForAnyLineItem = (transactionPayload) => {
303
- let isAnyClassMissing = false;
304
- if (transactionPayload.lines != null) {
305
- transactionPayload.lines.forEach((line) => {
306
- if (line.line_detail?.class
307
- ?.accounting_class_id == null) {
308
- isAnyClassMissing = true;
268
+ const getLineIdsEligibleForReview = (transaction, uncategorizedIncomeExpense, isAccountingClassesEnabled, isExpenseAutomationEnabled) => {
269
+ // For non-EA, any line missing a required field (class when enabled, or entity on JE/Deposit)
270
+ // suppresses the entire call. EA keeps the per-line model, returning only eligible line IDs.
271
+ if (!isExpenseAutomationEnabled) {
272
+ const isApplicableType = isVendorTransaction(transaction) ||
273
+ toJEAndDepositTransactionTypeStrict(transaction.type) != null;
274
+ if (isApplicableType) {
275
+ const lines = (transaction.lines ?? []).filter((line) => line.type === 'transaction_with_account_and_class_line' ||
276
+ line.type === 'journal_entry_transaction_line' ||
277
+ line.type === 'transaction_with_product_or_service_line');
278
+ if (isAccountingClassesEnabled) {
279
+ const anyLineMissingClass = lines.some((line) => (line.class?.classId ??
280
+ '') === '');
281
+ if (anyLineMissingClass) {
282
+ return [];
283
+ }
309
284
  }
310
- });
311
- }
312
- return isAnyClassMissing;
313
- };
314
- const checkIfVendorOrCustomerMissingForAnyLineItem = (transactionPayload) => {
315
- let isAnyVendorOrCustomerMissing = false;
316
- if (transactionPayload.lines != null) {
317
- transactionPayload.lines.forEach((line) => {
318
- if (line.line_detail
319
- ?.vendor == null &&
320
- line.line_detail
321
- ?.customer == null) {
322
- isAnyVendorOrCustomerMissing = true;
285
+ if (toJEAndDepositTransactionTypeStrict(transaction.type) != null) {
286
+ const anyLineMissingEntity = lines.some((line) => {
287
+ const tl = line;
288
+ return tl.vendor == null && tl.customer == null;
289
+ });
290
+ if (anyLineMissingEntity) {
291
+ return [];
292
+ }
323
293
  }
324
- });
294
+ }
325
295
  }
326
- return isAnyVendorOrCustomerMissing;
296
+ return (transaction.lines ?? [])
297
+ .filter((line) => {
298
+ if (line.type !== 'transaction_with_account_and_class_line' &&
299
+ line.type !== 'journal_entry_transaction_line' &&
300
+ line.type !== 'transaction_with_product_or_service_line') {
301
+ return false;
302
+ }
303
+ const typedLine = line;
304
+ const hasCategory = !isAccountUncategorized(uncategorizedIncomeExpense, typedLine.account?.qboId);
305
+ // class is always a ClassBase object (never null) because the payload mapper
306
+ // calls mapClassBasePayloadToClassBase(payload ?? {}) which returns {} with classId:''.
307
+ // A real class is indicated by a non-empty classId.
308
+ const hasClass = !isAccountingClassesEnabled || (typedLine.class?.classId ?? '') !== '';
309
+ const hasAmount = (typedLine.amount?.amount ?? 0) !== 0;
310
+ // JE lines and Deposit transactions require vendor or customer before being eligible for review.
311
+ const isJEOrDepositContext = line.type === 'journal_entry_transaction_line' ||
312
+ toJEAndDepositTransactionTypeStrict(transaction.type) != null;
313
+ const hasEntity = !isJEOrDepositContext ||
314
+ typedLine.vendor != null ||
315
+ typedLine.customer != null;
316
+ return hasCategory && hasClass && hasAmount && hasEntity;
317
+ })
318
+ .map((line) => line.id)
319
+ .filter(Boolean);
327
320
  };
328
321
  // Converts a locally-created line item (from edit mode) into the wire shape
329
322
  // expected by the categorize endpoint. Sending id='' signals the backend to
@@ -755,6 +755,7 @@ export const setEntityRecommendationForLineIdInLocalData = (transaction, transac
755
755
  latestUpdatedLineWithEntity: transactionDetailLocalData.latestUpdatedLineWithEntity,
756
756
  vendorBulkUpdateCategoryClassRecommendationsFetchStatus: transactionDetailLocalData.vendorBulkUpdateCategoryClassRecommendationsFetchStatus,
757
757
  selectedEntity: transactionDetailLocalData.selectedEntity,
758
+ editMode: transactionDetailLocalData.editMode,
758
759
  };
759
760
  return newLocalData;
760
761
  };
@@ -808,6 +809,7 @@ export const removeEntityRecommendationForLineIdInLocalData = (transaction, tran
808
809
  latestSelectedEntityOnLineItem: transactionDetailLocalData.latestSelectedEntityOnLineItem,
809
810
  latestUpdatedLineWithEntity: transactionDetailLocalData.latestUpdatedLineWithEntity,
810
811
  selectedEntity: transactionDetailLocalData.selectedEntity,
812
+ editMode: transactionDetailLocalData.editMode,
811
813
  };
812
814
  return newLocalData;
813
815
  };
@@ -827,7 +829,8 @@ export const getChangedLineItemCountFromLocalData = (transaction, transactionLoc
827
829
  const accountLine = line;
828
830
  hasCategoryChanged =
829
831
  accountLineLocal.account?.qboId !== accountLine.account.qboId &&
830
- accountLineLocal.account?.accountId !== accountLine.account.accountId;
832
+ accountLineLocal.account?.accountId !==
833
+ accountLine.account.accountId;
831
834
  hasClassChanged =
832
835
  (accountLineLocal.class?.qboId != null ||
833
836
  accountLine.class?.qboId != null) &&
@@ -190,12 +190,13 @@ const transactionDetail = createSlice({
190
190
  };
191
191
  }
192
192
  },
193
- prepare(transactionId, transactionSaveUpdates, isExpenseAutomationEnabled) {
193
+ prepare(transactionId, transactionSaveUpdates, isExpenseAutomationEnabled, lineIds) {
194
194
  return {
195
195
  payload: {
196
196
  transactionId,
197
197
  transactionSaveUpdates,
198
198
  isExpenseAutomationEnabled,
199
+ lineIds,
199
200
  },
200
201
  };
201
202
  },
@@ -7,11 +7,99 @@ const timePeriod_1 = require("../../../commonStateTypes/timePeriod");
7
7
  const snackbarReducer_1 = require("../../../entity/snackbar/snackbarReducer");
8
8
  const tenantSelector_1 = require("../../../entity/tenant/tenantSelector");
9
9
  const transactionReducer_1 = require("../../../entity/transaction/transactionReducer");
10
+ const transactionSelector_1 = require("../../../entity/transaction/transactionSelector");
10
11
  const responsePayload_1 = require("../../../responsePayload");
11
12
  const transactionsViewReducer_1 = require("../../expenseAutomationView/reducers/transactionsViewReducer");
12
13
  const transactionDetailReducer_1 = require("../transactionDetailReducer");
13
14
  const transactionDetailState_1 = require("../transactionDetailState");
14
15
  const markTransactionAsNotMiscategorizedEpic = (actions$, state$, zeniAPI) => actions$.pipe((0, operators_1.filter)(transactionDetailReducer_1.markTransactionAsNotMiscategorized.match), (0, operators_1.mergeMap)((action) => {
16
+ if (action.payload.isExpenseAutomationEnabled === true) {
17
+ const transactionKey = (0, transactionDetailState_1.getTransactionDetailKey)(action.payload.transactionId);
18
+ const transaction = (0, transactionSelector_1.getSupportedTransactionById)(state$.value.transactionState, transactionKey);
19
+ const lineIds = (action.payload.lineIds?.length ?? 0) > 0
20
+ ? action.payload.lineIds
21
+ : (transaction?.lines ?? []).map((line) => line.id);
22
+ return zeniAPI
23
+ .putAndGetJSON(`${zeniAPI.apiEndPoints.accountMicroServiceBaseUrl}/1.0/expense-automation/mark-as-reviewed`, {
24
+ transactions_reviewed: [
25
+ {
26
+ transaction_id: action.payload.transactionId.id,
27
+ line_ids: lineIds,
28
+ },
29
+ ],
30
+ })
31
+ .pipe((0, operators_1.mergeMap)((response) => {
32
+ if ((0, responsePayload_1.isSuccessResponse)(response)) {
33
+ const actions = [];
34
+ actions.push((0, transactionDetailReducer_1.updateStatusForTransactionNotMiscategorizedUpdate)(action.payload.transactionId, 'Completed', undefined));
35
+ const txnId = action.payload.transactionId.id;
36
+ const reviewedFailed = response.data?.transactions_reviewed_failed ?? [];
37
+ const isTransactionFailed = reviewedFailed.some((t) => t.transaction_id === txnId);
38
+ if (!isTransactionFailed) {
39
+ actions.push((0, transactionReducer_1.updateTransactionIsMiscategorized)(transactionKey, false));
40
+ const { expenseAutomationViewState, expenseAutomationTransactionsViewState, } = state$.value;
41
+ const { transactionCategorizationView, selectedTransactionCategorizationTab, } = expenseAutomationTransactionsViewState;
42
+ const currentTenantId = (0, tenantSelector_1.getCurrentTenant)(state$.value)?.tenantId;
43
+ const { selectedPeriodByTenantId } = expenseAutomationViewState;
44
+ const selectedPeriod = currentTenantId != null
45
+ ? selectedPeriodByTenantId[currentTenantId]
46
+ : undefined;
47
+ const autoTabLineItems = transactionCategorizationView.autoCategorized
48
+ .transactionReviewLocalDataById[txnId]
49
+ ?.transactionReviewLocalData.tabSpecificLineItems
50
+ ?.autoCategorized ?? [];
51
+ const hasAutoLines = autoTabLineItems.length > 0;
52
+ const { uiState } = transactionCategorizationView[selectedTransactionCategorizationTab];
53
+ const removeIdsFromPeriod = selectedTransactionCategorizationTab === 'review'
54
+ ? [txnId]
55
+ : hasAutoLines
56
+ ? []
57
+ : [txnId];
58
+ if (selectedPeriod != null) {
59
+ actions.push((0, transactionsViewReducer_1.updateStatusForTransactionNotMiscategorizedUpdateForCategorization)(selectedTransactionCategorizationTab, [txnId], removeIdsFromPeriod, selectedPeriod, 'Completed'));
60
+ }
61
+ if (!hasAutoLines) {
62
+ actions.push((0, transactionReducer_1.removeTransactionCategorization)({
63
+ transactionIds: [txnId],
64
+ }));
65
+ }
66
+ if (removeIdsFromPeriod.length > 0 &&
67
+ selectedPeriod != null) {
68
+ const totalCountByPeriod = uiState.totalCount[(0, timePeriod_1.toMonthYearPeriodId)(selectedPeriod)];
69
+ const safeTotalCount = totalCountByPeriod ?? 0;
70
+ const updatedTotalCount = Math.max(0, safeTotalCount - 1);
71
+ actions.push((0, transactionsViewReducer_1.updateTotalCountForTransactionCategorization)({
72
+ selectedPeriod,
73
+ selectedTab: selectedTransactionCategorizationTab,
74
+ totalCount: updatedTotalCount,
75
+ }));
76
+ }
77
+ }
78
+ actions.push((0, transactionDetailReducer_1.updateStatusConsolidatedSaveTransactionDetail)({
79
+ transactionId: action.payload.transactionId,
80
+ fetchState: 'Completed',
81
+ }));
82
+ if (action.payload.transactionSaveUpdates.anyBulkUpdates === true) {
83
+ actions.push((0, snackbarReducer_1.openSnackbar)({
84
+ messageSection: 'transactionDetails_updatingPastTransactions',
85
+ messageText: 'success',
86
+ type: 'success',
87
+ }));
88
+ }
89
+ else {
90
+ actions.push((0, snackbarReducer_1.openSnackbar)({
91
+ messageSection: 'transactionDetails_transactionUpdated',
92
+ messageText: 'success',
93
+ type: 'success',
94
+ }));
95
+ }
96
+ return (0, rxjs_1.from)(actions);
97
+ }
98
+ else {
99
+ return (0, rxjs_1.of)((0, transactionDetailReducer_1.updateStatusForTransactionNotMiscategorizedUpdate)(action.payload.transactionId, 'Error', response.status));
100
+ }
101
+ }), (0, operators_1.catchError)((error) => (0, rxjs_1.of)((0, transactionDetailReducer_1.updateStatusForTransactionNotMiscategorizedUpdate)(action.payload.transactionId, 'Error', (0, responsePayload_1.createZeniAPIStatus)('Unexpected Error', `Transaction mark as reviewed REST API call errored out ${error}`)))));
102
+ }
15
103
  return zeniAPI
16
104
  .putAndGetJSON(`${zeniAPI.apiEndPoints.accountMicroServiceBaseUrl}/1.0/transactions/${action.payload.transactionId.id}/mark-miscategorized-as-correct`, preparePayload(action.payload.transactionId))
17
105
  .pipe((0, operators_1.mergeMap)((response) => {
@@ -19,44 +107,7 @@ const markTransactionAsNotMiscategorizedEpic = (actions$, state$, zeniAPI) => ac
19
107
  const actions = [];
20
108
  actions.push((0, transactionDetailReducer_1.updateStatusForTransactionNotMiscategorizedUpdate)(action.payload.transactionId, 'Completed', undefined));
21
109
  const transactionKey = (0, transactionDetailState_1.getTransactionDetailKey)(action.payload.transactionId);
22
- // Mark this transaction as not miscategorized anymore
23
110
  actions.push((0, transactionReducer_1.updateTransactionIsMiscategorized)(transactionKey, false));
24
- if (action.payload.isExpenseAutomationEnabled === true) {
25
- const { expenseAutomationViewState, expenseAutomationTransactionsViewState, } = state$.value;
26
- const { transactionCategorizationView, selectedTransactionCategorizationTab, } = expenseAutomationTransactionsViewState;
27
- const currentTenant = (0, tenantSelector_1.getCurrentTenant)(state$.value);
28
- const { selectedPeriodByTenantId } = expenseAutomationViewState;
29
- const selectedPeriod = selectedPeriodByTenantId[currentTenant.tenantId];
30
- const txnId = action.payload.transactionId.id;
31
- const autoTabLineItems = transactionCategorizationView.autoCategorized
32
- .transactionReviewLocalDataById[txnId]
33
- ?.transactionReviewLocalData.tabSpecificLineItems
34
- .autoCategorized ?? [];
35
- const hasAutoLines = autoTabLineItems.length > 0;
36
- const { uiState } = transactionCategorizationView[selectedTransactionCategorizationTab];
37
- const removeIdsFromPeriod = selectedTransactionCategorizationTab === 'review'
38
- ? [txnId]
39
- : hasAutoLines
40
- ? []
41
- : [txnId];
42
- actions.push((0, transactionsViewReducer_1.updateStatusForTransactionNotMiscategorizedUpdateForCategorization)(selectedTransactionCategorizationTab, [txnId], removeIdsFromPeriod, selectedPeriod, 'Completed'));
43
- if (!hasAutoLines) {
44
- actions.push((0, transactionReducer_1.removeTransactionCategorization)({
45
- transactionIds: [txnId],
46
- }));
47
- }
48
- if (removeIdsFromPeriod.length > 0) {
49
- const totalCountByPeriod = uiState.totalCount[(0, timePeriod_1.toMonthYearPeriodId)(selectedPeriod)];
50
- const updatedTotalCount = totalCountByPeriod === 0
51
- ? totalCountByPeriod
52
- : totalCountByPeriod - 1;
53
- actions.push((0, transactionsViewReducer_1.updateTotalCountForTransactionCategorization)({
54
- selectedPeriod,
55
- selectedTab: selectedTransactionCategorizationTab,
56
- totalCount: updatedTotalCount,
57
- }));
58
- }
59
- }
60
111
  actions.push((0, transactionDetailReducer_1.updateStatusConsolidatedSaveTransactionDetail)({
61
112
  transactionId: action.payload.transactionId,
62
113
  fetchState: 'Completed',
@@ -69,7 +120,6 @@ const markTransactionAsNotMiscategorizedEpic = (actions$, state$, zeniAPI) => ac
69
120
  }));
70
121
  }
71
122
  else {
72
- //Finally everything done for this transaction
73
123
  actions.push((0, snackbarReducer_1.openSnackbar)({
74
124
  messageSection: 'transactionDetails_transactionUpdated',
75
125
  messageText: 'success',
@@ -9,6 +9,7 @@ const rxjs_1 = require("rxjs");
9
9
  const operators_1 = require("rxjs/operators");
10
10
  const recommendationPayload_1 = require("../../../commonPayloadTypes/recommendationPayload");
11
11
  const snackbarReducer_1 = require("../../../entity/snackbar/snackbarReducer");
12
+ const tenantSelector_1 = require("../../../entity/tenant/tenantSelector");
12
13
  const customerTransactionPayload_1 = require("../../../entity/transaction/payloadTypes/customerTransactionPayload");
13
14
  const otherTransactionPayload_1 = require("../../../entity/transaction/payloadTypes/otherTransactionPayload");
14
15
  const transactionIDPayload_1 = require("../../../entity/transaction/payloadTypes/transactionIDPayload");
@@ -25,13 +26,12 @@ const transactionSelector_1 = require("../../../entity/transaction/transactionSe
25
26
  const vendorReducer_1 = require("../../../entity/vendor/vendorReducer");
26
27
  const vendorSelector_1 = require("../../../entity/vendor/vendorSelector");
27
28
  const responsePayload_1 = require("../../../responsePayload");
28
- const tenantSelector_1 = require("../../../entity/tenant/tenantSelector");
29
29
  const accountListSelector_1 = require("../../accountList/accountListSelector");
30
30
  const transactionCategorizationLocalDataHelper_1 = require("../../expenseAutomationView/helpers/transactionCategorizationLocalDataHelper");
31
31
  const transactionsViewReducer_1 = require("../../expenseAutomationView/reducers/transactionsViewReducer");
32
+ const transactionDetailLocalDataHelper_1 = require("../transactionDetailLocalDataHelper");
32
33
  const transactionDetailReducer_1 = require("../transactionDetailReducer");
33
34
  const transactionDetailState_1 = require("../transactionDetailState");
34
- const saveTransactionHelper_1 = require("./saveTransactionHelper");
35
35
  const updateTransactionDetailEpic = (actions$, state$, zeniAPI) => actions$.pipe((0, operators_1.filter)(transactionDetailReducer_1.updateTransactionDetail.match), (0, operators_1.mergeMap)((action) => {
36
36
  const { transactionState, vendorState, transactionDetailState } = state$.value;
37
37
  const isAccountingProjectsEnabled = (0, tenantSelector_1.getIsAccountingProjectsEnabled)(state$.value);
@@ -42,24 +42,6 @@ const updateTransactionDetailEpic = (actions$, state$, zeniAPI) => actions$.pipe
42
42
  if ((0, responsePayload_1.isSuccessResponse)(response) && response.data != null) {
43
43
  const transactionIdResp = (0, transactionIDPayload_1.toTransactionID)(response.data.transaction);
44
44
  const transactionDetailKeyForResponse = (0, transactionDetailState_1.getTransactionDetailKey)(transactionIdResp);
45
- const tranDetail = (0, get_1.default)(transactionDetailState.transactionDetailById, transactionDetailKeyForResponse, undefined);
46
- const tranVendorUpdates = tranDetail?.transactionDetailLocalData.vendorUpdates;
47
- let vendorUpdates = undefined;
48
- if (tranVendorUpdates?.qboId != null) {
49
- const vendor = (0, vendorSelector_1.getVendorByVendorId)(vendorState, tranVendorUpdates.qboId);
50
- if (vendor != null) {
51
- vendorUpdates = {
52
- id: vendor?.qboId,
53
- name: vendor?.name,
54
- };
55
- }
56
- else if (tranVendorUpdates.name != null) {
57
- vendorUpdates = {
58
- id: tranVendorUpdates.qboId,
59
- name: tranVendorUpdates.name,
60
- };
61
- }
62
- }
63
45
  const actions = [];
64
46
  if (response.data.vendors != null) {
65
47
  actions.push((0, vendorReducer_1.updateVendors)(response.data.vendors, 'merge'));
@@ -72,30 +54,14 @@ const updateTransactionDetailEpic = (actions$, state$, zeniAPI) => actions$.pipe
72
54
  vendorId: response.data.vendors?.[0]?.vendor_id,
73
55
  }));
74
56
  actions.push((0, transactionDetailReducer_1.updateTransactionDetailSaveStatus)(transactionIdResp, 'Completed'));
75
- let callMarkTransactionAsNotMiscategorized = true;
76
- // NOTE: If tran is vendorType OR JE OR Deposit transaction and there is no vendor chosen on this tranaction while saving,
77
- // then we need to skip mark-miscategorized-as-correct api call
78
- if ((0, transactionType_1.toVendorTransactionTypeStrict)(response.data.transaction.transaction_type) != null ||
79
- (0, transactionType_1.toJEAndDepositTransactionTypeStrict)(response.data.transaction.transaction_type) != null) {
80
- const isAccountingClassesEnabled = (0, tenantSelector_1.getIsAccountingClassesEnabled)(state$.value);
81
- const isAnyClassMissing = isAccountingClassesEnabled &&
82
- checkIfClassMissingForAnyLineItem(response.data.transaction);
83
- const isVendorOrCustomerMissingInAnyLine = (0, transactionType_1.toJEAndDepositTransactionTypeStrict)(response.data.transaction.transaction_type) != null &&
84
- checkIfVendorOrCustomerMissingForAnyLineItem(response.data.transaction);
85
- if ((vendorUpdates?.id == null &&
86
- isVendorOrCustomerMissingInAnyLine == true) ||
87
- isAnyClassMissing == true) {
88
- callMarkTransactionAsNotMiscategorized = false;
89
- }
90
- }
91
57
  const uncategorizedIncomeExpense = (0, accountListSelector_1.getUncategorizedAccounts)(state$.value.accountState, state$.value.accountListState, 'accountList');
92
58
  const transaction = (0, toSupportedTransaction_1.default)(response.data.transaction);
93
- const isTransactionUnCategorized = (0, saveTransactionHelper_1.isTransactionUncategorized)(transaction, uncategorizedIncomeExpense);
59
+ const isAccountingClassesEnabled = (0, tenantSelector_1.getIsAccountingClassesEnabled)(state$.value);
60
+ const eligibleLineIds = getLineIdsEligibleForReview(transaction, uncategorizedIncomeExpense, isAccountingClassesEnabled, action.payload.isExpenseAutomationEnabled);
94
61
  if (action.payload.transactionSaveUpdates.markAsMiscatUpdates ===
95
62
  true &&
96
- callMarkTransactionAsNotMiscategorized === true &&
97
- isTransactionUnCategorized === false) {
98
- actions.push((0, transactionDetailReducer_1.markTransactionAsNotMiscategorized)(transactionIdResp, action.payload.transactionSaveUpdates, action.payload.isExpenseAutomationEnabled));
63
+ eligibleLineIds.length > 0) {
64
+ actions.push((0, transactionDetailReducer_1.markTransactionAsNotMiscategorized)(transactionIdResp, action.payload.transactionSaveUpdates, action.payload.isExpenseAutomationEnabled, eligibleLineIds));
99
65
  }
100
66
  else {
101
67
  actions.push((0, transactionDetailReducer_1.updateStatusConsolidatedSaveTransactionDetail)({
@@ -307,31 +273,58 @@ const toTransactionUpdatePayload = (updates, detail, cotTransactionTracking, isA
307
273
  return (0, transactionPayload_1.injectCOTFlagsIntoTransactionPayload)(basePayload, transaction);
308
274
  };
309
275
  exports.toTransactionUpdatePayload = toTransactionUpdatePayload;
310
- const checkIfClassMissingForAnyLineItem = (transactionPayload) => {
311
- let isAnyClassMissing = false;
312
- if (transactionPayload.lines != null) {
313
- transactionPayload.lines.forEach((line) => {
314
- if (line.line_detail?.class
315
- ?.accounting_class_id == null) {
316
- isAnyClassMissing = true;
276
+ const getLineIdsEligibleForReview = (transaction, uncategorizedIncomeExpense, isAccountingClassesEnabled, isExpenseAutomationEnabled) => {
277
+ // For non-EA, any line missing a required field (class when enabled, or entity on JE/Deposit)
278
+ // suppresses the entire call. EA keeps the per-line model, returning only eligible line IDs.
279
+ if (!isExpenseAutomationEnabled) {
280
+ const isApplicableType = (0, vendorTransaction_1.isVendorTransaction)(transaction) ||
281
+ (0, transactionType_1.toJEAndDepositTransactionTypeStrict)(transaction.type) != null;
282
+ if (isApplicableType) {
283
+ const lines = (transaction.lines ?? []).filter((line) => line.type === 'transaction_with_account_and_class_line' ||
284
+ line.type === 'journal_entry_transaction_line' ||
285
+ line.type === 'transaction_with_product_or_service_line');
286
+ if (isAccountingClassesEnabled) {
287
+ const anyLineMissingClass = lines.some((line) => (line.class?.classId ??
288
+ '') === '');
289
+ if (anyLineMissingClass) {
290
+ return [];
291
+ }
317
292
  }
318
- });
319
- }
320
- return isAnyClassMissing;
321
- };
322
- const checkIfVendorOrCustomerMissingForAnyLineItem = (transactionPayload) => {
323
- let isAnyVendorOrCustomerMissing = false;
324
- if (transactionPayload.lines != null) {
325
- transactionPayload.lines.forEach((line) => {
326
- if (line.line_detail
327
- ?.vendor == null &&
328
- line.line_detail
329
- ?.customer == null) {
330
- isAnyVendorOrCustomerMissing = true;
293
+ if ((0, transactionType_1.toJEAndDepositTransactionTypeStrict)(transaction.type) != null) {
294
+ const anyLineMissingEntity = lines.some((line) => {
295
+ const tl = line;
296
+ return tl.vendor == null && tl.customer == null;
297
+ });
298
+ if (anyLineMissingEntity) {
299
+ return [];
300
+ }
331
301
  }
332
- });
302
+ }
333
303
  }
334
- return isAnyVendorOrCustomerMissing;
304
+ return (transaction.lines ?? [])
305
+ .filter((line) => {
306
+ if (line.type !== 'transaction_with_account_and_class_line' &&
307
+ line.type !== 'journal_entry_transaction_line' &&
308
+ line.type !== 'transaction_with_product_or_service_line') {
309
+ return false;
310
+ }
311
+ const typedLine = line;
312
+ const hasCategory = !(0, transactionDetailLocalDataHelper_1.isAccountUncategorized)(uncategorizedIncomeExpense, typedLine.account?.qboId);
313
+ // class is always a ClassBase object (never null) because the payload mapper
314
+ // calls mapClassBasePayloadToClassBase(payload ?? {}) which returns {} with classId:''.
315
+ // A real class is indicated by a non-empty classId.
316
+ const hasClass = !isAccountingClassesEnabled || (typedLine.class?.classId ?? '') !== '';
317
+ const hasAmount = (typedLine.amount?.amount ?? 0) !== 0;
318
+ // JE lines and Deposit transactions require vendor or customer before being eligible for review.
319
+ const isJEOrDepositContext = line.type === 'journal_entry_transaction_line' ||
320
+ (0, transactionType_1.toJEAndDepositTransactionTypeStrict)(transaction.type) != null;
321
+ const hasEntity = !isJEOrDepositContext ||
322
+ typedLine.vendor != null ||
323
+ typedLine.customer != null;
324
+ return hasCategory && hasClass && hasAmount && hasEntity;
325
+ })
326
+ .map((line) => line.id)
327
+ .filter(Boolean);
335
328
  };
336
329
  // Converts a locally-created line item (from edit mode) into the wire shape
337
330
  // expected by the categorize endpoint. Sending id='' signals the backend to
@@ -764,6 +764,7 @@ const setEntityRecommendationForLineIdInLocalData = (transaction, transactionDet
764
764
  latestUpdatedLineWithEntity: transactionDetailLocalData.latestUpdatedLineWithEntity,
765
765
  vendorBulkUpdateCategoryClassRecommendationsFetchStatus: transactionDetailLocalData.vendorBulkUpdateCategoryClassRecommendationsFetchStatus,
766
766
  selectedEntity: transactionDetailLocalData.selectedEntity,
767
+ editMode: transactionDetailLocalData.editMode,
767
768
  };
768
769
  return newLocalData;
769
770
  };
@@ -818,6 +819,7 @@ const removeEntityRecommendationForLineIdInLocalData = (transaction, transaction
818
819
  latestSelectedEntityOnLineItem: transactionDetailLocalData.latestSelectedEntityOnLineItem,
819
820
  latestUpdatedLineWithEntity: transactionDetailLocalData.latestUpdatedLineWithEntity,
820
821
  selectedEntity: transactionDetailLocalData.selectedEntity,
822
+ editMode: transactionDetailLocalData.editMode,
821
823
  };
822
824
  return newLocalData;
823
825
  };
@@ -838,7 +840,8 @@ const getChangedLineItemCountFromLocalData = (transaction, transactionLocalData)
838
840
  const accountLine = line;
839
841
  hasCategoryChanged =
840
842
  accountLineLocal.account?.qboId !== accountLine.account.qboId &&
841
- accountLineLocal.account?.accountId !== accountLine.account.accountId;
843
+ accountLineLocal.account?.accountId !==
844
+ accountLine.account.accountId;
842
845
  hasClassChanged =
843
846
  (accountLineLocal.class?.qboId != null ||
844
847
  accountLine.class?.qboId != null) &&
@@ -38,10 +38,11 @@ export declare const downloadAccountingProviderAttachment: import("@reduxjs/tool
38
38
  error: ZeniAPIStatus<Record<string, unknown>> | undefined;
39
39
  }, "transactionDetail/updateTransactionDetailSaveStatus", never, never>, clearTransactionDetailSaveStatus: import("@reduxjs/toolkit").ActionCreatorWithPreparedPayload<[transactionId: TransactionID], {
40
40
  transactionId: TransactionID;
41
- }, "transactionDetail/clearTransactionDetailSaveStatus", never, never>, markTransactionAsNotMiscategorized: import("@reduxjs/toolkit").ActionCreatorWithPreparedPayload<[transactionId: TransactionID, transactionSaveUpdates: TransactionSaveUpdates, isExpenseAutomationEnabled: boolean], {
41
+ }, "transactionDetail/clearTransactionDetailSaveStatus", never, never>, markTransactionAsNotMiscategorized: import("@reduxjs/toolkit").ActionCreatorWithPreparedPayload<[transactionId: TransactionID, transactionSaveUpdates: TransactionSaveUpdates, isExpenseAutomationEnabled: boolean, lineIds?: string[] | undefined], {
42
42
  transactionId: TransactionID;
43
43
  transactionSaveUpdates: TransactionSaveUpdates;
44
44
  isExpenseAutomationEnabled: boolean;
45
+ lineIds: string[] | undefined;
45
46
  }, "transactionDetail/markTransactionAsNotMiscategorized", never, never>, deleteTransactionAttachment: import("@reduxjs/toolkit").ActionCreatorWithPreparedPayload<[attachmentFileName: string, transactionId: TransactionID], {
46
47
  attachmentFileName: string;
47
48
  transactionId: TransactionID;
@@ -197,12 +197,13 @@ const transactionDetail = (0, toolkit_1.createSlice)({
197
197
  };
198
198
  }
199
199
  },
200
- prepare(transactionId, transactionSaveUpdates, isExpenseAutomationEnabled) {
200
+ prepare(transactionId, transactionSaveUpdates, isExpenseAutomationEnabled, lineIds) {
201
201
  return {
202
202
  payload: {
203
203
  transactionId,
204
204
  transactionSaveUpdates,
205
205
  isExpenseAutomationEnabled,
206
+ lineIds,
206
207
  },
207
208
  };
208
209
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zeniai/client-epic-state",
3
- "version": "5.1.52-beta0RD",
3
+ "version": "5.1.53-betaRD1",
4
4
  "description": "Shared module between Web & Mobile containing required abstractions for state management, async network communication. ",
5
5
  "main": "lib/index.js",
6
6
  "module": "lib/esm/index.js",