@zeniai/client-epic-state 5.2.30-betaRD0 → 5.2.31

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 (93) hide show
  1. package/lib/commonStateTypes/coaBalance/classBalances/getSumOfBalancesGroupedByPeriod.js +16 -3
  2. package/lib/commonStateTypes/viewAndReport/reportIDHelper.d.ts +6 -0
  3. package/lib/commonStateTypes/viewAndReport/reportIDHelper.js +12 -1
  4. package/lib/commonStateTypes/viewAndReport/viewAndReport.d.ts +2 -2
  5. package/lib/commonStateTypes/viewAndReport/viewAndReport.js +2 -0
  6. package/lib/entity/accountGroup/accountGroupState.d.ts +1 -1
  7. package/lib/entity/class/classReducer.d.ts +4 -4
  8. package/lib/entity/class/classState.d.ts +1 -1
  9. package/lib/entity/forecast/forecastState.d.ts +1 -1
  10. package/lib/entity/sectionAccountsView/sectionAccountsView.d.ts +1 -1
  11. package/lib/entity/sectionClassesViewV2/sectionClassesView.d.ts +1 -1
  12. package/lib/entity/sectionClassesViewV2/sectionClassesViewEpic.js +2 -1
  13. package/lib/entity/sectionClassesViewV2/sectionClassesViewReducer.d.ts +2 -2
  14. package/lib/entity/sectionProjectView/sectionProjectView.d.ts +1 -1
  15. package/lib/entity/sectionProjectView/sectionProjectViewReducer.d.ts +2 -2
  16. package/lib/epic.d.ts +5 -1
  17. package/lib/epic.js +8 -0
  18. package/lib/esm/commonStateTypes/coaBalance/classBalances/getSumOfBalancesGroupedByPeriod.js +16 -3
  19. package/lib/esm/commonStateTypes/viewAndReport/reportIDHelper.js +7 -0
  20. package/lib/esm/commonStateTypes/viewAndReport/viewAndReport.js +2 -0
  21. package/lib/esm/entity/sectionClassesViewV2/sectionClassesViewEpic.js +2 -1
  22. package/lib/esm/epic.js +8 -0
  23. package/lib/esm/index.js +7 -3
  24. package/lib/esm/reducer.js +6 -0
  25. package/lib/esm/view/cashFlow/cashFlowGroupedSectionOrder.js +47 -0
  26. package/lib/esm/view/cashFlowClassesView/cashFlowClassesViewEpic.js +30 -0
  27. package/lib/esm/view/cashFlowClassesView/cashFlowClassesViewPayload.js +1 -0
  28. package/lib/esm/view/cashFlowClassesView/cashFlowClassesViewReducer.js +129 -0
  29. package/lib/esm/view/cashFlowClassesView/cashFlowClassesViewSelector.js +53 -0
  30. package/lib/esm/view/cashFlowClassesView/cashFlowClassesViewSelectorTypes.js +1 -0
  31. package/lib/esm/view/cashFlowClassesView/cashFlowClassesViewState.js +1 -0
  32. package/lib/esm/view/cashFlowClassesView/cashFlowForTimeframeClassesViewEpic.js +55 -0
  33. package/lib/esm/view/cashFlowProjectView/cashFlowForTimeframeProjectViewEpic.js +57 -0
  34. package/lib/esm/view/cashFlowProjectView/cashFlowProjectViewEpic.js +30 -0
  35. package/lib/esm/view/cashFlowProjectView/cashFlowProjectViewPayload.js +1 -0
  36. package/lib/esm/view/cashFlowProjectView/cashFlowProjectViewReducer.js +125 -0
  37. package/lib/esm/view/cashFlowProjectView/cashFlowProjectViewSelector.js +54 -0
  38. package/lib/esm/view/cashFlowProjectView/cashFlowProjectViewSelectorTypes.js +1 -0
  39. package/lib/esm/view/cashFlowProjectView/cashFlowProjectViewState.js +1 -0
  40. package/lib/esm/view/expenseAutomationView/epics/jeSchedule/fetchJeSchedulesEpic.js +11 -29
  41. package/lib/esm/view/financeStatement/financeStatementSelector.js +21 -1
  42. package/lib/index.d.ts +11 -3
  43. package/lib/index.js +71 -48
  44. package/lib/reducer.d.ts +2 -0
  45. package/lib/reducer.js +6 -0
  46. package/lib/rootStateTypes.d.ts +4 -0
  47. package/lib/view/auditReportView/auditReportViewState.d.ts +1 -1
  48. package/lib/view/billPayCard/billPayCardSelector.d.ts +1 -1
  49. package/lib/view/cardBalance/cardBalanceSelector.d.ts +1 -1
  50. package/lib/view/cashBalance/cashBalanceSelector.d.ts +1 -1
  51. package/lib/view/cashFlow/cashFlowGroupedSectionOrder.d.ts +15 -0
  52. package/lib/view/cashFlow/cashFlowGroupedSectionOrder.js +50 -0
  53. package/lib/view/cashFlowClassesView/cashFlowClassesViewEpic.d.ts +6 -0
  54. package/lib/view/cashFlowClassesView/cashFlowClassesViewEpic.js +34 -0
  55. package/lib/view/cashFlowClassesView/cashFlowClassesViewPayload.d.ts +14 -0
  56. package/lib/view/cashFlowClassesView/cashFlowClassesViewPayload.js +2 -0
  57. package/lib/view/cashFlowClassesView/cashFlowClassesViewReducer.d.ts +34 -0
  58. package/lib/view/cashFlowClassesView/cashFlowClassesViewReducer.js +133 -0
  59. package/lib/view/cashFlowClassesView/cashFlowClassesViewSelector.d.ts +7 -0
  60. package/lib/view/cashFlowClassesView/cashFlowClassesViewSelector.js +57 -0
  61. package/lib/view/cashFlowClassesView/cashFlowClassesViewSelectorTypes.d.ts +15 -0
  62. package/lib/view/cashFlowClassesView/cashFlowClassesViewSelectorTypes.js +2 -0
  63. package/lib/view/cashFlowClassesView/cashFlowClassesViewState.d.ts +23 -0
  64. package/lib/view/cashFlowClassesView/cashFlowClassesViewState.js +2 -0
  65. package/lib/view/cashFlowClassesView/cashFlowForTimeframeClassesViewEpic.d.ts +9 -0
  66. package/lib/view/cashFlowClassesView/cashFlowForTimeframeClassesViewEpic.js +60 -0
  67. package/lib/view/cashFlowProjectView/cashFlowForTimeframeProjectViewEpic.d.ts +10 -0
  68. package/lib/view/cashFlowProjectView/cashFlowForTimeframeProjectViewEpic.js +62 -0
  69. package/lib/view/cashFlowProjectView/cashFlowProjectViewEpic.d.ts +6 -0
  70. package/lib/view/cashFlowProjectView/cashFlowProjectViewEpic.js +34 -0
  71. package/lib/view/cashFlowProjectView/cashFlowProjectViewPayload.d.ts +14 -0
  72. package/lib/view/cashFlowProjectView/cashFlowProjectViewPayload.js +2 -0
  73. package/lib/view/cashFlowProjectView/cashFlowProjectViewReducer.d.ts +33 -0
  74. package/lib/view/cashFlowProjectView/cashFlowProjectViewReducer.js +129 -0
  75. package/lib/view/cashFlowProjectView/cashFlowProjectViewSelector.d.ts +7 -0
  76. package/lib/view/cashFlowProjectView/cashFlowProjectViewSelector.js +58 -0
  77. package/lib/view/cashFlowProjectView/cashFlowProjectViewSelectorTypes.d.ts +19 -0
  78. package/lib/view/cashFlowProjectView/cashFlowProjectViewSelectorTypes.js +2 -0
  79. package/lib/view/cashFlowProjectView/cashFlowProjectViewState.d.ts +22 -0
  80. package/lib/view/cashFlowProjectView/cashFlowProjectViewState.js +2 -0
  81. package/lib/view/cashInCashOut/cashInCashOutSelector.d.ts +1 -1
  82. package/lib/view/cashPosition/cashPositionSelector.d.ts +1 -1
  83. package/lib/view/dashboard/dashboardReducer.d.ts +2 -2
  84. package/lib/view/expenseAutomationView/epics/jeSchedule/fetchJeSchedulesEpic.js +11 -29
  85. package/lib/view/financeStatement/financeStatementReducer.d.ts +1 -1
  86. package/lib/view/financeStatement/financeStatementSelector.d.ts +4 -0
  87. package/lib/view/financeStatement/financeStatementSelector.js +21 -1
  88. package/lib/view/globalMerchantView/globalMerchantViewReducer.d.ts +2 -2
  89. package/lib/view/reimbursementCard/reimbursementCardSelector.d.ts +1 -1
  90. package/lib/view/reportsResync/reportsResyncReducer.d.ts +2 -2
  91. package/lib/view/tasksCard/tasksCardSelector.d.ts +1 -1
  92. package/lib/view/topEx/topExSelector.d.ts +1 -1
  93. package/package.json +1 -1
