@zeniai/client-epic-state 5.1.42-betaDI4 → 5.1.42

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 (74) hide show
  1. package/lib/entity/snackbar/snackbarTypes.d.ts +1 -1
  2. package/lib/entity/snackbar/snackbarTypes.js +1 -1
  3. package/lib/entity/task/taskPayload.d.ts +1 -2
  4. package/lib/entity/task/taskPayload.js +1 -8
  5. package/lib/entity/task/taskReducer.d.ts +1 -8
  6. package/lib/entity/task/taskReducer.js +4 -29
  7. package/lib/entity/task/taskState.d.ts +0 -1
  8. package/lib/entity/tenant/epic/saveExternalConnectionEpic.js +15 -11
  9. package/lib/epic.d.ts +3 -5
  10. package/lib/epic.js +3 -5
  11. package/lib/esm/entity/snackbar/snackbarTypes.js +1 -1
  12. package/lib/esm/entity/task/taskPayload.js +1 -8
  13. package/lib/esm/entity/task/taskReducer.js +3 -28
  14. package/lib/esm/entity/tenant/epic/saveExternalConnectionEpic.js +15 -11
  15. package/lib/esm/epic.js +3 -5
  16. package/lib/esm/index.js +7 -7
  17. package/lib/esm/view/companyTaskManagerView/epics/fetchCompanyTaskManagerViewEpic.js +0 -3
  18. package/lib/esm/view/companyTaskManagerView/epics/fetchTaskManagerMetricsEpic.js +1 -7
  19. package/lib/esm/view/expenseAutomationView/reducers/transactionsViewReducer.js +7 -0
  20. package/lib/esm/view/taskManager/taskDetailView/epics/initializeTaskToLocalStoreEpic.js +0 -1
  21. package/lib/esm/view/taskManager/taskDetailView/epics/saveTaskDetailEpic.js +9 -70
  22. package/lib/esm/view/taskManager/taskDetailView/taskDetailReducer.js +2 -56
  23. package/lib/esm/view/taskManager/taskDetailView/taskDetailSelector.js +9 -32
  24. package/lib/esm/view/taskManager/taskListView/epics/fetchTaskListEpic.js +1 -3
  25. package/lib/esm/view/taskManager/taskListView/taskList.js +0 -1
  26. package/lib/esm/view/taskManager/taskListView/taskListReducer.js +12 -240
  27. package/lib/esm/view/taskManager/taskListView/taskListSelector.js +0 -1
  28. package/lib/esm/view/taskManager/taskListView/taskListViewHelpers.js +0 -58
  29. package/lib/esm/view/transactionDetail/epics/saveTransactionHelper.js +22 -0
  30. package/lib/esm/view/transactionDetail/epics/updateTransactionDetailEpic.js +75 -3
  31. package/lib/esm/view/transactionDetail/journalEntryLinesViewModel.js +58 -0
  32. package/lib/esm/view/transactionDetail/transactionDetailLocalDataHelper.js +10 -8
  33. package/lib/esm/view/transactionDetail/transactionDetailReducer.js +66 -1
  34. package/lib/index.d.ts +10 -10
  35. package/lib/index.js +25 -25
  36. package/lib/view/companyTaskManagerView/epics/fetchCompanyTaskManagerViewEpic.js +0 -3
  37. package/lib/view/companyTaskManagerView/epics/fetchTaskManagerMetricsEpic.js +1 -7
  38. package/lib/view/companyView/types/cockpitTypes.d.ts +0 -3
  39. package/lib/view/expenseAutomationView/helpers/transactionCategorizationLocalDataHelper.d.ts +1 -1
  40. package/lib/view/expenseAutomationView/reducers/transactionsViewReducer.js +7 -0
  41. package/lib/view/taskManager/taskDetailView/epics/initializeTaskToLocalStoreEpic.js +0 -1
  42. package/lib/view/taskManager/taskDetailView/epics/saveTaskDetailEpic.d.ts +2 -2
  43. package/lib/view/taskManager/taskDetailView/epics/saveTaskDetailEpic.js +8 -69
  44. package/lib/view/taskManager/taskDetailView/taskDetail.d.ts +0 -15
  45. package/lib/view/taskManager/taskDetailView/taskDetailReducer.d.ts +2 -14
  46. package/lib/view/taskManager/taskDetailView/taskDetailReducer.js +3 -57
  47. package/lib/view/taskManager/taskDetailView/taskDetailSelector.d.ts +1 -2
  48. package/lib/view/taskManager/taskDetailView/taskDetailSelector.js +7 -30
  49. package/lib/view/taskManager/taskListView/epics/fetchTaskListEpic.js +1 -3
  50. package/lib/view/taskManager/taskListView/taskList.d.ts +1 -1
  51. package/lib/view/taskManager/taskListView/taskList.js +0 -1
  52. package/lib/view/taskManager/taskListView/taskListPayload.d.ts +0 -4
  53. package/lib/view/taskManager/taskListView/taskListReducer.d.ts +2 -6
  54. package/lib/view/taskManager/taskListView/taskListReducer.js +13 -241
  55. package/lib/view/taskManager/taskListView/taskListSelector.js +0 -1
  56. package/lib/view/taskManager/taskListView/taskListViewHelpers.d.ts +0 -11
  57. package/lib/view/taskManager/taskListView/taskListViewHelpers.js +1 -60
  58. package/lib/view/transactionDetail/epics/saveTransactionHelper.js +22 -0
  59. package/lib/view/transactionDetail/epics/updateTransactionDetailEpic.d.ts +5 -1
  60. package/lib/view/transactionDetail/epics/updateTransactionDetailEpic.js +77 -4
  61. package/lib/view/transactionDetail/journalEntryLinesViewModel.d.ts +27 -0
  62. package/lib/view/transactionDetail/journalEntryLinesViewModel.js +61 -1
  63. package/lib/view/transactionDetail/transactionDetailLocalDataHelper.js +10 -8
  64. package/lib/view/transactionDetail/transactionDetailReducer.d.ts +11 -2
  65. package/lib/view/transactionDetail/transactionDetailReducer.js +67 -2
  66. package/lib/view/transactionDetail/transactionDetailState.d.ts +8 -1
  67. package/lib/view/transactionDetail/transactionDetailTypes.d.ts +2 -0
  68. package/package.json +1 -1
  69. package/lib/esm/view/taskManager/taskDetailView/epics/createSubTaskEpic.js +0 -96
  70. package/lib/esm/view/taskManager/taskDetailView/epics/fetchSubTasksEpic.js +0 -53
  71. package/lib/view/taskManager/taskDetailView/epics/createSubTaskEpic.d.ts +0 -9
  72. package/lib/view/taskManager/taskDetailView/epics/createSubTaskEpic.js +0 -100
  73. package/lib/view/taskManager/taskDetailView/epics/fetchSubTasksEpic.d.ts +0 -8
  74. package/lib/view/taskManager/taskDetailView/epics/fetchSubTasksEpic.js +0 -57
@@ -1,16 +1,5 @@
1
- import { Tag } from '../../../entity/tag/tagState';
2
1
  import { Task } from '../../../entity/task/taskState';
