@zeniai/client-epic-state 5.2.93 → 5.2.94-beta0KB

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 (96) hide show
  1. package/lib/commonStateTypes/timeframeTick.d.ts +1 -0
  2. package/lib/commonStateTypes/timeframeTick.js +1 -0
  3. package/lib/commonStateTypes/viewAndReport/reportUIState.d.ts +2 -0
  4. package/lib/commonStateTypes/viewAndReport/viewAndReport.d.ts +2 -0
  5. package/lib/entity/reconciliationReport/reconciliationReportPayload.d.ts +1 -0
  6. package/lib/entity/reconciliationReport/reconciliationReportReducer.js +1 -0
  7. package/lib/entity/reconciliationReport/reconciliationReportState.d.ts +4 -2
  8. package/lib/entity/reconciliationReport/reconciliationReportState.js +4 -1
  9. package/lib/esm/commonStateTypes/timeframeTick.js +1 -0
  10. package/lib/esm/entity/reconciliationReport/reconciliationReportReducer.js +1 -0
  11. package/lib/esm/entity/reconciliationReport/reconciliationReportState.js +4 -1
  12. package/lib/esm/view/accountList/accountListReducer.js +2 -0
  13. package/lib/esm/view/balanceSheet/balanceSheetForTimeframeEpic.js +4 -0
  14. package/lib/esm/view/balanceSheet/balanceSheetReducer.js +8 -2
  15. package/lib/esm/view/cashFlow/cashFlowForTimeframeEpic.js +4 -0
  16. package/lib/esm/view/cashFlow/cashFlowReducer.js +2 -2
  17. package/lib/esm/view/cashInCashOut/cashInCashOutEpic.js +22 -14
  18. package/lib/esm/view/cashInCashOut/cashInCashOutReducer.js +161 -41
  19. package/lib/esm/view/cashInCashOut/cashInCashOutSelector.js +42 -15
  20. package/lib/esm/view/cashPosition/cashPositionEpic.js +21 -13
  21. package/lib/esm/view/cashPosition/cashPositionReducer.js +155 -44
  22. package/lib/esm/view/cashPosition/cashPositionSelector.js +28 -9
  23. package/lib/esm/view/invoicing/invoicingQboView/invoicingQboViewSelector.js +5 -7
  24. package/lib/esm/view/netBurnOrIncome/netBurnOrIncomeEpic.js +7 -7
  25. package/lib/esm/view/netBurnOrIncome/netBurnOrIncomeForTimeframeEpic.js +25 -10
  26. package/lib/esm/view/netBurnOrIncome/netBurnOrIncomeReducer.js +184 -40
  27. package/lib/esm/view/netBurnOrIncome/netBurnOrIncomeSelector.js +26 -8
  28. package/lib/esm/view/opEx/opExEpic.js +7 -7
  29. package/lib/esm/view/opEx/opExForTimeFrameEpic.js +21 -6
  30. package/lib/esm/view/opEx/opExReducer.js +169 -32
  31. package/lib/esm/view/opEx/opExSelector.js +28 -10
  32. package/lib/esm/view/profitAndLoss/profitAndLossForTimeframeEpic.js +4 -0
  33. package/lib/esm/view/profitAndLoss/profitAndLossReducer.js +9 -2
  34. package/lib/esm/view/revenue/revenueEpic.js +7 -7
  35. package/lib/esm/view/revenue/revenueForTimeframeEpic.js +23 -8
  36. package/lib/esm/view/revenue/revenueReducer.js +183 -40
  37. package/lib/esm/view/revenue/revenueSelector.js +30 -10
  38. package/lib/esm/view/transactionDetail/createTransaction/createTransactionSelector.js +2 -1
  39. package/lib/view/accountList/accountListReducer.js +2 -0
  40. package/lib/view/accountList/accountListState.d.ts +1 -1
  41. package/lib/view/balanceSheet/balanceSheetForTimeframeEpic.js +4 -0
  42. package/lib/view/balanceSheet/balanceSheetReducer.d.ts +3 -1
  43. package/lib/view/balanceSheet/balanceSheetReducer.js +8 -2
  44. package/lib/view/cashFlow/cashFlowForTimeframeEpic.js +4 -0
  45. package/lib/view/cashFlow/cashFlowReducer.d.ts +3 -1
  46. package/lib/view/cashFlow/cashFlowReducer.js +2 -2
  47. package/lib/view/cashInCashOut/cashInCashOutEpic.d.ts +8 -2
  48. package/lib/view/cashInCashOut/cashInCashOutEpic.js +22 -14
  49. package/lib/view/cashInCashOut/cashInCashOutReducer.d.ts +13 -1
  50. package/lib/view/cashInCashOut/cashInCashOutReducer.js +161 -41
  51. package/lib/view/cashInCashOut/cashInCashOutSelector.d.ts +2 -1
  52. package/lib/view/cashInCashOut/cashInCashOutSelector.js +42 -15
  53. package/lib/view/cashInCashOut/cashInCashOutState.d.ts +5 -1
  54. package/lib/view/cashPosition/cashPositionEpic.d.ts +8 -2
  55. package/lib/view/cashPosition/cashPositionEpic.js +21 -13
  56. package/lib/view/cashPosition/cashPositionReducer.d.ts +13 -2
  57. package/lib/view/cashPosition/cashPositionReducer.js +155 -44
  58. package/lib/view/cashPosition/cashPositionSelector.d.ts +2 -1
  59. package/lib/view/cashPosition/cashPositionSelector.js +28 -9
  60. package/lib/view/cashPosition/cashPositionState.d.ts +5 -1
  61. package/lib/view/financeStatement/financeStatementEpic.d.ts +6 -0
  62. package/lib/view/invoicing/invoicingQboView/invoicingQboViewSelector.js +5 -7
  63. package/lib/view/netBurnOrIncome/fetchNetBurnOrIncomeWithForecastEpic.d.ts +1 -0
  64. package/lib/view/netBurnOrIncome/netBurnOrIncomeEpic.js +7 -7
  65. package/lib/view/netBurnOrIncome/netBurnOrIncomeForTimeframeEpic.d.ts +2 -1
  66. package/lib/view/netBurnOrIncome/netBurnOrIncomeForTimeframeEpic.js +26 -11
  67. package/lib/view/netBurnOrIncome/netBurnOrIncomeReducer.d.ts +12 -3
  68. package/lib/view/netBurnOrIncome/netBurnOrIncomeReducer.js +184 -40
  69. package/lib/view/netBurnOrIncome/netBurnOrIncomeSelector.d.ts +1 -1
  70. package/lib/view/netBurnOrIncome/netBurnOrIncomeSelector.js +26 -8
  71. package/lib/view/netBurnOrIncome/netBurnOrIncomeState.d.ts +8 -0
  72. package/lib/view/opEx/fetchOpExWithForecastEpic.d.ts +1 -0
  73. package/lib/view/opEx/opExEpic.js +7 -7
  74. package/lib/view/opEx/opExForTimeFrameEpic.d.ts +2 -1
  75. package/lib/view/opEx/opExForTimeFrameEpic.js +22 -7
  76. package/lib/view/opEx/opExReducer.d.ts +12 -3
  77. package/lib/view/opEx/opExReducer.js +168 -31
  78. package/lib/view/opEx/opExSelector.d.ts +1 -1
  79. package/lib/view/opEx/opExSelector.js +28 -10
  80. package/lib/view/opEx/opExState.d.ts +6 -0
  81. package/lib/view/profitAndLoss/profitAndLossForTimeframeEpic.js +4 -0
  82. package/lib/view/profitAndLoss/profitAndLossReducer.d.ts +3 -1
  83. package/lib/view/profitAndLoss/profitAndLossReducer.js +9 -2
  84. package/lib/view/reportUIOptions/updateReportUIOptionCOABalancesRangeEpic.d.ts +15 -0
  85. package/lib/view/revenue/fetchRevenueWithForecastEpic.d.ts +1 -0
  86. package/lib/view/revenue/revenueEpic.js +7 -7
  87. package/lib/view/revenue/revenueForTimeframeEpic.d.ts +2 -1
  88. package/lib/view/revenue/revenueForTimeframeEpic.js +24 -9
  89. package/lib/view/revenue/revenueReducer.d.ts +12 -3
  90. package/lib/view/revenue/revenueReducer.js +182 -39
  91. package/lib/view/revenue/revenueSelector.d.ts +1 -1
  92. package/lib/view/revenue/revenueSelector.js +30 -10
  93. package/lib/view/revenue/revenueState.d.ts +8 -0
  94. package/lib/view/transactionDetail/createTransaction/createTransactionSelector.d.ts +5 -3
  95. package/lib/view/transactionDetail/createTransaction/createTransactionSelector.js +2 -1
  96. package/package.json +1 -1
