@zeniai/client-epic-state 5.2.48 → 5.2.49-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/esm/view/expenseAutomationView/reducers/reconciliationViewReducer.js +80 -53
- package/lib/esm/view/invoicing/editInvoicingCatalogItemDetailView/editInvoicingCatalogItemDetailViewReducer.js +19 -11
- package/lib/esm/view/invoicing/editInvoicingCatalogItemDetailView/editInvoicingCatalogItemDetailViewSelector.js +4 -1
- package/lib/esm/view/invoicing/editInvoicingCatalogItemDetailView/saveInvoicingCatalogItemEpic.js +3 -2
- package/lib/esm/view/invoicing/editInvoicingCatalogItemDetailView/seedInvoicingCatalogItemFormDraftEpic.js +18 -9
- package/lib/esm/view/invoicing/editInvoicingCouponDetailView/editInvoicingCouponDetailViewReducer.js +19 -11
- package/lib/esm/view/invoicing/editInvoicingCouponDetailView/editInvoicingCouponDetailViewSelector.js +3 -1
- package/lib/esm/view/invoicing/editInvoicingCouponDetailView/saveInvoicingCouponEpic.js +1 -1
- package/lib/esm/view/invoicing/editInvoicingCouponDetailView/seedInvoicingCouponFormDraftEpic.js +16 -7
- package/lib/esm/view/invoicing/editInvoicingCustomerDetailView/editInvoicingCustomerDetailViewReducer.js +19 -11
- package/lib/esm/view/invoicing/editInvoicingCustomerDetailView/editInvoicingCustomerDetailViewSelector.js +4 -2
- package/lib/esm/view/invoicing/editInvoicingCustomerDetailView/saveInvoicingCustomerEpic.js +1 -1
- package/lib/esm/view/invoicing/editInvoicingSubscriptionDetailView/editInvoicingSubscriptionDetailViewReducer.js +21 -13
- package/lib/esm/view/invoicing/editInvoicingSubscriptionDetailView/editInvoicingSubscriptionDetailViewSelector.js +3 -2
- package/lib/esm/view/invoicing/editInvoicingSubscriptionDetailView/fetchInvoicingSubscriptionFormPageEpic.js +4 -1
- package/lib/esm/view/invoicing/editInvoicingSubscriptionDetailView/saveInvoicingSubscriptionEpic.js +1 -1
- package/lib/esm/view/invoicing/editInvoicingSubscriptionDetailView/seedInvoicingSubscriptionFormDraftEpic.js +18 -9
- package/lib/esm/view/invoicing/invoicingFormDraft.js +56 -0
- package/lib/view/expenseAutomationView/reducers/reconciliationViewReducer.js +80 -53
- package/lib/view/invoicing/editInvoicingCatalogItemDetailView/editInvoicingCatalogItemDetailViewReducer.d.ts +3 -1
- package/lib/view/invoicing/editInvoicingCatalogItemDetailView/editInvoicingCatalogItemDetailViewReducer.js +19 -11
- package/lib/view/invoicing/editInvoicingCatalogItemDetailView/editInvoicingCatalogItemDetailViewSelector.d.ts +10 -0
- package/lib/view/invoicing/editInvoicingCatalogItemDetailView/editInvoicingCatalogItemDetailViewSelector.js +4 -1
- package/lib/view/invoicing/editInvoicingCatalogItemDetailView/editInvoicingCatalogItemDetailViewState.d.ts +2 -1
- package/lib/view/invoicing/editInvoicingCatalogItemDetailView/saveInvoicingCatalogItemEpic.js +3 -2
- package/lib/view/invoicing/editInvoicingCatalogItemDetailView/seedInvoicingCatalogItemFormDraftEpic.d.ts +10 -6
- package/lib/view/invoicing/editInvoicingCatalogItemDetailView/seedInvoicingCatalogItemFormDraftEpic.js +17 -8
- package/lib/view/invoicing/editInvoicingCouponDetailView/editInvoicingCouponDetailViewReducer.d.ts +3 -1
- package/lib/view/invoicing/editInvoicingCouponDetailView/editInvoicingCouponDetailViewReducer.js +19 -11
- package/lib/view/invoicing/editInvoicingCouponDetailView/editInvoicingCouponDetailViewSelector.d.ts +13 -0
- package/lib/view/invoicing/editInvoicingCouponDetailView/editInvoicingCouponDetailViewSelector.js +3 -1
- package/lib/view/invoicing/editInvoicingCouponDetailView/editInvoicingCouponDetailViewState.d.ts +2 -1
- package/lib/view/invoicing/editInvoicingCouponDetailView/saveInvoicingCouponEpic.js +1 -1
- package/lib/view/invoicing/editInvoicingCouponDetailView/seedInvoicingCouponFormDraftEpic.d.ts +10 -6
- package/lib/view/invoicing/editInvoicingCouponDetailView/seedInvoicingCouponFormDraftEpic.js +15 -6
- package/lib/view/invoicing/editInvoicingCustomerDetailView/editInvoicingCustomerDetailViewReducer.d.ts +3 -1
- package/lib/view/invoicing/editInvoicingCustomerDetailView/editInvoicingCustomerDetailViewReducer.js +19 -11
- package/lib/view/invoicing/editInvoicingCustomerDetailView/editInvoicingCustomerDetailViewSelector.d.ts +9 -0
- package/lib/view/invoicing/editInvoicingCustomerDetailView/editInvoicingCustomerDetailViewSelector.js +4 -2
- package/lib/view/invoicing/editInvoicingCustomerDetailView/editInvoicingCustomerDetailViewState.d.ts +2 -1
- package/lib/view/invoicing/editInvoicingCustomerDetailView/saveInvoicingCustomerEpic.js +1 -1
- package/lib/view/invoicing/editInvoicingSubscriptionDetailView/editInvoicingSubscriptionDetailViewReducer.d.ts +3 -1
- package/lib/view/invoicing/editInvoicingSubscriptionDetailView/editInvoicingSubscriptionDetailViewReducer.js +21 -13
- package/lib/view/invoicing/editInvoicingSubscriptionDetailView/editInvoicingSubscriptionDetailViewSelector.d.ts +9 -0
- package/lib/view/invoicing/editInvoicingSubscriptionDetailView/editInvoicingSubscriptionDetailViewSelector.js +3 -2
- package/lib/view/invoicing/editInvoicingSubscriptionDetailView/editInvoicingSubscriptionDetailViewState.d.ts +2 -1
- package/lib/view/invoicing/editInvoicingSubscriptionDetailView/fetchInvoicingSubscriptionFormPageEpic.js +4 -1
- package/lib/view/invoicing/editInvoicingSubscriptionDetailView/saveInvoicingSubscriptionEpic.js +1 -1
- package/lib/view/invoicing/editInvoicingSubscriptionDetailView/seedInvoicingSubscriptionFormDraftEpic.d.ts +10 -6
- package/lib/view/invoicing/editInvoicingSubscriptionDetailView/seedInvoicingSubscriptionFormDraftEpic.js +17 -8
- package/lib/view/invoicing/invoicingFormDraft.d.ts +50 -0
- package/lib/view/invoicing/invoicingFormDraft.js +63 -0
- package/package.json +1 -1
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { createSlice } from '@reduxjs/toolkit';
|
|
2
2
|
import { clearAll } from '../../../rootActions';
|
|
3
|
+
import { applyInvoicingFormDraftEdit, applyInvoicingFormDraftFailure, applyInvoicingFormDraftSeed, } from '../invoicingFormDraft';
|
|
3
4
|
import { initialInvoicingSubscriptionFormLocalData } from './invoicingSubscriptionFormConfig';
|
|
4
5
|
export const initialState = {
|
|
5
6
|
fetchState: 'Not-Started',
|
|
@@ -65,15 +66,12 @@ const editInvoicingSubscriptionDetailView = createSlice({
|
|
|
65
66
|
fetchState: 'Error',
|
|
66
67
|
error: action.payload.error,
|
|
67
68
|
};
|
|
69
|
+
applyInvoicingFormDraftFailure(draft.editDraftById, action.payload.id, initialInvoicingSubscriptionFormLocalData);
|
|
68
70
|
},
|
|
69
|
-
/**
|
|
70
|
-
* Seed the draft from the loaded record, once. Skipped when a draft already
|
|
71
|
-
* exists so a late refetch cannot overwrite in-progress edits.
|
|
72
|
-
*/
|
|
71
|
+
/** Fill the draft from the loaded record — see `applyInvoicingFormDraftSeed`. */
|
|
73
72
|
seedInvoicingSubscriptionFormDraft(draft, action) {
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
}
|
|
73
|
+
const { id, localData } = action.payload;
|
|
74
|
+
applyInvoicingFormDraftSeed(draft.editDraftById, id, localData);
|
|
77
75
|
},
|
|
78
76
|
fetchInvoicingSubscriptionFormPage: {
|
|
79
77
|
reducer() {
|
|
@@ -87,7 +85,7 @@ const editInvoicingSubscriptionDetailView = createSlice({
|
|
|
87
85
|
updateInvoicingSubscriptionFormDraft(draft, action) {
|
|
88
86
|
const { id, localData } = action.payload;
|
|
89
87
|
if (id != null) {
|
|
90
|
-
draft.editDraftById
|
|
88
|
+
applyInvoicingFormDraftEdit(draft.editDraftById, id, localData);
|
|
91
89
|
}
|
|
92
90
|
else {
|
|
93
91
|
draft.newSubscriptionDraft = localData;
|
|
@@ -112,14 +110,24 @@ const editInvoicingSubscriptionDetailView = createSlice({
|
|
|
112
110
|
draft.fetchState = 'Error';
|
|
113
111
|
draft.error = action.payload;
|
|
114
112
|
},
|
|
115
|
-
|
|
113
|
+
/**
|
|
114
|
+
* Discard a form's draft — the subscription's own when `id` is given,
|
|
115
|
+
* the create draft otherwise. Scoped to the one form on purpose: another
|
|
116
|
+
* subscription's half-finished edit is not this form's to throw away.
|
|
117
|
+
* Clearing every draft at once is what `clearAll` is for.
|
|
118
|
+
*/
|
|
119
|
+
resetEditInvoicingSubscriptionDetailView(draft, action) {
|
|
116
120
|
draft.fetchState = 'Not-Started';
|
|
117
121
|
draft.error = undefined;
|
|
118
122
|
draft.savedSubscriptionId = undefined;
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
+
const id = action.payload?.id;
|
|
124
|
+
if (id == null) {
|
|
125
|
+
draft.newSubscriptionDraft = {
|
|
126
|
+
...initialInvoicingSubscriptionFormLocalData,
|
|
127
|
+
};
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
delete draft.editDraftById[id];
|
|
123
131
|
},
|
|
124
132
|
// Trigger only — handled by initializeInvoicingSubscriptionAddressEpic.
|
|
125
133
|
initializeInvoicingSubscriptionAddress: {
|
|
@@ -5,6 +5,7 @@ import { getInvoicingCouponListItems } from '../couponView/couponViewSelector';
|
|
|
5
5
|
import { getInvoicingCatalogListView, getInvoicingCatalogPrices, } from '../invoicingCatalogListView/invoicingCatalogListViewSelector';
|
|
6
6
|
import { getInvoicingConfigView, invoicingFieldHelpForGroup, } from '../invoicingConfigView/invoicingConfigViewSelector';
|
|
7
7
|
import { getInvoicingCustomerListView } from '../invoicingCustomerListView/invoicingCustomerListViewSelector';
|
|
8
|
+
import { getInvoicingFormDraftLocalData, isInvoicingFormDraftPending, } from '../invoicingFormDraft';
|
|
8
9
|
import { initialInvoicingSubscriptionFormLocalData } from './invoicingSubscriptionFormConfig';
|
|
9
10
|
import { INVOICING_SUBSCRIPTION_SHIPPING_ADDRESS_TYPE } from './invoicingSubscriptionFormConfig';
|
|
10
11
|
export const getEditInvoicingSubscriptionDetailViewState = (state, invoicingSubscriptionID) => {
|
|
@@ -33,8 +34,7 @@ export const getInvoicingSubscriptionFormView = (state, invoicingSubscriptionID)
|
|
|
33
34
|
// `seedInvoicingSubscriptionFormDraftEpic` when the detail load lands. Until
|
|
34
35
|
// then the page is showing its skeleton.
|
|
35
36
|
const localData = invoicingSubscriptionID != null
|
|
36
|
-
? (editDraftById
|
|
37
|
-
initialInvoicingSubscriptionFormLocalData)
|
|
37
|
+
? getInvoicingFormDraftLocalData(editDraftById, invoicingSubscriptionID, initialInvoicingSubscriptionFormLocalData)
|
|
38
38
|
: newSubscriptionDraft;
|
|
39
39
|
// Shipping address is captured via the shared Address screen and seeded there
|
|
40
40
|
// (looked up in the shared address store by `shippingAddressId`) on detail
|
|
@@ -65,6 +65,7 @@ export const getInvoicingSubscriptionFormView = (state, invoicingSubscriptionID)
|
|
|
65
65
|
subscriptionLifecycleActionOptions: configState.subscriptionLifecycleActionOptions,
|
|
66
66
|
subscriptionMetadataEditAllowedFromStatuses: configState.subscriptionMetadataEditAllowedFromStatuses,
|
|
67
67
|
recordState,
|
|
68
|
+
isFormLoading: isInvoicingFormDraftPending(editDraftById, invoicingSubscriptionID),
|
|
68
69
|
fieldHelp: invoicingFieldHelpForGroup(state, 'subscription'),
|
|
69
70
|
coupons: getInvoicingCouponListItems(state),
|
|
70
71
|
customers: getInvoicingCustomerListView(state).customers,
|
|
@@ -16,8 +16,11 @@ export const fetchInvoicingSubscriptionFormPageEpic = (actions$) => actions$.pip
|
|
|
16
16
|
const { subscriptionId } = action.payload;
|
|
17
17
|
const pageActions = [
|
|
18
18
|
fetchInvoicingCustomerList({ filters: {}, keepExistingListItems: false }),
|
|
19
|
+
// Plans only: the form's plan picker is the one consumer, and an
|
|
20
|
+
// unfiltered list would replace the store's plans with every catalog
|
|
21
|
+
// item.
|
|
19
22
|
fetchInvoicingCatalogPlanList({
|
|
20
|
-
filters: {},
|
|
23
|
+
filters: { type: 'plan' },
|
|
21
24
|
keepExistingListItems: false,
|
|
22
25
|
}),
|
|
23
26
|
fetchInvoicingCouponList({ filters: {}, keepExistingListItems: false }),
|
package/lib/esm/view/invoicing/editInvoicingSubscriptionDetailView/saveInvoicingSubscriptionEpic.js
CHANGED
|
@@ -17,7 +17,7 @@ export const saveInvoicingSubscriptionEpic = (actions$, state$, zeniAPI) => acti
|
|
|
17
17
|
const rootState = state$.value;
|
|
18
18
|
const viewState = rootState.editInvoicingSubscriptionDetailViewState;
|
|
19
19
|
const localData = (id != null
|
|
20
|
-
? viewState.editDraftById[id]
|
|
20
|
+
? viewState.editDraftById[id]?.localData
|
|
21
21
|
: viewState.newSubscriptionDraft) ?? viewState.newSubscriptionDraft;
|
|
22
22
|
// Shipping address captured via the shared Address screen is sent as an
|
|
23
23
|
// object in the payload (same pattern as the customer save flow).
|
|
@@ -1,18 +1,27 @@
|
|
|
1
1
|
import { EMPTY, of } from 'rxjs';
|
|
2
2
|
import { filter, mergeMap, withLatestFrom } from 'rxjs/operators';
|
|
3
|
-
import { seedInvoicingSubscriptionFormDraft, updateInvoicingSubscriptionDetail, } from './editInvoicingSubscriptionDetailViewReducer';
|
|
3
|
+
import { fetchInvoicingSubscriptionFormPage, seedInvoicingSubscriptionFormDraft, updateInvoicingSubscriptionDetail, } from './editInvoicingSubscriptionDetailViewReducer';
|
|
4
4
|
import { getInvoicingSubscriptionById } from './invoicingSubscriptionDetailHelper';
|
|
5
5
|
import { entityToInvoicingSubscriptionFormLocalData } from './invoicingSubscriptionFormConfig';
|
|
6
6
|
/**
|
|
7
|
-
* Seeds the edit draft from the record the
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
7
|
+
* Seeds the edit draft from the record, so the form has its values before it
|
|
8
|
+
* mounts. The reducer ignores a seed once the draft holds the record, which is
|
|
9
|
+
* what stops a background refetch from overwriting edits in progress.
|
|
10
|
+
*
|
|
11
|
+
* Opening the page counts as a trigger, not just the detail response: the page
|
|
12
|
+
* fetch is cache-guarded, so re-opening a form for a record already loaded this
|
|
13
|
+
* session issues no request at all — and a draft cleared in between (by a save
|
|
14
|
+
* or a cancel) would have nothing left to seed it.
|
|
11
15
|
*/
|
|
12
|
-
export const seedInvoicingSubscriptionFormDraftEpic = (actions$, state$) => actions$.pipe(filter(updateInvoicingSubscriptionDetail.match
|
|
13
|
-
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
+
export const seedInvoicingSubscriptionFormDraftEpic = (actions$, state$) => actions$.pipe(filter((action) => updateInvoicingSubscriptionDetail.match(action) ||
|
|
17
|
+
fetchInvoicingSubscriptionFormPage.match(action)), withLatestFrom(state$), mergeMap(([action, state]) => {
|
|
18
|
+
const id = updateInvoicingSubscriptionDetail.match(action)
|
|
19
|
+
? action.payload.id
|
|
20
|
+
: action.payload.subscriptionId;
|
|
21
|
+
// Nothing to seed from on a cold open; the detail response triggers this
|
|
22
|
+
// again once it lands.
|
|
23
|
+
const subscription = id != null ? getInvoicingSubscriptionById(state, id) : undefined;
|
|
24
|
+
if (id == null || subscription == null) {
|
|
16
25
|
return EMPTY;
|
|
17
26
|
}
|
|
18
27
|
return of(seedInvoicingSubscriptionFormDraft({
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The form's own write. Keeps the status: an edit on top of a seeded draft is
|
|
3
|
+
* still the record's draft, and an edit made before the record arrived must not
|
|
4
|
+
* pass for one.
|
|
5
|
+
*/
|
|
6
|
+
export function applyInvoicingFormDraftEdit(draftById, id, localData) {
|
|
7
|
+
draftById[id] = {
|
|
8
|
+
fetchState: draftById[id]?.fetchState ?? 'Not-Started',
|
|
9
|
+
localData,
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Fill the draft from the loaded record, once. A second seed is ignored so a
|
|
14
|
+
* late refetch cannot overwrite edits in progress — but a draft the form wrote
|
|
15
|
+
* before the record landed is replaced, since it was never the record's.
|
|
16
|
+
*/
|
|
17
|
+
export function applyInvoicingFormDraftSeed(draftById, id, localData) {
|
|
18
|
+
if (draftById[id]?.fetchState === 'Completed') {
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
draftById[id] = { fetchState: 'Completed', localData };
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Stand the draft up blank when the record's fetch fails, so the form renders
|
|
25
|
+
* instead of waiting behind a skeleton that will never resolve. A draft that
|
|
26
|
+
* already exists is left alone: a background refetch failing is no reason to
|
|
27
|
+
* discard what the user has.
|
|
28
|
+
*/
|
|
29
|
+
export function applyInvoicingFormDraftFailure(draftById, id, blankLocalData) {
|
|
30
|
+
if (draftById[id] != null) {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
draftById[id] = { fetchState: 'Error', localData: blankLocalData };
|
|
34
|
+
}
|
|
35
|
+
/** The draft's values, or the blank form when nothing is drafted yet. */
|
|
36
|
+
export function getInvoicingFormDraftLocalData(draftById, id, blankLocalData) {
|
|
37
|
+
if (id == null) {
|
|
38
|
+
return blankLocalData;
|
|
39
|
+
}
|
|
40
|
+
return draftById[id]?.localData ?? blankLocalData;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Whether the form should still be showing its skeleton — the record has
|
|
44
|
+
* neither reached the draft nor failed trying.
|
|
45
|
+
*
|
|
46
|
+
* Settles once and stays settled, which is what keeps a later refetch from
|
|
47
|
+
* swapping a form the user is typing into back for a skeleton. Create mode
|
|
48
|
+
* (no id) has no record to wait for.
|
|
49
|
+
*/
|
|
50
|
+
export function isInvoicingFormDraftPending(draftById, id) {
|
|
51
|
+
if (id == null) {
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
const fetchState = draftById[id]?.fetchState;
|
|
55
|
+
return fetchState !== 'Completed' && fetchState !== 'Error';
|
|
56
|
+
}
|
|
@@ -107,32 +107,27 @@ const expenseAutomationReconciliationView = (0, toolkit_1.createSlice)({
|
|
|
107
107
|
draft.selectedTab = 'review';
|
|
108
108
|
draft.selectedAccountId = action.payload.accountId;
|
|
109
109
|
}
|
|
110
|
-
const
|
|
111
|
-
|
|
112
|
-
|
|
110
|
+
const accountRecon = draft.accountReconciliationRecordsBySelectedPeriod[(0, timePeriod_1.toMonthYearPeriodId)(action.payload.selectedPeriod)]?.reconciliationByAccountID[action.payload.accountId];
|
|
111
|
+
if (accountRecon == null) {
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
if (accountRecon.transactionFetchState.fetchState !== 'Completed' ||
|
|
113
115
|
action.payload.cacheOverride === true) {
|
|
114
|
-
const data = accountReconciliationRecords.reconciliationByAccountID[action.payload.accountId];
|
|
115
116
|
if (action.payload.refreshViewInBackground === true) {
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
fetchState: 'In-Progress',
|
|
120
|
-
error: undefined,
|
|
121
|
-
},
|
|
122
|
-
accountId: action.payload.accountId,
|
|
123
|
-
reconciliationId: undefined,
|
|
117
|
+
accountRecon.refreshStatus = {
|
|
118
|
+
fetchState: 'In-Progress',
|
|
119
|
+
error: undefined,
|
|
124
120
|
};
|
|
121
|
+
accountRecon.accountId = action.payload.accountId;
|
|
122
|
+
accountRecon.reconciliationId = undefined;
|
|
125
123
|
}
|
|
126
124
|
else {
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
fetchState: 'In-Progress',
|
|
131
|
-
error: undefined,
|
|
132
|
-
},
|
|
133
|
-
accountId: action.payload.accountId,
|
|
134
|
-
reconciliationId: undefined,
|
|
125
|
+
accountRecon.transactionFetchState = {
|
|
126
|
+
fetchState: 'In-Progress',
|
|
127
|
+
error: undefined,
|
|
135
128
|
};
|
|
129
|
+
accountRecon.accountId = action.payload.accountId;
|
|
130
|
+
accountRecon.reconciliationId = undefined;
|
|
136
131
|
}
|
|
137
132
|
}
|
|
138
133
|
}
|
|
@@ -184,14 +179,17 @@ const expenseAutomationReconciliationView = (0, toolkit_1.createSlice)({
|
|
|
184
179
|
}
|
|
185
180
|
}
|
|
186
181
|
else {
|
|
187
|
-
const
|
|
182
|
+
const accountRecon = draft.accountReconciliationRecordsBySelectedPeriod[(0, timePeriod_1.toMonthYearPeriodId)(action.payload.selectedPeriod)]?.reconciliationByAccountID[action.payload.accountId];
|
|
183
|
+
if (accountRecon == null) {
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
188
186
|
if (action.payload.refreshViewInBackground === true) {
|
|
189
|
-
|
|
190
|
-
|
|
187
|
+
accountRecon.refreshStatus.error = action.payload.error;
|
|
188
|
+
accountRecon.refreshStatus.fetchState = 'Error';
|
|
191
189
|
}
|
|
192
190
|
else {
|
|
193
|
-
|
|
194
|
-
|
|
191
|
+
accountRecon.transactionFetchState.error = action.payload.error;
|
|
192
|
+
accountRecon.transactionFetchState.fetchState = 'Error';
|
|
195
193
|
}
|
|
196
194
|
}
|
|
197
195
|
},
|
|
@@ -396,8 +394,11 @@ const expenseAutomationReconciliationView = (0, toolkit_1.createSlice)({
|
|
|
396
394
|
},
|
|
397
395
|
setStatementParseInProgress: (draft, action) => {
|
|
398
396
|
const { accountId, selectedPeriod, statementParseInProgress } = action.payload;
|
|
399
|
-
draft.accountReconciliationRecordsBySelectedPeriod[(0, timePeriod_1.toMonthYearPeriodId)(selectedPeriod)]
|
|
400
|
-
|
|
397
|
+
const accountRecon = draft.accountReconciliationRecordsBySelectedPeriod[(0, timePeriod_1.toMonthYearPeriodId)(selectedPeriod)]?.reconciliationByAccountID[accountId];
|
|
398
|
+
if (accountRecon == null) {
|
|
399
|
+
return;
|
|
400
|
+
}
|
|
401
|
+
accountRecon.statementParseInProgress = statementParseInProgress;
|
|
401
402
|
},
|
|
402
403
|
setStatementProcessingDismissedToBackground: (draft, action) => {
|
|
403
404
|
const { accountId, selectedPeriod, dismissedToBackground } = action.payload;
|
|
@@ -481,15 +482,23 @@ const expenseAutomationReconciliationView = (0, toolkit_1.createSlice)({
|
|
|
481
482
|
},
|
|
482
483
|
uploadAccountStatementSuccess: (draft, action) => {
|
|
483
484
|
const { accountId, selectedPeriod } = action.payload;
|
|
484
|
-
draft.accountReconciliationRecordsBySelectedPeriod[(0, timePeriod_1.toMonthYearPeriodId)(selectedPeriod)]
|
|
485
|
+
const accountRecon = draft.accountReconciliationRecordsBySelectedPeriod[(0, timePeriod_1.toMonthYearPeriodId)(selectedPeriod)]?.reconciliationByAccountID[accountId];
|
|
486
|
+
if (accountRecon == null) {
|
|
487
|
+
return;
|
|
488
|
+
}
|
|
489
|
+
accountRecon.statementUploadStatus = {
|
|
485
490
|
fetchState: 'Completed',
|
|
486
491
|
error: undefined,
|
|
487
492
|
};
|
|
488
|
-
|
|
493
|
+
accountRecon.statementParseInProgress = true;
|
|
489
494
|
},
|
|
490
495
|
uploadAccountStatementFailure: (draft, action) => {
|
|
491
496
|
const { accountId, selectedPeriod } = action.payload;
|
|
492
|
-
draft.accountReconciliationRecordsBySelectedPeriod[(0, timePeriod_1.toMonthYearPeriodId)(selectedPeriod)]
|
|
497
|
+
const accountRecon = draft.accountReconciliationRecordsBySelectedPeriod[(0, timePeriod_1.toMonthYearPeriodId)(selectedPeriod)]?.reconciliationByAccountID[accountId];
|
|
498
|
+
if (accountRecon == null) {
|
|
499
|
+
return;
|
|
500
|
+
}
|
|
501
|
+
accountRecon.statementUploadStatus = {
|
|
493
502
|
fetchState: 'Error',
|
|
494
503
|
error: action.payload.error,
|
|
495
504
|
};
|
|
@@ -682,23 +691,34 @@ const expenseAutomationReconciliationView = (0, toolkit_1.createSlice)({
|
|
|
682
691
|
},
|
|
683
692
|
reparseStatementFailure: (draft, action) => {
|
|
684
693
|
const { accountId, selectedPeriod } = action.payload;
|
|
685
|
-
draft.accountReconciliationRecordsBySelectedPeriod[(0, timePeriod_1.toMonthYearPeriodId)(selectedPeriod)]
|
|
694
|
+
const accountRecon = draft.accountReconciliationRecordsBySelectedPeriod[(0, timePeriod_1.toMonthYearPeriodId)(selectedPeriod)]?.reconciliationByAccountID[accountId];
|
|
695
|
+
if (accountRecon == null) {
|
|
696
|
+
return;
|
|
697
|
+
}
|
|
698
|
+
accountRecon.reparseStatementStatus = {
|
|
686
699
|
fetchState: 'Error',
|
|
687
700
|
error: action.payload.error,
|
|
688
701
|
};
|
|
689
|
-
|
|
702
|
+
accountRecon.statementProcessingFailed = true;
|
|
690
703
|
},
|
|
691
704
|
resetReparseStatementStatus: (draft, action) => {
|
|
692
705
|
const { accountId, selectedPeriod } = action.payload;
|
|
693
|
-
draft.accountReconciliationRecordsBySelectedPeriod[(0, timePeriod_1.toMonthYearPeriodId)(selectedPeriod)]
|
|
706
|
+
const accountRecon = draft.accountReconciliationRecordsBySelectedPeriod[(0, timePeriod_1.toMonthYearPeriodId)(selectedPeriod)]?.reconciliationByAccountID[accountId];
|
|
707
|
+
if (accountRecon == null) {
|
|
708
|
+
return;
|
|
709
|
+
}
|
|
710
|
+
accountRecon.reparseStatementStatus = {
|
|
694
711
|
fetchState: 'Not-Started',
|
|
695
712
|
error: undefined,
|
|
696
713
|
};
|
|
697
714
|
},
|
|
698
715
|
updateParsedStatementData: (draft, action) => {
|
|
699
716
|
const { accountId, selectedPeriod, parsedStatementData } = action.payload;
|
|
700
|
-
draft.accountReconciliationRecordsBySelectedPeriod[(0, timePeriod_1.toMonthYearPeriodId)(selectedPeriod)]
|
|
701
|
-
|
|
717
|
+
const accountRecon = draft.accountReconciliationRecordsBySelectedPeriod[(0, timePeriod_1.toMonthYearPeriodId)(selectedPeriod)]?.reconciliationByAccountID[accountId];
|
|
718
|
+
if (accountRecon == null) {
|
|
719
|
+
return;
|
|
720
|
+
}
|
|
721
|
+
accountRecon.parsedStatementData = parsedStatementData;
|
|
702
722
|
},
|
|
703
723
|
updateStatementUpdateLocalData: (draft, action) => {
|
|
704
724
|
const { accountId, selectedPeriod, statementUpdateLocalData, statementUploadId, } = action.payload;
|
|
@@ -824,17 +844,21 @@ const expenseAutomationReconciliationView = (0, toolkit_1.createSlice)({
|
|
|
824
844
|
},
|
|
825
845
|
updateAccountReconciliationLocalData: (draft, action) => {
|
|
826
846
|
const { accountId, selectedPeriod, localData } = action.payload;
|
|
827
|
-
const
|
|
847
|
+
const accountRecon = draft.accountReconciliationRecordsBySelectedPeriod[(0, timePeriod_1.toMonthYearPeriodId)(selectedPeriod)]?.reconciliationByAccountID[accountId];
|
|
848
|
+
if (accountRecon == null) {
|
|
849
|
+
return;
|
|
850
|
+
}
|
|
851
|
+
const oldLocalData = accountRecon.localData;
|
|
828
852
|
if (oldLocalData == null) {
|
|
829
|
-
|
|
853
|
+
accountRecon.localData = localData;
|
|
830
854
|
}
|
|
831
855
|
else {
|
|
832
|
-
|
|
856
|
+
accountRecon.localData = {
|
|
833
857
|
...oldLocalData,
|
|
834
858
|
...localData,
|
|
835
859
|
};
|
|
836
860
|
}
|
|
837
|
-
|
|
861
|
+
accountRecon.localData = {
|
|
838
862
|
...oldLocalData,
|
|
839
863
|
...localData,
|
|
840
864
|
};
|
|
@@ -1124,7 +1148,10 @@ function updateAllReconciliation(draft, accounts, selectedPeriod, reconciliation
|
|
|
1124
1148
|
}
|
|
1125
1149
|
}
|
|
1126
1150
|
function updateReconciliationByAccountID(draft, accounts, selectedPeriod, accountId, reconciliationData, refreshViewInBackground) {
|
|
1127
|
-
const
|
|
1151
|
+
const accountRecon = draft.accountReconciliationRecordsBySelectedPeriod[(0, timePeriod_1.toMonthYearPeriodId)(selectedPeriod)]?.reconciliationByAccountID[accountId];
|
|
1152
|
+
if (accountRecon == null) {
|
|
1153
|
+
return;
|
|
1154
|
+
}
|
|
1128
1155
|
const oldConnectionStatus = draft.accountConnectionStatusByAccountID[accountId];
|
|
1129
1156
|
draft.accountConnectionStatusByAccountID[accountId] = {
|
|
1130
1157
|
status: (0, accountReconState_1.toBankStatusCodeType)(reconciliationData.bank_status.code),
|
|
@@ -1134,21 +1161,17 @@ function updateReconciliationByAccountID(draft, accounts, selectedPeriod, accoun
|
|
|
1134
1161
|
: false,
|
|
1135
1162
|
paymentAccountId: accounts.payment_account_id ?? undefined,
|
|
1136
1163
|
};
|
|
1137
|
-
const statementParseInProgress = resolveStatementParseInProgressAfterFetch(
|
|
1164
|
+
const statementParseInProgress = resolveStatementParseInProgressAfterFetch(accountRecon, reconciliationData);
|
|
1165
|
+
accountRecon.reconciliationId =
|
|
1166
|
+
reconciliationData.reconciliation_id ?? undefined;
|
|
1167
|
+
accountRecon.statementParseInProgress = statementParseInProgress;
|
|
1138
1168
|
if (refreshViewInBackground === true) {
|
|
1139
|
-
|
|
1140
|
-
...oldRecord,
|
|
1141
|
-
reconciliationId: reconciliationData.reconciliation_id ?? undefined,
|
|
1142
|
-
refreshStatus: { fetchState: 'Completed', error: undefined },
|
|
1143
|
-
statementParseInProgress,
|
|
1144
|
-
};
|
|
1169
|
+
accountRecon.refreshStatus = { fetchState: 'Completed', error: undefined };
|
|
1145
1170
|
}
|
|
1146
1171
|
else {
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
reconciliationId: reconciliationData.reconciliation_id ?? undefined,
|
|
1151
|
-
transactionFetchState: { fetchState: 'Completed', error: undefined },
|
|
1172
|
+
accountRecon.transactionFetchState = {
|
|
1173
|
+
fetchState: 'Completed',
|
|
1174
|
+
error: undefined,
|
|
1152
1175
|
};
|
|
1153
1176
|
}
|
|
1154
1177
|
}
|
|
@@ -1178,7 +1201,11 @@ const updateExcludedAccountExclusionInfo = (draft, account) => {
|
|
|
1178
1201
|
};
|
|
1179
1202
|
};
|
|
1180
1203
|
const updateReconciliationByAccountIDOnIncludeAccount = (draft, accountId, reconciliation, monthYearPeriodId) => {
|
|
1181
|
-
draft.accountReconciliationRecordsBySelectedPeriod[monthYearPeriodId]
|
|
1204
|
+
const accountReconciliationRecords = draft.accountReconciliationRecordsBySelectedPeriod[monthYearPeriodId];
|
|
1205
|
+
if (accountReconciliationRecords == null) {
|
|
1206
|
+
return;
|
|
1207
|
+
}
|
|
1208
|
+
accountReconciliationRecords.reconciliationByAccountID[accountId] = {
|
|
1182
1209
|
accountId,
|
|
1183
1210
|
reconciliationId: reconciliation.reconciliation_id ?? undefined,
|
|
1184
1211
|
transactionFetchState: { fetchState: 'Not-Started', error: undefined },
|
|
@@ -74,6 +74,8 @@ export declare const updateInvoicingCatalogItemFormDraft: import("@reduxjs/toolk
|
|
|
74
74
|
}, "editInvoicingCatalogItemDetailView/runInvoicingCatalogProductActionFailure">, submitInvoicingCatalogItemForm: import("@reduxjs/toolkit").ActionCreatorWithPreparedPayload<[payload: SubmitInvoicingCatalogItemFormPayload], SubmitInvoicingCatalogItemFormPayload, "editInvoicingCatalogItemDetailView/submitInvoicingCatalogItemForm", never, never>, submitInvoicingCatalogItemFormSuccess: import("@reduxjs/toolkit").ActionCreatorWithPayload<{
|
|
75
75
|
itemId: ID;
|
|
76
76
|
kind: InvoicingCatalogItemKind;
|
|
77
|
-
}, "editInvoicingCatalogItemDetailView/submitInvoicingCatalogItemFormSuccess">, submitInvoicingCatalogItemFormFailure: import("@reduxjs/toolkit").ActionCreatorWithPayload<ZeniAPIStatus<Record<string, unknown>>, "editInvoicingCatalogItemDetailView/submitInvoicingCatalogItemFormFailure">, resetEditInvoicingCatalogItemDetailView: import("@reduxjs/toolkit").
|
|
77
|
+
}, "editInvoicingCatalogItemDetailView/submitInvoicingCatalogItemFormSuccess">, submitInvoicingCatalogItemFormFailure: import("@reduxjs/toolkit").ActionCreatorWithPayload<ZeniAPIStatus<Record<string, unknown>>, "editInvoicingCatalogItemDetailView/submitInvoicingCatalogItemFormFailure">, resetEditInvoicingCatalogItemDetailView: import("@reduxjs/toolkit").ActionCreatorWithOptionalPayload<{
|
|
78
|
+
id?: ID;
|
|
79
|
+
} | undefined, "editInvoicingCatalogItemDetailView/resetEditInvoicingCatalogItemDetailView">;
|
|
78
80
|
declare const _default: import("redux").Reducer<EditInvoicingCatalogItemDetailViewState>;
|
|
79
81
|
export default _default;
|
|
@@ -4,6 +4,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
4
4
|
exports.resetEditInvoicingCatalogItemDetailView = exports.submitInvoicingCatalogItemFormFailure = exports.submitInvoicingCatalogItemFormSuccess = exports.submitInvoicingCatalogItemForm = exports.runInvoicingCatalogProductActionFailure = exports.runInvoicingCatalogProductActionSuccess = exports.runInvoicingCatalogProductAction = exports.runInvoicingCatalogPlanActionFailure = exports.runInvoicingCatalogPlanActionSuccess = exports.runInvoicingCatalogPlanAction = exports.seedInvoicingCatalogItemFormDraft = exports.updateInvoicingCatalogItemDetailFailure = exports.updateInvoicingCatalogItemDetail = exports.fetchInvoicingCatalogItemDetailPage = exports.fetchInvoicingCatalogItemDetail = exports.updateInvoicingCatalogItemFormDraft = exports.initialState = void 0;
|
|
5
5
|
const toolkit_1 = require("@reduxjs/toolkit");
|
|
6
6
|
const rootActions_1 = require("../../../rootActions");
|
|
7
|
+
const invoicingFormDraft_1 = require("../invoicingFormDraft");
|
|
7
8
|
const invoicingCatalogItemFormHelper_1 = require("./invoicingCatalogItemFormHelper");
|
|
8
9
|
exports.initialState = {
|
|
9
10
|
fetchState: 'Not-Started',
|
|
@@ -25,7 +26,7 @@ const editInvoicingCatalogItemDetailView = (0, toolkit_1.createSlice)({
|
|
|
25
26
|
updateInvoicingCatalogItemFormDraft(draft, action) {
|
|
26
27
|
const { id, localData } = action.payload;
|
|
27
28
|
if (id != null) {
|
|
28
|
-
draft.editDraftById
|
|
29
|
+
(0, invoicingFormDraft_1.applyInvoicingFormDraftEdit)(draft.editDraftById, id, localData);
|
|
29
30
|
}
|
|
30
31
|
else {
|
|
31
32
|
draft.newItemDraft = localData;
|
|
@@ -68,6 +69,7 @@ const editInvoicingCatalogItemDetailView = (0, toolkit_1.createSlice)({
|
|
|
68
69
|
fetchState: 'Error',
|
|
69
70
|
error: action.payload.error,
|
|
70
71
|
};
|
|
72
|
+
(0, invoicingFormDraft_1.applyInvoicingFormDraftFailure)(draft.editDraftById, action.payload.id, invoicingCatalogItemFormHelper_1.initialInvoicingCatalogItemFormLocalData);
|
|
71
73
|
},
|
|
72
74
|
fetchInvoicingCatalogItemDetailPage: {
|
|
73
75
|
reducer() {
|
|
@@ -83,14 +85,10 @@ const editInvoicingCatalogItemDetailView = (0, toolkit_1.createSlice)({
|
|
|
83
85
|
};
|
|
84
86
|
},
|
|
85
87
|
},
|
|
86
|
-
/**
|
|
87
|
-
* Seed the draft from the loaded record, once. Skipped when a draft already
|
|
88
|
-
* exists so a late-arriving refetch cannot overwrite in-progress edits.
|
|
89
|
-
*/
|
|
88
|
+
/** Fill the draft from the loaded record — see `applyInvoicingFormDraftSeed`. */
|
|
90
89
|
seedInvoicingCatalogItemFormDraft(draft, action) {
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
}
|
|
90
|
+
const { id, localData } = action.payload;
|
|
91
|
+
(0, invoicingFormDraft_1.applyInvoicingFormDraftSeed)(draft.editDraftById, id, localData);
|
|
94
92
|
},
|
|
95
93
|
runInvoicingCatalogPlanAction(draft, action) {
|
|
96
94
|
draft.actionStateById[action.payload.id] = {
|
|
@@ -129,13 +127,23 @@ const editInvoicingCatalogItemDetailView = (0, toolkit_1.createSlice)({
|
|
|
129
127
|
error: action.payload.error,
|
|
130
128
|
};
|
|
131
129
|
},
|
|
132
|
-
|
|
130
|
+
/**
|
|
131
|
+
* Discard a form's draft — the catalog item's own when `id` is given,
|
|
132
|
+
* the create draft otherwise. Scoped to the one form on purpose: another
|
|
133
|
+
* catalog item's half-finished edit is not this form's to throw away.
|
|
134
|
+
* Clearing every draft at once is what `clearAll` is for.
|
|
135
|
+
*/
|
|
136
|
+
resetEditInvoicingCatalogItemDetailView(draft, action) {
|
|
133
137
|
draft.fetchState = 'Not-Started';
|
|
134
138
|
draft.error = undefined;
|
|
135
139
|
draft.savedItemId = undefined;
|
|
136
140
|
draft.savedKind = undefined;
|
|
137
|
-
|
|
138
|
-
|
|
141
|
+
const id = action.payload?.id;
|
|
142
|
+
if (id == null) {
|
|
143
|
+
draft.newItemDraft = { ...invoicingCatalogItemFormHelper_1.initialInvoicingCatalogItemFormLocalData };
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
delete draft.editDraftById[id];
|
|
139
147
|
},
|
|
140
148
|
},
|
|
141
149
|
extraReducers: (builder) => {
|
|
@@ -44,6 +44,16 @@ export interface InvoicingCatalogItemFormView extends SelectorView {
|
|
|
44
44
|
* Folded in here so the screen never reaches for the config view itself.
|
|
45
45
|
*/
|
|
46
46
|
fieldHelp: InvoicingFieldHelpByCode;
|
|
47
|
+
/**
|
|
48
|
+
* True while anything the form's fields mount against is still loading: the
|
|
49
|
+
* accounting lists, and — until it has reached the draft — the item being
|
|
50
|
+
* edited. The form holds a skeleton on this rather than mount, because both
|
|
51
|
+
* arrive too late to be picked up: it seeds react-hook-form once, at first
|
|
52
|
+
* render, and the accounting fields choose between a picker and a text input
|
|
53
|
+
* from the lists. A later refetch does not raise this again, so a refresh
|
|
54
|
+
* leaves the form on screen instead of blanking it mid-edit.
|
|
55
|
+
*/
|
|
56
|
+
isFormLoading: boolean;
|
|
47
57
|
/**
|
|
48
58
|
* True while the accounting lists are still loading, so the form can wait
|
|
49
59
|
* instead of mounting pickers whose options are not there yet.
|
|
@@ -5,6 +5,7 @@ const toolkit_1 = require("@reduxjs/toolkit");
|
|
|
5
5
|
const reduceFetchState_1 = require("../../../commonStateTypes/reduceFetchState");
|
|
6
6
|
const planSelector_1 = require("../../../entity/invoicing/plan/planSelector");
|
|
7
7
|
const invoicingConfigViewSelector_1 = require("../invoicingConfigView/invoicingConfigViewSelector");
|
|
8
|
+
const invoicingFormDraft_1 = require("../invoicingFormDraft");
|
|
8
9
|
const invoicingQboViewSelector_1 = require("../invoicingQboView/invoicingQboViewSelector");
|
|
9
10
|
const invoicingCatalogItemFormHelper_1 = require("./invoicingCatalogItemFormHelper");
|
|
10
11
|
const getEditInvoicingCatalogItemDetailViewState = (state) => ({
|
|
@@ -58,7 +59,7 @@ const getInvoicingCatalogItemFormView = (state, id) => {
|
|
|
58
59
|
// lands. Until then the page is showing its skeleton, so the blank shape here
|
|
59
60
|
// is never rendered.
|
|
60
61
|
const localData = id != null
|
|
61
|
-
? (editDraftById
|
|
62
|
+
? (0, invoicingFormDraft_1.getInvoicingFormDraftLocalData)(editDraftById, id, invoicingCatalogItemFormHelper_1.initialInvoicingCatalogItemFormLocalData)
|
|
62
63
|
: newItemDraft;
|
|
63
64
|
const allApplicableItemOptions = (0, exports.getInvoicingApplicableCatalogItemOptions)(state);
|
|
64
65
|
const applicableItemOptions = id != null
|
|
@@ -96,6 +97,8 @@ const getInvoicingCatalogItemFormView = (state, id) => {
|
|
|
96
97
|
catalogProductLifecycleActionOptions,
|
|
97
98
|
recordState,
|
|
98
99
|
fieldHelp: (0, invoicingConfigViewSelector_1.invoicingFieldHelpForGroup)(state, 'catalog'),
|
|
100
|
+
isFormLoading: (0, invoicingQboViewSelector_1.isInvoicingAccountingPickerDataLoading)(state) ||
|
|
101
|
+
(0, invoicingFormDraft_1.isInvoicingFormDraftPending)(editDraftById, id),
|
|
99
102
|
isQboPickerDataLoading: (0, invoicingQboViewSelector_1.isInvoicingAccountingPickerDataLoading)(state),
|
|
100
103
|
qboAccountHierarchy: qboView?.nestedAccountHierarchy ?? [],
|
|
101
104
|
qboClassHierarchy: qboView?.nestedClassHierarchy ?? [],
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Amount } from '../../../commonStateTypes/amount';
|
|
2
2
|
import { FetchStateAndError, FetchedState, ID } from '../../../commonStateTypes/common';
|
|
3
|
+
import { InvoicingFormDraftById } from '../invoicingFormDraft';
|
|
3
4
|
/** A single pricing tier within a price point (values kept as raw strings). */
|
|
4
5
|
export interface InvoicingCatalogItemFormTier {
|
|
5
6
|
endingUnit: string;
|
|
@@ -63,7 +64,7 @@ export interface EditInvoicingCatalogItemDetailViewState extends FetchedState {
|
|
|
63
64
|
*/
|
|
64
65
|
actionStateById: Record<ID, FetchStateAndError>;
|
|
65
66
|
/** Inline-edit drafts, keyed by catalog item id. */
|
|
66
|
-
editDraftById:
|
|
67
|
+
editDraftById: InvoicingFormDraftById<InvoicingCatalogItemFormLocalData>;
|
|
67
68
|
newItemDraft: InvoicingCatalogItemFormLocalData;
|
|
68
69
|
/**
|
|
69
70
|
* Detail-load state per catalog item id. Keyed rather than a single slot, so
|
package/lib/view/invoicing/editInvoicingCatalogItemDetailView/saveInvoicingCatalogItemEpic.js
CHANGED
|
@@ -20,8 +20,9 @@ const saveInvoicingCatalogItemEpic = (actions$, state$, zeniAPI) => actions$.pip
|
|
|
20
20
|
// class become the records the service stores. Price points ride along
|
|
21
21
|
// nested, so the detail page and the create/edit form share one request.
|
|
22
22
|
const viewState = state.editInvoicingCatalogItemDetailViewState;
|
|
23
|
-
const localData = (id != null
|
|
24
|
-
viewState.
|
|
23
|
+
const localData = (id != null
|
|
24
|
+
? viewState.editDraftById[id]?.localData
|
|
25
|
+
: viewState.newItemDraft) ?? viewState.newItemDraft;
|
|
25
26
|
const data = (0, editInvoicingCatalogItemDetailViewPayload_1.localDataToSaveInvoicingCatalogItemPayload)(localData, id == null, accountingLookup);
|
|
26
27
|
const baseUrl = `${zeniAPI.apiEndPoints.invoicingMicroServiceBaseUrl}/1.0`;
|
|
27
28
|
const path = id != null
|
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
import { ActionsObservable, StateObservable } from 'redux-observable';
|
|
2
2
|
import { Observable } from 'rxjs';
|
|
3
3
|
import { RootState } from '../../../rootStateTypes';
|
|
4
|
-
import { seedInvoicingCatalogItemFormDraft, updateInvoicingCatalogItemDetail } from './editInvoicingCatalogItemDetailViewReducer';
|
|
5
|
-
export type ActionType = ReturnType<typeof updateInvoicingCatalogItemDetail> | ReturnType<typeof seedInvoicingCatalogItemFormDraft>;
|
|
4
|
+
import { fetchInvoicingCatalogItemDetailPage, seedInvoicingCatalogItemFormDraft, updateInvoicingCatalogItemDetail } from './editInvoicingCatalogItemDetailViewReducer';
|
|
5
|
+
export type ActionType = ReturnType<typeof fetchInvoicingCatalogItemDetailPage> | ReturnType<typeof updateInvoicingCatalogItemDetail> | ReturnType<typeof seedInvoicingCatalogItemFormDraft>;
|
|
6
6
|
/**
|
|
7
|
-
* Seeds the edit draft from the record the
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
7
|
+
* Seeds the edit draft from the record, so the form has its values before it
|
|
8
|
+
* mounts. The reducer ignores a seed once the draft holds the record, which is
|
|
9
|
+
* what stops a background refetch from overwriting edits in progress.
|
|
10
|
+
*
|
|
11
|
+
* Opening the page counts as a trigger, not just the detail response: the page
|
|
12
|
+
* fetch is cache-guarded, so re-opening a form for a record already loaded this
|
|
13
|
+
* session issues no request at all — and a draft cleared in between (by a save
|
|
14
|
+
* or a cancel) would have nothing left to seed it.
|
|
11
15
|
*/
|
|
12
16
|
export declare const seedInvoicingCatalogItemFormDraftEpic: (actions$: ActionsObservable<ActionType>, state$: StateObservable<RootState>) => Observable<ActionType>;
|