@zeniai/client-epic-state 4.19.90-beta1AD → 4.19.90-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 (62) hide show
  1. package/lib/entity/aiCfo/aiCfoSelector.d.ts +2 -1
  2. package/lib/entity/reimbursement/reimbursementPayload.d.ts +2 -0
  3. package/lib/entity/reimbursement/reimbursementPayload.js +7 -0
  4. package/lib/entity/reimbursement/reimbursementState.d.ts +2 -0
  5. package/lib/entity/transaction/payloadTypes/transactionPayload.d.ts +0 -3
  6. package/lib/entity/transaction/payloadTypes/transactionPayload.js +0 -11
  7. package/lib/entity/transaction/stateTypes/transaction.d.ts +0 -3
  8. package/lib/epic.d.ts +1 -2
  9. package/lib/epic.js +1 -2
  10. package/lib/esm/entity/reimbursement/reimbursementPayload.js +7 -0
  11. package/lib/esm/entity/transaction/payloadTypes/transactionPayload.js +0 -11
  12. package/lib/esm/epic.js +1 -2
  13. package/lib/esm/index.js +5 -4
  14. package/lib/esm/view/aiCfoView/aiCfoViewReducer.js +1 -82
  15. package/lib/esm/view/aiCfoView/aiCfoViewSelector.js +2 -8
  16. package/lib/esm/view/financeStatement/financeStatementReducer.js +11 -8
  17. package/lib/esm/view/financeStatement/financeStatementSelector.js +13 -4
  18. package/lib/esm/view/profitAndLossClassesView/profitAndLossClassesByClassHorizontalSelector.js +281 -0
  19. package/lib/esm/view/profitAndLossClassesView/profitAndLossClassesByClassHorizontalSelectorTypes.js +1 -0
  20. package/lib/esm/view/profitAndLossClassesView/profitAndLossClassesViewReducer.js +5 -1
  21. package/lib/esm/view/spendManagement/billPay/billList/billListSelector.js +0 -10
  22. package/lib/esm/view/spendManagement/billPay/billList/billListState.js +0 -1
  23. package/lib/esm/view/spendManagement/chargeCards/chargeCardList/chargeCardList.js +0 -1
  24. package/lib/esm/view/spendManagement/chargeCards/chargeCardList/chargeCardListSelector.js +0 -2
  25. package/lib/esm/view/spendManagement/reimbursement/remiListView/remiListSelector.js +0 -12
  26. package/lib/esm/view/spendManagement/reimbursement/remiListView/remiListState.js +0 -1
  27. package/lib/esm/view/taskManager/taskDetailView/taskDetailSelector.js +2 -2
  28. package/lib/index.d.ts +8 -8
  29. package/lib/index.js +28 -31
  30. package/lib/tsconfig.typecheck.tsbuildinfo +1 -1
  31. package/lib/view/aiCfoView/aiCfoViewPayload.d.ts +0 -7
  32. package/lib/view/aiCfoView/aiCfoViewReducer.d.ts +1 -15
  33. package/lib/view/aiCfoView/aiCfoViewReducer.js +2 -83
  34. package/lib/view/aiCfoView/aiCfoViewSelector.d.ts +1 -4
  35. package/lib/view/aiCfoView/aiCfoViewSelector.js +2 -9
  36. package/lib/view/aiCfoView/aiCfoViewState.d.ts +1 -18
  37. package/lib/view/auditReportView/auditReportViewState.d.ts +1 -1
  38. package/lib/view/financeStatement/financeStatementReducer.js +11 -8
  39. package/lib/view/financeStatement/financeStatementSelector.d.ts +2 -0
  40. package/lib/view/financeStatement/financeStatementSelector.js +13 -4
  41. package/lib/view/profitAndLossClassesView/profitAndLossClassesByClassHorizontalSelector.d.ts +13 -0
  42. package/lib/view/profitAndLossClassesView/profitAndLossClassesByClassHorizontalSelector.js +285 -0
  43. package/lib/view/profitAndLossClassesView/profitAndLossClassesByClassHorizontalSelectorTypes.d.ts +57 -0
  44. package/lib/view/profitAndLossClassesView/profitAndLossClassesByClassHorizontalSelectorTypes.js +2 -0
  45. package/lib/view/profitAndLossClassesView/profitAndLossClassesViewReducer.d.ts +4 -2
  46. package/lib/view/profitAndLossClassesView/profitAndLossClassesViewReducer.js +6 -2
  47. package/lib/view/profitAndLossClassesView/profitAndLossClassesViewState.d.ts +2 -0
  48. package/lib/view/spendManagement/billPay/billList/billListSelector.js +0 -10
  49. package/lib/view/spendManagement/billPay/billList/billListState.d.ts +1 -1
  50. package/lib/view/spendManagement/billPay/billList/billListState.js +0 -1
  51. package/lib/view/spendManagement/chargeCards/chargeCardList/chargeCardList.d.ts +1 -1
  52. package/lib/view/spendManagement/chargeCards/chargeCardList/chargeCardList.js +0 -1
  53. package/lib/view/spendManagement/chargeCards/chargeCardList/chargeCardListSelector.js +0 -2
  54. package/lib/view/spendManagement/reimbursement/remiListView/remiListSelector.d.ts +0 -1
  55. package/lib/view/spendManagement/reimbursement/remiListView/remiListSelector.js +0 -12
  56. package/lib/view/spendManagement/reimbursement/remiListView/remiListState.d.ts +1 -1
  57. package/lib/view/spendManagement/reimbursement/remiListView/remiListState.js +0 -1
  58. package/lib/view/taskManager/taskDetailView/taskDetailSelector.js +2 -2
  59. package/package.json +1 -1
  60. package/lib/esm/view/aiCfoView/epics/fetchSuggestedQuestionsEpic.js +0 -29
  61. package/lib/view/aiCfoView/epics/fetchSuggestedQuestionsEpic.d.ts +0 -8
  62. package/lib/view/aiCfoView/epics/fetchSuggestedQuestionsEpic.js +0 -33
