@zeniai/client-epic-state 5.1.37-betaRD3 → 5.1.38

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 (29) hide show
  1. package/lib/entity/snackbar/snackbarTypes.d.ts +1 -1
  2. package/lib/entity/snackbar/snackbarTypes.js +2 -0
  3. package/lib/esm/entity/snackbar/snackbarTypes.js +2 -0
  4. package/lib/esm/index.js +3 -2
  5. package/lib/esm/view/expenseAutomationView/epics/accountRecon/updateStatementInfoEpic.js +5 -3
  6. package/lib/esm/view/expenseAutomationView/epics/transactionCategorization/fetchTransactionCategorizationViewEpic.js +2 -3
  7. package/lib/esm/view/expenseAutomationView/helpers/mapStatementUpdateLocalDataToRequestBody.js +32 -0
  8. package/lib/esm/view/expenseAutomationView/payload/reconciliationPayload.js +1 -0
  9. package/lib/esm/view/expenseAutomationView/reducers/reconciliationViewReducer.js +163 -21
  10. package/lib/esm/view/expenseAutomationView/selectors/reconciliationViewSelector.js +30 -26
  11. package/lib/esm/view/expenseAutomationView/selectors/statementParseViewSelector.js +295 -0
  12. package/lib/esm/view/expenseAutomationView/selectors/transactionCategorizationSelector.js +5 -24
  13. package/lib/index.d.ts +4 -3
  14. package/lib/index.js +44 -38
  15. package/lib/view/expenseAutomationView/epics/accountRecon/updateStatementInfoEpic.js +4 -2
  16. package/lib/view/expenseAutomationView/epics/transactionCategorization/fetchTransactionCategorizationViewEpic.js +2 -3
  17. package/lib/view/expenseAutomationView/helpers/mapStatementUpdateLocalDataToRequestBody.d.ts +2 -0
  18. package/lib/view/expenseAutomationView/helpers/mapStatementUpdateLocalDataToRequestBody.js +36 -0
  19. package/lib/view/expenseAutomationView/helpers/transactionCategorizationLocalDataHelper.d.ts +1 -1
  20. package/lib/view/expenseAutomationView/payload/reconciliationPayload.js +1 -0
  21. package/lib/view/expenseAutomationView/reducers/reconciliationViewReducer.d.ts +14 -1
  22. package/lib/view/expenseAutomationView/reducers/reconciliationViewReducer.js +165 -23
  23. package/lib/view/expenseAutomationView/selectorTypes/reconciliationViewSelectorTypes.d.ts +10 -2
  24. package/lib/view/expenseAutomationView/selectors/reconciliationViewSelector.js +30 -26
  25. package/lib/view/expenseAutomationView/selectors/statementParseViewSelector.d.ts +25 -0
  26. package/lib/view/expenseAutomationView/selectors/statementParseViewSelector.js +313 -0
  27. package/lib/view/expenseAutomationView/selectors/transactionCategorizationSelector.js +5 -24
  28. package/lib/view/expenseAutomationView/types/reconciliationViewState.d.ts +52 -2
  29. package/package.json +1 -1
