@zeniai/client-epic-state 5.0.47 → 5.0.48-betaRD1

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 (32) hide show
  1. package/lib/entity/project/projectPayload.d.ts +3 -3
  2. package/lib/entity/project/projectPayload.js +3 -3
  3. package/lib/entity/project/projectState.d.ts +4 -2
  4. package/lib/entity/transaction/payloadTypes/transactionLinePayload.js +1 -1
  5. package/lib/entity/transaction/stateTypes/transactionLine.d.ts +2 -2
  6. package/lib/esm/entity/project/projectPayload.js +1 -1
  7. package/lib/esm/entity/transaction/payloadTypes/transactionLinePayload.js +2 -2
  8. package/lib/esm/index.js +2 -1
  9. package/lib/esm/view/expenseAutomationView/epics/transactionCategorization/fetchTransactionCategorizationViewEpic.js +6 -0
  10. package/lib/esm/view/expenseAutomationView/epics/transactionCategorization/updateTransactionCategorizationEpic.js +3 -0
  11. package/lib/esm/view/expenseAutomationView/helpers/transactionCategorizationLocalDataHelper.js +19 -2
  12. package/lib/esm/view/expenseAutomationView/reducers/transactionsViewReducer.js +13 -4
  13. package/lib/esm/view/expenseAutomationView/selectors/transactionCategorizationSelector.js +7 -2
  14. package/lib/esm/view/profitAndLossClassesView/profitAndLossHorizontalEnrichment.js +44 -0
  15. package/lib/index.d.ts +4 -3
  16. package/lib/index.js +30 -28
  17. package/lib/tsconfig.typecheck.tsbuildinfo +1 -1
  18. package/lib/view/expenseAutomationView/epics/transactionCategorization/fetchTransactionCategorizationViewEpic.d.ts +2 -1
  19. package/lib/view/expenseAutomationView/epics/transactionCategorization/fetchTransactionCategorizationViewEpic.js +6 -0
  20. package/lib/view/expenseAutomationView/epics/transactionCategorization/updateTransactionCategorizationEpic.js +3 -0
  21. package/lib/view/expenseAutomationView/helpers/transactionCategorizationLocalDataHelper.d.ts +2 -1
  22. package/lib/view/expenseAutomationView/helpers/transactionCategorizationLocalDataHelper.js +19 -2
  23. package/lib/view/expenseAutomationView/reducers/transactionsViewReducer.d.ts +3 -1
  24. package/lib/view/expenseAutomationView/reducers/transactionsViewReducer.js +13 -4
  25. package/lib/view/expenseAutomationView/selectorTypes/transactionsViewSelectorTypes.d.ts +3 -0
  26. package/lib/view/expenseAutomationView/selectors/transactionCategorizationSelector.js +6 -1
  27. package/lib/view/expenseAutomationView/types/transactionsViewState.d.ts +3 -0
  28. package/lib/view/profitAndLossClassesView/profitAndLossHorizontalEnrichment.d.ts +15 -1
  29. package/lib/view/profitAndLossClassesView/profitAndLossHorizontalEnrichment.js +45 -0
  30. package/lib/view/transactionDetail/transactionDetailState.d.ts +2 -2
  31. package/lib/view/transactionDetail/transactionDetailTypes.d.ts +2 -2
  32. package/package.json +1 -1
