@zeniai/client-epic-state 5.2.4 → 5.2.6

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 (64) hide show
  1. package/lib/epic.js +14 -4
  2. package/lib/esm/epic.js +14 -4
  3. package/lib/esm/index.js +4 -3
  4. package/lib/esm/view/accountList/accountListReducer.js +1 -0
  5. package/lib/esm/view/aiCfoView/aiCfoViewReducer.js +411 -9
  6. package/lib/esm/view/aiCfoView/aiCfoViewSelector.js +49 -0
  7. package/lib/esm/view/aiCfoView/epics/deleteSkillEpic.js +23 -0
  8. package/lib/esm/view/aiCfoView/epics/fetchSkillDetailEpic.js +35 -0
  9. package/lib/esm/view/aiCfoView/epics/fetchSkillVersionBodyEpic.js +45 -0
  10. package/lib/esm/view/aiCfoView/epics/fetchSkillVersionsEpic.js +37 -0
  11. package/lib/esm/view/aiCfoView/epics/fetchSkillsEpic.js +11 -6
  12. package/lib/esm/view/aiCfoView/epics/saveSkillEpic.js +72 -0
  13. package/lib/esm/view/aiCfoView/epics/setSkillVisibilityEpic.js +32 -0
  14. package/lib/esm/view/aiCfoView/epics/toSkillDetail.js +102 -0
  15. package/lib/esm/view/invoicing/editInvoicingCatalogItemDetailView/editInvoicingCatalogItemDetailViewSelector.js +8 -0
  16. package/lib/esm/view/invoicing/invoicingQboView/epics/fetchInvoicingAccountingPickersEpic.js +29 -0
  17. package/lib/esm/view/invoicing/invoicingQboView/invoicingQboViewPayload.js +0 -23
  18. package/lib/esm/view/invoicing/invoicingQboView/invoicingQboViewReducer.js +3 -40
  19. package/lib/esm/view/invoicing/invoicingQboView/invoicingQboViewSelector.js +124 -6
  20. package/lib/index.d.ts +7 -5
  21. package/lib/index.js +31 -14
  22. package/lib/view/accountList/accountListReducer.js +1 -0
  23. package/lib/view/accountList/accountListState.d.ts +1 -1
  24. package/lib/view/aiCfoView/aiCfoViewPayload.d.ts +85 -0
  25. package/lib/view/aiCfoView/aiCfoViewReducer.d.ts +67 -2
  26. package/lib/view/aiCfoView/aiCfoViewReducer.js +413 -10
  27. package/lib/view/aiCfoView/aiCfoViewSelector.d.ts +7 -1
  28. package/lib/view/aiCfoView/aiCfoViewSelector.js +54 -0
  29. package/lib/view/aiCfoView/aiCfoViewState.d.ts +143 -1
  30. package/lib/view/aiCfoView/epics/deleteSkillEpic.d.ts +8 -0
  31. package/lib/view/aiCfoView/epics/deleteSkillEpic.js +27 -0
  32. package/lib/view/aiCfoView/epics/fetchSkillDetailEpic.d.ts +8 -0
  33. package/lib/view/aiCfoView/epics/fetchSkillDetailEpic.js +39 -0
  34. package/lib/view/aiCfoView/epics/fetchSkillVersionBodyEpic.d.ts +13 -0
  35. package/lib/view/aiCfoView/epics/fetchSkillVersionBodyEpic.js +49 -0
  36. package/lib/view/aiCfoView/epics/fetchSkillVersionsEpic.d.ts +8 -0
  37. package/lib/view/aiCfoView/epics/fetchSkillVersionsEpic.js +41 -0
  38. package/lib/view/aiCfoView/epics/fetchSkillsEpic.js +11 -6
  39. package/lib/view/aiCfoView/epics/saveSkillEpic.d.ts +14 -0
  40. package/lib/view/aiCfoView/epics/saveSkillEpic.js +76 -0
  41. package/lib/view/aiCfoView/epics/setSkillVisibilityEpic.d.ts +14 -0
  42. package/lib/view/aiCfoView/epics/setSkillVisibilityEpic.js +36 -0
  43. package/lib/view/aiCfoView/epics/toSkillDetail.d.ts +88 -0
  44. package/lib/view/aiCfoView/epics/toSkillDetail.js +110 -0
  45. package/lib/view/invoicing/editInvoicingCatalogItemDetailView/editInvoicingCatalogItemDetailViewSelector.d.ts +9 -0
  46. package/lib/view/invoicing/editInvoicingCatalogItemDetailView/editInvoicingCatalogItemDetailViewSelector.js +8 -0
  47. package/lib/view/invoicing/invoicingQboView/epics/fetchInvoicingAccountingPickersEpic.d.ts +5 -0
  48. package/lib/view/invoicing/invoicingQboView/epics/fetchInvoicingAccountingPickersEpic.js +33 -0
  49. package/lib/view/invoicing/invoicingQboView/epics/invoicingQboViewActionType.d.ts +4 -2
  50. package/lib/view/invoicing/invoicingQboView/invoicingQboViewPayload.d.ts +0 -26
  51. package/lib/view/invoicing/invoicingQboView/invoicingQboViewPayload.js +0 -24
  52. package/lib/view/invoicing/invoicingQboView/invoicingQboViewReducer.d.ts +2 -10
  53. package/lib/view/invoicing/invoicingQboView/invoicingQboViewReducer.js +4 -41
  54. package/lib/view/invoicing/invoicingQboView/invoicingQboViewSelector.d.ts +43 -6
  55. package/lib/view/invoicing/invoicingQboView/invoicingQboViewSelector.js +128 -7
  56. package/lib/view/invoicing/invoicingQboView/invoicingQboViewState.d.ts +1 -5
  57. package/lib/view/taskManager/taskListView/taskList.d.ts +1 -1
  58. package/package.json +1 -1
  59. package/lib/esm/view/invoicing/invoicingQboView/epics/fetchInvoicingQboAccountsEpic.js +0 -25
  60. package/lib/esm/view/invoicing/invoicingQboView/epics/fetchInvoicingQboClassesEpic.js +0 -15
  61. package/lib/view/invoicing/invoicingQboView/epics/fetchInvoicingQboAccountsEpic.d.ts +0 -6
  62. package/lib/view/invoicing/invoicingQboView/epics/fetchInvoicingQboAccountsEpic.js +0 -29
  63. package/lib/view/invoicing/invoicingQboView/epics/fetchInvoicingQboClassesEpic.d.ts +0 -6
  64. package/lib/view/invoicing/invoicingQboView/epics/fetchInvoicingQboClassesEpic.js +0 -19
