@zeniai/client-epic-state 5.2.32 → 5.2.34-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 (86) hide show
  1. package/lib/commonPayloadTypes/classesView/reportByClassPayloadV2.d.ts +13 -1
  2. package/lib/entity/notificationRegistry/notificationRegistryReducer.d.ts +2 -1
  3. package/lib/entity/notificationRegistry/notificationRegistryReducer.js +20 -9
  4. package/lib/entity/notificationRegistry/notificationRegistrySelector.d.ts +2 -1
  5. package/lib/entity/notificationRegistry/notificationRegistrySelector.js +7 -1
  6. package/lib/entity/notificationRegistry/notificationRegistryState.d.ts +5 -0
  7. package/lib/entity/notificationRegistry/notificationRegistryState.js +7 -1
  8. package/lib/entity/sectionClassesViewV2/sectionClassesViewEpic.js +10 -7
  9. package/lib/entity/sectionClassesViewV2/sectionClassesViewReducer.d.ts +3 -3
  10. package/lib/entity/sectionProjectView/sectionProjectViewEpic.js +6 -2
  11. package/lib/entity/sectionProjectView/sectionProjectViewReducer.d.ts +3 -3
  12. package/lib/entity/sectionProjectView/sectionProjectViewReducer.js +6 -3
  13. package/lib/epic.js +12 -0
  14. package/lib/esm/entity/class/classReducer.js +1 -1
  15. package/lib/esm/entity/notificationRegistry/notificationRegistryReducer.js +20 -9
  16. package/lib/esm/entity/notificationRegistry/notificationRegistrySelector.js +5 -0
  17. package/lib/esm/entity/notificationRegistry/notificationRegistryState.js +5 -0
  18. package/lib/esm/entity/sectionClassesViewV2/sectionClassesViewEpic.js +10 -7
  19. package/lib/esm/entity/sectionProjectView/sectionProjectViewEpic.js +6 -2
  20. package/lib/esm/entity/sectionProjectView/sectionProjectViewReducer.js +7 -4
  21. package/lib/esm/epic.js +12 -0
  22. package/lib/esm/index.js +6 -6
  23. package/lib/esm/view/aiCfoView/aiCfoViewReducer.js +268 -1
  24. package/lib/esm/view/aiCfoView/aiCfoViewSelector.js +50 -0
  25. package/lib/esm/view/aiCfoView/epics/deleteRoutineEpic.js +35 -0
  26. package/lib/esm/view/aiCfoView/epics/fetchRoutineRunsEpic.js +37 -0
  27. package/lib/esm/view/aiCfoView/epics/fetchRoutinesEpic.js +37 -0
  28. package/lib/esm/view/aiCfoView/epics/runRoutineEpic.js +34 -0
  29. package/lib/esm/view/aiCfoView/epics/saveRoutineEpic.js +65 -0
  30. package/lib/esm/view/aiCfoView/epics/setRoutineEnabledEpic.js +28 -0
  31. package/lib/esm/view/aiCfoView/epics/toRoutine.js +64 -0
  32. package/lib/esm/view/aiCfoView/epics/toRoutineRun.js +42 -0
  33. package/lib/esm/view/expenseAutomationView/selectors/fluxAnalysisViewSelector.js +12 -10
  34. package/lib/esm/view/expenseAutomationView/selectors/jeSchedulesViewSelector.js +3 -11
  35. package/lib/esm/view/expenseAutomationView/selectors/missingReceiptsSelector.js +10 -11
  36. package/lib/esm/view/invoicing/editInvoicingCustomerDetailView/editInvoicingCustomerDetailViewPayload.js +4 -0
  37. package/lib/esm/view/notificationPreferencesView/epics/saveNotificationPreferencesEpic.js +20 -7
  38. package/lib/esm/view/notificationPreferencesView/notificationPreferencesViewReducer.js +43 -17
  39. package/lib/esm/view/notificationPreferencesView/notificationPreferencesViewSelector.js +24 -4
  40. package/lib/esm/view/settingsView/epic/fetchNotificationSettingsEpic.js +12 -2
  41. package/lib/index.d.ts +8 -8
  42. package/lib/index.js +63 -41
  43. package/lib/view/aiCfoView/aiCfoViewPayload.d.ts +90 -0
  44. package/lib/view/aiCfoView/aiCfoViewReducer.d.ts +164 -123
  45. package/lib/view/aiCfoView/aiCfoViewReducer.js +270 -3
  46. package/lib/view/aiCfoView/aiCfoViewSelector.d.ts +13 -1
  47. package/lib/view/aiCfoView/aiCfoViewSelector.js +55 -0
  48. package/lib/view/aiCfoView/aiCfoViewState.d.ts +157 -0
  49. package/lib/view/aiCfoView/epics/deleteRoutineEpic.d.ts +18 -0
  50. package/lib/view/aiCfoView/epics/deleteRoutineEpic.js +39 -0
  51. package/lib/view/aiCfoView/epics/fetchRoutineRunsEpic.d.ts +13 -0
  52. package/lib/view/aiCfoView/epics/fetchRoutineRunsEpic.js +41 -0
  53. package/lib/view/aiCfoView/epics/fetchRoutinesEpic.d.ts +7 -0
  54. package/lib/view/aiCfoView/epics/fetchRoutinesEpic.js +41 -0
  55. package/lib/view/aiCfoView/epics/runRoutineEpic.d.ts +14 -0
  56. package/lib/view/aiCfoView/epics/runRoutineEpic.js +38 -0
  57. package/lib/view/aiCfoView/epics/saveRoutineEpic.d.ts +13 -0
  58. package/lib/view/aiCfoView/epics/saveRoutineEpic.js +69 -0
  59. package/lib/view/aiCfoView/epics/setRoutineEnabledEpic.d.ts +15 -0
  60. package/lib/view/aiCfoView/epics/setRoutineEnabledEpic.js +32 -0
  61. package/lib/view/aiCfoView/epics/toRoutine.d.ts +17 -0
  62. package/lib/view/aiCfoView/epics/toRoutine.js +69 -0
  63. package/lib/view/aiCfoView/epics/toRoutineRun.d.ts +12 -0
  64. package/lib/view/aiCfoView/epics/toRoutineRun.js +47 -0
  65. package/lib/view/cashFlowClassesView/cashFlowClassesViewPayload.d.ts +4 -4
  66. package/lib/view/cashFlowProjectView/cashFlowProjectViewPayload.d.ts +4 -4
  67. package/lib/view/expenseAutomationView/selectors/fluxAnalysisViewSelector.js +12 -10
  68. package/lib/view/expenseAutomationView/selectors/jeSchedulesViewSelector.js +3 -11
  69. package/lib/view/expenseAutomationView/selectors/missingReceiptsSelector.js +10 -11
  70. package/lib/view/invoicing/editInvoicingCustomerDetailView/editInvoicingCustomerDetailViewPayload.d.ts +1 -11
  71. package/lib/view/invoicing/editInvoicingCustomerDetailView/editInvoicingCustomerDetailViewPayload.js +4 -0
  72. package/lib/view/invoicing/editInvoicingCustomerDetailView/editInvoicingCustomerDetailViewReducer.d.ts +1 -2
  73. package/lib/view/invoicing/editInvoicingCustomerDetailView/editInvoicingCustomerDetailViewSelector.d.ts +1 -2
  74. package/lib/view/invoicing/editInvoicingCustomerDetailView/editInvoicingCustomerDetailViewState.d.ts +15 -1
  75. package/lib/view/invoicing/invoicingCustomerListView/invoicingCustomerListViewReducer.d.ts +1 -1
  76. package/lib/view/invoicing/invoicingCustomerListView/invoicingCustomerListViewState.d.ts +1 -1
  77. package/lib/view/notificationPreferencesView/epics/saveNotificationPreferencesEpic.js +19 -6
  78. package/lib/view/notificationPreferencesView/notificationPreferencesViewReducer.d.ts +8 -5
  79. package/lib/view/notificationPreferencesView/notificationPreferencesViewReducer.js +43 -17
  80. package/lib/view/notificationPreferencesView/notificationPreferencesViewSelector.d.ts +4 -0
  81. package/lib/view/notificationPreferencesView/notificationPreferencesViewSelector.js +30 -5
  82. package/lib/view/notificationPreferencesView/notificationPreferencesViewState.d.ts +4 -3
  83. package/lib/view/profitAndLossProjectView/profitAndLossProjectViewPayload.d.ts +12 -0
  84. package/lib/view/settingsView/epic/fetchNotificationSettingsEpic.js +12 -2
  85. package/lib/view/settingsView/settingsViewPayload.d.ts +2 -0
  86. package/package.json +1 -1
