@zeniai/client-epic-state 5.2.73 → 5.2.74-betaAK0
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/aiCfo/aiCfoState.d.ts +2 -2
- package/lib/entity/billPay/billTransaction/billTransactionState.d.ts +2 -2
- package/lib/entity/chargeCardTransaction/chargeCardTransaction.d.ts +1 -1
- package/lib/entity/depositAccountTransaction/depositAccountTransaction.d.ts +1 -1
- package/lib/entity/invoicing/coupon/couponSelector.d.ts +1 -0
- package/lib/entity/invoicing/coupon/couponSelector.js +5 -0
- package/lib/entity/invoicing/{product/productPayload.d.ts → invoicingCatalog/invoicingCatalogPayload.d.ts} +44 -6
- package/lib/entity/invoicing/{product/productPayload.js → invoicingCatalog/invoicingCatalogPayload.js} +50 -6
- package/lib/entity/invoicing/invoicingCatalog/invoicingCatalogReducer.d.ts +12 -0
- package/lib/entity/invoicing/{plan/planReducer.js → invoicingCatalog/invoicingCatalogReducer.js} +20 -21
- package/lib/entity/invoicing/{plan/planSelector.d.ts → invoicingCatalog/invoicingCatalogSelector.d.ts} +5 -6
- package/lib/entity/invoicing/{plan/planSelector.js → invoicingCatalog/invoicingCatalogSelector.js} +12 -12
- package/lib/entity/invoicing/{product/productState.d.ts → invoicingCatalog/invoicingCatalogState.d.ts} +42 -6
- package/lib/entity/jeSchedules/jeSchedulesTypes.d.ts +4 -4
- package/lib/entity/snackbar/snackbarTypes.d.ts +1 -1
- package/lib/entity/snackbar/snackbarTypes.js +1 -0
- package/lib/epic.d.ts +4 -2
- package/lib/epic.js +5 -1
- package/lib/esm/entity/invoicing/coupon/couponSelector.js +4 -0
- package/lib/esm/entity/invoicing/{product/productPayload.js → invoicingCatalog/invoicingCatalogPayload.js} +46 -3
- package/lib/esm/entity/invoicing/{plan/planReducer.js → invoicingCatalog/invoicingCatalogReducer.js} +19 -20
- package/lib/esm/entity/invoicing/{plan/planSelector.js → invoicingCatalog/invoicingCatalogSelector.js} +8 -8
- package/lib/esm/entity/snackbar/snackbarTypes.js +1 -0
- package/lib/esm/epic.js +5 -1
- package/lib/esm/index.js +7 -10
- package/lib/esm/reducer.js +4 -7
- package/lib/esm/view/invoicing/createInvoice/createInvoiceEpic.js +9 -1
- package/lib/esm/view/invoicing/createInvoice/createInvoiceSelector.js +1 -1
- package/lib/esm/view/invoicing/editInvoicingCatalogItemDetailView/editInvoicingCatalogItemDetailViewSelector.js +5 -3
- package/lib/esm/view/invoicing/editInvoicingCatalogItemDetailView/fetchInvoicingCatalogItemDetailEpic.js +2 -2
- package/lib/esm/view/invoicing/editInvoicingCatalogItemDetailView/runInvoicingCatalogPlanActionEpic.js +2 -2
- package/lib/esm/view/invoicing/editInvoicingCatalogItemDetailView/runInvoicingCatalogProductActionEpic.js +4 -4
- package/lib/esm/view/invoicing/editInvoicingCatalogItemDetailView/saveInvoicingCatalogItemEpic.js +3 -7
- package/lib/esm/view/invoicing/editInvoicingCatalogItemDetailView/seedInvoicingCatalogItemFormDraftEpic.js +2 -2
- package/lib/esm/view/invoicing/editInvoicingCustomerDetailView/editInvoicingCustomerDetailViewReducer.js +5 -1
- package/lib/esm/view/invoicing/editInvoicingSubscriptionDetailView/editInvoicingSubscriptionDetailViewPayload.js +55 -0
- package/lib/esm/view/invoicing/editInvoicingSubscriptionDetailView/editInvoicingSubscriptionDetailViewReducer.js +35 -1
- package/lib/esm/view/invoicing/editInvoicingSubscriptionDetailView/editInvoicingSubscriptionDetailViewSelector.js +80 -2
- package/lib/esm/view/invoicing/editInvoicingSubscriptionDetailView/fetchInvoicingSubscriptionEstimateEpic.js +49 -0
- package/lib/esm/view/invoicing/editInvoicingSubscriptionDetailView/saveInvoicingSubscriptionEpic.js +23 -14
- package/lib/esm/view/invoicing/invoiceList/invoiceListReducer.js +8 -1
- package/lib/esm/view/invoicing/invoicingCatalogListView/epics/fetchInvoicingCatalogPlanListEpic.js +2 -2
- package/lib/esm/view/invoicing/invoicingCatalogListView/epics/fetchInvoicingCatalogProductListEpic.js +2 -2
- package/lib/esm/view/invoicing/invoicingCatalogListView/invoicingCatalogListViewSelector.js +55 -22
- package/lib/esm/view/invoicing/invoicingCatalogListView/invoicingCatalogListViewState.js +1 -20
- package/lib/esm/view/spendManagement/commonSetup/epic/setup/resetKycKybEpic.js +37 -0
- package/lib/esm/view/spendManagement/commonSetup/setupViewReducer.js +21 -1
- package/lib/esm/view/spendManagement/commonSetup/setupViewSelector.js +2 -1
- package/lib/index.d.ts +12 -16
- package/lib/index.js +41 -45
- package/lib/reducer.d.ts +1 -2
- package/lib/reducer.js +4 -7
- package/lib/rootStateTypes.d.ts +3 -5
- package/lib/view/expenseAutomationView/helpers/transactionCategorizationLocalDataHelper.d.ts +1 -1
- package/lib/view/expenseAutomationView/reducers/jeSchedulesViewReducer.d.ts +2 -2
- package/lib/view/invoicing/createInvoice/createInvoiceEpic.d.ts +3 -2
- package/lib/view/invoicing/createInvoice/createInvoiceEpic.js +8 -0
- package/lib/view/invoicing/createInvoice/createInvoiceSelector.d.ts +2 -2
- package/lib/view/invoicing/createInvoice/createInvoiceSelector.js +2 -2
- package/lib/view/invoicing/editInvoicingCatalogItemDetailView/editInvoicingCatalogItemDetailViewPayload.d.ts +8 -9
- package/lib/view/invoicing/editInvoicingCatalogItemDetailView/editInvoicingCatalogItemDetailViewSelector.d.ts +2 -2
- package/lib/view/invoicing/editInvoicingCatalogItemDetailView/editInvoicingCatalogItemDetailViewSelector.js +5 -3
- package/lib/view/invoicing/editInvoicingCatalogItemDetailView/fetchInvoicingCatalogItemDetailEpic.d.ts +2 -2
- package/lib/view/invoicing/editInvoicingCatalogItemDetailView/fetchInvoicingCatalogItemDetailEpic.js +2 -2
- package/lib/view/invoicing/editInvoicingCatalogItemDetailView/invoicingCatalogItemFormHelper.d.ts +3 -4
- package/lib/view/invoicing/editInvoicingCatalogItemDetailView/runInvoicingCatalogPlanActionEpic.d.ts +2 -2
- package/lib/view/invoicing/editInvoicingCatalogItemDetailView/runInvoicingCatalogPlanActionEpic.js +2 -2
- package/lib/view/invoicing/editInvoicingCatalogItemDetailView/runInvoicingCatalogProductActionEpic.d.ts +2 -3
- package/lib/view/invoicing/editInvoicingCatalogItemDetailView/runInvoicingCatalogProductActionEpic.js +4 -4
- package/lib/view/invoicing/editInvoicingCatalogItemDetailView/saveInvoicingCatalogItemEpic.d.ts +2 -3
- package/lib/view/invoicing/editInvoicingCatalogItemDetailView/saveInvoicingCatalogItemEpic.js +3 -7
- package/lib/view/invoicing/editInvoicingCatalogItemDetailView/seedInvoicingCatalogItemFormDraftEpic.js +2 -2
- package/lib/view/invoicing/editInvoicingCustomerDetailView/editInvoicingCustomerDetailViewReducer.d.ts +3 -1
- package/lib/view/invoicing/editInvoicingCustomerDetailView/editInvoicingCustomerDetailViewReducer.js +6 -2
- package/lib/view/invoicing/editInvoicingSubscriptionDetailView/editInvoicingSubscriptionDetailViewPayload.d.ts +46 -1
- package/lib/view/invoicing/editInvoicingSubscriptionDetailView/editInvoicingSubscriptionDetailViewPayload.js +57 -1
- package/lib/view/invoicing/editInvoicingSubscriptionDetailView/editInvoicingSubscriptionDetailViewReducer.d.ts +10 -2
- package/lib/view/invoicing/editInvoicingSubscriptionDetailView/editInvoicingSubscriptionDetailViewReducer.js +36 -2
- package/lib/view/invoicing/editInvoicingSubscriptionDetailView/editInvoicingSubscriptionDetailViewSelector.d.ts +23 -3
- package/lib/view/invoicing/editInvoicingSubscriptionDetailView/editInvoicingSubscriptionDetailViewSelector.js +83 -3
- package/lib/view/invoicing/editInvoicingSubscriptionDetailView/editInvoicingSubscriptionDetailViewState.d.ts +58 -0
- package/lib/view/invoicing/editInvoicingSubscriptionDetailView/fetchInvoicingSubscriptionEstimateEpic.d.ts +7 -0
- package/lib/view/invoicing/editInvoicingSubscriptionDetailView/fetchInvoicingSubscriptionEstimateEpic.js +53 -0
- package/lib/view/invoicing/editInvoicingSubscriptionDetailView/saveInvoicingSubscriptionEpic.d.ts +11 -1
- package/lib/view/invoicing/editInvoicingSubscriptionDetailView/saveInvoicingSubscriptionEpic.js +26 -15
- package/lib/view/invoicing/invoiceList/invoiceListReducer.d.ts +3 -1
- package/lib/view/invoicing/invoiceList/invoiceListReducer.js +9 -2
- package/lib/view/invoicing/invoicingCatalogListView/epics/fetchInvoicingCatalogListPageEpic.d.ts +2 -2
- package/lib/view/invoicing/invoicingCatalogListView/epics/fetchInvoicingCatalogPlanListEpic.d.ts +2 -2
- package/lib/view/invoicing/invoicingCatalogListView/epics/fetchInvoicingCatalogPlanListEpic.js +2 -2
- package/lib/view/invoicing/invoicingCatalogListView/epics/fetchInvoicingCatalogProductListEpic.d.ts +2 -2
- package/lib/view/invoicing/invoicingCatalogListView/epics/fetchInvoicingCatalogProductListEpic.js +2 -2
- package/lib/view/invoicing/invoicingCatalogListView/invoicingCatalogListViewPayload.d.ts +3 -3
- package/lib/view/invoicing/invoicingCatalogListView/invoicingCatalogListViewSelector.d.ts +33 -15
- package/lib/view/invoicing/invoicingCatalogListView/invoicingCatalogListViewSelector.js +57 -23
- package/lib/view/invoicing/invoicingCatalogListView/invoicingCatalogListViewState.d.ts +0 -15
- package/lib/view/invoicing/invoicingCatalogListView/invoicingCatalogListViewState.js +0 -22
- package/lib/view/invoicing/invoicingDunningCaseListView/invoicingDunningCaseListViewReducer.d.ts +1 -1
- package/lib/view/invoicing/invoicingDunningCaseListView/invoicingDunningCaseListViewState.d.ts +1 -1
- package/lib/view/invoicing/invoicingSubscriptionListView/invoicingSubscriptionListViewReducer.d.ts +1 -1
- package/lib/view/invoicing/invoicingSubscriptionListView/invoicingSubscriptionListViewState.d.ts +1 -1
- package/lib/view/scheduleView/scheduleListView/scheduleListTypes.d.ts +1 -1
- package/lib/view/spendManagement/billPay/billList/billListReducer.d.ts +5 -5
- package/lib/view/spendManagement/billPay/billList/billListState.d.ts +1 -1
- package/lib/view/spendManagement/billPay/editBillView/editBillViewReducer.d.ts +2 -2
- package/lib/view/spendManagement/commonSetup/epic/setup/resetKycKybEpic.d.ts +8 -0
- package/lib/view/spendManagement/commonSetup/epic/setup/resetKycKybEpic.js +41 -0
- package/lib/view/spendManagement/commonSetup/setupViewReducer.d.ts +1 -1
- package/lib/view/spendManagement/commonSetup/setupViewReducer.js +22 -2
- package/lib/view/spendManagement/commonSetup/setupViewSelector.d.ts +1 -0
- package/lib/view/spendManagement/commonSetup/setupViewSelector.js +2 -1
- package/lib/view/spendManagement/commonSetup/setupViewState.d.ts +1 -0
- package/lib/view/spendManagement/reimbursement/editRemiView/editRemiViewReducer.d.ts +2 -2
- package/lib/view/spendManagement/reimbursement/remiListView/remiListReducer.d.ts +5 -5
- package/lib/view/spendManagement/reimbursement/remiListView/remiListState.d.ts +2 -2
- package/package.json +1 -26
- package/lib/entity/invoicing/plan/planPayload.d.ts +0 -40
- package/lib/entity/invoicing/plan/planPayload.js +0 -51
- package/lib/entity/invoicing/plan/planReducer.d.ts +0 -14
- package/lib/entity/invoicing/plan/planState.d.ts +0 -43
- package/lib/entity/invoicing/product/productReducer.d.ts +0 -9
- package/lib/entity/invoicing/product/productReducer.js +0 -50
- package/lib/entity/invoicing/product/productSelector.d.ts +0 -4
- package/lib/entity/invoicing/product/productSelector.js +0 -17
- package/lib/entity/invoicing/product/productState.js +0 -2
- package/lib/esm/entity/invoicing/plan/planPayload.js +0 -47
- package/lib/esm/entity/invoicing/product/productReducer.js +0 -43
- package/lib/esm/entity/invoicing/product/productSelector.js +0 -10
- package/lib/esm/entity/invoicing/product/productState.js +0 -1
- /package/lib/entity/invoicing/{plan/planState.js → invoicingCatalog/invoicingCatalogState.js} +0 -0
- /package/lib/esm/entity/invoicing/{plan/planState.js → invoicingCatalog/invoicingCatalogState.js} +0 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { getAddressByAddressId } from '../../../entity/address/addressSelector';
|
|
2
|
-
import { invoicingPricePointDisplayAmount } from '../../../entity/invoicing/
|
|
2
|
+
import { invoicingPricePointDisplayAmount } from '../../../entity/invoicing/invoicingCatalog/invoicingCatalogSelector';
|
|
3
3
|
import { getInvoicingCatalogListView, getInvoicingCatalogPrices, } from '../invoicingCatalogListView/invoicingCatalogListViewSelector';
|
|
4
4
|
import { invoicingFieldHelpForGroup } from '../invoicingConfigView/invoicingConfigViewSelector';
|
|
5
5
|
import { getInvoicingCustomerListView } from '../invoicingCustomerListView/invoicingCustomerListViewSelector';
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createSelector } from '@reduxjs/toolkit';
|
|
2
2
|
import { reduceAllFetchState } from '../../../commonStateTypes/reduceFetchState';
|
|
3
|
-
import {
|
|
3
|
+
import { getInvoicingCatalogItemById, getInvoicingCatalogItemsByIds, } from '../../../entity/invoicing/invoicingCatalog/invoicingCatalogSelector';
|
|
4
4
|
import { getInvoicingConfigView, invoicingFieldHelpForGroup, } from '../invoicingConfigView/invoicingConfigViewSelector';
|
|
5
5
|
import { getInvoicingFormDraftLocalData, isInvoicingFormDraftPending, } from '../invoicingFormDraft';
|
|
6
6
|
import { INVOICING_DEFAULT_INCOME_ACCOUNT_TYPES, INVOICING_INCOME_ACCOUNT_MAPPING_SLOT, NO_INVOICING_ACCOUNT_HIERARCHY, NO_INVOICING_ACCOUNT_IDS, NO_INVOICING_CLASS_HIERARCHY, getInvoicingQboAccountOptions, getInvoicingQboView, invoicingQboAccountIdsForMappingSlot, isInvoicingAccountingPickerDataLoading, isInvoicingQboConnected, } from '../invoicingQboView/invoicingQboViewSelector';
|
|
@@ -36,7 +36,7 @@ const buildApplicableCatalogItemOptions = (plans) => {
|
|
|
36
36
|
* Kept referentially stable across unrelated dispatches; the form view filters
|
|
37
37
|
* out the item being edited (see `getInvoicingCatalogItemFormView`).
|
|
38
38
|
*/
|
|
39
|
-
export const getInvoicingApplicableCatalogItemOptions = createSelector((state) => state.
|
|
39
|
+
export const getInvoicingApplicableCatalogItemOptions = createSelector((state) => state.invoicingCatalogState, (state) => state.invoicingCatalogListViewState, (catalogState, listViewState) => buildApplicableCatalogItemOptions(getInvoicingCatalogItemsByIds(catalogState, [
|
|
40
40
|
...listViewState.planIds,
|
|
41
41
|
...listViewState.productIds,
|
|
42
42
|
])));
|
|
@@ -61,7 +61,9 @@ const getInvoicingCatalogIncomeAccountIds = createSelector([
|
|
|
61
61
|
: NO_INVOICING_ACCOUNT_IDS);
|
|
62
62
|
export const getInvoicingCatalogItemFormView = (state, id) => {
|
|
63
63
|
const { editDraftById, error, fetchState, newItemDraft, savedItemId, savedKind, } = state.editInvoicingCatalogItemDetailViewState;
|
|
64
|
-
const plan = id != null
|
|
64
|
+
const plan = id != null
|
|
65
|
+
? getInvoicingCatalogItemById(state.invoicingCatalogState, id)
|
|
66
|
+
: undefined;
|
|
65
67
|
const kind = plan != null ? 'plan' : undefined;
|
|
66
68
|
// No entity fallback: the draft is the one source of form values, seeded from
|
|
67
69
|
// the record by `seedInvoicingCatalogItemFormDraftEpic` when the detail load
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { of } from 'rxjs';
|
|
2
2
|
import { catchError, filter, switchMap } from 'rxjs/operators';
|
|
3
|
-
import {
|
|
3
|
+
import { updateInvoicingCatalogItems } from '../../../entity/invoicing/invoicingCatalog/invoicingCatalogReducer';
|
|
4
4
|
import { createZeniAPIStatus, isSuccessResponse } from '../../../responsePayload';
|
|
5
5
|
import { fetchInvoicingCatalogItemDetail, updateInvoicingCatalogItemDetail, updateInvoicingCatalogItemDetailFailure, } from './editInvoicingCatalogItemDetailViewReducer';
|
|
6
6
|
export const fetchInvoicingCatalogItemDetailEpic = (actions$, _state$, zeniAPI) => actions$.pipe(filter(fetchInvoicingCatalogItemDetail.match), switchMap((action) => {
|
|
@@ -9,7 +9,7 @@ export const fetchInvoicingCatalogItemDetailEpic = (actions$, _state$, zeniAPI)
|
|
|
9
9
|
return zeniAPI.getJSON(apiUrl).pipe(switchMap((response) => {
|
|
10
10
|
if (isSuccessResponse(response) && response.data != null) {
|
|
11
11
|
return [
|
|
12
|
-
|
|
12
|
+
updateInvoicingCatalogItems([response.data], 'merge'),
|
|
13
13
|
updateInvoicingCatalogItemDetail({ id: response.data.id, kind }),
|
|
14
14
|
];
|
|
15
15
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { of } from 'rxjs';
|
|
2
2
|
import { catchError, filter, mergeMap } from 'rxjs/operators';
|
|
3
|
-
import {
|
|
3
|
+
import { updateInvoicingCatalogItems } from '../../../entity/invoicing/invoicingCatalog/invoicingCatalogReducer';
|
|
4
4
|
import { createZeniAPIStatus, isSuccessResponse } from '../../../responsePayload';
|
|
5
5
|
import { runInvoicingCatalogPlanAction, runInvoicingCatalogPlanActionFailure, runInvoicingCatalogPlanActionSuccess, } from './editInvoicingCatalogItemDetailViewReducer';
|
|
6
6
|
export const runInvoicingCatalogPlanActionEpic = (actions$, _state$, zeniAPI) => actions$.pipe(filter(runInvoicingCatalogPlanAction.match), mergeMap((action) => {
|
|
@@ -12,7 +12,7 @@ export const runInvoicingCatalogPlanActionEpic = (actions$, _state$, zeniAPI) =>
|
|
|
12
12
|
.pipe(mergeMap((response) => {
|
|
13
13
|
if (isSuccessResponse(response) && response.data != null) {
|
|
14
14
|
return [
|
|
15
|
-
|
|
15
|
+
updateInvoicingCatalogItems([response.data], 'merge'),
|
|
16
16
|
runInvoicingCatalogPlanActionSuccess({ id }),
|
|
17
17
|
];
|
|
18
18
|
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { of } from 'rxjs';
|
|
2
2
|
import { catchError, filter, mergeMap } from 'rxjs/operators';
|
|
3
|
-
import {
|
|
4
|
-
import { updateInvoicingProducts } from '../../../entity/invoicing/product/productReducer';
|
|
3
|
+
import { mergeInvoicingCatalogPricePoint } from '../../../entity/invoicing/invoicingCatalog/invoicingCatalogReducer';
|
|
5
4
|
import { createZeniAPIStatus, isSuccessResponse } from '../../../responsePayload';
|
|
6
5
|
import { runInvoicingCatalogProductAction, runInvoicingCatalogProductActionFailure, runInvoicingCatalogProductActionSuccess, } from './editInvoicingCatalogItemDetailViewReducer';
|
|
7
6
|
export const runInvoicingCatalogProductActionEpic = (actions$, _state$, zeniAPI) => actions$.pipe(filter(runInvoicingCatalogProductAction.match), mergeMap((action) => {
|
|
@@ -16,9 +15,10 @@ export const runInvoicingCatalogProductActionEpic = (actions$, _state$, zeniAPI)
|
|
|
16
15
|
.pipe(mergeMap((response) => {
|
|
17
16
|
if (isSuccessResponse(response) && response.data != null) {
|
|
18
17
|
return [
|
|
19
|
-
updateInvoicingProducts([response.data], 'merge'),
|
|
20
18
|
...(parentItemId != null
|
|
21
|
-
? [
|
|
19
|
+
? [
|
|
20
|
+
mergeInvoicingCatalogPricePoint(parentItemId, response.data),
|
|
21
|
+
]
|
|
22
22
|
: []),
|
|
23
23
|
runInvoicingCatalogProductActionSuccess({ id: stateKey }),
|
|
24
24
|
];
|
package/lib/esm/view/invoicing/editInvoicingCatalogItemDetailView/saveInvoicingCatalogItemEpic.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { forkJoin, from } from 'rxjs';
|
|
2
2
|
import { catchError, filter, mergeMap } from 'rxjs/operators';
|
|
3
|
-
import {
|
|
4
|
-
import { updateInvoicingProducts } from '../../../entity/invoicing/product/productReducer';
|
|
3
|
+
import { mergeInvoicingCatalogPricePoint, updateInvoicingCatalogItems, } from '../../../entity/invoicing/invoicingCatalog/invoicingCatalogReducer';
|
|
5
4
|
import { apiErrorSnackbarVariables } from '../../../entity/snackbar/snackbarApiError';
|
|
6
5
|
import { openSnackbar } from '../../../entity/snackbar/snackbarReducer';
|
|
7
6
|
import { createZeniAPIStatus, isSuccessResponse } from '../../../responsePayload';
|
|
@@ -51,11 +50,8 @@ export const saveInvoicingCatalogItemEpic = (actions$, state$, zeniAPI) => actio
|
|
|
51
50
|
}
|
|
52
51
|
const savedProducts = productResponses.flatMap((productResponse) => productResponse.data != null ? [productResponse.data] : []);
|
|
53
52
|
const resultActions = [
|
|
54
|
-
|
|
55
|
-
...
|
|
56
|
-
? [updateInvoicingProducts(savedProducts, 'merge')]
|
|
57
|
-
: []),
|
|
58
|
-
...savedProducts.map((product) => mergeInvoicingPlanSubFamily(savedItem.id, product)),
|
|
53
|
+
updateInvoicingCatalogItems([savedItem], 'merge'),
|
|
54
|
+
...savedProducts.map((product) => mergeInvoicingCatalogPricePoint(savedItem.id, product)),
|
|
59
55
|
submitInvoicingCatalogItemFormSuccess({
|
|
60
56
|
itemId: savedItem.id,
|
|
61
57
|
kind: 'plan',
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { EMPTY, of } from 'rxjs';
|
|
2
2
|
import { filter, mergeMap, withLatestFrom } from 'rxjs/operators';
|
|
3
|
-
import {
|
|
3
|
+
import { getInvoicingCatalogItemById } from '../../../entity/invoicing/invoicingCatalog/invoicingCatalogSelector';
|
|
4
4
|
import { fetchInvoicingCatalogItemDetailPage, seedInvoicingCatalogItemFormDraft, updateInvoicingCatalogItemDetail, } from './editInvoicingCatalogItemDetailViewReducer';
|
|
5
5
|
import { entityToInvoicingCatalogItemFormLocalData } from './invoicingCatalogItemFormHelper';
|
|
6
6
|
/**
|
|
@@ -34,7 +34,7 @@ export const seedInvoicingCatalogItemFormDraftEpic = (actions$, state$) => actio
|
|
|
34
34
|
}
|
|
35
35
|
// Nothing to seed from on a cold open; the detail response triggers this
|
|
36
36
|
// again once it lands.
|
|
37
|
-
const plan =
|
|
37
|
+
const plan = getInvoicingCatalogItemById(state.invoicingCatalogState, id);
|
|
38
38
|
if (plan == null) {
|
|
39
39
|
return EMPTY;
|
|
40
40
|
}
|
|
@@ -95,6 +95,10 @@ const editInvoicingCustomerDetailView = createSlice({
|
|
|
95
95
|
error: undefined,
|
|
96
96
|
};
|
|
97
97
|
},
|
|
98
|
+
// Makes the page epic refetch a record that changed server-side (e.g. a new subscription).
|
|
99
|
+
invalidateInvoicingCustomerDetail(draft, action) {
|
|
100
|
+
delete draft.recordStateById[action.payload.id];
|
|
101
|
+
},
|
|
98
102
|
fetchInvoicingCustomerDetail(draft, action) {
|
|
99
103
|
draft.recordStateById[action.payload.id] = {
|
|
100
104
|
fetchState: 'In-Progress',
|
|
@@ -198,5 +202,5 @@ const editInvoicingCustomerDetailView = createSlice({
|
|
|
198
202
|
});
|
|
199
203
|
},
|
|
200
204
|
});
|
|
201
|
-
export const { fetchInvoicingPaymentLinkEmailPreview, fetchInvoicingPaymentLinkEmailPreviewPage, updateInvoicingPaymentLinkEmailPreview, updateInvoicingPaymentLinkEmailPreviewFailure, addPromotionalCreditsFailure, addPromotionalCreditsSuccess, fetchInvoicingCustomerDetail, fetchInvoicingCustomerDetailPage, seedInvoicingCustomerFormDraft, updateInvoicingCustomerDetail, resetPromotionalCreditAction, submitAddPromotionalCredits, updateInvoicingCustomerDetailFailure, updateInvoicingCustomerFormDraft, submitInvoicingCustomerForm, submitInvoicingCustomerFormSuccess, submitInvoicingCustomerFormFailure, resetEditInvoicingCustomerDetailView, initializeInvoicingCustomerAddress, claimInvoicingCustomerBillingAddress, } = editInvoicingCustomerDetailView.actions;
|
|
205
|
+
export const { fetchInvoicingPaymentLinkEmailPreview, fetchInvoicingPaymentLinkEmailPreviewPage, updateInvoicingPaymentLinkEmailPreview, updateInvoicingPaymentLinkEmailPreviewFailure, addPromotionalCreditsFailure, addPromotionalCreditsSuccess, fetchInvoicingCustomerDetail, fetchInvoicingCustomerDetailPage, invalidateInvoicingCustomerDetail, seedInvoicingCustomerFormDraft, updateInvoicingCustomerDetail, resetPromotionalCreditAction, submitAddPromotionalCredits, updateInvoicingCustomerDetailFailure, updateInvoicingCustomerFormDraft, submitInvoicingCustomerForm, submitInvoicingCustomerFormSuccess, submitInvoicingCustomerFormFailure, resetEditInvoicingCustomerDetailView, initializeInvoicingCustomerAddress, claimInvoicingCustomerBillingAddress, } = editInvoicingCustomerDetailView.actions;
|
|
202
206
|
export default editInvoicingCustomerDetailView.reducer;
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { toAmount } from '../../../commonStateTypes/amount';
|
|
2
|
+
import { date } from '../../../zeniDayJS';
|
|
1
3
|
import { toAddressUpdatableInfoPayload } from '../../addressView/addressViewPayload';
|
|
2
4
|
import { invoicingSubscriptionTodayIso, toInvoicingSubscriptionBillingCycles, toInvoicingSubscriptionQuantity, } from './invoicingSubscriptionFormConfig';
|
|
3
5
|
/**
|
|
@@ -56,3 +58,56 @@ export const localDataToSaveInvoicingSubscriptionPayload = (localData, shippingA
|
|
|
56
58
|
: {}),
|
|
57
59
|
};
|
|
58
60
|
};
|
|
61
|
+
const toEstimateDate = (value) => value != null && value !== '' ? date(value) : undefined;
|
|
62
|
+
const mapInvoiceEstimate = (payload, money) => payload == null
|
|
63
|
+
? undefined
|
|
64
|
+
: {
|
|
65
|
+
amountDue: money(payload.amount_due),
|
|
66
|
+
couponDiscountTotal: money(payload.coupon_discount_total),
|
|
67
|
+
creditsApplied: money(payload.credits_applied),
|
|
68
|
+
dueDate: toEstimateDate(payload.due_date),
|
|
69
|
+
grossSubTotal: money(payload.gross_sub_total),
|
|
70
|
+
invoiceDate: toEstimateDate(payload.invoice_date),
|
|
71
|
+
lineItems: payload.line_items.map((lineItem) => ({
|
|
72
|
+
amount: money(lineItem.amount),
|
|
73
|
+
dateFrom: toEstimateDate(lineItem.date_from),
|
|
74
|
+
dateTo: toEstimateDate(lineItem.date_to),
|
|
75
|
+
description: lineItem.description,
|
|
76
|
+
discountAmount: money(lineItem.discount_amount),
|
|
77
|
+
quantity: lineItem.quantity,
|
|
78
|
+
taxAmount: lineItem.tax_amount != null
|
|
79
|
+
? money(lineItem.tax_amount)
|
|
80
|
+
: undefined,
|
|
81
|
+
unitAmount: money(lineItem.unit_amount),
|
|
82
|
+
})),
|
|
83
|
+
promotionalCreditsApplied: money(payload.promotional_credits_applied),
|
|
84
|
+
subTotal: money(payload.sub_total),
|
|
85
|
+
tax: payload.tax != null ? money(payload.tax) : undefined,
|
|
86
|
+
total: money(payload.total),
|
|
87
|
+
};
|
|
88
|
+
export const mapInvoicingSubscriptionEstimatePayloadToState = (payload) => {
|
|
89
|
+
const money = (amount) => toAmount(amount, payload.currency_code, payload.currency_symbol);
|
|
90
|
+
const term = payload.subscription_estimate;
|
|
91
|
+
const proration = payload.proration_estimate;
|
|
92
|
+
return {
|
|
93
|
+
invoice: mapInvoiceEstimate(payload.invoice_estimate, money),
|
|
94
|
+
nextInvoice: mapInvoiceEstimate(payload.next_invoice_estimate, money),
|
|
95
|
+
proration: proration != null
|
|
96
|
+
? {
|
|
97
|
+
documentType: proration.document_type,
|
|
98
|
+
invoice: mapInvoiceEstimate(proration.invoice_estimate, money),
|
|
99
|
+
net: money(proration.net),
|
|
100
|
+
}
|
|
101
|
+
: undefined,
|
|
102
|
+
reason: payload.reason,
|
|
103
|
+
term: term != null
|
|
104
|
+
? {
|
|
105
|
+
becomesNonRenewing: term.becomes_non_renewing_after_this_term === true,
|
|
106
|
+
currentTermEnd: toEstimateDate(term.current_term_end),
|
|
107
|
+
currentTermStart: toEstimateDate(term.current_term_start),
|
|
108
|
+
nextBillingAt: toEstimateDate(term.next_billing_at),
|
|
109
|
+
}
|
|
110
|
+
: undefined,
|
|
111
|
+
willBeInvoiced: payload.will_be_invoiced,
|
|
112
|
+
};
|
|
113
|
+
};
|
|
@@ -2,10 +2,14 @@ import { createSlice } from '@reduxjs/toolkit';
|
|
|
2
2
|
import { clearAll } from '../../../rootActions';
|
|
3
3
|
import { applyInvoicingFormDraftEdit, applyInvoicingFormDraftFailure, applyInvoicingFormDraftSaved, applyInvoicingFormDraftSeed, } from '../invoicingFormDraft';
|
|
4
4
|
import { initialInvoicingSubscriptionFormLocalData } from './invoicingSubscriptionFormConfig';
|
|
5
|
+
const initialEstimateState = {
|
|
6
|
+
fetchState: { fetchState: 'Not-Started', error: undefined },
|
|
7
|
+
};
|
|
5
8
|
export const initialState = {
|
|
6
9
|
fetchState: 'Not-Started',
|
|
7
10
|
error: undefined,
|
|
8
11
|
editDraftById: {},
|
|
12
|
+
estimateState: initialEstimateState,
|
|
9
13
|
actionStateById: {},
|
|
10
14
|
recordStateById: {},
|
|
11
15
|
shippingAddressOwnerId: undefined,
|
|
@@ -19,6 +23,36 @@ const editInvoicingSubscriptionDetailView = createSlice({
|
|
|
19
23
|
name: 'editInvoicingSubscriptionDetailView',
|
|
20
24
|
initialState,
|
|
21
25
|
reducers: {
|
|
26
|
+
// What saving the draft would bill (no id: create); see the estimate epic.
|
|
27
|
+
fetchInvoicingSubscriptionEstimate(draft, action) {
|
|
28
|
+
draft.estimateState = {
|
|
29
|
+
fetchState: { fetchState: 'In-Progress', error: undefined },
|
|
30
|
+
subscriptionId: action.payload.id,
|
|
31
|
+
};
|
|
32
|
+
},
|
|
33
|
+
// Dropped when the dialog has since moved to another subscription.
|
|
34
|
+
updateInvoicingSubscriptionEstimate(draft, action) {
|
|
35
|
+
if (draft.estimateState.subscriptionId !== action.payload.id) {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
draft.estimateState.estimate = action.payload.estimate;
|
|
39
|
+
draft.estimateState.fetchState = {
|
|
40
|
+
fetchState: 'Completed',
|
|
41
|
+
error: undefined,
|
|
42
|
+
};
|
|
43
|
+
},
|
|
44
|
+
updateInvoicingSubscriptionEstimateFailure(draft, action) {
|
|
45
|
+
if (draft.estimateState.subscriptionId !== action.payload.id) {
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
draft.estimateState.fetchState = {
|
|
49
|
+
fetchState: 'Error',
|
|
50
|
+
error: action.payload.status,
|
|
51
|
+
};
|
|
52
|
+
},
|
|
53
|
+
clearInvoicingSubscriptionEstimate(draft) {
|
|
54
|
+
draft.estimateState = initialEstimateState;
|
|
55
|
+
},
|
|
22
56
|
runInvoicingSubscriptionAction(draft, action) {
|
|
23
57
|
draft.actionStateById[action.payload.id] = {
|
|
24
58
|
fetchState: 'In-Progress',
|
|
@@ -158,5 +192,5 @@ const editInvoicingSubscriptionDetailView = createSlice({
|
|
|
158
192
|
});
|
|
159
193
|
},
|
|
160
194
|
});
|
|
161
|
-
export const { runInvoicingSubscriptionAction, runInvoicingSubscriptionActionFailure, runInvoicingSubscriptionActionSuccess, fetchInvoicingSubscriptionDetail, fetchInvoicingSubscriptionDetailPage, fetchInvoicingSubscriptionFormPage, seedInvoicingSubscriptionFormDraft, updateInvoicingSubscriptionDetail, updateInvoicingSubscriptionDetailFailure, updateInvoicingSubscriptionFormDraft, submitInvoicingSubscriptionForm, submitInvoicingSubscriptionFormSuccess, submitInvoicingSubscriptionFormFailure, resetEditInvoicingSubscriptionDetailView, initializeInvoicingSubscriptionAddress, claimInvoicingSubscriptionShippingAddress, syncInvoicingSubscriptionCoupons, } = editInvoicingSubscriptionDetailView.actions;
|
|
195
|
+
export const { runInvoicingSubscriptionAction, runInvoicingSubscriptionActionFailure, runInvoicingSubscriptionActionSuccess, fetchInvoicingSubscriptionDetail, fetchInvoicingSubscriptionDetailPage, fetchInvoicingSubscriptionFormPage, seedInvoicingSubscriptionFormDraft, updateInvoicingSubscriptionDetail, updateInvoicingSubscriptionDetailFailure, updateInvoicingSubscriptionFormDraft, submitInvoicingSubscriptionForm, submitInvoicingSubscriptionFormSuccess, submitInvoicingSubscriptionFormFailure, resetEditInvoicingSubscriptionDetailView, initializeInvoicingSubscriptionAddress, claimInvoicingSubscriptionShippingAddress, syncInvoicingSubscriptionCoupons, fetchInvoicingSubscriptionEstimate, updateInvoicingSubscriptionEstimate, updateInvoicingSubscriptionEstimateFailure, clearInvoicingSubscriptionEstimate, } = editInvoicingSubscriptionDetailView.actions;
|
|
162
196
|
export default editInvoicingSubscriptionDetailView.reducer;
|
|
@@ -9,6 +9,57 @@ import { getInvoicingCustomerListView } from '../invoicingCustomerListView/invoi
|
|
|
9
9
|
import { getInvoicingFormDraftLocalData, isInvoicingFormDraftPending, } from '../invoicingFormDraft';
|
|
10
10
|
import { initialInvoicingSubscriptionFormLocalData } from './invoicingSubscriptionFormConfig';
|
|
11
11
|
import { INVOICING_SUBSCRIPTION_SHIPPING_ADDRESS_TYPE } from './invoicingSubscriptionFormConfig';
|
|
12
|
+
const notStartedEstimateView = {
|
|
13
|
+
creditsMayChange: false,
|
|
14
|
+
fetchState: { fetchState: 'Not-Started', error: undefined },
|
|
15
|
+
nonRenewing: false,
|
|
16
|
+
};
|
|
17
|
+
export const toInvoicingSubscriptionEstimateView = ({ estimate, fetchState, subscriptionId }, invoicingSubscriptionID) => {
|
|
18
|
+
// A slot asked for another subscription is not this form's estimate.
|
|
19
|
+
if (subscriptionId !== invoicingSubscriptionID) {
|
|
20
|
+
return notStartedEstimateView;
|
|
21
|
+
}
|
|
22
|
+
if (estimate == null) {
|
|
23
|
+
return { ...notStartedEstimateView, fetchState };
|
|
24
|
+
}
|
|
25
|
+
const { invoice, nextInvoice, proration, reason, term } = estimate;
|
|
26
|
+
const base = {
|
|
27
|
+
fetchState,
|
|
28
|
+
nonRenewing: term?.becomesNonRenewing === true,
|
|
29
|
+
nextBillingAt: term?.nextBillingAt,
|
|
30
|
+
reason: reason !== '' ? reason : undefined,
|
|
31
|
+
termEnd: term?.currentTermEnd,
|
|
32
|
+
termStart: term?.currentTermStart,
|
|
33
|
+
};
|
|
34
|
+
if (proration?.documentType === 'invoice' && proration.invoice != null) {
|
|
35
|
+
return {
|
|
36
|
+
...base,
|
|
37
|
+
creditsMayChange: false,
|
|
38
|
+
invoice: proration.invoice,
|
|
39
|
+
kind: 'proration_invoice',
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
if (proration?.documentType === 'credit_note') {
|
|
43
|
+
return {
|
|
44
|
+
...base,
|
|
45
|
+
creditsMayChange: false,
|
|
46
|
+
kind: 'proration_credit',
|
|
47
|
+
storeCredit: { ...proration.net, amount: Math.abs(proration.net.amount) },
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
if (estimate.willBeInvoiced && invoice != null) {
|
|
51
|
+
return { ...base, creditsMayChange: false, invoice, kind: 'invoice_now' };
|
|
52
|
+
}
|
|
53
|
+
if (nextInvoice != null) {
|
|
54
|
+
return {
|
|
55
|
+
...base,
|
|
56
|
+
creditsMayChange: true,
|
|
57
|
+
invoice: nextInvoice,
|
|
58
|
+
kind: 'later_invoice',
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
return { ...base, creditsMayChange: false, kind: 'reason_only' };
|
|
62
|
+
};
|
|
12
63
|
export const getEditInvoicingSubscriptionDetailViewState = (state, invoicingSubscriptionID) => {
|
|
13
64
|
const { error, fetchState, savedSubscriptionId } = state.editInvoicingSubscriptionDetailViewState;
|
|
14
65
|
const { subscriptionCommercialEditAllowedFromStatuses, subscriptionLifecycleActionOptions, subscriptionMetadataEditAllowedFromStatuses, } = getInvoicingConfigView(state);
|
|
@@ -86,6 +137,27 @@ export const getInvoicingItemsAllowedForPlan = (products, plan) => {
|
|
|
86
137
|
}
|
|
87
138
|
return products.filter((product) => product.itemID != null && allowedItemIDs.includes(product.itemID));
|
|
88
139
|
};
|
|
140
|
+
// Years count as months so they compare; any other unit only meets itself.
|
|
141
|
+
const billingPeriodInCommonUnit = (pricePoint) => {
|
|
142
|
+
const { period, periodUnit } = pricePoint;
|
|
143
|
+
if (period == null || periodUnit == null || periodUnit === '') {
|
|
144
|
+
return undefined;
|
|
145
|
+
}
|
|
146
|
+
return periodUnit === 'year'
|
|
147
|
+
? { count: period * 12, unit: 'month' }
|
|
148
|
+
: { count: period, unit: periodUnit };
|
|
149
|
+
};
|
|
150
|
+
// The add-on's period must divide the plan's (create 422s otherwise); fails open when they cannot be compared.
|
|
151
|
+
export const isInvoicingAddonPeriodCompatible = (addon, plan) => {
|
|
152
|
+
const addonPeriod = billingPeriodInCommonUnit(addon);
|
|
153
|
+
const planPeriod = plan != null ? billingPeriodInCommonUnit(plan) : undefined;
|
|
154
|
+
if (addonPeriod == null ||
|
|
155
|
+
planPeriod == null ||
|
|
156
|
+
addonPeriod.unit !== planPeriod.unit) {
|
|
157
|
+
return true;
|
|
158
|
+
}
|
|
159
|
+
return addonPeriod.count > 0 && planPeriod.count % addonPeriod.count === 0;
|
|
160
|
+
};
|
|
89
161
|
/**
|
|
90
162
|
* A price row can only be subscribed by its price code, so one without a code
|
|
91
163
|
* is not an option the form can offer.
|
|
@@ -149,7 +221,7 @@ const toInvoicingCustomerOption = (customer) => ({
|
|
|
149
221
|
value: customer.id,
|
|
150
222
|
});
|
|
151
223
|
export const getInvoicingSubscriptionFormView = (state, invoicingSubscriptionID) => {
|
|
152
|
-
const { editDraftById, error, fetchState, newSubscriptionDraft, savedSubscriptionId, } = state.editInvoicingSubscriptionDetailViewState;
|
|
224
|
+
const { editDraftById, error, estimateState, fetchState, newSubscriptionDraft, savedSubscriptionId, } = state.editInvoicingSubscriptionDetailViewState;
|
|
153
225
|
const mode = invoicingSubscriptionID != null ? 'edit' : 'create';
|
|
154
226
|
const subscription = invoicingSubscriptionID != null
|
|
155
227
|
? getInvoicingSubscriptionById(state.invoicingSubscriptionState, invoicingSubscriptionID)
|
|
@@ -174,7 +246,12 @@ export const getInvoicingSubscriptionFormView = (state, invoicingSubscriptionID)
|
|
|
174
246
|
// pickers offer only those: subscription create rejects the rest with a 422,
|
|
175
247
|
// and an option that can only fail is not a choice. The applicability rides
|
|
176
248
|
// on the price rows already in the store, so this is a join, not a refetch.
|
|
177
|
-
const
|
|
249
|
+
const selectedPlanPrice = planPrices.find((plan) => plan.priceCode === localData.planPriceCode);
|
|
250
|
+
// Add-ons also have to bill on the plan's cadence; charges are one-time.
|
|
251
|
+
const allowedCatalogPrices = getInvoicingItemsAllowedForPlan(getInvoicingCatalogPrices(catalogListView.products), selectedPlanPrice)
|
|
252
|
+
.filter(hasInvoicingPriceCode)
|
|
253
|
+
.filter((product) => product.itemType !== 'addon' ||
|
|
254
|
+
isInvoicingAddonPeriodCompatible(product, selectedPlanPrice));
|
|
178
255
|
const catalogItemOptionsOfType = (itemType) => allowedCatalogPrices
|
|
179
256
|
.filter((product) => product.itemType === itemType)
|
|
180
257
|
.map(toInvoicingCatalogItemOption);
|
|
@@ -198,6 +275,7 @@ export const getInvoicingSubscriptionFormView = (state, invoicingSubscriptionID)
|
|
|
198
275
|
subscriptionMetadataEditAllowedFromStatuses: configState.subscriptionMetadataEditAllowedFromStatuses,
|
|
199
276
|
recordState,
|
|
200
277
|
isFormLoading: isInvoicingFormDraftPending(editDraftById, invoicingSubscriptionID),
|
|
278
|
+
estimate: toInvoicingSubscriptionEstimateView(estimateState, invoicingSubscriptionID),
|
|
201
279
|
fieldHelp: invoicingFieldHelpForGroup(state, 'subscription'),
|
|
202
280
|
addons: catalogItemOptionsOfType('addon'),
|
|
203
281
|
charges: catalogItemOptionsOfType('charge'),
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { of } from 'rxjs';
|
|
2
|
+
import { catchError, filter, map, switchMap } from 'rxjs/operators';
|
|
3
|
+
import { getInvoicingCouponByCode } from '../../../entity/invoicing/coupon/couponSelector';
|
|
4
|
+
import { createZeniAPIStatus, isSuccessResponse } from '../../../responsePayload';
|
|
5
|
+
import { mapInvoicingSubscriptionEstimatePayloadToState, } from './editInvoicingSubscriptionDetailViewPayload';
|
|
6
|
+
import { fetchInvoicingSubscriptionEstimate, updateInvoicingSubscriptionEstimate, updateInvoicingSubscriptionEstimateFailure, } from './editInvoicingSubscriptionDetailViewReducer';
|
|
7
|
+
import { invoicingSubscriptionSaveRequest, isInvoicingSubscriptionCouponChanged, } from './saveInvoicingSubscriptionEpic';
|
|
8
|
+
// Posts Save's exact body; a changed coupon rides along as `coupons` (Save syncs it after the PUT).
|
|
9
|
+
export const fetchInvoicingSubscriptionEstimateEpic = (actions$, state$, zeniAPI) => actions$.pipe(filter(fetchInvoicingSubscriptionEstimate.match), switchMap((action) => {
|
|
10
|
+
const { id } = action.payload;
|
|
11
|
+
const { data, localData, previousCouponCode } = invoicingSubscriptionSaveRequest(state$.value, id);
|
|
12
|
+
const couponCode = localData.couponId;
|
|
13
|
+
const coupon = getInvoicingCouponByCode(state$.value.invoicingCouponState, couponCode);
|
|
14
|
+
const body = id != null &&
|
|
15
|
+
isInvoicingSubscriptionCouponChanged(previousCouponCode, couponCode)
|
|
16
|
+
? {
|
|
17
|
+
...data,
|
|
18
|
+
coupons: couponCode !== ''
|
|
19
|
+
? [
|
|
20
|
+
{
|
|
21
|
+
applied_count: 0,
|
|
22
|
+
apply_till: null,
|
|
23
|
+
coupon_code: couponCode,
|
|
24
|
+
coupon_id: coupon?.id ?? couponCode,
|
|
25
|
+
},
|
|
26
|
+
]
|
|
27
|
+
: [],
|
|
28
|
+
}
|
|
29
|
+
: data;
|
|
30
|
+
const baseUrl = `${zeniAPI.apiEndPoints.invoicingMicroServiceBaseUrl}/1.0/subscriptions`;
|
|
31
|
+
const url = id != null
|
|
32
|
+
? `${baseUrl}/${encodeURIComponent(id)}/estimate`
|
|
33
|
+
: `${baseUrl}/estimate`;
|
|
34
|
+
return zeniAPI
|
|
35
|
+
.postAndGetJSON(url, body)
|
|
36
|
+
.pipe(map((response) => isSuccessResponse(response) && response.data != null
|
|
37
|
+
? updateInvoicingSubscriptionEstimate({
|
|
38
|
+
estimate: mapInvoicingSubscriptionEstimatePayloadToState(response.data),
|
|
39
|
+
id,
|
|
40
|
+
})
|
|
41
|
+
: updateInvoicingSubscriptionEstimateFailure({
|
|
42
|
+
id,
|
|
43
|
+
status: response.status,
|
|
44
|
+
})), catchError((error) => of(updateInvoicingSubscriptionEstimateFailure({
|
|
45
|
+
id,
|
|
46
|
+
status: createZeniAPIStatus('Unexpected Error', 'Subscription estimate REST API call errored out ' +
|
|
47
|
+
JSON.stringify(error)),
|
|
48
|
+
}))));
|
|
49
|
+
}));
|
package/lib/esm/view/invoicing/editInvoicingSubscriptionDetailView/saveInvoicingSubscriptionEpic.js
CHANGED
|
@@ -7,14 +7,17 @@ import { apiErrorSnackbarVariables } from '../../../entity/snackbar/snackbarApiE
|
|
|
7
7
|
import { openSnackbar } from '../../../entity/snackbar/snackbarReducer';
|
|
8
8
|
import { createZeniAPIStatus, isSuccessResponse } from '../../../responsePayload';
|
|
9
9
|
import { resetNewAddressDataInLocalStore } from '../../addressView/addressViewReducer';
|
|
10
|
+
import { invalidateInvoicingCustomerDetail } from '../editInvoicingCustomerDetailView/editInvoicingCustomerDetailViewReducer';
|
|
11
|
+
import { invalidateInvoiceListForCustomer } from '../invoiceList/invoiceListReducer';
|
|
10
12
|
import { addInvoicingSubscriptionToList } from '../invoicingSubscriptionListView/invoicingSubscriptionListViewReducer';
|
|
11
13
|
import { localDataToSaveInvoicingSubscriptionPayload, } from './editInvoicingSubscriptionDetailViewPayload';
|
|
12
14
|
import { submitInvoicingSubscriptionForm, submitInvoicingSubscriptionFormFailure, submitInvoicingSubscriptionFormSuccess, syncInvoicingSubscriptionCoupons, } from './editInvoicingSubscriptionDetailViewReducer';
|
|
13
15
|
import { INVOICING_SUBSCRIPTION_SHIPPING_ADDRESS_TYPE } from './invoicingSubscriptionFormConfig';
|
|
14
16
|
const normalizeCouponCode = (value) => (value ?? '').trim().toUpperCase();
|
|
15
|
-
export const
|
|
16
|
-
|
|
17
|
-
|
|
17
|
+
export const isInvoicingSubscriptionCouponChanged = (previousCouponCode, nextCouponCode) => normalizeCouponCode(previousCouponCode) !==
|
|
18
|
+
normalizeCouponCode(nextCouponCode);
|
|
19
|
+
// The draft a save reads and the body it sends; the estimate previews the same body.
|
|
20
|
+
export const invoicingSubscriptionSaveRequest = (rootState, id) => {
|
|
18
21
|
const viewState = rootState.editInvoicingSubscriptionDetailViewState;
|
|
19
22
|
const localData = (id != null
|
|
20
23
|
? viewState.editDraftById[id]?.localData
|
|
@@ -23,18 +26,23 @@ export const saveInvoicingSubscriptionEpic = (actions$, state$, zeniAPI) => acti
|
|
|
23
26
|
// object in the payload (same pattern as the customer save flow).
|
|
24
27
|
const shippingAddress = rootState.addressViewState.newAddressState?.[INVOICING_SUBSCRIPTION_SHIPPING_ADDRESS_TYPE]?.addressToCreate;
|
|
25
28
|
const data = localDataToSaveInvoicingSubscriptionPayload(localData, shippingAddress);
|
|
26
|
-
// Coupons are synced via apply/remove-coupon after PUT (not via coupon_ids).
|
|
27
29
|
if (id != null) {
|
|
28
30
|
delete data.coupon_ids;
|
|
29
31
|
}
|
|
32
|
+
const savedCoupon = id != null
|
|
33
|
+
? rootState.invoicingSubscriptionState.invoicingSubscriptionByID[id]
|
|
34
|
+
?.coupons?.[0]
|
|
35
|
+
: undefined;
|
|
36
|
+
return {
|
|
37
|
+
data,
|
|
38
|
+
localData,
|
|
39
|
+
previousCouponCode: savedCoupon?.couponCode ?? savedCoupon?.couponID ?? '',
|
|
40
|
+
};
|
|
41
|
+
};
|
|
42
|
+
export const saveInvoicingSubscriptionEpic = (actions$, state$, zeniAPI) => actions$.pipe(filter(submitInvoicingSubscriptionForm.match), mergeMap((action) => {
|
|
43
|
+
const { id } = action.payload;
|
|
44
|
+
const { data, localData, previousCouponCode } = invoicingSubscriptionSaveRequest(state$.value, id);
|
|
30
45
|
const baseUrl = `${zeniAPI.apiEndPoints.invoicingMicroServiceBaseUrl}/1.0`;
|
|
31
|
-
const previousCouponCode = id != null
|
|
32
|
-
? (rootState.invoicingSubscriptionState.invoicingSubscriptionByID[id]
|
|
33
|
-
?.coupons?.[0]?.couponCode ??
|
|
34
|
-
rootState.invoicingSubscriptionState.invoicingSubscriptionByID[id]
|
|
35
|
-
?.coupons?.[0]?.couponID ??
|
|
36
|
-
'')
|
|
37
|
-
: '';
|
|
38
46
|
const request$ = id != null
|
|
39
47
|
? zeniAPI.putAndGetJSON(`${baseUrl}/subscriptions/${encodeURIComponent(id)}`, { ...data })
|
|
40
48
|
: zeniAPI.postAndGetJSON(`${baseUrl}/subscriptions`, { ...data });
|
|
@@ -52,8 +60,7 @@ export const saveInvoicingSubscriptionEpic = (actions$, state$, zeniAPI) => acti
|
|
|
52
60
|
}
|
|
53
61
|
const saved = response.data;
|
|
54
62
|
const couponChanged = id != null &&
|
|
55
|
-
|
|
56
|
-
normalizeCouponCode(localData.couponId);
|
|
63
|
+
isInvoicingSubscriptionCouponChanged(previousCouponCode, localData.couponId);
|
|
57
64
|
// Edit + coupon change: merge the saved subscription, then hand the
|
|
58
65
|
// coupon attach/detach to syncInvoicingSubscriptionCouponsEpic, which
|
|
59
66
|
// finishes the flow (form success on both calls ok, form failure
|
|
@@ -86,7 +93,9 @@ export const saveInvoicingSubscriptionEpic = (actions$, state$, zeniAPI) => acti
|
|
|
86
93
|
resultActions.push(addInvoicingSubscriptionToList({
|
|
87
94
|
id: saved.id,
|
|
88
95
|
status: saved.status,
|
|
89
|
-
})
|
|
96
|
+
}),
|
|
97
|
+
// The service may bill the first invoice on create.
|
|
98
|
+
invalidateInvoicingCustomerDetail({ id: saved.customer_id }), invalidateInvoiceListForCustomer({ customerId: saved.customer_id }));
|
|
90
99
|
}
|
|
91
100
|
resultActions.push(openSnackbar({
|
|
92
101
|
messageSection: 'invoicing_subscription_saved',
|
|
@@ -124,6 +124,13 @@ const invoiceList = createSlice({
|
|
|
124
124
|
draft.kpisFetchState.fetchState = 'Error';
|
|
125
125
|
draft.kpisFetchState.error = action.payload;
|
|
126
126
|
},
|
|
127
|
+
// Counts are fetched under the same customer filter, so they go stale with the tabs.
|
|
128
|
+
invalidateInvoiceListForCustomer(draft, action) {
|
|
129
|
+
if (draft.filters.customerId === action.payload.customerId) {
|
|
130
|
+
draft.invoiceByTab = makeInvoiceByTab();
|
|
131
|
+
draft.countsFetchState = { fetchState: 'Not-Started', error: undefined };
|
|
132
|
+
}
|
|
133
|
+
},
|
|
127
134
|
updateInvoiceListFilters(draft, action) {
|
|
128
135
|
draft.filters = action.payload;
|
|
129
136
|
},
|
|
@@ -166,5 +173,5 @@ const invoiceList = createSlice({
|
|
|
166
173
|
});
|
|
167
174
|
},
|
|
168
175
|
});
|
|
169
|
-
export const { fetchInvoiceList, fetchInvoiceListPage, updateInvoiceList, updateInvoiceListFailure, fetchInvoiceCounts, updateInvoiceCounts, updateInvoiceCountsFailure, fetchInvoiceKPIs, updateInvoiceKPIs, updateInvoiceKPIsFailure, updateInvoiceListFilters, setInvoiceActiveTab, addInvoiceToList, } = invoiceList.actions;
|
|
176
|
+
export const { fetchInvoiceList, fetchInvoiceListPage, updateInvoiceList, updateInvoiceListFailure, fetchInvoiceCounts, updateInvoiceCounts, updateInvoiceCountsFailure, fetchInvoiceKPIs, updateInvoiceKPIs, updateInvoiceKPIsFailure, updateInvoiceListFilters, setInvoiceActiveTab, addInvoiceToList, invalidateInvoiceListForCustomer, } = invoiceList.actions;
|
|
170
177
|
export default invoiceList.reducer;
|
package/lib/esm/view/invoicing/invoicingCatalogListView/epics/fetchInvoicingCatalogPlanListEpic.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { of } from 'rxjs';
|
|
2
2
|
import { catchError, filter, switchMap } from 'rxjs/operators';
|
|
3
|
-
import {
|
|
3
|
+
import { updateInvoicingCatalogItems } from '../../../../entity/invoicing/invoicingCatalog/invoicingCatalogReducer';
|
|
4
4
|
import { createZeniAPIStatus, isSuccessResponse, } from '../../../../responsePayload';
|
|
5
5
|
import { INVOICING_LIST_PAGE_SIZE, applyListSortParams, withQuery, } from '../../invoicingApiHelper';
|
|
6
6
|
import { fetchInvoicingCatalogPlanList, updateInvoicingCatalogPlanList, updateInvoicingCatalogPlanListFailure, } from '../invoicingCatalogListViewReducer';
|
|
@@ -29,7 +29,7 @@ export const fetchInvoicingCatalogPlanListEpic = (actions$, _state$, zeniAPI) =>
|
|
|
29
29
|
if (isSuccessResponse(response)) {
|
|
30
30
|
const items = response.data?.items ?? [];
|
|
31
31
|
return [
|
|
32
|
-
|
|
32
|
+
updateInvoicingCatalogItems(items, 'merge'),
|
|
33
33
|
updateInvoicingCatalogPlanList({
|
|
34
34
|
ids: items.map((item) => item.id),
|
|
35
35
|
nextCursor: response.data?.next_cursor ?? null,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { of } from 'rxjs';
|
|
2
2
|
import { catchError, filter, switchMap } from 'rxjs/operators';
|
|
3
|
-
import {
|
|
3
|
+
import { updateInvoicingCatalogItems } from '../../../../entity/invoicing/invoicingCatalog/invoicingCatalogReducer';
|
|
4
4
|
import { createZeniAPIStatus, isSuccessResponse, } from '../../../../responsePayload';
|
|
5
5
|
import { INVOICING_LIST_PAGE_SIZE, applyListSortParams, withQuery, } from '../../invoicingApiHelper';
|
|
6
6
|
import { fetchInvoicingCatalogProductList, updateInvoicingCatalogProductList, updateInvoicingCatalogProductListFailure, } from '../invoicingCatalogListViewReducer';
|
|
@@ -29,7 +29,7 @@ export const fetchInvoicingCatalogProductListEpic = (actions$, _state$, zeniAPI)
|
|
|
29
29
|
if (isSuccessResponse(response)) {
|
|
30
30
|
const items = response.data?.items ?? [];
|
|
31
31
|
return [
|
|
32
|
-
|
|
32
|
+
updateInvoicingCatalogItems(items, 'merge'),
|
|
33
33
|
updateInvoicingCatalogProductList({
|
|
34
34
|
ids: items.map((item) => item.id),
|
|
35
35
|
nextCursor: response.data?.next_cursor ?? null,
|