@zeniai/client-epic-state 4.20.3 → 4.20.5

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 (169) hide show
  1. package/lib/commonStateTypes/coaBalance/additionalBalances/getSummationBalance.d.ts +1 -1
  2. package/lib/commonStateTypes/coaBalance/coaBalanceType.d.ts +0 -2
  3. package/lib/commonStateTypes/coaBalance/coaBalanceType.js +0 -6
  4. package/lib/commonStateTypes/currencyHelper.d.ts +0 -3
  5. package/lib/commonStateTypes/currencyHelper.js +1 -6
  6. package/lib/commonStateTypes/reduceFetchState.d.ts +0 -9
  7. package/lib/commonStateTypes/reduceFetchState.js +0 -26
  8. package/lib/entity/account/accountPayload.d.ts +0 -2
  9. package/lib/entity/account/accountPayload.js +0 -2
  10. package/lib/entity/account/accountState.d.ts +0 -2
  11. package/lib/entity/customer/customerState.d.ts +2 -0
  12. package/lib/entity/customer/customerState.js +5 -0
  13. package/lib/entity/depositAccount/depositAccount.d.ts +1 -1
  14. package/lib/entity/depositAccount/depositAccount.js +1 -8
  15. package/lib/entity/reimbursement/reimbursementPayload.d.ts +0 -2
  16. package/lib/entity/reimbursement/reimbursementPayload.js +0 -7
  17. package/lib/entity/reimbursement/reimbursementState.d.ts +0 -2
  18. package/lib/entity/snackbar/snackbarTypes.d.ts +1 -1
  19. package/lib/entity/snackbar/snackbarTypes.js +0 -1
  20. package/lib/entity/task/taskPayload.d.ts +0 -1
  21. package/lib/entity/task/taskPayload.js +0 -1
  22. package/lib/entity/task/taskState.d.ts +0 -1
  23. package/lib/epic.d.ts +1 -12
  24. package/lib/epic.js +1 -12
  25. package/lib/esm/commonStateTypes/coaBalance/coaBalanceType.js +0 -5
  26. package/lib/esm/commonStateTypes/currencyHelper.js +0 -5
  27. package/lib/esm/commonStateTypes/reduceFetchState.js +0 -25
  28. package/lib/esm/entity/account/accountPayload.js +0 -2
  29. package/lib/esm/entity/customer/customerState.js +4 -1
  30. package/lib/esm/entity/depositAccount/depositAccount.js +1 -8
  31. package/lib/esm/entity/reimbursement/reimbursementPayload.js +0 -7
  32. package/lib/esm/entity/snackbar/snackbarTypes.js +0 -1
  33. package/lib/esm/entity/task/taskPayload.js +0 -1
  34. package/lib/esm/epic.js +1 -12
  35. package/lib/esm/index.js +10 -19
  36. package/lib/esm/reducer.js +0 -6
  37. package/lib/esm/view/arAgingView/arAgingReport/arAgingReducer.js +1 -1
  38. package/lib/esm/view/arAgingView/arAgingReport/arAgingSelector.js +1 -1
  39. package/lib/esm/view/expenseAutomationView/epics/missingReceipts/uploadMissingReceiptSuccessEpic.js +5 -7
  40. package/lib/esm/view/expenseAutomationView/payload/missingReceiptsPayload.js +1 -189
  41. package/lib/esm/view/expenseAutomationView/reducers/missingReceiptsViewReducer.js +2 -433
  42. package/lib/esm/view/expenseAutomationView/reducers/transactionsViewReducer.js +1 -23
  43. package/lib/esm/view/expenseAutomationView/selectors/missingReceiptsSelector.js +1 -244
  44. package/lib/esm/view/expenseAutomationView/selectors/transactionCategorizationSelector.js +1 -2
  45. package/lib/esm/view/expenseAutomationView/types/missingReceiptsViewState.js +0 -44
  46. package/lib/esm/view/expenseAutomationView/types/transactionsViewState.js +1 -2
  47. package/lib/esm/view/financeStatement/financeStatementReducer.js +8 -11
  48. package/lib/esm/view/financeStatement/financeStatementSelector.js +9 -24
  49. package/lib/esm/view/profitAndLossClassesView/profitAndLossClassesViewReducer.js +1 -6
  50. package/lib/esm/view/profitAndLossClassesView/profitAndLossClassesViewSelector.js +29 -2
  51. package/lib/esm/view/profitAndLossClassesView/profitAndLossForTimeframeClassesViewEpic.js +5 -7
  52. package/lib/esm/view/reportUIOptions/updateReportUIOptionCOABalancesRangeEpic.js +7 -9
  53. package/lib/esm/view/taskManager/taskDetailView/taskDetailSelector.js +1 -1
  54. package/lib/esm/view/taskManager/taskListView/epics/bulkUpdateTaskListEpic.js +0 -1
  55. package/lib/esm/view/taskManager/taskListView/taskListSelector.js +1 -3
  56. package/lib/esm/view/twoFactorAuthentication/resendOtpEpic.js +16 -2
  57. package/lib/esm/view/userListView/fetchUserListByTypeEpic.js +0 -11
  58. package/lib/esm/zeniAPI.js +2 -0
  59. package/lib/index.d.ts +12 -20
  60. package/lib/index.js +34 -94
  61. package/lib/reducer.d.ts +0 -6
  62. package/lib/reducer.js +0 -6
  63. package/lib/view/arAgingView/arAgingReport/arAgingReducer.js +2 -2
  64. package/lib/view/arAgingView/arAgingReport/arAgingSelector.js +5 -5
  65. package/lib/view/expenseAutomationView/epics/missingReceipts/uploadMissingReceiptSuccessEpic.js +5 -7
  66. package/lib/view/expenseAutomationView/helpers/transactionCategorizationLocalDataHelper.d.ts +1 -1
  67. package/lib/view/expenseAutomationView/payload/missingReceiptsPayload.d.ts +0 -118
  68. package/lib/view/expenseAutomationView/payload/missingReceiptsPayload.js +0 -201
  69. package/lib/view/expenseAutomationView/reducers/missingReceiptsViewReducer.d.ts +3 -76
  70. package/lib/view/expenseAutomationView/reducers/missingReceiptsViewReducer.js +2 -434
  71. package/lib/view/expenseAutomationView/reducers/transactionsViewReducer.d.ts +3 -11
  72. package/lib/view/expenseAutomationView/reducers/transactionsViewReducer.js +2 -24
  73. package/lib/view/expenseAutomationView/selectorTypes/missingReceiptsSelectorTypes.d.ts +1 -69
  74. package/lib/view/expenseAutomationView/selectorTypes/transactionsViewSelectorTypes.d.ts +0 -1
  75. package/lib/view/expenseAutomationView/selectors/missingReceiptsSelector.d.ts +1 -1
  76. package/lib/view/expenseAutomationView/selectors/missingReceiptsSelector.js +0 -243
  77. package/lib/view/expenseAutomationView/selectors/transactionCategorizationSelector.js +1 -2
  78. package/lib/view/expenseAutomationView/types/missingReceiptsViewState.d.ts +0 -142
  79. package/lib/view/expenseAutomationView/types/missingReceiptsViewState.js +1 -54
  80. package/lib/view/expenseAutomationView/types/transactionsViewState.d.ts +1 -2
  81. package/lib/view/expenseAutomationView/types/transactionsViewState.js +1 -2
  82. package/lib/view/financeStatement/financeStatementReducer.js +8 -11
  83. package/lib/view/financeStatement/financeStatementSelector.d.ts +0 -2
  84. package/lib/view/financeStatement/financeStatementSelector.js +9 -24
  85. package/lib/view/profitAndLossClassesView/profitAndLossClassesViewReducer.d.ts +2 -4
  86. package/lib/view/profitAndLossClassesView/profitAndLossClassesViewReducer.js +2 -7
  87. package/lib/view/profitAndLossClassesView/profitAndLossClassesViewSelector.js +29 -2
  88. package/lib/view/profitAndLossClassesView/profitAndLossClassesViewState.d.ts +0 -4
  89. package/lib/view/profitAndLossClassesView/profitAndLossForTimeframeClassesViewEpic.js +5 -7
  90. package/lib/view/reportUIOptions/updateReportUIOptionCOABalancesRangeEpic.js +6 -8
  91. package/lib/view/taskManager/taskDetailView/taskDetailSelector.js +1 -1
  92. package/lib/view/taskManager/taskListView/epics/bulkUpdateTaskListEpic.d.ts +0 -1
  93. package/lib/view/taskManager/taskListView/epics/bulkUpdateTaskListEpic.js +0 -1
  94. package/lib/view/taskManager/taskListView/taskListSelector.d.ts +0 -2
  95. package/lib/view/taskManager/taskListView/taskListSelector.js +1 -3
  96. package/lib/view/twoFactorAuthentication/resendOtpEpic.d.ts +1 -1
  97. package/lib/view/twoFactorAuthentication/resendOtpEpic.js +15 -1
  98. package/lib/view/userListView/fetchUserListByTypeEpic.d.ts +1 -3
  99. package/lib/view/userListView/fetchUserListByTypeEpic.js +0 -11
  100. package/lib/view/userListView/userListViewPayload.d.ts +0 -2
  101. package/lib/zeniAPI.js +2 -0
  102. package/package.json +3 -3
  103. package/lib/entity/userGroups/userGroupsPayload.d.ts +0 -7
  104. package/lib/entity/userGroups/userGroupsPayload.js +0 -10
  105. package/lib/entity/userGroups/userGroupsReducer.d.ts +0 -8
  106. package/lib/entity/userGroups/userGroupsReducer.js +0 -29
  107. package/lib/entity/userGroups/userGroupsSelector.d.ts +0 -4
  108. package/lib/entity/userGroups/userGroupsSelector.js +0 -12
  109. package/lib/entity/userGroups/userGroupsState.d.ts +0 -9
  110. package/lib/entity/userGroups/userGroupsState.js +0 -2
  111. package/lib/esm/entity/userGroups/userGroupsPayload.js +0 -7
  112. package/lib/esm/entity/userGroups/userGroupsReducer.js +0 -25
  113. package/lib/esm/entity/userGroups/userGroupsSelector.js +0 -8
  114. package/lib/esm/entity/userGroups/userGroupsState.js +0 -1
  115. package/lib/esm/view/expenseAutomationView/bulkUploadTiming.js +0 -10
  116. package/lib/esm/view/expenseAutomationView/epics/missingReceipts/bulkUploadReceiptsEpic.js +0 -29
  117. package/lib/esm/view/expenseAutomationView/epics/missingReceipts/confirmBulkUploadMatchEpic.js +0 -49
  118. package/lib/esm/view/expenseAutomationView/epics/missingReceipts/fetchBulkUploadBatchDetailsEpic.js +0 -40
  119. package/lib/esm/view/expenseAutomationView/epics/missingReceipts/fetchBulkUploadBatchesEpic.js +0 -47
  120. package/lib/esm/view/expenseAutomationView/epics/missingReceipts/fetchCompletedTransactionsEpic.js +0 -61
  121. package/lib/esm/view/expenseAutomationView/epics/missingReceipts/fetchMoreBatchDetailsEpic.js +0 -33
  122. package/lib/esm/view/expenseAutomationView/epics/missingReceipts/fetchMultipleBatchDetailsEpic.js +0 -56
  123. package/lib/esm/view/expenseAutomationView/epics/missingReceipts/searchTransactionsForManualMatchEpic.js +0 -77
  124. package/lib/esm/view/expenseAutomationView/epics/missingReceipts/watchBulkUploadBatchStatusEpic.js +0 -79
  125. package/lib/esm/view/expenseAutomationView/epics/transactionCategorization/uploadTransactionReceiptSuccessEpic.js +0 -24
  126. package/lib/esm/view/profitAndLossClassesView/horizontalSectionEmptyBalancesSlice.js +0 -17
  127. package/lib/esm/view/profitAndLossClassesView/profitAndLossClassesByClassHorizontalSelector.js +0 -331
  128. package/lib/esm/view/profitAndLossClassesView/profitAndLossClassesByClassHorizontalSelectorTypes.js +0 -9
  129. package/lib/esm/view/profitAndLossClassesView/profitAndLossHorizontalEnrichment.js +0 -262
  130. package/lib/esm/view/userGroupListView/userGroupListViewReducer.js +0 -65
  131. package/lib/esm/view/userGroupListView/userGroupListViewSelector.js +0 -16
  132. package/lib/esm/view/userGroupListView/userGroupListViewState.js +0 -1
  133. package/lib/tsconfig.typecheck.tsbuildinfo +0 -1
  134. package/lib/view/expenseAutomationView/bulkUploadTiming.d.ts +0 -10
  135. package/lib/view/expenseAutomationView/bulkUploadTiming.js +0 -13
  136. package/lib/view/expenseAutomationView/epics/missingReceipts/bulkUploadReceiptsEpic.d.ts +0 -18
  137. package/lib/view/expenseAutomationView/epics/missingReceipts/bulkUploadReceiptsEpic.js +0 -33
  138. package/lib/view/expenseAutomationView/epics/missingReceipts/confirmBulkUploadMatchEpic.d.ts +0 -7
  139. package/lib/view/expenseAutomationView/epics/missingReceipts/confirmBulkUploadMatchEpic.js +0 -53
  140. package/lib/view/expenseAutomationView/epics/missingReceipts/fetchBulkUploadBatchDetailsEpic.d.ts +0 -8
  141. package/lib/view/expenseAutomationView/epics/missingReceipts/fetchBulkUploadBatchDetailsEpic.js +0 -44
  142. package/lib/view/expenseAutomationView/epics/missingReceipts/fetchBulkUploadBatchesEpic.d.ts +0 -7
  143. package/lib/view/expenseAutomationView/epics/missingReceipts/fetchBulkUploadBatchesEpic.js +0 -51
  144. package/lib/view/expenseAutomationView/epics/missingReceipts/fetchCompletedTransactionsEpic.d.ts +0 -8
  145. package/lib/view/expenseAutomationView/epics/missingReceipts/fetchCompletedTransactionsEpic.js +0 -65
  146. package/lib/view/expenseAutomationView/epics/missingReceipts/fetchMoreBatchDetailsEpic.d.ts +0 -8
  147. package/lib/view/expenseAutomationView/epics/missingReceipts/fetchMoreBatchDetailsEpic.js +0 -37
  148. package/lib/view/expenseAutomationView/epics/missingReceipts/fetchMultipleBatchDetailsEpic.d.ts +0 -9
  149. package/lib/view/expenseAutomationView/epics/missingReceipts/fetchMultipleBatchDetailsEpic.js +0 -61
  150. package/lib/view/expenseAutomationView/epics/missingReceipts/searchTransactionsForManualMatchEpic.d.ts +0 -14
  151. package/lib/view/expenseAutomationView/epics/missingReceipts/searchTransactionsForManualMatchEpic.js +0 -81
  152. package/lib/view/expenseAutomationView/epics/missingReceipts/watchBulkUploadBatchStatusEpic.d.ts +0 -25
  153. package/lib/view/expenseAutomationView/epics/missingReceipts/watchBulkUploadBatchStatusEpic.js +0 -85
  154. package/lib/view/expenseAutomationView/epics/transactionCategorization/uploadTransactionReceiptSuccessEpic.d.ts +0 -7
  155. package/lib/view/expenseAutomationView/epics/transactionCategorization/uploadTransactionReceiptSuccessEpic.js +0 -28
  156. package/lib/view/profitAndLossClassesView/horizontalSectionEmptyBalancesSlice.d.ts +0 -9
  157. package/lib/view/profitAndLossClassesView/horizontalSectionEmptyBalancesSlice.js +0 -20
  158. package/lib/view/profitAndLossClassesView/profitAndLossClassesByClassHorizontalSelector.d.ts +0 -15
  159. package/lib/view/profitAndLossClassesView/profitAndLossClassesByClassHorizontalSelector.js +0 -335
  160. package/lib/view/profitAndLossClassesView/profitAndLossClassesByClassHorizontalSelectorTypes.d.ts +0 -111
  161. package/lib/view/profitAndLossClassesView/profitAndLossClassesByClassHorizontalSelectorTypes.js +0 -13
  162. package/lib/view/profitAndLossClassesView/profitAndLossHorizontalEnrichment.d.ts +0 -6
  163. package/lib/view/profitAndLossClassesView/profitAndLossHorizontalEnrichment.js +0 -265
  164. package/lib/view/userGroupListView/userGroupListViewReducer.d.ts +0 -23
  165. package/lib/view/userGroupListView/userGroupListViewReducer.js +0 -69
  166. package/lib/view/userGroupListView/userGroupListViewSelector.d.ts +0 -7
  167. package/lib/view/userGroupListView/userGroupListViewSelector.js +0 -20
  168. package/lib/view/userGroupListView/userGroupListViewState.d.ts +0 -8
  169. package/lib/view/userGroupListView/userGroupListViewState.js +0 -2