3
- import { User } from '../../../entity/user/userState';
4
2
  import { ZeniDate } from '../../../zeniDayJS';
5
3
  import { TaskGroupKey } from './taskList';
6
- export type SubtaskSortKey = 'name' | 'status' | 'assignee' | 'tag';
7
- export type SubtaskSortOrder = 'asc' | 'desc';
8
4
  export declare const getDueDateValueFromDueDateGroupId: (groupId: string) => ZeniDate;
9
5
  export declare const getTaskUpdates: (groupByKey: TaskGroupKey, groupId: string) => Partial<Task>;
10
- /**
11
- * Sort a subtask list by one of the sortable columns. Pure — no state
12
- * dependency. `statusOrder` is derived from `allTaskStatus` so any new
13
- * status added upstream slots in by its enum position instead of
14
- * falling into a catch-all bucket.
15
- */
16
- export declare const sortSubtasks: (subtasks: Task[], sortKey: SubtaskSortKey | null, sortOrder: SubtaskSortOrder, userList: User[], tagList: Tag[]) => Task[];
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.sortSubtasks = exports.getTaskUpdates = exports.getDueDateValueFromDueDateGroupId = void 0;
3
+ exports.getTaskUpdates = exports.getDueDateValueFromDueDateGroupId = void 0;
4
4
  const zeniDayJS_1 = require("../../../zeniDayJS");
5
5
  const taskDetailSelector_1 = require("../taskDetailView/taskDetailSelector");
6
6
  const taskList_1 = require("./taskList");
@@ -53,62 +53,3 @@ const getTaskUpdates = (groupByKey, groupId) => {
53
53
  };
54
54
  };
55
55
  exports.getTaskUpdates = getTaskUpdates;