@@ -0,0 +1,313 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.applyStatementParseFormChange = exports.toStatementUpdateLocalDataFromFormView = exports.getStatementParseFormView = exports.mergeStatementParseDeletedTransactionIds = exports.areStatementParseBalancesConsistent = exports.resolveStatementParseBalanceFields = exports.shouldRecalculateStatementParseBalances = exports.isStatementParseManualBalanceField = exports.isStatementParseTransactionFieldChange = exports.getDateFilteredDeletedTransactionIds = exports.getStatementTransactionPeriodById = exports.getParsedStatementDataForView = exports.filterStatementTransactionsByPeriod = exports.isStatementTransactionInPeriod = exports.getEffectiveStatementPeriod = void 0;
4
+ const zeniDayJS_1 = require("../../../zeniDayJS");
5
+ const getEffectiveStatementPeriod = (parsedMeta, localMeta) => ({
6
+ statementStartDate: localMeta?.statementStartDate != null && localMeta.statementStartDate !== ''
7
+ ? localMeta.statementStartDate
8
+ : parsedMeta.statementStartDate,
9
+ statementEndDate: localMeta?.statementEndDate != null && localMeta.statementEndDate !== ''
10
+ ? localMeta.statementEndDate
11
+ : parsedMeta.statementEndDate,
12
+ });
13
+ exports.getEffectiveStatementPeriod = getEffectiveStatementPeriod;
14
+ const isStatementTransactionInPeriod = (transactionDate, statementStartDate, statementEndDate) => {
15
+ if (statementStartDate === '' ||
16
+ statementEndDate === '' ||
17
+ transactionDate === '') {
18
+ return true;
19
+ }
20
+ const txn = (0, zeniDayJS_1.date)(transactionDate).startOf('day');
21
+ const start = (0, zeniDayJS_1.date)(statementStartDate).startOf('day');
22
+ const end = (0, zeniDayJS_1.date)(statementEndDate).startOf('day');
23
+ return txn.isSameOrAfter(start, 'day') && txn.isSameOrBefore(end, 'day');
24
+ };
25
+ exports.isStatementTransactionInPeriod = isStatementTransactionInPeriod;
26
+ const filterStatementTransactionsByPeriod = (transactions, statementPeriod) => {
27
+ const { statementStartDate, statementEndDate } = statementPeriod;
28
+ if (statementStartDate === '' || statementEndDate === '') {
29
+ return transactions;
30
+ }
31
+ return transactions.filter((transaction) => (0, exports.isStatementTransactionInPeriod)(transaction.transactionDate, statementStartDate, statementEndDate));
32
+ };
33
+ exports.filterStatementTransactionsByPeriod = filterStatementTransactionsByPeriod;
34
+ const getParsedStatementDataForView = (parsedStatementData, statementUpdateLocalData) => {
35
+ if (parsedStatementData == null) {
36
+ return undefined;
37
+ }
38
+ const statementUpload = parsedStatementData.statementUpload;
39
+ if (statementUpload == null) {
40
+ return undefined;
41
+ }
42
+ const parsedMeta = statementUpload.statementMeta;
43
+ const localMeta = statementUpdateLocalData?.statementMeta;
44
+ const effectiveMeta = localMeta ?? parsedMeta;
45
+ const statementPeriod = (0, exports.getEffectiveStatementPeriod)(parsedMeta, localMeta);
46
+ return {
47
+ statementUpload: {
48
+ ...statementUpload,
49
+ statementMeta: effectiveMeta,
50
+ statementTransactions: (0, exports.filterStatementTransactionsByPeriod)(statementUpload.statementTransactions, statementPeriod),
51
+ },
52
+ };
53
+ };
54
+ exports.getParsedStatementDataForView = getParsedStatementDataForView;
55
+ const getStatementTransactionPeriodById = (allParsedTransactions, parsedMeta, localMeta) => {
56
+ const statementPeriod = (0, exports.getEffectiveStatementPeriod)(parsedMeta, localMeta);
57
+ return Object.fromEntries(allParsedTransactions
58
+ .filter((transaction) => transaction.statementTransactionId !== '')
59
+ .map((transaction) => [
60
+ transaction.statementTransactionId,
61
+ (0, exports.isStatementTransactionInPeriod)(transaction.transactionDate, statementPeriod.statementStartDate, statementPeriod.statementEndDate),
62
+ ]));
63
+ };
64
+ exports.getStatementTransactionPeriodById = getStatementTransactionPeriodById;
65
+ const getDateFilteredDeletedTransactionIds = (allParsedTransactions, statementPeriod) => {
66
+ if (statementPeriod.statementStartDate === '' ||
67
+ statementPeriod.statementEndDate === '') {
68
+ return [];
69
+ }
70
+ return allParsedTransactions
71
+ .filter((transaction) => transaction.statementTransactionId !== '' &&
72
+ !(0, exports.isStatementTransactionInPeriod)(transaction.transactionDate, statementPeriod.statementStartDate, statementPeriod.statementEndDate))
73
+ .map((transaction) => transaction.statementTransactionId);
74
+ };
75
+ exports.getDateFilteredDeletedTransactionIds = getDateFilteredDeletedTransactionIds;
76
+ const roundCurrency = (value) => Math.round(value * 100) / 100;
77
+ const toApiDateString = (value) => {
78
+ if (value == null || value === '') {
79
+ return '';
80
+ }
81
+ return (0, zeniDayJS_1.date)(value).format('YYYY-MM-DD');
82
+ };
83
+ const computeStatementTransactionTotals = (transactions) => {
84
+ const totalDeposits = roundCurrency(transactions
85
+ .filter((txn) => txn.transactionDirection === 'credit')
86
+ .reduce((sum, txn) => sum + Math.abs(txn.amount ?? 0), 0));
87
+ const totalPayments = roundCurrency(transactions
88
+ .filter((txn) => txn.transactionDirection === 'debit')
89
+ .reduce((sum, txn) => sum + Math.abs(txn.amount ?? 0), 0));
90
+ return { totalDeposits, totalPayments };
91
+ };
92
+ const computeStatementClosingBalance = (openingBalance, totalDeposits, totalPayments) => roundCurrency(openingBalance + totalDeposits - totalPayments);
93
+ const filterFormTransactionsInPeriod = (transactions, statementStartDate, statementEndDate) => {
94
+ if (statementStartDate === '' || statementEndDate === '') {
95
+ return transactions;
96
+ }
97
+ const periodStart = (0, zeniDayJS_1.date)(statementStartDate).startOf('day');
98
+ const periodEnd = (0, zeniDayJS_1.date)(statementEndDate).startOf('day');
99
+ return transactions.filter((txn) => {
100
+ if (txn.transactionDate === '') {
101
+ return true;
102
+ }
103
+ const txnDate = (0, zeniDayJS_1.date)(txn.transactionDate).startOf('day');
104
+ return (txnDate.isSameOrAfter(periodStart, 'day') &&
105
+ txnDate.isSameOrBefore(periodEnd, 'day'));
106
+ });
107
+ };
108
+ const isStatementParseTransactionFieldChange = (changedField) => changedField === 'transactions' ||
109
+ changedField?.startsWith('transactions.') === true;
110
+ exports.isStatementParseTransactionFieldChange = isStatementParseTransactionFieldChange;
111
+ const isStatementParseBalanceAffectingTransactionFieldChange = (changedField) => {
112
+ if (changedField === 'transactions') {
113
+ return true;
114
+ }
115
+ if (changedField?.startsWith('transactions.') !== true) {
116
+ return false;
117
+ }
118
+ return !changedField.endsWith('.transactionMemo');
119
+ };
120
+ const isStatementParseManualBalanceField = (field) => field === 'totalDeposits' ||
121
+ field === 'totalPayments' ||
122
+ field === 'closingBalance';
123
+ exports.isStatementParseManualBalanceField = isStatementParseManualBalanceField;
124
+ const shouldRecalculateStatementParseBalances = (changedField) => isStatementParseBalanceAffectingTransactionFieldChange(changedField) ||
125
+ changedField === 'statementStartDate' ||
126
+ changedField === 'statementEndDate';
127
+ exports.shouldRecalculateStatementParseBalances = shouldRecalculateStatementParseBalances;
128
+ const resolveStatementParseBalanceFields = (formView, changedField) => {
129
+ const inPeriodTransactions = filterFormTransactionsInPeriod(formView.transactions, formView.statementStartDate, formView.statementEndDate);
130
+ const { totalDeposits, totalPayments } = computeStatementTransactionTotals(inPeriodTransactions);
131
+ if (!(0, exports.shouldRecalculateStatementParseBalances)(changedField)) {
132
+ return {
133
+ openingBalance: formView.openingBalance,
134
+ closingBalance: formView.closingBalance,
135
+ totalDeposits: formView.totalDeposits,
136
+ totalPayments: formView.totalPayments,
137
+ };
138
+ }
139
+ return {
140
+ openingBalance: formView.openingBalance,
141
+ totalDeposits,
142
+ totalPayments,
143
+ closingBalance: computeStatementClosingBalance(formView.openingBalance, totalDeposits, totalPayments),
144
+ };
145
+ };
146
+ exports.resolveStatementParseBalanceFields = resolveStatementParseBalanceFields;
147
+ const areStatementParseBalancesConsistent = (formView) => {
148
+ const { totalDeposits, totalPayments } = computeStatementTransactionTotals(filterFormTransactionsInPeriod(formView.transactions, formView.statementStartDate, formView.statementEndDate));
149
+ return (roundCurrency(formView.closingBalance) ===
150
+ computeStatementClosingBalance(formView.openingBalance, totalDeposits, totalPayments));
151
+ };
152
+ exports.areStatementParseBalancesConsistent = areStatementParseBalancesConsistent;
153
+ const mapParsedTransactionToFormRow = (transaction, index) => ({
154
+ id: transaction.statementTransactionId || `parsed-${index}`,
155
+ transactionDate: toApiDateString(transaction.transactionDate),
156
+ transactionMemo: transaction.transactionMemo,
157
+ amount: transaction.amount,
158
+ transactionDirection: transaction.transactionDirection,
159
+ statementTransactionId: transaction.statementTransactionId,
160
+ });
161
+ const mapLocalTransactionToFormRow = (transaction, id) => ({
162
+ id,
163
+ transactionDate: toApiDateString(transaction.transactionDate),
164
+ transactionMemo: transaction.transactionMemo,
165
+ amount: transaction.amount,
166
+ transactionDirection: transaction.transactionDirection,
167
+ statementTransactionId: transaction.statementTransactionId,
168
+ });
169
+ const buildStatementParseFormTransactions = (originalTransactions, localData) => {
170
+ const deletedIdSet = new Set(localData.statementTransactions?.deletedIds ?? []);
171
+ const updatedById = new Map((localData.statementTransactions?.updated ?? [])
172
+ .filter((transaction) => transaction.statementTransactionId != null)
173
+ .map((transaction) => [
174
+ transaction.statementTransactionId,
175
+ transaction,
176
+ ]));
177
+ const persistedTransactions = originalTransactions
178
+ .filter((transaction) => !deletedIdSet.has(transaction.statementTransactionId))
179
+ .map((transaction, index) => {
180
+ const updated = updatedById.get(transaction.statementTransactionId);
181
+ return updated != null
182
+ ? mapLocalTransactionToFormRow(updated, transaction.statementTransactionId)
183
+ : mapParsedTransactionToFormRow(transaction, index);
184
+ });
185
+ const addedTransactions = (localData.statementTransactions?.added ?? []).map((transaction, index) => mapLocalTransactionToFormRow(transaction, `added-${index}`));
186
+ return [...persistedTransactions, ...addedTransactions];
187
+ };
188
+ const mergeStatementParseDeletedTransactionIds = (originalTransactions, statementPeriod, currentTransactionIds) => {
189
+ const dateFilteredDeletedIds = (0, exports.getDateFilteredDeletedTransactionIds)(originalTransactions, statementPeriod);
190
+ const dateFilteredDeletedIdSet = new Set(dateFilteredDeletedIds);
191
+ const userDeletedIds = originalTransactions
192
+ .filter((transaction) => transaction.statementTransactionId !== '' &&
193
+ !currentTransactionIds.has(transaction.statementTransactionId) &&
194
+ !dateFilteredDeletedIdSet.has(transaction.statementTransactionId))
195
+ .map((transaction) => transaction.statementTransactionId);
196
+ return [...new Set([...userDeletedIds, ...dateFilteredDeletedIds])];
197
+ };
198
+ exports.mergeStatementParseDeletedTransactionIds = mergeStatementParseDeletedTransactionIds;
199
+ const resolveStatementMetaDates = (parsedStatementMeta, localMeta) => ({
200
+ statementStartDate: localMeta?.statementStartDate != null && localMeta.statementStartDate !== ''
201
+ ? localMeta.statementStartDate
202
+ : (parsedStatementMeta?.statementStartDate ?? ''),
203
+ statementEndDate: localMeta?.statementEndDate != null && localMeta.statementEndDate !== ''
204
+ ? localMeta.statementEndDate
205
+ : (parsedStatementMeta?.statementEndDate ?? ''),
206
+ });
207
+ const getStatementParseFormView = ({ parsedStatementMeta, originalTransactions, statementUpdateLocalData, }) => {
208
+ const transactions = statementUpdateLocalData != null
209
+ ? buildStatementParseFormTransactions(originalTransactions, statementUpdateLocalData)
210
+ : originalTransactions.map(mapParsedTransactionToFormRow);
211
+ const localMeta = statementUpdateLocalData?.statementMeta;
212
+ const { statementStartDate, statementEndDate } = resolveStatementMetaDates(parsedStatementMeta, localMeta);
213
+ const { totalDeposits, totalPayments } = computeStatementTransactionTotals(filterFormTransactionsInPeriod(transactions, statementStartDate, statementEndDate));
214
+ const openingBalance = localMeta?.openingBalance ?? parsedStatementMeta?.openingBalance ?? 0;
215
+ const computedClosingBalance = computeStatementClosingBalance(openingBalance, totalDeposits, totalPayments);
216
+ return {
217
+ statementStartDate,
218
+ statementEndDate,
219
+ openingBalance,
220
+ closingBalance: localMeta?.closingBalance ?? computedClosingBalance,
221
+ totalDeposits: localMeta?.totalDeposits ?? totalDeposits,
222
+ totalPayments: localMeta?.totalPayments ?? totalPayments,
223
+ transactions: filterFormTransactionsInPeriod(transactions, statementStartDate, statementEndDate),
224
+ };
225
+ };
226
+ exports.getStatementParseFormView = getStatementParseFormView;
227
+ const toStatementUpdateLocalDataFromFormView = ({ formView, parsedStatementMeta, originalTransactions, changedField, }) => {
228
+ const balanceFields = (0, exports.resolveStatementParseBalanceFields)(formView, changedField);
229
+ const currentTransactionIds = new Set(formView.transactions
230
+ .map((transaction) => transaction.statementTransactionId)
231
+ .filter((id) => id != null && id !== ''));
232
+ const added = [];
233
+ const updated = [];
234
+ formView.transactions.forEach((transaction) => {
235
+ const transactionDate = toApiDateString(transaction.transactionDate);
236
+ if (transaction.statementTransactionId == null) {
237
+ if (transaction.transactionDirection != null) {
238
+ added.push({
239
+ amount: transaction.amount,
240
+ transactionDate,
241
+ transactionDirection: transaction.transactionDirection,
242
+ transactionMemo: transaction.transactionMemo,
243
+ });
244
+ }
245
+ return;
246
+ }
247
+ const original = originalTransactions.find((candidate) => candidate.statementTransactionId === transaction.statementTransactionId);
248
+ if (original != null &&
249
+ (original.amount !== transaction.amount ||
250
+ toApiDateString(original.transactionDate) !== transactionDate ||
251
+ original.transactionMemo !== transaction.transactionMemo)) {
252
+ updated.push({
253
+ amount: transaction.amount,
254
+ transactionDate,
255
+ transactionDirection: transaction.transactionDirection,
256
+ transactionMemo: transaction.transactionMemo,
257
+ statementTransactionId: transaction.statementTransactionId,
258
+ });
259
+ }
260
+ });
261
+ const statementPeriod = {
262
+ statementStartDate: toApiDateString(formView.statementStartDate),
263
+ statementEndDate: toApiDateString(formView.statementEndDate),
264
+ };
265
+ const deletedIds = (0, exports.mergeStatementParseDeletedTransactionIds)(originalTransactions, statementPeriod, currentTransactionIds);
266
+ const deletedIdSet = new Set(deletedIds);
267
+ const inPeriodAdded = filterFormTransactionsInPeriod(added, statementPeriod.statementStartDate, statementPeriod.statementEndDate);
268
+ const inPeriodUpdated = updated.filter((transaction) => transaction.statementTransactionId != null &&
269
+ !deletedIdSet.has(transaction.statementTransactionId));
270
+ return {
271
+ statementMeta: {
272
+ closingBalance: balanceFields.closingBalance,
273
+ openingBalance: balanceFields.openingBalance,
274
+ statementDataStatus: parsedStatementMeta?.statementDataStatus ?? {
275
+ code: '',
276
+ label: '',
277
+ },
278
+ statementEndDate: statementPeriod.statementEndDate,
279
+ statementStartDate: statementPeriod.statementStartDate,
280
+ statementStatus: parsedStatementMeta?.statementStatus ?? {
281
+ code: '',
282
+ label: '',
283
+ },
284
+ statementUploadId: parsedStatementMeta?.statementUploadId ?? '',
285
+ totalDeposits: balanceFields.totalDeposits,
286
+ totalPayments: balanceFields.totalPayments,
287
+ },
288
+ statementTransactions: {
289
+ added: inPeriodAdded,
290
+ deletedIds,
291
+ updated: inPeriodUpdated,
292
+ },
293
+ };
294
+ };
295
+ exports.toStatementUpdateLocalDataFromFormView = toStatementUpdateLocalDataFromFormView;
296
+ const applyStatementParseFormChange = ({ formView, changedField, parsedStatementMeta, originalTransactions, }) => {
297
+ const balanceFields = (0, exports.resolveStatementParseBalanceFields)(formView, changedField);
298
+ const shouldRecalculate = (0, exports.shouldRecalculateStatementParseBalances)(changedField);
299
+ const mergedFormView = shouldRecalculate
300
+ ? { ...formView, ...balanceFields }
301
+ : formView;
302
+ return {
303
+ didRecalculateBalances: shouldRecalculate,
304
+ formView: mergedFormView,
305
+ localData: (0, exports.toStatementUpdateLocalDataFromFormView)({
306
+ formView: mergedFormView,
307
+ parsedStatementMeta,
308
+ originalTransactions,
309
+ changedField,
310
+ }),
311
+ };
312
+ };
313
+ exports.applyStatementParseFormChange = applyStatementParseFormChange;
@@ -198,28 +198,15 @@ function getExpenseAutomationTransactionView(state) {
198
198
  };