@@ -10,18 +10,22 @@ export declare const defaultPeriods: {
10
10
  };
11
11
  export declare const initialRevState: RevenueState;
12
12
  export declare const initialState: RevenueWithForecastState;
13
- export declare const fetchRevenue: import("@reduxjs/toolkit").ActionCreatorWithPreparedPayload<[fetchFullReport?: any, forecastId?: string | undefined, cacheOverride?: any], {
13
+ export declare const fetchRevenue: import("@reduxjs/toolkit").ActionCreatorWithPreparedPayload<[fetchFullReport?: any, forecastId?: string | undefined, cacheOverride?: any, startDate?: string | undefined, endDate?: string | undefined], {
14
14
  fetchFullReport: any;
15
15
  forecastId: string | undefined;
16
16
  cacheOverride: any;
17
+ startDate: string | undefined;
18
+ endDate: string | undefined;
17
19
  }, "revenue/fetchRevenue", never, never>, fetchRevenueWithForecast: import("@reduxjs/toolkit").ActionCreatorWithPreparedPayload<[cacheOverride?: boolean | undefined, forecastId?: string | undefined], {
18
20
  cacheOverride: boolean;
19
21
  forecastId: string | undefined;
20
- }, "revenue/fetchRevenueWithForecast", never, never>, fetchRevenueForTimeframe: import("@reduxjs/toolkit").ActionCreatorWithPreparedPayload<[timeframe: "month" | "year" | "quarter", forecastId?: string | undefined, fetchFullReport?: boolean | undefined, cacheOverride?: boolean | undefined], {
22
+ }, "revenue/fetchRevenueWithForecast", never, never>, fetchRevenueForTimeframe: import("@reduxjs/toolkit").ActionCreatorWithPreparedPayload<[timeframe: "month" | "year" | "quarter", forecastId?: string | undefined, fetchFullReport?: boolean | undefined, cacheOverride?: boolean | undefined, startDate?: string | undefined, endDate?: string | undefined], {
21
23
  forecastId: string | undefined;
22
24
  timeframe: "month" | "year" | "quarter";
23
25
  fetchFullReport: boolean;
24
26
  cacheOverride: boolean;
27
+ startDate: string | undefined;
28
+ endDate: string | undefined;
25
29
  }, "revenue/fetchRevenueForTimeframe", never, never>, waitForForecastListThenFetchRevenueWithForecast: import("@reduxjs/toolkit").ActionCreatorWithPreparedPayload<[cacheOverride?: boolean | undefined, forecastId?: string | undefined], {
26
30
  cacheOverride: boolean;
27
31
  forecastId: string | undefined;
@@ -29,17 +33,22 @@ export declare const fetchRevenue: import("@reduxjs/toolkit").ActionCreatorWithP
29
33
  forecastId: string | undefined;
30
34
  report: ZeniReportData<RevenueReportPayload>;
31
35
  timeframe: "month" | "quarter" | "year";
32
- }, "revenue/updateRevenueForTimeframe">, updateRevenueForTimeframeFailure: import("@reduxjs/toolkit").ActionCreatorWithPreparedPayload<[error: ZeniAPIStatus<Record<string, unknown>>, forecastId: string | undefined, timeframe: "month" | "year" | "quarter"], {
36
+ isCustomDateRange?: boolean;
37
+ }, "revenue/updateRevenueForTimeframe">, updateRevenueForTimeframeFailure: import("@reduxjs/toolkit").ActionCreatorWithPreparedPayload<[error: ZeniAPIStatus<Record<string, unknown>>, forecastId: string | undefined, timeframe: "month" | "year" | "quarter", isCustomDateRange?: boolean | undefined], {
33
38
  error: ZeniAPIStatus<Record<string, unknown>>;
34
39
  forecastId: string | undefined;
35
40
  timeframe: "month" | "year" | "quarter";
41
+ isCustomDateRange: boolean;
36
42
  }, "revenue/updateRevenueForTimeframeFailure", never, never>, updateRevenueAdditionalBalancesSelection: import("@reduxjs/toolkit").ActionCreatorWithPayload<{
37
43
  additionalBalances: COABalanceType[];
38
44
  isCompareModeOn: boolean;
39
45
  maxNumOfPeriodsToHighlight: number;
40
46
  }, "revenue/updateRevenueAdditionalBalancesSelection">, updateRevenueCOABalancesRange: import("@reduxjs/toolkit").ActionCreatorWithPayload<{
41
47
  selectedCOABalancesRange: COABalancesRange;
48
+ customEndDate?: string;
49
+ customStartDate?: string;
42
50
  highlightedCOABalancesRange?: COABalancesRangeWithThisPeriod;
51
+ isCustomDateRange?: boolean;
43
52
  }, "revenue/updateRevenueCOABalancesRange">, updateRevenueThisPeriod: import("@reduxjs/toolkit").ActionCreatorWithPayload<{
44
53
  thisPeriod: TimeframeTick | undefined;
45
54
  }, "revenue/updateRevenueThisPeriod">, updateRevenueDownloadState: import("@reduxjs/toolkit").ActionCreatorWithPayload<FetchState, "revenue/updateRevenueDownloadState">, updateRevenueTimeFrame: import("@reduxjs/toolkit").ActionCreatorWithPayload<"month" | "year" | "quarter", "revenue/updateRevenueTimeFrame">, updateRevenueUIState: import("@reduxjs/toolkit").ActionCreatorWithPayload<{
@@ -73,8 +73,16 @@ const revenue = (0, toolkit_1.createSlice)({
73
73
  initialState: exports.initialState,
74
74
  reducers: {
75
75
  fetchRevenue: {
76
- prepare(fetchFullReport = false, forecastId, cacheOverride = false) {
77
- return { payload: { fetchFullReport, forecastId, cacheOverride } };
76
+ prepare(fetchFullReport = false, forecastId, cacheOverride = false, startDate, endDate) {
77
+ return {
78
+ payload: {
79
+ fetchFullReport,
80
+ forecastId,
81
+ cacheOverride,
82
+ startDate,
83
+ endDate,
84
+ },
85
+ };
78
86
  },
79
87
  reducer(
80
88
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
@@ -85,22 +93,49 @@ const revenue = (0, toolkit_1.createSlice)({
85
93
  },
86
94
  },
87
95
  fetchRevenueForTimeframe: {
88
- prepare(timeframe, forecastId, fetchFullReport = true, cacheOverride = false) {
96
+ prepare(timeframe, forecastId, fetchFullReport = true, cacheOverride = false, startDate, endDate) {
89
97
  return {
90
- payload: { forecastId, timeframe, fetchFullReport, cacheOverride },
98
+ payload: {
99
+ forecastId,
100
+ timeframe,
101
+ fetchFullReport,
102
+ cacheOverride,
103
+ startDate,
104
+ endDate,
105
+ },
91
106
  };
92
107
  },
93
108
  reducer(draft, action) {
94
109
  const timeframe = action.payload.timeframe;
95
110
  const revState = getState(draft, draft.uiState.isForecastModeOn, action.payload.forecastId);
96
- const fetchState = {
97
- ...revState.fetchState,
98
- };
99
- fetchState[timeframe] = {
100
- fetchState: 'In-Progress',
101
- error: undefined,
102
- };
103
- updateState(draft, action.payload.forecastId, { fetchState });
111
+ // Route by the fetch's own dates, not the global flag: a custom fetch
112
+ // (carries start/end) marks customFetchState so the shared fetchState the
113
+ // dashboard reads is never left In-Progress by a detail-screen fetch.
114
+ const isCustomDateRange = action.payload.startDate != null && action.payload.endDate != null;
115
+ if (isCustomDateRange) {
116
+ const customFetchState = {
117
+ ...(revState.customFetchState ?? {
118
+ month: { fetchState: 'Not-Started', error: undefined },
119
+ quarter: { fetchState: 'Not-Started', error: undefined },
120
+ year: { fetchState: 'Not-Started', error: undefined },
121
+ }),
122
+ };
123
+ customFetchState[timeframe] = {
124
+ fetchState: 'In-Progress',
125
+ error: undefined,
126
+ };
127
+ updateState(draft, action.payload.forecastId, { customFetchState });
128
+ }
129
+ else {
130
+ const fetchState = {
131
+ ...revState.fetchState,
132
+ };
133
+ fetchState[timeframe] = {
134
+ fetchState: 'In-Progress',
135
+ error: undefined,
136
+ };
137
+ updateState(draft, action.payload.forecastId, { fetchState });
138
+ }
104
139
  },
105
140
  },
106
141
  fetchRevenueWithForecast: {
@@ -130,28 +165,47 @@ const revenue = (0, toolkit_1.createSlice)({
130
165
  },
131
166
  },
132
167
  updateRevenueForTimeframe(draft, action) {
133
- doUpdateRevenue(draft, action.payload.forecastId, action.payload.timeframe, action.payload.report);
168
+ doUpdateRevenue(draft, action.payload.forecastId, action.payload.timeframe, action.payload.report, action.payload.isCustomDateRange === true);
134
169
  },
135
170
  updateRevenueForTimeframeFailure: {
136
- prepare(error, forecastId, timeframe) {
171
+ prepare(error, forecastId, timeframe, isCustomDateRange = false) {
137
172
  return {
138
173
  payload: {
139
174
  error,
140
175
  forecastId,
141
176
  timeframe,
177
+ isCustomDateRange,
142
178
  },
143
179
  };
144
180
  },
145
181
  reducer(draft, action) {
146
182
  const revState = getState(draft, draft.uiState.isForecastModeOn, action.payload.forecastId);
147
- const fetchState = {
148
- ...revState.fetchState,
149
- };
150
- fetchState[action.payload.timeframe] = {
151
- fetchState: 'Error',
152
- error: action.payload.error,
153
- };
154
- updateState(draft, action.payload.forecastId, { fetchState });
183
+ // Route the error like the request/success: a custom (dated) fetch's
184
+ // failure lands in customFetchState so the shared default the dashboard
185
+ // reads isn't marked Error, and the detail's customFetchState isn't left
186
+ // stuck In-Progress.
187
+ if (action.payload.isCustomDateRange === true) {
188
+ const customFetchState = revState.customFetchState ?? {
189
+ month: { fetchState: 'Not-Started', error: undefined },
190
+ quarter: { fetchState: 'Not-Started', error: undefined },
191
+ year: { fetchState: 'Not-Started', error: undefined },
192
+ };
193
+ customFetchState[action.payload.timeframe] = {
194
+ fetchState: 'Error',
195
+ error: action.payload.error,
196
+ };
197
+ updateState(draft, action.payload.forecastId, { customFetchState });
198
+ }
199
+ else {
200
+ const fetchState = {
201
+ ...revState.fetchState,
202
+ };
203
+ fetchState[action.payload.timeframe] = {
204
+ fetchState: 'Error',
205
+ error: action.payload.error,
206
+ };
207
+ updateState(draft, action.payload.forecastId, { fetchState });
208
+ }
155
209
  },
156
210
  },
157
211
  updateRevenueAdditionalBalancesSelection(draft, action) {
@@ -170,7 +224,9 @@ const revenue = (0, toolkit_1.createSlice)({
170
224
  ? true
171
225
  : false;
172
226
  // Note: We don't want to update selected & highlighted ranges in Ui State when forecast is ON
173
- if (dataAvailable != null && draft.uiState.isForecastModeOn === false) {
227
+ if (dataAvailable != null &&
228
+ draft.uiState.isForecastModeOn === false &&
229
+ draft.uiState.customTimeframe == null) {
174
230
  const coaBalances = (0, getSelectedAndHighlightedRanges_1.getAvailableCOABalancesWithMostRecentNonZeroBalance)(revenueReport.firstMonthOfFY, timeframe, [income, cogs, grossMargin], dataAvailable);
175
231
  const thisPeriod = (0, thisPeriodHelpers_1.extractThisPeriod)(revenueReport.firstMonthOfFY, timeframe, selectedCOABalancesRange, coaBalances);
176
232
  if (thisPeriod != null &&
@@ -199,6 +255,48 @@ const revenue = (0, toolkit_1.createSlice)({
199
255
  }
200
256
  },
201
257
  updateRevenueCOABalancesRange(draft, action) {
258
+ if (action.payload.isCustomDateRange != null) {
259
+ if (action.payload.isCustomDateRange === true) {
260
+ // Convert the string dates the action carries to ZeniDate values and
261
+ // store as a single customTimeframe. Presence signals custom-mode active.
262
+ draft.uiState.customTimeframe =
263
+ action.payload.customStartDate != null &&
264
+ action.payload.customEndDate != null
265
+ ? {
266
+ startDate: (0, zeniDayJS_1.date)(action.payload.customStartDate),
267
+ endDate: (0, zeniDayJS_1.date)(action.payload.customEndDate),
268
+ }
269
+ : undefined;
270
+ // Invalidate the shared default-window fetchState so dashboardEpic
271
+ // refetches the default data on the next dashboard visit — the custom
272
+ // fetch writes into the shared section store, and this lets the card
273
+ // self-heal without a reload. The detail screen reads customFetchState.
274
+ draft.revenue.fetchState = {
275
+ month: { fetchState: 'Not-Started', error: undefined },
276
+ quarter: { fetchState: 'Not-Started', error: undefined },
277
+ year: { fetchState: 'Not-Started', error: undefined },
278
+ };
279
+ }
280
+ else {
281
+ draft.uiState.customTimeframe = undefined;
282
+ }
283
+ }
284
+ // A custom date range drives the visible window directly: honor the exact
285
+ // numberOfPeriods + thisPeriod from the payload instead of re-deriving
286
+ // (which clamps the range to the available/non-zero periods).
287
+ if (draft.uiState.customTimeframe != null) {
288
+ draft.uiState.selectedCOABalancesRange =
289
+ action.payload.selectedCOABalancesRange;
290
+ if (action.payload.highlightedCOABalancesRange != null) {
291
+ // Clamp the highlight to the same cap the non-custom path enforces so
292
+ // a wide custom range doesn't highlight all N periods at once.
293
+ draft.uiState.highlightedCOABalancesRange = {
294
+ ...action.payload.highlightedCOABalancesRange,
295
+ numberOfPeriods: Math.min(action.payload.highlightedCOABalancesRange.numberOfPeriods, draft.uiState.maxNumOfPeriodsToHighlight),
296
+ };
297
+ }
298
+ return;
299
+ }
202
300
  // Note: When thisPeriod changes the selected and highlighted balances ranges should be calculated based on full report period.
203
301
  const revenueReport = getState(draft, undefined, undefined);
204
302
  const { income, cogs, grossMargin, dataAvailable } = revenueReport;
@@ -242,6 +340,13 @@ const revenue = (0, toolkit_1.createSlice)({
242
340
  }
243
341
  },
244
342
  updateRevenueThisPeriod(draft, action) {
343
+ // In custom-date mode the picker drives the window; skip re-deriving the
344
+ // selected/highlighted ranges from the default dataAvailable (the epic
345
+ // re-dispatches the default thisPeriod on every month response, which
346
+ // would otherwise clamp the custom window back to the default).
347
+ if (draft.uiState.customTimeframe != null) {
348
+ return;
349
+ }
245
350
  // Note: When thisPeriod changes the selected and highlighted balances ranges should be calculated based on full report period.
246
351
  const revenueReport = getState(draft, undefined, undefined);
247
352
  const { income, cogs, grossMargin, dataAvailable } = revenueReport;
@@ -291,7 +396,7 @@ const revenue = (0, toolkit_1.createSlice)({
291
396
  draft.uiState.maxNumOfPeriodsToHighlight =
292
397
  exports.initialState.uiState.maxNumOfPeriodsToHighlight;
293
398
  }
294
- if (dataAvailable != null) {
399
+ if (dataAvailable != null && draft.uiState.customTimeframe == null) {
295
400
  const timeframe = draft.uiState.timeframe;
296
401
  const coaBalances = (0, getSelectedAndHighlightedRanges_1.getAvailableCOABalancesWithMostRecentNonZeroBalance)(revenueReport.firstMonthOfFY, timeframe, [income, cogs, grossMargin], dataAvailable);
297
402
  const selectionRanges = (0, getSelectedAndHighlightedRanges_1.getInitialSelectedAndHighlightedRanges)({
@@ -346,37 +451,75 @@ const revenue = (0, toolkit_1.createSlice)({
346
451
  });
347
452
  _a = revenue.actions, exports.fetchRevenue = _a.fetchRevenue, exports.fetchRevenueWithForecast = _a.fetchRevenueWithForecast, exports.fetchRevenueForTimeframe = _a.fetchRevenueForTimeframe, exports.waitForForecastListThenFetchRevenueWithForecast = _a.waitForForecastListThenFetchRevenueWithForecast, exports.updateRevenueForTimeframe = _a.updateRevenueForTimeframe, exports.updateRevenueForTimeframeFailure = _a.updateRevenueForTimeframeFailure, exports.updateRevenueAdditionalBalancesSelection = _a.updateRevenueAdditionalBalancesSelection, exports.updateRevenueCOABalancesRange = _a.updateRevenueCOABalancesRange, exports.updateRevenueThisPeriod = _a.updateRevenueThisPeriod, exports.updateRevenueDownloadState = _a.updateRevenueDownloadState, exports.updateRevenueTimeFrame = _a.updateRevenueTimeFrame, exports.updateRevenueUIState = _a.updateRevenueUIState, exports.revenueToggleForecastMode = _a.revenueToggleForecastMode;
348
453
  exports.default = revenue.reducer;
349
- const doUpdateRevenue = (draft, forecastId, timeframe, revReport) => {
454
+ const doUpdateRevenue = (draft, forecastId, timeframe, revReport, isCustomDateRange) => {
350
455
  const revenue = getState(draft, draft.uiState.isForecastModeOn, forecastId);
351
456
  const report = (0, viewAndReportPayloadv2_1.mapReportPayloadV2ToReportV2)(revReport.report);
352
457
  const { income, cogs, insights, currency } = revReport.report;
353
458
  const incomeCOABalance = (0, coaBalancePayload_1.mapCOABalanceGroupedByPeriodPayloadV2ToCOABalanceGroupedByPeriod)(income, currency, timeframe);
354
459
  const cogsCOABalance = (0, coaBalancePayload_1.mapCOABalanceGroupedByPeriodPayloadV2ToCOABalanceGroupedByPeriod)(cogs, currency, timeframe);
355
460
  const grossMarginCOABalance = (0, coaBalancePayload_1.mapCOABalanceGroupedByPeriodPayloadV2ToCOABalanceGroupedByPeriod)(revReport.report.gross_profit, currency, timeframe);
356
- revenue.income = Object.assign({}, revenue.income, incomeCOABalance);
357
- revenue.cogs = Object.assign({}, revenue.cogs, cogsCOABalance);
358
- revenue.grossMargin = Object.assign({}, revenue.grossMargin, grossMarginCOABalance);
359
- if (timeframe === 'month' && insights != null) {
461
+ // Route custom-date results (identified by the fetch's own dates, not the
462
+ // global flag) to the separate slots so the dashboard card (which reads
463
+ // income/cogs/grossMargin) keeps the default window.
464
+ if (isCustomDateRange) {
465
+ revenue.customIncome = Object.assign({}, revenue.customIncome, incomeCOABalance);
466
+ revenue.customCogs = Object.assign({}, revenue.customCogs, cogsCOABalance);
467
+ revenue.customGrossMargin = Object.assign({}, revenue.customGrossMargin, grossMarginCOABalance);
468
+ }
469
+ else {
470
+ revenue.income = Object.assign({}, revenue.income, incomeCOABalance);
471
+ revenue.cogs = Object.assign({}, revenue.cogs, cogsCOABalance);
472
+ revenue.grossMargin = Object.assign({}, revenue.grossMargin, grossMarginCOABalance);
473
+ }
474
+ // Insights are shared (read by the dashboard card); a custom slice must not
475
+ // overwrite them.
476
+ if (timeframe === 'month' && insights != null && !isCustomDateRange) {
360
477
  // we will get insights only in the month timeframe api
361
478
  revenue.insights = (0, insightSummaryPayload_1.mapInsightsPayloadtoInsightsState)(insights);
362
479
  }
363
480
  const isIncomeEmpty = (0, reportsHelpers_1.isCOABalanceGroupedByPeriodEmptyForTimeframe)(incomeCOABalance, timeframe);
364
481
  const isCogsEmpty = (0, reportsHelpers_1.isCOABalanceGroupedByPeriodEmptyForTimeframe)(cogsCOABalance, timeframe);
482
+ // Derive dataAvailable from the slot this fetch actually populated.
365
483
  const cogsOrIncome = isIncomeEmpty
366
484
  ? isCogsEmpty
367
- ? revenue.grossMargin
368
- : revenue.cogs
369
- : revenue.income;
370
- revenue.dataAvailable = report.dataAvailable
371
- ? report.dataAvailable
372
- : (0, dataAvailable_1.getDataAvailableDateRange)(cogsOrIncome);
485
+ ? isCustomDateRange
486
+ ? revenue.customGrossMargin
487
+ : revenue.grossMargin
488
+ : isCustomDateRange
489
+ ? revenue.customCogs
490
+ : revenue.cogs
491
+ : isCustomDateRange
492
+ ? revenue.customIncome
493
+ : revenue.income;
494
+ // Don't let a custom slice shrink the shared dataAvailable the dashboard uses
495
+ // for its period count; a default fetch always updates it, a custom one only
496
+ // populates it on first load (when nothing has set it yet).
497
+ if (!isCustomDateRange || revenue.dataAvailable == null) {
498
+ revenue.dataAvailable =
499
+ report.dataAvailable ?? (0, dataAvailable_1.getDataAvailableDateRange)(cogsOrIncome ?? {});
500
+ }
373
501
  revenue.firstMonthOfFY = report.firstMonthOfFY;
374
502
  revenue.bookCloseDate = report.bookCloseDate;
375
- revenue.fetchState[timeframe].fetchState = 'Completed';
376
- revenue.fetchState[timeframe].error = undefined;
503
+ // Mark the matching fetchState Completed. A custom fetch uses customFetchState so
504
+ // dashboardEpic's Completed check on the default fetchState is untouched.
505
+ if (isCustomDateRange) {
506
+ const customFetchState = revenue.customFetchState ?? {
507
+ month: { fetchState: 'Not-Started', error: undefined },
508
+ quarter: { fetchState: 'Not-Started', error: undefined },
509
+ year: { fetchState: 'Not-Started', error: undefined },
510
+ };
511
+ customFetchState[timeframe] = { fetchState: 'Completed', error: undefined };
512
+ revenue.customFetchState = customFetchState;
513
+ }
514
+ else {
515
+ revenue.fetchState[timeframe].fetchState = 'Completed';
516
+ revenue.fetchState[timeframe].error = undefined;
517
+ }
377
518
  if (revenue.dataAvailable != null &&
378
- forecastId == null // Note: We don't want to update selected & highlighted ranges in Ui State when forecast is ON
379
- ) {
519
+ forecastId == null && // Note: We don't want to update selected & highlighted ranges in Ui State when forecast is ON
520
+ // A custom date range drives the window; don't reset it to the default on
521
+ // every report receipt (this is what clamped wide ranges back to 8).
522
+ draft.uiState.customTimeframe == null) {
380
523
  const timeframe = draft.uiState.timeframe;
381
524
  const coaBalances = (0, getSelectedAndHighlightedRanges_1.getAvailableCOABalancesWithMostRecentNonZeroBalance)(revenue.firstMonthOfFY, timeframe, [revenue.income, revenue.cogs, revenue.grossMargin], revenue.dataAvailable);
382
525
  const selectionRanges = (0, getSelectedAndHighlightedRanges_1.getInitialSelectedAndHighlightedRanges)({
@@ -8,7 +8,7 @@ import { RootState } from '../../rootStateTypes';
8
8
  import { ForecastListState } from '../forecastList/forecastListState';
9
9
  import { RevenueReport, RevenueUIStateSelectorView } from '../revenue/revenueSelectorTypes';
10
10
  import { RevenueWithForecastState } from './revenueState';
11
- export declare const getRevenueReport: (filter: COABalancesFilter, accountState: AccountState, accountGroupState: AccountGroupState, sectionsState: SectionAccountsViewState, forecastListState: ForecastListState, revenueWithForecastState: RevenueWithForecastState, forecastState: ForecastState, forecastId?: ID) => RevenueReport;
11
+ export declare const getRevenueReport: (filter: COABalancesFilter, accountState: AccountState, accountGroupState: AccountGroupState, sectionsState: SectionAccountsViewState, forecastListState: ForecastListState, revenueWithForecastState: RevenueWithForecastState, forecastState: ForecastState, forecastId?: ID, readCustomRangeSlot?: boolean) => RevenueReport;
12
12
  export declare const getRevenueUIState: (state: RootState) => RevenueUIStateSelectorView;
13
13
  export declare const getRevenueForSelectedRange: (state: RootState) => RevenueReport;
14
14
  export declare const getRevenueForHighlightedRange: (state: RootState) => RevenueReport;
@@ -16,14 +16,26 @@ const revenueSelectorTypes_1 = require("../revenue/revenueSelectorTypes");
16
16
  const revenueForTimeframeEpic_1 = require("./revenueForTimeframeEpic");
17
17
  const revenueHelper_1 = require("./revenueHelper");
18
18
  const revenueReducer_1 = require("./revenueReducer");
19
- const getRevenueReport = (filter, accountState, accountGroupState, sectionsState, forecastListState, revenueWithForecastState, forecastState, forecastId) => {
19
+ const getRevenueReport = (filter, accountState, accountGroupState, sectionsState, forecastListState, revenueWithForecastState, forecastState, forecastId, readCustomRangeSlot) => {
20
20
  const revState = (0, revenueReducer_1.getState)(revenueWithForecastState, revenueWithForecastState.uiState.isForecastModeOn, forecastId);
21
+ // The detail screen opts into customFetchState so its Completed check fires
22
+ // off the custom fetch; the dashboard (no flag) reads the shared fetchState,
23
+ // which custom-mode activation reset to Not-Started so dashboardEpic refetches.
24
+ const activeFetchState = readCustomRangeSlot === true &&
25
+ revenueWithForecastState.uiState.customTimeframe != null &&
26
+ revState.customFetchState != null
27
+ ? revState.customFetchState
28
+ : revState.fetchState;
21
29
  const fetchState = (0, reduceFetchState_1.reduceAnyFetchState)([
22
- revState.fetchState.month,
23
- revState.fetchState.quarter,
24
- revState.fetchState.year,
30
+ activeFetchState.month,
31
+ activeFetchState.quarter,
32
+ activeFetchState.year,
25
33
  ]);
26
- const reportId = (0, revenueForTimeframeEpic_1.reportIdUniqueKey)(forecastId);
34
+ // Custom detail reads the isolated custom section-accounts slot; dashboard
35
+ // (no flag) reads the default slot, so a default fetch can't clobber it.
36
+ const useCustomRange = readCustomRangeSlot === true &&
37
+ revenueWithForecastState.uiState.customTimeframe != null;
38
+ const reportId = (0, revenueForTimeframeEpic_1.reportIdUniqueKey)(forecastId, useCustomRange);
27
39
  const sections = {};
28
40
  const calculatedSections = {};
29
41
  let allTimeframeTicks = [];
@@ -49,16 +61,16 @@ const getRevenueReport = (filter, accountState, accountGroupState, sectionsState
49
61
  const timeframe = revenueWithForecastState.uiState.timeframe;
50
62
  // To get incomeBalances without forecast init.
51
63
  const incomeBalance = (0, sectionAccountsViewSelector_1.getSectionBalances)(sectionsState, {
52
- reportId: (0, revenueForTimeframeEpic_1.reportIdUniqueKey)(), // reportId without forecastId init to fetch Accouts incomeBalances
64
+ reportId: (0, revenueForTimeframeEpic_1.reportIdUniqueKey)(undefined, useCustomRange), // reportId without forecastId init to fetch Accouts incomeBalances
53
65
  sectionId: 'income',
54
66
  });
55
67
  // To get cogsBalance without forecast init.
56
68
  const cogsBalance = (0, sectionAccountsViewSelector_1.getSectionBalances)(sectionsState, {
57
- reportId: (0, revenueForTimeframeEpic_1.reportIdUniqueKey)(), // reportId without forecastId init to fetch Accouts cogsBalance
69
+ reportId: (0, revenueForTimeframeEpic_1.reportIdUniqueKey)(undefined, useCustomRange), // reportId without forecastId init to fetch Accouts cogsBalance
58
70
  sectionId: 'cogs',
59
71
  });
60
72
  const grossMarginBalances = (0, sectionAccountsViewSelector_1.getSectionBalances)(sectionsState, {
61
- reportId: (0, revenueForTimeframeEpic_1.reportIdUniqueKey)(), // reportId without forecastId init to fetch Accouts cogsBalance
73
+ reportId: (0, revenueForTimeframeEpic_1.reportIdUniqueKey)(undefined, useCustomRange), // reportId without forecastId init to fetch Accouts cogsBalance
62
74
  sectionId: 'grossMargin',
63
75
  });
64
76
  const incomeOrCogs = incomeBalance != null
@@ -100,6 +112,8 @@ const getRevenueReport = (filter, accountState, accountGroupState, sectionsState
100
112
  allTimeframeTicks,
101
113
  insights: revenueWithForecastState.revenue.insights ?? [], // Picking insights from RevenueState in both foregast mode on and off
102
114
  availableNumberOfPeriods: (0, dataAvailable_1.getNumberOfPeriods)(revState.firstMonthOfFY, filter.timeframe, revState.dataAvailable),
115
+ // Surface the saved custom timeframe so the detail picker can restore them.
116
+ customTimeframe: revenueWithForecastState.uiState.customTimeframe,
103
117
  };
104
118
  };
105
119
  exports.getRevenueReport = getRevenueReport;
@@ -157,7 +171,10 @@ exports.getRevenueForSelectedRange = (0, toolkit_1.createSelector)((state) => st
157
171
  selectedForecast?.id != null &&
158
172
  (0, exports.getRevenueReportFetchState)(selectedForecast.id, revenueWithForecastState).fetchState === 'Completed'
159
173
  ? getRevenueWithForecastOnDataAvailableDateRange(noFilter, selectedForecast?.id, accountState, accountGroupState, sectionAccountsViewState, forecastListState, revenueWithForecastState, forecastState)
160
- : (0, exports.getRevenueReport)(noFilter, accountState, accountGroupState, sectionAccountsViewState, forecastListState, revenueWithForecastState, forecastState, selectedForecast?.id);
174
+ : (0, exports.getRevenueReport)(noFilter, accountState, accountGroupState, sectionAccountsViewState, forecastListState, revenueWithForecastState, forecastState, selectedForecast?.id,
175
+ // Detail selectors read the custom slot when the detail is in custom
176
+ // mode; the dashboard's getRevenueForLast4Periods does not.
177
+ revenueWithForecastState.uiState.customTimeframe != null);
161
178
  });
162
179
  exports.getRevenueForHighlightedRange = (0, toolkit_1.createSelector)((state) => state.accountGroupState, (state) => state.accountState, (state) => state.revenueState, (state) => state.forecastState, (state) => state.forecastListState, (state) => state.sectionAccountsViewState, (accountGroupState, accountState, revenueWithForecastState, forecastState, forecastListState, sectionsState) => {
163
180
  const { timeframe, highlightedCOABalancesRange, selectedAdditionalBalances, maxNumOfPeriodsToHighlight, isForecastModeOn, selectedForecast, } = revenueWithForecastState.uiState;
@@ -168,7 +185,10 @@ exports.getRevenueForHighlightedRange = (0, toolkit_1.createSelector)((state) =>
168
185
  (0, exports.getRevenueReportFetchState)(selectedForecast.id, revenueWithForecastState)
169
186
  .fetchState === 'Completed'
170
187
  ? getRevenueWithForecastOnDataAvailableDateRange(filter, selectedForecast?.id, accountState, accountGroupState, sectionsState, forecastListState, revenueWithForecastState, forecastState)
171
- : (0, exports.getRevenueReport)(filter, accountState, accountGroupState, sectionsState, forecastListState, revenueWithForecastState, forecastState, selectedForecast?.id);
188
+ : (0, exports.getRevenueReport)(filter, accountState, accountGroupState, sectionsState, forecastListState, revenueWithForecastState, forecastState, selectedForecast?.id,
189
+ // Detail selectors read the custom slot when the detail is in custom
190
+ // mode; the dashboard's getRevenueForLast4Periods does not.
191
+ revenueWithForecastState.uiState.customTimeframe != null);
172
192
  });
173
193
  /**
174
194
  * Get Revenue with forecast data overlaid on actuals.
@@ -19,6 +19,14 @@ export interface RevenueState extends Report {
19
19
  };
20
20
  insights: InsightSummary[];
21
21
  cogs?: COABalanceGroupedByPeriod;
22
+ customCogs?: COABalanceGroupedByPeriod;
23
+ customFetchState?: {
24
+ month: FetchStateAndError;
25
+ quarter: FetchStateAndError;
26
+ year: FetchStateAndError;
27
+ };
28
+ customGrossMargin?: COABalanceGroupedByPeriod;
29
+ customIncome?: COABalanceGroupedByPeriod;
22
30
  forecastId?: ID;
23
31
  grossMargin?: COABalanceGroupedByPeriod;
24
32
  income?: COABalanceGroupedByPeriod;
@@ -10,14 +10,16 @@ import { RootState } from '../../../rootStateTypes';
10
10
  import { CreatableTransactionType, CreateTransactionLocalData } from './createTransactionState';
11
11
  export interface CreateTransactionView extends SelectorView {
12
12
  /**
13
- * COA and class trees for the header account and the line dropdowns. The hierarchies rather
14
- * than flat lists, because the create page builds its grouped "Recommended" / "All" options
15
- * with the same helpers the detail page uses.
13
+ * COA and class trees for the line dropdowns. The hierarchies rather than flat lists, because
14
+ * the create page builds its grouped "Recommended" / "All" options with the same helpers the
15
+ * detail page uses.
16
16
  */
17
17
  accountListNestedAccountHierarchy: NestedAccountHierarchyForReport[];
18
18
  classListNestedAccountHierarchy: NestedClassHierarchyForReport[];
19
19
  createStatus: FetchStateAndError;
20
20
  localData: CreateTransactionLocalData;
21
+ /** Bank, credit card and other current asset accounts for the header payment account. */
22
+ paymentAccountListNestedAccountHierarchy: NestedAccountHierarchyForReport[];
21
23
  transactionType: CreatableTransactionType;
22
24
  /** Populated for transfer only; the COA would offer ineligible accounts. */
23
25
  transferAccountsList: AccountBase[];
@@ -45,7 +45,8 @@ exports.getCreateTransactionView = (0, toolkit_1.createSelector)([
45
45
  createStatus: createTransactionsState.createStatus,
46
46
  createdTransactionId: createTransactionsState.createdTransactionId,
47
47
  vendorList: Object.values(vendorByID),
48
- accountListNestedAccountHierarchy: (0, accountListSelector_1.getNestedAccountListHierarchy)(accountListState, 'accountList'),
48
+ accountListNestedAccountHierarchy: (0, accountListSelector_1.getNestedAccountListHierarchy)(accountListState, 'createTransactionLineAccountList'),
49
+ paymentAccountListNestedAccountHierarchy: (0, accountListSelector_1.getNestedAccountListHierarchy)(accountListState, 'createTransactionPaymentAccountList'),
49
50
  classListNestedAccountHierarchy: (0, classListSelector_1.getNestedClassListHierarchy)(classListState),
50
51
  transferAccountsList: transactionType === 'transfer' ? transferAccountsList : [],
51
52
  fetchState: createTransactionsState.createStatus.fetchState,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zeniai/client-epic-state",
3
- "version": "5.2.93",
3
+ "version": "5.2.94-beta0KB",
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",