@zeniai/client-epic-state 4.19.91-beta0ND → 4.19.91-beta2ND

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 (23) hide show
  1. package/lib/commonStateTypes/reduceFetchState.d.ts +9 -0
  2. package/lib/commonStateTypes/reduceFetchState.js +26 -0
  3. package/lib/esm/commonStateTypes/reduceFetchState.js +25 -0
  4. package/lib/esm/index.js +2 -2
  5. package/lib/esm/view/financeStatement/financeStatementReducer.js +2 -2
  6. package/lib/esm/view/financeStatement/financeStatementSelector.js +8 -12
  7. package/lib/esm/view/profitAndLossClassesView/profitAndLossClassesByClassHorizontalSelector.js +44 -48
  8. package/lib/esm/view/profitAndLossClassesView/profitAndLossClassesViewReducer.js +1 -0
  9. package/lib/esm/view/profitAndLossClassesView/profitAndLossClassesViewSelector.js +2 -29
  10. package/lib/esm/view/profitAndLossClassesView/profitAndLossForTimeframeClassesViewEpic.js +7 -5
  11. package/lib/esm/view/reportUIOptions/updateReportUIOptionCOABalancesRangeEpic.js +9 -7
  12. package/lib/index.d.ts +2 -2
  13. package/lib/index.js +14 -13
  14. package/lib/tsconfig.typecheck.tsbuildinfo +1 -1
  15. package/lib/view/financeStatement/financeStatementReducer.js +2 -2
  16. package/lib/view/financeStatement/financeStatementSelector.js +8 -12
  17. package/lib/view/profitAndLossClassesView/profitAndLossClassesByClassHorizontalSelector.js +44 -48
  18. package/lib/view/profitAndLossClassesView/profitAndLossClassesViewReducer.js +1 -0
  19. package/lib/view/profitAndLossClassesView/profitAndLossClassesViewSelector.js +2 -29
  20. package/lib/view/profitAndLossClassesView/profitAndLossClassesViewState.d.ts +2 -0
  21. package/lib/view/profitAndLossClassesView/profitAndLossForTimeframeClassesViewEpic.js +7 -5
  22. package/lib/view/reportUIOptions/updateReportUIOptionCOABalancesRangeEpic.js +8 -6
  23. package/package.json +1 -1
@@ -11,6 +11,15 @@ export declare const isAllFetchCompleted: (fetchStateList: FetchStateAndError[])
11
11
  * @return single fetchStateWithError
12
12
  */
13
13
  export declare function reduceFetchState(fetchStateList: FetchStateAndError[]): FetchStateAndError;