@@ -0,0 +1,129 @@
1
+ import { createSlice } from '@reduxjs/toolkit';
2
+ import { mapReportPayloadV2ToReportV2 } from '../../commonPayloadTypes/v2/viewAndReportPayloadv2';
3
+ import { geNestedClassIDHierarchyForReport } from '../../commonStateTypes/classesView/getNestedClassIDHierarchyForReport';
4
+ import { toClassMetadata } from '../../entity/class/classPayload';
5
+ import { clearAll } from '../../rootActions';
6
+ export const initialState = {
7
+ classHierarchy: [],
8
+ bookCloseDate: undefined,
9
+ fetchState: {
10
+ month: {
11
+ fetchState: 'Not-Started',
12
+ error: undefined,
13
+ },
14
+ quarter: {
15
+ fetchState: 'Not-Started',
16
+ error: undefined,
17
+ },
18
+ year: {
19
+ fetchState: 'Not-Started',
20
+ error: undefined,
21
+ },
22
+ },
23
+ uiState: {
24
+ nodeCollapseState: {},
25
+ scrollPosition: undefined,
26
+ accountViewMode: 'account_group',
27
+ classesToFilterOut: [],
28
+ },
29
+ firstMonthOfFY: 1,
30
+ reportId: 'cash_flow_by_classes',
31
+ reportTitle: 'cash_flow_by_classes',
32
+ };
33
+ const cashFlowClassesView = createSlice({
34
+ name: 'cashFlowClassesView',
35
+ initialState,
36
+ reducers: {
37
+ fetchCashFlowClassesView: {
38
+ prepare(cacheOverride = false) {
39
+ return { payload: { cacheOverride } };
40
+ },
41
+ reducer(
42
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
43
+ _draft,
44
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
45
+ _action) {
46
+ // nothing to do here
47
+ },
48
+ },
49
+ fetchCashFlowForTimeframeClassesView: {
50
+ prepare(timeframe, cacheOverride = false) {
51
+ return { payload: { timeframe, cacheOverride } };
52
+ },
53
+ reducer(draft, action) {
54
+ const timeframe = action.payload.timeframe;
55
+ draft.fetchState[timeframe].fetchState = 'In-Progress';
56
+ draft.fetchState[timeframe].error = undefined;
57
+ },
58
+ },
59
+ updateCashFlowForTimeframeClassesView(draft, action) {
60
+ doUpdateCashFlowClassesView(draft, action.payload.timeframe, action.payload.report);
61
+ },
62
+ updateCashFlowForTimeframeClassesViewFailure: {
63
+ prepare(error, timeframe) {
64
+ return {
65
+ payload: {
66
+ error,
67
+ timeframe,
68
+ },
69
+ };
70
+ },
71
+ reducer(draft, action) {
72
+ draft.fetchState[action.payload.timeframe] = {
73
+ fetchState: 'Error',
74
+ error: action.payload.error,
75
+ };
76
+ },
77
+ },
78
+ updateCashFlowClassesToFilterOut(draft, action) {
79
+ draft.uiState.classesToFilterOut = action.payload.classesToFilterOut;
80
+ },
81
+ updateCashFlowClassesViewUIState(draft, action) {
82
+ const { nodeCollapseState, scrollPosition, resetLeftScroll } = action.payload;
83
+ if (nodeCollapseState != null) {
84
+ draft.uiState.nodeCollapseState = Object.assign({}, draft.uiState.nodeCollapseState, {
85
+ [nodeCollapseState.nodeId]: nodeCollapseState.isCollapsed,
86
+ });
87
+ }
88
+ // Mutually exclusive: resetLeftScroll clears only scrollLeft from the
89
+ // recorded position, scrollPosition replaces the whole record.
90
+ if (resetLeftScroll === true && draft.uiState.scrollPosition != null) {
91
+ draft.uiState.scrollPosition.scrollLeft = undefined;
92
+ }
93
+ else if (scrollPosition != null) {
94
+ draft.uiState.scrollPosition = scrollPosition;
95
+ }
96
+ },
97
+ updateCashFlowClassesViewAccountViewMode(draft, action) {
98
+ draft.uiState.accountViewMode = action.payload.viewMode;
99
+ },
100
+ resetCashFlowClassesNodeCollapseState(draft) {
101
+ draft.uiState.nodeCollapseState = {};
102
+ },
103
+ },
104
+ extraReducers: (builder) => {
105
+ builder.addCase(clearAll, (draft) => {
106
+ Object.assign(draft, initialState);
107
+ });
108
+ },
109
+ });
110
+ export const { fetchCashFlowClassesView, fetchCashFlowForTimeframeClassesView, updateCashFlowForTimeframeClassesView, updateCashFlowForTimeframeClassesViewFailure, updateCashFlowClassesToFilterOut, updateCashFlowClassesViewUIState, updateCashFlowClassesViewAccountViewMode, resetCashFlowClassesNodeCollapseState, } = cashFlowClassesView.actions;
111
+ export default cashFlowClassesView.reducer;
112
+ const doUpdateCashFlowClassesView = (draft, timeframe, cashFlowReport) => {
113
+ const report = mapReportPayloadV2ToReportV2(cashFlowReport.report);
114
+ draft.currency = report.currency;
115
+ draft.dataAvailable = report.dataAvailable;
116
+ draft.firstMonthOfFY = report.firstMonthOfFY;
117
+ draft.bookCloseDate = report.bookCloseDate;
118
+ draft.status = report.status;
119
+ draft.fetchState[timeframe].fetchState = 'Completed';
120
+ draft.fetchState[timeframe].error = undefined;
121
+ const classMetadata = cashFlowReport.report.classes.map((eachClass) => toClassMetadata(eachClass));
122
+ // Populated once and then left alone, so a per-timeframe response cannot
123
+ // clobber a hierarchy built from a full one. A class added mid-session is
124
+ // therefore not picked up until the store is cleared, even by a hard refresh —
125
+ // the same trade-off profitAndLossClassesViewReducer makes.
126
+ if (draft.classHierarchy.length <= 0) {
127
+ draft.classHierarchy = geNestedClassIDHierarchyForReport(classMetadata);
128
+ }
129
+ };
@@ -0,0 +1,53 @@
1
+ import { getCOABalances } from '../../commonStateTypes/coaBalance/getCOABalancesSlice';
2
+ import { reduceFetchStateParallelTimeframes } from '../../commonStateTypes/reduceFetchState';
3
+ import { getSectionClassesViewReport } from '../../entity/sectionClassesViewV2/sectionClassesViewSelector';
4
+ import { CASH_FLOW_GROUPED_BREAKDOWN_IDS, CASH_FLOW_GROUPED_CALCULATED_IDS, CASH_FLOW_GROUPED_FINANCING_IDS_IN_ORDER, CASH_FLOW_GROUPED_IDS_IN_ORDER, CASH_FLOW_GROUPED_INVESTING_IDS_IN_ORDER, CASH_FLOW_GROUPED_OPERATING_IDS_IN_ORDER, } from '../cashFlow/cashFlowGroupedSectionOrder';
5
+ export const getCashFlowClassesView = (filter, accountState, classState, sectionsState, cashFlowClassesViewState) => {
6
+ const fetchState = reduceFetchStateParallelTimeframes([
7
+ cashFlowClassesViewState.fetchState.month,
8
+ cashFlowClassesViewState.fetchState.quarter,
9
+ cashFlowClassesViewState.fetchState.year,
10
+ ]);
11
+ const reportId = 'cash_flow_by_classes';
12
+ const sections = {};
13
+ const calculatedSections = {};
14
+ if (fetchState.fetchState === 'Completed') {
15
+ CASH_FLOW_GROUPED_BREAKDOWN_IDS.forEach((sectionId) => {
16
+ sections[sectionId] = getSectionClassesViewReport(accountState, classState, sectionsState, { reportId, sectionId }, filter);
17
+ });
18
+ // These come back grouped, so each figure is read through the section layer
19
+ // and arrives already pruned by the class/project picker — the same source
20
+ // the breakdown rows above render from, so the two can never disagree.
21
+ CASH_FLOW_GROUPED_CALCULATED_IDS.forEach((calculatedSectionId) => {
22
+ calculatedSections[calculatedSectionId] = getCOABalances(
23
+ // Already built by the breakdown loop above; rebuilding it would repeat
24
+ // the whole section walk on every recompute.
25
+ sections[calculatedSectionId].sectionBalancesTotalByPeriod, filter);
26
+ });
27
+ calculatedSections['investingActivitiesTotal'] = getCOABalances(sections['investingActivities'].sectionBalancesTotalByPeriod, filter);
28
+ calculatedSections['financingActivitiesTotal'] = getCOABalances(sections['financingActivities'].sectionBalancesTotalByPeriod, filter);
29
+ }
30
+ return {
31
+ sections,
32
+ calculatedSections,
33
+ fetchState: fetchState.fetchState,
34
+ error: fetchState.error,
35
+ filter,
36
+ uiState: cashFlowClassesViewState.uiState,
37
+ classHierarchy: cashFlowClassesViewState.classHierarchy,
38
+ classesToFilterOut: cashFlowClassesViewState.uiState.classesToFilterOut,
39
+ reportId: cashFlowClassesViewState.reportId,
40
+ reportTitle: cashFlowClassesViewState.reportTitle,
41
+ firstMonthOfFY: cashFlowClassesViewState.firstMonthOfFY,
42
+ dataAvailable: cashFlowClassesViewState.dataAvailable,
43
+ bookCloseDate: cashFlowClassesViewState.bookCloseDate,
44
+ version: cashFlowClassesViewState.version,
45
+ status: cashFlowClassesViewState.status,
46
+ idsInOrder: CASH_FLOW_GROUPED_IDS_IN_ORDER,
47
+ rootSectionsWithSubSectionIds: {
48
+ operatingActivities: CASH_FLOW_GROUPED_OPERATING_IDS_IN_ORDER,
49
+ investingActivities: CASH_FLOW_GROUPED_INVESTING_IDS_IN_ORDER,
50
+ financingActivities: CASH_FLOW_GROUPED_FINANCING_IDS_IN_ORDER,
51
+ },
52
+ };
53
+ };
@@ -0,0 +1,55 @@
1
+ import { from, of } from 'rxjs';
2
+ import { catchError, filter, mergeMap } from 'rxjs/operators';
3
+ import { updateSectionClassesView } from '../../entity/sectionClassesViewV2/sectionClassesViewReducer';
4
+ import { createZeniAPIStatus, isSuccessResponse } from '../../responsePayload';
5
+ import { toCashFlowReportViewID } from '../cashFlow/cashFlowPayload';
6
+ import { fetchCashFlowForTimeframeClassesView, updateCashFlowForTimeframeClassesView, updateCashFlowForTimeframeClassesViewFailure, } from './cashFlowClassesViewReducer';
7
+ const getTimeSeriesGroup = (timeframe) => {
8
+ switch (timeframe) {
9
+ case 'month':
10
+ return 'months';
11
+ case 'quarter':
12
+ return 'quarters';
13
+ case 'year':
14
+ return 'years';
15
+ }
16
+ };
17
+ export const reportIdUniqueKey = () => 'cash_flow_by_classes';
18
+ export const fetchCashFlowForTimeframeClassesViewEpic = (actions$, _, zeniAPI) => actions$.pipe(filter(fetchCashFlowForTimeframeClassesView.match), mergeMap((action) => {
19
+ const queryValue = {
20
+ is_full_response: true,
21
+ time_series_groups: [getTimeSeriesGroup(action.payload.timeframe)],
22
+ is_group_by_class: true,
23
+ // Only sent when refreshing: the flag makes the backend bypass its saved
24
+ // report and regenerate, so a normal lookup must not carry it.
25
+ ...(action.payload.cacheOverride === true
26
+ ? { is_hard_refresh: true }
27
+ : {}),
28
+ };
29
+ return zeniAPI
30
+ .getJSON(`${zeniAPI.apiEndPoints.accountMicroServiceBaseUrl}/2.0/reports/cash_flow?query=${encodeURIComponent(JSON.stringify(queryValue))}`)
31
+ .pipe(mergeMap((response) => {
32
+ if (response.data != null && isSuccessResponse(response)) {
33
+ const actions = [];
34
+ const { report } = response.data;
35
+ const currency = report.currency;
36
+ actions.push(updateSectionClassesView(action.payload.timeframe, toCashFlowReportViewID('adjustments'), reportIdUniqueKey(), report.operating_activities.adjustments, report.classes, report.accounts, currency));
37
+ actions.push(updateSectionClassesView(action.payload.timeframe, toCashFlowReportViewID('investing_activities'), reportIdUniqueKey(), report.investing_activities, report.classes, report.accounts, currency));
38
+ actions.push(updateSectionClassesView(action.payload.timeframe, toCashFlowReportViewID('financing_activities'), reportIdUniqueKey(), report.financing_activities, report.classes, report.accounts, currency));
39
+ actions.push(updateSectionClassesView(action.payload.timeframe, toCashFlowReportViewID('net_income'), reportIdUniqueKey(), report.operating_activities.net_income, report.classes, report.accounts, currency));
40
+ actions.push(updateSectionClassesView(action.payload.timeframe, toCashFlowReportViewID('operating_activities'), reportIdUniqueKey(), report.operating_activities, report.classes, report.accounts, currency));
41
+ actions.push(updateSectionClassesView(action.payload.timeframe, toCashFlowReportViewID('change_in_cash_and_equivalents'), reportIdUniqueKey(), report.change_in_cash_and_equivalents, report.classes, report.accounts, currency));
42
+ actions.push(updateCashFlowForTimeframeClassesView({
43
+ timeframe: action.payload.timeframe,
44
+ report: response.data,
45
+ }));
46
+ return from(actions);
47
+ }
48
+ else {
49
+ return of(updateCashFlowForTimeframeClassesViewFailure(response.status, action.payload.timeframe));
50
+ }
51
+ }), catchError((error) => of(updateCashFlowForTimeframeClassesViewFailure(createZeniAPIStatus('Unexpected Error', 'Cash Flow by Class REST API call errored out: ' +
52
+ (error instanceof Error
53
+ ? error.message
54
+ : JSON.stringify(error))), action.payload.timeframe))));
55
+ }));
@@ -0,0 +1,57 @@
1
+ import { from, of } from 'rxjs';
2
+ import { catchError, filter, mergeMap } from 'rxjs/operators';
3
+ import { updateProjects } from '../../entity/project/projectReducer';
4
+ import { updateSectionProjectView } from '../../entity/sectionProjectView/sectionProjectViewReducer';
5
+ import { createZeniAPIStatus, isSuccessResponse } from '../../responsePayload';
6
+ import { toCashFlowReportViewID } from '../cashFlow/cashFlowPayload';
7
+ import { fetchCashFlowForTimeframeProjectView, updateCashFlowForTimeframeProjectView, updateCashFlowForTimeframeProjectViewFailure, } from './cashFlowProjectViewReducer';
8
+ const getTimeSeriesGroup = (timeframe) => {
9
+ switch (timeframe) {
10
+ case 'month':
11
+ return 'months';
12
+ case 'quarter':
13
+ return 'quarters';
14
+ case 'year':
15
+ return 'years';
16
+ }
17
+ };
18
+ export const reportIdUniqueKey = () => 'cash_flow_by_projects';
19
+ export const fetchCashFlowForTimeframeProjectViewEpic = (actions$, _, zeniAPI) => actions$.pipe(filter(fetchCashFlowForTimeframeProjectView.match), mergeMap((action) => {
20
+ const queryValue = {
21
+ is_full_response: true,
22
+ time_series_groups: [getTimeSeriesGroup(action.payload.timeframe)],
23
+ is_group_by_project: true,
24
+ // Only sent when refreshing: the flag makes the backend bypass its saved
25
+ // report and regenerate, so a normal lookup must not carry it.
26
+ ...(action.payload.cacheOverride === true
27
+ ? { is_hard_refresh: true }
28
+ : {}),
29
+ };
30
+ return zeniAPI
31
+ .getJSON(`${zeniAPI.apiEndPoints.accountMicroServiceBaseUrl}/2.0/reports/cash_flow?query=${encodeURIComponent(JSON.stringify(queryValue))}`)
32
+ .pipe(mergeMap((response) => {
33
+ if (response.data != null && isSuccessResponse(response)) {
34
+ const actions = [];
35
+ const { report } = response.data;
36
+ const currency = report.currency;
37
+ actions.push(updateProjects(report.projects));
38
+ actions.push(updateSectionProjectView(action.payload.timeframe, toCashFlowReportViewID('adjustments'), reportIdUniqueKey(), report.operating_activities.adjustments, report.accounts, currency));
39
+ actions.push(updateSectionProjectView(action.payload.timeframe, toCashFlowReportViewID('investing_activities'), reportIdUniqueKey(), report.investing_activities, report.accounts, currency));
40
+ actions.push(updateSectionProjectView(action.payload.timeframe, toCashFlowReportViewID('financing_activities'), reportIdUniqueKey(), report.financing_activities, report.accounts, currency));
41
+ actions.push(updateSectionProjectView(action.payload.timeframe, toCashFlowReportViewID('net_income'), reportIdUniqueKey(), report.operating_activities.net_income, report.accounts, currency));
42
+ actions.push(updateSectionProjectView(action.payload.timeframe, toCashFlowReportViewID('operating_activities'), reportIdUniqueKey(), report.operating_activities, report.accounts, currency));
43
+ actions.push(updateSectionProjectView(action.payload.timeframe, toCashFlowReportViewID('change_in_cash_and_equivalents'), reportIdUniqueKey(), report.change_in_cash_and_equivalents, report.accounts, currency));
44
+ actions.push(updateCashFlowForTimeframeProjectView({
45
+ timeframe: action.payload.timeframe,
46
+ report: response.data,
47
+ }));
48
+ return from(actions);
49
+ }
50
+ else {
51
+ return of(updateCashFlowForTimeframeProjectViewFailure(response.status, action.payload.timeframe));
52
+ }
53
+ }), catchError((error) => of(updateCashFlowForTimeframeProjectViewFailure(createZeniAPIStatus('Unexpected Error', 'Cash Flow by Project REST API call errored out: ' +
54
+ (error instanceof Error
55
+ ? error.message
56
+ : JSON.stringify(error))), action.payload.timeframe))));
57
+ }));
@@ -0,0 +1,30 @@
1
+ import { from } from 'rxjs';
2
+ import { filter, switchMap } from 'rxjs/operators';
3
+ import { fetchCashFlowForTimeframeProjectView, fetchCashFlowProjectView, } from './cashFlowProjectViewReducer';
4
+ export const fetchCashFlowProjectViewEpic = (actions$, state) => actions$.pipe(filter(fetchCashFlowProjectView.match), switchMap((action) => {
5
+ const { cacheOverride } = action.payload;
6
+ if (cacheOverride === true) {
7
+ return from([
8
+ fetchCashFlowForTimeframeProjectView('month', true),
9
+ fetchCashFlowForTimeframeProjectView('quarter', true),
10
+ fetchCashFlowForTimeframeProjectView('year', true),
11
+ ]);
12
+ }
13
+ else {
14
+ const fetchActions = [];
15
+ const projectFetchState = state.value.cashFlowProjectViewState.fetchState;
16
+ if (projectFetchState['month'].fetchState !== 'In-Progress' &&
17
+ projectFetchState['month'].fetchState !== 'Completed') {
18
+ fetchActions.push(fetchCashFlowForTimeframeProjectView('month'));
19
+ }
20
+ if (projectFetchState['quarter'].fetchState !== 'In-Progress' &&
21
+ projectFetchState['quarter'].fetchState !== 'Completed') {
22
+ fetchActions.push(fetchCashFlowForTimeframeProjectView('quarter'));
23
+ }
24
+ if (projectFetchState['year'].fetchState !== 'In-Progress' &&
25
+ projectFetchState['year'].fetchState !== 'Completed') {
26
+ fetchActions.push(fetchCashFlowForTimeframeProjectView('year'));
27
+ }
28
+ return from(fetchActions);
29
+ }
30
+ }));
@@ -0,0 +1,125 @@
1
+ import { createSlice } from '@reduxjs/toolkit';
2
+ import { mapReportPayloadV2ToReportV2 } from '../../commonPayloadTypes/v2/viewAndReportPayloadv2';
3
+ import { clearAll } from '../../rootActions';
4
+ export const initialState = {
5
+ bookCloseDate: undefined,
6
+ fetchState: {
7
+ month: {
8
+ fetchState: 'Not-Started',
9
+ error: undefined,
10
+ },
11
+ quarter: {
12
+ fetchState: 'Not-Started',
13
+ error: undefined,
14
+ },
15
+ year: {
16
+ fetchState: 'Not-Started',
17
+ error: undefined,
18
+ },
19
+ },
20
+ firstMonthOfFY: 1,
21
+ projectIds: [],
22
+ reportId: 'cash_flow_by_projects',
23
+ reportTitle: 'cash_flow_by_projects',
24
+ uiState: {
25
+ accountViewMode: 'account_group',
26
+ nodeCollapseState: {},
27
+ projectsToFilterOut: [],
28
+ scrollPosition: undefined,
29
+ },
30
+ };
31
+ const cashFlowProjectView = createSlice({
32
+ name: 'cashFlowProjectView',
33
+ initialState,
34
+ reducers: {
35
+ fetchCashFlowProjectView: {
36
+ prepare(cacheOverride = false) {
37
+ return { payload: { cacheOverride } };
38
+ },
39
+ reducer(
40
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
41
+ _draft,
42
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
43
+ _action) {
44
+ // nothing to do here
45
+ },
46
+ },
47
+ fetchCashFlowForTimeframeProjectView: {
48
+ prepare(timeframe, cacheOverride = false) {
49
+ return { payload: { timeframe, cacheOverride } };
50
+ },
51
+ reducer(draft, action) {
52
+ const timeframe = action.payload.timeframe;
53
+ draft.fetchState[timeframe].fetchState = 'In-Progress';
54
+ draft.fetchState[timeframe].error = undefined;
55
+ },
56
+ },
57
+ updateCashFlowForTimeframeProjectView(draft, action) {
58
+ doUpdateCashFlowProjectView(draft, action.payload.timeframe, action.payload.report);
59
+ },
60
+ updateCashFlowForTimeframeProjectViewFailure: {
61
+ prepare(error, timeframe) {
62
+ return {
63
+ payload: {
64
+ error,
65
+ timeframe,
66
+ },
67
+ };
68
+ },
69
+ reducer(draft, action) {
70
+ draft.fetchState[action.payload.timeframe] = {
71
+ fetchState: 'Error',
72
+ error: action.payload.error,
73
+ };
74
+ },
75
+ },
76
+ updateCashFlowProjectsToFilterOut(draft, action) {
77
+ draft.uiState.projectsToFilterOut = action.payload.projectsToFilterOut;
78
+ },
79
+ updateCashFlowProjectViewUIState(draft, action) {
80
+ const { nodeCollapseState, scrollPosition, resetLeftScroll } = action.payload;
81
+ if (nodeCollapseState != null) {
82
+ draft.uiState.nodeCollapseState = Object.assign({}, draft.uiState.nodeCollapseState, {
83
+ [nodeCollapseState.nodeId]: nodeCollapseState.isCollapsed,
84
+ });
85
+ }
86
+ // Mutually exclusive: resetLeftScroll clears only scrollLeft from the
87
+ // recorded position, scrollPosition replaces the whole record.
88
+ if (resetLeftScroll === true && draft.uiState.scrollPosition != null) {
89
+ draft.uiState.scrollPosition.scrollLeft = undefined;
90
+ }
91
+ else if (scrollPosition != null) {
92
+ draft.uiState.scrollPosition = scrollPosition;
93
+ }
94
+ },
95
+ updateCashFlowProjectViewAccountViewMode(draft, action) {
96
+ draft.uiState.accountViewMode = action.payload.viewMode;
97
+ },
98
+ resetCashFlowProjectNodeCollapseState(draft) {
99
+ draft.uiState.nodeCollapseState = {};
100
+ },
101
+ },
102
+ extraReducers: (builder) => {
103
+ builder.addCase(clearAll, (draft) => {
104
+ Object.assign(draft, initialState);
105
+ });
106
+ },
107
+ });
108
+ export const { fetchCashFlowProjectView, fetchCashFlowForTimeframeProjectView, updateCashFlowForTimeframeProjectView, updateCashFlowForTimeframeProjectViewFailure, updateCashFlowProjectsToFilterOut, updateCashFlowProjectViewUIState, updateCashFlowProjectViewAccountViewMode, resetCashFlowProjectNodeCollapseState, } = cashFlowProjectView.actions;
109
+ export default cashFlowProjectView.reducer;
110
+ const doUpdateCashFlowProjectView = (draft, timeframe, cashFlowReport) => {
111
+ const report = mapReportPayloadV2ToReportV2(cashFlowReport.report);
112
+ draft.currency = report.currency;
113
+ draft.dataAvailable = report.dataAvailable;
114
+ draft.firstMonthOfFY = report.firstMonthOfFY;
115
+ draft.bookCloseDate = report.bookCloseDate;
116
+ draft.status = report.status;
117
+ draft.fetchState[timeframe].fetchState = 'Completed';
118
+ draft.fetchState[timeframe].error = undefined;
119
+ // Populated once, as with classHierarchy in the classes view: a project added
120
+ // mid-session is not picked up until the store is cleared, even by a hard
121
+ // refresh. Matches profitAndLossProjectViewReducer.
122
+ if (draft.projectIds.length <= 0) {
123
+ draft.projectIds = cashFlowReport.report.projects.map((project) => project.accounting_project_id);
124
+ }
125
+ };
@@ -0,0 +1,54 @@
1
+ import { getCOABalances } from '../../commonStateTypes/coaBalance/getCOABalancesSlice';
2
+ import { reduceFetchStateParallelTimeframes } from '../../commonStateTypes/reduceFetchState';
3
+ import { getProjectsByIds } from '../../entity/project/projectSelector';
4
+ import { getSectionProjectViewReport } from '../../entity/sectionProjectView/sectionProjectViewSelector';
5
+ import { CASH_FLOW_GROUPED_BREAKDOWN_IDS, CASH_FLOW_GROUPED_CALCULATED_IDS, CASH_FLOW_GROUPED_FINANCING_IDS_IN_ORDER, CASH_FLOW_GROUPED_IDS_IN_ORDER, CASH_FLOW_GROUPED_INVESTING_IDS_IN_ORDER, CASH_FLOW_GROUPED_OPERATING_IDS_IN_ORDER, } from '../cashFlow/cashFlowGroupedSectionOrder';
6
+ export const getCashFlowProjectView = (filter, accountState, projectState, sectionsState, cashFlowProjectViewState) => {
7
+ const fetchState = reduceFetchStateParallelTimeframes([
8
+ cashFlowProjectViewState.fetchState.month,
9
+ cashFlowProjectViewState.fetchState.quarter,
10
+ cashFlowProjectViewState.fetchState.year,
11
+ ]);
12
+ const reportId = 'cash_flow_by_projects';
13
+ const sections = {};
14
+ const calculatedSections = {};
15
+ if (fetchState.fetchState === 'Completed') {
16
+ CASH_FLOW_GROUPED_BREAKDOWN_IDS.forEach((sectionId) => {
17
+ sections[sectionId] = getSectionProjectViewReport(accountState, projectState, sectionsState, cashFlowProjectViewState.projectIds, { reportId, sectionId }, filter);
18
+ });
19
+ // These come back grouped, so each figure is read through the section layer
20
+ // and arrives already pruned by the class/project picker — the same source
21
+ // the breakdown rows above render from, so the two can never disagree.
22
+ CASH_FLOW_GROUPED_CALCULATED_IDS.forEach((calculatedSectionId) => {
23
+ calculatedSections[calculatedSectionId] = getCOABalances(
24
+ // Already built by the breakdown loop above; rebuilding it would repeat
25
+ // the whole section walk on every recompute.
26
+ sections[calculatedSectionId].sectionBalancesTotalByPeriod, filter);
27
+ });
28
+ calculatedSections['investingActivitiesTotal'] = getCOABalances(sections['investingActivities'].sectionBalancesTotalByPeriod, filter);
29
+ calculatedSections['financingActivitiesTotal'] = getCOABalances(sections['financingActivities'].sectionBalancesTotalByPeriod, filter);
30
+ }
31
+ return {
32
+ sections,
33
+ calculatedSections,
34
+ fetchState: fetchState.fetchState,
35
+ error: fetchState.error,
36
+ filter,
37
+ uiState: cashFlowProjectViewState.uiState,
38
+ projects: getProjectsByIds(projectState, cashFlowProjectViewState.projectIds),
39
+ projectsToFilterOut: cashFlowProjectViewState.uiState.projectsToFilterOut,
40
+ reportId: cashFlowProjectViewState.reportId,
41
+ reportTitle: cashFlowProjectViewState.reportTitle,
42
+ firstMonthOfFY: cashFlowProjectViewState.firstMonthOfFY,
43
+ dataAvailable: cashFlowProjectViewState.dataAvailable,
44
+ bookCloseDate: cashFlowProjectViewState.bookCloseDate,
45
+ version: cashFlowProjectViewState.version,
46
+ status: cashFlowProjectViewState.status,
47
+ idsInOrder: CASH_FLOW_GROUPED_IDS_IN_ORDER,
48
+ rootSectionsWithSubSectionIds: {
49
+ operatingActivities: CASH_FLOW_GROUPED_OPERATING_IDS_IN_ORDER,
50
+ investingActivities: CASH_FLOW_GROUPED_INVESTING_IDS_IN_ORDER,
51
+ financingActivities: CASH_FLOW_GROUPED_FINANCING_IDS_IN_ORDER,
52
+ },
53
+ };
54
+ };
@@ -9,18 +9,6 @@ import { clearAll } from '../../../../rootActions';
9
9
  import { date } from '../../../../zeniDayJS';