@@ -1,19 +1,140 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getInvoicingQboView = void 0;
3
+ exports.getInvoicingQboView = exports.invoicingAccountHierarchyForTypes = exports.invoicingAccountsForQboAccountTypes = exports.INVOICING_INCOME_ACCOUNT_TYPES = exports.isInvoicingQboConnected = exports.INVOICING_ACCOUNT_LIST_KEY = void 0;
4
+ const accountListSelector_1 = require("../../accountList/accountListSelector");
5
+ const classListSelector_1 = require("../../classList/classListSelector");
6
+ // Its own bucket: invoicing needs the AP/AR exclusions that the vendor and
7
+ // transaction screens sharing `accountList` do not pass.
8
+ exports.INVOICING_ACCOUNT_LIST_KEY = 'invoicingAccountList';
9
+ /** Whether QBO is connected, without building the option lists to find out. */
10
+ const isInvoicingQboConnected = (state) => state.invoicingQboViewState.connection?.connected === true;
11
+ exports.isInvoicingQboConnected = isInvoicingQboConnected;
12
+ /**
13
+ * Account types that can back invoicing revenue. Named here so screens ask for
14
+ * "income accounts" rather than repeating the accounting service's vocabulary.
15
+ */
16
+ exports.INVOICING_INCOME_ACCOUNT_TYPES = [
17
+ 'income',
18
+ 'other_income',
19
+ ];
20
+ /**
21
+ * Accounts and classes as the invoicing pickers need them. Both come from the
22
+ * shared accounting-service lists, so whatever another screen already loaded
23
+ * is reused. `qboId` is the submitted value because that is what the invoicing
24
+ * API stores; an account without one cannot be posted, so it is dropped rather
25
+ * than offered.
26
+ */
27
+ const invoicingAccountingOptions = (state, accountTypes) => {
28
+ const { accounts } = (0, accountListSelector_1.getAccountList)(state.accountState, state.accountListState, exports.INVOICING_ACCOUNT_LIST_KEY);
29
+ return accounts.flatMap((account) => {
30
+ if (account.qboId == null || account.isDeleted === true) {
31
+ return [];
32
+ }
33
+ if (accountTypes != null &&
34
+ (account.accountType == null ||
35
+ !accountTypes.includes(account.accountType))) {
36
+ return [];
37
+ }
38
+ return [
39
+ {
40
+ id: String(account.qboId),
41
+ name: account.accountName,
42
+ accountType: account.accountType,
43
+ },
44
+ ];
45
+ });
46
+ };
47
+ /**
48
+ * Normalises an account-type name for comparison. The config's mapping kinds
49
+ * name QuickBooks types the way QuickBooks displays them ("Other Current
50
+ * Asset", "Expense"), while the accounting service uses snake_case and
51
+ * sometimes pluralises ("other_current_assets", "expenses"). Comparing the
52
+ * normalised, de-pluralised forms keeps a picker from silently emptying because
53
+ * of a trailing "s".
54
+ */
55
+ const normalizeAccountTypeName = (value) => {
56
+ const normalized = value
57
+ .trim()
58
+ .toLowerCase()
59
+ .replace(/[^a-z0-9]+/g, '_');
60
+ // Singularise so "Other Current Liability" meets `other_current_liabilities`
61
+ // and "Expense" meets `expenses`. `-ies` needs its own case: stripping the
62
+ // trailing "s" alone would leave "liabilitie", matching nothing.
63
+ return normalized.replace(/ies$/, 'y').replace(/([^s])s$/, '$1');
64
+ };
65
+ /**
66
+ * The accounts valid for one QBO mapping kind, whose `accountTypes` is a
67
+ * comma-separated list of QuickBooks type names. An unrecognised list yields no
68
+ * accounts rather than every account: offering a bank account where income is
69
+ * required would post a mapping QuickBooks rejects.
70
+ */
71
+ const invoicingAccountsForQboAccountTypes = (accounts, accountTypes) => {
72
+ const wanted = new Set(accountTypes
73
+ .split(',')
74
+ .map(normalizeAccountTypeName)
75
+ .filter((accountType) => accountType !== ''));
76
+ if (wanted.size === 0) {
77
+ return accounts;
78
+ }
79
+ return accounts.filter((account) => account.accountType != null &&
80
+ wanted.has(normalizeAccountTypeName(account.accountType)));
81
+ };
82
+ exports.invoicingAccountsForQboAccountTypes = invoicingAccountsForQboAccountTypes;
83
+ /** Classes as the invoicing pickers need them, from the shared class list. */
84
+ const invoicingAccountingClassOptions = (state) => {
85
+ const { classes } = (0, classListSelector_1.getClassList)(state.classState, state.classListState);
86
+ return classes.flatMap((accountingClass) => accountingClass.qboId == null || accountingClass.isDeleted === true
87
+ ? []
88
+ : [
89
+ {
90
+ id: String(accountingClass.qboId),
91
+ name: accountingClass.className,
92
+ },
93
+ ]);
94
+ };
95
+ /**
96
+ * The account tree narrowed to the given account types. The tree itself carries
97
+ * no account type, so the flat account list supplies that and the matching
98
+ * `qboId`s are kept — which is why callers that only need "income accounts, as a
99
+ * tree" do not have to hold a second flat list to filter with.
100
+ */
101
+ const invoicingAccountHierarchyForTypes = (state, accountTypes) => {
102
+ const allowedQboIds = new Set(invoicingAccountingOptions(state, accountTypes).map((account) => account.id));
103
+ const keepMatching = (nodes) => nodes.flatMap((node) => {
104
+ const children = keepMatching(node.children);
105
+ const isMatch = node.qboId != null && allowedQboIds.has(String(node.qboId));
106
+ // A parent that is not itself a match is still kept when it holds one, so
107
+ // the tree's shape survives the filter.
108
+ if (!isMatch && children.length === 0) {
109
+ return [];
110
+ }
111
+ return [{ ...node, children }];
112
+ });
113
+ return keepMatching((0, accountListSelector_1.getNestedAccountListHierarchy)(state.accountListState, exports.INVOICING_ACCOUNT_LIST_KEY));
114
+ };
115
+ exports.invoicingAccountHierarchyForTypes = invoicingAccountHierarchyForTypes;
4
116
  /**
5
117
  * Single bundled view for the QBO integration settings screen — the
6
- * connection, per-account-types account options, classes, sync health, and the
7
- * fetch/action states for each of them.
118
+ * connection, the account and class options, sync health, and the fetch/action
119
+ * states for each of them.
8
120
  */
