@zeniai/client-epic-state 4.19.87-betaVR2 → 4.19.88-beta0ND

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 (42) hide show
  1. package/lib/entity/depositAccount/depositAccount.js +8 -1
  2. package/lib/epic.d.ts +4 -4
  3. package/lib/epic.js +4 -4
  4. package/lib/esm/entity/depositAccount/depositAccount.js +8 -1
  5. package/lib/esm/epic.js +4 -4
  6. package/lib/esm/index.js +4 -3
  7. package/lib/esm/view/expenseAutomationView/epics/missingReceipts/fetchMoreBatchDetailsEpic.js +1 -2
  8. package/lib/esm/view/expenseAutomationView/epics/missingReceipts/fetchMultipleBatchDetailsEpic.js +1 -2
  9. package/lib/esm/view/expenseAutomationView/epics/missingReceipts/searchTransactionsForManualMatchEpic.js +2 -3
  10. package/lib/esm/view/expenseAutomationView/epics/missingReceipts/watchBulkUploadBatchStatusEpic.js +1 -4
  11. package/lib/esm/view/expenseAutomationView/reducers/missingReceiptsViewReducer.js +11 -4
  12. package/lib/esm/view/expenseAutomationView/reducers/transactionsViewReducer.js +4 -1
  13. package/lib/esm/view/expenseAutomationView/selectors/missingReceiptsSelector.js +5 -7
  14. package/lib/esm/view/expenseAutomationView/types/missingReceiptsViewState.js +6 -1
  15. package/lib/esm/view/financeStatement/financeStatementSelector.js +5 -0
  16. package/lib/esm/view/profitAndLossClassesView/profitAndLossClassesByClassHorizontalSelector.js +282 -0
  17. package/lib/esm/view/profitAndLossClassesView/profitAndLossClassesByClassHorizontalSelectorTypes.js +1 -0
  18. package/lib/esm/view/profitAndLossClassesView/profitAndLossClassesViewReducer.js +5 -1
  19. package/lib/index.d.ts +8 -6
  20. package/lib/index.js +43 -40
  21. package/lib/tsconfig.typecheck.tsbuildinfo +1 -0
  22. package/lib/view/expenseAutomationView/epics/missingReceipts/fetchMoreBatchDetailsEpic.js +1 -2
  23. package/lib/view/expenseAutomationView/epics/missingReceipts/fetchMultipleBatchDetailsEpic.js +1 -2
  24. package/lib/view/expenseAutomationView/epics/missingReceipts/searchTransactionsForManualMatchEpic.js +1 -2
  25. package/lib/view/expenseAutomationView/epics/missingReceipts/watchBulkUploadBatchStatusEpic.js +1 -4
  26. package/lib/view/expenseAutomationView/payload/missingReceiptsPayload.d.ts +1 -1
  27. package/lib/view/expenseAutomationView/reducers/missingReceiptsViewReducer.d.ts +1 -1
  28. package/lib/view/expenseAutomationView/reducers/missingReceiptsViewReducer.js +11 -4
  29. package/lib/view/expenseAutomationView/reducers/transactionsViewReducer.js +4 -1
  30. package/lib/view/expenseAutomationView/selectorTypes/missingReceiptsSelectorTypes.d.ts +1 -1
  31. package/lib/view/expenseAutomationView/selectors/missingReceiptsSelector.js +5 -7
  32. package/lib/view/expenseAutomationView/types/missingReceiptsViewState.js +6 -1
  33. package/lib/view/financeStatement/financeStatementSelector.d.ts +2 -0
  34. package/lib/view/financeStatement/financeStatementSelector.js +5 -0
  35. package/lib/view/profitAndLossClassesView/profitAndLossClassesByClassHorizontalSelector.d.ts +13 -0
  36. package/lib/view/profitAndLossClassesView/profitAndLossClassesByClassHorizontalSelector.js +286 -0
  37. package/lib/view/profitAndLossClassesView/profitAndLossClassesByClassHorizontalSelectorTypes.d.ts +57 -0
  38. package/lib/view/profitAndLossClassesView/profitAndLossClassesByClassHorizontalSelectorTypes.js +2 -0
  39. package/lib/view/profitAndLossClassesView/profitAndLossClassesViewReducer.d.ts +4 -2
  40. package/lib/view/profitAndLossClassesView/profitAndLossClassesViewReducer.js +6 -2
  41. package/lib/view/profitAndLossClassesView/profitAndLossClassesViewState.d.ts +2 -0
  42. package/package.json +2 -2
@@ -31,8 +31,7 @@ const fetchMoreBatchDetailsEpic = (actions$, state$, zeniAPI) => actions$.pipe((
31
31
  }
32
32
  const nextPage = unfetchedBatchIds.slice(0, MORE_BATCH_PAGE_SIZE);
33
33
  return (0, rxjs_1.concat)((0, fetchMultipleBatchDetailsEpic_1.fetchBatchDetailsByIds)(nextPage, zeniAPI), (0, rxjs_1.of)((0, missingReceiptsViewReducer_1.fetchMoreBatchDetailsComplete)())).pipe((0, operators_1.catchError)((error) => (0, rxjs_1.of)((0, missingReceiptsViewReducer_1.fetchMoreBatchDetailsFailure)({
34
- error: (0, responsePayload_1.createZeniAPIStatus)('Unexpected Error', 'Fetch more batch details errored out: ' +
35
- JSON.stringify(error)),
34
+ error: (0, responsePayload_1.createZeniAPIStatus)('Unexpected Error', 'Fetch more batch details errored out: ' + JSON.stringify(error)),
36
35
  }))));
37
36
  }));
38
37
  exports.fetchMoreBatchDetailsEpic = fetchMoreBatchDetailsEpic;