56
- /**
57
- * Sort a subtask list by one of the sortable columns. Pure — no state
58
- * dependency. `statusOrder` is derived from `allTaskStatus` so any new
59
- * status added upstream slots in by its enum position instead of
60
- * falling into a catch-all bucket.
61
- */
62
- const sortSubtasks = (subtasks, sortKey, sortOrder, userList, tagList) => {
63
- if (sortKey == null) {
64
- return subtasks;
65
- }
66
- const statusOrder = taskDetailSelector_1.allTaskStatus.reduce((acc, status, index) => {
67
- acc[status.code] = index;
68
- return acc;
69
- }, {});
70
- const unknownStatusRank = taskDetailSelector_1.allTaskStatus.length;
71
- const cmpStr = (a, b) => a.localeCompare(b, undefined, { sensitivity: 'base' });
72
- const keyFn = (task) => {
73
- if (sortKey === 'name') {
74
- return task.name ?? '';
75
- }
76
- if (sortKey === 'status') {
77
- return statusOrder[task.status.code] ?? unknownStatusRank;
78
- }
79
- if (sortKey === 'assignee') {
80
- const firstId = task.assignees?.[0];
81
- if (firstId == null) {
82
- return '';
83
- }
84
- const user = userList.find((candidate) => candidate.userId === firstId);
85
- return user
86
- ? `${user.firstName ?? ''} ${user.lastName ?? ''}`.trim()
87
- : '';
88
- }
89
- if (sortKey === 'tag') {
90
- const firstTagId = task.tagIds?.[0];
91
- if (firstTagId == null) {
92
- return '';
93
- }
94
- const tag = tagList.find((candidate) => candidate.tagId === firstTagId);
95
- return tag?.name ?? '';
96
- }
97
- return '';
98
- };
99
- const copy = [...subtasks];
100
- copy.sort((a, b) => {
101
- const av = keyFn(a);
102
- const bv = keyFn(b);
103
- let cmp;
104
- if (typeof av === 'number' && typeof bv === 'number') {
105
- cmp = av - bv;
106
- }
107
- else {
108
- cmp = cmpStr(String(av), String(bv));
109
- }
110
- return sortOrder === 'asc' ? cmp : -cmp;
111
- });
112
- return copy;
113
- };
114
- exports.sortSubtasks = sortSubtasks;
@@ -48,7 +48,15 @@ const toTransactionUpdatesFromTransactionDetailLocalData = (transactionDetailLoc
48
48
  let lastUpdatedCategory;
49
49
  let lastUpdatedClass;
50
50
  // single recommendation to just pick the last recommendation in the list of lines
51
+ const existingLineIds = new Set(transaction.lines?.map((l) => l.id) ?? []);
52
+ const deletedLineIds = new Set((transactionDetailLocalData.deletedLineIds ?? []).filter((id) => existingLineIds.has(id)));
53
+ // Collect new lines — in lineItemById but not yet in transaction.lines.
54
+ const addedLines = Object.values(transactionDetailLocalData.lineItemById).filter((item) => !existingLineIds.has(item.id));
51
55
  transaction.lines?.forEach((line) => {
56
+ // Skip lines the user deleted in edit mode.
57
+ if (deletedLineIds.has(line.id)) {
58
+ return;
59
+ }
52
60
  const lineItem = transactionDetailLocalData.lineItemById[line.id];
53
61
  if (line.type === 'transaction_with_account_and_class_line' ||
54
62
  line.type === 'transaction_with_product_or_service_line' ||
@@ -59,6 +67,7 @@ const toTransactionUpdatesFromTransactionDetailLocalData = (transactionDetailLoc
59
67
  const entityDetails = getEntityDetails(selectedEntity, lineItem, transactionDetailLocalData.vendorUpdates, transactionDetailLocalData.customerUpdates);
60
68
  updatesByLineId[line.id] = {
61
69
  lineDescription: lineItem.lineDescription,
70
+ amount: lineItem.amount,
62
71
  class: lineItem.class,
63
72
  account: lineItem.account,
64
73
  // Project is at the transaction level for invoice/credit_memo; omit from lines.
@@ -80,6 +89,17 @@ const toTransactionUpdatesFromTransactionDetailLocalData = (transactionDetailLoc
80
89
  currentSelectedEntityLine.type === 'vendor'
81
90
  ? lineItem.vendor
82
91
  : undefined,
92
+ // Carry the JE debit/credit side so a split edit that flips an
93
+ // existing line's posting side persists through the line patch.
94
+ ...(line.type === 'journal_entry_transaction_line'
95
+ ? {
96
+ postingType: lineItem.postingType,
97
+ postingTypeName: lineItem.postingType != null
98
+ ? lineItem.postingType.charAt(0).toUpperCase() +
99
+ lineItem.postingType.slice(1)
100
+ : lineItem.postingTypeName,
101
+ }
102
+ : {}),
83
103
  };
84
104
  }
85
105
  let hasCategoryChanged = false;
@@ -166,6 +186,8 @@ const toTransactionUpdatesFromTransactionDetailLocalData = (transactionDetailLoc
166
186
  and all the line items are NOT uncategorized*/
167
187
  markAsMiscatUpdates: isTransactionMisCategorized,
168
188
  anyBulkUpdates: isBulkUpdate,
189
+ addedLines: addedLines.length > 0 ? addedLines : undefined,
190
+ deletedLineIds: deletedLineIds.size > 0 ? [...deletedLineIds] : undefined,
169
191
  };
170
192
  };
171
193
  exports.toTransactionUpdatesFromTransactionDetailLocalData = toTransactionUpdatesFromTransactionDetailLocalData;
@@ -1,9 +1,11 @@
1
1
  import { ActionsObservable, StateObservable } from 'redux-observable';
2
+ import { ID } from '../../../commonStateTypes/common';
2
3
  import { COTTransactionTracking } from '../../../commonStateTypes/recommendationBase';
3
4
  import { CustomerBase } from '../../../entity/customer/customerState';
4
5
  import { Entity } from '../../../entity/genericEntity/entity';
5
6
  import { openSnackbar } from '../../../entity/snackbar/snackbarReducer';
6
7
  import { TransactionPayload } from '../../../entity/transaction/payloadTypes/transactionPayload';
8
+ import { Line } from '../../../entity/transaction/stateTypes/transactionLine';
7
9
  import { updateTransaction, updateTransactions } from '../../../entity/transaction/transactionReducer';
8
10
  import { SupportedTransaction } from '../../../entity/transaction/transactionState';
9
11
  import { updateVendors } from '../../../entity/vendor/vendorReducer';
@@ -13,6 +15,8 @@ import { ZeniAPI } from '../../../zeniAPI';
13
15
  import { syncTransactionCategorizationFromDetailSave } from '../../expenseAutomationView/reducers/transactionsViewReducer';
14
16
  import { initializeTransactionDetailLocalData, markTransactionAsNotMiscategorized, updateStatusConsolidatedSaveTransactionDetail, updateTransactionDetail, updateTransactionDetailSaveStatus, updateTransactionStateIfUpdateActionNotAllowed } from '../transactionDetailReducer';
15
17
  import { TransactionUpdates } from '../transactionDetailState';
18
+ import { TransactionDetailLineItemData } from '../transactionDetailTypes';
16
19
  export type ActionType = ReturnType<typeof updateTransactionDetail> | ReturnType<typeof updateTransaction> | ReturnType<typeof updateTransactions> | ReturnType<typeof updateTransactionStateIfUpdateActionNotAllowed> | ReturnType<typeof openSnackbar> | ReturnType<typeof updateTransactionDetailSaveStatus> | ReturnType<typeof updateStatusConsolidatedSaveTransactionDetail> | ReturnType<typeof initializeTransactionDetailLocalData> | ReturnType<typeof updateVendors> | ReturnType<typeof markTransactionAsNotMiscategorized> | ReturnType<typeof syncTransactionCategorizationFromDetailSave>;
17
20
  export declare const updateTransactionDetailEpic: (actions$: ActionsObservable<ActionType>, state$: StateObservable<RootState>, zeniAPI: ZeniAPI) => import("rxjs").Observable<ActionType>;
18
- export declare const toTransactionUpdatePayload: (updates: TransactionUpdates, detail: SupportedTransaction, cotTransactionTracking: COTTransactionTracking, isAccountingProjectsEnabled: boolean, vendorUpdates?: VendorBase, customerUpdates?: CustomerBase, tranSelectedEntity?: Entity) => Partial<TransactionPayload>;
21
+ export declare const toTransactionUpdatePayload: (updates: TransactionUpdates, detail: SupportedTransaction, cotTransactionTracking: COTTransactionTracking, isAccountingProjectsEnabled: boolean, vendorUpdates?: VendorBase, customerUpdates?: CustomerBase, tranSelectedEntity?: Entity, deletedLineIds?: ID[], addedLines?: TransactionDetailLineItemData[]) => Partial<TransactionPayload>;
22
+ export declare const toNewTransactionLine: (item: TransactionDetailLineItemData) => Line;
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.toTransactionUpdatePayload = exports.updateTransactionDetailEpic = void 0;
6
+ exports.toNewTransactionLine = exports.toTransactionUpdatePayload = exports.updateTransactionDetailEpic = void 0;
7
7
  const get_1 = __importDefault(require("lodash/get"));
8
8
  const rxjs_1 = require("rxjs");
9
9
  const operators_1 = require("rxjs/operators");
@@ -64,7 +64,9 @@ const updateTransactionDetailEpic = (actions$, state$, zeniAPI) => actions$.pipe
64
64
  if (response.data.vendors != null) {
65
65
  actions.push((0, vendorReducer_1.updateVendors)(response.data.vendors, 'merge'));
66
66
  }
67
- actions.push((0, transactionReducer_1.updateTransaction)(transactionDetailKeyForResponse, response.data.transaction));
67
+ // 'replace' so deleted lines are removed from Redux; 'merge' would
68
+ // keep any line absent from the server response (deletion bug).
69
+ actions.push((0, transactionReducer_1.updateTransaction)(transactionDetailKeyForResponse, response.data.transaction, 'replace'));
68
70
  actions.push((0, transactionDetailReducer_1.initializeTransactionDetailLocalData)({
69
71
  transactionId: transactionIdResp,
70
72
  vendorId: response.data.vendors?.[0]?.vendor_id,
@@ -166,7 +168,7 @@ function preparePayload(transactionState, vendorState, transactionDetailState, t
166
168
  };
167
169
  }
168
170
  if (detail != null) {
169
- const transUpdates = (0, exports.toTransactionUpdatePayload)(saveUpdates.updates, detail, cotTransactionTracking, isAccountingProjectsEnabled, vendorUpdates, customerUpdates, currentSelectedEntity);
171
+ const transUpdates = (0, exports.toTransactionUpdatePayload)(saveUpdates.updates, detail, cotTransactionTracking, isAccountingProjectsEnabled, vendorUpdates, customerUpdates, currentSelectedEntity, saveUpdates.deletedLineIds, saveUpdates.addedLines);
170
172
  const entityUpdates = toEntityUpdatePayload(saveUpdates.entityRelatedUpdates);
171
173
  return {
172
174
  entity_related_updates: entityUpdates,
@@ -176,7 +178,7 @@ function preparePayload(transactionState, vendorState, transactionDetailState, t
176
178
  }
177
179
  return undefined;
178
180
  }
179
- const toTransactionUpdatePayload = (updates, detail, cotTransactionTracking, isAccountingProjectsEnabled, vendorUpdates, customerUpdates, tranSelectedEntity) => {
181
+ const toTransactionUpdatePayload = (updates, detail, cotTransactionTracking, isAccountingProjectsEnabled, vendorUpdates, customerUpdates, tranSelectedEntity, deletedLineIds, addedLines) => {
180
182
  const transaction = Object.assign({}, detail);
181
183
  transaction.memo = updates.memo ?? '';
182
184
  // Unlike `memo` (always reset to '' when absent), `documentId` falls back to
@@ -185,6 +187,11 @@ const toTransactionUpdatePayload = (updates, detail, cotTransactionTracking, isA
185
187
  // either the unchanged current value or undefined, and we must not send an
186
188
  // empty `document_id` that the server would treat as clearing the number.
187
189
  transaction.documentId = updates.documentId ?? transaction.documentId;
190
+ // Remove lines deleted by the user in edit mode before patching or serializing.
191
+ if (deletedLineIds != null && deletedLineIds.length > 0) {
192
+ const deletedSet = new Set(deletedLineIds);
193
+ transaction.lines = transaction.lines?.filter((line) => !deletedSet.has(line.id));
194
+ }
188
195
  const recommendationUpdates = {};
189
196
  for (const key in updates.updatesByLineId) {
190
197
  const lineUpdates = updates.updatesByLineId[key];
@@ -192,6 +199,9 @@ const toTransactionUpdatePayload = (updates, detail, cotTransactionTracking, isA
192
199
  if (line.id === key) {
193
200
  const lineTobeUpdated = Object.assign({}, line);
194
201
  lineTobeUpdated.lineDescription = lineUpdates.lineDescription ?? '';
202
+ if (lineUpdates.amount != null) {
203
+ lineTobeUpdated.amount = lineUpdates.amount;
204
+ }
195
205
  if (lineUpdates.class != null) {
196
206
  lineTobeUpdated.class = lineUpdates.class;
197
207
  }
@@ -213,6 +223,16 @@ const toTransactionUpdatePayload = (updates, detail, cotTransactionTracking, isA
213
223
  else {
214
224
  lineTobeUpdated.vendor = undefined;
215
225
  }
226
+ // Persist a debit↔credit flip on an existing JE line. `lineTobeUpdated`
227
+ // is typed as the account-and-class shape, but for a JE the runtime
228
+ // object is a JournalEntryTransactionLine, so cast to set the side.
229
+ if (lineUpdates.postingType != null) {
230
+ const journalEntryLine = lineTobeUpdated;
231
+ journalEntryLine.postingType = lineUpdates.postingType;
232
+ if (lineUpdates.postingTypeName != null) {
233
+ journalEntryLine.postingTypeName = lineUpdates.postingTypeName;
234
+ }
235
+ }
216
236
  if (lineUpdates.recommendation != null) {
217
237
  recommendationUpdates[line.id] = lineUpdates.recommendation;
218
238
  }
@@ -232,6 +252,12 @@ const toTransactionUpdatePayload = (updates, detail, cotTransactionTracking, isA
232
252
  });
233
253
  transaction.lines = newLineMap;
234
254
  }
255
+ // Append lines added by the user in edit mode. Sending id='' tells the
256
+ // backend to create a new QBO line rather than updating an existing one.
257
+ if (addedLines != null && addedLines.length > 0) {
258
+ const newLines = addedLines.map(exports.toNewTransactionLine);
259
+ transaction.lines = [...(transaction.lines ?? []), ...newLines];
260
+ }
235
261
  transaction.recommendations = recommendationUpdates;
236
262
  // Convert transaction to payload first
237
263
  let basePayload;
@@ -307,6 +333,53 @@ const checkIfVendorOrCustomerMissingForAnyLineItem = (transactionPayload) => {
307
333
  }
308
334
  return isAnyVendorOrCustomerMissing;
309
335
  };
336
+ // Converts a locally-created line item (from edit mode) into the wire shape
337
+ // expected by the categorize endpoint. Sending id='' signals the backend to
338
+ // create a new QBO line rather than update an existing one.
339
+ //
340
+ // Type-dispatched so each transaction family contributes its own new-line
341
+ // shape: journal entries carry debit/credit `postingType`, expenses carry
342
+ // `billable`. Phase 3 types extend this by adding a branch rather than forking
343
+ // the function.
344
+ const EMPTY_ACCOUNT = {
345
+ accountDescription: '',
346
+ accountId: '',
347
+ accountName: '',
348
+ labels: [],
349
+ };
350
+ const toNewTransactionLine = (item) => {
351
+ if (item.type === 'journal_entry_transaction_line') {
352
+ const journalEntryLine = {
353
+ ...item,
354
+ id: '',
355
+ type: 'journal_entry_transaction_line',
356
+ platformLineDetailType: item.platformLineDetailType ?? 'journal_entry_line_detail',
357
+ account: item.account ?? EMPTY_ACCOUNT,
358
+ billable: item.billable ?? 'not_billable',
359
+ isCategoryMiscategorized: false,
360
+ isClassMiscategorized: false,
361
+ lineTransactionType: item.lineTransactionType ?? 'journal_entry',
362
+ postingType: item.postingType ?? 'debit',
363
+ postingTypeName: item.postingTypeName != null && item.postingTypeName !== ''
364
+ ? item.postingTypeName
365
+ : (item.postingType ?? 'debit').charAt(0).toUpperCase() +
366
+ (item.postingType ?? 'debit').slice(1),
367
+ };
368
+ return journalEntryLine;
369
+ }
370
+ const accountAndClassLine = {
371
+ ...item,
372
+ id: '',
373
+ type: 'transaction_with_account_and_class_line',
374
+ platformLineDetailType: item.platformLineDetailType ?? 'account_based_expense_line_detail',
375
+ account: item.account ?? EMPTY_ACCOUNT,
376
+ billable: item.billable ?? 'not_billable',
377
+ isCategoryMiscategorized: false,
378
+ isClassMiscategorized: false,
379
+ };
380
+ return accountAndClassLine;
381
+ };
382
+ exports.toNewTransactionLine = toNewTransactionLine;
310
383
  const toEntityUpdatePayload = (entityUpdates) => {
311
384
  const entityUpdatesPayload = [];
312
385
  let entityKey;
@@ -64,6 +64,33 @@ export declare const journalEntryTotalsByPostingSide: (lines: ReadonlyArray<Pick
64
64
  creditTotal?: Amount;
65
65
  debitTotal?: Amount;
66
66
  };
67
+ type PostingBearer = Pick<JournalEntryTransactionLine, 'amount' | 'postingType'>;
68
+ export interface JournalEntrySplitReconciliation {
69
+ creditTotal: number;
70
+ debitTotal: number;
71
+ /** Total debits == total credits == original transaction total. */
72
+ isBalanced: boolean;
73
+ }
74
+ /**
75
+ * Sums debit and credit sides (big.js-exact) and reports whether the split is
76
+ * balanced: total debits equal total credits AND equal the original
77
+ * transaction total. The original total of a valid JE equals its total debits,
78
+ * so it is passed in by the caller (`transaction.amount.amount`).
79
+ */
80
+ export declare const journalEntrySplitReconciliation: (lines: ReadonlyArray<PostingBearer>, originalTotal: number) => JournalEntrySplitReconciliation;
81
+ export type JournalEntrySplitErrorKey = 'lineNeedsDebitOrCredit' | 'totalsMustEqual';
82
+ export interface JournalEntrySplitValidation {
83
+ ok: boolean;
84
+ errorKey?: JournalEntrySplitErrorKey;
85
+ }
86
+ /**
87
+ * Save-time validation for a JE split (Figma `4165:33028` / `4165:45379`):
88
+ * 1. every line needs a non-zero debit or credit, and
89
+ * 2. total debits == total credits == original total.
90
+ * Returns the first failing rule's `errorKey` (maps to a strings entry). Does
91
+ * NOT require category/class to be chosen — balance is the only gate.
92
+ */
93
+ export declare const validateJournalEntrySplit: (lines: ReadonlyArray<PostingBearer>, originalTotal: number) => JournalEntrySplitValidation;
67
94
  /**
68
95
  * Drops `linked_transaction_line` entries — equivalent to legacy
69
96
  * `filterOutLinkedTransactionLine` in `transactionDetail.helpers.ts`.
@@ -3,7 +3,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.sortJournalEntryLines = exports.filterJournalEntryLinesExcludingLinked = exports.journalEntryTotalsByPostingSide = exports.sumJournalEntryAmounts = exports.journalEntryDefaultSortConfig = exports.toJournalEntrySortKey = exports.JOURNAL_ENTRY_SORT_KEYS = void 0;
6
+ exports.sortJournalEntryLines = exports.filterJournalEntryLinesExcludingLinked = exports.validateJournalEntrySplit = exports.journalEntrySplitReconciliation = exports.journalEntryTotalsByPostingSide = exports.sumJournalEntryAmounts = exports.journalEntryDefaultSortConfig = exports.toJournalEntrySortKey = exports.JOURNAL_ENTRY_SORT_KEYS = void 0;
7
+ const big_js_1 = __importDefault(require("big.js"));
7
8
  const orderBy_1 = __importDefault(require("lodash/orderBy"));
8
9
  const stringToUnion_1 = require("../../commonStateTypes/stringToUnion");
9
10
  const transactionLine_1 = require("../../entity/transaction/stateTypes/transactionLine");
@@ -110,6 +111,65 @@ const journalEntryTotalsByPostingSide = (lines) => {
110
111
  };
111
112
  exports.journalEntryTotalsByPostingSide = journalEntryTotalsByPostingSide;
112
113
  // ---------------------------------------------------------------------------
114
+ // Split reconciliation & validation (Phase 2 — JE split)
115
+ // ---------------------------------------------------------------------------
116
+ // Half-cent tolerance for the balance equality checks, matching the expense
117
+ // reconciliation row's epsilon. Sums use big.js so repeated decimal addition
118
+ // doesn't drift before the comparison.
119
+ const JOURNAL_ENTRY_BALANCE_EPSILON = 0.005;
120
+ /**
121
+ * Sums debit and credit sides (big.js-exact) and reports whether the split is
122
+ * balanced: total debits equal total credits AND equal the original
123
+ * transaction total. The original total of a valid JE equals its total debits,
124
+ * so it is passed in by the caller (`transaction.amount.amount`).
125
+ */
126
+ const journalEntrySplitReconciliation = (lines, originalTotal) => {
127
+ let debit = new big_js_1.default(0);
128
+ let credit = new big_js_1.default(0);
129
+ for (const line of lines) {
130
+ const amount = new big_js_1.default(line.amount?.amount ?? 0);
131
+ if (line.postingType === 'debit') {
132
+ debit = debit.plus(amount);
133
+ }
134
+ else if (line.postingType === 'credit') {
135
+ credit = credit.plus(amount);
136
+ }
137
+ }
138
+ const balancedToEachOther = debit
139
+ .minus(credit)
140
+ .abs()
141
+ .lt(JOURNAL_ENTRY_BALANCE_EPSILON);
142
+ const balancedToOriginal = debit
143
+ .minus(originalTotal)
144
+ .abs()
145
+ .lt(JOURNAL_ENTRY_BALANCE_EPSILON);
146
+ return {
147
+ debitTotal: debit.toNumber(),
148
+ creditTotal: credit.toNumber(),
149
+ isBalanced: balancedToEachOther && balancedToOriginal,
150
+ };
151
+ };
152
+ exports.journalEntrySplitReconciliation = journalEntrySplitReconciliation;
153
+ /**
154
+ * Save-time validation for a JE split (Figma `4165:33028` / `4165:45379`):
155
+ * 1. every line needs a non-zero debit or credit, and
156
+ * 2. total debits == total credits == original total.
157
+ * Returns the first failing rule's `errorKey` (maps to a strings entry). Does
158
+ * NOT require category/class to be chosen — balance is the only gate.
159
+ */
160
+ const validateJournalEntrySplit = (lines, originalTotal) => {
161
+ const everyLineHasSide = lines.every((line) => (line.postingType === 'debit' || line.postingType === 'credit') &&
162
+ Math.abs(line.amount?.amount ?? 0) > JOURNAL_ENTRY_BALANCE_EPSILON);
163
+ if (!everyLineHasSide) {
164
+ return { ok: false, errorKey: 'lineNeedsDebitOrCredit' };
165
+ }
166
+ if (!(0, exports.journalEntrySplitReconciliation)(lines, originalTotal).isBalanced) {
167
+ return { ok: false, errorKey: 'totalsMustEqual' };
168
+ }
169
+ return { ok: true };
170
+ };
171
+ exports.validateJournalEntrySplit = validateJournalEntrySplit;
172
+ // ---------------------------------------------------------------------------
113
173
  // Filter
114
174
  // ---------------------------------------------------------------------------
115
175
  /**
@@ -834,14 +834,16 @@ const getChangedLineItemCountFromLocalData = (transaction, transactionLocalData)
834
834
  line.type === 'transaction_with_product_or_service_line' ||
835
835
  line.type === 'journal_entry_transaction_line') {
836
836
  const accountLineLocal = transactionLocalData.lineItemById[lineId];
837
- const accountLine = line;
838
- hasCategoryChanged =
839
- accountLineLocal.account?.qboId !== accountLine.account.qboId &&
840
- accountLineLocal.account?.accountId !== accountLine.account.accountId;
841
- hasClassChanged =
842
- (accountLineLocal.class?.qboId != null ||
843
- accountLine.class?.qboId != null) &&
844
- accountLineLocal.class?.qboId !== accountLine.class?.qboId;
837
+ if (accountLineLocal != null) {
838
+ const accountLine = line;
839
+ hasCategoryChanged =
840
+ accountLineLocal.account?.qboId !== accountLine.account.qboId &&
841
+ accountLineLocal.account?.accountId !== accountLine.account.accountId;
842
+ hasClassChanged =
843
+ (accountLineLocal.class?.qboId != null ||
844
+ accountLine.class?.qboId != null) &&
845
+ accountLineLocal.class?.qboId !== accountLine.class?.qboId;
846
+ }
845
847
  }
846
848
  hasDescriptionChanged =
847
849
  transactionLocalData.lineItemById[lineId] != null
@@ -8,7 +8,7 @@ import { SupportedTransaction, SupportedTransactionPayload } from '../../entity/
8
8
  import { ZeniAPIStatus } from '../../responsePayload';
9
9
  import { UncategorizedAccounts } from '../accountList/accountListSelector';
10
10
  import { TransactionDetailState, TransactionSaveUpdates } from './transactionDetailState';
11
- import { TransactionDetailLocalData, TransactionVendorUpdates } from './transactionDetailTypes';
11
+ import { TransactionDetailLineItemData, TransactionDetailLocalData, TransactionVendorUpdates } from './transactionDetailTypes';
12
12
  export declare const initialState: TransactionDetailState;
13
13
  export declare const downloadAccountingProviderAttachment: import("@reduxjs/toolkit").ActionCreatorWithPreparedPayload<[attachmentId: string], {
14
14
  attachmentId: string;
@@ -166,6 +166,15 @@ export declare const downloadAccountingProviderAttachment: import("@reduxjs/tool
166
166
  transactionId: TransactionID;
167
167
  transaction: SupportedTransaction;
168
168
  isEnabled: boolean;
169
- }, "transactionDetail/setIsVendorUpdateAllChecked", never, never>;
169
+ }, "transactionDetail/setIsVendorUpdateAllChecked", never, never>, addSplitLine: import("@reduxjs/toolkit").ActionCreatorWithPreparedPayload<[transactionId: TransactionID, newLine: TransactionDetailLineItemData], {
170
+ transactionId: TransactionID;
171
+ newLine: TransactionDetailLineItemData;
172
+ }, "transactionDetail/addSplitLine", never, never>, deleteSplitLine: import("@reduxjs/toolkit").ActionCreatorWithPreparedPayload<[transactionId: TransactionID, lineId: string], {
173
+ transactionId: TransactionID;
174
+ lineId: string;
175
+ }, "transactionDetail/deleteSplitLine", never, never>, toggleSplitEditMode: import("@reduxjs/toolkit").ActionCreatorWithPreparedPayload<[transactionId: TransactionID, editMode: boolean], {
176
+ transactionId: TransactionID;
177
+ editMode: boolean;
178
+ }, "transactionDetail/toggleSplitEditMode", never, never>;
170
179
  declare const _default: import("redux").Reducer<TransactionDetailState>;
171
180
  export default _default;
@@ -4,7 +4,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  var _a;
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
- exports.setIsVendorUpdateAllChecked = exports.updateVendorBulkRecommendationsFetchStatus = exports.updateLatestSelectedEntity = exports.resetAllLineItemsToVendor = exports.updateVendorToAllLineItems = exports.updateLocalDataWithTransactionsUpdateWithVendorCheckbox = exports.resetSelectedCustomer = exports.resetVendor = exports.updateSelectedCustomer = exports.updateSelectedVendor = exports.removeEntityRecommendationForLineIdForTransactionDetail = exports.markCategoryClassRecommendationsFailureForTransactionDetail = exports.markCategoryClassRecommendationsCompletedForTransactionDetail = exports.markCategoryClassRecommendationsInProgressForTransactionDetail = exports.setEntityRecommendationForLineIdsForTransactionDetail = exports.setAllLineItemsLocalDataIsUpdateAllChecked = exports.setAllLineItemsToCategoryClassInLocalData = exports.resetLineItemsCategoryClassInLocalData = exports.clearTransactionDetailLocalData = exports.saveTransactionDetailLocalData = exports.initializeTransactionDetailLocalData = exports.saveTransactionDetail = exports.updateTransactionStateIfUpdateActionNotAllowed = exports.updateStatusConsolidatedSaveTransactionDetail = exports.updateStatusForTransactionNotMiscategorizedUpdate = exports.deleteTransactionAttachmentSuccess = exports.deleteTransactionAttachmentFailure = exports.deleteTransactionAttachment = exports.markTransactionAsNotMiscategorized = exports.clearTransactionDetailSaveStatus = exports.updateTransactionDetailSaveStatus = exports.updateTransactionDetail = exports.uploadMissingAttachmentSuccess = exports.clearTransactionDetail = exports.removeTransactionDetail = exports.updateTransactionDetailFetchState = exports.fetchTransactionDetail = exports.downloadAccountingProviderAttachmentFailure = exports.downloadAccountingProviderAttachmentSuccess = exports.downloadAccountingProviderAttachment = exports.initialState = void 0;
7
+ exports.toggleSplitEditMode = exports.deleteSplitLine = exports.addSplitLine = exports.setIsVendorUpdateAllChecked = exports.updateVendorBulkRecommendationsFetchStatus = exports.updateLatestSelectedEntity = exports.resetAllLineItemsToVendor = exports.updateVendorToAllLineItems = exports.updateLocalDataWithTransactionsUpdateWithVendorCheckbox = exports.resetSelectedCustomer = exports.resetVendor = exports.updateSelectedCustomer = exports.updateSelectedVendor = exports.removeEntityRecommendationForLineIdForTransactionDetail = exports.markCategoryClassRecommendationsFailureForTransactionDetail = exports.markCategoryClassRecommendationsCompletedForTransactionDetail = exports.markCategoryClassRecommendationsInProgressForTransactionDetail = exports.setEntityRecommendationForLineIdsForTransactionDetail = exports.setAllLineItemsLocalDataIsUpdateAllChecked = exports.setAllLineItemsToCategoryClassInLocalData = exports.resetLineItemsCategoryClassInLocalData = exports.clearTransactionDetailLocalData = exports.saveTransactionDetailLocalData = exports.initializeTransactionDetailLocalData = exports.saveTransactionDetail = exports.updateTransactionStateIfUpdateActionNotAllowed = exports.updateStatusConsolidatedSaveTransactionDetail = exports.updateStatusForTransactionNotMiscategorizedUpdate = exports.deleteTransactionAttachmentSuccess = exports.deleteTransactionAttachmentFailure = exports.deleteTransactionAttachment = exports.markTransactionAsNotMiscategorized = exports.clearTransactionDetailSaveStatus = exports.updateTransactionDetailSaveStatus = exports.updateTransactionDetail = exports.uploadMissingAttachmentSuccess = exports.clearTransactionDetail = exports.removeTransactionDetail = exports.updateTransactionDetailFetchState = exports.fetchTransactionDetail = exports.downloadAccountingProviderAttachmentFailure = exports.downloadAccountingProviderAttachmentSuccess = exports.downloadAccountingProviderAttachment = exports.initialState = void 0;
8
8
  const toolkit_1 = require("@reduxjs/toolkit");
9
9
  const get_1 = __importDefault(require("lodash/get"));
10
10
  const entity_1 = require("../../entity/genericEntity/entity");
@@ -670,6 +670,71 @@ const transactionDetail = (0, toolkit_1.createSlice)({
670
670
  };
671
671
  },
672
672
  },
673
+ // Split transaction — Phase 1 (expense)
674
+ addSplitLine: {
675
+ reducer(draft, action) {
676
+ const key = (0, transactionDetailState_1.getTransactionDetailKey)(action.payload.transactionId);
677
+ const detail = (0, get_1.default)(draft.transactionDetailById, key, undefined);
678
+ if (detail != null) {
679
+ draft.transactionDetailById[key] = {
680
+ ...detail,
681
+ transactionDetailLocalData: {
682
+ ...detail.transactionDetailLocalData,
683
+ lineItemById: {
684
+ ...detail.transactionDetailLocalData.lineItemById,
685
+ [action.payload.newLine.id]: action.payload.newLine,
686
+ },
687
+ },
688
+ };
689
+ }
690
+ },
691
+ prepare(transactionId, newLine) {
692
+ return { payload: { transactionId, newLine } };
693
+ },
694
+ },
695
+ deleteSplitLine: {
696
+ reducer(draft, action) {
697
+ const key = (0, transactionDetailState_1.getTransactionDetailKey)(action.payload.transactionId);
698
+ const detail = (0, get_1.default)(draft.transactionDetailById, key, undefined);
699
+ if (detail == null) {
700
+ return;
701
+ }
702
+ const { lineItemById, deletedLineIds = [] } = detail.transactionDetailLocalData;
703
+ const updated = { ...lineItemById };
704
+ delete updated[action.payload.lineId];
705
+ draft.transactionDetailById[key] = {
706
+ ...detail,
707
+ transactionDetailLocalData: {
708
+ ...detail.transactionDetailLocalData,
709
+ lineItemById: updated,
710
+ deletedLineIds: [...deletedLineIds, action.payload.lineId],
711
+ },
712
+ };
713
+ },
714
+ prepare(transactionId, lineId) {
715
+ return { payload: { transactionId, lineId } };
716
+ },
717
+ },
718
+ toggleSplitEditMode: {
719
+ reducer(draft, action) {
720
+ const key = (0, transactionDetailState_1.getTransactionDetailKey)(action.payload.transactionId);
721
+ const detail = (0, get_1.default)(draft.transactionDetailById, key, undefined);
722
+ if (detail != null) {
723
+ draft.transactionDetailById[key] = {
724
+ ...detail,
725
+ transactionDetailLocalData: {
726
+ ...detail.transactionDetailLocalData,
727
+ editMode: action.payload.editMode,
728
+ // Clear delete tracking when exiting edit mode without saving.
729
+ ...(!action.payload.editMode && { deletedLineIds: [] }),
730
+ },
731
+ };
732
+ }
733
+ },
734
+ prepare(transactionId, editMode) {
735
+ return { payload: { transactionId, editMode } };
736
+ },
737
+ },
673
738
  clearRecommendedVendorsList(draft) {
674
739
  Object.assign(draft, exports.initialState);
675
740
  },
@@ -778,7 +843,7 @@ const transactionDetail = (0, toolkit_1.createSlice)({
778
843
  },
779
844
  },
780
845
  });
781
- _a = transactionDetail.actions, exports.downloadAccountingProviderAttachment = _a.downloadAccountingProviderAttachment, exports.downloadAccountingProviderAttachmentSuccess = _a.downloadAccountingProviderAttachmentSuccess, exports.downloadAccountingProviderAttachmentFailure = _a.downloadAccountingProviderAttachmentFailure, exports.fetchTransactionDetail = _a.fetchTransactionDetail, exports.updateTransactionDetailFetchState = _a.updateTransactionDetailFetchState, exports.removeTransactionDetail = _a.removeTransactionDetail, exports.clearTransactionDetail = _a.clearTransactionDetail, exports.uploadMissingAttachmentSuccess = _a.uploadMissingAttachmentSuccess, exports.updateTransactionDetail = _a.updateTransactionDetail, exports.updateTransactionDetailSaveStatus = _a.updateTransactionDetailSaveStatus, exports.clearTransactionDetailSaveStatus = _a.clearTransactionDetailSaveStatus, exports.markTransactionAsNotMiscategorized = _a.markTransactionAsNotMiscategorized, exports.deleteTransactionAttachment = _a.deleteTransactionAttachment, exports.deleteTransactionAttachmentFailure = _a.deleteTransactionAttachmentFailure, exports.deleteTransactionAttachmentSuccess = _a.deleteTransactionAttachmentSuccess, exports.updateStatusForTransactionNotMiscategorizedUpdate = _a.updateStatusForTransactionNotMiscategorizedUpdate, exports.updateStatusConsolidatedSaveTransactionDetail = _a.updateStatusConsolidatedSaveTransactionDetail, exports.updateTransactionStateIfUpdateActionNotAllowed = _a.updateTransactionStateIfUpdateActionNotAllowed, exports.saveTransactionDetail = _a.saveTransactionDetail, exports.initializeTransactionDetailLocalData = _a.initializeTransactionDetailLocalData, exports.saveTransactionDetailLocalData = _a.saveTransactionDetailLocalData, exports.clearTransactionDetailLocalData = _a.clearTransactionDetailLocalData, exports.resetLineItemsCategoryClassInLocalData = _a.resetLineItemsCategoryClassInLocalData, exports.setAllLineItemsToCategoryClassInLocalData = _a.setAllLineItemsToCategoryClassInLocalData, exports.setAllLineItemsLocalDataIsUpdateAllChecked = _a.setAllLineItemsLocalDataIsUpdateAllChecked, exports.setEntityRecommendationForLineIdsForTransactionDetail = _a.setEntityRecommendationForLineIdsForTransactionDetail, exports.markCategoryClassRecommendationsInProgressForTransactionDetail = _a.markCategoryClassRecommendationsInProgressForTransactionDetail, exports.markCategoryClassRecommendationsCompletedForTransactionDetail = _a.markCategoryClassRecommendationsCompletedForTransactionDetail, exports.markCategoryClassRecommendationsFailureForTransactionDetail = _a.markCategoryClassRecommendationsFailureForTransactionDetail, exports.removeEntityRecommendationForLineIdForTransactionDetail = _a.removeEntityRecommendationForLineIdForTransactionDetail, exports.updateSelectedVendor = _a.updateSelectedVendor, exports.updateSelectedCustomer = _a.updateSelectedCustomer, exports.resetVendor = _a.resetVendor, exports.resetSelectedCustomer = _a.resetSelectedCustomer, exports.updateLocalDataWithTransactionsUpdateWithVendorCheckbox = _a.updateLocalDataWithTransactionsUpdateWithVendorCheckbox, exports.updateVendorToAllLineItems = _a.updateVendorToAllLineItems, exports.resetAllLineItemsToVendor = _a.resetAllLineItemsToVendor, exports.updateLatestSelectedEntity = _a.updateLatestSelectedEntity, exports.updateVendorBulkRecommendationsFetchStatus = _a.updateVendorBulkRecommendationsFetchStatus, exports.setIsVendorUpdateAllChecked = _a.setIsVendorUpdateAllChecked;
846
+ _a = transactionDetail.actions, exports.downloadAccountingProviderAttachment = _a.downloadAccountingProviderAttachment, exports.downloadAccountingProviderAttachmentSuccess = _a.downloadAccountingProviderAttachmentSuccess, exports.downloadAccountingProviderAttachmentFailure = _a.downloadAccountingProviderAttachmentFailure, exports.fetchTransactionDetail = _a.fetchTransactionDetail, exports.updateTransactionDetailFetchState = _a.updateTransactionDetailFetchState, exports.removeTransactionDetail = _a.removeTransactionDetail, exports.clearTransactionDetail = _a.clearTransactionDetail, exports.uploadMissingAttachmentSuccess = _a.uploadMissingAttachmentSuccess, exports.updateTransactionDetail = _a.updateTransactionDetail, exports.updateTransactionDetailSaveStatus = _a.updateTransactionDetailSaveStatus, exports.clearTransactionDetailSaveStatus = _a.clearTransactionDetailSaveStatus, exports.markTransactionAsNotMiscategorized = _a.markTransactionAsNotMiscategorized, exports.deleteTransactionAttachment = _a.deleteTransactionAttachment, exports.deleteTransactionAttachmentFailure = _a.deleteTransactionAttachmentFailure, exports.deleteTransactionAttachmentSuccess = _a.deleteTransactionAttachmentSuccess, exports.updateStatusForTransactionNotMiscategorizedUpdate = _a.updateStatusForTransactionNotMiscategorizedUpdate, exports.updateStatusConsolidatedSaveTransactionDetail = _a.updateStatusConsolidatedSaveTransactionDetail, exports.updateTransactionStateIfUpdateActionNotAllowed = _a.updateTransactionStateIfUpdateActionNotAllowed, exports.saveTransactionDetail = _a.saveTransactionDetail, exports.initializeTransactionDetailLocalData = _a.initializeTransactionDetailLocalData, exports.saveTransactionDetailLocalData = _a.saveTransactionDetailLocalData, exports.clearTransactionDetailLocalData = _a.clearTransactionDetailLocalData, exports.resetLineItemsCategoryClassInLocalData = _a.resetLineItemsCategoryClassInLocalData, exports.setAllLineItemsToCategoryClassInLocalData = _a.setAllLineItemsToCategoryClassInLocalData, exports.setAllLineItemsLocalDataIsUpdateAllChecked = _a.setAllLineItemsLocalDataIsUpdateAllChecked, exports.setEntityRecommendationForLineIdsForTransactionDetail = _a.setEntityRecommendationForLineIdsForTransactionDetail, exports.markCategoryClassRecommendationsInProgressForTransactionDetail = _a.markCategoryClassRecommendationsInProgressForTransactionDetail, exports.markCategoryClassRecommendationsCompletedForTransactionDetail = _a.markCategoryClassRecommendationsCompletedForTransactionDetail, exports.markCategoryClassRecommendationsFailureForTransactionDetail = _a.markCategoryClassRecommendationsFailureForTransactionDetail, exports.removeEntityRecommendationForLineIdForTransactionDetail = _a.removeEntityRecommendationForLineIdForTransactionDetail, exports.updateSelectedVendor = _a.updateSelectedVendor, exports.updateSelectedCustomer = _a.updateSelectedCustomer, exports.resetVendor = _a.resetVendor, exports.resetSelectedCustomer = _a.resetSelectedCustomer, exports.updateLocalDataWithTransactionsUpdateWithVendorCheckbox = _a.updateLocalDataWithTransactionsUpdateWithVendorCheckbox, exports.updateVendorToAllLineItems = _a.updateVendorToAllLineItems, exports.resetAllLineItemsToVendor = _a.resetAllLineItemsToVendor, exports.updateLatestSelectedEntity = _a.updateLatestSelectedEntity, exports.updateVendorBulkRecommendationsFetchStatus = _a.updateVendorBulkRecommendationsFetchStatus, exports.setIsVendorUpdateAllChecked = _a.setIsVendorUpdateAllChecked, exports.addSplitLine = _a.addSplitLine, exports.deleteSplitLine = _a.deleteSplitLine, exports.toggleSplitEditMode = _a.toggleSplitEditMode;
782
847
  exports.default = transactionDetail.reducer;
783
848
  function getUpdatedEntityLocalData(detail, updates, entity, resetRecommendations, type) {
784
849
  const key = type === 'customer' ? 'customerUpdates' : 'vendorUpdates';
@@ -1,4 +1,5 @@
1
1
  import { FetchState, FetchStateAndError, FetchedState, ID } from '../../commonStateTypes/common';
2
+ import { Amount } from '../../commonStateTypes/amount';
2
3
  import { Recommendation } from '../../commonStateTypes/recommendationBase';
3
4
  import { AccountBase } from '../../entity/account/accountState';
4
5
  import { ClassBase } from '../../entity/class/classState';
@@ -6,9 +7,10 @@ import { CustomerBase } from '../../entity/customer/customerState';
6
7
  import { EntityType } from '../../entity/genericEntity/entity';
7
8
  import { ProjectBase } from '../../entity/project/projectState';
8
9
  import { Transaction, TransactionID } from '../../entity/transaction/stateTypes/transaction';
10
+ import { TransactionCategory } from '../../entity/transaction/stateTypes/transactionType';
9
11
  import { VendorBase } from '../../entity/vendor/vendorState';
10
12
  import { EntityRecommendationKey } from '../recommendation/recommendationState';
11
- import { TransactionDetailLocalData } from './transactionDetailTypes';
13
+ import { TransactionDetailLineItemData, TransactionDetailLocalData } from './transactionDetailTypes';
12
14
  export declare const getTransactionDetailKey: (transactionId: TransactionID) => `${string}_bill` | `${string}_expense` | `${string}_check` | `${string}_refund` | `${string}_invoice` | `${string}_payment` | `${string}_credit_memo` | `${string}_sales_receipt` | `${string}_delayed_credit` | `${string}_delayed_charge` | `${string}_bill_payment` | `${string}_bill_payment_check` | `${string}_bill_payment_credit_card` | `${string}_vendor_credit` | `${string}_credit_card_credit` | `${string}_transfer` | `${string}_credit_card_payment` | `${string}_deposit` | `${string}_journal_entry`;
13
15
  export type TransactionDetailKey = ReturnType<typeof getTransactionDetailKey>;
14
16
  export declare const getZeniTransactionDetailKey: (transactionId: TransactionID) => `${string}_bill` | `${string}_expense` | `${string}_check` | `${string}_refund` | `${string}_invoice` | `${string}_payment` | `${string}_credit_memo` | `${string}_sales_receipt` | `${string}_delayed_credit` | `${string}_delayed_charge` | `${string}_bill_payment` | `${string}_bill_payment_check` | `${string}_bill_payment_credit_card` | `${string}_vendor_credit` | `${string}_credit_card_credit` | `${string}_transfer` | `${string}_credit_card_payment` | `${string}_deposit` | `${string}_journal_entry`;
@@ -50,9 +52,12 @@ export interface TransactionDetailState {
50
52
  }
51
53
  export interface TransactionLineUpdates {
52
54
  account?: AccountBase;
55
+ amount?: Amount;
53
56
  class?: ClassBase;
54
57
  customer?: CustomerBase;
55
58
  lineDescription?: string;
59
+ postingType?: TransactionCategory;
60
+ postingTypeName?: string;
56
61
  project?: ProjectBase;
57
62
  recommendation?: Recommendation[];
58
63
  vendor?: VendorBase;
@@ -78,5 +83,7 @@ export interface TransactionSaveUpdates {
78
83
  entityRelatedUpdates: Record<EntityRecommendationKey, EntityRelatedRecommendationUpdates>;
79
84
  markAsMiscatUpdates: boolean;
80
85
  updates: TransactionUpdates;
86
+ addedLines?: TransactionDetailLineItemData[];
87
+ deletedLineIds?: ID[];
81
88
  }
82
89
  export {};
@@ -50,7 +50,9 @@ export interface TransactionDetailLocalData {
50
50
  qboId: ID;
51
51
  };
52
52
  customerUpdates?: TransactionVendorUpdates;
53
+ deletedLineIds?: ID[];
53
54
  documentId?: string;
55
+ editMode?: boolean;
54
56
  latestSelectedEntityOnLineItem?: Entity;
55
57
  latestUpdatedLineWithEntity?: ID;
56
58
  memo?: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zeniai/client-epic-state",
3
- "version": "5.1.42-betaDI4",
3
+ "version": "5.1.42",
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",