14
+ /**
15
+ * Combine multiple independent fetches (e.g. month / quarter / year for one report).
16
+ * In-Progress if any fetch is in progress; Completed only when all complete; Error if
17
+ * any failed once nothing is still in progress.
18
+ *
19
+ * Differs from {@link reduceFetchState} (requires all In-Progress for that state) and
20
+ * from {@link reduceAllFetchState} (Error only when every fetch failed).
21
+ */
22
+ export declare function reduceFetchStateParallelTimeframes(fetchStateList: FetchStateAndError[]): FetchStateAndError;
14
23
  /**
15
24
  * Reduces list of fetch state to boolean
16
25
  * @param fetchStateList list of fetch states
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.reduceAnyCompletedFetchState = exports.reduceAllFetchState = exports.reduceAnyFetchState = exports.isAnyFetchCompleted = exports.isAnyFetchInProgress = exports.isAllFetchCompleted = void 0;
4
4
  exports.reduceFetchState = reduceFetchState;
5
+ exports.reduceFetchStateParallelTimeframes = reduceFetchStateParallelTimeframes;
5
6
  /**
6
7
  * Reduces list of fetch state to boolean
7
8
  * @param fetchStateList list of fetch states
@@ -55,6 +56,31 @@ function reduceFetchState(fetchStateList) {
55
56
  error,
56
57
  };
57
58
  }
59
+ /**
60
+ * Combine multiple independent fetches (e.g. month / quarter / year for one report).
61
+ * In-Progress if any fetch is in progress; Completed only when all complete; Error if
62
+ * any failed once nothing is still in progress.
63
+ *
64
+ * Differs from {@link reduceFetchState} (requires all In-Progress for that state) and
65
+ * from {@link reduceAllFetchState} (Error only when every fetch failed).
66
+ */
67
+ function reduceFetchStateParallelTimeframes(fetchStateList) {
68
+ const isAnyProgress = fetchStateList.some((s) => s.fetchState === 'In-Progress');
69
+ const isAllCompleted = fetchStateList.every((s) => s.fetchState === 'Completed');
70
+ const isAnyError = fetchStateList.some((s) => s.fetchState === 'Error');
71
+ const error = reduceError(fetchStateList);
72
+ let fetchState = 'Not-Started';
73
+ if (isAnyProgress) {
74
+ fetchState = 'In-Progress';
75
+ }
76
+ else if (isAllCompleted) {
77
+ fetchState = 'Completed';
78
+ }
79
+ else if (isAnyError) {
80
+ fetchState = 'Error';
81
+ }
82
+ return { fetchState, error };
83
+ }
58
84
  /**
59
85
  * Reduces list of fetch state to boolean
60
86
  * @param fetchStateList list of fetch states
@@ -50,6 +50,31 @@ export function reduceFetchState(fetchStateList) {
50
50
  error,
51
51
  };
52
52
  }
53
+ /**
54
+ * Combine multiple independent fetches (e.g. month / quarter / year for one report).
55
+ * In-Progress if any fetch is in progress; Completed only when all complete; Error if
56
+ * any failed once nothing is still in progress.
57
+ *
58
+ * Differs from {@link reduceFetchState} (requires all In-Progress for that state) and
59
+ * from {@link reduceAllFetchState} (Error only when every fetch failed).
60
+ */
61
+ export function reduceFetchStateParallelTimeframes(fetchStateList) {
62
+ const isAnyProgress = fetchStateList.some((s) => s.fetchState === 'In-Progress');
63
+ const isAllCompleted = fetchStateList.every((s) => s.fetchState === 'Completed');
64
+ const isAnyError = fetchStateList.some((s) => s.fetchState === 'Error');
65
+ const error = reduceError(fetchStateList);
66
+ let fetchState = 'Not-Started';
67
+ if (isAnyProgress) {
68
+ fetchState = 'In-Progress';
69
+ }
70
+ else if (isAllCompleted) {
71
+ fetchState = 'Completed';
72
+ }
73
+ else if (isAnyError) {
74
+ fetchState = 'Error';
75
+ }
76
+ return { fetchState, error };
77
+ }
53
78
  /**
54
79
  * Reduces list of fetch state to boolean
55
80
  * @param fetchStateList list of fetch states
package/lib/esm/index.js CHANGED
@@ -14,7 +14,7 @@ import { getActualPeriodOfFY, getActualPeriodOfFYQtr, } from './commonStateTypes
14
14
  import { getFYMonths } from './commonStateTypes/fiscalYearHelpers/getFYMonths';
15
15
  import { getFYQuarterAndYear, getLastMonthOfFYQuarter, getLastMonthOfFYYear, } from './commonStateTypes/fiscalYearHelpers/getFYQuarterAndYear';
16
16
  import { getStartOfAndEndOfTimeframeForFY } from './commonStateTypes/fiscalYearHelpers/getStartOfAndEndOfTimeframeFY';
17
- import { isAllFetchCompleted, isAnyFetchInProgress, reduceAllFetchState, reduceAnyCompletedFetchState, reduceAnyFetchState, reduceFetchState, } from './commonStateTypes/reduceFetchState';
17
+ import { isAllFetchCompleted, isAnyFetchInProgress, reduceAllFetchState, reduceAnyCompletedFetchState, reduceAnyFetchState, reduceFetchState, reduceFetchStateParallelTimeframes, } from './commonStateTypes/reduceFetchState';
18
18
  import { toReimbursementTypeCode } from './commonStateTypes/reimbursementTypeCode';
19
19
  import { stringToUnion, stringToUnionStrict, } from './commonStateTypes/stringToUnion';
20
20
  import { SCHEDULE_DAYS_OF_MONTH, convertToPeriod, toAbsoluteDay, toMonth, toMonthStrict, toMonthYearPeriodId, toQuarter, toQuarterStrict, toScheduleDaysOfMonth, } from './commonStateTypes/timePeriod';
@@ -544,7 +544,7 @@ export { saveRealTimeApproval, updateIsEditModeRealTimeApprovals, };
544
544
  export { deleteRemi, fetchRemiDetail, cancelAndDeleteRemi, approveOrRejectRemi, clearRemiDetailView, fetchDuplicateReimbursement, clearDuplicateReimbursementDetail, removeDuplicateReimbursementByLineId, };
545
545
  export { getEditRemiDetail, };
546
546
  export { fetchEditRemiDetailPage, fetchRecommendationsAndUpdateMerchantRecommendations, fetchRemiAndInitializeLocalStore, initializeRemiToLocalStore, saveRemiUpdatesToLocalStore, fetchCurrencyConversionValue, discardRemiUpdatesInLocalStore, removeFileFromRemiUpdatesInLocalStore, saveRemiDetail, saveRemiSuccessOrFailure, parseReceiptsToRemi, clearEditRemiViewDetail, updateAddRemiAutoFields, updateUploadFetchState, updateTentativeMerchantNames, clearTentativeMerchantName, updateReimbursementType, updateHomeCurrencyConversion, clearAddRemiAutoFields, };
547
- export { reduceFetchState, reduceAnyFetchState, reduceAllFetchState, reduceAnyCompletedFetchState, isAnyFetchInProgress, isAllFetchCompleted, ALL_FILE_TYPES, toFileTypeStrict, };
547
+ export { reduceFetchState, reduceFetchStateParallelTimeframes, reduceAnyFetchState, reduceAllFetchState, reduceAnyCompletedFetchState, isAnyFetchInProgress, isAllFetchCompleted, ALL_FILE_TYPES, toFileTypeStrict, };
548
548
  export { getNextNthWorkingDay, getPreviousNthWorkingDay, isHolidayToday, filterDays, getYearsList, isHoliday, holidaysFormatted, PAYMENT_BUSINESS_DAYS, };
549
549
  export { fetchCompanyOnboardingView, fetchQBOConnectionPool, updateQBOConnectionPoolExternalConnection, fetchOnboardingCompletedCompanies, saveOnboardingCustomerCompletedStatus, toProductType, toProductTypeStrict, getOnboardingCockpitView, getNewOnboardingCustomerView, getOnboardingEmailGroup, initializeOnboardingCustomerViewUpdateData, clearOnboardingCustomerViewUpdateData, saveOnboardingCustomerViewUpdateData, updateOnboardingCustomerListUIState, updateCustomerCreationStatus, updateStatusAfterOnboardingCompleted, saveOnboardingCustomerViewUpdates, saveOnboardingCustomerNotes, saveOnboardingCustomerDataInLocalStore, updateOnboardingCustomerDataInLocalStore, resetNewOnboardedCustomerId, sendOnboardingCustomerViewInvite, retryBankAccountConnectionForOnboarding, };
550
550
  export { getTransactionsListByCategoryType, getTransactionListUIStateByCategoryType, };
@@ -44,7 +44,7 @@ const financeStatement = createSlice({
44
44
  updateFinanceStatementThisPeriod(draft, action) {
45
45
  const { firstMonthOfFY, coaBalances, maxNumOfPeriodsToHighlight } = action.payload;
46
46
  const { timeframe, selectedCOABalancesRange } = draft;
47
- const safeCoaBalances = coaBalances ?? [];
47
+ const safeCoaBalances = coaBalances != null ? coaBalances : [];
48
48
  if (safeCoaBalances.length > 0) {
49
49
  const prevThisPeriod = extractThisPeriod(action.payload.firstMonthOfFY, timeframe, selectedCOABalancesRange, safeCoaBalances);
50
50
  if (prevThisPeriod != null) {
@@ -77,7 +77,7 @@ const financeStatement = createSlice({
77
77
  },
78
78
  updateFinanceStatementAdditionalBalancesSelection(draft, action) {
79
79
  const { firstMonthOfFY, additionalBalances: additionalBalancesPayload, coaBalances, maxNumOfPeriodsToHighlight, } = action.payload;
80
- const safeCoaBalances = coaBalances ?? [];
80
+ const safeCoaBalances = coaBalances != null ? coaBalances : [];
81
81
  const additionalBalances = additionalBalancesPayload ?? [];
82
82
  let tempAdditionalBalances = [...additionalBalances];
83
83
  if (additionalBalances.includes('this_period_vs_last_period') ||
@@ -43,15 +43,11 @@ export const getFinanceStatement = createSelector((state) => state.accountState,
43
43
  if (dataAvailable != null) {
44
44
  const noFilter = newBalancesFilterForDateRange(firstMonthOfFY, financeStatementState.timeframe, dataAvailable, 'ascending_date', []);
45
45
  const profitAndLossForTimeframeTicks = getProfitAndLossReport(noFilter, undefined, accountState, accountGroupState, sectionAccountsViewState, profitAndLossState, forecastState);
46
- const expensesSection = profitAndLossForTimeframeTicks.sections.expenses;
47
- const incomeSection = profitAndLossForTimeframeTicks.sections.income;
48
- const expensesBalances = expensesSection?.balances;
49
- const incomeBalances = incomeSection?.balances;
50
- if (expensesSection != null) {
51
- balancesInTimeframe =
52
- (expensesBalances?.length ?? 0) > 0
53
- ? expensesBalances ?? []
54
- : incomeBalances ?? [];
46
+ const expensesBalances = profitAndLossForTimeframeTicks.sections.expenses?.balances ?? [];
47
+ const incomeBalances = profitAndLossForTimeframeTicks.sections.income?.balances ?? [];
48
+ balancesInTimeframe =
49
+ expensesBalances.length > 0 ? expensesBalances : incomeBalances;
50
+ if (balancesInTimeframe.length > 0) {
55
51
  allTimeframeTicks = balancesInTimeframe.map((balance) => toTimeframeTick(balance));
56
52
  }
57
53
  }
@@ -65,9 +61,9 @@ export const getFinanceStatement = createSelector((state) => state.accountState,
65
61
  const profitAndLossReport = getProfitAndLossReport(filter, undefined, accountState, accountGroupState, sectionAccountsViewState, profitAndLossState, forecastState);
66
62
  const classFilter = newBalancesFilterClassesView(filter.firstMonthOfFY, filter.timeframe, filter.balancesRange.numberOfPeriods, filter.balancesRange.thisPeriod, filter.balancesRange.orderBy, filter.additionalBalances, profitAndLossClassesViewState.uiState.classesToFilterOut);
67
63
  const profitAndLossByClassReport = getProfitAndLossClassesView(classFilter, accountState, classState, sectionsState, profitAndLossClassesViewState);
68
- const profitAndLossByClassHorizontalReport = profitAndLossClassesViewState.uiState.classViewLayout === 'horizontal'
69
- ? getProfitAndLossClassesHorizontalView(classFilter, accountState, classState, sectionsState, profitAndLossClassesViewState, thisPeriod)
70
- : getProfitAndLossClassesHorizontalView(classFilter, accountState, classState, sectionsState, profitAndLossClassesViewState, undefined);
64
+ const profitAndLossByClassHorizontalReport = getProfitAndLossClassesHorizontalView(classFilter, accountState, classState, sectionsState, profitAndLossClassesViewState, profitAndLossClassesViewState.uiState.classViewLayout === 'horizontal'
65
+ ? thisPeriod
66
+ : undefined);
71
67
  const balanceSheetReport = getBalanceSheet(filter, accountState, accountGroupState, sectionAccountsViewState, balanceSheetState);
72
68
  const cashFlowReport = getCashFlow(filter, accountState, accountGroupState, sectionAccountsViewState, cashFlowState);
73
69
  const allReports = [
@@ -1,7 +1,11 @@
1
1
  import { toAmountWC } from '../../commonStateTypes/amount';
2
+ import { reduceFetchStateParallelTimeframes } from '../../commonStateTypes/reduceFetchState';
2
3
  import { getSectionClassesViewReport } from '../../entity/sectionClassesViewV2/sectionClassesViewSelector';
3
- import { ALL_PANDL_REPORT_VIEW_SECTION_IDS, } from './profitAndLossClassesViewSelectorTypes';
4
- const DEFAULT_CURRENCY = { currencyCode: 'USD', currencySymbol: '$' };
4
+ /** If `ProfitAndLossClassesViewState.currency` is unset (e.g. tests). */
5
+ const FALLBACK_REPORT_CURRENCY = {
6
+ currencyCode: 'USD',
7
+ currencySymbol: '$',
8
+ };
5
9
  export const profitAndLossHorizontalIdsInOrder = [
6
10
  'income',
7
11
  'cogs',
@@ -24,8 +28,16 @@ const SECTION_TITLES = {
24
28
  netOtherIncome: 'Net Other Income',
25
29
  netIncome: 'Net Income',
26
30
  };
31
+ /** Sections fetched and pivoted for horizontal layout (excludes e.g. `netOperatingIncome`). */
32
+ const HORIZONTAL_PANDL_SECTION_IDS = [
33
+ 'income',
34
+ 'cogs',
35
+ 'expenses',
36
+ 'otherIncome',
37
+ 'otherExpenses',
38
+ ];
27
39
  export const getProfitAndLossClassesHorizontalView = (filter, accountState, classState, sectionsState, profitAndLossClassesViewState, selectedPeriod) => {
28
- const fetchState = reduceFetchState([
40
+ const fetchState = reduceFetchStateParallelTimeframes([
29
41
  profitAndLossClassesViewState.fetchState.month,
30
42
  profitAndLossClassesViewState.fetchState.quarter,
31
43
  profitAndLossClassesViewState.fetchState.year,
@@ -35,24 +47,20 @@ export const getProfitAndLossClassesHorizontalView = (filter, accountState, clas
35
47
  const classColumns = buildClassColumns(profitAndLossClassesViewState.classHierarchy, profitAndLossClassesViewState.uiState.classesToFilterOut);
36
48
  const sections = {};
37
49
  const calculatedSections = {};
50
+ let reportCurrency = FALLBACK_REPORT_CURRENCY;
38
51
  if (fetchState.fetchState === 'Completed' && selectedPeriod != null) {
39
52
  // Get section reports using existing selector infrastructure
40
53
  const sectionReports = {};
41
- ALL_PANDL_REPORT_VIEW_SECTION_IDS.forEach((sectionId) => {
54
+ HORIZONTAL_PANDL_SECTION_IDS.forEach((sectionId) => {
42
55
  sectionReports[sectionId] = getSectionClassesViewReport(accountState, classState, sectionsState, { reportId, sectionId }, filter);
43
56
  });
57
+ reportCurrency =
58
+ profitAndLossClassesViewState.currency ?? FALLBACK_REPORT_CURRENCY;
44
59
  // Pivot each section from class→account to account→class
45
- const sectionIds = [
46
- 'income',
47
- 'cogs',
48
- 'expenses',
49
- 'otherIncome',
50
- 'otherExpenses',
51
- ];
52
- sectionIds.forEach((sectionId) => {
60
+ HORIZONTAL_PANDL_SECTION_IDS.forEach((sectionId) => {
53
61
  const sectionReport = sectionReports[sectionId];
54
62
  if (sectionReport != null) {
55
- sections[sectionId] = pivotSectionToHorizontal(sectionReport, classColumns, SECTION_TITLES[sectionId] ?? sectionId);
63
+ sections[sectionId] = pivotSectionToHorizontal(sectionReport, classColumns, SECTION_TITLES[sectionId] ?? sectionId, reportCurrency);
56
64
  }
57
65
  });
58
66
  // Calculate derived sections
@@ -83,7 +91,7 @@ export const getProfitAndLossClassesHorizontalView = (filter, accountState, clas
83
91
  }
84
92
  }
85
93
  // Build synthetic header COABalances for rendering class names as column headers
86
- const headerBalances = buildHeaderBalances(classColumns, filter.timeframe, selectedPeriod);
94
+ const headerBalances = buildHeaderBalances(classColumns, filter.timeframe, selectedPeriod, reportCurrency);
87
95
  return {
88
96
  reportId,
89
97
  reportTitle: 'Profit and Loss by Class',
@@ -104,19 +112,21 @@ export const getProfitAndLossClassesHorizontalView = (filter, accountState, clas
104
112
  * Build column definitions from class hierarchy (top-level classes only).
105
113
  */
106
114
  function buildClassColumns(classHierarchy, classesToFilterOut) {
115
+ const filteredIds = new Set(classesToFilterOut.map((id) => id[0]));
107
116
  return classHierarchy
108
- .filter((c) => !classesToFilterOut.some((filteredId) => filteredId[0] === c.classId[0]))
117
+ .filter((c) => !filteredIds.has(c.classId[0]))
109
118
  .map((c) => ({
110
119
  classId: c.classId[0],
111
120
  className: c.className,
112
121
  nestedClassId: c.classId,
113
122
  qboId: c.qboId,
114
- }));
123
+ }))
124
+ .sort((a, b) => a.className.localeCompare(b.className, undefined, { sensitivity: 'base' }));
115
125
  }
116
126
  /**
117
127
  * Pivot a section from class→account→balances to account→class→balance (single period).
118
128
  */
119
- function pivotSectionToHorizontal(section, classColumns, title) {
129
+ function pivotSectionToHorizontal(section, classColumns, title, currency) {
120
130
  // Map: accountId -> { accountName, classId -> balance }
121
131
  const accountMap = new Map();
122
132
  // Also track class totals
@@ -126,7 +136,7 @@ function pivotSectionToHorizontal(section, classColumns, title) {
126
136
  section.classes.forEach((classReport) => {
127
137
  const classId = classReport.id[0];
128
138
  // Only process classes that are in our columns
129
- if (!classColumns.some((col) => col.classId === classId)) {
139
+ if (!classTotalMap.has(classId)) {
130
140
  return;
131
141
  }
132
142
  // Sum all sliced period balances (same slice as vertical columns; multi-period = range total)
@@ -140,27 +150,27 @@ function pivotSectionToHorizontal(section, classColumns, title) {
140
150
  accountMap.forEach((accountData, accountId) => {
141
151
  const classBalances = classColumns.map((col) => ({
142
152
  classId: col.classId,
143
- balance: toAmountWC(accountData.classBalances.get(col.classId) ?? 0, DEFAULT_CURRENCY),
153
+ balance: toAmountWC(accountData.classBalances.get(col.classId) ?? 0, currency),
144
154
  }));
145
155
  const rowTotal = classBalances.reduce((sum, cb) => sum + cb.balance.amount, 0);
146
156
  accounts.push({
147
157
  accountId,
148
158
  accountName: accountData.accountName,
149
159
  classBalances,
150
- rowTotal: toAmountWC(rowTotal, DEFAULT_CURRENCY),
160
+ rowTotal: toAmountWC(rowTotal, currency),
151
161
  });
152
162
  });
153
163
  // Build class totals
154
164
  const classTotals = classColumns.map((col) => ({
155
165
  classId: col.classId,
156
- balance: toAmountWC(classTotalMap.get(col.classId) ?? 0, DEFAULT_CURRENCY),
166
+ balance: toAmountWC(classTotalMap.get(col.classId) ?? 0, currency),
157
167
  }));
158
168
  const sectionTotal = classTotals.reduce((sum, ct) => sum + ct.balance.amount, 0);
159
169
  return {
160
170
  title,
161
171
  accounts,
162
172
  classTotals,
163
- sectionTotal: toAmountWC(sectionTotal, DEFAULT_CURRENCY),
173
+ sectionTotal: toAmountWC(sectionTotal, currency),
164
174
  };
165
175
  }
166
176
  /**
@@ -205,8 +215,7 @@ function collectAccountsFromNestedAccount(nestedAccount, classId, accountMap) {
205
215
  function sumBalancesInSlice(balances) {
206
216
  let sum = 0;
207
217
  for (const bal of balances) {
208
- const b = bal;
209
- sum += b.balance?.amount ?? 0;
218
+ sum += bal.balance?.amount ?? 0;
210
219
  }
211
220
  return sum;
212
221
  }
@@ -214,15 +223,19 @@ function sumBalancesInSlice(balances) {
214
223
  * Compute a calculated section by adding or subtracting two sets of class totals.
215
224
  */
216
225
  function computeCalculatedSection(aTotals, bTotals, aTotal, bTotal, operation, title) {
226
+ const currency = {
227
+ currencyCode: aTotal.currencyCode,
228
+ currencySymbol: aTotal.currencySymbol,
229
+ };
230
+ const bTotalsMap = new Map(bTotals.map((b) => [b.classId, b.balance.amount]));
217
231
  const classTotals = aTotals.map((a) => {
218
- const bMatch = bTotals.find((b) => b.classId === a.classId);
219
- const bAmount = bMatch?.balance.amount ?? 0;
232
+ const bAmount = bTotalsMap.get(a.classId) ?? 0;
220
233
  const resultAmount = operation === 'add'
221
234
  ? a.balance.amount + bAmount
222
235
  : a.balance.amount - bAmount;
223
236
  return {
224
237
  classId: a.classId,
225
- balance: toAmountWC(resultAmount, DEFAULT_CURRENCY),
238
+ balance: toAmountWC(resultAmount, currency),
226
239
  };
227
240
  });
228
241
  const sectionTotalAmount = operation === 'add'
@@ -231,20 +244,20 @@ function computeCalculatedSection(aTotals, bTotals, aTotal, bTotal, operation, t
231
244
  return {
232
245
  title,
233
246
  classTotals,
234
- sectionTotal: toAmountWC(sectionTotalAmount, DEFAULT_CURRENCY),
247
+ sectionTotal: toAmountWC(sectionTotalAmount, currency),
235
248
  };
236
249
  }
237
250
  /**
238
251
  * Build synthetic COABalance array for header rendering.
239
252
  * One entry per class column + one for "Total".
240
253
  */
241
- function buildHeaderBalances(classColumns, timeframe, selectedPeriod) {
254
+ function buildHeaderBalances(classColumns, timeframe, selectedPeriod, currency) {
242
255
  if (selectedPeriod == null) {
243
256
  return [];
244
257
  }
245
258
  const { endDate, startDate } = selectedPeriod;
246
259
  const balances = classColumns.map((col) => ({
247
- balance: toAmountWC(0, DEFAULT_CURRENCY),
260
+ balance: toAmountWC(0, currency),
248
261
  balanceTimeFrame: timeframe,
249
262
  endDate,
250
263
  startDate,
@@ -253,7 +266,7 @@ function buildHeaderBalances(classColumns, timeframe, selectedPeriod) {
253
266
  }));
254
267
  // Add a "Total" column
255
268
  balances.push({
256
- balance: toAmountWC(0, DEFAULT_CURRENCY),
269
+ balance: toAmountWC(0, currency),
257
270
  balanceTimeFrame: timeframe,
258
271
  endDate,
259
272
  startDate,
@@ -262,20 +275,3 @@ function buildHeaderBalances(classColumns, timeframe, selectedPeriod) {
262
275
  });
263
276
  return balances;
264
277
  }
265
- function reduceFetchState(fetchStateList) {
266
- const isAnyProgress = fetchStateList.some((s) => s.fetchState === 'In-Progress');
267
- const isAllCompleted = fetchStateList.every((s) => s.fetchState === 'Completed');
268
- const isAnyError = fetchStateList.some((s) => s.fetchState === 'Error');
269
- const error = fetchStateList.find((s) => s.error != null)?.error;
270
- let fetchState = 'Not-Started';
271
- if (isAnyProgress) {
272
- fetchState = 'In-Progress';
273
- }
274
- else if (isAllCompleted) {
275
- fetchState = 'Completed';
276
- }
277
- else if (isAnyError) {
278
- fetchState = 'Error';
279
- }
280
- return { fetchState, error };
281
- }
@@ -110,6 +110,7 @@ export const { fetchProfitAndLossClassesView, fetchProfitAndLossForTimeframeClas
110
110
  export default profitAndLossClassesView.reducer;
111
111
  const doUpdatedProfitAndLossClassesView = (draft, timeframe, pandLReport) => {
112
112
  const report = mapReportPayloadV2ToReportV2(pandLReport.report);
113
+ draft.currency = report.currency;
113
114
  draft.dataAvailable = report.dataAvailable;
114
115
  draft.firstMonthOfFY = report.firstMonthOfFY;
115
116
  draft.bookCloseDate = report.bookCloseDate;
@@ -1,6 +1,7 @@
1
1
  import { getDifferenceOfBalancesGroupedByPeriod } from '../../commonStateTypes/coaBalance/classBalances/getDifferenceOfBalancesGroupedByPeriod';
2
2
  import { getSumOfBalancesByGroupedByPeriod } from '../../commonStateTypes/coaBalance/classBalances/getSumOfBalancesGroupedByPeriod';
3
3
  import { getCOABalances } from '../../commonStateTypes/coaBalance/coaBalance';
4
+ import { reduceFetchStateParallelTimeframes } from '../../commonStateTypes/reduceFetchState';
4
5
  import { getSectionClassesViewReport } from '../../entity/sectionClassesViewV2/sectionClassesViewSelector';
5
6
  import { ALL_PANDL_REPORT_VIEW_SECTION_IDS, } from './profitAndLossClassesViewSelectorTypes';
6
7
  export const profitAndLossIdsInOrder = [
@@ -15,7 +16,7 @@ export const profitAndLossIdsInOrder = [
15
16
  'netIncome',
16
17
  ];
17
18
  export const getProfitAndLossClassesView = (filter, accountState, classState, sectionsState, profitAndLossClassesViewState) => {
18
- const fetchState = reduceFetchState([
19
+ const fetchState = reduceFetchStateParallelTimeframes([
19
20
  profitAndLossClassesViewState.fetchState.month,
20
21
  profitAndLossClassesViewState.fetchState.quarter,
21
22
  profitAndLossClassesViewState.fetchState.year,
@@ -64,31 +65,3 @@ export const getProfitAndLossClassesView = (filter, accountState, classState, se
64
65
  idsInOrder: profitAndLossIdsInOrder,
65
66
  };
66
67
  };
67
- function reduceFetchState(fetchStateList) {
68
- const isAnyProgress = fetchStateList.reduce((prevValue, currentValue) => {
69
- return prevValue === true || currentValue.fetchState === 'In-Progress';
70
- }, false);
71
- const isAllCompleted = fetchStateList.reduce((prevValue, currentValue) => {
72
- return prevValue === true && currentValue.fetchState === 'Completed';
73
- }, true);
74
- const isAnyError = fetchStateList.reduce((prevValue, currentValue) => {
75
- return prevValue === true || currentValue.fetchState === 'Error';
76
- }, false);
77
- const error = fetchStateList.reduce((prevValue, currentValue) => {
78
- return prevValue ?? currentValue.error;
79
- }, undefined);
80
- let fetchState = 'Not-Started';
81
- if (isAnyProgress) {
82
- fetchState = 'In-Progress';
83
- }
84
- else if (isAllCompleted) {
85
- fetchState = 'Completed';
86
- }
87
- else if (isAnyError) {
88
- fetchState = 'Error';
89
- }
90
- return {
91
- fetchState,
92
- error,
93
- };
94
- }
@@ -24,11 +24,13 @@ export const fetchProfitAndLossForTimeframeClassesViewEpic = (actions$, _, zeniA
24
24
  .pipe(mergeMap((response) => {
25
25
  if (response.data != null && isSuccessResponse(response)) {
26
26
  const actions = [];
27
- actions.push(updateSectionClassesView(action.payload.timeframe, 'cogs', 'profit_and_loss_by_classes', response.data.report.cogs, response.data.report.classes, response.data.report.accounts, response.data.report.currency));
28
- actions.push(updateSectionClassesView(action.payload.timeframe, 'expenses', 'profit_and_loss_by_classes', response.data.report.expenses, response.data.report.classes, response.data.report.accounts, response.data.report.currency));
29
- actions.push(updateSectionClassesView(action.payload.timeframe, 'income', 'profit_and_loss_by_classes', response.data.report.income, response.data.report.classes, response.data.report.accounts, response.data.report.currency));
30
- actions.push(updateSectionClassesView(action.payload.timeframe, 'otherExpenses', 'profit_and_loss_by_classes', response.data.report.other_expenses, response.data.report.classes, response.data.report.accounts, response.data.report.currency));
31
- actions.push(updateSectionClassesView(action.payload.timeframe, 'otherIncome', 'profit_and_loss_by_classes', response.data.report.other_income, response.data.report.classes, response.data.report.accounts, response.data.report.currency));
27
+ const report = response.data.report;
28
+ const currency = report.currency;
29
+ actions.push(updateSectionClassesView(action.payload.timeframe, 'cogs', 'profit_and_loss_by_classes', report.cogs, report.classes, report.accounts, currency));
30
+ actions.push(updateSectionClassesView(action.payload.timeframe, 'expenses', 'profit_and_loss_by_classes', report.expenses, report.classes, report.accounts, currency));
31
+ actions.push(updateSectionClassesView(action.payload.timeframe, 'income', 'profit_and_loss_by_classes', report.income, report.classes, report.accounts, currency));
32
+ actions.push(updateSectionClassesView(action.payload.timeframe, 'otherExpenses', 'profit_and_loss_by_classes', report.other_expenses, report.classes, report.accounts, currency));
33
+ actions.push(updateSectionClassesView(action.payload.timeframe, 'otherIncome', 'profit_and_loss_by_classes', report.other_income, report.classes, report.accounts, currency));
32
34
  actions.push(updateProfitAndLossForTimeframeClassesView({
33
35
  timeframe: action.payload.timeframe,
34
36
  report: response.data,
@@ -2,15 +2,19 @@ import { of } from 'rxjs';
2
2
  import { filter, switchMap } from 'rxjs/operators';
3
3
  import { updateCashInCashOutCOABalancesRange } from '../cashInCashOut/cashInCashOutReducer';
4
4
  import { updateCashPositionCOABalancesRange } from '../cashPosition/cashPositionReducer';
5
- import { initialFinanceStatementState, updateCOABalancesRange, } from '../financeStatement/financeStatementReducer';
5
+ import { updateCOABalancesRange } from '../financeStatement/financeStatementReducer';
6
6
  import { updateNetBurnOrIncomeCOABalancesRange } from '../netBurnOrIncome/netBurnOrIncomeReducer';
7
7
  import { updateOpExCOABalancesRange } from '../opEx/opExReducer';
8
8
  import { updateRevenueCOABalancesRange } from '../revenue/revenueReducer';
9
9
  import { updateReportUIOptionCOABalancesRange } from './reportUIOptionsReducer';
10
10
  export const updateReportUIOptionCOABalancesRangeEpic = (actions$, state$) => actions$.pipe(filter(updateReportUIOptionCOABalancesRange.match), switchMap((action) => {
11
- const thisPeriod = action.payload.selectedCOABalancesRange.thisPeriod;
12
- const orderBy = action.payload.selectedCOABalancesRange.orderBy;
13
- const highlightedNoOfPeriods = state$.value.reportUIOptionsState.isCompareModeOn === true ? 4 : 1;
11
+ const selectedRange = action.payload.selectedCOABalancesRange;
12
+ const thisPeriod = selectedRange.thisPeriod;
13
+ const orderBy = selectedRange.orderBy;
14
+ const numberOfPeriodsFromSelection = selectedRange.numberOfPeriods;
15
+ const highlightedNoOfPeriods = state$.value.reportUIOptionsState.isCompareModeOn === true
16
+ ? numberOfPeriodsFromSelection
17
+ : 1;
14
18
  const highlightedCOABalancesRange = action.payload.highlightedCOABalancesRange == null && thisPeriod != null
15
19
  ? {
16
20
  numberOfPeriods: highlightedNoOfPeriods,
@@ -62,9 +66,7 @@ export const updateReportUIOptionCOABalancesRangeEpic = (actions$, state$) => ac
62
66
  thisPeriod: action.payload.highlightedCOABalancesRange != null
63
67
  ? action.payload.highlightedCOABalancesRange.thisPeriod
64
68
  : thisPeriod,
65
- numberOfPeriods: state$.value.reportUIOptionsState.isCompareModeOn === true
66
- ? 4
67
- : initialFinanceStatementState.maxNumOfPeriodsToHighlight,
69
+ numberOfPeriods: numberOfPeriodsFromSelection,
68
70
  orderBy,
69
71
  }));
70
72
  }));
package/lib/index.d.ts CHANGED
@@ -24,7 +24,7 @@ import { getFYMonths } from './commonStateTypes/fiscalYearHelpers/getFYMonths';
24
24
  import { getFYQuarterAndYear, getLastMonthOfFYQuarter, getLastMonthOfFYYear } from './commonStateTypes/fiscalYearHelpers/getFYQuarterAndYear';
25
25
  import { getStartOfAndEndOfTimeframeForFY } from './commonStateTypes/fiscalYearHelpers/getStartOfAndEndOfTimeframeFY';
26
26
  import { COTLineItemTracking, COTRecommendationTracking, COTTransactionTracking, COTTransactionTrackingByTransactionId, Recommendation, RecommendationWithCOT, TransactionLineRecommendationFieldTypes } from './commonStateTypes/recommendationBase';
27
- import { isAllFetchCompleted, isAnyFetchInProgress, reduceAllFetchState, reduceAnyCompletedFetchState, reduceAnyFetchState, reduceFetchState } from './commonStateTypes/reduceFetchState';
27
+ import { isAllFetchCompleted, isAnyFetchInProgress, reduceAllFetchState, reduceAnyCompletedFetchState, reduceAnyFetchState, reduceFetchState, reduceFetchStateParallelTimeframes } from './commonStateTypes/reduceFetchState';
28
28
  import { toReimbursementTypeCode } from './commonStateTypes/reimbursementTypeCode';
29
29
  import { ClassesViewSelectorReportV2 } from './commonStateTypes/selectorTypes/classesViewSelectorReport';
30
30
  import { COABalancesSliceOrder, CalculatedSection, EntityOrder, Section, SectionWithAccountOnly, TransactionsOrder } from './commonStateTypes/selectorTypes/selectorTypes';
@@ -782,7 +782,7 @@ export { RemiDetailLocalData, AutoFields, OutofPocketReimbursementLineWithRecomm
782
782
  export { Merchant };
783
783
  export { fetchEditRemiDetailPage, fetchRecommendationsAndUpdateMerchantRecommendations, fetchRemiAndInitializeLocalStore, initializeRemiToLocalStore, saveRemiUpdatesToLocalStore, fetchCurrencyConversionValue, discardRemiUpdatesInLocalStore, removeFileFromRemiUpdatesInLocalStore, saveRemiDetail, saveRemiSuccessOrFailure, parseReceiptsToRemi, clearEditRemiViewDetail, updateAddRemiAutoFields, updateUploadFetchState, updateTentativeMerchantNames, clearTentativeMerchantName, updateReimbursementType, updateHomeCurrencyConversion, clearAddRemiAutoFields, };
784
784
  export { AnimationFileName };
785
- export { reduceFetchState, reduceAnyFetchState, reduceAllFetchState, reduceAnyCompletedFetchState, isAnyFetchInProgress, isAllFetchCompleted, SortOrder, ALL_FILE_TYPES, FileType, toFileTypeStrict, };
785
+ export { reduceFetchState, reduceFetchStateParallelTimeframes, reduceAnyFetchState, reduceAllFetchState, reduceAnyCompletedFetchState, isAnyFetchInProgress, isAllFetchCompleted, SortOrder, ALL_FILE_TYPES, FileType, toFileTypeStrict, };
786
786
  export { getNextNthWorkingDay, getPreviousNthWorkingDay, isHolidayToday, filterDays, getYearsList, isHoliday, holidaysFormatted, PAYMENT_BUSINESS_DAYS, };
787
787
  export { CustomerRatingCodeType, TeamRatingCodeType };
788
788
  export { CustomerSatisfactionInfo };