@@ -29,8 +29,7 @@ function fetchBatchDetailsByIds(batchIds, zeniAPI) {
29
29
  actions.push((0, missingReceiptsViewReducer_1.storeBatchDetails)((0, missingReceiptsPayload_1.toBatchDetails)(response.data, zeniAPI.apiEndPoints.fileMicroServiceBaseUrl)));
30
30
  return (0, rxjs_1.from)(actions);
31
31
  }), (0, operators_1.catchError)((error) => {
32
- console.error(`Failed to fetch batch details for ${batchId}:`, (0, responsePayload_1.createZeniAPIStatus)('Unexpected Error', 'Fetch batch details errored out: ' +
33
- JSON.stringify(error)));
32
+ console.error(`Failed to fetch batch details for ${batchId}:`, (0, responsePayload_1.createZeniAPIStatus)('Unexpected Error', 'Fetch batch details errored out: ' + JSON.stringify(error)));
34
33
  return (0, rxjs_1.of)((0, missingReceiptsViewReducer_1.batchDetailFetchFailed)({ batchId }));
35
34
  })), 5));
36
35
  }
@@ -28,8 +28,7 @@ const searchTransactionsForManualMatchEpic = (actions$, state$, zeniAPI) => {
28
28
  action.payload.query.trim().length >= 2), (0, operators_1.debounceTime)(300));
