@zeniai/client-epic-state 5.3.5 → 5.3.7-betaRD0
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.
- package/lib/entity/customerIncome/customerIncomeTrend.d.ts +18 -2
- package/lib/entity/customerIncome/customerIncomeTrend.js +65 -5
- package/lib/entity/insights/insightSummary.d.ts +3 -0
- package/lib/entity/insights/insightSummaryPayload.js +2 -0
- package/lib/entity/invoicing/invoice/invoicePayload.d.ts +1 -0
- package/lib/entity/invoicing/invoice/invoicePayload.js +3 -0
- package/lib/entity/invoicing/invoice/invoiceState.d.ts +2 -0
- package/lib/entity/snackbar/snackbarTypes.d.ts +1 -1
- package/lib/entity/snackbar/snackbarTypes.js +1 -0
- package/lib/entity/tenant/epic/fetchAllTenantsEpic.js +45 -38
- package/lib/entity/vendorExpense/vendorSpendTrendFilterTabType.d.ts +2 -2
- package/lib/epic.d.ts +4 -1
- package/lib/epic.js +6 -0
- package/lib/esm/entity/customerIncome/customerIncomeTrend.js +63 -4
- package/lib/esm/entity/insights/insightSummaryPayload.js +2 -0
- package/lib/esm/entity/invoicing/invoice/invoicePayload.js +3 -0
- package/lib/esm/entity/snackbar/snackbarTypes.js +1 -0
- package/lib/esm/entity/tenant/epic/fetchAllTenantsEpic.js +46 -39
- package/lib/esm/epic.js +6 -0
- package/lib/esm/index.js +5 -2
- package/lib/esm/init.js +14 -3
- package/lib/esm/reducer.js +3 -0
- package/lib/esm/sessionEndpointsLoader.js +7 -0
- package/lib/esm/view/customerSummaryView/customerSummaryViewPayload.js +1 -0
- package/lib/esm/view/customerSummaryView/customerSummaryViewReducer.js +197 -0
- package/lib/esm/view/customerSummaryView/customerSummaryViewSelector.js +35 -0
- package/lib/esm/view/customerSummaryView/customerSummaryViewState.js +10 -0
- package/lib/esm/view/customerSummaryView/epic/fetchCustomerSummaryDetailEpic.js +30 -0
- package/lib/esm/view/customerSummaryView/epic/fetchCustomerSummaryListEpic.js +27 -0
- package/lib/esm/view/customerSummaryView/epic/saveCustomerSummaryDetailEpic.js +44 -0
- package/lib/esm/view/expenseAutomationView/epics/accountRecon/fetchReconciliationViewEpic.js +2 -1
- package/lib/esm/view/trend/fetchIncomeTrendEpic.js +29 -6
- package/lib/esm/view/trend/trendReducer.js +4 -2
- package/lib/index.d.ts +9 -2
- package/lib/index.js +73 -51
- package/lib/init.d.ts +9 -0
- package/lib/init.js +17 -4
- package/lib/reducer.d.ts +1 -0
- package/lib/reducer.js +3 -0
- package/lib/rootStateTypes.d.ts +2 -0
- package/lib/sessionEndpointsLoader.d.ts +9 -0
- package/lib/sessionEndpointsLoader.js +11 -0
- package/lib/tsconfig.mocks.tsbuildinfo +1 -0
- package/lib/tsconfig.typecheck.tsbuildinfo +1 -0
- package/lib/view/commonVendorView/vendorsTabVendorView/vendorsTabVendorViewReducer.d.ts +2 -2
- package/lib/view/customerSummaryView/customerSummaryViewPayload.d.ts +38 -0
- package/lib/view/customerSummaryView/customerSummaryViewPayload.js +2 -0
- package/lib/view/customerSummaryView/customerSummaryViewReducer.d.ts +32 -0
- package/lib/view/customerSummaryView/customerSummaryViewReducer.js +202 -0
- package/lib/view/customerSummaryView/customerSummaryViewSelector.d.ts +20 -0
- package/lib/view/customerSummaryView/customerSummaryViewSelector.js +43 -0
- package/lib/view/customerSummaryView/customerSummaryViewState.d.ts +63 -0
- package/lib/view/customerSummaryView/customerSummaryViewState.js +13 -0
- package/lib/view/customerSummaryView/epic/fetchCustomerSummaryDetailEpic.d.ts +9 -0
- package/lib/view/customerSummaryView/epic/fetchCustomerSummaryDetailEpic.js +34 -0
- package/lib/view/customerSummaryView/epic/fetchCustomerSummaryListEpic.d.ts +7 -0
- package/lib/view/customerSummaryView/epic/fetchCustomerSummaryListEpic.js +31 -0
- package/lib/view/customerSummaryView/epic/saveCustomerSummaryDetailEpic.d.ts +8 -0
- package/lib/view/customerSummaryView/epic/saveCustomerSummaryDetailEpic.js +48 -0
- package/lib/view/expenseAutomationView/epics/accountRecon/fetchReconciliationViewEpic.js +2 -1
- package/lib/view/expenseAutomationView/helpers/transactionCategorizationLocalDataHelper.d.ts +1 -1
- package/lib/view/trend/fetchIncomeTrendEpic.d.ts +1 -1
- package/lib/view/trend/fetchIncomeTrendEpic.js +28 -5
- package/lib/view/trend/trendReducer.d.ts +8 -1
- package/lib/view/trend/trendReducer.js +4 -2
- package/lib/zeniAPI.d.ts +2 -2
- package/package.json +26 -1
|
@@ -9,7 +9,9 @@ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (
|
|
|
9
9
|
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
|
10
10
|
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
11
11
|
};
|
|
12
|
-
var _CustomerIncomeTrend_customer, _CustomerIncomeTrend_timePeriod, _CustomerIncomeTrend_incomeTrend, _CustomerIncomeTrend_insights;
|
|
12
|
+
var _CustomerIncomeTrend_customer, _CustomerIncomeTrend_filter, _CustomerIncomeTrend_timePeriod, _CustomerIncomeTrend_incomeTrend, _CustomerIncomeTrend_insights;
|
|
13
|
+
import { dateNow } from '../../zeniDayJS';
|
|
14
|
+
import { getFilteredExpenseTrend } from '../vendorExpense/vendorExpenseTrend';
|
|
13
15
|
import { getCustomerIncomeTrendKey } from './customerIncomeTrendKey';
|
|
14
16
|
export class CustomerIncomeTrend {
|
|
15
17
|
get balancesKind() {
|
|
@@ -19,7 +21,18 @@ export class CustomerIncomeTrend {
|
|
|
19
21
|
return 'ascending_date';
|
|
20
22
|
}
|
|
21
23
|
get monthly() {
|
|
22
|
-
|
|
24
|
+
const months = __classPrivateFieldGet(this, _CustomerIncomeTrend_incomeTrend, "f").months ?? [];
|
|
25
|
+
if (__classPrivateFieldGet(this, _CustomerIncomeTrend_filter, "f") == null) {
|
|
26
|
+
return months;
|
|
27
|
+
}
|
|
28
|
+
const { filter, firstMonthOfFY, last30DaysRevenue, last90DaysRevenue } = __classPrivateFieldGet(this, _CustomerIncomeTrend_filter, "f");
|
|
29
|
+
if (filter === 'last_30_days') {
|
|
30
|
+
return getLastNDaysMonths(months, last30DaysRevenue, 30);
|
|
31
|
+
}
|
|
32
|
+
if (filter === 'last_90_days') {
|
|
33
|
+
return getLastNDaysMonths(months, last90DaysRevenue, 90);
|
|
34
|
+
}
|
|
35
|
+
return getFilteredExpenseTrend({ ...__classPrivateFieldGet(this, _CustomerIncomeTrend_incomeTrend, "f"), last30DaysMonths: null, last90DaysMonths: null }, filter, firstMonthOfFY);
|
|
23
36
|
}
|
|
24
37
|
get quarterly() {
|
|
25
38
|
return __classPrivateFieldGet(this, _CustomerIncomeTrend_incomeTrend, "f").quarters ?? [];
|
|
@@ -48,8 +61,17 @@ export class CustomerIncomeTrend {
|
|
|
48
61
|
get key() {
|
|
49
62
|
return getCustomerIncomeTrendKey(__classPrivateFieldGet(this, _CustomerIncomeTrend_customer, "f").id, __classPrivateFieldGet(this, _CustomerIncomeTrend_timePeriod, "f"));
|
|
50
63
|
}
|
|
51
|
-
|
|
64
|
+
withFilter(filter) {
|
|
65
|
+
return new CustomerIncomeTrend({
|
|
66
|
+
customer: __classPrivateFieldGet(this, _CustomerIncomeTrend_customer, "f"),
|
|
67
|
+
timePeriod: __classPrivateFieldGet(this, _CustomerIncomeTrend_timePeriod, "f"),
|
|
68
|
+
incomeTrend: __classPrivateFieldGet(this, _CustomerIncomeTrend_incomeTrend, "f"),
|
|
69
|
+
insights: __classPrivateFieldGet(this, _CustomerIncomeTrend_insights, "f"),
|
|
70
|
+
}, filter);
|
|
71
|
+
}
|
|
72
|
+
constructor({ customer, timePeriod, incomeTrend, insights }, filter) {
|
|
52
73
|
_CustomerIncomeTrend_customer.set(this, void 0);
|
|
74
|
+
_CustomerIncomeTrend_filter.set(this, void 0);
|
|
53
75
|
_CustomerIncomeTrend_timePeriod.set(this, void 0);
|
|
54
76
|
_CustomerIncomeTrend_incomeTrend.set(this, void 0);
|
|
55
77
|
_CustomerIncomeTrend_insights.set(this, void 0);
|
|
@@ -57,9 +79,46 @@ export class CustomerIncomeTrend {
|
|
|
57
79
|
__classPrivateFieldSet(this, _CustomerIncomeTrend_timePeriod, timePeriod, "f");
|
|
58
80
|
__classPrivateFieldSet(this, _CustomerIncomeTrend_incomeTrend, incomeTrend, "f");
|
|
59
81
|
__classPrivateFieldSet(this, _CustomerIncomeTrend_insights, insights, "f");
|
|
82
|
+
__classPrivateFieldSet(this, _CustomerIncomeTrend_filter, filter, "f");
|
|
60
83
|
}
|
|
61
84
|
}
|
|
62
|
-
_CustomerIncomeTrend_customer = new WeakMap(), _CustomerIncomeTrend_timePeriod = new WeakMap(), _CustomerIncomeTrend_incomeTrend = new WeakMap(), _CustomerIncomeTrend_insights = new WeakMap();
|
|
85
|
+
_CustomerIncomeTrend_customer = new WeakMap(), _CustomerIncomeTrend_filter = new WeakMap(), _CustomerIncomeTrend_timePeriod = new WeakMap(), _CustomerIncomeTrend_incomeTrend = new WeakMap(), _CustomerIncomeTrend_insights = new WeakMap();
|
|
86
|
+
/**
|
|
87
|
+
* Months covering the last `n` days, oldest month cut so the slices add up to the precomputed
|
|
88
|
+
* last-n-days total. Port of accounting `SpendTrendReport._prepare_last_n_days_time_slice`
|
|
89
|
+
* (Vendors gets these slices from the spend_trend API; income_trend has no equivalent).
|
|
90
|
+
* The cut slice is clamped at 0: the total (gl_entries, daily) and the months (live QBO) can disagree.
|
|
91
|
+
*/
|
|
92
|
+
export const getLastNDaysMonths = (months, lastNDaysAmount, n) => {
|
|
93
|
+
const findMonth = (start) => months.find((month) => month.startDate.isSame(start, 'day'));
|
|
94
|
+
const withAmount = (month, amount) => ({
|
|
95
|
+
...month,
|
|
96
|
+
balance: { ...month.balance, amount: Math.max(amount, 0) },
|
|
97
|
+
});
|
|
98
|
+
const now = dateNow();
|
|
99
|
+
let monthStart = now.startOf('month');
|
|
100
|
+
let daysCollected = now.date();
|
|
101
|
+
const currentMonth = findMonth(monthStart);
|
|
102
|
+
// The current month alone covers the n days (e.g. the 31st for last 30 days): it is the whole total.
|
|
103
|
+
if (daysCollected >= n) {
|
|
104
|
+
return currentMonth != null
|
|
105
|
+
? [withAmount(currentMonth, lastNDaysAmount)]
|
|
106
|
+
: [];
|
|
107
|
+
}
|
|
108
|
+
const slices = currentMonth != null ? [currentMonth] : [];
|
|
109
|
+
let total = currentMonth?.balance.amount ?? 0;
|
|
110
|
+
while (daysCollected < n) {
|
|
111
|
+
monthStart = monthStart.subtract(1, 'month');
|
|
112
|
+
daysCollected += monthStart.daysInMonth();
|
|
113
|
+
const month = findMonth(monthStart);
|
|
114
|
+
if (month != null) {
|
|
115
|
+
const slice = daysCollected > n ? withAmount(month, lastNDaysAmount - total) : month;
|
|
116
|
+
total += slice.balance.amount;
|
|
117
|
+
slices.unshift(slice);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return slices;
|
|
121
|
+
};
|
|
63
122
|
/**
|
|
64
123
|
* Returns an average amount for the list of COABalances
|
|
65
124
|
* @param coaBalances
|
|
@@ -14,6 +14,9 @@ const mapLineItem = (payload, currencyCode, currencySymbol) => ({
|
|
|
14
14
|
: undefined,
|
|
15
15
|
entityDescription: payload.entity_description,
|
|
16
16
|
entityType: payload.entity_type,
|
|
17
|
+
grossAmount: payload.gross_amount != null
|
|
18
|
+
? toAmount(payload.gross_amount, currencyCode, currencySymbol)
|
|
19
|
+
: undefined,
|
|
17
20
|
id: payload.id,
|
|
18
21
|
isTaxed: payload.is_taxed,
|
|
19
22
|
itemLevelDiscountAmount: payload.item_level_discount_amount != null
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { from, of } from 'rxjs';
|
|
1
|
+
import { defer, from, of } from 'rxjs';
|
|
2
2
|
import { catchError, filter, mergeMap, switchMap } from 'rxjs/operators';
|
|
3
3
|
import { toMonth } from '../../../commonStateTypes/timePeriod';
|
|
4
4
|
import { createZeniAPIStatus, isSuccessResponse } from '../../../responsePayload';
|
|
5
|
+
import { getSessionEndpointsLoader } from '../../../sessionEndpointsLoader';
|
|
5
6
|
import { initializeSelectedPeriodForTenants } from '../../../view/expenseAutomationView/expenseAutomationViewReducer';
|
|
6
7
|
import { date, dateNow } from '../../../zeniDayJS';
|
|
7
8
|
import { updateCompanies } from '../../company/companyReducer';
|
|
@@ -10,48 +11,54 @@ export const fetchAllTenantsEpic = (actions$, state, zeniAPI) => {
|
|
|
10
11
|
const queryParam = {
|
|
11
12
|
is_include_companies_in_response: true,
|
|
12
13
|
};
|
|
13
|
-
return actions$.pipe(filter(fetchAllTenants.match), switchMap((action) =>
|
|
14
|
-
.getJSON(`${zeniAPI.apiEndPoints.tenantMicroServiceBaseUrl}/1.0/users/${action.payload.userId}/tenants?query=${encodeURIComponent(JSON.stringify(queryParam))}`, Object.assign({ 'zeni-user-id': action.payload.userId }, { 'zeni-tenant-id': '' }, action.payload.zeniSessionId != null
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
selectedPeriodByTenantId: defaultSelectedPeriodsForTenants,
|
|
25
|
-
}),
|
|
26
|
-
];
|
|
27
|
-
if (response.data.companies != null) {
|
|
28
|
-
let companiesPayload = response.data.companies;
|
|
29
|
-
if (currentTenantId != null) {
|
|
30
|
-
// filter out this tenant ID related company detail
|
|
31
|
-
// Find companyID for this tenant
|
|
32
|
-
const currentTenantPayload = response.data.tenants.find((tenant) => tenant.tenant_id === currentTenantId);
|
|
33
|
-
if (currentTenantPayload != null) {
|
|
34
|
-
companiesPayload = response.data.companies.filter((company) => company.company_id !== currentTenantPayload.company_id);
|
|
35
|
-
}
|
|
36
|
-
else {
|
|
37
|
-
console.warn('Selected tenant record not found in response', currentTenantId);
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
actions = [
|
|
41
|
-
updateCompanies({
|
|
42
|
-
payload: companiesPayload,
|
|
43
|
-
schema: {},
|
|
14
|
+
return actions$.pipe(filter(fetchAllTenants.match), switchMap((action) => {
|
|
15
|
+
const fetchTenants$ = defer(() => zeniAPI.getJSON(`${zeniAPI.apiEndPoints.tenantMicroServiceBaseUrl}/1.0/users/${action.payload.userId}/tenants?query=${encodeURIComponent(JSON.stringify(queryParam))}`, Object.assign({ 'zeni-user-id': action.payload.userId }, { 'zeni-tenant-id': '' }, action.payload.zeniSessionId != null
|
|
16
|
+
? { 'zeni-session-id': action.payload.zeniSessionId }
|
|
17
|
+
: {}))).pipe(mergeMap((response) => {
|
|
18
|
+
const currentTenantId = state.value.tenantState.currentTenantId;
|
|
19
|
+
if (isSuccessResponse(response) && response.data != null) {
|
|
20
|
+
const defaultSelectedPeriodsForTenants = getDefaultSelectedPeriodsForTenants(response.data);
|
|
21
|
+
let actions = [
|
|
22
|
+
updateTenantsSuccess(response.data),
|
|
23
|
+
initializeSelectedPeriodForTenants({
|
|
24
|
+
selectedPeriodByTenantId: defaultSelectedPeriodsForTenants,
|
|
44
25
|
}),
|
|
45
|
-
...actions,
|
|
46
26
|
];
|
|
27
|
+
if (response.data.companies != null) {
|
|
28
|
+
let companiesPayload = response.data.companies;
|
|
29
|
+
if (currentTenantId != null) {
|
|
30
|
+
// filter out this tenant ID related company detail
|
|
31
|
+
// Find companyID for this tenant
|
|
32
|
+
const currentTenantPayload = response.data.tenants.find((tenant) => tenant.tenant_id === currentTenantId);
|
|
33
|
+
if (currentTenantPayload != null) {
|
|
34
|
+
companiesPayload = response.data.companies.filter((company) => company.company_id !== currentTenantPayload.company_id);
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
console.warn('Selected tenant record not found in response', currentTenantId);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
actions = [
|
|
41
|
+
updateCompanies({
|
|
42
|
+
payload: companiesPayload,
|
|
43
|
+
schema: {},
|
|
44
|
+
}),
|
|
45
|
+
...actions,
|
|
46
|
+
];
|
|
47
|
+
}
|
|
48
|
+
return from(actions);
|
|
47
49
|
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
50
|
+
else {
|
|
51
|
+
return of(updateTenantsFailure(response.status));
|
|
52
|
+
}
|
|
53
|
+
}), catchError((error) => of(updateTenantsFailure(createZeniAPIStatus('Unexpected error', 'Fetch All Tenants REST API call errored out' +
|
|
54
|
+
JSON.stringify(error))))));
|
|
55
|
+
const sessionEndpointsLoader = getSessionEndpointsLoader();
|
|
56
|
+
if (sessionEndpointsLoader == null) {
|
|
57
|
+
return fetchTenants$;
|
|
52
58
|
}
|
|
53
|
-
|
|
54
|
-
|
|
59
|
+
return defer(() => from(sessionEndpointsLoader(action.payload.userId, action.payload.zeniSessionId))).pipe(switchMap(() => fetchTenants$), catchError((error) => of(updateTenantsFailure(createZeniAPIStatus('Unexpected error', 'Loading session endpoints errored out: ' +
|
|
60
|
+
JSON.stringify(error))))));
|
|
61
|
+
}));
|
|
55
62
|
};
|
|
56
63
|
export const defaultMonthYearPeriod = {
|
|
57
64
|
month: toMonth(dateNow().get('month') + 1),
|
package/lib/esm/epic.js
CHANGED
|
@@ -178,6 +178,9 @@ import { fetchCreditAgentMacroEpic, } from './view/creditAgentView/epics/fetchCr
|
|
|
178
178
|
import { saveCreditAgentMacroEpic, } from './view/creditAgentView/epics/saveCreditAgentMacroEpic';
|
|
179
179
|
import { scheduleTenantCreditScoreCronEpic, } from './view/creditAgentView/epics/scheduleTenantCreditScoreCronEpic';
|
|
180
180
|
import { updateCardProfileEpic, } from './view/creditAgentView/epics/updateCardProfileEpic';
|
|
181
|
+
import { fetchCustomerSummaryDetailEpic, } from './view/customerSummaryView/epic/fetchCustomerSummaryDetailEpic';
|
|
182
|
+
import { fetchCustomerSummaryListEpic, } from './view/customerSummaryView/epic/fetchCustomerSummaryListEpic';
|
|
183
|
+
import { saveCustomerSummaryDetailEpic, } from './view/customerSummaryView/epic/saveCustomerSummaryDetailEpic';
|
|
181
184
|
import { fetchDashboardEpic, } from './view/dashboard/dashboardEpic';
|
|
182
185
|
import { fetchDashboardLayoutEpic, } from './view/dashboardLayout/fetchDashboardLayoutEpic';
|
|
183
186
|
import { updateDashboardLayoutEpic, } from './view/dashboardLayout/updateDashboardLayoutEpic';
|
|
@@ -1003,6 +1006,8 @@ const ALL_EPICS = [
|
|
|
1003
1006
|
fetchCreditAccountRepaymentEpic,
|
|
1004
1007
|
fetchCreditAgentMacroEpic,
|
|
1005
1008
|
fetchCurrencyConversionValueEpic,
|
|
1009
|
+
fetchCustomerSummaryDetailEpic,
|
|
1010
|
+
fetchCustomerSummaryListEpic,
|
|
1006
1011
|
fetchDashboardEpic,
|
|
1007
1012
|
fetchDashboardLayoutEpic,
|
|
1008
1013
|
fetchDebitCardSummaryEpic,
|
|
@@ -1401,6 +1406,7 @@ const ALL_EPICS = [
|
|
|
1401
1406
|
saveCompanyPassportDetailsEpic,
|
|
1402
1407
|
saveConnectorCredentialsEpic,
|
|
1403
1408
|
saveCreditAgentMacroEpic,
|
|
1409
|
+
saveCustomerSummaryDetailEpic,
|
|
1404
1410
|
saveExpenseAutomationReconciliationDetailsEpic,
|
|
1405
1411
|
saveExpenseAutomationReconciliationReviewEpic,
|
|
1406
1412
|
saveExternalConnectionEpic,
|
package/lib/esm/index.js
CHANGED
|
@@ -90,7 +90,7 @@ import { getUserRoleMap, hasAICFOAccess, hasAdminLevelAccess, hasAnyCompanyRole,
|
|
|
90
90
|
import { updateVendors } from './entity/vendor/vendorReducer';
|
|
91
91
|
import { defaultVendorCurrency, } from './entity/vendor/vendorState';
|
|
92
92
|
import { VendorExpenseTrend } from './entity/vendorExpense/vendorExpenseTrend';
|
|
93
|
-
import initialize, { disconnectPusher, initializePusher, initializeWithNewStore, injectFeatureEpics, injectFeatureModules, updateZeniAPIClientConfig, zeniAPIClientConfig, } from './init';
|
|
93
|
+
import initialize, { disconnectPusher, initializePusher, initializeWithNewStore, injectFeatureEpics, injectFeatureModules, setSessionEndpointsLoader, updateZeniAPIClientConfig, updateZeniAPIEndpoints, zeniAPIClientConfig, } from './init';
|
|
94
94
|
import { isAccessDeniedError, isInvalidSessionError, isSuccessResponse, } from './responsePayload';
|
|
95
95
|
import { clearAll } from './rootActions';
|
|
96
96
|
import { fetchAccountList } from './view/accountList/accountListReducer';
|
|
@@ -485,7 +485,7 @@ export { updateCreditAgentMacro, updateCreditAgentRow, updateCreditAgentRows, }
|
|
|
485
485
|
export { getCreditAgentRows, getCreditAgentMacroEntity, } from './entity/creditAgent/creditAgentSelector';
|
|
486
486
|
export { saveJeAccountSettings, saveJeAccountSettingsLocalData };
|
|
487
487
|
export default initialize;
|
|
488
|
-
export { initializePusher, disconnectPusher, Dayjs, dateInLocal as zeniDateInLocal, date as zeniDate, dateLocal, dateNow, getBusinessDayOfDate, formatZeniDateFY as formatZeniDate, getFYMonths, getFYQuarterAndYear, getStartOfAndEndOfTimeframeForFY, getActualPeriodOfFYQtr, getActualPeriodOfFY, getLocalTimezone, addPeriod, subtractPeriod, setLocalTimezone, updateZeniDateLocaleID, toZeniUrl, toZeniUrlWithoutBaseURL, updateZeniAPIClientConfig, zeniAPIClientConfig, injectFeatureEpics, injectFeatureModules, initializeWithNewStore, getTimeframeTickTag, isIncompleteMonthPresentInTimeframeTick, getLastMonthOfFYQuarter, getLastMonthOfFYYear, getMonthIndex, dateFromYearMonthDate, getMinDate, };
|
|
488
|
+
export { initializePusher, disconnectPusher, Dayjs, dateInLocal as zeniDateInLocal, date as zeniDate, dateLocal, dateNow, getBusinessDayOfDate, formatZeniDateFY as formatZeniDate, getFYMonths, getFYQuarterAndYear, getStartOfAndEndOfTimeframeForFY, getActualPeriodOfFYQtr, getActualPeriodOfFY, getLocalTimezone, addPeriod, subtractPeriod, setLocalTimezone, updateZeniDateLocaleID, toZeniUrl, toZeniUrlWithoutBaseURL, updateZeniAPIClientConfig, updateZeniAPIEndpoints, setSessionEndpointsLoader, zeniAPIClientConfig, injectFeatureEpics, injectFeatureModules, initializeWithNewStore, getTimeframeTickTag, isIncompleteMonthPresentInTimeframeTick, getLastMonthOfFYQuarter, getLastMonthOfFYYear, getMonthIndex, dateFromYearMonthDate, getMinDate, };
|
|
489
489
|
export { toAmount };
|
|
490
490
|
export { toReportID, toReportFormatStrict, isCashFlowOrBalanceSheetReport, isBalanceSheetReport, isBalanceSheetClassesViewReport, isBalanceSheetProjectViewReport, isCashFlowClassesViewReport, isCashFlowProjectViewReport, isCashFlowReport, isGroupedCashFlowReport, isGroupedBalanceSheetReport, isDashboardReport, isDashboardClassesViewReport, isDashboardProjectViewReport, isPAndLReport, isPAndLClassesViewReport, isPAndLProjectViewReport, isAgingReport, isOpExByVendorReport, isFluxAnalysisOpExReport, };
|
|
491
491
|
export { toCOABalanceTimeframe, };
|
|
@@ -872,3 +872,6 @@ export { getInvoicingOnboardingGateView } from './view/invoicing/invoicingConfig
|
|
|
872
872
|
// narrow a JEScheduleRow before reading baseTransaction / recurringAmount.
|
|
873
873
|
export { getExpenseAutomationJEOneTimeAccrualDetailView, getJEScheduleRowType, isAccruedScheduleRow, isAiAccountantSchedule, isExistingTabRow, isOneTimeAccrualRow, isRenderableOneTimeAccrual, isPendingReviewRow, } from './view/expenseAutomationView/selectors/jeSchedulesViewSelector';
|
|
874
874
|
export { getActualMatchingAmount, getActualMatchingReversalDate, getActualMatchingMatchModalConfirmStatus, getCloseMatchAfterIgnoreMatchId, getCloseMatchAfterUndo, getCloseMatchAfterUndoMatchId, parseActualMatchingMatchId, getExpenseAutomationActualMatchingMatchView, getExpenseAutomationActualMatchingView, isAutoReversedAccrual, isManuallyReversedAccrual, isPendingReviewAccrual, } from './view/expenseAutomationView/selectors/actualMatchingViewSelector';
|
|
875
|
+
export { fetchCustomerSummaryDetail, fetchCustomerSummaryList, resetCustomerSummaryDetailUiState, saveCustomerSummaryDetail, updateCustomerAllTransactionsTabData, updateCustomerDetailSelectedTab, updateCustomerSummaryRevenueSelection, updateCustomerSummaryScrollYOffset, updateCustomerSummarySortUiState, updateCustomerTrendFilter, } from './view/customerSummaryView/customerSummaryViewReducer';
|
|
876
|
+
export { getCustomerSummaryAllTransactionsTabData, getCustomerSummaryDetail, getCustomerSummaryList, getCustomerSummarySelectedTabType, getCustomerSummaryTrendFilter, isCustomersTabVisible, } from './view/customerSummaryView/customerSummaryViewSelector';
|
|
877
|
+
export { CUSTOMER_REVENUE_KEYS } from './view/customerSummaryView/customerSummaryViewState';
|
package/lib/esm/init.js
CHANGED
|
@@ -2,6 +2,7 @@ import configureNewStore from './configureStore';
|
|
|
2
2
|
import { ZeniAPIClient } from './zeniAPI';
|
|
3
3
|
import { setZeniAPIClient, zeniAPI } from './zeniAPIClientState';
|
|
4
4
|
export { zeniAPI } from './zeniAPIClientState';
|
|
5
|
+
export { setSessionEndpointsLoader } from './sessionEndpointsLoader';
|
|
5
6
|
export { disconnectPusher, initializePusher, pusher } from './pusherClient';
|
|
6
7
|
export let store;
|
|
7
8
|
let initialized = false;
|
|
@@ -13,11 +14,10 @@ let initialized = false;
|
|
|
13
14
|
*/
|
|
14
15
|
export default function initialize(restEndPoints, userId, sessionId, tenantId, isSandboxEnv, onSessionInvalidated) {
|
|
15
16
|
if (initialized) {
|
|
16
|
-
console.warn(
|
|
17
|
+
console.warn('client-epic-state is already initialized');
|
|
17
18
|
return store;
|
|
18
19
|
}
|
|
19
20
|
initialized = true;
|
|
20
|
-
console.info(`initializing with ${JSON.stringify(restEndPoints)}`);
|
|
21
21
|
initialize3rdPartyExtensions();
|
|
22
22
|
setZeniAPIClient(new ZeniAPIClient(restEndPoints, userId, sessionId, tenantId, onSessionInvalidated));
|
|
23
23
|
store = configureNewStore(zeniAPI, isSandboxEnv);
|
|
@@ -35,6 +35,18 @@ export { injectFeatureModules, injectFeatureEpics } from './configureStore';
|
|
|
35
35
|
export function updateZeniAPIClientConfig(userId, sessionId, tenantId) {
|
|
36
36
|
zeniAPI.update(userId, sessionId, tenantId);
|
|
37
37
|
}
|
|
38
|
+
/**
|
|
39
|
+
* Replace the REST endpoints the API client calls. Epics read the endpoints
|
|
40
|
+
* at request time, so every request made after this uses the new ones.
|
|
41
|
+
* Note: This version SHOULD BE used only in the client side.
|
|
42
|
+
* @param restEndPoints
|
|
43
|
+
*/
|
|
44
|
+
export function updateZeniAPIEndpoints(restEndPoints) {
|
|
45
|
+
if (zeniAPI == null) {
|
|
46
|
+
throw new Error('Call initialize() before updateZeniAPIEndpoints()');
|
|
47
|
+
}
|
|
48
|
+
zeniAPI.apiEndPoints = restEndPoints;
|
|
49
|
+
}
|
|
38
50
|
/**
|
|
39
51
|
* Returns current Zeni REST API client config.
|
|
40
52
|
* @returns
|
|
@@ -61,7 +73,6 @@ export function zeniAPIClientConfig() {
|
|
|
61
73
|
* @param tenantId tenant id
|
|
62
74
|
*/
|
|
63
75
|
export async function initializeWithNewStore(restEndPoints, userId, sessionId, tenantId) {
|
|
64
|
-
console.info(`initializeWithNewStore with ${JSON.stringify(restEndPoints)}`);
|
|
65
76
|
initialize3rdPartyExtensions();
|
|
66
77
|
const zeniHttpClient = new ZeniAPIClient(restEndPoints, userId, sessionId, tenantId);
|
|
67
78
|
const store = configureNewStore(zeniHttpClient, false);
|
package/lib/esm/reducer.js
CHANGED
|
@@ -116,6 +116,7 @@ import companyTaskManagerView, { initialState as initialCompanyTaskManagerViewSt
|
|
|
116
116
|
import companyView, { initialState as initialCompanyViewState, } from './view/companyView/companyViewReducer';
|
|
117
117
|
import createTransferEntry, { initialState as initialCreateTransferEntryState, } from './view/createTransferEntry/createTransferEntryReducer';
|
|
118
118
|
import creditAgentView, { initialState as initialCreditAgentViewState, } from './view/creditAgentView/creditAgentViewReducer';
|
|
119
|
+
import customerSummaryView, { initialState as initialCustomerSummaryViewState, } from './view/customerSummaryView/customerSummaryViewReducer';
|
|
119
120
|
import dashboard, { initialState as initialDashboardState, } from './view/dashboard/dashboardReducer';
|
|
120
121
|
import dashboardLayout, { initialState as initialDashboardLayoutState, } from './view/dashboardLayout/dashboardLayoutReducer';
|
|
121
122
|
import expenseAutomationView, { initialState as initialExpenseAutomationViewState, } from './view/expenseAutomationView/expenseAutomationViewReducer';
|
|
@@ -545,6 +546,7 @@ const initialViewsState = {
|
|
|
545
546
|
vendorFirstReviewViewState: initialVendorFirstReviewViewState,
|
|
546
547
|
vendorGlobalReviewViewState: initialVendorGlobalReviewViewState,
|
|
547
548
|
vendorListState: initialVendorListState,
|
|
549
|
+
customerSummaryViewState: initialCustomerSummaryViewState,
|
|
548
550
|
vendorFiling1099ListState: initialVendorFiling1099ListState,
|
|
549
551
|
vendorFiling1099UploadDetailsState: initialVendorFiling1099UploadDetails,
|
|
550
552
|
vendorsTabVendorViewState: initialVendorsTabVendorViewReducer,
|
|
@@ -833,6 +835,7 @@ const viewReducers = {
|
|
|
833
835
|
vendorFirstReviewViewState: vendorFirstReviewView,
|
|
834
836
|
vendorGlobalReviewViewState: vendorGlobalReviewView,
|
|
835
837
|
vendorListState: vendorList,
|
|
838
|
+
customerSummaryViewState: customerSummaryView,
|
|
836
839
|
vendorFiling1099ListState: vendorFiling1099List,
|
|
837
840
|
vendorFiling1099UploadDetailsState: vendorFiling1099UploadDetails,
|
|
838
841
|
vendorsTabVendorViewState: vendorsTabVendorView,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import { createSlice } from '@reduxjs/toolkit';
|
|
2
|
+
import { clearAll } from '../../rootActions';
|
|
3
|
+
import { CUSTOMER_REVENUE_KEYS, } from './customerSummaryViewState';
|
|
4
|
+
export const initialState = {
|
|
5
|
+
customers: [],
|
|
6
|
+
detailById: {},
|
|
7
|
+
fetchState: 'Not-Started',
|
|
8
|
+
searchString: '',
|
|
9
|
+
selectedTabType: 'income_transactions',
|
|
10
|
+
totalCount: 0,
|
|
11
|
+
trendFilter: 'last_12_months',
|
|
12
|
+
uiState: {
|
|
13
|
+
pageToken: null,
|
|
14
|
+
revenueSelection: 'year_to_date_revenue',
|
|
15
|
+
scrollYOffset: 0,
|
|
16
|
+
sortKey: 'year_to_date_revenue',
|
|
17
|
+
sortOrder: 'descending',
|
|
18
|
+
},
|
|
19
|
+
allTransactionsTabData: undefined,
|
|
20
|
+
detailCustomerId: undefined,
|
|
21
|
+
error: undefined,
|
|
22
|
+
};
|
|
23
|
+
export const toCustomerSummary = (payload) => ({
|
|
24
|
+
id: payload.customer_id,
|
|
25
|
+
qboId: payload.qbo_id ?? undefined,
|
|
26
|
+
name: payload.customer_name ?? '',
|
|
27
|
+
balance: payload.balance ?? 0,
|
|
28
|
+
currencyCode: payload.currency_code ?? undefined,
|
|
29
|
+
currencySymbol: payload.currency_symbol ?? undefined,
|
|
30
|
+
revenue: Object.fromEntries(CUSTOMER_REVENUE_KEYS.map((key) => [key, payload[key] ?? 0])),
|
|
31
|
+
account: payload.account?.account_id != null
|
|
32
|
+
? {
|
|
33
|
+
id: payload.account.account_id,
|
|
34
|
+
name: payload.account.account_name ?? '',
|
|
35
|
+
}
|
|
36
|
+
: undefined,
|
|
37
|
+
accountingClass: payload.accounting_class?.accounting_class_id != null
|
|
38
|
+
? {
|
|
39
|
+
id: payload.accounting_class.accounting_class_id,
|
|
40
|
+
name: payload.accounting_class.accounting_class_name ?? '',
|
|
41
|
+
}
|
|
42
|
+
: undefined,
|
|
43
|
+
});
|
|
44
|
+
const getDetail = (draft, customerId) => {
|
|
45
|
+
var _a;
|
|
46
|
+
return ((_a = draft.detailById)[customerId] ?? (_a[customerId] = {
|
|
47
|
+
fetchState: 'Not-Started',
|
|
48
|
+
saveState: 'Not-Started',
|
|
49
|
+
}));
|
|
50
|
+
};
|
|
51
|
+
const customerSummaryView = createSlice({
|
|
52
|
+
name: 'customerSummaryView',
|
|
53
|
+
initialState,
|
|
54
|
+
reducers: {
|
|
55
|
+
fetchCustomerSummaryList: {
|
|
56
|
+
prepare(keepExistingListItems, searchString = '', pageToken = null) {
|
|
57
|
+
return { payload: { keepExistingListItems, pageToken, searchString } };
|
|
58
|
+
},
|
|
59
|
+
reducer(draft, action) {
|
|
60
|
+
const { keepExistingListItems, pageToken, searchString } = action.payload;
|
|
61
|
+
// Load-more while a page is in flight: keep that page (the epic drops this fetch too).
|
|
62
|
+
if (keepExistingListItems && draft.fetchState === 'In-Progress') {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
if (!keepExistingListItems) {
|
|
66
|
+
draft.customers = [];
|
|
67
|
+
draft.uiState.scrollYOffset = 0;
|
|
68
|
+
}
|
|
69
|
+
draft.searchString = searchString;
|
|
70
|
+
draft.uiState.pageToken = pageToken;
|
|
71
|
+
draft.fetchState = 'In-Progress';
|
|
72
|
+
draft.error = undefined;
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
updateCustomerSummaryList: {
|
|
76
|
+
prepare(response, keepExistingListItems) {
|
|
77
|
+
return { payload: { keepExistingListItems, response } };
|
|
78
|
+
},
|
|
79
|
+
reducer(draft, action) {
|
|
80
|
+
const { response, keepExistingListItems } = action.payload;
|
|
81
|
+
const customers = (response.data?.customers ?? []).map(toCustomerSummary);
|
|
82
|
+
const existingIds = new Set(draft.customers.map(({ id }) => id));
|
|
83
|
+
draft.customers = keepExistingListItems
|
|
84
|
+
? [
|
|
85
|
+
...draft.customers,
|
|
86
|
+
...customers.filter(({ id }) => !existingIds.has(id)),
|
|
87
|
+
]
|
|
88
|
+
: customers;
|
|
89
|
+
draft.totalCount = response.data?.total_count ?? 0;
|
|
90
|
+
draft.uiState.pageToken = response.data?.query?.page_token ?? null;
|
|
91
|
+
draft.fetchState = 'Completed';
|
|
92
|
+
},
|
|
93
|
+
},
|
|
94
|
+
updateCustomerSummaryListFailure(draft, action) {
|
|
95
|
+
draft.fetchState = 'Error';
|
|
96
|
+
draft.error = action.payload;
|
|
97
|
+
// No more pages after a failure, so load-more doesn't keep retrying.
|
|
98
|
+
draft.uiState.pageToken = null;
|
|
99
|
+
},
|
|
100
|
+
updateCustomerSummarySortUiState: {
|
|
101
|
+
prepare(sortKey, sortOrder) {
|
|
102
|
+
return { payload: { sortKey, sortOrder } };
|
|
103
|
+
},
|
|
104
|
+
reducer(draft, action) {
|
|
105
|
+
draft.uiState.sortKey = action.payload.sortKey;
|
|
106
|
+
draft.uiState.sortOrder = action.payload.sortOrder;
|
|
107
|
+
},
|
|
108
|
+
},
|
|
109
|
+
updateCustomerSummaryRevenueSelection(draft, action) {
|
|
110
|
+
draft.uiState.revenueSelection = action.payload;
|
|
111
|
+
},
|
|
112
|
+
updateCustomerSummaryScrollYOffset(draft, action) {
|
|
113
|
+
draft.uiState.scrollYOffset = action.payload;
|
|
114
|
+
},
|
|
115
|
+
fetchCustomerSummaryDetail(draft, action) {
|
|
116
|
+
const detail = getDetail(draft, action.payload);
|
|
117
|
+
detail.fetchState = 'In-Progress';
|
|
118
|
+
detail.error = undefined;
|
|
119
|
+
},
|
|
120
|
+
updateCustomerSummaryDetail: {
|
|
121
|
+
prepare(customerId, customer) {
|
|
122
|
+
return { payload: { customer, customerId } };
|
|
123
|
+
},
|
|
124
|
+
reducer(draft, action) {
|
|
125
|
+
const detail = getDetail(draft, action.payload.customerId);
|
|
126
|
+
detail.customer = toCustomerSummary(action.payload.customer);
|
|
127
|
+
detail.fetchState = 'Completed';
|
|
128
|
+
// Keep the list row in sync after an edit.
|
|
129
|
+
const index = draft.customers.findIndex(({ id }) => id === action.payload.customerId);
|
|
130
|
+
if (index >= 0) {
|
|
131
|
+
draft.customers[index] = {
|
|
132
|
+
...draft.customers[index],
|
|
133
|
+
account: detail.customer.account,
|
|
134
|
+
accountingClass: detail.customer.accountingClass,
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
},
|
|
138
|
+
},
|
|
139
|
+
updateCustomerSummaryDetailFailure: {
|
|
140
|
+
prepare(customerId, status) {
|
|
141
|
+
return { payload: { customerId, status } };
|
|
142
|
+
},
|
|
143
|
+
reducer(draft, action) {
|
|
144
|
+
const detail = getDetail(draft, action.payload.customerId);
|
|
145
|
+
detail.fetchState = 'Error';
|
|
146
|
+
detail.error = action.payload.status;
|
|
147
|
+
},
|
|
148
|
+
},
|
|
149
|
+
saveCustomerSummaryDetail: {
|
|
150
|
+
prepare(customerId, changes) {
|
|
151
|
+
return { payload: { changes, customerId } };
|
|
152
|
+
},
|
|
153
|
+
reducer(draft, action) {
|
|
154
|
+
getDetail(draft, action.payload.customerId).saveState = 'In-Progress';
|
|
155
|
+
},
|
|
156
|
+
},
|
|
157
|
+
updateCustomerSummarySaveState: {
|
|
158
|
+
prepare(customerId, saveState) {
|
|
159
|
+
return { payload: { customerId, saveState } };
|
|
160
|
+
},
|
|
161
|
+
reducer(draft, action) {
|
|
162
|
+
getDetail(draft, action.payload.customerId).saveState =
|
|
163
|
+
action.payload.saveState;
|
|
164
|
+
},
|
|
165
|
+
},
|
|
166
|
+
updateCustomerAllTransactionsTabData(draft, action) {
|
|
167
|
+
draft.allTransactionsTabData = action.payload;
|
|
168
|
+
},
|
|
169
|
+
updateCustomerDetailSelectedTab(draft, action) {
|
|
170
|
+
draft.selectedTabType = action.payload;
|
|
171
|
+
},
|
|
172
|
+
updateCustomerTrendFilter(draft, action) {
|
|
173
|
+
draft.trendFilter = action.payload;
|
|
174
|
+
},
|
|
175
|
+
// Detail page opened for `customerId`: a different customer starts from the default filter / tab /
|
|
176
|
+
// transactions window / save state; the same one (e.g. back from a transaction) keeps them.
|
|
177
|
+
resetCustomerSummaryDetailUiState(draft, action) {
|
|
178
|
+
if (draft.detailCustomerId === action.payload) {
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
draft.detailCustomerId = action.payload;
|
|
182
|
+
draft.trendFilter = initialState.trendFilter;
|
|
183
|
+
draft.selectedTabType = initialState.selectedTabType;
|
|
184
|
+
draft.allTransactionsTabData = undefined;
|
|
185
|
+
Object.values(draft.detailById).forEach((detail) => {
|
|
186
|
+
detail.saveState = 'Not-Started';
|
|
187
|
+
});
|
|
188
|
+
},
|
|
189
|
+
},
|
|
190
|
+
extraReducers: (builder) => {
|
|
191
|
+
builder.addCase(clearAll, (draft) => {
|
|
192
|
+
Object.assign(draft, initialState);
|
|
193
|
+
});
|
|
194
|
+
},
|
|
195
|
+
});
|
|
196
|
+
export const { fetchCustomerSummaryList, updateCustomerSummaryList, updateCustomerSummaryListFailure, updateCustomerSummarySortUiState, updateCustomerSummaryRevenueSelection, updateCustomerSummaryScrollYOffset, fetchCustomerSummaryDetail, updateCustomerSummaryDetail, updateCustomerSummaryDetailFailure, saveCustomerSummaryDetail, updateCustomerSummarySaveState, updateCustomerDetailSelectedTab, updateCustomerAllTransactionsTabData, updateCustomerTrendFilter, resetCustomerSummaryDetailUiState, } = customerSummaryView.actions;
|
|
197
|
+
export default customerSummaryView.reducer;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { createSelector } from '@reduxjs/toolkit';
|
|
2
|
+
import { getIsAccountingClassesEnabled } from '../../entity/tenant/tenantSelector';
|
|
3
|
+
import { getNestedAccountListHierarchy } from '../accountList/accountListSelector';
|
|
4
|
+
import { getNestedClassListHierarchy } from '../classList/classListSelector';
|
|
5
|
+
const isLoadingFetchState = (fetchState) => fetchState === 'Not-Started' || fetchState === 'In-Progress';
|
|
6
|
+
const NOT_STARTED_DETAIL = {
|
|
7
|
+
fetchState: 'Not-Started',
|
|
8
|
+
saveState: 'Not-Started',
|
|
9
|
+
};
|
|
10
|
+
export const getCustomerSummaryList = (state) => state.customerSummaryViewState;
|
|
11
|
+
export const getCustomerSummaryDetail = createSelector((state, customerId) => state.customerSummaryViewState.detailById[customerId], (state) => state.accountListState, (state) => state.classListState, getIsAccountingClassesEnabled, (detail, accountListState, classListState, isAccountingClassesEnabled) => ({
|
|
12
|
+
...(detail ?? NOT_STARTED_DETAIL),
|
|
13
|
+
accountsHierarchyList: getNestedAccountListHierarchy(accountListState, 'accountList'),
|
|
14
|
+
classHierarchyList: getNestedClassListHierarchy(classListState),
|
|
15
|
+
isAccountingClassesEnabled,
|
|
16
|
+
isReferenceDataLoading: isLoadingFetchState(accountListState.byReportId.accountList.fetchState) ||
|
|
17
|
+
(isAccountingClassesEnabled &&
|
|
18
|
+
isLoadingFetchState(classListState.fetchState)),
|
|
19
|
+
}));
|
|
20
|
+
export const getCustomerSummarySelectedTabType = (state) => state.customerSummaryViewState.selectedTabType;
|
|
21
|
+
export const getCustomerSummaryAllTransactionsTabData = (state) => state.customerSummaryViewState.allTransactionsTabData;
|
|
22
|
+
export const getCustomerSummaryTrendFilter = (state) => state.customerSummaryViewState.trendFilter;
|
|
23
|
+
// Roles holding the customers / customers.read_only scopes (tenant role_scopes_v32). Same roles as the
|
|
24
|
+
// Vendors tab today, kept separate so AR access can diverge; bookkeeping_user is read-only.
|
|
25
|
+
export const isCustomersTabVisible = (roleMap) => [
|
|
26
|
+
'admin',
|
|
27
|
+
'bookkeeping_admin',
|
|
28
|
+
'bookkeeping_user',
|
|
29
|
+
'ap_analyst',
|
|
30
|
+
'controller',
|
|
31
|
+
'customer_success_manager',
|
|
32
|
+
'reviewer',
|
|
33
|
+
'tax_lead',
|
|
34
|
+
'preparer',
|
|
35
|
+
].some((role) => roleMap?.[role] === true);
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { concat, from, of } from 'rxjs';
|
|
2
|
+
import { catchError, filter, map, mergeMap } from 'rxjs/operators';
|
|
3
|
+
import { createZeniAPIStatus, isSuccessResponse } from '../../../responsePayload';
|
|
4
|
+
import { fetchAccountList } from '../../accountList/accountListReducer';
|
|
5
|
+
import { fetchClassList } from '../../classList/classListReducer';
|
|
6
|
+
import { fetchCustomerSummaryDetail, updateCustomerSummaryDetail, updateCustomerSummaryDetailFailure, } from '../customerSummaryViewReducer';
|
|
7
|
+
export const fetchCustomerSummaryDetailEpic = (actions$, state$, zeniAPI) => actions$.pipe(filter(fetchCustomerSummaryDetail.match), mergeMap(({ payload: customerId }) => {
|
|
8
|
+
// Category / class pickers on the detail page need the account and class lists.
|
|
9
|
+
const { accountListState, classListState } = state$.value;
|
|
10
|
+
const listActions = [];
|
|
11
|
+
const accountList = accountListState.byReportId.accountList;
|
|
12
|
+
if (!accountList.hasValidState() &&
|
|
13
|
+
accountList.fetchState !== 'In-Progress') {
|
|
14
|
+
listActions.push(fetchAccountList('accountList', true));
|
|
15
|
+
}
|
|
16
|
+
if (!classListState.hasValidState() &&
|
|
17
|
+
classListState.fetchState !== 'In-Progress') {
|
|
18
|
+
listActions.push(fetchClassList());
|
|
19
|
+
}
|
|
20
|
+
const customer$ = zeniAPI
|
|
21
|
+
.getJSON(`${zeniAPI.apiEndPoints.accountMicroServiceBaseUrl}/1.0/customers/summary/${encodeURIComponent(customerId)}`)
|
|
22
|
+
.pipe(map((response) => {
|
|
23
|
+
const customer = response.data?.customers?.[0];
|
|
24
|
+
return isSuccessResponse(response) && customer != null
|
|
25
|
+
? updateCustomerSummaryDetail(customerId, customer)
|
|
26
|
+
: updateCustomerSummaryDetailFailure(customerId, response.status);
|
|
27
|
+
}), catchError((error) => of(updateCustomerSummaryDetailFailure(customerId, createZeniAPIStatus('Unexpected Error', 'Fetch customer summary REST API call errored out' +
|
|
28
|
+
JSON.stringify(error))))));
|
|
29
|
+
return concat(from(listActions), customer$);
|
|
30
|
+
}));
|