10
10
  import { SERVICE_MONTH_FORMAT, } from '../../payload/jeSchedulesPayload';
11
11
  import { fetchJeSchedules, fetchJeSchedulesFailure, fetchJeSchedulesSuccess, initializeJeScheduleLocalData, } from '../../reducers/jeSchedulesViewReducer';
12
- /**
13
- * How far back to look for accrued schedules, matching the Vendors page
14
- * (`getDefaultSelectedTimeframeForScheduleType`, 'accrued_expenses'), which queries the same
15
- * start_date filter through /1.0/schedules.
16
- */
17
- const ACCRUED_LOOKBACK_MONTHS = 24;
18
- /**
19
- * One-time accruals keep their own, narrower lookback. Unlike an accrued schedule they belong to
20
- * exactly one service month and never span months, so the reason the accrued window has to reach
21
- * back does not apply to them -- widening it would only add older history to the Existing tab.
22
- */
23
- const ONE_TIME_ACCRUAL_LOOKBACK_MONTHS = 12;
24
12
  export const fetchJeSchedulesEpic = (actions$, state$, zeniAPI) => actions$.pipe(filter(fetchJeSchedules.match), mergeMap((action) => {
25
13
  const state = state$.value;
26
14
  const { expenseAutomationViewState: { selectedPeriodByTenantId }, } = state;
@@ -42,21 +30,15 @@ export const fetchJeSchedulesEpic = (actions$, state$, zeniAPI) => actions$.pipe
42
30
  const endDate = isClosePeriodTab || existingEndDate == null
43
31
  ? toString(period.end)
44
32
  : toString(existingEndDate);
45
- // Accrued rows are windowed on the schedule's own start_date -- its FIRST posting date --
46
- // rather than a transaction date, so the lower bound has to reach back past the start of
47
- // every accrual still running in this period, not just the ones that began in it. Review
48
- // used to send the close month itself, which returned only accruals whose first posting fell
49
- // in that month and hid every longer-running one.
50
- const parsedEndDate = date(endDate);
51
- const accruedStartDate = parsedEndDate
52
- .subtract(ACCRUED_LOOKBACK_MONTHS, 'month')
53
- .startOf('month')
54
- .format(DEFAULT_DATE_FORMAT);
55
- // Formatted as a service month, not a date: that is the only thing it is sent as.
56
- const oneTimeAccrualServiceMonthStart = parsedEndDate
57
- .subtract(ONE_TIME_ACCRUAL_LOOKBACK_MONTHS, 'month')
58
- .startOf('month')
59
- .format(SERVICE_MONTH_FORMAT);
33
+ // Accrued rows are windowed on the schedule's own start_date rather than a transaction date,
34
+ // so they get their own bounds. Review wants the month being closed; Existing wants the year
35
+ // up to endDate, snapped to the 1st — the same window the server used to compute for itself.
36
+ const accruedStartDate = isClosePeriodTab
37
+ ? toString(period.start)
38
+ : date(endDate)
39
+ .subtract(1, 'year')
40
+ .startOf('month')
41
+ .format(DEFAULT_DATE_FORMAT);
60
42
  const queryParam = {
61
43
  base_transaction_end_date: endDate,
62
44
  end_date: endDate,
@@ -77,8 +59,8 @@ export const fetchJeSchedulesEpic = (actions$, state$, zeniAPI) => actions$.pipe
77
59
  filter_failed_je_postings_by_date: true,
78
60
  }
79
61
  : {
80
- one_time_accrual_service_month_start: oneTimeAccrualServiceMonthStart,
81
- one_time_accrual_service_month_end: parsedEndDate.format(SERVICE_MONTH_FORMAT),
62
+ one_time_accrual_service_month_start: date(accruedStartDate).format(SERVICE_MONTH_FORMAT),
63
+ one_time_accrual_service_month_end: date(endDate).format(SERVICE_MONTH_FORMAT),
82
64
  // Existing lists what actually reached the books.
83
65
  one_time_accrual_statuses: ['posted'],
84
66
  // A failed posting from an earlier month still needs to surface here, so this list
@@ -11,6 +11,8 @@ import { getBalanceSheet } from '../balanceSheet/balanceSheetSelector';
11
11
  import { getBalanceSheetClassesView } from '../balanceSheetClassesView/balanceSheetClassesViewSelector';
12
12
  import { getBalanceSheetProjectView } from '../balanceSheetProjectView/balanceSheetProjectViewSelector';
13
13
  import { getCashFlow } from '../cashFlow/cashFlowSelector';
14
+ import { getCashFlowClassesView } from '../cashFlowClassesView/cashFlowClassesViewSelector';
15
+ import { getCashFlowProjectView } from '../cashFlowProjectView/cashFlowProjectViewSelector';
14
16
  import { getProfitAndLossReport, } from '../profitAndLoss/profitAndLossSelector';
15
17
  import { getProfitAndLossClassesHorizontalView } from '../profitAndLossClassesView/profitAndLossClassesByClassHorizontalSelector';
16
18
  import { getProfitAndLossClassesView } from '../profitAndLossClassesView/profitAndLossClassesViewSelector';
@@ -39,7 +41,7 @@ function getFirstMonthOfFY(profitAndLossState, balanceSheetState, cashFlowState)
39
41
  cashFlowState.firstMonthOfFY ??
40
42
  1);
41
43
  }
42
- export const getFinanceStatement = createSelector((state) => state.accountState, (state) => state.accountGroupState, (state) => state.classState, (state) => state.sectionClassesViewStateV2, (state) => state.profitAndLossState, (state) => state.profitAndLossClassesViewState, (state) => state.projectState, (state) => state.sectionProjectViewState, (state) => state.profitAndLossProjectViewState, (state) => state.balanceSheetState, (state) => state.balanceSheetClassesViewState, (state) => state.balanceSheetProjectViewState, (state) => state.cashFlowState, (state) => state.financeStatementState, (state) => state.sectionAccountsViewState, (state) => state.forecastState, (accountState, accountGroupState, classState, sectionsState, profitAndLossState, profitAndLossClassesViewState, projectState, sectionProjectViewState, profitAndLossProjectViewState, balanceSheetState, balanceSheetClassesViewState, balanceSheetProjectViewState, cashFlowState, financeStatementState, sectionAccountsViewState, forecastState) => {
44
+ export const getFinanceStatement = createSelector((state) => state.accountState, (state) => state.accountGroupState, (state) => state.classState, (state) => state.sectionClassesViewStateV2, (state) => state.profitAndLossState, (state) => state.profitAndLossClassesViewState, (state) => state.projectState, (state) => state.sectionProjectViewState, (state) => state.profitAndLossProjectViewState, (state) => state.balanceSheetState, (state) => state.balanceSheetClassesViewState, (state) => state.balanceSheetProjectViewState, (state) => state.cashFlowState, (state) => state.cashFlowClassesViewState, (state) => state.cashFlowProjectViewState, (state) => state.financeStatementState, (state) => state.sectionAccountsViewState, (state) => state.forecastState, (accountState, accountGroupState, classState, sectionsState, profitAndLossState, profitAndLossClassesViewState, projectState, sectionProjectViewState, profitAndLossProjectViewState, balanceSheetState, balanceSheetClassesViewState, balanceSheetProjectViewState, cashFlowState, cashFlowClassesViewState, cashFlowProjectViewState, financeStatementState, sectionAccountsViewState, forecastState) => {
43
45
  // to get min and max dates across three reports
44
46
  let allTimeframeTicks = [];
45
47
  let balancesInTimeframe = [];
@@ -81,6 +83,10 @@ export const getFinanceStatement = createSelector((state) => state.accountState,
81
83
  const balanceSheetReport = getBalanceSheet(filter, accountState, accountGroupState, sectionAccountsViewState, balanceSheetState);
82
84
  const balanceSheetClassFilter = newBalancesFilterClassesView(filter.firstMonthOfFY, filter.timeframe, filter.balancesRange.numberOfPeriods, filter.balancesRange.thisPeriod, filter.balancesRange.orderBy, filter.additionalBalances, balanceSheetClassesViewState.uiState.classesToFilterOut);
83
85
  const balanceSheetByClassReport = getBalanceSheetClassesView(balanceSheetClassFilter, accountState, classState, sectionsState, balanceSheetClassesViewState);
86
+ const cashFlowClassFilter = newBalancesFilterClassesView(filter.firstMonthOfFY, filter.timeframe, filter.balancesRange.numberOfPeriods, filter.balancesRange.thisPeriod, filter.balancesRange.orderBy, filter.additionalBalances, cashFlowClassesViewState.uiState.classesToFilterOut);
87
+ const cashFlowByClassReport = getCashFlowClassesView(cashFlowClassFilter, accountState, classState, sectionsState, cashFlowClassesViewState);
88
+ const cashFlowProjectFilter = newBalancesFilterProjectView(filter.firstMonthOfFY, filter.timeframe, filter.balancesRange.numberOfPeriods, filter.balancesRange.thisPeriod, filter.balancesRange.orderBy, filter.additionalBalances, cashFlowProjectViewState.uiState.projectsToFilterOut);
89
+ const cashFlowByProjectReport = getCashFlowProjectView(cashFlowProjectFilter, accountState, projectState, sectionProjectViewState, cashFlowProjectViewState);
84
90
  const balanceSheetProjectFilter = newBalancesFilterProjectView(filter.firstMonthOfFY, filter.timeframe, filter.balancesRange.numberOfPeriods, filter.balancesRange.thisPeriod, filter.balancesRange.orderBy, filter.additionalBalances, balanceSheetProjectViewState.uiState.projectsToFilterOut);
85
91
  const balanceSheetByProjectReport = getBalanceSheetProjectView(balanceSheetProjectFilter, accountState, projectState, sectionProjectViewState, balanceSheetProjectViewState);
86
92
  const cashFlowReport = getCashFlow(filter, accountState, accountGroupState, sectionAccountsViewState, cashFlowState);
@@ -129,6 +135,18 @@ export const getFinanceStatement = createSelector((state) => state.accountState,
129
135
  error: cashFlowReport.error,
130
136
  };
131
137
  break;
138
+ case 'cash_flow_by_classes':
139
+ selectedReportAndTimeframeFetchState = {
140
+ fetchState: cashFlowByClassReport.fetchState,
141
+ error: cashFlowByClassReport.error,
142
+ };
143
+ break;
144
+ case 'cash_flow_by_projects':
145
+ selectedReportAndTimeframeFetchState = {
146
+ fetchState: cashFlowByProjectReport.fetchState,
147
+ error: cashFlowByProjectReport.error,
148
+ };
149
+ break;
132
150
  default:
133
151
  break;
134
152
  }
@@ -151,6 +169,8 @@ export const getFinanceStatement = createSelector((state) => state.accountState,
151
169
  profitAndLossByClassHorizontalReport,
152
170
  balanceSheetReport,
153
171
  balanceSheetByClassReport,
172
+ cashFlowByClassReport,
173
+ cashFlowByProjectReport,
154
174
  balanceSheetByProjectReport,
155
175
  cashFlowReport,
156
176
  filter,