@@ -1,19 +1,4 @@
1
1
  import { createSlice } from '@reduxjs/toolkit';
2
- function removeSessionIdFromPageKeyMap(draft, sessionId) {
3
- const map = draft.uiState.chatSessionIdByPageKey;
4
- for (const key of Object.keys(map)) {
5
- if (map[key] === sessionId) {
6
- delete map[key];
7
- }
8
- }
9
- }
10
- function mirrorCurrentSessionToPageKeyMapIfActive(draft) {
11
- const key = draft.uiState.aiCfoSidePanelHostPageKey;
12
- const sessionId = draft.uiState.currentChatSessionId;
13
- if (key != null && sessionId != null) {
14
- draft.uiState.chatSessionIdByPageKey[key] = sessionId;
15
- }
16
- }
17
2
  export const initialAiCfoViewState = {
18
3
  fetchState: 'Not-Started',
19
4
  error: undefined,
@@ -21,8 +6,6 @@ export const initialAiCfoViewState = {
21
6
  currentInput: '',
22
7
  scrollPositionByChatSessionId: {},
23
8
  currentChatSessionId: undefined,
24
- aiCfoSidePanelHostPageKey: undefined,
25
- chatSessionIdByPageKey: {},
26
9
  agentId: undefined,
27
10
  chatHistoryNextPageToken: {},
28
11
  chatSessionsNextPageToken: { nextPageToken: null, hasMore: false },
@@ -46,7 +29,6 @@ export const initialAiCfoViewState = {
46
29
  fetchChatSessionHistoryByChatSessionId: {},
47
30
  responseStateByChatSessionId: {},
48
31
  stopSubmitStateByChatSessionId: {},
49
- suggestedQuestionsByPageContext: {},
50
32
  };
51
33
  const aiCfoView = createSlice({
52
34
  name: 'aiCfoView',
@@ -180,7 +162,6 @@ const aiCfoView = createSlice({
180
162
  setSession(draft, action) {
181
163
  draft.uiState.currentChatSessionId = action.payload.sessionId;
182
164
  draft.uiState.agentId = action.payload.agentId;
183
- mirrorCurrentSessionToPageKeyMapIfActive(draft);
184
165
  },
185
166
  createSession: {
186
167
  reducer(draft, action) {
@@ -204,7 +185,6 @@ const aiCfoView = createSlice({
204
185
  nextPageToken: null,
205
186
  hasMore: false,
206
187
  };
207
- mirrorCurrentSessionToPageKeyMapIfActive(draft);
208
188
  },
209
189
  createSessionFailure(draft, action) {
210
190
  draft.createSessionState.fetchState = 'Error';
@@ -242,7 +222,6 @@ const aiCfoView = createSlice({
242
222
  nextPageToken: null,
243
223
  hasMore: false,
244
224
  };
245
- mirrorCurrentSessionToPageKeyMapIfActive(draft);
246
225
  },
247
226
  createSessionAndSubmitSuccess(draft) {
248
227
  // Note: currentChatSessionId, agentId, and chatHistoryNextPageToken are set by initializeNewSessionState
@@ -359,7 +338,6 @@ const aiCfoView = createSlice({
359
338
  draft.uiState.currentChatSessionId = undefined;
360
339
  draft.uiState.agentId = undefined;
361
340
  }
362
- removeSessionIdFromPageKeyMap(draft, chatSessionId);
363
341
  delete draft.uiState.chatHistoryNextPageToken[chatSessionId];
364
342
  delete draft.uiState.scrollPositionByChatSessionId[chatSessionId];
365
343
  delete draft.fetchChatSessionHistoryByChatSessionId[chatSessionId];
@@ -382,65 +360,6 @@ const aiCfoView = createSlice({
382
360
  },
383
361
  clearCurrentSessionId(draft) {
384
362
  draft.uiState.currentChatSessionId = undefined;
385
- const key = draft.uiState.aiCfoSidePanelHostPageKey;
386
- if (key != null) {
387
- delete draft.uiState.chatSessionIdByPageKey[key];
388
- }
389
- },
390
- applyAiCfoSidePanelHostPageTransition(draft, action) {
391
- const { newAiCfoSidePanelHostPageKey, nextCurrentChatSessionId } = action.payload;
392
- const prevKey = draft.uiState.aiCfoSidePanelHostPageKey;
393
- const curId = draft.uiState.currentChatSessionId;
394
- if (prevKey != null &&
395
- prevKey !== newAiCfoSidePanelHostPageKey &&
396
- curId != null) {
397
- draft.uiState.chatSessionIdByPageKey[prevKey] = curId;
398
- }
399
- draft.uiState.aiCfoSidePanelHostPageKey = newAiCfoSidePanelHostPageKey;
400
- draft.uiState.currentChatSessionId =
401
- nextCurrentChatSessionId != null ? nextCurrentChatSessionId : undefined;
402
- draft.uiState.currentInput = '';
403
- mirrorCurrentSessionToPageKeyMapIfActive(draft);
404
- },
405
- clearAiCfoSidePanelHostPageContext(draft) {
406
- const key = draft.uiState.aiCfoSidePanelHostPageKey;
407
- const curId = draft.uiState.currentChatSessionId;
408
- if (key != null && curId != null) {
409
- draft.uiState.chatSessionIdByPageKey[key] = curId;
410
- }
411
- draft.uiState.aiCfoSidePanelHostPageKey = undefined;
412
- },
413
- fetchSuggestedQuestions(draft, action) {
414
- const { pageContext } = action.payload;
415
- const prev = draft.suggestedQuestionsByPageContext[pageContext];
416
- draft.suggestedQuestionsByPageContext[pageContext] = {
417
- fetchState: 'In-Progress',
418
- pageContext,
419
- suggestedQuestions: prev?.suggestedQuestions ?? [],
420
- generatedAt: prev?.generatedAt,
421
- error: undefined,
422
- };
423
- },
424
- fetchSuggestedQuestionsSuccess(draft, action) {
425
- const { pageContext, generatedAt, suggestedQuestions } = action.payload;
426
- draft.suggestedQuestionsByPageContext[pageContext] = {
427
- fetchState: 'Completed',
428
- pageContext,
429
- generatedAt,
430
- suggestedQuestions,
431
- error: undefined,
432
- };
433
- },
434
- fetchSuggestedQuestionsFailure(draft, action) {
435
- const { pageContext, error } = action.payload;
436
- const prev = draft.suggestedQuestionsByPageContext[pageContext];
437
- draft.suggestedQuestionsByPageContext[pageContext] = {
438
- fetchState: 'Error',
439
- pageContext,
440
- suggestedQuestions: prev?.suggestedQuestions ?? [],
441
- generatedAt: prev?.generatedAt,
442
- error,
443
- };
444
363
  },
445
364
  clearAiCfoView(draft) {
446
365
  Object.assign(draft, initialAiCfoViewState);
@@ -452,5 +371,5 @@ const aiCfoView = createSlice({
452
371
  },
453
372
  },
454
373
  });
455
- export const { submitQuestion, submitQuestionSuccess, submitQuestionFailure, updateResponseState, updateAiCfoViewScrollPosition, updateCurrentInput, clearInput, setSession, createSession, createSessionSuccess, createSessionFailure, createSessionAndSubmit, initializeNewSessionState, createSessionAndSubmitSuccess, createSessionAndSubmitFailure, fetchChatSessionsForUser, fetchChatSessionsForUserSuccess, fetchChatSessionsForUserFailure, fetchChatHistory, fetchChatHistorySuccess, fetchChatHistoryFailure, clearCurrentSessionId, clearLastContextMessage, stopSubmit, stopSubmitSuccess, stopSubmitFailure, stopSubmitQuestion, stopSubmitQuestionSuccess, stopSubmitQuestionFailure, acceptMasterTOS, acceptMasterTOSSuccess, acceptMasterTOSFailure, deleteChatSession, deleteChatSessionSuccess, deleteChatSessionFailure, clearDeleteChatSessionStatus, clearAiCfoView, updateCotCollapsedState, fetchSuggestedQuestions, fetchSuggestedQuestionsSuccess, fetchSuggestedQuestionsFailure, applyAiCfoSidePanelHostPageTransition, clearAiCfoSidePanelHostPageContext, } = aiCfoView.actions;
374
+ export const { submitQuestion, submitQuestionSuccess, submitQuestionFailure, updateResponseState, updateAiCfoViewScrollPosition, updateCurrentInput, clearInput, setSession, createSession, createSessionSuccess, createSessionFailure, createSessionAndSubmit, initializeNewSessionState, createSessionAndSubmitSuccess, createSessionAndSubmitFailure, fetchChatSessionsForUser, fetchChatSessionsForUserSuccess, fetchChatSessionsForUserFailure, fetchChatHistory, fetchChatHistorySuccess, fetchChatHistoryFailure, clearCurrentSessionId, clearLastContextMessage, stopSubmit, stopSubmitSuccess, stopSubmitFailure, stopSubmitQuestion, stopSubmitQuestionSuccess, stopSubmitQuestionFailure, acceptMasterTOS, acceptMasterTOSSuccess, acceptMasterTOSFailure, deleteChatSession, deleteChatSessionSuccess, deleteChatSessionFailure, clearDeleteChatSessionStatus, clearAiCfoView, updateCotCollapsedState, } = aiCfoView.actions;
456
375
  export default aiCfoView.reducer;
@@ -1,8 +1,6 @@
1
- import { initialAiCfoState } from '../../entity/aiCfo/aiCfoReducer';
2
1
  import { getAiCfoSelectorView, } from '../../entity/aiCfo/aiCfoSelector';
3
- import { initialAiCfoViewState } from './aiCfoViewReducer';
4
2
  export const getAiCfoView = (state) => {
5
- const { fetchState, error, uiState, createSessionState, createSessionAndSubmitState, deleteChatSessionStateByChatSessionId, stopSubmitStateByChatSessionId, responseStateByChatSessionId, fetchChatSessionHistoryByChatSessionId, masterTOSAcceptanceState, suggestedQuestionsByPageContext, } = state.aiCfoViewState ?? initialAiCfoViewState;
3
+ const { fetchState, error, uiState, createSessionState, createSessionAndSubmitState, deleteChatSessionStateByChatSessionId, stopSubmitStateByChatSessionId, responseStateByChatSessionId, fetchChatSessionHistoryByChatSessionId, masterTOSAcceptanceState, } = state.aiCfoViewState;
6
4
  return {
7
5
  fetchState,
8
6
  error,
@@ -14,11 +12,7 @@ export const getAiCfoView = (state) => {
14
12
  uiState,
15
13
  fetchChatSessionHistoryByChatSessionId,
16
14
  masterTOSAcceptanceState,
17
- suggestedQuestionsByPageContext,
18
- ...getAiCfoSelectorView(state.aiCfoState ?? initialAiCfoState),
15
+ ...getAiCfoSelectorView(state.aiCfoState),
19
16
  version: 1,
20
17
  };
21
18
  };
22
- export function getSuggestedQuestionsForPageContext(state, pageContext) {
23
- return state.aiCfoViewState?.suggestedQuestionsByPageContext?.[pageContext];
24
- }
@@ -44,19 +44,20 @@ const financeStatement = createSlice({
44
44
  updateFinanceStatementThisPeriod(draft, action) {
45
45
  const { firstMonthOfFY, coaBalances, maxNumOfPeriodsToHighlight } = action.payload;
46
46
  const { timeframe, selectedCOABalancesRange } = draft;
47
- if (coaBalances.length > 0) {
48
- const prevThisPeriod = extractThisPeriod(action.payload.firstMonthOfFY, timeframe, selectedCOABalancesRange, coaBalances);
47
+ const safeCoaBalances = coaBalances ?? [];
48
+ if (safeCoaBalances.length > 0) {
49
+ const prevThisPeriod = extractThisPeriod(action.payload.firstMonthOfFY, timeframe, selectedCOABalancesRange, safeCoaBalances);
49
50
  if (prevThisPeriod != null) {
50
51
  const selectedCoaBalancesRangeWithThisPeriod = {
51
52
  ...selectedCOABalancesRange,
52
53
  thisPeriod: prevThisPeriod,
53
54
  };
54
- const newThisPeriod = getMatchingThisPeriod(action.payload.firstMonthOfFY, timeframe, action.payload.thisPeriod, coaBalances);
55
+ const newThisPeriod = getMatchingThisPeriod(action.payload.firstMonthOfFY, timeframe, action.payload.thisPeriod, safeCoaBalances);
55
56
  if (newThisPeriod != null) {
56
57
  const selectionRanges = getSelectedAndHighlightedRangesForThisPeriod({
57
58
  firstMonthOfFY,
58
59
  thisPeriod: newThisPeriod,
59
- coaBalances,
60
+ coaBalances: safeCoaBalances,
60
61
  timeframe,
61
62
  maxNumOfPeriodsToSelect: maxNumOfPeriodsToHighlight,
62
63
  currentSelection: {
@@ -75,7 +76,9 @@ const financeStatement = createSlice({
75
76
  draft.selectedReportId = action.payload;
76
77
  },
77
78
  updateFinanceStatementAdditionalBalancesSelection(draft, action) {
78
- const { firstMonthOfFY, additionalBalances, coaBalances, maxNumOfPeriodsToHighlight, } = action.payload;
79
+ const { firstMonthOfFY, additionalBalances: additionalBalancesPayload, coaBalances, maxNumOfPeriodsToHighlight, } = action.payload;
80
+ const safeCoaBalances = coaBalances ?? [];
81
+ const additionalBalances = additionalBalancesPayload ?? [];
79
82
  let tempAdditionalBalances = [...additionalBalances];
80
83
  if (additionalBalances.includes('this_period_vs_last_period') ||
81
84
  additionalBalances.includes('this_period_vs_last_period_percent')) {
@@ -91,8 +94,8 @@ const financeStatement = createSlice({
91
94
  draft.isAdditionalBalancesShown =
92
95
  additionalBalances.length != 0 ? true : false;
93
96
  const { timeframe, selectedCOABalancesRange } = draft;
94
- if (coaBalances.length > 0) {
95
- const thisPeriod = extractThisPeriod(action.payload.firstMonthOfFY, timeframe, selectedCOABalancesRange, coaBalances);
97
+ if (safeCoaBalances.length > 0) {
98
+ const thisPeriod = extractThisPeriod(action.payload.firstMonthOfFY, timeframe, selectedCOABalancesRange, safeCoaBalances);
96
99
  if (thisPeriod != null) {
97
100
  const selectedCoaBalancesRangeWithThisPeriod = {
98
101
  ...selectedCOABalancesRange,
@@ -101,7 +104,7 @@ const financeStatement = createSlice({
101
104
  const selectionRanges = getSelectedAndHighlightedRangesForThisPeriod({
102
105
  firstMonthOfFY,
103
106
  thisPeriod: thisPeriod,
104
- coaBalances,
107
+ coaBalances: safeCoaBalances,
105
108
  timeframe,
106
109
  maxNumOfPeriodsToHighlight: maxNumOfPeriodsToHighlight,
107
110
  currentSelection: {
@@ -8,6 +8,7 @@ import { extractThisPeriod } from '../../commonStateTypes/viewAndReport/thisPeri
8
8
  import { getBalanceSheet } from '../balanceSheet/balanceSheetSelector';
9
9
  import { getCashFlow } from '../cashFlow/cashFlowSelector';
10
10
  import { getProfitAndLossReport, } from '../profitAndLoss/profitAndLossSelector';
11
+ import { getProfitAndLossClassesHorizontalView } from '../profitAndLossClassesView/profitAndLossClassesByClassHorizontalSelector';
11
12
  import { getProfitAndLossClassesView } from '../profitAndLossClassesView/profitAndLossClassesViewSelector';
12
13
  function getDateRangeForFinanceReports(profitAndLossState, balanceSheetState, cashFlowState) {
13
14
  const datesSorted = [
@@ -42,11 +43,15 @@ export const getFinanceStatement = createSelector((state) => state.accountState,
42
43
  if (dataAvailable != null) {
43
44
  const noFilter = newBalancesFilterForDateRange(firstMonthOfFY, financeStatementState.timeframe, dataAvailable, 'ascending_date', []);
44
45
  const profitAndLossForTimeframeTicks = getProfitAndLossReport(noFilter, undefined, accountState, accountGroupState, sectionAccountsViewState, profitAndLossState, forecastState);
45
- if (profitAndLossForTimeframeTicks.sections.expenses != null) {
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) {
46
51
  balancesInTimeframe =
47
- profitAndLossForTimeframeTicks.sections.expenses.balances.length > 0
48
- ? profitAndLossForTimeframeTicks.sections['expenses'].balances
49
- : profitAndLossForTimeframeTicks.sections['income'].balances;
52
+ (expensesBalances?.length ?? 0) > 0
53
+ ? expensesBalances ?? []
54
+ : incomeBalances ?? [];
50
55
  allTimeframeTicks = balancesInTimeframe.map((balance) => toTimeframeTick(balance));
51
56
  }
52
57
  }
@@ -60,6 +65,9 @@ export const getFinanceStatement = createSelector((state) => state.accountState,
60
65
  const profitAndLossReport = getProfitAndLossReport(filter, undefined, accountState, accountGroupState, sectionAccountsViewState, profitAndLossState, forecastState);
61
66
  const classFilter = newBalancesFilterClassesView(filter.firstMonthOfFY, filter.timeframe, filter.balancesRange.numberOfPeriods, filter.balancesRange.thisPeriod, filter.balancesRange.orderBy, filter.additionalBalances, profitAndLossClassesViewState.uiState.classesToFilterOut);
62
67
  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);
63
71
  const balanceSheetReport = getBalanceSheet(filter, accountState, accountGroupState, sectionAccountsViewState, balanceSheetState);
64
72
  const cashFlowReport = getCashFlow(filter, accountState, accountGroupState, sectionAccountsViewState, cashFlowState);
65
73
  const allReports = [
@@ -107,6 +115,7 @@ export const getFinanceStatement = createSelector((state) => state.accountState,
107
115
  dataAvailable,
108
116
  profitAndLossReport,
109
117
  profitAndLossByClassReport,
118
+ profitAndLossByClassHorizontalReport,
110
119
  balanceSheetReport,
111
120
  cashFlowReport,
112
121
  filter,
@@ -0,0 +1,281 @@
1
+ import { toAmountWC } from '../../commonStateTypes/amount';
2
+ import { getSectionClassesViewReport } from '../../entity/sectionClassesViewV2/sectionClassesViewSelector';
3
+ import { ALL_PANDL_REPORT_VIEW_SECTION_IDS, } from './profitAndLossClassesViewSelectorTypes';
4
+ const DEFAULT_CURRENCY = { currencyCode: 'USD', currencySymbol: '$' };
5
+ export const profitAndLossHorizontalIdsInOrder = [
6
+ 'income',
7
+ 'cogs',
8
+ 'grossProfit',
9
+ 'expenses',
10
+ 'earningsBeforeInterestAndTax',
11
+ 'otherIncome',
12
+ 'otherExpenses',
13
+ 'netOtherIncome',
14
+ 'netIncome',
15
+ ];
16
+ const SECTION_TITLES = {
17
+ income: 'Income',
18
+ cogs: 'Cost of Goods Sold',
19
+ grossProfit: 'Gross Profit',
20
+ expenses: 'Expenses',
21
+ earningsBeforeInterestAndTax: 'Net Operating Income',
22
+ otherIncome: 'Other Income',
23
+ otherExpenses: 'Other Expenses',
24
+ netOtherIncome: 'Net Other Income',
25
+ netIncome: 'Net Income',
26
+ };
27
+ export const getProfitAndLossClassesHorizontalView = (filter, accountState, classState, sectionsState, profitAndLossClassesViewState, selectedPeriod) => {
28
+ const fetchState = reduceFetchState([
29
+ profitAndLossClassesViewState.fetchState.month,
30
+ profitAndLossClassesViewState.fetchState.quarter,
31
+ profitAndLossClassesViewState.fetchState.year,
32
+ ]);
33
+ const reportId = 'profit_and_loss_by_classes';
34
+ // Build class columns from the class hierarchy (top-level only)
35
+ const classColumns = buildClassColumns(profitAndLossClassesViewState.classHierarchy, profitAndLossClassesViewState.uiState.classesToFilterOut);
36
+ const sections = {};
37
+ const calculatedSections = {};
38
+ if (fetchState.fetchState === 'Completed' && selectedPeriod != null) {
39
+ // Get section reports using existing selector infrastructure
40
+ const sectionReports = {};
41
+ ALL_PANDL_REPORT_VIEW_SECTION_IDS.forEach((sectionId) => {
42
+ sectionReports[sectionId] = getSectionClassesViewReport(accountState, classState, sectionsState, { reportId, sectionId }, filter);
43
+ });
44
+ // 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) => {
53
+ const sectionReport = sectionReports[sectionId];
54
+ if (sectionReport != null) {
55
+ sections[sectionId] = pivotSectionToHorizontal(sectionReport, classColumns, SECTION_TITLES[sectionId] ?? sectionId);
56
+ }
57
+ });
58
+ // Calculate derived sections
59
+ const incomeSection = sections['income'];
60
+ const cogsSection = sections['cogs'];
61
+ const expensesSection = sections['expenses'];
62
+ const otherIncomeSection = sections['otherIncome'];
63
+ const otherExpensesSection = sections['otherExpenses'];
64
+ // Gross Profit = Income - COGS
65
+ if (incomeSection != null && cogsSection != null) {
66
+ calculatedSections['grossProfit'] = computeCalculatedSection(incomeSection.classTotals, cogsSection.classTotals, incomeSection.sectionTotal, cogsSection.sectionTotal, 'subtract', SECTION_TITLES['grossProfit']);
67
+ }
68
+ // EBIT = Gross Profit - Expenses
69
+ const grossProfit = calculatedSections['grossProfit'];
70
+ if (grossProfit != null && expensesSection != null) {
71
+ calculatedSections['earningsBeforeInterestAndTax'] =
72
+ computeCalculatedSection(grossProfit.classTotals, expensesSection.classTotals, grossProfit.sectionTotal, expensesSection.sectionTotal, 'subtract', SECTION_TITLES['earningsBeforeInterestAndTax']);
73
+ }
74
+ // Net Other Income = Other Income - Other Expenses
75
+ if (otherIncomeSection != null && otherExpensesSection != null) {
76
+ calculatedSections['netOtherIncome'] = computeCalculatedSection(otherIncomeSection.classTotals, otherExpensesSection.classTotals, otherIncomeSection.sectionTotal, otherExpensesSection.sectionTotal, 'subtract', SECTION_TITLES['netOtherIncome']);
77
+ }
78
+ // Net Income = EBIT + Net Other Income
79
+ const ebit = calculatedSections['earningsBeforeInterestAndTax'];
80
+ const netOtherIncome = calculatedSections['netOtherIncome'];
81
+ if (ebit != null && netOtherIncome != null) {
82
+ calculatedSections['netIncome'] = computeCalculatedSection(ebit.classTotals, netOtherIncome.classTotals, ebit.sectionTotal, netOtherIncome.sectionTotal, 'add', SECTION_TITLES['netIncome']);
83
+ }
84
+ }
85
+ // Build synthetic header COABalances for rendering class names as column headers
86
+ const headerBalances = buildHeaderBalances(classColumns, filter.timeframe, selectedPeriod);
87
+ return {
88
+ reportId,
89
+ reportTitle: 'Profit and Loss by Class',
90
+ firstMonthOfFY: profitAndLossClassesViewState.firstMonthOfFY,
91
+ fetchState: fetchState.fetchState,
92
+ bookCloseDate: profitAndLossClassesViewState.bookCloseDate,
93
+ dataAvailable: profitAndLossClassesViewState.dataAvailable,
94
+ selectedPeriod,
95
+ classColumns,
96
+ sections,
97
+ calculatedSections,
98
+ idsInOrder: profitAndLossHorizontalIdsInOrder,
99
+ uiState: profitAndLossClassesViewState.uiState,
100
+ headerBalances,
101
+ };
102
+ };
103
+ /**
104
+ * Build column definitions from class hierarchy (top-level classes only).
105
+ */
106
+ function buildClassColumns(classHierarchy, classesToFilterOut) {
107
+ return classHierarchy
108
+ .filter((c) => !classesToFilterOut.some((filteredId) => filteredId[0] === c.classId[0]))
109
+ .map((c) => ({
110
+ classId: c.classId[0],
111
+ className: c.className,
112
+ nestedClassId: c.classId,
113
+ qboId: c.qboId,
114
+ }));
115
+ }
116
+ /**
117
+ * Pivot a section from class→account→balances to account→class→balance (single period).
118
+ */
119
+ function pivotSectionToHorizontal(section, classColumns, title) {
120
+ // Map: accountId -> { accountName, classId -> balance }
121
+ const accountMap = new Map();
122
+ // Also track class totals
123
+ const classTotalMap = new Map();
124
+ classColumns.forEach((col) => classTotalMap.set(col.classId, 0));
125
+ // Iterate over each class in the section
126
+ section.classes.forEach((classReport) => {
127
+ const classId = classReport.id[0];
128
+ // Only process classes that are in our columns
129
+ if (!classColumns.some((col) => col.classId === classId)) {
130
+ return;
131
+ }
132
+ // Sum all sliced period balances (same slice as vertical columns; multi-period = range total)
133
+ const classBalance = sumBalancesInSlice(classReport.childrenBalancesTotal.balances);
134
+ classTotalMap.set(classId, classBalance);
135
+ // Iterate through accounts in this class
136
+ collectAccountsFromClass(classReport, classId, accountMap);
137
+ });
138
+ // Build account rows
139
+ const accounts = [];
140
+ accountMap.forEach((accountData, accountId) => {
141
+ const classBalances = classColumns.map((col) => ({
142
+ classId: col.classId,
143
+ balance: toAmountWC(accountData.classBalances.get(col.classId) ?? 0, DEFAULT_CURRENCY),
144
+ }));
145
+ const rowTotal = classBalances.reduce((sum, cb) => sum + cb.balance.amount, 0);
146
+ accounts.push({
147
+ accountId,
148
+ accountName: accountData.accountName,
149
+ classBalances,
150
+ rowTotal: toAmountWC(rowTotal, DEFAULT_CURRENCY),
151
+ });
152
+ });
153
+ // Build class totals
154
+ const classTotals = classColumns.map((col) => ({
155
+ classId: col.classId,
156
+ balance: toAmountWC(classTotalMap.get(col.classId) ?? 0, DEFAULT_CURRENCY),
157
+ }));
158
+ const sectionTotal = classTotals.reduce((sum, ct) => sum + ct.balance.amount, 0);
159
+ return {
160
+ title,
161
+ accounts,
162
+ classTotals,
163
+ sectionTotal: toAmountWC(sectionTotal, DEFAULT_CURRENCY),
164
+ };
165
+ }
166
+ /**
167
+ * Recursively collect account balances from a NestedClassReportV2 into accountMap.
168
+ */
169
+ function collectAccountsFromClass(classReport, classId, accountMap) {
170
+ // Process accounts directly on this class
171
+ classReport.class.accounts.forEach((nestedAccount) => {
172
+ collectAccountsFromNestedAccount(nestedAccount, classId, accountMap);
173
+ });
174
+ }
175
+ /**
176
+ * Recursively collect accounts from NestedAccountReport (handles sub-accounts).
177
+ */
178
+ function collectAccountsFromNestedAccount(nestedAccount, classId, accountMap) {
179
+ const accountId = nestedAccount.account.accountId;
180
+ const accountName = nestedAccount.account.accountName;
181
+ const balance = sumBalancesInSlice(nestedAccount.account.balancesInTimeframe.balances);
182
+ if (!accountMap.has(accountId)) {
183
+ accountMap.set(accountId, {
184
+ accountName,
185
+ classBalances: new Map(),
186
+ });
187
+ }
188
+ const accountData = accountMap.get(accountId);
189
+ if (accountData == null) {
190
+ return;
191
+ }
192
+ const existing = accountData.classBalances.get(classId) ?? 0;
193
+ accountData.classBalances.set(classId, existing + balance);
194
+ // Process sub-accounts
195
+ nestedAccount.children.forEach((child) => {
196
+ collectAccountsFromNestedAccount(child, classId, accountMap);
197
+ });
198
+ }
199
+ /**
200
+ * Total amount for the current report slice. `balances` is already narrowed by
201
+ * `getCOABalances` / `sliceCOABalances` to the selected range; summing matches
202
+ * multi-period selections and avoids mismatches when `thisPeriod` does not
203
+ * align with a row (e.g. slice fallback) or when several period rows are shown.
204
+ */
205
+ function sumBalancesInSlice(balances) {
206
+ let sum = 0;
207
+ for (const bal of balances) {
208
+ const b = bal;
209
+ sum += b.balance?.amount ?? 0;
210
+ }
211
+ return sum;
212
+ }
213
+ /**
214
+ * Compute a calculated section by adding or subtracting two sets of class totals.
215
+ */
216
+ function computeCalculatedSection(aTotals, bTotals, aTotal, bTotal, operation, title) {
217
+ const classTotals = aTotals.map((a) => {
218
+ const bMatch = bTotals.find((b) => b.classId === a.classId);
219
+ const bAmount = bMatch?.balance.amount ?? 0;
220
+ const resultAmount = operation === 'add'
221
+ ? a.balance.amount + bAmount
222
+ : a.balance.amount - bAmount;
223
+ return {
224
+ classId: a.classId,
225
+ balance: toAmountWC(resultAmount, DEFAULT_CURRENCY),
226
+ };
227
+ });
228
+ const sectionTotalAmount = operation === 'add'
229
+ ? aTotal.amount + bTotal.amount
230
+ : aTotal.amount - bTotal.amount;
231
+ return {
232
+ title,
233
+ classTotals,
234
+ sectionTotal: toAmountWC(sectionTotalAmount, DEFAULT_CURRENCY),
235
+ };
236
+ }
237
+ /**
238
+ * Build synthetic COABalance array for header rendering.
239
+ * One entry per class column + one for "Total".
240
+ */
241
+ function buildHeaderBalances(classColumns, timeframe, selectedPeriod) {
242
+ if (selectedPeriod == null) {
243
+ return [];
244
+ }
245
+ const { endDate, startDate } = selectedPeriod;
246
+ const balances = classColumns.map((col) => ({
247
+ balance: toAmountWC(0, DEFAULT_CURRENCY),
248
+ balanceTimeFrame: timeframe,
249
+ endDate,
250
+ startDate,
251
+ type: 'default',
252
+ label: col.className,
253
+ }));
254
+ // Add a "Total" column
255
+ balances.push({
256
+ balance: toAmountWC(0, DEFAULT_CURRENCY),
257
+ balanceTimeFrame: timeframe,
258
+ endDate,
259
+ startDate,
260
+ type: 'default',
261
+ label: 'Total',
262
+ });
263
+ return balances;
264
+ }
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
+ }
@@ -23,6 +23,7 @@ export const initialState = {
23
23
  nodeCollapseState: {},
24
24
  scrollPosition: undefined,
25
25
  accountViewMode: 'account_group',
26
+ classViewLayout: 'vertical',
26
27
  classesToFilterOut: [],
27
28
  },
28
29
  firstMonthOfFY: 1,
@@ -94,6 +95,9 @@ const profitAndLossClassesView = createSlice({
94
95
  updateAccountViewMode(draft, action) {
95
96
  draft.uiState.accountViewMode = action.payload.viewMode;
96
97
  },
98
+ updateClassViewLayout(draft, action) {
99
+ draft.uiState.classViewLayout = action.payload.classViewLayout;
100
+ },
97
101
  resetProfitAndLossClassesNodeCollapseState(draft) {
98
102
  draft.uiState.nodeCollapseState = {};
99
103
  },
@@ -102,7 +106,7 @@ const profitAndLossClassesView = createSlice({
102
106
  },
103
107
  },
104
108
  });
105
- export const { fetchProfitAndLossClassesView, fetchProfitAndLossForTimeframeClassesView, updateProfitAndLossForTimeframeClassesView, updateProfitAndLossForTimeframeClassesViewFailure, updateClassesToFilterOut, updateProfitAndLossClassesViewUIState, updateAccountViewMode, resetProfitAndLossClassesNodeCollapseState, clearProfitAndLossClassesView, } = profitAndLossClassesView.actions;
109
+ export const { fetchProfitAndLossClassesView, fetchProfitAndLossForTimeframeClassesView, updateProfitAndLossForTimeframeClassesView, updateProfitAndLossForTimeframeClassesViewFailure, updateClassesToFilterOut, updateProfitAndLossClassesViewUIState, updateAccountViewMode, updateClassViewLayout, resetProfitAndLossClassesNodeCollapseState, clearProfitAndLossClassesView, } = profitAndLossClassesView.actions;
106
110
  export default profitAndLossClassesView.reducer;
107
111
  const doUpdatedProfitAndLossClassesView = (draft, timeframe, pandLReport) => {
108
112
  const report = mapReportPayloadV2ToReportV2(pandLReport.report);
@@ -416,14 +416,6 @@ export const getBillListOnSort = (sortKey, sortOrder, currentTab, transactions)
416
416
  transactionsWithNoSortKeyValue.push(transaction);
417
417
  }
418
418
  break;
419
- case 'invoiceNumber':
420
- if (transaction.documentId != null) {
421
- transactionsWithSortKeyValue.push(transaction);
422
- }
423
- else {
424
- transactionsWithNoSortKeyValue.push(transaction);
425
- }
426
- break;
427
419
  case 'dueDate':
428
420
  if (currentTab === 'draft') {
429
421
  if (Boolean(transaction.dueDate) === true) {
@@ -526,8 +518,6 @@ export const getBillListOnSort = (sortKey, sortOrder, currentTab, transactions)
526
518
  return transaction.totalAmountInUSD?.amount ?? 0;
527
519
  case 'paymentMethod':
528
520
  return transaction.billPayInfo.billPaymentMethodType.name;
529
- case 'invoiceNumber':
530
- return transaction.documentId;
531
521
  }
532
522
  }, getColumnWiseSortOrder(sortOrder));
533
523
  return [
@@ -43,7 +43,6 @@ const ALL_BILL_PAY_SORT_KEYS = [
43
43
  'invoiceDate',
44
44
  'name',
45
45
  'paymentMethod',
46
- 'invoiceNumber',
47
46
  ];
48
47
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
49
48
  const toBillPaySortKeyType = (v) => stringToUnion(v, ALL_BILL_PAY_SORT_KEYS);
@@ -75,7 +75,6 @@ const ALL_CHARGE_CARD_SORT_KEYS = [
75
75
  'cardName',
76
76
  'department',
77
77
  'cardType',
78
- 'accountType',
79
78
  'status',
80
79
  'utilisation',
81
80
  'limit',
@@ -219,8 +219,6 @@ const getCardsBySortKey = (card, sortKey) => {
219
219
  return card.cardName.toLowerCase();
220
220
  case 'cardType':
221
221
  return card.type.code;
222
- case 'accountType':
223
- return card.type.code.includes('credit') ? 'credit' : 'debit';
224
222
  case 'status':
225
223
  return card.status.code;
226
224
  case 'utilisation':