9
121
  const getInvoicingQboView = (state) => {
10
122
  const qboState = state.invoicingQboViewState;
123
+ const accountList = state.accountListState.byReportId[exports.INVOICING_ACCOUNT_LIST_KEY];
11
124
  return {
12
- accountsByAccountTypes: qboState.accountsByAccountTypes,
13
- accountsFetchStateByAccountTypes: qboState.accountsFetchStateByAccountTypes,
125
+ accounts: invoicingAccountingOptions(state),
126
+ accountsFetchState: {
127
+ fetchState: accountList.fetchState,
128
+ error: accountList.error,
129
+ },
130
+ nestedAccountHierarchy: (0, accountListSelector_1.getNestedAccountListHierarchy)(state.accountListState, exports.INVOICING_ACCOUNT_LIST_KEY),
131
+ nestedClassHierarchy: (0, classListSelector_1.getNestedClassListHierarchy)(state.classListState),
14
132
  backfillState: qboState.backfillFetchState,
15
- classes: qboState.classes,
16
- classesFetchState: qboState.classesFetchState,
133
+ classes: invoicingAccountingClassOptions(state),
134
+ classesFetchState: {
135
+ fetchState: state.classListState.fetchState,
136
+ error: state.classListState.error,
137
+ },
17
138
  connection: qboState.connection,
18
139
  connectionFetchState: qboState.connectionFetchState,
19
140
  retryFetchStateByTaskId: qboState.retryFetchStateByTaskId,
@@ -1,11 +1,7 @@
1
1
  import { FetchStateAndError } from '../../../commonStateTypes/common';
2
- import { InvoicingQboAccountOption, InvoicingQboConnection, InvoicingQboSyncHealth } from './invoicingQboViewPayload';
2
+ import { InvoicingQboConnection, InvoicingQboSyncHealth } from './invoicingQboViewPayload';
3
3
  export interface InvoicingQboViewState {
4
- accountsByAccountTypes: Record<string, InvoicingQboAccountOption[]>;
5
- accountsFetchStateByAccountTypes: Record<string, FetchStateAndError>;
6
4
  backfillFetchState: FetchStateAndError;
7
- classes: InvoicingQboAccountOption[];
8
- classesFetchState: FetchStateAndError;
9
5
  connectionFetchState: FetchStateAndError;
10
6
  retryFetchStateByTaskId: Record<string, FetchStateAndError>;
11
7
  syncHealthFetchState: FetchStateAndError;
@@ -58,7 +58,7 @@ export interface TaskListUIState extends SpendManagementUIState {
58
58
  searchText: string;
59
59
  sortKey: TaskListViewSortKey;
60
60
  }
61
- declare const toTaskListSortKeyType: (v: string) => "status" | "dueDate" | "name" | "priority" | "assignee" | "creationDate" | "tag" | "timeSpent" | "visibility";
61
+ declare const toTaskListSortKeyType: (v: string) => "status" | "dueDate" | "name" | "priority" | "assignee" | "creationDate" | "tag" | "visibility" | "timeSpent";
62
62
  export declare type TaskListViewSortKey = ReturnType<typeof toTaskListSortKeyType>;
63
63
  export declare const TASK_LIST_FILTER_CATEGORIES: TaskListFilterCategoryOption[];
64
64
  interface TaskListFilterCategoryOption {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zeniai/client-epic-state",
3
- "version": "5.2.4",
3
+ "version": "5.2.6",
4
4
  "description": "Shared module between Web & Mobile containing required abstractions for state management, async network communication. ",
5
5
  "main": "lib/index.js",
6
6
  "module": "lib/esm/index.js",
@@ -1,25 +0,0 @@
1
- import { of } from 'rxjs';
2
- import { catchError, filter, map, mergeMap } from 'rxjs/operators';
3
- import { createZeniAPIStatus, isSuccessResponse, } from '../../../../responsePayload';
4
- import { mapInvoicingQboAccountOptions, } from '../invoicingQboViewPayload';
5
- import { fetchInvoicingQboAccounts, updateInvoicingQboAccounts, updateInvoicingQboAccountsFailure, } from '../invoicingQboViewReducer';
6
- export const fetchInvoicingQboAccountsEpic = (actions$, _state$, zeniAPI) => actions$.pipe(filter(fetchInvoicingQboAccounts.match), mergeMap((action) => {
7
- const { accountTypes } = action.payload;
8
- const apiUrl = `${zeniAPI.apiEndPoints.invoicingMicroServiceBaseUrl}/1.0/qbo/accounts?account_types=${encodeURIComponent(accountTypes)}`;
9
- return zeniAPI.getJSON(apiUrl).pipe(map((response) => {
10
- if (isSuccessResponse(response)) {
11
- return updateInvoicingQboAccounts({
12
- accountTypes,
13
- accounts: mapInvoicingQboAccountOptions(response.data?.accounts ?? []),
14
- });
15
- }
16
- return updateInvoicingQboAccountsFailure({
17
- accountTypes,
18
- status: response.status,
19
- });
20
- }), catchError((error) => of(updateInvoicingQboAccountsFailure({
21
- accountTypes,
22
- status: createZeniAPIStatus('Unexpected Error', 'Fetch QBO accounts REST API call errored out ' +
23
- JSON.stringify(error)),
24
- }))));
25
- }));
@@ -1,15 +0,0 @@
1
- import { of } from 'rxjs';
2
- import { catchError, filter, map, switchMap } from 'rxjs/operators';
3
- import { createZeniAPIStatus, isSuccessResponse, } from '../../../../responsePayload';
4
- import { mapInvoicingQboAccountOptions, } from '../invoicingQboViewPayload';
5
- import { fetchInvoicingQboClasses, updateInvoicingQboClasses, updateInvoicingQboClassesFailure, } from '../invoicingQboViewReducer';
6
- export const fetchInvoicingQboClassesEpic = (actions$, _state$, zeniAPI) => actions$.pipe(filter(fetchInvoicingQboClasses.match), switchMap(() => {
7
- const apiUrl = `${zeniAPI.apiEndPoints.invoicingMicroServiceBaseUrl}/1.0/qbo/classes`;
8
- return zeniAPI.getJSON(apiUrl).pipe(map((response) => {
9
- if (isSuccessResponse(response)) {
10
- return updateInvoicingQboClasses(mapInvoicingQboAccountOptions(response.data?.accounts ?? []));
11
- }
12
- return updateInvoicingQboClassesFailure(response.status);
13
- }), catchError((error) => of(updateInvoicingQboClassesFailure(createZeniAPIStatus('Unexpected Error', 'Fetch QBO classes REST API call errored out ' +
14
- JSON.stringify(error))))));
15
- }));
@@ -1,6 +0,0 @@
1
- import { ActionsObservable, StateObservable } from 'redux-observable';
2
- import { Observable } from 'rxjs';
3
- import { RootState } from '../../../../rootStateTypes';
4
- import { ZeniAPI } from '../../../../zeniAPI';
5
- import { InvoicingQboViewActionType } from './invoicingQboViewActionType';
6
- export declare const fetchInvoicingQboAccountsEpic: (actions$: ActionsObservable<InvoicingQboViewActionType>, _state$: StateObservable<RootState>, zeniAPI: ZeniAPI) => Observable<InvoicingQboViewActionType>;
@@ -1,29 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.fetchInvoicingQboAccountsEpic = void 0;
4
- const rxjs_1 = require("rxjs");
5
- const operators_1 = require("rxjs/operators");
6
- const responsePayload_1 = require("../../../../responsePayload");
7
- const invoicingQboViewPayload_1 = require("../invoicingQboViewPayload");
8
- const invoicingQboViewReducer_1 = require("../invoicingQboViewReducer");
9
- const fetchInvoicingQboAccountsEpic = (actions$, _state$, zeniAPI) => actions$.pipe((0, operators_1.filter)(invoicingQboViewReducer_1.fetchInvoicingQboAccounts.match), (0, operators_1.mergeMap)((action) => {
10
- const { accountTypes } = action.payload;
11
- const apiUrl = `${zeniAPI.apiEndPoints.invoicingMicroServiceBaseUrl}/1.0/qbo/accounts?account_types=${encodeURIComponent(accountTypes)}`;
12
- return zeniAPI.getJSON(apiUrl).pipe((0, operators_1.map)((response) => {
13
- if ((0, responsePayload_1.isSuccessResponse)(response)) {
14
- return (0, invoicingQboViewReducer_1.updateInvoicingQboAccounts)({
15
- accountTypes,
16
- accounts: (0, invoicingQboViewPayload_1.mapInvoicingQboAccountOptions)(response.data?.accounts ?? []),
17
- });
18
- }
19
- return (0, invoicingQboViewReducer_1.updateInvoicingQboAccountsFailure)({
20
- accountTypes,
21
- status: response.status,
22
- });
23
- }), (0, operators_1.catchError)((error) => (0, rxjs_1.of)((0, invoicingQboViewReducer_1.updateInvoicingQboAccountsFailure)({
24
- accountTypes,
25
- status: (0, responsePayload_1.createZeniAPIStatus)('Unexpected Error', 'Fetch QBO accounts REST API call errored out ' +
26
- JSON.stringify(error)),
27
- }))));
28
- }));
29
- exports.fetchInvoicingQboAccountsEpic = fetchInvoicingQboAccountsEpic;
@@ -1,6 +0,0 @@
1
- import { ActionsObservable, StateObservable } from 'redux-observable';
2
- import { Observable } from 'rxjs';
3
- import { RootState } from '../../../../rootStateTypes';
4
- import { ZeniAPI } from '../../../../zeniAPI';
5
- import { InvoicingQboViewActionType } from './invoicingQboViewActionType';
6
- export declare const fetchInvoicingQboClassesEpic: (actions$: ActionsObservable<InvoicingQboViewActionType>, _state$: StateObservable<RootState>, zeniAPI: ZeniAPI) => Observable<InvoicingQboViewActionType>;
@@ -1,19 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.fetchInvoicingQboClassesEpic = void 0;
4
- const rxjs_1 = require("rxjs");
5
- const operators_1 = require("rxjs/operators");
6
- const responsePayload_1 = require("../../../../responsePayload");
7
- const invoicingQboViewPayload_1 = require("../invoicingQboViewPayload");
8
- const invoicingQboViewReducer_1 = require("../invoicingQboViewReducer");
9
- const fetchInvoicingQboClassesEpic = (actions$, _state$, zeniAPI) => actions$.pipe((0, operators_1.filter)(invoicingQboViewReducer_1.fetchInvoicingQboClasses.match), (0, operators_1.switchMap)(() => {
10
- const apiUrl = `${zeniAPI.apiEndPoints.invoicingMicroServiceBaseUrl}/1.0/qbo/classes`;
11
- return zeniAPI.getJSON(apiUrl).pipe((0, operators_1.map)((response) => {
12
- if ((0, responsePayload_1.isSuccessResponse)(response)) {
13
- return (0, invoicingQboViewReducer_1.updateInvoicingQboClasses)((0, invoicingQboViewPayload_1.mapInvoicingQboAccountOptions)(response.data?.accounts ?? []));
14
- }
15
- return (0, invoicingQboViewReducer_1.updateInvoicingQboClassesFailure)(response.status);
16
- }), (0, operators_1.catchError)((error) => (0, rxjs_1.of)((0, invoicingQboViewReducer_1.updateInvoicingQboClassesFailure)((0, responsePayload_1.createZeniAPIStatus)('Unexpected Error', 'Fetch QBO classes REST API call errored out ' +
17
- JSON.stringify(error))))));
18
- }));
19
- exports.fetchInvoicingQboClassesEpic = fetchInvoicingQboClassesEpic;