29
29
  return (0, rxjs_1.merge)(pagination$, newSearchTooShortOrClear$, newSearchDebounced$).pipe((0, operators_1.switchMap)((action) => {
30
30
  const { query, pageToken } = action.payload;
31
- if (pageToken == null &&
32
- query.trim().length < 2) {
31
+ if (pageToken == null && query.trim().length < 2) {
33
32
  return rxjs_1.EMPTY;
34
33
  }
35
34
  const state = state$.value;
@@ -33,12 +33,9 @@ const bulkUploadAutomatchingTimeoutEpic = (actions$, state$) => actions$.pipe((0
33
33
  const { batchId } = action.payload;
34
34
  return (0, rxjs_1.timer)(bulkUploadTiming_1.BULK_UPLOAD_AUTOMATCHING_TIMEOUT_MS).pipe((0, operators_1.mergeMap)(() => {
35
35
  const bulk = state$.value.expenseAutomationMissingReceiptsViewState.bulkUpload;
36
- if (bulk.phase !== 'matching' ||
37
- bulk.currentBatchId !== batchId) {
36
+ if (bulk.phase !== 'matching' || bulk.currentBatchId !== batchId) {
38
37
  return rxjs_1.EMPTY;
39
38
  }
40
- // eslint-disable-next-line no-console
41
- console.warn('[bulk-upload] Automatching wait exceeded timeout; releasing UI', { batchId });
42
39
  return (0, rxjs_1.from)([
43
40
  (0, missingReceiptsViewReducer_1.bulkUploadAutomatchingTimedOut)(batchId),
44
41
  (0, missingReceiptsViewReducer_1.fetchBulkUploadBatches)({ cacheOverride: true }),
@@ -1,8 +1,8 @@
1
1
  import { URLPayload } from '../../../commonPayloadTypes/urlPayload';
2
+ import type { MonthYearPeriodId } from '../../../commonStateTypes/timePeriod';
2
3
  import { TransactionPayload } from '../../../entity/transaction/payloadTypes/transactionPayload';
3
4
  import { SupportedTransactionPayload } from '../../../entity/transaction/transactionState';
4
5
  import { ZeniAPIResponse } from '../../../responsePayload';
5
- import type { MonthYearPeriodId } from '../../../commonStateTypes/timePeriod';
6
6
  import { type BatchDetails, type BatchFile, type BatchFileStatus, type BatchListItem, type BatchStatus, type BatchSummary, type ManualSearchResult } from '../types/missingReceiptsViewState';
7
7
  export interface MissingReceiptsQueryPayload {
8
8
  end_date: string;
@@ -2,11 +2,11 @@ import { Amount } from '../../../commonStateTypes/amount';
2
2
  import { FetchStateAndError, ID, UpdateType } from '../../../commonStateTypes/common';
3
3
  import { SortOrder } from '../../../commonStateTypes/selectorTypes/sortOrderTypes';
4
4
  import { MonthYearPeriod, TimePeriod } from '../../../commonStateTypes/timePeriod';
5
+ import type { MonthYearPeriodId } from '../../../commonStateTypes/timePeriod';
5
6
  import { TransactionPayload } from '../../../entity/transaction/payloadTypes/transactionPayload';
6
7
  import { SupportedTransactionPayload } from '../../../entity/transaction/transactionState';
7
8
  import { ZeniAPIStatus } from '../../../responsePayload';
8
9
  import { type BatchDetails, type BatchListItem, type BatchStatus, type BulkUploadResultsTab, type BulkUploadSortKey, type BulkUploadState, type CompletedSubTab, type ManualSearchResult, type MissingReceiptsViewState, type MissingReceiptsViewUIState } from '../types/missingReceiptsViewState';
9
- import type { MonthYearPeriodId } from '../../../commonStateTypes/timePeriod';
10
10
  export declare const getCompletedTransactionsCacheKey: (periodId: MonthYearPeriodId, sortKey: BulkUploadSortKey, sortOrder: SortOrder, subTab: CompletedSubTab) => string;
11
11
  export declare const initialBulkUploadState: BulkUploadState;
12
12
  export declare const initialState: MissingReceiptsViewState;
@@ -328,8 +328,7 @@ const expenseAutomationMissingReceiptsView = (0, toolkit_1.createSlice)({
328
328
  * dismiss the Unmatched skeleton.
329
329
  */
330
330
  const currentId = draft.bulkUpload.currentBatchId;
331
- if (draft.bulkUpload.phase === 'matching' &&
332
- currentId != null) {
331
+ if (draft.bulkUpload.phase === 'matching' && currentId != null) {
333
332
  const existing = draft.bulkUpload.batchDetailsById[currentId];
334
333
  if (existing?.status === 'completed') {
335
334
  draft.bulkUpload.phase = 'completed';
@@ -350,7 +349,15 @@ const expenseAutomationMissingReceiptsView = (0, toolkit_1.createSlice)({
350
349
  };
351
350
  },
352
351
  prepare(batchId, attachmentId, transactionId, transactionType, fileName) {
353
- return { payload: { batchId, attachmentId, transactionId, transactionType, fileName } };
352
+ return {
353
+ payload: {
354
+ batchId,
355
+ attachmentId,
356
+ transactionId,
357
+ transactionType,
358
+ fileName,
359
+ },
360
+ };
354
361
  },
355
362
  },
356
363
  confirmBulkUploadMatchSuccess(draft, action) {
@@ -497,7 +504,7 @@ const expenseAutomationMissingReceiptsView = (0, toolkit_1.createSlice)({
497
504
  },
498
505
  },
499
506
  fetchCompletedTransactionsSuccess(draft, action) {
500
- const { transactionIds, nextPageToken, totalCount, selectedPeriod, isInitialLoad } = action.payload;
507
+ const { transactionIds, nextPageToken, totalCount, selectedPeriod, isInitialLoad, } = action.payload;
501
508
  const periodId = (0, timePeriod_1.toMonthYearPeriodId)(selectedPeriod);
502
509
  const cacheKey = (0, exports.getCompletedTransactionsCacheKey)(periodId, draft.bulkUpload.sortKey, draft.bulkUpload.sortOrder, draft.bulkUpload.completedSubTab);
503
510
  if (isInitialLoad) {
@@ -719,7 +719,10 @@ const expenseAutomationTransactionsView = (0, toolkit_1.createSlice)({
719
719
  uploadTransactionCategorizationReceiptSuccess: {
720
720
  reducer(draft, action) {
721
721
  const { transactionPayload, selectedTab } = action.payload;
722
- draft.transactionCategorizationView[selectedTab].uploadReceiptStatusById[transactionPayload.transaction_id] = { fetchState: 'Completed', error: undefined };
722
+ draft.transactionCategorizationView[selectedTab].uploadReceiptStatusById[transactionPayload.transaction_id] = {
723
+ fetchState: 'Completed',
724
+ error: undefined,
725
+ };
723
726
  },
724
727
  prepare(selectedTab, transactionPayload, updateType = 'merge') {
725
728
  return { payload: { selectedTab, transactionPayload, updateType } };
@@ -1,7 +1,7 @@
1
1
  import { CompletionStatusType, FetchStateAndError, ID } from '../../../commonStateTypes/common';
2
+ import { SortOrder } from '../../../commonStateTypes/selectorTypes/sortOrderTypes';
2
3
  import { SelectorView } from '../../../commonStateTypes/viewAndReport/viewAndReport';
3
4
  import { SupportedTransaction } from '../../../entity/transaction/transactionState';
4
- import { SortOrder } from '../../../commonStateTypes/selectorTypes/sortOrderTypes';
5
5
  import type { BatchFileStatus, BatchListItem, BatchStatus, BulkUploadPhase, BulkUploadResultsTab, BulkUploadSortKey, CompletedSubTab, ManualSearchState, MatchSource, MissingReceiptsViewUIState } from '../types/missingReceiptsViewState';
6
6
  export interface CompletedTransactionsSelectorData {
7
7
  fetchState: FetchStateAndError;
@@ -8,9 +8,9 @@ const orderBy_1 = __importDefault(require("lodash/orderBy"));
8
8
  const timePeriod_1 = require("../../../commonStateTypes/timePeriod");
9
9
  const tenantSelector_1 = require("../../../entity/tenant/tenantSelector");
10
10
  const transactionSelector_1 = require("../../../entity/transaction/transactionSelector");
11
- const expenseAutomationViewSelectorTypes_1 = require("../selectorTypes/expenseAutomationViewSelectorTypes");
12
11
  const missingReceiptsPayload_1 = require("../payload/missingReceiptsPayload");
13
12
  const missingReceiptsViewReducer_1 = require("../reducers/missingReceiptsViewReducer");
13
+ const expenseAutomationViewSelectorTypes_1 = require("../selectorTypes/expenseAutomationViewSelectorTypes");
14
14
  const missingReceiptsViewState_1 = require("../types/missingReceiptsViewState");
15
15
  function getExpenseAutomationMissingReceiptsView(state) {
16
16
  const { expenseAutomationMissingReceiptsViewState, expenseAutomationViewState, transactionState, monthEndCloseChecksState, } = state;
@@ -56,12 +56,12 @@ function getExpenseAutomationMissingReceiptsView(state) {
56
56
  const completionStatus = missingReceiptsCompletionStatus != null
57
57
  ? missingReceiptsCompletionStatus
58
58
  : 'incomplete';
59
- const { bulkUpload, pendingMissingReceiptsTabNavigation, } = expenseAutomationMissingReceiptsViewState;
59
+ const { bulkUpload, pendingMissingReceiptsTabNavigation } = expenseAutomationMissingReceiptsViewState;
60
60
  const currentBatchStatus = bulkUpload.currentBatchId != null
61
61
  ? bulkUpload.batchStatusById[bulkUpload.currentBatchId]
62
62
  : undefined;
63
63
  const batchList = monthYearPeriodId != null
64
- ? bulkUpload.batchListByPeriod[monthYearPeriodId] ?? []
64
+ ? (bulkUpload.batchListByPeriod[monthYearPeriodId] ?? [])
65
65
  : [];
66
66
  /**
67
67
  * Primary "Unmatched" batch = first completed batch in platform list order, preferring the
@@ -110,7 +110,7 @@ function getExpenseAutomationMissingReceiptsView(state) {
110
110
  }
111
111
  switch (bulkUpload.sortKey) {
112
112
  case 'vendor':
113
- return (matchedTransaction.vendorName ?? matchedTransaction.description)?.toLowerCase() ?? null;
113
+ return ((matchedTransaction.vendorName ?? matchedTransaction.description)?.toLowerCase() ?? null);
114
114
  case 'category': {
115
115
  const line = matchedTransaction.lines?.[0];
116
116
  return line?.account?.accountName?.toLowerCase() ?? null;
@@ -302,9 +302,7 @@ function resolveBatchFile(file, batchId, transactionState) {
302
302
  filePreviewUrl: file.filePreviewUrl,
303
303
  status: file.status,
304
304
  matchedTransaction,
305
- candidates: candidates != null && candidates.length > 0
306
- ? candidates
307
- : undefined,
305
+ candidates: candidates != null && candidates.length > 0 ? candidates : undefined,
308
306
  matchSource: file.matchSource,
309
307
  };
310
308
  }
@@ -35,7 +35,12 @@ const UNMATCHED_TAB_FILE_STATUSES = ['possible_matches', 'no_match'];
35
35
  function isUnmatchedTabFileStatus(status) {
36
36
  return UNMATCHED_TAB_FILE_STATUSES.includes(status);
37
37
  }
38
- const BULK_UPLOAD_PHASES = ['idle', 'uploading', 'matching', 'completed'];
38
+ const BULK_UPLOAD_PHASES = [
39
+ 'idle',
40
+ 'uploading',
41
+ 'matching',
42
+ 'completed',
43
+ ];
39
44
  const toBulkUploadPhase = (v) => (0, stringToUnion_1.stringToUnion)(v.trim().toLowerCase(), BULK_UPLOAD_PHASES);
40
45
  exports.toBulkUploadPhase = toBulkUploadPhase;
41
46
  const BULK_UPLOAD_RESULTS_TABS = ['completed', 'unmatched'];
@@ -8,6 +8,7 @@ import { RootState } from '../../reducer';
8
8
  import { BalanceSheetReport } from '../balanceSheet/balanceSheetSelectorTypes';
9
9
  import { CashFlowReport } from '../cashFlow/cashFlowSelectorTypes';
10
10
  import { ProfitAndLossReport } from '../profitAndLoss/profitAndLossSelector';
11
+ import { ProfitAndLossByClassHorizontalReport } from '../profitAndLossClassesView/profitAndLossClassesByClassHorizontalSelectorTypes';
11
12
  import { ProfitAndLossClassesViewReport } from '../profitAndLossClassesView/profitAndLossClassesViewSelectorTypes';
12
13
  export interface FinanceStatementReport extends SelectorReport {
13
14
  allTimeframeTicks: TimeframeTickWithMetaData[];
@@ -18,6 +19,7 @@ export interface FinanceStatementReport extends SelectorReport {
18
19
  downloadState: FetchState;
19
20
  filter: COABalancesFilter;
20
21
  isCompareModeOn: boolean;
22
+ profitAndLossByClassHorizontalReport: ProfitAndLossByClassHorizontalReport;
21
23
  profitAndLossByClassReport: ProfitAndLossClassesViewReport;
22
24
  profitAndLossReport: ProfitAndLossReport;
23
25
  reportFormat: ReportFormat;
@@ -11,6 +11,7 @@ const thisPeriodHelpers_1 = require("../../commonStateTypes/viewAndReport/thisPe
11
11
  const balanceSheetSelector_1 = require("../balanceSheet/balanceSheetSelector");
12
12
  const cashFlowSelector_1 = require("../cashFlow/cashFlowSelector");
13
13
  const profitAndLossSelector_1 = require("../profitAndLoss/profitAndLossSelector");
14
+ const profitAndLossClassesByClassHorizontalSelector_1 = require("../profitAndLossClassesView/profitAndLossClassesByClassHorizontalSelector");
14
15
  const profitAndLossClassesViewSelector_1 = require("../profitAndLossClassesView/profitAndLossClassesViewSelector");
15
16
  function getDateRangeForFinanceReports(profitAndLossState, balanceSheetState, cashFlowState) {
16
17
  const datesSorted = [
@@ -63,6 +64,9 @@ exports.getFinanceStatement = (0, toolkit_1.createSelector)((state) => state.acc
63
64
  const profitAndLossReport = (0, profitAndLossSelector_1.getProfitAndLossReport)(filter, undefined, accountState, accountGroupState, sectionAccountsViewState, profitAndLossState, forecastState);
64
65
  const classFilter = (0, coaBalancesFilterClassesView_1.newBalancesFilterClassesView)(filter.firstMonthOfFY, filter.timeframe, filter.balancesRange.numberOfPeriods, filter.balancesRange.thisPeriod, filter.balancesRange.orderBy, filter.additionalBalances, profitAndLossClassesViewState.uiState.classesToFilterOut);
65
66
  const profitAndLossByClassReport = (0, profitAndLossClassesViewSelector_1.getProfitAndLossClassesView)(classFilter, accountState, classState, sectionsState, profitAndLossClassesViewState);
67
+ const profitAndLossByClassHorizontalReport = profitAndLossClassesViewState.uiState.classViewLayout === 'horizontal'
68
+ ? (0, profitAndLossClassesByClassHorizontalSelector_1.getProfitAndLossClassesHorizontalView)(classFilter, accountState, classState, sectionsState, profitAndLossClassesViewState, thisPeriod)
69
+ : (0, profitAndLossClassesByClassHorizontalSelector_1.getProfitAndLossClassesHorizontalView)(classFilter, accountState, classState, sectionsState, profitAndLossClassesViewState, undefined);
66
70
  const balanceSheetReport = (0, balanceSheetSelector_1.getBalanceSheet)(filter, accountState, accountGroupState, sectionAccountsViewState, balanceSheetState);
67
71
  const cashFlowReport = (0, cashFlowSelector_1.getCashFlow)(filter, accountState, accountGroupState, sectionAccountsViewState, cashFlowState);
68
72
  const allReports = [
@@ -110,6 +114,7 @@ exports.getFinanceStatement = (0, toolkit_1.createSelector)((state) => state.acc
110
114
  dataAvailable,
111
115
  profitAndLossReport,
112
116
  profitAndLossByClassReport,
117
+ profitAndLossByClassHorizontalReport,
113
118
  balanceSheetReport,
114
119
  cashFlowReport,
115
120
  filter,
@@ -0,0 +1,13 @@
1
+ import { COABalancesFilterClassesView } from '../../commonStateTypes/coaBalance/coaBalancesFilterClassesView';
2
+ import { AccountState } from '../../entity/account/accountState';
3
+ import { ClassState } from '../../entity/class/classState';
4
+ import { SectionClassesViewStateV2 } from '../../entity/sectionClassesViewV2/sectionClassesViewState';
5
+ import { ZeniDate } from '../../zeniDayJS';
6
+ import { ProfitAndLossByClassHorizontalReport } from './profitAndLossClassesByClassHorizontalSelectorTypes';
7
+ import { PandLReportViewCalculatedSectionID, PandLReportViewSectionID } from './profitAndLossClassesViewSelectorTypes';
8
+ import { ProfitAndLossClassesViewState } from './profitAndLossClassesViewState';
9
+ export declare const profitAndLossHorizontalIdsInOrder: (PandLReportViewSectionID | PandLReportViewCalculatedSectionID)[];
10
+ export declare const getProfitAndLossClassesHorizontalView: (filter: COABalancesFilterClassesView, accountState: AccountState, classState: ClassState, sectionsState: SectionClassesViewStateV2, profitAndLossClassesViewState: ProfitAndLossClassesViewState, selectedPeriod: {
11
+ endDate: ZeniDate;
12
+ startDate: ZeniDate;
13
+ } | undefined) => ProfitAndLossByClassHorizontalReport;
@@ -0,0 +1,286 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getProfitAndLossClassesHorizontalView = exports.profitAndLossHorizontalIdsInOrder = void 0;
4
+ const amount_1 = require("../../commonStateTypes/amount");
5
+ const sectionClassesViewSelector_1 = require("../../entity/sectionClassesViewV2/sectionClassesViewSelector");
6
+ const profitAndLossClassesViewSelectorTypes_1 = require("./profitAndLossClassesViewSelectorTypes");
7
+ const DEFAULT_CURRENCY = { currencyCode: 'USD', currencySymbol: '$' };
8
+ exports.profitAndLossHorizontalIdsInOrder = [
9
+ 'income',
10
+ 'cogs',
11
+ 'grossProfit',
12
+ 'expenses',
13
+ 'earningsBeforeInterestAndTax',
14
+ 'otherIncome',
15
+ 'otherExpenses',
16
+ 'netOtherIncome',
17
+ 'netIncome',
18
+ ];
19
+ const SECTION_TITLES = {
20
+ income: 'Income',
21
+ cogs: 'Cost of Goods Sold',
22
+ grossProfit: 'Gross Profit',
23
+ expenses: 'Expenses',
24
+ earningsBeforeInterestAndTax: 'Net Operating Income',
25
+ otherIncome: 'Other Income',
26
+ otherExpenses: 'Other Expenses',
27
+ netOtherIncome: 'Net Other Income',
28
+ netIncome: 'Net Income',
29
+ };
30
+ const getProfitAndLossClassesHorizontalView = (filter, accountState, classState, sectionsState, profitAndLossClassesViewState, selectedPeriod) => {
31
+ const fetchState = reduceFetchState([
32
+ profitAndLossClassesViewState.fetchState.month,
33
+ profitAndLossClassesViewState.fetchState.quarter,
34
+ profitAndLossClassesViewState.fetchState.year,
35
+ ]);
36
+ const reportId = 'profit_and_loss_by_classes';
37
+ // Build class columns from the class hierarchy (top-level only)
38
+ const classColumns = buildClassColumns(profitAndLossClassesViewState.classHierarchy, profitAndLossClassesViewState.uiState.classesToFilterOut);
39
+ const sections = {};
40
+ const calculatedSections = {};
41
+ if (fetchState.fetchState === 'Completed' && selectedPeriod != null) {
42
+ // Get section reports using existing selector infrastructure
43
+ const sectionReports = {};
44
+ profitAndLossClassesViewSelectorTypes_1.ALL_PANDL_REPORT_VIEW_SECTION_IDS.forEach((sectionId) => {
45
+ sectionReports[sectionId] = (0, sectionClassesViewSelector_1.getSectionClassesViewReport)(accountState, classState, sectionsState, { reportId, sectionId }, filter);
46
+ });
47
+ // Pivot each section from class→account to account→class
48
+ const sectionIds = [
49
+ 'income',
50
+ 'cogs',
51
+ 'expenses',
52
+ 'otherIncome',
53
+ 'otherExpenses',
54
+ ];
55
+ sectionIds.forEach((sectionId) => {
56
+ const sectionReport = sectionReports[sectionId];
57
+ if (sectionReport != null) {
58
+ sections[sectionId] = pivotSectionToHorizontal(sectionReport, classColumns, selectedPeriod, SECTION_TITLES[sectionId] ?? sectionId);
59
+ }
60
+ });
61
+ // Calculate derived sections
62
+ const incomeSection = sections['income'];
63
+ const cogsSection = sections['cogs'];
64
+ const expensesSection = sections['expenses'];
65
+ const otherIncomeSection = sections['otherIncome'];
66
+ const otherExpensesSection = sections['otherExpenses'];
67
+ // Gross Profit = Income - COGS
68
+ if (incomeSection != null && cogsSection != null) {
69
+ calculatedSections['grossProfit'] = computeCalculatedSection(incomeSection.classTotals, cogsSection.classTotals, incomeSection.sectionTotal, cogsSection.sectionTotal, 'subtract', SECTION_TITLES['grossProfit']);
70
+ }
71
+ // EBIT = Gross Profit - Expenses
72
+ const grossProfit = calculatedSections['grossProfit'];
73
+ if (grossProfit != null && expensesSection != null) {
74
+ calculatedSections['earningsBeforeInterestAndTax'] =
75
+ computeCalculatedSection(grossProfit.classTotals, expensesSection.classTotals, grossProfit.sectionTotal, expensesSection.sectionTotal, 'subtract', SECTION_TITLES['earningsBeforeInterestAndTax']);
76
+ }
77
+ // Net Other Income = Other Income - Other Expenses
78
+ if (otherIncomeSection != null && otherExpensesSection != null) {
79
+ calculatedSections['netOtherIncome'] = computeCalculatedSection(otherIncomeSection.classTotals, otherExpensesSection.classTotals, otherIncomeSection.sectionTotal, otherExpensesSection.sectionTotal, 'subtract', SECTION_TITLES['netOtherIncome']);
80
+ }
81
+ // Net Income = EBIT + Net Other Income
82
+ const ebit = calculatedSections['earningsBeforeInterestAndTax'];
83
+ const netOtherIncome = calculatedSections['netOtherIncome'];
84
+ if (ebit != null && netOtherIncome != null) {
85
+ calculatedSections['netIncome'] = computeCalculatedSection(ebit.classTotals, netOtherIncome.classTotals, ebit.sectionTotal, netOtherIncome.sectionTotal, 'add', SECTION_TITLES['netIncome']);
86
+ }
87
+ }
88
+ // Build synthetic header COABalances for rendering class names as column headers
89
+ const headerBalances = buildHeaderBalances(classColumns, filter.timeframe, selectedPeriod);
90
+ return {
91
+ reportId,
92
+ reportTitle: 'Profit and Loss by Class',
93
+ firstMonthOfFY: profitAndLossClassesViewState.firstMonthOfFY,
94
+ fetchState: fetchState.fetchState,
95
+ bookCloseDate: profitAndLossClassesViewState.bookCloseDate,
96
+ dataAvailable: profitAndLossClassesViewState.dataAvailable,
97
+ selectedPeriod,
98
+ classColumns,
99
+ sections,
100
+ calculatedSections,
101
+ idsInOrder: exports.profitAndLossHorizontalIdsInOrder,
102
+ uiState: profitAndLossClassesViewState.uiState,
103
+ headerBalances,
104
+ };
105
+ };
106
+ exports.getProfitAndLossClassesHorizontalView = getProfitAndLossClassesHorizontalView;
107
+ /**
108
+ * Build column definitions from class hierarchy (top-level classes only).
109
+ */
110
+ function buildClassColumns(classHierarchy, classesToFilterOut) {
111
+ return classHierarchy
112
+ .filter((c) => !classesToFilterOut.some((filteredId) => filteredId[0] === c.classId[0]))
113
+ .map((c) => ({
114
+ classId: c.classId[0],
115
+ className: c.className,
116
+ nestedClassId: c.classId,
117
+ qboId: c.qboId,
118
+ }));
119
+ }
120
+ /**
121
+ * Pivot a section from class→account→balances to account→class→balance (single period).
122
+ */
123
+ function pivotSectionToHorizontal(section, classColumns, selectedPeriod, title) {
124
+ // Map: accountId -> { accountName, classId -> balance }
125
+ const accountMap = new Map();
126
+ // Also track class totals
127
+ const classTotalMap = new Map();
128
+ classColumns.forEach((col) => classTotalMap.set(col.classId, 0));
129
+ // Iterate over each class in the section
130
+ section.classes.forEach((classReport) => {
131
+ const classId = classReport.id[0];
132
+ // Only process classes that are in our columns
133
+ if (!classColumns.some((col) => col.classId === classId)) {
134
+ return;
135
+ }
136
+ // Get the class-level balance for this period (total for the class in this section)
137
+ const classBalance = findBalanceForPeriod(classReport.childrenBalancesTotal.balances, selectedPeriod);
138
+ classTotalMap.set(classId, classBalance);
139
+ // Iterate through accounts in this class
140
+ collectAccountsFromClass(classReport, classId, selectedPeriod, accountMap);
141
+ });
142
+ // Build account rows
143
+ const accounts = [];
144
+ accountMap.forEach((accountData, accountId) => {
145
+ const classBalances = classColumns.map((col) => ({
146
+ classId: col.classId,
147
+ balance: (0, amount_1.toAmountWC)(accountData.classBalances.get(col.classId) ?? 0, DEFAULT_CURRENCY),
148
+ }));
149
+ const rowTotal = classBalances.reduce((sum, cb) => sum + cb.balance.amount, 0);
150
+ accounts.push({
151
+ accountId,
152
+ accountName: accountData.accountName,
153
+ classBalances,
154
+ rowTotal: (0, amount_1.toAmountWC)(rowTotal, DEFAULT_CURRENCY),
155
+ });
156
+ });
157
+ // Build class totals
158
+ const classTotals = classColumns.map((col) => ({
159
+ classId: col.classId,
160
+ balance: (0, amount_1.toAmountWC)(classTotalMap.get(col.classId) ?? 0, DEFAULT_CURRENCY),
161
+ }));
162
+ const sectionTotal = classTotals.reduce((sum, ct) => sum + ct.balance.amount, 0);
163
+ return {
164
+ title,
165
+ accounts,
166
+ classTotals,
167
+ sectionTotal: (0, amount_1.toAmountWC)(sectionTotal, DEFAULT_CURRENCY),
168
+ };
169
+ }
170
+ /**
171
+ * Recursively collect account balances from a NestedClassReportV2 into accountMap.
172
+ */
173
+ function collectAccountsFromClass(classReport, classId, selectedPeriod, accountMap) {
174
+ // Process accounts directly on this class
175
+ classReport.class.accounts.forEach((nestedAccount) => {
176
+ collectAccountsFromNestedAccount(nestedAccount, classId, selectedPeriod, accountMap);
177
+ });
178
+ }
179
+ /**
180
+ * Recursively collect accounts from NestedAccountReport (handles sub-accounts).
181
+ */
182
+ function collectAccountsFromNestedAccount(nestedAccount, classId, selectedPeriod, accountMap) {
183
+ const accountId = nestedAccount.account.accountId;
184
+ const accountName = nestedAccount.account.accountName;
185
+ const balance = findBalanceForPeriod(nestedAccount.account.balancesInTimeframe.balances, selectedPeriod);
186
+ if (!accountMap.has(accountId)) {
187
+ accountMap.set(accountId, {
188
+ accountName,
189
+ classBalances: new Map(),
190
+ });
191
+ }
192
+ const accountData = accountMap.get(accountId);
193
+ if (accountData == null) {
194
+ return;
195
+ }
196
+ const existing = accountData.classBalances.get(classId) ?? 0;
197
+ accountData.classBalances.set(classId, existing + balance);
198
+ // Process sub-accounts
199
+ nestedAccount.children.forEach((child) => {
200
+ collectAccountsFromNestedAccount(child, classId, selectedPeriod, accountMap);
201
+ });
202
+ }
203
+ /**
204
+ * Find the balance amount for a specific period from a COABalance array.
205
+ */
206
+ function findBalanceForPeriod(balances, selectedPeriod) {
207
+ for (const bal of balances) {
208
+ const b = bal;
209
+ if (b.startDate != null && b.endDate != null) {
210
+ if (b.startDate.isSame(selectedPeriod.startDate, 'day') &&
211
+ b.endDate.isSame(selectedPeriod.endDate, 'day')) {
212
+ return b.balance?.amount ?? 0;
213
+ }
214
+ }
215
+ }
216
+ return 0;
217
+ }
218
+ /**
219
+ * Compute a calculated section by adding or subtracting two sets of class totals.
220
+ */
221
+ function computeCalculatedSection(aTotals, bTotals, aTotal, bTotal, operation, title) {
222
+ const classTotals = aTotals.map((a) => {
223
+ const bMatch = bTotals.find((b) => b.classId === a.classId);
224
+ const bAmount = bMatch?.balance.amount ?? 0;
225
+ const resultAmount = operation === 'add'
226
+ ? a.balance.amount + bAmount
227
+ : a.balance.amount - bAmount;
228
+ return {
229
+ classId: a.classId,
230
+ balance: (0, amount_1.toAmountWC)(resultAmount, DEFAULT_CURRENCY),
231
+ };
232
+ });
233
+ const sectionTotalAmount = operation === 'add'
234
+ ? aTotal.amount + bTotal.amount
235
+ : aTotal.amount - bTotal.amount;
236
+ return {
237
+ title,
238
+ classTotals,
239
+ sectionTotal: (0, amount_1.toAmountWC)(sectionTotalAmount, DEFAULT_CURRENCY),
240
+ };
241
+ }
242
+ /**
243
+ * Build synthetic COABalance array for header rendering.
244
+ * One entry per class column + one for "Total".
245
+ */
246
+ function buildHeaderBalances(classColumns, timeframe, selectedPeriod) {
247
+ if (selectedPeriod == null) {
248
+ return [];
249
+ }
250
+ const { endDate, startDate } = selectedPeriod;
251
+ const balances = classColumns.map((col) => ({
252
+ balance: (0, amount_1.toAmountWC)(0, DEFAULT_CURRENCY),
253
+ balanceTimeFrame: timeframe,
254
+ endDate,
255
+ startDate,
256
+ type: 'default',
257
+ label: col.className,
258
+ }));
259
+ // Add a "Total" column
260
+ balances.push({
261
+ balance: (0, amount_1.toAmountWC)(0, DEFAULT_CURRENCY),
262
+ balanceTimeFrame: timeframe,
263
+ endDate,
264
+ startDate,
265
+ type: 'default',
266
+ label: 'Total',
267
+ });
268
+ return balances;
269
+ }
270
+ function reduceFetchState(fetchStateList) {
271
+ const isAnyProgress = fetchStateList.some((s) => s.fetchState === 'In-Progress');
272
+ const isAllCompleted = fetchStateList.every((s) => s.fetchState === 'Completed');
273
+ const isAnyError = fetchStateList.some((s) => s.fetchState === 'Error');
274
+ const error = fetchStateList.find((s) => s.error != null)?.error;
275
+ let fetchState = 'Not-Started';
276
+ if (isAnyProgress) {
277
+ fetchState = 'In-Progress';
278
+ }
279
+ else if (isAllCompleted) {
280
+ fetchState = 'Completed';
281
+ }
282
+ else if (isAnyError) {
283
+ fetchState = 'Error';
284
+ }
285
+ return { fetchState, error };
286
+ }
@@ -0,0 +1,57 @@
1
+ import { Amount } from '../../commonStateTypes/amount';
2
+ import { NestedClassID } from '../../commonStateTypes/classesView/nestedClassID';
3
+ import { COABalance } from '../../commonStateTypes/coaBalance/coaBalance';
4
+ import { FetchState, ID } from '../../commonStateTypes/common';
5
+ import { DataAvailable } from '../../commonStateTypes/dataAvailable';
6
+ import { Month } from '../../commonStateTypes/timePeriod';
7
+ import { ReportID } from '../../commonStateTypes/viewAndReport/viewAndReport';
8
+ import { ZeniDate } from '../../zeniDayJS';
9
+ import { PandLReportViewCalculatedSectionID, PandLReportViewSectionID } from './profitAndLossClassesViewSelectorTypes';
10
+ import { ProfitAndLossClassesViewUIState } from './profitAndLossClassesViewState';
11
+ export interface ClassColumnDefinition {
12
+ classId: string;
13
+ className: string;
14
+ nestedClassId: NestedClassID;
15
+ qboId?: ID;
16
+ }
17
+ export interface HorizontalClassBalance {
18
+ balance: Amount;
19
+ classId: string;
20
+ }
21
+ export interface HorizontalAccountRow {
22
+ accountId: string;
23
+ accountName: string;
24
+ classBalances: HorizontalClassBalance[];
25
+ rowTotal: Amount;
26
+ }
27
+ export interface HorizontalSectionReport {
28
+ accounts: HorizontalAccountRow[];
29
+ classTotals: HorizontalClassBalance[];
30
+ sectionTotal: Amount;
31
+ title: string;
32
+ }
33
+ export interface HorizontalCalculatedSection {
34
+ classTotals: HorizontalClassBalance[];
35
+ sectionTotal: Amount;
36
+ title: string;
37
+ }
38
+ export interface ProfitAndLossByClassHorizontalReport {
39
+ calculatedSections: Partial<Record<PandLReportViewCalculatedSectionID, HorizontalCalculatedSection>>;
40
+ classColumns: ClassColumnDefinition[];
41
+ fetchState: FetchState;
42
+ firstMonthOfFY: Month;
43
+ /** Synthetic COABalance array for header rendering — one per class column + total */
44
+ headerBalances: COABalance[];
45
+ idsInOrder: (PandLReportViewSectionID | PandLReportViewCalculatedSectionID)[];
46
+ reportId: ReportID;
47
+ reportTitle: string;
48
+ sections: Partial<Record<PandLReportViewSectionID, HorizontalSectionReport>>;
49
+ selectedPeriod: {
50
+ endDate: ZeniDate;
51
+ startDate: ZeniDate;
52
+ label?: string;
53
+ } | undefined;
54
+ uiState: ProfitAndLossClassesViewUIState;
55
+ bookCloseDate?: ZeniDate;
56
+ dataAvailable?: DataAvailable;
57
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -2,7 +2,7 @@ import { NestedClassID } from '../../commonStateTypes/classesView/nestedClassID'
2
2
  import { AccountViewMode, ID } from '../../commonStateTypes/common';
3
3
  import { ZeniAPIStatus, ZeniReportData } from '../../responsePayload';
4
4
  import { ProfitAndLossClassesViewPayload } from './profitAndLossClassesViewPayload';
5
- import { ProfitAndLossClassesViewState } from './profitAndLossClassesViewState';
5
+ import { ClassViewLayout, ProfitAndLossClassesViewState } from './profitAndLossClassesViewState';
6
6
  export declare const initialState: ProfitAndLossClassesViewState;
7
7
  export declare const fetchProfitAndLossClassesView: import("@reduxjs/toolkit").ActionCreatorWithPreparedPayload<[cacheOverride?: boolean | undefined], {
8
8
  cacheOverride: boolean;
@@ -30,6 +30,8 @@ export declare const fetchProfitAndLossClassesView: import("@reduxjs/toolkit").A
30
30
  };
31
31
  }, "profitAndLossClassesView/updateProfitAndLossClassesViewUIState">, updateAccountViewMode: import("@reduxjs/toolkit").ActionCreatorWithPayload<{
32
32
  viewMode: AccountViewMode;
33
- }, "profitAndLossClassesView/updateAccountViewMode">, resetProfitAndLossClassesNodeCollapseState: import("@reduxjs/toolkit").ActionCreatorWithoutPayload<"profitAndLossClassesView/resetProfitAndLossClassesNodeCollapseState">, clearProfitAndLossClassesView: import("@reduxjs/toolkit").ActionCreatorWithoutPayload<"profitAndLossClassesView/clearProfitAndLossClassesView">;
33
+ }, "profitAndLossClassesView/updateAccountViewMode">, updateClassViewLayout: import("@reduxjs/toolkit").ActionCreatorWithPayload<{
34
+ classViewLayout: ClassViewLayout;
35
+ }, "profitAndLossClassesView/updateClassViewLayout">, resetProfitAndLossClassesNodeCollapseState: import("@reduxjs/toolkit").ActionCreatorWithoutPayload<"profitAndLossClassesView/resetProfitAndLossClassesNodeCollapseState">, clearProfitAndLossClassesView: import("@reduxjs/toolkit").ActionCreatorWithoutPayload<"profitAndLossClassesView/clearProfitAndLossClassesView">;
34
36
  declare const _default: import("redux").Reducer<ProfitAndLossClassesViewState>;
35
37
  export default _default;