@@ -18,8 +18,20 @@ interface ClassCOABalanceGroupedPayloadByClassIdV2 {
18
18
  export interface SectionByClassCOABalanceGroupedPayloadV2 extends COABalanceGroupedPayloadV2 {
19
19
  classes: ClassCOABalanceGroupedPayloadByClassIdV2;
20
20
  }
21
+ interface CalculatedClassCOABalanceGroupedPayloadByClassIdV2 {
22
+ [class_Id: string]: COABalanceGroupedPayloadV2;
23
+ }
24
+ /**
25
+ * A section whose per-class entries carry a time series and nothing else: the
26
+ * backend fills the account-wise map only for the account sections, so the
27
+ * calculated sections (net income, the operating total, change in cash) are
28
+ * grouped by class with no accounts underneath.
29
+ */
30
+ export interface CalculatedSectionByClassCOABalanceGroupedPayloadV2 extends COABalanceGroupedPayloadV2 {
31
+ classes: CalculatedClassCOABalanceGroupedPayloadByClassIdV2;
32
+ }
21
33
  export interface ClassMetadataWithBalancesPayloadV2 {
22
- classBalances: ClassCOABalanceGroupedPayloadV2;
34
+ classBalances: COABalanceGroupedPayloadV2;
23
35
  classMetadata: ClassMetadataPayload;
24
36
  nestedAccountIds: NestedAccountIDHierarchy[];
25
37
  nestedClassId: NestedClassID;
@@ -1,9 +1,10 @@
1
1
  import { NotificationRegistryPayload } from './notificationRegistryPayload';
2
- import { NotificationRegistryState } from './notificationRegistryState';
2
+ import { NotificationDomain, NotificationRegistryState } from './notificationRegistryState';
3
3
  export declare const initialState: NotificationRegistryState;
4
4
  export interface UpdateNotificationRegistryPayload {
5
5
  registry: NotificationRegistryPayload | undefined;
6
6
  registryEpochAtDispatch: number;
7
+ registriesByDomain?: Partial<Record<NotificationDomain, NotificationRegistryPayload | undefined>>;
7
8
  }
8
9
  export declare const updateNotificationRegistry: import("@reduxjs/toolkit").ActionCreatorWithPayload<UpdateNotificationRegistryPayload, "notificationRegistry/updateNotificationRegistry">;
9
10
  declare const _default: import("redux").Reducer<NotificationRegistryState>;
@@ -4,6 +4,7 @@ exports.updateNotificationRegistry = exports.initialState = void 0;
4
4
  const toolkit_1 = require("@reduxjs/toolkit");
5
5
  const rootActions_1 = require("../../rootActions");
6
6
  const notificationRegistryPayload_1 = require("./notificationRegistryPayload");
7
+ const notificationRegistryState_1 = require("./notificationRegistryState");
7
8
  exports.initialState = { registryEpoch: 0 };
8
9
  const notificationRegistry = (0, toolkit_1.createSlice)({
9
10
  name: 'notificationRegistry',
@@ -22,16 +23,24 @@ const notificationRegistry = (0, toolkit_1.createSlice)({
22
23
  // `task_checklist_registry` CLEARS so tenant B doesn't inherit A's
23
24
  // Task Checklist section. Guarded by the epoch check above so a stale
24
25
  // absent-response can't wipe a fresh registry.
25
- if (action.payload.registry == null) {
26
- draft.registry = undefined;
27
- return;
26
+ const { registriesByDomain } = action.payload;
27
+ // Absent `registriesByDomain` means a pre-domain caller: treat `registry`
28
+ // as the task_checklist snapshot so their behaviour is unchanged.
29
+ const snapshots = registriesByDomain ?? { task_checklist: action.payload.registry };
30
+ draft.registryByDomain = {};
31
+ for (const domain of notificationRegistryState_1.NOTIFICATION_DOMAINS) {
32
+ const payload = snapshots[domain];
33
+ // Full-replace per domain, and an absent snapshot CLEARS that domain so
34
+ // tenant B never inherits A's section. The gates are independent
35
+ // server-side, so one domain clearing must not touch the other.
36
+ if (payload != null) {
37
+ draft.registryByDomain[domain] =
38
+ (0, notificationRegistryPayload_1.mapNotificationRegistryPayloadToRegistry)(payload);
39
+ }
28
40
  }
29
- // Full-replace contract: the registry payload is atomic — every fetch
30
- // returns the complete group/event tree. If the backend ever ships a
31
- // partial registry response, this write MUST switch to a merge (mirror
32
- // the preferences slice's `mergePreferences`). Do not assume the
33
- // current payload is partial without also updating this reducer.
34
- draft.registry = (0, notificationRegistryPayload_1.mapNotificationRegistryPayloadToRegistry)(action.payload.registry);
41
+ // Mirrored for consumers that predate the domain-keyed map.
42
+ draft.registry = draft.registryByDomain.task_checklist;
43
+ return;
35
44
  },
36
45
  },
37
46
  extraReducers: (builder) => {
@@ -44,6 +53,8 @@ const notificationRegistry = (0, toolkit_1.createSlice)({
44
53
  // — Cursor Bugbot 67d6918e. Returning `initialState` verbatim would
45
54
  // rewind the counter and let a stale in-flight GET restore A's
46
55
  // registry over B's.
56
+ // Drops `registry` and `registryByDomain` together: a tenant switch must
57
+ // not leave one domain's section behind.
47
58
  return { registryEpoch: draft.registryEpoch + 1 };
48
59
  });
49
60
  },
@@ -1,2 +1,3 @@
1
- import { NotificationRegistry, NotificationRegistryState } from './notificationRegistryState';
1
+ import { NotificationDomain, NotificationRegistry, NotificationRegistryState } from './notificationRegistryState';
2
2
  export declare const getNotificationRegistry: (slice: NotificationRegistryState) => NotificationRegistry | undefined;
3
+ export declare const getNotificationRegistryForDomain: (slice: NotificationRegistryState, domain: NotificationDomain) => NotificationRegistry | undefined;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getNotificationRegistry = void 0;
3
+ exports.getNotificationRegistryForDomain = exports.getNotificationRegistry = void 0;
4
4
  // Current behavior: a null / undefined registry means the whole notification
5
5
  // section is hidden by consumers. This conflates two distinct states —
6
6
  // "user has no saved preferences yet" vs "the Task Checklist feature gate is
@@ -8,3 +8,9 @@ exports.getNotificationRegistry = void 0;
8
8
  // them (e.g. an explicit `gate_off` signal from the server) is a follow-up.
9
9
  const getNotificationRegistry = (slice) => slice.registry;
10
10
  exports.getNotificationRegistry = getNotificationRegistry;
11
+ // `undefined` conflates gate-off with no-preferences-yet; consumers hide the
12
+ // section either way. Falls back to `registry` for task_checklist so state built
13
+ // in the pre-domain shape (consumer mocks, older callers) still resolves.
14
+ const getNotificationRegistryForDomain = (slice, domain) => slice.registryByDomain?.[domain] ??
15
+ (domain === 'task_checklist' ? slice.registry : undefined);
16
+ exports.getNotificationRegistryForDomain = getNotificationRegistryForDomain;
@@ -1,3 +1,7 @@
1
+ export declare const NOTIFICATION_DOMAINS: readonly ["task_checklist", "commenting"];
2
+ export type NotificationDomain = (typeof NOTIFICATION_DOMAINS)[number];
3
+ export declare const DEFAULT_NOTIFICATION_DOMAIN: NotificationDomain;
4
+ export declare const toNotificationDomain: (raw: string) => NotificationDomain;
1
5
  export declare const NOTIFICATION_CHANNELS: readonly ["email", "slack", "in_app"];
2
6
  export type NotificationChannel = (typeof NOTIFICATION_CHANNELS)[number];
3
7
  export declare const toNotificationChannel: (raw: string) => NotificationChannel;
@@ -30,4 +34,5 @@ export interface NotificationRegistry {
30
34
  export interface NotificationRegistryState {
31
35
  registryEpoch: number;
32
36
  registry?: NotificationRegistry;
37
+ registryByDomain?: Partial<Record<NotificationDomain, NotificationRegistry>>;
33
38
  }
@@ -1,7 +1,13 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.toNotificationFrequency = exports.NOTIFICATION_FREQUENCIES = exports.toNotificationChannel = exports.NOTIFICATION_CHANNELS = void 0;
3
+ exports.toNotificationFrequency = exports.NOTIFICATION_FREQUENCIES = exports.toNotificationChannel = exports.NOTIFICATION_CHANNELS = exports.toNotificationDomain = exports.DEFAULT_NOTIFICATION_DOMAIN = exports.NOTIFICATION_DOMAINS = void 0;
4
4
  const stringToUnion_1 = require("../../commonStateTypes/stringToUnion");
5
+ // Wire vocabulary owned by the notifications service, not by this slice.
6
+ exports.NOTIFICATION_DOMAINS = ['task_checklist', 'commenting'];
7
+ // Callers that predate the domain split mean Task Checklist.
8
+ exports.DEFAULT_NOTIFICATION_DOMAIN = 'task_checklist';
9
+ const toNotificationDomain = (raw) => (0, stringToUnion_1.stringToUnion)(raw, exports.NOTIFICATION_DOMAINS);
10
+ exports.toNotificationDomain = toNotificationDomain;
5
11
  exports.NOTIFICATION_CHANNELS = ['email', 'slack', 'in_app'];
6
12
  const toNotificationChannel = (raw) => (0, stringToUnion_1.stringToUnion)(raw, exports.NOTIFICATION_CHANNELS);
7
13
  exports.toNotificationChannel = toNotificationChannel;
@@ -29,17 +29,20 @@ const updateSectionClassesViewEpic = (actions$) => actions$.pipe((0, operators_1
29
29
  Object.keys(sectionPayload.classes).forEach((eachClassId) => {
30
30
  const classId = classMetadataRecordsByID[eachClassId].nestedClassId;
31
31
  const classMetadataPayload = classMetadataRecordsByID[eachClassId].classMetadataPayload;
32
- const classAccounts = sectionPayload.classes[eachClassId].accounts;
33
- const classSubAccounts = (0, subAccountPayloadHelper_1.getSubAccountsMetadataForAccountList)(classAccounts, accountMetadataRecordsByID, sectionId, { parentClassId: classId, sectionId });
34
- accountMetadataWithBalancesPayloads.push(...classSubAccounts);
35
- // The grouped cash flow aggregates carry no accounts under a class.
36
- const classAccountIds = Object.keys(classAccounts ?? {});
37
- const nestedAccountIds = (0, getNestedAccountIDHierarchyForSection_1.getNestedAccountIDHierarchyForAccounts)(classAccountIds, accountsMetadata);
32
+ const classPayload = sectionPayload.classes[eachClassId];
33
+ // A calculated section is grouped by class with no accounts under it.
34
+ const classAccounts = 'accounts' in classPayload ? classPayload.accounts : undefined;
35
+ if (classAccounts != null) {
36
+ accountMetadataWithBalancesPayloads.push(...(0, subAccountPayloadHelper_1.getSubAccountsMetadataForAccountList)(classAccounts, accountMetadataRecordsByID, sectionId, { parentClassId: classId, sectionId }));
37
+ }
38
+ const nestedAccountIds = classAccounts != null
39
+ ? (0, getNestedAccountIDHierarchyForSection_1.getNestedAccountIDHierarchyForAccounts)(Object.keys(classAccounts), accountsMetadata)
40
+ : [];
38
41
  if (classMetadataPayload != null) {
39
42
  classMetadataWithBalancesPayloads.push({
40
43
  nestedClassId: classId,
41
44
  classMetadata: classMetadataPayload,
42
- classBalances: sectionPayload.classes[eachClassId],
45
+ classBalances: classPayload,
43
46
  nestedAccountIds,
44
47
  });
45
48
  }
@@ -1,14 +1,14 @@
1
- import { SectionByClassCOABalanceGroupedPayloadV2 } from '../../commonPayloadTypes/classesView/reportByClassPayloadV2';
1
+ import { CalculatedSectionByClassCOABalanceGroupedPayloadV2, SectionByClassCOABalanceGroupedPayloadV2 } from '../../commonPayloadTypes/classesView/reportByClassPayloadV2';
2
2
  import { CurrencyPayload } from '../../commonPayloadTypes/v2/viewAndReportPayloadv2';
3
3
  import { AccountMetadataPayload } from '../account/accountPayload';
4
4
  import { ClassMetadataPayload } from '../class/classPayload';
5
5
  import { SectionClassesViewStateV2 } from './sectionClassesViewState';
6
6
  export declare const initialState: SectionClassesViewStateV2;
7
- export declare const updateSectionClassesView: import("@reduxjs/toolkit").ActionCreatorWithPreparedPayload<[timeframe: "month" | "year" | "quarter", sectionId: string, reportId: "cash_balance" | "card_balance" | "expense_automation_card" | "operating_expenses" | "operating_expenses_by_classes" | "operating_expenses_by_projects" | "revenue" | "revenue_by_classes" | "revenue_by_projects" | "net_burn_or_income" | "net_burn_or_income_story_card" | "net_burn_or_income_by_classes" | "net_burn_or_income_by_projects" | "cash_position" | "top_expenses" | "profit_and_loss" | "profit_and_loss_by_classes" | "profit_and_loss_by_projects" | "balance_sheet" | "balance_sheet_by_classes" | "balance_sheet_by_projects" | "cash_flow" | "cash_flow_by_classes" | "cash_flow_by_projects" | "dashboard" | "finance_statement" | "transaction_list_of_account" | "transaction_list_missing_receipt" | "transaction_list_of_account_by_class" | "transaction_list_of_account_by_project" | "transaction_detail" | "cash_in_cash_out" | "insights_dashboard" | "company_details" | "class_list" | "account_list" | "account_list_by_type" | "vendor" | "task_card" | "bill_pay" | "reimbursement" | "bill_pay_card" | "reimbursement_card" | "bill_pay_promo_card" | "zeni_accounts" | "zeni_accounts_promo_card" | "zeni_treasury" | "charge_cards" | "other_connection" | "reimbursement_promo_card" | "onboarding" | "operating_expenses_by_vendor" | "operating_expenses_by_vendor_summary" | "vendors_tab" | "accounts_payable_aging" | "accounts_receivable_aging" | "vendor_global_review" | "prepaid_expenses" | "fixed_assets" | "notification" | "rewards_card" | "billing" | "flux_analysis_operating_expense" | "account_reconciliation" | "zeni_credit_promo_card" | "treasury_promo_card" | "excluded_account_reconciliation" | "transfer_accounts" | "credit_card_transfer_accounts", sectionPayload: SectionByClassCOABalanceGroupedPayloadV2, classesMetadataPayload: ClassMetadataPayload[], accountsMetadataPayload: AccountMetadataPayload[], currencyPayload: CurrencyPayload], {
7
+ export declare const updateSectionClassesView: import("@reduxjs/toolkit").ActionCreatorWithPreparedPayload<[timeframe: "month" | "year" | "quarter", sectionId: string, reportId: "cash_balance" | "card_balance" | "expense_automation_card" | "operating_expenses" | "operating_expenses_by_classes" | "operating_expenses_by_projects" | "revenue" | "revenue_by_classes" | "revenue_by_projects" | "net_burn_or_income" | "net_burn_or_income_story_card" | "net_burn_or_income_by_classes" | "net_burn_or_income_by_projects" | "cash_position" | "top_expenses" | "profit_and_loss" | "profit_and_loss_by_classes" | "profit_and_loss_by_projects" | "balance_sheet" | "balance_sheet_by_classes" | "balance_sheet_by_projects" | "cash_flow" | "cash_flow_by_classes" | "cash_flow_by_projects" | "dashboard" | "finance_statement" | "transaction_list_of_account" | "transaction_list_missing_receipt" | "transaction_list_of_account_by_class" | "transaction_list_of_account_by_project" | "transaction_detail" | "cash_in_cash_out" | "insights_dashboard" | "company_details" | "class_list" | "account_list" | "account_list_by_type" | "vendor" | "task_card" | "bill_pay" | "reimbursement" | "bill_pay_card" | "reimbursement_card" | "bill_pay_promo_card" | "zeni_accounts" | "zeni_accounts_promo_card" | "zeni_treasury" | "charge_cards" | "other_connection" | "reimbursement_promo_card" | "onboarding" | "operating_expenses_by_vendor" | "operating_expenses_by_vendor_summary" | "vendors_tab" | "accounts_payable_aging" | "accounts_receivable_aging" | "vendor_global_review" | "prepaid_expenses" | "fixed_assets" | "notification" | "rewards_card" | "billing" | "flux_analysis_operating_expense" | "account_reconciliation" | "zeni_credit_promo_card" | "treasury_promo_card" | "excluded_account_reconciliation" | "transfer_accounts" | "credit_card_transfer_accounts", sectionPayload: SectionByClassCOABalanceGroupedPayloadV2 | CalculatedSectionByClassCOABalanceGroupedPayloadV2, classesMetadataPayload: ClassMetadataPayload[], accountsMetadataPayload: AccountMetadataPayload[], currencyPayload: CurrencyPayload], {
8
8
  timeframe: "month" | "year" | "quarter";
9
9
  reportId: "cash_balance" | "card_balance" | "expense_automation_card" | "operating_expenses" | "operating_expenses_by_classes" | "operating_expenses_by_projects" | "revenue" | "revenue_by_classes" | "revenue_by_projects" | "net_burn_or_income" | "net_burn_or_income_story_card" | "net_burn_or_income_by_classes" | "net_burn_or_income_by_projects" | "cash_position" | "top_expenses" | "profit_and_loss" | "profit_and_loss_by_classes" | "profit_and_loss_by_projects" | "balance_sheet" | "balance_sheet_by_classes" | "balance_sheet_by_projects" | "cash_flow" | "cash_flow_by_classes" | "cash_flow_by_projects" | "dashboard" | "finance_statement" | "transaction_list_of_account" | "transaction_list_missing_receipt" | "transaction_list_of_account_by_class" | "transaction_list_of_account_by_project" | "transaction_detail" | "cash_in_cash_out" | "insights_dashboard" | "company_details" | "class_list" | "account_list" | "account_list_by_type" | "vendor" | "task_card" | "bill_pay" | "reimbursement" | "bill_pay_card" | "reimbursement_card" | "bill_pay_promo_card" | "zeni_accounts" | "zeni_accounts_promo_card" | "zeni_treasury" | "charge_cards" | "other_connection" | "reimbursement_promo_card" | "onboarding" | "operating_expenses_by_vendor" | "operating_expenses_by_vendor_summary" | "vendors_tab" | "accounts_payable_aging" | "accounts_receivable_aging" | "vendor_global_review" | "prepaid_expenses" | "fixed_assets" | "notification" | "rewards_card" | "billing" | "flux_analysis_operating_expense" | "account_reconciliation" | "zeni_credit_promo_card" | "treasury_promo_card" | "excluded_account_reconciliation" | "transfer_accounts" | "credit_card_transfer_accounts";
10
10
  sectionId: string;
11
- sectionPayload: SectionByClassCOABalanceGroupedPayloadV2;
11
+ sectionPayload: SectionByClassCOABalanceGroupedPayloadV2 | CalculatedSectionByClassCOABalanceGroupedPayloadV2;
12
12
  classesMetadataPayload: ClassMetadataPayload[];
13
13
  accountsMetadataPayload: AccountMetadataPayload[];
14
14
  currencyPayload: CurrencyPayload;
@@ -18,12 +18,16 @@ const updateSectionProjectViewEpic = (actions$) => actions$.pipe((0, operators_1
18
18
  const accountMetadataWithBalancesPayloads = [];
19
19
  if (sectionPayload.projects != null) {
20
20
  Object.keys(sectionPayload.projects).forEach((projectId) => {
21
- const projectAccounts = sectionPayload.projects[projectId].accounts;
21
+ const projectPayload = sectionPayload.projects[projectId];
22
+ // A calculated section is grouped by project with no accounts under it.
23
+ const projectAccounts = 'accounts' in projectPayload ? projectPayload.accounts : undefined;
22
24
  const projectsViewParentID = {
23
25
  projectId,
24
26
  sectionId,
25
27
  };
26
- getSubAccountsMetadataForProjectAccountList(projectAccounts, accountMetadataRecordsByID, sectionId, projectsViewParentID, accountMetadataWithBalancesPayloads);
28
+ if (projectAccounts != null) {
29
+ getSubAccountsMetadataForProjectAccountList(projectAccounts, accountMetadataRecordsByID, sectionId, projectsViewParentID, accountMetadataWithBalancesPayloads);
30
+ }
27
31
  });
28
32
  }
29
33
  if (accountMetadataWithBalancesPayloads.length > 0) {
@@ -1,13 +1,13 @@
1
1
  import { CurrencyPayload } from '../../commonPayloadTypes/v2/viewAndReportPayloadv2';
2
- import { SectionByProjectCOABalanceGroupedPayloadV2 } from '../../view/profitAndLossProjectView/profitAndLossProjectViewPayload';
2
+ import { CalculatedSectionByProjectCOABalanceGroupedPayloadV2, SectionByProjectCOABalanceGroupedPayloadV2 } from '../../view/profitAndLossProjectView/profitAndLossProjectViewPayload';
3
3
  import { AccountMetadataPayload } from '../account/accountPayload';
4
4
  import { SectionProjectViewState } from './sectionProjectViewState';
5
5
  export declare const initialState: SectionProjectViewState;
6
- export declare const updateSectionProjectView: import("@reduxjs/toolkit").ActionCreatorWithPreparedPayload<[timeframe: "month" | "year" | "quarter", sectionId: string, reportId: "cash_balance" | "card_balance" | "expense_automation_card" | "operating_expenses" | "operating_expenses_by_classes" | "operating_expenses_by_projects" | "revenue" | "revenue_by_classes" | "revenue_by_projects" | "net_burn_or_income" | "net_burn_or_income_story_card" | "net_burn_or_income_by_classes" | "net_burn_or_income_by_projects" | "cash_position" | "top_expenses" | "profit_and_loss" | "profit_and_loss_by_classes" | "profit_and_loss_by_projects" | "balance_sheet" | "balance_sheet_by_classes" | "balance_sheet_by_projects" | "cash_flow" | "cash_flow_by_classes" | "cash_flow_by_projects" | "dashboard" | "finance_statement" | "transaction_list_of_account" | "transaction_list_missing_receipt" | "transaction_list_of_account_by_class" | "transaction_list_of_account_by_project" | "transaction_detail" | "cash_in_cash_out" | "insights_dashboard" | "company_details" | "class_list" | "account_list" | "account_list_by_type" | "vendor" | "task_card" | "bill_pay" | "reimbursement" | "bill_pay_card" | "reimbursement_card" | "bill_pay_promo_card" | "zeni_accounts" | "zeni_accounts_promo_card" | "zeni_treasury" | "charge_cards" | "other_connection" | "reimbursement_promo_card" | "onboarding" | "operating_expenses_by_vendor" | "operating_expenses_by_vendor_summary" | "vendors_tab" | "accounts_payable_aging" | "accounts_receivable_aging" | "vendor_global_review" | "prepaid_expenses" | "fixed_assets" | "notification" | "rewards_card" | "billing" | "flux_analysis_operating_expense" | "account_reconciliation" | "zeni_credit_promo_card" | "treasury_promo_card" | "excluded_account_reconciliation" | "transfer_accounts" | "credit_card_transfer_accounts", sectionPayload: SectionByProjectCOABalanceGroupedPayloadV2, accountsMetadataPayload: AccountMetadataPayload[], currencyPayload: CurrencyPayload], {
6
+ export declare const updateSectionProjectView: import("@reduxjs/toolkit").ActionCreatorWithPreparedPayload<[timeframe: "month" | "year" | "quarter", sectionId: string, reportId: "cash_balance" | "card_balance" | "expense_automation_card" | "operating_expenses" | "operating_expenses_by_classes" | "operating_expenses_by_projects" | "revenue" | "revenue_by_classes" | "revenue_by_projects" | "net_burn_or_income" | "net_burn_or_income_story_card" | "net_burn_or_income_by_classes" | "net_burn_or_income_by_projects" | "cash_position" | "top_expenses" | "profit_and_loss" | "profit_and_loss_by_classes" | "profit_and_loss_by_projects" | "balance_sheet" | "balance_sheet_by_classes" | "balance_sheet_by_projects" | "cash_flow" | "cash_flow_by_classes" | "cash_flow_by_projects" | "dashboard" | "finance_statement" | "transaction_list_of_account" | "transaction_list_missing_receipt" | "transaction_list_of_account_by_class" | "transaction_list_of_account_by_project" | "transaction_detail" | "cash_in_cash_out" | "insights_dashboard" | "company_details" | "class_list" | "account_list" | "account_list_by_type" | "vendor" | "task_card" | "bill_pay" | "reimbursement" | "bill_pay_card" | "reimbursement_card" | "bill_pay_promo_card" | "zeni_accounts" | "zeni_accounts_promo_card" | "zeni_treasury" | "charge_cards" | "other_connection" | "reimbursement_promo_card" | "onboarding" | "operating_expenses_by_vendor" | "operating_expenses_by_vendor_summary" | "vendors_tab" | "accounts_payable_aging" | "accounts_receivable_aging" | "vendor_global_review" | "prepaid_expenses" | "fixed_assets" | "notification" | "rewards_card" | "billing" | "flux_analysis_operating_expense" | "account_reconciliation" | "zeni_credit_promo_card" | "treasury_promo_card" | "excluded_account_reconciliation" | "transfer_accounts" | "credit_card_transfer_accounts", sectionPayload: SectionByProjectCOABalanceGroupedPayloadV2 | CalculatedSectionByProjectCOABalanceGroupedPayloadV2, accountsMetadataPayload: AccountMetadataPayload[], currencyPayload: CurrencyPayload], {
7
7
  timeframe: "month" | "year" | "quarter";
8
8
  reportId: "cash_balance" | "card_balance" | "expense_automation_card" | "operating_expenses" | "operating_expenses_by_classes" | "operating_expenses_by_projects" | "revenue" | "revenue_by_classes" | "revenue_by_projects" | "net_burn_or_income" | "net_burn_or_income_story_card" | "net_burn_or_income_by_classes" | "net_burn_or_income_by_projects" | "cash_position" | "top_expenses" | "profit_and_loss" | "profit_and_loss_by_classes" | "profit_and_loss_by_projects" | "balance_sheet" | "balance_sheet_by_classes" | "balance_sheet_by_projects" | "cash_flow" | "cash_flow_by_classes" | "cash_flow_by_projects" | "dashboard" | "finance_statement" | "transaction_list_of_account" | "transaction_list_missing_receipt" | "transaction_list_of_account_by_class" | "transaction_list_of_account_by_project" | "transaction_detail" | "cash_in_cash_out" | "insights_dashboard" | "company_details" | "class_list" | "account_list" | "account_list_by_type" | "vendor" | "task_card" | "bill_pay" | "reimbursement" | "bill_pay_card" | "reimbursement_card" | "bill_pay_promo_card" | "zeni_accounts" | "zeni_accounts_promo_card" | "zeni_treasury" | "charge_cards" | "other_connection" | "reimbursement_promo_card" | "onboarding" | "operating_expenses_by_vendor" | "operating_expenses_by_vendor_summary" | "vendors_tab" | "accounts_payable_aging" | "accounts_receivable_aging" | "vendor_global_review" | "prepaid_expenses" | "fixed_assets" | "notification" | "rewards_card" | "billing" | "flux_analysis_operating_expense" | "account_reconciliation" | "zeni_credit_promo_card" | "treasury_promo_card" | "excluded_account_reconciliation" | "transfer_accounts" | "credit_card_transfer_accounts";
9
9
  sectionId: string;
10
- sectionPayload: SectionByProjectCOABalanceGroupedPayloadV2;
10
+ sectionPayload: SectionByProjectCOABalanceGroupedPayloadV2 | CalculatedSectionByProjectCOABalanceGroupedPayloadV2;
11
11
  accountsMetadataPayload: AccountMetadataPayload[];
12
12
  currencyPayload: CurrencyPayload;
13
13
  }, "sectionProjectView/updateSectionProjectView", never, never>;
@@ -34,10 +34,13 @@ const sectionProjectView = (0, toolkit_1.createSlice)({
34
34
  const accountsMetadata = accountsMetadataPayload.map((eachAccount) => (0, accountPayload_1.mapAccountMetadataPayloadToAccountMetadata)(eachAccount));
35
35
  Object.keys(sectionPayload.projects).forEach((projectId) => {
36
36
  const projectPayload = sectionPayload.projects[projectId];
37
- const projectAccountIds = projectPayload.accounts != null
38
- ? Object.keys(projectPayload.accounts)
37
+ // A calculated section is grouped by project with no accounts under it.
38
+ const projectAccounts = 'accounts' in projectPayload
39
+ ? projectPayload.accounts
40
+ : undefined;
41
+ const nestedAccountIds = projectAccounts != null
42
+ ? (0, getNestedAccountIDHierarchyForSection_1.getNestedAccountIDHierarchyForAccounts)(Object.keys(projectAccounts), accountsMetadata)
39
43
  : [];
40
- const nestedAccountIds = (0, getNestedAccountIDHierarchyForSection_1.getNestedAccountIDHierarchyForAccounts)(projectAccountIds, accountsMetadata);
41
44
  updateSectionProject(draft, timeframe, projectPayload, sectionId, projectId, reportId, currencyPayload, nestedAccountIds);
42
45
  });
43
46
  }
package/lib/epic.js CHANGED
@@ -53,15 +53,21 @@ const acceptMasterTOSEpic_1 = require("./view/aiCfoView/epics/acceptMasterTOSEpi
53
53
  const createSessionAndSubmitEpic_1 = require("./view/aiCfoView/epics/createSessionAndSubmitEpic");
54
54
  const createSessionEpic_1 = require("./view/aiCfoView/epics/createSessionEpic");
55
55
  const deleteChatSessionEpic_1 = require("./view/aiCfoView/epics/deleteChatSessionEpic");
56
+ const deleteRoutineEpic_1 = require("./view/aiCfoView/epics/deleteRoutineEpic");
56
57
  const deleteSkillEpic_1 = require("./view/aiCfoView/epics/deleteSkillEpic");
57
58
  const fetchChatHistoryEpic_1 = require("./view/aiCfoView/epics/fetchChatHistoryEpic");
58
59
  const fetchChatSessionsForUserEpic_1 = require("./view/aiCfoView/epics/fetchChatSessionsForUserEpic");
60
+ const fetchRoutineRunsEpic_1 = require("./view/aiCfoView/epics/fetchRoutineRunsEpic");
61
+ const fetchRoutinesEpic_1 = require("./view/aiCfoView/epics/fetchRoutinesEpic");
59
62
  const fetchSkillDetailEpic_1 = require("./view/aiCfoView/epics/fetchSkillDetailEpic");
60
63
  const fetchSkillVersionBodyEpic_1 = require("./view/aiCfoView/epics/fetchSkillVersionBodyEpic");
61
64
  const fetchSkillVersionsEpic_1 = require("./view/aiCfoView/epics/fetchSkillVersionsEpic");
62
65
  const fetchSkillsEpic_1 = require("./view/aiCfoView/epics/fetchSkillsEpic");
63
66
  const fetchSuggestedQuestionsEpic_1 = require("./view/aiCfoView/epics/fetchSuggestedQuestionsEpic");
67
+ const runRoutineEpic_1 = require("./view/aiCfoView/epics/runRoutineEpic");
68
+ const saveRoutineEpic_1 = require("./view/aiCfoView/epics/saveRoutineEpic");
64
69
  const saveSkillEpic_1 = require("./view/aiCfoView/epics/saveSkillEpic");
70
+ const setRoutineEnabledEpic_1 = require("./view/aiCfoView/epics/setRoutineEnabledEpic");
65
71
  const setSkillVisibilityEpic_1 = require("./view/aiCfoView/epics/setSkillVisibilityEpic");
66
72
  const stopSubmitEpic_1 = require("./view/aiCfoView/epics/stopSubmitEpic");
67
73
  const stopSubmitQuestionEpic_1 = require("./view/aiCfoView/epics/stopSubmitQuestionEpic");
@@ -845,6 +851,7 @@ const ALL_EPICS = [
845
851
  deletePersonEpic_1.deletePersonEpic,
846
852
  deleteRemiApprovalRuleEpic_1.deleteRemiApprovalRuleEpic,
847
853
  deleteRemiEpic_1.deleteRemiEpic,
854
+ deleteRoutineEpic_1.deleteRoutineEpic,
848
855
  deleteScheduleAccruedDetailEpic_1.deleteScheduleAccruedDetailEpic,
849
856
  deleteScheduleDetailEpic_1.deleteScheduleDetailEpic,
850
857
  deleteSkillEpic_1.deleteSkillEpic,
@@ -1169,6 +1176,8 @@ const ALL_EPICS = [
1169
1176
  fetchReviewCompanyViewEpic_1.fetchReviewCompanyViewEpic,
1170
1177
  fetchReviewTransferDetailEpic_1.fetchReviewTransferDetailEpic,
1171
1178
  fetchRewardsPlanEpic_1.fetchRewardsPlanEpic,
1179
+ fetchRoutineRunsEpic_1.fetchRoutineRunsEpic,
1180
+ fetchRoutinesEpic_1.fetchRoutinesEpic,
1172
1181
  fetchScheduleAccruedDetailsEpic_1.fetchScheduleAccruedDetailsEpic,
1173
1182
  fetchScheduleAccruedDetailsPageEpic_1.fetchScheduleAccruedDetailsPageEpic,
1174
1183
  fetchScheduleDetailsEpic_1.fetchScheduleDetailsEpic,
@@ -1347,6 +1356,7 @@ const ALL_EPICS = [
1347
1356
  runInvoicingInvoiceActionEpic_1.runInvoicingInvoiceActionEpic,
1348
1357
  runInvoicingPaymentActionEpic_1.runInvoicingPaymentActionEpic,
1349
1358
  runInvoicingSubscriptionActionEpic_1.runInvoicingSubscriptionActionEpic,
1359
+ runRoutineEpic_1.runRoutineEpic,
1350
1360
  saveAccountMappingViewEpic_1.saveAccountMappingViewEpic,
1351
1361
  saveAccountSettingsViewEpic_1.saveAccountSettingsViewEpic,
1352
1362
  saveAPIKeyConnectionEpic_1.saveAPIKeyConnectionEpic,
@@ -1382,6 +1392,7 @@ const ALL_EPICS = [
1382
1392
  saveReasonForAuditRuleEpic_1.saveReasonForAuditRuleEpic,
1383
1393
  saveRemiDetailEpic_1.saveRemiDetailEpic,
1384
1394
  saveRemiSetupApproverViewUpdatesEpic_1.saveRemiSetupApproverViewUpdatesEpic,
1395
+ saveRoutineEpic_1.saveRoutineEpic,
1385
1396
  saveScheduleAccruedDetailsEpic_1.saveScheduleAccruedDetailsEpic,
1386
1397
  saveScheduleDetailsEpic_1.saveScheduleDetailsEpic,
1387
1398
  saveSkillEpic_1.saveSkillEpic,
@@ -1410,6 +1421,7 @@ const ALL_EPICS = [
1410
1421
  sendOtpEpic_1.sendOtpEpic,
1411
1422
  sendReferralInviteEpic_1.sendReferralInviteEpic,
1412
1423
  sessionHeartbeatEpic_1.sessionHeartbeatEpic,
1424
+ setRoutineEnabledEpic_1.setRoutineEnabledEpic,
1413
1425
  setSkillVisibilityEpic_1.setSkillVisibilityEpic,
1414
1426
  snoozeTaskEpic_1.snoozeTaskEpic,
1415
1427
  startInvoicingQboBackfillEpic_1.startInvoicingQboBackfillEpic,
@@ -1,7 +1,7 @@
1
1
  import { createSlice } from '@reduxjs/toolkit';
2
2
  import assignWith from 'lodash/assignWith';
3
3
  import recordGet from 'lodash/get';
4
- import { mapCOABalanceGroupedByPeriodPayloadV2ToCOABalanceGroupedByPeriod } from '../../commonPayloadTypes/v2/coaBalancePayload';
4
+ import { mapCOABalanceGroupedByPeriodPayloadV2ToCOABalanceGroupedByPeriod, } from '../../commonPayloadTypes/v2/coaBalancePayload';
5
5
  import { getParentClassId } from '../../commonStateTypes/classesView/getNestedClassIDHierarchyForReport';
6
6
  import { clearAll } from '../../rootActions';
7
7
  import { mapClassBasePayloadToClass, } from './classPayload';
@@ -1,6 +1,7 @@
1
1
  import { createSlice } from '@reduxjs/toolkit';
2
2
  import { clearAll } from '../../rootActions';
3
3
  import { mapNotificationRegistryPayloadToRegistry, } from './notificationRegistryPayload';
4
+ import { NOTIFICATION_DOMAINS, } from './notificationRegistryState';
4
5
  export const initialState = { registryEpoch: 0 };
5
6
  const notificationRegistry = createSlice({
6
7
  name: 'notificationRegistry',
@@ -19,16 +20,24 @@ const notificationRegistry = createSlice({
19
20
  // `task_checklist_registry` CLEARS so tenant B doesn't inherit A's
20
21
  // Task Checklist section. Guarded by the epoch check above so a stale
21
22
  // absent-response can't wipe a fresh registry.
22
- if (action.payload.registry == null) {
23
- draft.registry = undefined;
24
- return;
23
+ const { registriesByDomain } = action.payload;
24
+ // Absent `registriesByDomain` means a pre-domain caller: treat `registry`
25
+ // as the task_checklist snapshot so their behaviour is unchanged.
26
+ const snapshots = registriesByDomain ?? { task_checklist: action.payload.registry };
27
+ draft.registryByDomain = {};
28
+ for (const domain of NOTIFICATION_DOMAINS) {
29
+ const payload = snapshots[domain];
30
+ // Full-replace per domain, and an absent snapshot CLEARS that domain so
31
+ // tenant B never inherits A's section. The gates are independent
32
+ // server-side, so one domain clearing must not touch the other.
33
+ if (payload != null) {
34
+ draft.registryByDomain[domain] =
35
+ mapNotificationRegistryPayloadToRegistry(payload);
36
+ }
25
37
  }
26
- // Full-replace contract: the registry payload is atomic — every fetch
27
- // returns the complete group/event tree. If the backend ever ships a
28
- // partial registry response, this write MUST switch to a merge (mirror
29
- // the preferences slice's `mergePreferences`). Do not assume the
30
- // current payload is partial without also updating this reducer.
31
- draft.registry = mapNotificationRegistryPayloadToRegistry(action.payload.registry);
38
+ // Mirrored for consumers that predate the domain-keyed map.
39
+ draft.registry = draft.registryByDomain.task_checklist;
40
+ return;
32
41
  },
33
42
  },
34
43
  extraReducers: (builder) => {
@@ -41,6 +50,8 @@ const notificationRegistry = createSlice({
41
50
  // — Cursor Bugbot 67d6918e. Returning `initialState` verbatim would
42
51
  // rewind the counter and let a stale in-flight GET restore A's
43
52
  // registry over B's.
53
+ // Drops `registry` and `registryByDomain` together: a tenant switch must
54
+ // not leave one domain's section behind.
44
55
  return { registryEpoch: draft.registryEpoch + 1 };
45
56
  });
46
57
  },
@@ -4,3 +4,8 @@
4
4
  // off for this tenant". Both currently surface as `undefined`. Distinguishing
5
5
  // them (e.g. an explicit `gate_off` signal from the server) is a follow-up.
6
6
  export const getNotificationRegistry = (slice) => slice.registry;
7
+ // `undefined` conflates gate-off with no-preferences-yet; consumers hide the
8
+ // section either way. Falls back to `registry` for task_checklist so state built
9
+ // in the pre-domain shape (consumer mocks, older callers) still resolves.
10
+ export const getNotificationRegistryForDomain = (slice, domain) => slice.registryByDomain?.[domain] ??
11
+ (domain === 'task_checklist' ? slice.registry : undefined);
@@ -1,4 +1,9 @@
1
1
  import { stringToUnion } from '../../commonStateTypes/stringToUnion';
2
+ // Wire vocabulary owned by the notifications service, not by this slice.
3
+ export const NOTIFICATION_DOMAINS = ['task_checklist', 'commenting'];
4
+ // Callers that predate the domain split mean Task Checklist.
5
+ export const DEFAULT_NOTIFICATION_DOMAIN = 'task_checklist';
6
+ export const toNotificationDomain = (raw) => stringToUnion(raw, NOTIFICATION_DOMAINS);
2
7
  export const NOTIFICATION_CHANNELS = ['email', 'slack', 'in_app'];
3
8
  export const toNotificationChannel = (raw) => stringToUnion(raw, NOTIFICATION_CHANNELS);
4
9
  export const NOTIFICATION_FREQUENCIES = ['instant', 'daily'];
@@ -26,17 +26,20 @@ export const updateSectionClassesViewEpic = (actions$) => actions$.pipe(filter(u
26
26
  Object.keys(sectionPayload.classes).forEach((eachClassId) => {
27
27
  const classId = classMetadataRecordsByID[eachClassId].nestedClassId;
28
28
  const classMetadataPayload = classMetadataRecordsByID[eachClassId].classMetadataPayload;
29
- const classAccounts = sectionPayload.classes[eachClassId].accounts;
30
- const classSubAccounts = getSubAccountsMetadataForAccountList(classAccounts, accountMetadataRecordsByID, sectionId, { parentClassId: classId, sectionId });
31
- accountMetadataWithBalancesPayloads.push(...classSubAccounts);
32
- // The grouped cash flow aggregates carry no accounts under a class.
33
- const classAccountIds = Object.keys(classAccounts ?? {});
34
- const nestedAccountIds = getNestedAccountIDHierarchyForAccounts(classAccountIds, accountsMetadata);
29
+ const classPayload = sectionPayload.classes[eachClassId];
30
+ // A calculated section is grouped by class with no accounts under it.
31
+ const classAccounts = 'accounts' in classPayload ? classPayload.accounts : undefined;
32
+ if (classAccounts != null) {
33
+ accountMetadataWithBalancesPayloads.push(...getSubAccountsMetadataForAccountList(classAccounts, accountMetadataRecordsByID, sectionId, { parentClassId: classId, sectionId }));
34
+ }
35
+ const nestedAccountIds = classAccounts != null
36
+ ? getNestedAccountIDHierarchyForAccounts(Object.keys(classAccounts), accountsMetadata)
37
+ : [];
35
38
  if (classMetadataPayload != null) {
36
39
  classMetadataWithBalancesPayloads.push({
37
40
  nestedClassId: classId,
38
41
  classMetadata: classMetadataPayload,
39
- classBalances: sectionPayload.classes[eachClassId],
42
+ classBalances: classPayload,
40
43
  nestedAccountIds,
41
44
  });
42
45
  }
@@ -15,12 +15,16 @@ export const updateSectionProjectViewEpic = (actions$) => actions$.pipe(filter(u
15
15
  const accountMetadataWithBalancesPayloads = [];
16
16
  if (sectionPayload.projects != null) {
17
17
  Object.keys(sectionPayload.projects).forEach((projectId) => {
18
- const projectAccounts = sectionPayload.projects[projectId].accounts;
18
+ const projectPayload = sectionPayload.projects[projectId];
19
+ // A calculated section is grouped by project with no accounts under it.
20
+ const projectAccounts = 'accounts' in projectPayload ? projectPayload.accounts : undefined;
19
21
  const projectsViewParentID = {
20
22
  projectId,
21
23
  sectionId,
22
24
  };
23
- getSubAccountsMetadataForProjectAccountList(projectAccounts, accountMetadataRecordsByID, sectionId, projectsViewParentID, accountMetadataWithBalancesPayloads);
25
+ if (projectAccounts != null) {
26
+ getSubAccountsMetadataForProjectAccountList(projectAccounts, accountMetadataRecordsByID, sectionId, projectsViewParentID, accountMetadataWithBalancesPayloads);
27
+ }
24
28
  });
25
29
  }
26
30
  if (accountMetadataWithBalancesPayloads.length > 0) {
@@ -1,5 +1,5 @@
1
1
  import { createSlice } from '@reduxjs/toolkit';
2
- import { mapCOABalanceGroupedByPeriodPayloadV2ToCOABalanceGroupedByPeriod } from '../../commonPayloadTypes/v2/coaBalancePayload';
2
+ import { mapCOABalanceGroupedByPeriodPayloadV2ToCOABalanceGroupedByPeriod, } from '../../commonPayloadTypes/v2/coaBalancePayload';
3
3
  import { clearAll } from '../../rootActions';
4
4
  import { mapAccountMetadataPayloadToAccountMetadata, } from '../account/accountPayload';
5
5
  import { getNestedAccountIDHierarchyForAccounts } from '../sectionAccountsView/getNestedAccountIDHierarchyForSection';
@@ -31,10 +31,13 @@ const sectionProjectView = createSlice({
31
31
  const accountsMetadata = accountsMetadataPayload.map((eachAccount) => mapAccountMetadataPayloadToAccountMetadata(eachAccount));
32
32
  Object.keys(sectionPayload.projects).forEach((projectId) => {
33
33
  const projectPayload = sectionPayload.projects[projectId];
34
- const projectAccountIds = projectPayload.accounts != null
35
- ? Object.keys(projectPayload.accounts)
34
+ // A calculated section is grouped by project with no accounts under it.
35
+ const projectAccounts = 'accounts' in projectPayload
36
+ ? projectPayload.accounts
37
+ : undefined;
38
+ const nestedAccountIds = projectAccounts != null
39
+ ? getNestedAccountIDHierarchyForAccounts(Object.keys(projectAccounts), accountsMetadata)
36
40
  : [];
37
- const nestedAccountIds = getNestedAccountIDHierarchyForAccounts(projectAccountIds, accountsMetadata);
38
41
  updateSectionProject(draft, timeframe, projectPayload, sectionId, projectId, reportId, currencyPayload, nestedAccountIds);
39
42
  });
40
43
  }
package/lib/esm/epic.js CHANGED
@@ -50,15 +50,21 @@ import { acceptMasterTOSEpic, } from './view/aiCfoView/epics/acceptMasterTOSEpic
50
50
  import { createSessionAndSubmitEpic, } from './view/aiCfoView/epics/createSessionAndSubmitEpic';
51
51
  import { createSessionEpic, } from './view/aiCfoView/epics/createSessionEpic';
52
52
  import { deleteChatSessionEpic, } from './view/aiCfoView/epics/deleteChatSessionEpic';
53
+ import { deleteRoutineEpic } from './view/aiCfoView/epics/deleteRoutineEpic';
53
54
  import { deleteSkillEpic } from './view/aiCfoView/epics/deleteSkillEpic';
54
55
  import { fetchChatHistoryEpic, } from './view/aiCfoView/epics/fetchChatHistoryEpic';
55
56
  import { fetchChatSessionsForUserEpic, } from './view/aiCfoView/epics/fetchChatSessionsForUserEpic';
57
+ import { fetchRoutineRunsEpic } from './view/aiCfoView/epics/fetchRoutineRunsEpic';
58
+ import { fetchRoutinesEpic } from './view/aiCfoView/epics/fetchRoutinesEpic';
56
59
  import { fetchSkillDetailEpic } from './view/aiCfoView/epics/fetchSkillDetailEpic';
57
60
  import { fetchSkillVersionBodyEpic } from './view/aiCfoView/epics/fetchSkillVersionBodyEpic';
58
61
  import { fetchSkillVersionsEpic } from './view/aiCfoView/epics/fetchSkillVersionsEpic';
59
62
  import { fetchSkillsEpic, } from './view/aiCfoView/epics/fetchSkillsEpic';
60
63
  import { fetchSuggestedQuestionsEpic, } from './view/aiCfoView/epics/fetchSuggestedQuestionsEpic';
64
+ import { runRoutineEpic } from './view/aiCfoView/epics/runRoutineEpic';
65
+ import { saveRoutineEpic } from './view/aiCfoView/epics/saveRoutineEpic';
61
66
  import { saveSkillEpic } from './view/aiCfoView/epics/saveSkillEpic';
67
+ import { setRoutineEnabledEpic } from './view/aiCfoView/epics/setRoutineEnabledEpic';
62
68
  import { setSkillVisibilityEpic } from './view/aiCfoView/epics/setSkillVisibilityEpic';
63
69
  import { stopSubmitEpic, } from './view/aiCfoView/epics/stopSubmitEpic';
64
70
  import { stopSubmitQuestionEpic, } from './view/aiCfoView/epics/stopSubmitQuestionEpic';
@@ -842,6 +848,7 @@ const ALL_EPICS = [
842
848
  deletePersonEpic,
843
849
  deleteRemiApprovalRuleEpic,
844
850
  deleteRemiEpic,
851
+ deleteRoutineEpic,
845
852
  deleteScheduleAccruedDetailEpic,
846
853
  deleteScheduleDetailEpic,
847
854
  deleteSkillEpic,
@@ -1166,6 +1173,8 @@ const ALL_EPICS = [
1166
1173
  fetchReviewCompanyViewEpic,
1167
1174
  fetchReviewTransferDetailEpic,
1168
1175
  fetchRewardsPlanEpic,
1176
+ fetchRoutineRunsEpic,
1177
+ fetchRoutinesEpic,
1169
1178
  fetchScheduleAccruedDetailsEpic,
1170
1179
  fetchScheduleAccruedDetailsPageEpic,
1171
1180
  fetchScheduleDetailsEpic,
@@ -1344,6 +1353,7 @@ const ALL_EPICS = [
1344
1353
  runInvoicingInvoiceActionEpic,
1345
1354
  runInvoicingPaymentActionEpic,
1346
1355
  runInvoicingSubscriptionActionEpic,
1356
+ runRoutineEpic,
1347
1357
  saveAccountMappingViewEpic,
1348
1358
  saveAccountSettingsViewEpic,
1349
1359
  saveAPIKeyConnectionEpic,
@@ -1379,6 +1389,7 @@ const ALL_EPICS = [
1379
1389
  saveReasonForAuditRuleEpic,
1380
1390
  saveRemiDetailEpic,
1381
1391
  saveRemiSetupApproverViewUpdatesEpic,
1392
+ saveRoutineEpic,
1382
1393
  saveScheduleAccruedDetailsEpic,
1383
1394
  saveScheduleDetailsEpic,
1384
1395
  saveSkillEpic,
@@ -1407,6 +1418,7 @@ const ALL_EPICS = [
1407
1418
  sendOtpEpic,
1408
1419
  sendReferralInviteEpic,
1409
1420
  sessionHeartbeatEpic,
1421
+ setRoutineEnabledEpic,
1410
1422
  setSkillVisibilityEpic,
1411
1423
  snoozeTaskEpic,
1412
1424
  startInvoicingQboBackfillEpic,