@@ -1,331 +0,0 @@
1
- import { toAmountWC } from '../../commonStateTypes/amount';
2
- import { getSummationFreeRangeTotalBalance } from '../../commonStateTypes/coaBalance/additionalBalances/getSummationBalance';
3
- import { defaultCustomerCurrency } from '../../commonStateTypes/currencyHelper';
4
- import { reduceFetchStateParallelTimeframes } from '../../commonStateTypes/reduceFetchState';
5
- import { getSectionClassesViewReport } from '../../entity/sectionClassesViewV2/sectionClassesViewSelector';
6
- import { enrichProfitAndLossByClassHorizontalReport } from './profitAndLossHorizontalEnrichment';
7
- /** Standard class ID returned by the accounting platform for transactions without an assigned class. */
8
- export const NOT_SPECIFIED_CLASS_ID = 'not_specified';
9
- export const profitAndLossHorizontalIdsInOrder = [
10
- 'income',
11
- 'cogs',
12
- 'grossProfit',
13
- 'expenses',
14
- 'earningsBeforeInterestAndTax',
15
- 'otherIncome',
16
- 'otherExpenses',
17
- 'netOtherIncome',
18
- 'netIncome',
19
- ];
20
- const SECTION_TITLES = {
21
- income: 'Income',
22
- cogs: 'Cost of Goods Sold',
23
- grossProfit: 'Gross Profit',
24
- expenses: 'Expenses',
25
- earningsBeforeInterestAndTax: 'Net Operating Income',
26
- otherIncome: 'Other Income',
27
- otherExpenses: 'Other Expenses',
28
- netOtherIncome: 'Net Other Income',
29
- netIncome: 'Net Income',
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
- ];
39
- export const getProfitAndLossClassesHorizontalView = (filter, accountState, classState, sectionsState, profitAndLossClassesViewState, selectedPeriod) => {
40
- const fetchState = reduceFetchStateParallelTimeframes([
41
- profitAndLossClassesViewState.fetchState.month,
42
- profitAndLossClassesViewState.fetchState.quarter,
43
- profitAndLossClassesViewState.fetchState.year,
44
- ]);
45
- const reportId = 'profit_and_loss_by_classes';
46
- // Build class columns from the class hierarchy (top-level only).
47
- // Use `filter.classesToFilterOut` so PDF/Excel downloads (fresh store, empty UI state)
48
- // respect `classes_to_filter_out` from the request; in-app `classFilter` mirrors uiState.
49
- const classColumns = buildClassColumns(profitAndLossClassesViewState.classHierarchy, filter.classesToFilterOut);
50
- const sections = {};
51
- const calculatedSections = {};
52
- let reportCurrency = defaultCustomerCurrency;
53
- if (fetchState.fetchState === 'Completed' && selectedPeriod != null) {
54
- reportCurrency =
55
- profitAndLossClassesViewState.currency ?? defaultCustomerCurrency;
56
- // Fetch each section and pivot class→account to account→class in one pass
57
- HORIZONTAL_PANDL_SECTION_IDS.forEach((sectionId) => {
58
- const sectionReport = getSectionClassesViewReport(accountState, classState, sectionsState, { reportId, sectionId }, filter);
59
- if (sectionReport != null) {
60
- sections[sectionId] = pivotSectionToHorizontal(sectionReport, classColumns, SECTION_TITLES[sectionId] ?? sectionId, reportCurrency);
61
- }
62
- });
63
- // Calculate derived sections
64
- const incomeSection = sections['income'];
65
- const cogsSection = sections['cogs'];
66
- const expensesSection = sections['expenses'];
67
- const otherIncomeSection = sections['otherIncome'];
68
- const otherExpensesSection = sections['otherExpenses'];
69
- // Gross Profit = Income - COGS
70
- if (incomeSection != null && cogsSection != null) {
71
- calculatedSections['grossProfit'] = computeCalculatedSection(incomeSection.classTotals, cogsSection.classTotals, incomeSection.sectionTotal, cogsSection.sectionTotal, 'subtract', SECTION_TITLES['grossProfit']);
72
- }
73
- // EBIT = Gross Profit - Expenses
74
- const grossProfit = calculatedSections['grossProfit'];
75
- if (grossProfit != null && expensesSection != null) {
76
- calculatedSections['earningsBeforeInterestAndTax'] =
77
- computeCalculatedSection(grossProfit.classTotals, expensesSection.classTotals, grossProfit.sectionTotal, expensesSection.sectionTotal, 'subtract', SECTION_TITLES['earningsBeforeInterestAndTax']);
78
- }
79
- // Net Other Income = Other Income - Other Expenses
80
- if (otherIncomeSection != null && otherExpensesSection != null) {
81
- calculatedSections['netOtherIncome'] = computeCalculatedSection(otherIncomeSection.classTotals, otherExpensesSection.classTotals, otherIncomeSection.sectionTotal, otherExpensesSection.sectionTotal, 'subtract', SECTION_TITLES['netOtherIncome']);
82
- }
83
- // Net Income = EBIT + Net Other Income
84
- const ebit = calculatedSections['earningsBeforeInterestAndTax'];
85
- const netOtherIncome = calculatedSections['netOtherIncome'];
86
- if (ebit != null && netOtherIncome != null) {
87
- calculatedSections['netIncome'] = computeCalculatedSection(ebit.classTotals, netOtherIncome.classTotals, ebit.sectionTotal, netOtherIncome.sectionTotal, 'add', SECTION_TITLES['netIncome']);
88
- }
89
- }
90
- // Build synthetic header COABalances for rendering class names as column headers
91
- const headerBalances = buildHeaderBalances(classColumns, filter.timeframe, selectedPeriod, reportCurrency);
92
- const report = {
93
- reportId,
94
- reportTitle: 'Profit and Loss by Class',
95
- firstMonthOfFY: profitAndLossClassesViewState.firstMonthOfFY,
96
- fetchState: fetchState.fetchState,
97
- bookCloseDate: profitAndLossClassesViewState.bookCloseDate,
98
- dataAvailable: profitAndLossClassesViewState.dataAvailable,
99
- selectedPeriod,
100
- classColumns,
101
- sections,
102
- calculatedSections,
103
- idsInOrder: profitAndLossHorizontalIdsInOrder,
104
- uiState: profitAndLossClassesViewState.uiState,
105
- headerBalances,
106
- };
107
- enrichProfitAndLossByClassHorizontalReport(report);
108
- return report;
109
- };
110
- /**
111
- * Build column definitions from class hierarchy (top-level classes only).
112
- */
113
- function buildClassColumns(classHierarchy, classesToFilterOut) {
114
- const filteredIds = new Set(classesToFilterOut.map((id) => id[0]));
115
- return classHierarchy
116
- .filter((c) => !filteredIds.has(c.classId[0]))
117
- .map((c) => ({
118
- classId: c.classId[0],
119
- className: c.className,
120
- nestedClassId: c.classId,
121
- qboId: c.qboId,
122
- }))
123
- .sort((a, b) => {
124
- const aIsNotSpecified = a.classId === NOT_SPECIFIED_CLASS_ID;
125
- const bIsNotSpecified = b.classId === NOT_SPECIFIED_CLASS_ID;
126
- if (aIsNotSpecified && !bIsNotSpecified) {
127
- return 1;
128
- }
129
- if (!aIsNotSpecified && bIsNotSpecified) {
130
- return -1;
131
- }
132
- return a.className.localeCompare(b.className, [], {
133
- sensitivity: 'base',
134
- });
135
- });
136
- }
137
- /**
138
- * Pivot a section from class→account→balances to account→class→balance (single period).
139
- */
140
- function pivotSectionToHorizontal(section, classColumns, title, currency) {
141
- // Map: accountId -> { accountName, classId -> balance }
142
- const accountMap = new Map();
143
- // Also track class totals
144
- const classTotalMap = new Map();
145
- classColumns.forEach((col) => classTotalMap.set(col.classId, 0));
146
- // Iterate over each class in the section
147
- section.classes.forEach((classReport) => {
148
- const classId = classReport.id[0];
149
- // Only process classes that are in our columns
150
- if (!classTotalMap.has(classId)) {
151
- return;
152
- }
153
- // Sum all sliced period balances (same slice as vertical columns; multi-period = range total)
154
- const classBalance = sumBalancesInSlice(classReport.childrenBalancesTotal.balances);
155
- classTotalMap.set(classId, classBalance);
156
- // Iterate through accounts in this class
157
- collectAccountsFromClass(classReport, classId, accountMap);
158
- });
159
- // Build account rows in COA tree order (same parent/child ordering as vertical P&L by class).
160
- // Sorting by depth + name flattened the hierarchy and broke sub-account grouping.
161
- const orderedIds = collectAccountIdsInTreeOrder(section, classColumns);
162
- const usedIds = new Set();
163
- const accounts = [];
164
- const pushAccountRow = (accountId) => {
165
- const accountData = accountMap.get(accountId);
166
- if (accountData == null) {
167
- return;
168
- }
169
- usedIds.add(accountId);
170
- const classBalances = classColumns.map((col) => ({
171
- classId: col.classId,
172
- balance: toAmountWC(accountData.classBalances.get(col.classId) ?? 0, currency),
173
- }));
174
- const rowTotal = classBalances.reduce((sum, cb) => sum + cb.balance.amount, 0);
175
- accounts.push({
176
- accountId,
177
- accountName: accountData.accountName,
178
- accountType: accountData.accountType,
179
- depth: accountData.depth,
180
- classBalances,
181
- rowTotal: toAmountWC(rowTotal, currency),
182
- });
183
- };
184
- for (const id of orderedIds) {
185
- pushAccountRow(id);
186
- }
187
- for (const accountId of accountMap.keys()) {
188
- if (!usedIds.has(accountId)) {
189
- pushAccountRow(accountId);
190
- }
191
- }
192
- // Build class totals
193
- const classTotals = classColumns.map((col) => ({
194
- classId: col.classId,
195
- balance: toAmountWC(classTotalMap.get(col.classId) ?? 0, currency),
196
- }));
197
- const sectionTotal = classTotals.reduce((sum, ct) => sum + ct.balance.amount, 0);
198
- return {
199
- title,
200
- accounts,
201
- classTotals,
202
- sectionTotal: toAmountWC(sectionTotal, currency),
203
- };
204
- }
205
- /**
206
- * DFS account ids in the same order as vertical P&L by class, using the first class
207
- * column that appears in this section as the canonical tree (structure is identical per class).
208
- */
209
- function collectAccountIdsInTreeOrder(section, classColumns) {
210
- const ids = [];
211
- const visitNested = (nested) => {
212
- ids.push(nested.account.accountId);
213
- nested.children.forEach(visitNested);
214
- };
215
- for (const col of classColumns) {
216
- const classReport = section.classes.find((c) => c.id[0] === col.classId);
217
- if (classReport == null) {
218
- continue;
219
- }
220
- classReport.class.accounts.forEach(visitNested);
221
- return ids;
222
- }
223
- return ids;
224
- }
225
- /**
226
- * Recursively collect account balances from a NestedClassReportV2 into accountMap.
227
- */
228
- function collectAccountsFromClass(classReport, classId, accountMap) {
229
- // Process accounts directly on this class (depth 2 matches vertical `AccountsSection` first level)
230
- classReport.class.accounts.forEach((nestedAccount) => {
231
- collectAccountsFromNestedAccount(nestedAccount, classId, accountMap, 2);
232
- });
233
- }
234
- /**
235
- * Recursively collect accounts from NestedAccountReport (handles sub-accounts).
236
- */
237
- function collectAccountsFromNestedAccount(nestedAccount, classId, accountMap, depth) {
238
- const accountId = nestedAccount.account.accountId;
239
- const accountName = nestedAccount.account.accountName;
240
- const balance = sumBalancesInSlice(nestedAccount.account.balancesInTimeframe.balances);
241
- if (!accountMap.has(accountId)) {
242
- accountMap.set(accountId, {
243
- accountName,
244
- accountType: nestedAccount.account.accountType,
245
- depth,
246
- classBalances: new Map(),
247
- });
248
- }
249
- const accountData = accountMap.get(accountId);
250
- if (accountData == null) {
251
- return;
252
- }
253
- accountData.depth = Math.max(accountData.depth, depth);
254
- if (accountData.accountType == null &&
255
- nestedAccount.account.accountType != null) {
256
- accountData.accountType = nestedAccount.account.accountType;
257
- }
258
- const existing = accountData.classBalances.get(classId) ?? 0;
259
- accountData.classBalances.set(classId, existing + balance);
260
- // Process sub-accounts
261
- nestedAccount.children.forEach((child) => {
262
- collectAccountsFromNestedAccount(child, classId, accountMap, depth + 1);
263
- });
264
- }
265
- /**
266
- * Total `balance.amount` for the current COA slice (same aggregation as
267
- * `getSummationFreeRangeTotalBalance`, used for class totals and account rows).
268
- */
269
- function sumBalancesInSlice(balances) {
270
- if (balances.length === 0) {
271
- return 0;
272
- }
273
- const summed = getSummationFreeRangeTotalBalance(balances);
274
- return summed?.balance.amount ?? 0;
275
- }
276
- /**
277
- * Compute a calculated section by adding or subtracting two sets of class totals.
278
- */
279
- function computeCalculatedSection(aTotals, bTotals, aTotal, bTotal, operation, title) {
280
- const currency = {
281
- currencyCode: aTotal.currencyCode,
282
- currencySymbol: aTotal.currencySymbol,
283
- };
284
- const bTotalsMap = new Map(bTotals.map((b) => [b.classId, b.balance.amount]));
285
- const classTotals = aTotals.map((a) => {
286
- const bAmount = bTotalsMap.get(a.classId) ?? 0;
287
- const resultAmount = operation === 'add'
288
- ? a.balance.amount + bAmount
289
- : a.balance.amount - bAmount;
290
- return {
291
- classId: a.classId,
292
- balance: toAmountWC(resultAmount, currency),
293
- };
294
- });
295
- const sectionTotalAmount = operation === 'add'
296
- ? aTotal.amount + bTotal.amount
297
- : aTotal.amount - bTotal.amount;
298
- return {
299
- title,
300
- classTotals,
301
- sectionTotal: toAmountWC(sectionTotalAmount, currency),
302
- };
303
- }
304
- /**
305
- * Build synthetic COABalance array for header rendering.
306
- * One entry per class column + one for "Total".
307
- */
308
- function buildHeaderBalances(classColumns, timeframe, selectedPeriod, currency) {
309
- if (selectedPeriod == null) {
310
- return [];
311
- }
312
- const { endDate, startDate } = selectedPeriod;
313
- const balances = classColumns.map((col) => ({
314
- balance: toAmountWC(0, currency),
315
- balanceTimeFrame: timeframe,
316
- endDate,
317
- startDate,
318
- type: 'default',
319
- label: col.className,
320
- }));
321
- // Add a "Total" column
322
- balances.push({
323
- balance: toAmountWC(0, currency),
324
- balanceTimeFrame: timeframe,
325
- endDate,
326
- startDate,
327
- type: 'default',
328
- label: 'Total',
329
- });
330
- return balances;
331
- }
@@ -1,9 +0,0 @@
1
- /** Synthetic COABalance `id` for the horizontal class report Total column (transaction routing). */
2
- export const HORIZONTAL_CLASS_TOTAL_BALANCE_ID = '__horizontal_total__';
3
- export function isHorizontalAccountReportData(data) {
4
- return (typeof data === 'object' &&
5
- data !== null &&
6
- 'depth' in data &&
7
- typeof data.depth === 'number' &&
8
- !('filter' in data));
9
- }
@@ -1,262 +0,0 @@
1
- import { defaultCustomerCurrency } from '../../commonStateTypes/currencyHelper';
2
- import { getEmptyHorizontalSectionBalancesSlice } from './horizontalSectionEmptyBalancesSlice';
3
- import { HORIZONTAL_CLASS_TOTAL_BALANCE_ID, } from './profitAndLossClassesByClassHorizontalSelectorTypes';
4
- /**
5
- * Populates `sectionHeaderBalancesSlice`, and per-account `accountReportData`, so the
6
- * web layer only maps selector output to tree nodes.
7
- */
8
- export function enrichProfitAndLossByClassHorizontalReport(report) {
9
- if (report.fetchState !== 'Completed' || report.selectedPeriod == null) {
10
- return;
11
- }
12
- for (const section of Object.values(report.sections)) {
13
- if (section != null) {
14
- enrichHorizontalSection(report, section);
15
- }
16
- }
17
- for (const calc of Object.values(report.calculatedSections)) {
18
- if (calc != null) {
19
- enrichHorizontalCalculatedSection(report, calc);
20
- }
21
- }
22
- }
23
- function enrichHorizontalSection(report, section) {
24
- const classAmounts = report.classColumns.map((col) => {
25
- const total = section.classTotals.find((ct) => ct.classId === col.classId);
26
- return total?.balance.amount ?? 0;
27
- });
28
- section.sectionHeaderBalancesSlice = buildHorizontalClassColumnBalancesSlice(report, classAmounts, section.sectionTotal.amount);
29
- const parentIndices = computeHorizontalAccountParentIndices(section.accounts);
30
- const childrenRowIndicesByParent = buildHorizontalAccountChildIndicesByParent(parentIndices);
31
- const childrenRecord = {};
32
- childrenRowIndicesByParent.forEach((childIndices, parentIndex) => {
33
- childrenRecord[parentIndex] = childIndices;
34
- });
35
- section.treeLayout = {
36
- rootAccountRowIndices: section.accounts
37
- .map((_, i) => (parentIndices[i] === -1 ? i : -1))
38
- .filter((i) => i >= 0),
39
- childrenRowIndicesByParent: childrenRecord,
40
- };
41
- const siblingsPerRow = buildSiblingIndicesPerRow(section.accounts, parentIndices);
42
- const hasChildFlags = computeHorizontalHasChildFlags(parentIndices);
43
- for (let accountIndex = 0; accountIndex < section.accounts.length; accountIndex++) {
44
- const row = section.accounts[accountIndex];
45
- // Same column order as `report.classColumns` (see `pivotSectionToHorizontal`).
46
- const classRowAmounts = row.classBalances.map((classBalance) => classBalance.balance.amount);
47
- row.accountReportData = {
48
- accountId: row.accountId,
49
- accountName: row.accountName,
50
- accountType: row.accountType,
51
- depth: row.depth,
52
- balancesInTimeframe: buildHorizontalClassColumnBalancesSlice(report, classRowAmounts, row.rowTotal.amount),
53
- horizontalTreeView: buildHorizontalTreeViewFromPrecomputed(section.accounts, accountIndex, parentIndices, siblingsPerRow, hasChildFlags),
54
- };
55
- }
56
- }
57
- function enrichHorizontalCalculatedSection(report, calcSection) {
58
- const classAmounts = report.classColumns.map((col) => {
59
- const total = calcSection.classTotals.find((ct) => ct.classId === col.classId);
60
- return total?.balance.amount ?? 0;
61
- });
62
- calcSection.sectionHeaderBalancesSlice =
63
- buildHorizontalClassColumnBalancesSlice(report, classAmounts, calcSection.sectionTotal.amount);
64
- }
65
- function buildHorizontalClassColumnBalancesSlice(report, classAmounts, totalAmount) {
66
- const fallback = defaultCustomerCurrency;
67
- const firstHeader = report.headerBalances[0];
68
- const currencyCode = firstHeader?.balance.currencyCode ?? fallback.currencyCode;
69
- const currencySymbol = firstHeader?.balance.currencySymbol ?? fallback.currencySymbol;
70
- const timeframe = firstHeader?.balanceTimeFrame ?? 'month';
71
- if (report.selectedPeriod == null) {
72
- return getEmptyHorizontalSectionBalancesSlice(report.firstMonthOfFY, timeframe);
73
- }
74
- const { startDate, endDate } = report.selectedPeriod;
75
- const balances = report.classColumns.map((col, columnIndex) => {
76
- const amount = classAmounts[columnIndex] ?? 0;
77
- return {
78
- balance: {
79
- amount,
80
- currencyCode,
81
- currencySymbol,
82
- },
83
- balanceTimeFrame: timeframe,
84
- startDate,
85
- endDate,
86
- type: amount === 0 ? 'default_with_null_balance' : 'default',
87
- id: col.classId,
88
- label: col.className,
89
- };
90
- });
91
- balances.push({
92
- balance: {
93
- amount: totalAmount,
94
- currencyCode,
95
- currencySymbol,
96
- },
97
- balanceTimeFrame: timeframe,
98
- startDate,
99
- endDate,
100
- type: totalAmount === 0 ? 'default_with_null_balance' : 'default',
101
- id: HORIZONTAL_CLASS_TOTAL_BALANCE_ID,
102
- label: 'Total',
103
- });
104
- return {
105
- balances,
106
- additionalBalances: [],
107
- filter: {
108
- firstMonthOfFY: report.firstMonthOfFY,
109
- timeframe,
110
- balancesRange: {
111
- numberOfPeriods: balances.length,
112
- orderBy: 'ascending_date',
113
- },
114
- additionalBalances: [],
115
- },
116
- };
117
- }
118
- /**
119
- * Parent index per DFS row — O(n). Matches the scan in the former
120
- * `findHorizontalAccountParentIndex` (depth &lt;= 2 rows have no parent).
121
- */
122
- function computeHorizontalAccountParentIndices(rows) {
123
- const accountRowCount = rows.length;
124
- const parentIndex = new Array(accountRowCount).fill(-1);
125
- const stack = [];
126
- for (let rowIndex = 0; rowIndex < accountRowCount; rowIndex++) {
127
- const accountRow = rows[rowIndex];
128
- if (accountRow == null) {
129
- continue;
130
- }
131
- const depth = accountRow.depth;
132
- while (stack.length > 0) {
133
- const topRow = rows[stack[stack.length - 1]];
134
- if (topRow == null) {
135
- stack.pop();
136
- continue;
137
- }
138
- if (topRow.depth < depth) {
139
- break;
140
- }
141
- stack.pop();
142
- }
143
- if (depth > 2 && stack.length > 0) {
144
- const peek = stack[stack.length - 1];
145
- const parentRow = rows[peek];
146
- if (parentRow != null && parentRow.depth === depth - 1) {
147
- parentIndex[rowIndex] = peek;
148
- }
149
- }
150
- stack.push(rowIndex);
151
- }
152
- return parentIndex;
153
- }
154
- /** Sibling row indices (same parent + depth), in traversal order — O(n). */
155
- function buildSiblingIndicesPerRow(rows, parentIndices) {
156
- const accountRowCount = rows.length;
157
- const keyToIndices = new Map();
158
- for (let rowIndex = 0; rowIndex < accountRowCount; rowIndex++) {
159
- const parentRowIndex = parentIndices[rowIndex];
160
- const accountRow = rows[rowIndex];
161
- if (parentRowIndex == null || accountRow == null) {
162
- continue;
163
- }
164
- const key = `${parentRowIndex}:${accountRow.depth}`;
165
- let siblingGroup = keyToIndices.get(key);
166
- if (siblingGroup == null) {
167
- siblingGroup = [];
168
- keyToIndices.set(key, siblingGroup);
169
- }
170
- siblingGroup.push(rowIndex);
171
- }
172
- const siblingsPerRow = new Array(accountRowCount);
173
- for (let rowIndex = 0; rowIndex < accountRowCount; rowIndex++) {
174
- const parentRowIndex = parentIndices[rowIndex];
175
- const accountRow = rows[rowIndex];
176
- if (parentRowIndex == null || accountRow == null) {
177
- siblingsPerRow[rowIndex] = [rowIndex];
178
- continue;
179
- }
180
- const key = `${parentRowIndex}:${accountRow.depth}`;
181
- const group = keyToIndices.get(key);
182
- siblingsPerRow[rowIndex] = group != null ? group : [rowIndex];
183
- }
184
- return siblingsPerRow;
185
- }
186
- function computeHorizontalHasChildFlags(parentIndices) {
187
- const rowCount = parentIndices.length;
188
- const flags = new Array(rowCount).fill(false);
189
- for (let rowIndex = 0; rowIndex < rowCount; rowIndex++) {
190
- const parentRowIndex = parentIndices[rowIndex];
191
- if (parentRowIndex != null &&
192
- parentRowIndex >= 0 &&
193
- parentRowIndex < rowCount) {
194
- flags[parentRowIndex] = true;
195
- }
196
- }
197
- return flags;
198
- }
199
- function buildHorizontalSubAccountAncestorMetadataFromPrecomputed(dfsOrderedAccountRows, currentRowIndex, parentIndices, siblingsPerRow, hasChildFlags) {
200
- const chain = [];
201
- let walkRowIndex = currentRowIndex;
202
- while (walkRowIndex >= 0) {
203
- chain.unshift(walkRowIndex);
204
- const parentRowIndex = parentIndices[walkRowIndex];
205
- if (parentRowIndex == null) {
206
- break;
207
- }
208
- walkRowIndex = parentRowIndex;
209
- }
210
- return chain.map((rowIndex) => {
211
- const accountRow = dfsOrderedAccountRows[rowIndex];
212
- const siblingIndices = siblingsPerRow[rowIndex];
213
- const hasChild = hasChildFlags[rowIndex];
214
- if (accountRow == null || siblingIndices == null) {
215
- return {
216
- depth: 0,
217
- hasSibling: false,
218
- hasChildren: hasChild === true,
219
- isLastNode: true,
220
- name: '',
221
- };
222
- }
223
- return {
224
- depth: accountRow.depth - 2,
225
- hasSibling: siblingIndices.length > 1,
226
- hasChildren: hasChild === true,
227
- isLastNode: rowIndex === siblingIndices[siblingIndices.length - 1],
228
- name: accountRow.accountName,
229
- };
230
- });
231
- }
232
- function buildHorizontalTreeViewFromPrecomputed(dfsOrderedAccountRows, currentRowIndex, parentIndices, siblingsPerRow, hasChildFlags) {
233
- const siblingIndices = siblingsPerRow[currentRowIndex];
234
- const accountRow = dfsOrderedAccountRows[currentRowIndex];
235
- const hasChildrenFlag = hasChildFlags[currentRowIndex];
236
- const treeDepth = accountRow != null ? Math.max(0, accountRow.depth - 2) : 0;
237
- const hasChildren = hasChildrenFlag === true;
238
- const siblings = siblingIndices ?? [currentRowIndex];
239
- return {
240
- subAccountAncestorMetadata: buildHorizontalSubAccountAncestorMetadataFromPrecomputed(dfsOrderedAccountRows, currentRowIndex, parentIndices, siblingsPerRow, hasChildFlags),
241
- treeDepth,
242
- hasChildren,
243
- hasSiblings: siblings.length > 1,
244
- isLeaf: !hasChildren,
245
- isLastNode: currentRowIndex === siblings[siblings.length - 1],
246
- };
247
- }
248
- function buildHorizontalAccountChildIndicesByParent(parentIndices) {
249
- const childIndicesByParentRowIndex = new Map();
250
- for (let rowIndex = 0; rowIndex < parentIndices.length; rowIndex++) {
251
- const parentRowIndex = parentIndices[rowIndex];
252
- if (parentRowIndex != null && parentRowIndex >= 0) {
253
- let childRowIndices = childIndicesByParentRowIndex.get(parentRowIndex);
254
- if (childRowIndices == null) {
255
- childRowIndices = [];
256
- childIndicesByParentRowIndex.set(parentRowIndex, childRowIndices);
257
- }
258
- childRowIndices.push(rowIndex);
259
- }
260
- }
261
- return childIndicesByParentRowIndex;
262
- }
@@ -1,65 +0,0 @@
1
- import { createSlice } from '@reduxjs/toolkit';
2
- const initialUserGroupList = {
3
- ids: [],
4
- fetchState: 'Not-Started',
5
- error: undefined,
6
- hasValidState() {
7
- return this.fetchState == 'Completed';
8
- },
9
- };
10
- export const initialState = {
11
- userGroupListByType: {
12
- taskManagerCandidate: initialUserGroupList,
13
- },
14
- };
15
- const userGroupListView = createSlice({
16
- name: 'userGroupListView',
17
- initialState,
18
- reducers: {
19
- fetchUserGroupListByType: {
20
- reducer(draft, action) {
21
- const { userGroupType } = action.payload;
22
- draft.userGroupListByType[userGroupType].fetchState = 'In-Progress';
23
- draft.userGroupListByType[userGroupType].error = undefined;
24
- },
25
- prepare(userGroupType, cacheOverride = false) {
26
- return {
27
- payload: {
28
- userGroupType,
29
- cacheOverride,
30
- },
31
- };
32
- },
33
- },
34
- updateUserGroupIds(draft, action) {
35
- const { userGroupType, userGroupsPayload } = action.payload;
36
- draft.userGroupListByType[userGroupType].fetchState = 'Completed';
37
- draft.userGroupListByType[userGroupType].error = undefined;
38
- draft.userGroupListByType[userGroupType].ids = userGroupsPayload.map((userGroup) => userGroup.id);
39
- },
40
- addUserGroupId(draft, action) {
41
- const { userGroupType, id } = action.payload;
42
- const index = draft.userGroupListByType[userGroupType].ids.indexOf(id);
43
- if (index < 0) {
44
- draft.userGroupListByType[userGroupType].ids.push(id);
45
- }
46
- },
47
- removeUserGroupId(draft, action) {
48
- const { userGroupType, id } = action.payload;
49
- const index = draft.userGroupListByType[userGroupType].ids.indexOf(id);
50
- if (index > -1) {
51
- draft.userGroupListByType[userGroupType].ids.splice(index, 1);
52
- }
53
- },
54
- updateUserGroupListFetchFailure(draft, action) {
55
- const { userGroupType, status } = action.payload;
56
- draft.userGroupListByType[userGroupType].fetchState = 'Error';
57
- draft.userGroupListByType[userGroupType].error = status;
58
- },
59
- clearUserGroupListView(draft) {
60
- Object.assign(draft, initialState);
61
- },
62
- },
63
- });
64
- export const { fetchUserGroupListByType, updateUserGroupIds, addUserGroupId, removeUserGroupId, updateUserGroupListFetchFailure, clearUserGroupListView, } = userGroupListView.actions;
65
- export default userGroupListView.reducer;
@@ -1,16 +0,0 @@
1
- import { getUserGroupByUserGroupId } from '../../entity/userGroups/userGroupsSelector';
2
- export const getUserGroupList = (userGroupsState, userGroupListViewState, userGroupType) => {
3
- const listState = userGroupListViewState.userGroupListByType[userGroupType];
4
- return {
5
- userGroups: getUserGroupListByIds(userGroupsState, userGroupListViewState, userGroupType),
6
- fetchState: listState?.fetchState,
7
- error: listState?.error,
8
- version: 0,
9
- };
10
- };
11
- function getUserGroupListByIds(userGroupsState, userGroupListViewState, type) {
12
- const userGroupIds = userGroupListViewState.userGroupListByType[type].ids;
13
- return userGroupIds
14
- .map((id) => getUserGroupByUserGroupId(userGroupsState, id))
15
- .filter((value) => value != null);
16
- }