199
199
  // Parent-tab badge counts. Sourced from `parent_tab_total_count` in the
200
200
  // listing response and used exclusively by the navbar / tab strip badges.
201
- // When a search or filter is active the badge switches to the filtered count
202
- // so the number tracks what the user is looking at.
203
- const autoCatTabView = expenseAutomationTransactionsViewState.transactionCategorizationView
204
- .autoCategorized;
205
- const reviewTabView = expenseAutomationTransactionsViewState.transactionCategorizationView.review;
206
- const autoCatSubTabCache = expenseAutomationTransactionsViewState.autoCategorizedSubTabCache;
207
- const autoCatFilterActive = autoCatTabView.uiState.searchString !== '' ||
208
- autoCatTabView.filters.categories.length > 0;
209
- const reviewFilterActive = reviewTabView.uiState.searchString !== '' ||
210
- reviewTabView.filters.categories.length > 0;
201
+ // Independent of which Completed sub-tab is active so the badge stays
202
+ // stable across sub-tab switches.
211
203
  const parentTotalCountByTab = expenseAutomationTransactionsViewState.parentTotalCountByTab;
212
204
  const parentTabTotalCountByTab = {
213
205
  review: monthYearPeriodId != null
214
- ? reviewFilterActive
215
- ? totalCountByReview[monthYearPeriodId]
216
- : (parentTotalCountByTab.review[monthYearPeriodId] ?? 0)
206
+ ? (parentTotalCountByTab.review[monthYearPeriodId] ?? 0)
217
207
  : 0,
218
208
  autoCategorized: monthYearPeriodId != null
219
- ? autoCatFilterActive
220
- ? (autoCatSubTabCache['all']?.totalCount[monthYearPeriodId] ??
221
- totalCountByAutoCat[monthYearPeriodId])
222
- : (parentTotalCountByTab.autoCategorized[monthYearPeriodId] ?? 0)
209
+ ? (parentTotalCountByTab.autoCategorized[monthYearPeriodId] ?? 0)
223
210
  : 0,
224
211
  };