@@ -1,4 +1,4 @@
1
- import { Project } from './projectState';
1
+ import { Project, ProjectBase } from './projectState';
2
2
  export interface ProjectMetadataPayload {
3
3
  accounting_provider_project_customer_id: string | null;
4
4
  accounting_provider_project_id: string | null;
@@ -15,6 +15,6 @@ export interface ProjectBasePayload {
15
15
  accounting_provider_project_id?: string | null;
16
16
  project_name?: string;
17
17
  }
18
- export declare const toProjectBasePayload: (project: Project | undefined) => ProjectBasePayload;
19
- export declare const mapProjectBasePayloadToProject: (payload: ProjectBasePayload) => Project;
18
+ export declare const toProjectBasePayload: (project: ProjectBase | undefined) => ProjectBasePayload;
19
+ export declare const mapProjectBasePayloadToProjectBase: (payload: ProjectBasePayload) => ProjectBase;
20
20
  export declare const mapProjectMetadataPayloadToProject: (payload: ProjectMetadataPayload) => Project;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.mapProjectMetadataPayloadToProject = exports.mapProjectBasePayloadToProject = exports.toProjectBasePayload = void 0;
3
+ exports.mapProjectMetadataPayloadToProject = exports.mapProjectBasePayloadToProjectBase = exports.toProjectBasePayload = void 0;
4
4
  const zeniDayJS_1 = require("../../zeniDayJS");
5
5
  const toProjectBasePayload = (project) => ({
6
6
  zeni_project_id: project?.projectId ?? '',
@@ -8,14 +8,14 @@ const toProjectBasePayload = (project) => ({
8
8
  project_name: project?.projectName ?? '',
9
9
  });
10
10
  exports.toProjectBasePayload = toProjectBasePayload;
11
- const mapProjectBasePayloadToProject = (payload) => ({
11
+ const mapProjectBasePayloadToProjectBase = (payload) => ({
12
12
  projectId: payload.zeni_project_id,
13
13
  projectName: payload.project_name ?? '',
14
14
  ...(payload.accounting_provider_project_id != null
15
15
  ? { accountingProviderProjectId: payload.accounting_provider_project_id }
16
16
  : {}),
17
17
  });
18
- exports.mapProjectBasePayloadToProject = mapProjectBasePayloadToProject;
18
+ exports.mapProjectBasePayloadToProjectBase = mapProjectBasePayloadToProjectBase;
19
19
  const mapProjectMetadataPayloadToProject = (payload) => ({
20
20
  projectId: payload.zeni_project_id,
21
21
  projectName: payload.project_name,
@@ -1,10 +1,12 @@
1
1
  import { ID } from '../../commonStateTypes/common';
2
2
  import { ZeniDate } from '../../zeniDayJS';
3
- export interface Project {
3
+ export interface ProjectBase {
4
4
  projectId: ID;
5
5
  projectName: string;
6
- accountingProviderProjectCustomerId?: ID;
7
6
  accountingProviderProjectId?: ID;
7
+ }
8
+ export interface Project extends ProjectBase {
9
+ accountingProviderProjectCustomerId?: ID;
8
10
  parentCustomerAccountingProviderId?: ID;
9
11
  startDate?: ZeniDate;
10
12
  status?: string;
@@ -47,7 +47,7 @@ const toTransactionWithAccountAndClassLine = (payload, currency, type = 'transac
47
47
  project: payload.line_detail.project != null &&
48
48
  payload.line_detail.project.zeni_project_id != null &&
49
49
  payload.line_detail.project.zeni_project_id !== ''
50
- ? (0, projectPayload_1.mapProjectBasePayloadToProject)(payload.line_detail.project)
50
+ ? (0, projectPayload_1.mapProjectBasePayloadToProjectBase)(payload.line_detail.project)
51
51
  : undefined,
52
52
  billable: payload.line_detail.billable_status ?? 'not_billable',
53
53
  isCategoryMiscategorized: payload.is_category_miscategorized ?? false,
@@ -4,7 +4,7 @@ import { COTLineItemTracking } from '../../../commonStateTypes/recommendationBas
4
4
  import { AccountBase } from '../../account/accountState';
5
5
  import { ClassBase } from '../../class/classState';
6
6
  import { CustomerBase } from '../../customer/customerState';
7
- import { Project } from '../../project/projectState';
7
+ import { ProjectBase } from '../../project/projectState';
8
8
  import { VendorBase } from '../../vendor/vendorState';
9
9
  import { TransactionID } from './transaction';
10
10
  import { TransactionCategory, TransactionType } from './transactionType';
@@ -40,7 +40,7 @@ export interface TransactionWithAccountAndClassLine extends TransactionLineBase
40
40
  isClassMiscategorized: boolean;
41
41
  class?: ClassBase;
42
42
  customer?: CustomerBase;
43
- project?: Project;
43
+ project?: ProjectBase;
44
44
  vendor?: VendorBase;
45
45
  }
46
46
  export interface TransactionWithAccountAndClassLineWithCOTTracking extends TransactionWithAccountAndClassLine {
@@ -4,7 +4,7 @@ export const toProjectBasePayload = (project) => ({
4
4
  accounting_provider_project_id: project?.accountingProviderProjectId ?? null,
5
5
  project_name: project?.projectName ?? '',
6
6
  });
7
- export const mapProjectBasePayloadToProject = (payload) => ({
7
+ export const mapProjectBasePayloadToProjectBase = (payload) => ({
8
8
  projectId: payload.zeni_project_id,
9
9
  projectName: payload.project_name ?? '',
10
10
  ...(payload.accounting_provider_project_id != null
@@ -2,7 +2,7 @@ import { toAmountWC } from '../../../commonStateTypes/amount';
2
2
  import { mapAccountBasePayloadToAccountBase, toAccountPayload, } from '../../account/accountPayload';
3
3
  import { mapClassBasePayloadToClassBase, toClassBasePayload, } from '../../class/classPayload';
4
4
  import { toCustomerBase, toCustomerBasePayload, } from '../../customer/customerPayload';
5
- import { mapProjectBasePayloadToProject, toProjectBasePayload, } from '../../project/projectPayload';
5
+ import { mapProjectBasePayloadToProjectBase, toProjectBasePayload, } from '../../project/projectPayload';
6
6
  import { mapVendorBasePayloadToVendorBase, toVendorBasePayload, } from '../../vendor/vendorPayload';
7
7
  import { toCategorizationStatusType, toPlatformLineDetailType, } from '../stateTypes/transactionLine';
8
8
  import { toTransactionCategory, toTransactionType, } from '../stateTypes/transactionType';
@@ -43,7 +43,7 @@ export const toTransactionWithAccountAndClassLine = (payload, currency, type = '
43
43
  project: payload.line_detail.project != null &&
44
44
  payload.line_detail.project.zeni_project_id != null &&
45
45
  payload.line_detail.project.zeni_project_id !== ''
46
- ? mapProjectBasePayloadToProject(payload.line_detail.project)
46
+ ? mapProjectBasePayloadToProjectBase(payload.line_detail.project)
47
47
  : undefined,
48
48
  billable: payload.line_detail.billable_status ?? 'not_billable',
49
49
  isCategoryMiscategorized: payload.is_category_miscategorized ?? false,
package/lib/esm/index.js CHANGED
@@ -224,6 +224,7 @@ import { fetchProfitAndLoss, fetchProfitAndLossForTimeframe, resetProfitAndLossN
224
224
  import { getPandLReportFetchState, getProfitAndLossReport, } from './view/profitAndLoss/profitAndLossSelector';
225
225
  import { getEmptyHorizontalSectionBalancesSlice } from './view/profitAndLossClassesView/horizontalSectionEmptyBalancesSlice';
226
226
  import { getProfitAndLossClassesHorizontalView } from './view/profitAndLossClassesView/profitAndLossClassesByClassHorizontalSelector';
227
+ import { aggregateHorizontalDescendantBalances } from './view/profitAndLossClassesView/profitAndLossHorizontalEnrichment';
227
228
  import { HORIZONTAL_CLASS_TOTAL_BALANCE_ID, isHorizontalAccountReportData, } from './view/profitAndLossClassesView/profitAndLossClassesByClassHorizontalSelectorTypes';
228
229
  import { fetchProfitAndLossClassesView, resetProfitAndLossClassesNodeCollapseState, updateClassViewLayout, updateAccountViewMode as updateProfitAndLossAccountViewMode, updateClassesToFilterOut as updateProfitAndLossClassesToFilterOut, updateProfitAndLossClassesViewUIState, } from './view/profitAndLossClassesView/profitAndLossClassesViewReducer';
229
230
  import { getProfitAndLossClassesView } from './view/profitAndLossClassesView/profitAndLossClassesViewSelector';
@@ -473,7 +474,7 @@ export { fetchDashboard, getDashboard, updateTreasuryVideoClosed, };
473
474
  export { updateDashboardLayout };
474
475
  export { getPandLWithForecast, } from './view/profitAndLoss/pAndLWithForecast/pAndLWithForecastSelector';
475
476
  export { fetchProfitAndLoss, resetProfitAndLossNodeCollapseState, updateProfitAndLossUIState, getProfitAndLossReport, getPandLReportFetchState, fetchProfitAndLossForTimeframe, };
476
- export { fetchProfitAndLossClassesView, updateProfitAndLossClassesToFilterOut, resetProfitAndLossClassesNodeCollapseState, updateProfitAndLossClassesViewUIState, updateProfitAndLossAccountViewMode, updateClassViewLayout, getProfitAndLossClassesView, getEmptyHorizontalSectionBalancesSlice, getProfitAndLossClassesHorizontalView, isPandLReportViewCalculatedSectionID, isPandLReportViewSectionID, HORIZONTAL_CLASS_TOTAL_BALANCE_ID, isHorizontalAccountReportData, };
477
+ export { fetchProfitAndLossClassesView, updateProfitAndLossClassesToFilterOut, resetProfitAndLossClassesNodeCollapseState, updateProfitAndLossClassesViewUIState, updateProfitAndLossAccountViewMode, updateClassViewLayout, getProfitAndLossClassesView, aggregateHorizontalDescendantBalances, getEmptyHorizontalSectionBalancesSlice, getProfitAndLossClassesHorizontalView, isPandLReportViewCalculatedSectionID, isPandLReportViewSectionID, HORIZONTAL_CLASS_TOTAL_BALANCE_ID, isHorizontalAccountReportData, };
477
478
  export { clearProfitAndLossProjectView, fetchProfitAndLossForTimeframeProjectView, fetchProfitAndLossProjectView, resetProfitAndLossProjectNodeCollapseState, updateProjectViewAccountViewMode, updateProjectsToFilterOut, updateProfitAndLossProjectViewUIState, getProfitAndLossProjectView, };
478
479
  export { getAccountingProviderAttachment };
479
480
  export { fetchUserListByType, getUserList };
@@ -4,6 +4,7 @@ import { toMonthYearPeriodId, } from '../../../../commonStateTypes/timePeriod';
4
4
  import { fetchAccountList } from '../../../accountList/accountListReducer';
5
5
  import { fetchClassList } from '../../../classList/classListReducer';
6
6
  import { fetchOwnerList } from '../../../ownerList/ownerListReducer';
7
+ import { fetchProjectList } from '../../../projectList/projectListReducer';
7
8
  import { fetchTransactionCategorization, fetchTransactionCategorizationView, } from '../../reducers/transactionsViewReducer';
8
9
  export const fetchTransactionCategorizationViewEpic = (actions$, state$) => actions$.pipe(filter(fetchTransactionCategorizationView.match), mergeMap((action) => {
9
10
  const { selectedTab, cacheOverride, keepExistingListItems, pageToken, period, refreshViewInBackground, searchString, resetListItems, isUncategorizedExpenseCategoryEnabled, } = action.payload;
@@ -26,6 +27,11 @@ export const fetchTransactionCategorizationViewEpic = (actions$, state$) => acti
26
27
  classList.fetchState === 'Not-Started') {
27
28
  updateActions.push(fetchClassList());
28
29
  }
30
+ const projectList = state$.value.projectListState;
31
+ if (projectList.hasValidState() === false &&
32
+ projectList.fetchState === 'Not-Started') {
33
+ updateActions.push(fetchProjectList());
34
+ }
29
35
  const vendorOwnerList = state$.value.ownerListState;
30
36
  if (vendorOwnerList.hasValidState() === false &&
31
37
  vendorOwnerList.fetchState === 'Not-Started') {
@@ -110,6 +110,9 @@ const toTransactionUpdatePayload = (updates, detail, transactionId, cotTrackingB
110
110
  if (lineUpdates.class != null) {
111
111
  lineToBeUpdated.class = lineUpdates.class;
112
112
  }
113
+ if (lineUpdates.project != null) {
114
+ lineToBeUpdated.project = lineUpdates.project;
115
+ }
113
116
  if (lineUpdates.account != null) {
114
117
  lineToBeUpdated.account = lineUpdates.account;
115
118
  }
@@ -183,6 +183,7 @@ const buildLineItemById = (transaction, uncategorizedIncomeExpense, recommendati
183
183
  const lineId = line.id;
184
184
  record.account = accountLine.account;
185
185
  record.class = accountLine.class;
186
+ record.project = accountLine.project;
186
187
  record.vendor =
187
188
  accountLine.vendor != null
188
189
  ? {
@@ -305,7 +306,7 @@ const buildLineItemById = (transaction, uncategorizedIncomeExpense, recommendati
305
306
  });
306
307
  return lineItemData;
307
308
  };
308
- export const toSetAllLineItemsToCategoryClass = (draft, transaction, transactionId, transactionLineId, transactionDetailLocalData, updatedItem, uncategorizedAccounts, selectedTab, vendorId, customerId, selectedAccount, selectedClassBase, isUncategorizedExpenseCategoryEnabled, isAccountingClassesEnabled) => {
309
+ export const toSetAllLineItemsToCategoryClass = (draft, transaction, transactionId, transactionLineId, transactionDetailLocalData, updatedItem, uncategorizedAccounts, selectedTab, vendorId, customerId, selectedAccount, selectedClassBase, selectedProject, isUncategorizedExpenseCategoryEnabled, isAccountingClassesEnabled) => {
309
310
  let newLineItems = {};
310
311
  let selectedCheckBoxTransactionIds = draft.selectedCheckBoxTransactionIds;
311
312
  let similarTransactionUpdated = false;
@@ -325,7 +326,7 @@ export const toSetAllLineItemsToCategoryClass = (draft, transaction, transaction
325
326
  const record = {
326
327
  ...transactionDetailLocalData.lineItemById[lineId],
327
328
  };
328
- // if the line is the one that is being updated, then set the account and class
329
+ // if the line is the one that is being updated, then set the account, class, and project
329
330
  if (transactionId === record.transactionId &&
330
331
  transactionLineId === lineId) {
331
332
  if (selectedAccount != null) {
@@ -334,6 +335,9 @@ export const toSetAllLineItemsToCategoryClass = (draft, transaction, transaction
334
335
  if (selectedClassBase != null) {
335
336
  record.class = selectedClassBase;
336
337
  }
338
+ if (selectedProject != null) {
339
+ record.project = selectedProject;
340
+ }
337
341
  const isClassSatisfied = isAccountingClassesEnabled === false || record.class != null;
338
342
  if (isClassSatisfied && record.account != null) {
339
343
  if (selectedCheckBoxTransactionIds.length < MAX_SELECTION_LIMIT &&
@@ -385,6 +389,18 @@ export const toSetAllLineItemsToCategoryClass = (draft, transaction, transaction
385
389
  selectedCheckBoxTransactionIds.push(transaction.id);
386
390
  }
387
391
  }
392
+ if (selectedProject != null &&
393
+ updatedItem === 'project' &&
394
+ record.project?.projectId == null) {
395
+ record.project = selectedProject;
396
+ similarTransactionUpdated = true;
397
+ if (record.account != null &&
398
+ selectedCheckBoxTransactionIds.length < MAX_SELECTION_LIMIT &&
399
+ selectedCheckBoxTransactionIds.includes(transaction.id) ===
400
+ false) {
401
+ selectedCheckBoxTransactionIds.push(transaction.id);
402
+ }
403
+ }
388
404
  }
389
405
  }
390
406
  if (line.type === 'transaction_with_product_or_service_line') {
@@ -514,6 +530,7 @@ export const setEntityRecommendationForLineIdInLocalData = (draft, transaction,
514
530
  ...newLineItem,
515
531
  class: matchingRecord.class,
516
532
  account: matchingRecord.account,
533
+ project: matchingRecord.project,
517
534
  lineEntity: matchingRecord.lineEntity,
518
535
  };
519
536
  }
@@ -282,7 +282,7 @@ const expenseAutomationTransactionsView = createSlice({
282
282
  },
283
283
  setAllItemsToCategoryClassInLocalDataForCategorization: {
284
284
  reducer(draft, action) {
285
- const { vendorId, customerId, selectedAccount, selectedClassBase, transactions, transactionId, lineId, selectedTab, uncategorizedAccounts, isAccountingClassesEnabled, isUncategorizedExpenseCategoryEnabled, } = action.payload;
285
+ const { vendorId, customerId, selectedAccount, selectedClassBase, selectedProject, transactions, transactionId, lineId, selectedTab, uncategorizedAccounts, isAccountingClassesEnabled, isUncategorizedExpenseCategoryEnabled, } = action.payload;
286
286
  const transactionLocalData = recordGet(draft.transactionCategorizationView[selectedTab]
287
287
  .transactionReviewLocalDataById, transactionId, undefined);
288
288
  const existingAccount = transactionLocalData?.transactionReviewLocalData.lineItemById[lineId]
@@ -296,6 +296,7 @@ const expenseAutomationTransactionsView = createSlice({
296
296
  ...transactionLocalData.transactionReviewLocalData.lineItemById[lineId],
297
297
  account: selectedAccount,
298
298
  class: selectedClassBase,
299
+ project: selectedProject,
299
300
  },
300
301
  },
301
302
  };
@@ -324,9 +325,16 @@ const expenseAutomationTransactionsView = createSlice({
324
325
  const transactionLocalData = recordGet(draft.transactionCategorizationView[selectedTab]
325
326
  .transactionReviewLocalDataById, transaction.id, undefined);
326
327
  if (transactionLocalData != null) {
327
- const { localData, similarTransactionUpdated } = toSetAllLineItemsToCategoryClass(draft.transactionCategorizationView[selectedTab], transaction, transactionId, lineId, transactionLocalData.transactionReviewLocalData, existingAccount?.accountId !== selectedAccount?.accountId
328
+ const existingClassId = transactionLocalData.transactionReviewLocalData.lineItemById[lineId]?.class?.classId;
329
+ const existingProjectId = transactionLocalData.transactionReviewLocalData.lineItemById[lineId]?.project?.projectId;
330
+ const updatedItem = existingAccount?.accountId !== selectedAccount?.accountId
328
331
  ? 'category'
329
- : 'class', uncategorizedAccounts, selectedTab, vendorId, customerId, selectedAccount, selectedClassBase, isUncategorizedExpenseCategoryEnabled, isAccountingClassesEnabled);
332
+ : existingClassId !== selectedClassBase?.classId
333
+ ? 'class'
334
+ : existingProjectId !== selectedProject?.projectId
335
+ ? 'project'
336
+ : 'class';
337
+ const { localData, similarTransactionUpdated } = toSetAllLineItemsToCategoryClass(draft.transactionCategorizationView[selectedTab], transaction, transactionId, lineId, transactionLocalData.transactionReviewLocalData, updatedItem, uncategorizedAccounts, selectedTab, vendorId, customerId, selectedAccount, selectedClassBase, selectedProject, isUncategorizedExpenseCategoryEnabled, isAccountingClassesEnabled);
330
338
  draft.transactionCategorizationView[selectedTab].transactionReviewLocalDataById[transaction.id] = {
331
339
  transactionId: transaction.id,
332
340
  transactionReviewLocalData: localData,
@@ -345,7 +353,7 @@ const expenseAutomationTransactionsView = createSlice({
345
353
  ...dirtyIds,
346
354
  ]);
347
355
  },
348
- prepare(selectedTab, transactionId, lineId, transactions, uncategorizedAccounts, vendorId, customerId, selectedAccount, selectedClassBase, lineEntity, recommendations, isUncategorizedExpenseCategoryEnabled, isAccountingClassesEnabled) {
356
+ prepare(selectedTab, transactionId, lineId, transactions, uncategorizedAccounts, vendorId, customerId, selectedAccount, selectedClassBase, selectedProject, lineEntity, recommendations, isUncategorizedExpenseCategoryEnabled, isAccountingClassesEnabled) {
349
357
  return {
350
358
  payload: {
351
359
  selectedTab,
@@ -357,6 +365,7 @@ const expenseAutomationTransactionsView = createSlice({
357
365
  lineEntity,
358
366
  selectedAccount,
359
367
  selectedClassBase,
368
+ selectedProject,
360
369
  recommendations,
361
370
  uncategorizedAccounts,
362
371
  isAccountingClassesEnabled,
@@ -1,14 +1,15 @@
1
1
  import omit from 'lodash/omit';
2
2
  import { reduceAllFetchState, reduceAnyFetchState, } from '../../../commonStateTypes/reduceFetchState';
3
3
  import { toMonthYearPeriodId } from '../../../commonStateTypes/timePeriod';
4
- import { getIsAccountingClassesEnabled, getCurrentTenant, } from '../../../entity/tenant/tenantSelector';
4
+ import { getCurrentTenant, getIsAccountingClassesEnabled, getIsAccountingProjectsEnabled, } from '../../../entity/tenant/tenantSelector';
5
5
  import { getTransactionWithCOT } from '../../../entity/transaction/transactionHelper';
6
6
  import { getSupportedTransactionsByIds } from '../../../entity/transaction/transactionSelector';
7
7
  import { getAccountList, getNestedAccountListHierarchy, getUncategorizedAccounts, } from '../../accountList/accountListSelector';
8
8
  import { getClassList, getNestedClassListHierarchy, } from '../../classList/classListSelector';
9
+ import { getProjectList } from '../../projectList/projectListSelector';
9
10
  import { getAllSteps } from '../selectorTypes/expenseAutomationViewSelectorTypes';
10
11
  export function getExpenseAutomationTransactionView(state) {
11
- const { accountState, classState, classListState, accountListState, expenseAutomationTransactionsViewState, expenseAutomationViewState, transactionState, monthEndCloseChecksState, monthEndCloseChecksViewState, } = state;
12
+ const { accountState, classState, classListState, accountListState, expenseAutomationTransactionsViewState, expenseAutomationViewState, projectState, projectListState, transactionState, monthEndCloseChecksState, monthEndCloseChecksViewState, } = state;
12
13
  const { selectedTransactionCategorizationTab } = expenseAutomationTransactionsViewState;
13
14
  const { uiState, refreshStatus, saveStatus, markAsNotMiscategorizedStatus, transactionIdsBySelectedPeriod, transactionReviewLocalDataById, selectedCheckBoxTransactionIds, transactionIdsWithUnsavedData, uploadReceiptStatusById, selectedTransactionId, selectedTransactionLineId, } = expenseAutomationTransactionsViewState.transactionCategorizationView[selectedTransactionCategorizationTab];
14
15
  const uncategorizedIncomeExpense = getUncategorizedAccounts(accountState, accountListState, 'accountList');
@@ -17,6 +18,8 @@ export function getExpenseAutomationTransactionView(state) {
17
18
  const allClasses = classList.classes.map((classData) => {
18
19
  return omit(classData, ['accountIds', 'key']);
19
20
  });
21
+ const projectList = getProjectList(projectState, projectListState);
22
+ const isAccountingProjectsEnabled = getIsAccountingProjectsEnabled(state);
20
23
  const accountsHierarchyList = getNestedAccountListHierarchy(accountListState, 'accountList');
21
24
  const classHierarchyList = getNestedClassListHierarchy(classListState);
22
25
  const currentTenant = getCurrentTenant(state);
@@ -96,6 +99,8 @@ export function getExpenseAutomationTransactionView(state) {
96
99
  classList: allClasses,
97
100
  accountsHierarchyList,
98
101
  classHierarchyList,
102
+ isAccountingProjectsEnabled,
103
+ projectList,
99
104
  transactionLocalData,
100
105
  uiState,
101
106
  refreshStatus,
@@ -115,6 +115,50 @@ function buildHorizontalClassColumnBalancesSlice(report, classAmounts, totalAmou
115
115
  },
116
116
  };
117
117
  }
118
+ /**
119
+ * Sum balances across descendant accounts (recursive).
120
+ *
121
+ * Parent rows in the horizontal pivot only carry their own direct postings
122
+ * (zero for "container" accounts), so callers rendering "Total {parent}" rows
123
+ * must aggregate from descendants to get the real per-class totals.
124
+ *
125
+ * Returns one `COABalance` per class column + total (same shape as the
126
+ * enriched `balancesInTimeframe.balances`), with `amount` summed and `type`
127
+ * set to `default_with_null_balance` for zeros. Returns `[]` when no
128
+ * descendants have enriched data.
129
+ */
130
+ export function aggregateHorizontalDescendantBalances(accounts, childIndices, childrenByParent) {
131
+ let template;
132
+ const sums = new Map();
133
+ const walk = (idx) => {
134
+ const childRow = accounts[idx];
135
+ const childBalances = childRow?.accountReportData?.balancesInTimeframe.balances;
136
+ if (childBalances != null) {
137
+ if (template == null) {
138
+ template = childBalances;
139
+ }
140
+ childBalances.forEach((b, i) => {
141
+ sums.set(i, (sums.get(i) ?? 0) + (b.balance.amount ?? 0));
142
+ });
143
+ }
144
+ // Always recurse — a missing intermediate node should not prune its
145
+ // descendants from the aggregate.
146
+ const grandChildren = childrenByParent[idx] ?? [];
147
+ grandChildren.forEach(walk);
148
+ };
149
+ childIndices.forEach(walk);
150
+ if (template == null) {
151
+ return [];
152
+ }
153
+ return template.map((tpl, i) => {
154
+ const amount = sums.get(i) ?? 0;
155
+ return {
156
+ ...tpl,
157
+ balance: { ...tpl.balance, amount },
158
+ type: amount === 0 ? 'default_with_null_balance' : 'default',
159
+ };
160
+ });
161
+ }
118
162
  /**
119
163
  * Parent index per DFS row — O(n). Matches the scan in the former
120
164
  * `findHorizontalAccountParentIndex` (depth &lt;= 2 rows have no parent).
package/lib/index.d.ts CHANGED
@@ -103,7 +103,7 @@ import { ExternalNotificationData, NotificationActivityType, NotificationGroup,
103
103
  import { Logo, PaymentAccount, VERIFIED_PAYMENT_ACCOUNT_PROVIDER_VERIFICATION_STATUS } from './entity/paymentAccount/paymentAccountState';
104
104
  import { PaymentInstrument } from './entity/paymentInstrument/paymentInstrument';
105
105
  import { AccountingSummary, Runway } from './entity/portfolio/accountingSummary/accountingSummaryState';
106
- import { Project } from './entity/project/projectState';
106
+ import { Project, ProjectBase } from './entity/project/projectState';
107
107
  import { MilageReimbursementLine, OutofPocketReimbursementLine, Reimbursement, ReimbursementLine, ReimbursementTypeCode } from './entity/reimbursement/reimbursementState';
108
108
  import { SectionAccountsViewReport } from './entity/sectionAccountsView/sectionAccountsViewSelector';
109
109
  import { SectionClassesView as SectionClassesViewV2 } from './entity/sectionClassesViewV2/sectionClassesView';
@@ -357,6 +357,7 @@ import { ProfitAndLossReport, getPandLReportFetchState, getProfitAndLossReport }
357
357
  import { ProfitAndLossState, ProfitAndLossUIState } from './view/profitAndLoss/profitAndLossState';
358
358
  import { getEmptyHorizontalSectionBalancesSlice } from './view/profitAndLossClassesView/horizontalSectionEmptyBalancesSlice';
359
359
  import { getProfitAndLossClassesHorizontalView } from './view/profitAndLossClassesView/profitAndLossClassesByClassHorizontalSelector';
360
+ import { aggregateHorizontalDescendantBalances } from './view/profitAndLossClassesView/profitAndLossHorizontalEnrichment';
360
361
  import { ClassColumnDefinition, HORIZONTAL_CLASS_TOTAL_BALANCE_ID, HorizontalAccountReportData, HorizontalAccountRow, HorizontalCalculatedSection, HorizontalClassBalance, HorizontalSectionReport, HorizontalSectionTreeLayout, ProfitAndLossByClassHorizontalReport, isHorizontalAccountReportData } from './view/profitAndLossClassesView/profitAndLossClassesByClassHorizontalSelectorTypes';
361
362
  import { fetchProfitAndLossClassesView, resetProfitAndLossClassesNodeCollapseState, updateClassViewLayout, updateAccountViewMode as updateProfitAndLossAccountViewMode, updateClassesToFilterOut as updateProfitAndLossClassesToFilterOut, updateProfitAndLossClassesViewUIState } from './view/profitAndLossClassesView/profitAndLossClassesViewReducer';
362
363
  import { getProfitAndLossClassesView } from './view/profitAndLossClassesView/profitAndLossClassesViewSelector';
@@ -696,8 +697,8 @@ export { fetchDashboard, getDashboard, DashboardReport, updateTreasuryVideoClose
696
697
  export { updateDashboardLayout };
697
698
  export { PandLWithForecastView, getPandLWithForecast, } from './view/profitAndLoss/pAndLWithForecast/pAndLWithForecastSelector';
698
699
  export { fetchProfitAndLoss, resetProfitAndLossNodeCollapseState, updateProfitAndLossUIState, ProfitAndLossState, ProfitAndLossUIState, getProfitAndLossReport, ProfitAndLossReport, getPandLReportFetchState, fetchProfitAndLossForTimeframe, };
699
- export { fetchProfitAndLossClassesView, updateProfitAndLossClassesToFilterOut, resetProfitAndLossClassesNodeCollapseState, updateProfitAndLossClassesViewUIState, updateProfitAndLossAccountViewMode, updateClassViewLayout, ProfitAndLossClassesViewUIState, ProfitAndLossClassesViewReport, getProfitAndLossClassesView, getEmptyHorizontalSectionBalancesSlice, getProfitAndLossClassesHorizontalView, PandLReportViewSectionID, PandLReportViewCalculatedSectionID, isPandLReportViewCalculatedSectionID, isPandLReportViewSectionID, ClassViewLayout, ProfitAndLossByClassHorizontalReport, ClassColumnDefinition, HORIZONTAL_CLASS_TOTAL_BALANCE_ID, HorizontalAccountReportData, HorizontalAccountRow, HorizontalClassBalance, HorizontalSectionReport, HorizontalSectionTreeLayout, HorizontalCalculatedSection, isHorizontalAccountReportData, };
700
- export { clearProfitAndLossProjectView, fetchProfitAndLossForTimeframeProjectView, fetchProfitAndLossProjectView, resetProfitAndLossProjectNodeCollapseState, updateProjectViewAccountViewMode, updateProjectsToFilterOut, updateProfitAndLossProjectViewUIState, ProfitAndLossProjectViewUIState, getProfitAndLossProjectView, ProfitAndLossProjectViewReport, Project, ProjectReportWithBalances, SectionProjectViewReport, };
700
+ export { fetchProfitAndLossClassesView, updateProfitAndLossClassesToFilterOut, resetProfitAndLossClassesNodeCollapseState, updateProfitAndLossClassesViewUIState, updateProfitAndLossAccountViewMode, updateClassViewLayout, ProfitAndLossClassesViewUIState, ProfitAndLossClassesViewReport, getProfitAndLossClassesView, aggregateHorizontalDescendantBalances, getEmptyHorizontalSectionBalancesSlice, getProfitAndLossClassesHorizontalView, PandLReportViewSectionID, PandLReportViewCalculatedSectionID, isPandLReportViewCalculatedSectionID, isPandLReportViewSectionID, ClassViewLayout, ProfitAndLossByClassHorizontalReport, ClassColumnDefinition, HORIZONTAL_CLASS_TOTAL_BALANCE_ID, HorizontalAccountReportData, HorizontalAccountRow, HorizontalClassBalance, HorizontalSectionReport, HorizontalSectionTreeLayout, HorizontalCalculatedSection, isHorizontalAccountReportData, };
701
+ export { clearProfitAndLossProjectView, fetchProfitAndLossForTimeframeProjectView, fetchProfitAndLossProjectView, resetProfitAndLossProjectNodeCollapseState, updateProjectViewAccountViewMode, updateProjectsToFilterOut, updateProfitAndLossProjectViewUIState, ProfitAndLossProjectViewUIState, getProfitAndLossProjectView, ProfitAndLossProjectViewReport, Project, ProjectBase, ProjectReportWithBalances, SectionProjectViewReport, };
701
702
  export { AccountingProviderAttachmentSelector, getAccountingProviderAttachment };
702
703
  export { fetchUserListByType, getUserList, UserListSelectorView };
703
704
  export { fetchAllPeopleRequiredViews, fetchPeoplePage, fetchPeople, deletePerson, resendInvite, changeZeniPersonRoles, invitePeople, inviteZeniPeople, updatePeopleUIState, peopleSaveUpdates, resetUpdateErrorMessage, peopleSaveDataInLocalStore, peopleClearDataInLocalStore, PeopleSelectorView, PeopleLocalDataView, Person, getPeople, getPeopleLocalData, PeopleState, PersonUpdateType, PeoplePageMode, ZeniPersonUserRoleType, PeopleUIState, PeopleLocalData, initializeEditPerson, };