225
212
  const fetchStateByTab = {
@@ -262,13 +249,7 @@ function getExpenseAutomationTransactionView(state) {
262
249
  isAccountingProjectsEnabled,
263
250
  projectList,
264
251
  transactionLocalData: filteredTransactionLocalData,
265
- // When a client-side category filter is active the server pageToken is
266
- // misleading — the server has more pages but the filter already reduces
267
- // what's visible. Masking pageToken here prevents the skeleton "loading"
268
- // row from appearing when the user is looking at a filtered result set.
269
- uiState: filters.categories.length > 0
270
- ? { ...uiState, pageToken: null }
271
- : uiState,
252
+ uiState,
272
253
  refreshStatus,
273
254
  saveStatus,
274
255
  markAsNotMiscategorizedStatus,
@@ -232,7 +232,6 @@ export interface ReconciliationViewState extends FetchStateAndError {
232
232
  reconTabsState: ReconciliationTabsState;
233
233
  refreshStatus: FetchStateAndError;
234
234
  selectedTab: ReconciliationViewTabType;
235
- statementProcessingFailed: boolean;
236
235
  statementUploadChosen: boolean;
237
236
  selectedAccountId?: ID;
238
237
  selectedDrawerAccountId?: ID;
@@ -259,12 +258,53 @@ export interface StatementTransactionsUpdate {
259
258
  deletedIds: string[];
260
259
  updated: StatementTransactionForUpdate[];
261
260
  }
261
+ export type StatementParseManualBalanceField = 'totalDeposits' | 'totalPayments' | 'closingBalance';
262
+ export interface StatementParseFormTransaction {
263
+ amount: number;
264
+ id: string;
265
+ transactionDate: string;
266
+ transactionDirection: 'debit' | 'credit';
267
+ transactionMemo: string;
268
+ statementTransactionId?: string;
269
+ }
270
+ export interface StatementParseFormView {
271
+ closingBalance: number;
272
+ openingBalance: number;
273
+ statementEndDate: string;
274
+ statementStartDate: string;
275
+ totalDeposits: number;
276
+ totalPayments: number;
277
+ transactions: StatementParseFormTransaction[];
278
+ }
279
+ export interface GetStatementParseFormViewInput {
280
+ originalTransactions: StatementTransaction[];
281
+ parsedStatementMeta: StatementMeta | undefined;
282
+ statementUpdateLocalData: StatementUpdateLocalData | undefined;
283
+ }
284
+ export interface ToStatementUpdateLocalDataFromFormViewInput {
285
+ formView: StatementParseFormView;
286
+ originalTransactions: StatementTransaction[];
287
+ parsedStatementMeta: StatementMeta | undefined;
288
+ changedField?: string;
289
+ }
290
+ export interface ApplyStatementParseFormChangeInput {
291
+ formView: StatementParseFormView;
292
+ originalTransactions: StatementTransaction[];
293
+ parsedStatementMeta: StatementMeta | undefined;
294
+ changedField?: string;
295
+ }
296
+ export interface ApplyStatementParseFormChangeResult {
297
+ didRecalculateBalances: boolean;
298
+ formView: StatementParseFormView;
299
+ localData: StatementUpdateLocalData;
300
+ }
262
301
  export interface StatementUpdateLocalData {
263
302
  statementMeta: StatementMeta;
264
303
  statementTransactions: StatementTransactionsUpdate;
265
304
  }
266
305
  export interface StatementUpdateLocalDataPayload {
267
306
  statement_meta: {
307
+ closing_balance: number;
268
308
  opening_balance: number;
269
309
  statement_end_date: string;
270
310
  statement_start_date: string;
@@ -299,15 +339,25 @@ export interface AccountReconciliationDataWithTransactionFetchState {
299
339
  reparseStatementStatus: FetchStateAndError;
300
340
  statementDateConflict: StatementDateConflict | null;
301
341
  statementDeleteStatus: FetchStateAndError;
302
- /** Derived from bank_status.code === 'parsing' for read-only list/drawer banners. */
342
+ /** True while statement upload/parse/reparse is in flight (local + entity signals). */
303
343
  statementParseInProgress: boolean;
304
344
  /** Client-driven fetch state for the parseStatement epic (In-Progress/Completed/Error). */
305
345
  statementParseStatus: FetchStateAndError;
346
+ /** Step progress saved when user continues parsing in background. */
347
+ statementProcessingBackgroundCompletedSteps: number;
348
+ /** User dismissed StatementProcessingPage via Continue in Background. */
349
+ statementProcessingDismissedToBackground: boolean;
350
+ /** True when statement upload/parse failed for this account (drives drawer error popup). */
351
+ statementProcessingFailed: boolean;
306
352
  statementUpdateStatus: FetchStateAndError;
307
353
  statementUploadStatus: FetchStateAndError;
308
354
  transactionFetchState: FetchStateAndError;
309
355
  accountDetectionReason?: string;
310
356
  parsedStatementData?: ParsedStatementData;
311
357
  reconciliationId?: ID;
358
+ /** Wall-clock start used to recompute UI step progress after backgrounding. */
359
+ statementProcessingStartedAtMs?: number;
360
+ /** Locked countdown duration (seconds) paired with startedAtMs. */
361
+ statementProcessingTotalDurationSeconds?: number;
312
362
  }
313
363
  export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zeniai/client-epic-state",
3
- "version": "5.1.37-betaRD3",
3
+ "version": "5.1.38",
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",