@zeniai/client-epic-state 5.2.41-beta1MM → 5.2.42-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/invoicing/editInvoicingCatalogItemDetailView/editInvoicingCatalogItemDetailViewPayload.js +39 -30
- package/lib/view/invoicing/editInvoicingCatalogItemDetailView/editInvoicingCatalogItemDetailViewPayload.d.ts +30 -5
- package/lib/view/invoicing/editInvoicingCatalogItemDetailView/editInvoicingCatalogItemDetailViewPayload.js +39 -30
- package/package.json +1 -1
|
@@ -3,7 +3,6 @@ import { toClassBasePayload } from '../../../entity/class/classPayload';
|
|
|
3
3
|
import { blankInvoicingCatalogSubFamily, isTieredCatalogPricingModel, } from './invoicingCatalogItemFormHelper';
|
|
4
4
|
const CURRENCY_CODE = 'USD';
|
|
5
5
|
const trimOrUndefined = (value) => value.trim() !== '' ? value.trim() : undefined;
|
|
6
|
-
const trimOrNull = (value) => value.trim() !== '' ? value.trim() : null;
|
|
7
6
|
const toProductCode = (value) => value
|
|
8
7
|
.trim()
|
|
9
8
|
.toLowerCase()
|
|
@@ -91,13 +90,24 @@ const buildSubFamilyPayload = (localData, subFamily, clearEmptyValues, lookup) =
|
|
|
91
90
|
/** `unit` is the item's unit of measure, not a currency; `CURRENCY_CODE` above prices Price points. */
|
|
92
91
|
const DEFAULT_CATALOG_ITEM_UNIT = 'USD';
|
|
93
92
|
/**
|
|
94
|
-
* Build the parent catalog-item request body from the form draft.
|
|
95
|
-
*
|
|
93
|
+
* Build the parent catalog-item request body from the form draft. Every field
|
|
94
|
+
* the form holds is sent, so a value cleared on the form is cleared on the
|
|
95
|
+
* item: the service reads an absent key as "leave it alone", which is why
|
|
96
|
+
* `description` travels as an empty string and `applicable_items` as an empty
|
|
97
|
+
* list rather than being dropped. Fields the form does not collect
|
|
98
|
+
* (`external_name`, `item_family_id`, `metadata`, …) stay off the body so an
|
|
99
|
+
* edit cannot overwrite them with a value nobody chose.
|
|
96
100
|
*/
|
|
97
101
|
export const localDataToSaveInvoicingCatalogItemPayload = (localData, isCreate, lookup) => {
|
|
98
|
-
const description = trimOrUndefined(localData.description);
|
|
99
102
|
const productCode = trimOrUndefined(localData.productCode) ?? toProductCode(localData.name);
|
|
100
103
|
const itemPayload = {
|
|
104
|
+
// `applicable_items` only means anything while the item is restricted;
|
|
105
|
+
// sending the empty list otherwise is what clears a restriction that was
|
|
106
|
+
// just lifted.
|
|
107
|
+
applicable_items: localData.itemApplicability === 'restricted'
|
|
108
|
+
? localData.applicableItems
|
|
109
|
+
: [],
|
|
110
|
+
description: trimOrUndefined(localData.description) ?? '',
|
|
101
111
|
enabled_for_checkout: true,
|
|
102
112
|
enabled_in_portal: true,
|
|
103
113
|
is_giftable: false,
|
|
@@ -107,41 +117,40 @@ export const localDataToSaveInvoicingCatalogItemPayload = (localData, isCreate,
|
|
|
107
117
|
name: localData.name.trim(),
|
|
108
118
|
product_code: productCode,
|
|
109
119
|
type: localData.catalogType,
|
|
120
|
+
// The form no longer collects a unit; an item that already has one keeps it.
|
|
121
|
+
unit: trimOrUndefined(localData.unit) ?? DEFAULT_CATALOG_ITEM_UNIT,
|
|
110
122
|
};
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
if (localData.itemApplicability === 'restricted' &&
|
|
118
|
-
localData.applicableItems.length > 0) {
|
|
119
|
-
itemPayload.applicable_items = localData.applicableItems;
|
|
123
|
+
// The external id is assigned by the billing service and shown read-only, so
|
|
124
|
+
// it is echoed back only when the item already has one: sending the blank a
|
|
125
|
+
// create starts with would clear an id assigned in the meantime.
|
|
126
|
+
const externalId = trimOrUndefined(localData.externalId);
|
|
127
|
+
if (externalId != null) {
|
|
128
|
+
itemPayload.external_id = externalId;
|
|
120
129
|
}
|
|
121
130
|
const subFamilies = localData.subFamilies.length > 0
|
|
122
131
|
? localData.subFamilies
|
|
123
132
|
: [blankInvoicingCatalogSubFamily()];
|
|
124
|
-
itemPayload.
|
|
133
|
+
itemPayload.sub_families = isCreate
|
|
125
134
|
? subFamilies.map((subFamily) => buildSubFamilyPayload(localData, subFamily, false, lookup))
|
|
126
135
|
: subFamilies.map((subFamily) => {
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
//
|
|
136
|
-
//
|
|
137
|
-
//
|
|
138
|
-
// empty: an absent key on an update reads as "leave it alone", which
|
|
139
|
-
// would make a cleared mapping impossible to save — the same reason
|
|
140
|
-
// `description` and `invoice_notes` go through `trimOrNull`.
|
|
136
|
+
// Edits send the same body a create does. The narrower hand-built one
|
|
137
|
+
// this replaces had to be kept in step by hand and was not: it dropped
|
|
138
|
+
// `accounting_detail` once, and `billing_cycles`, `price`,
|
|
139
|
+
// `pricing_model`, `period`, the trial fields, `proration_type`,
|
|
140
|
+
// `free_quantity`, `is_taxable` and `tiers` were never added at all, so
|
|
141
|
+
// editing any of them looked saved and changed nothing.
|
|
142
|
+
const detail = buildSubFamilyPayload(localData, subFamily, true, lookup);
|
|
143
|
+
// `null` rather than omitted when empty: an absent key on an update
|
|
144
|
+
// reads as "leave it alone", which would make a cleared mapping
|
|
145
|
+
// impossible to save — the same reason `description` and
|
|
146
|
+
// `invoice_notes` are cleared rather than dropped above.
|
|
141
147
|
detail.accounting_detail =
|
|
142
148
|
buildAccountingDetail(subFamily, lookup) ?? null;
|
|
143
|
-
|
|
144
|
-
|
|
149
|
+
// A price point added on this form has no id yet, and travels with a
|
|
150
|
+
// blank one. The update endpoint reads that as "create this one", so it
|
|
151
|
+
// lands in the same request as the edits around it rather than a second
|
|
152
|
+
// call that could fail on its own.
|
|
153
|
+
return { ...detail, id: subFamily.id.trim() };
|
|
145
154
|
});
|
|
146
155
|
return itemPayload;
|
|
147
156
|
};
|
|
@@ -1,10 +1,30 @@
|
|
|
1
1
|
import { AccountBase } from '../../../entity/account/accountState';
|
|
2
2
|
import { ClassBase } from '../../../entity/class/classState';
|
|
3
3
|
import { InvoicingPlanPayload } from '../../../entity/invoicing/plan/planPayload';
|
|
4
|
-
import { InvoicingProductPayload } from '../../../entity/invoicing/product/productPayload';
|
|
4
|
+
import { InvoicingAccountingDetailPayload, InvoicingProductPayload } from '../../../entity/invoicing/product/productPayload';
|
|
5
5
|
import { ZeniAPIResponse } from '../../../responsePayload';
|
|
6
6
|
import { InvoicingCatalogItemFormLocalData } from './editInvoicingCatalogItemDetailViewState';
|
|
7
7
|
export type SaveInvoicingCatalogItemResponse = ZeniAPIResponse<InvoicingPlanPayload>;
|
|
8
|
+
/**
|
|
9
|
+
* The price-point half of a save body. `InvoicingProductPayload` is the shape
|
|
10
|
+
* the service returns; a save differs in two ways: a field is cleared by
|
|
11
|
+
* sending an explicit `null` (an absent key reads as "leave it alone"), and a
|
|
12
|
+
* price point the form has only just added carries a blank `id`, which the
|
|
13
|
+
* update endpoint reads as "create this one".
|
|
14
|
+
*/
|
|
15
|
+
export type InvoicingCatalogSubFamilySavePayload = Omit<Partial<InvoicingProductPayload>, 'accounting_detail' | 'description' | 'invoice_notes'> & {
|
|
16
|
+
accounting_detail?: InvoicingAccountingDetailPayload | null;
|
|
17
|
+
description?: string | null;
|
|
18
|
+
invoice_notes?: string | null;
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* The catalog-item save body: the item the service returns, minus the fields it
|
|
22
|
+
* assigns itself. Price points keep the `sub_families` name they are read back
|
|
23
|
+
* under, and differ only in allowing the cleared and blank-id forms above.
|
|
24
|
+
*/
|
|
25
|
+
export type InvoicingCatalogItemSavePayload = Omit<Partial<InvoicingPlanPayload>, 'sub_families'> & {
|
|
26
|
+
sub_families?: InvoicingCatalogSubFamilySavePayload[];
|
|
27
|
+
};
|
|
8
28
|
/**
|
|
9
29
|
* Resolves the QuickBooks ids the pickers submit back to the entities they name.
|
|
10
30
|
* The payload carries the whole account and class, so the ids alone are not
|
|
@@ -20,12 +40,17 @@ export interface InvoicingCatalogAccountingFields {
|
|
|
20
40
|
accountingCode: string;
|
|
21
41
|
sku: string;
|
|
22
42
|
}
|
|
23
|
-
export declare const buildAccountingDetail: (subFamily: InvoicingCatalogAccountingFields, lookup: InvoicingCatalogAccountingLookup) =>
|
|
43
|
+
export declare const buildAccountingDetail: (subFamily: InvoicingCatalogAccountingFields, lookup: InvoicingCatalogAccountingLookup) => InvoicingAccountingDetailPayload | undefined;
|
|
24
44
|
/**
|
|
25
|
-
* Build the parent catalog-item request body from the form draft.
|
|
26
|
-
*
|
|
45
|
+
* Build the parent catalog-item request body from the form draft. Every field
|
|
46
|
+
* the form holds is sent, so a value cleared on the form is cleared on the
|
|
47
|
+
* item: the service reads an absent key as "leave it alone", which is why
|
|
48
|
+
* `description` travels as an empty string and `applicable_items` as an empty
|
|
49
|
+
* list rather than being dropped. Fields the form does not collect
|
|
50
|
+
* (`external_name`, `item_family_id`, `metadata`, …) stay off the body so an
|
|
51
|
+
* edit cannot overwrite them with a value nobody chose.
|
|
27
52
|
*/
|
|
28
|
-
export declare const localDataToSaveInvoicingCatalogItemPayload: (localData: InvoicingCatalogItemFormLocalData, isCreate: boolean, lookup: InvoicingCatalogAccountingLookup) =>
|
|
53
|
+
export declare const localDataToSaveInvoicingCatalogItemPayload: (localData: InvoicingCatalogItemFormLocalData, isCreate: boolean, lookup: InvoicingCatalogAccountingLookup) => InvoicingCatalogItemSavePayload;
|
|
29
54
|
export type RunInvoicingCatalogPlanActionResponse = ZeniAPIResponse<InvoicingPlanPayload>;
|
|
30
55
|
export type RunInvoicingCatalogProductActionResponse = ZeniAPIResponse<InvoicingProductPayload>;
|
|
31
56
|
export type InvoicingCatalogItemDetailResponse = ZeniAPIResponse<InvoicingPlanPayload>;
|
|
@@ -6,7 +6,6 @@ const classPayload_1 = require("../../../entity/class/classPayload");
|
|
|
6
6
|
const invoicingCatalogItemFormHelper_1 = require("./invoicingCatalogItemFormHelper");
|
|
7
7
|
const CURRENCY_CODE = 'USD';
|
|
8
8
|
const trimOrUndefined = (value) => value.trim() !== '' ? value.trim() : undefined;
|
|
9
|
-
const trimOrNull = (value) => value.trim() !== '' ? value.trim() : null;
|
|
10
9
|
const toProductCode = (value) => value
|
|
11
10
|
.trim()
|
|
12
11
|
.toLowerCase()
|
|
@@ -95,13 +94,24 @@ const buildSubFamilyPayload = (localData, subFamily, clearEmptyValues, lookup) =
|
|
|
95
94
|
/** `unit` is the item's unit of measure, not a currency; `CURRENCY_CODE` above prices Price points. */
|
|
96
95
|
const DEFAULT_CATALOG_ITEM_UNIT = 'USD';
|
|
97
96
|
/**
|
|
98
|
-
* Build the parent catalog-item request body from the form draft.
|
|
99
|
-
*
|
|
97
|
+
* Build the parent catalog-item request body from the form draft. Every field
|
|
98
|
+
* the form holds is sent, so a value cleared on the form is cleared on the
|
|
99
|
+
* item: the service reads an absent key as "leave it alone", which is why
|
|
100
|
+
* `description` travels as an empty string and `applicable_items` as an empty
|
|
101
|
+
* list rather than being dropped. Fields the form does not collect
|
|
102
|
+
* (`external_name`, `item_family_id`, `metadata`, …) stay off the body so an
|
|
103
|
+
* edit cannot overwrite them with a value nobody chose.
|
|
100
104
|
*/
|
|
101
105
|
const localDataToSaveInvoicingCatalogItemPayload = (localData, isCreate, lookup) => {
|
|
102
|
-
const description = trimOrUndefined(localData.description);
|
|
103
106
|
const productCode = trimOrUndefined(localData.productCode) ?? toProductCode(localData.name);
|
|
104
107
|
const itemPayload = {
|
|
108
|
+
// `applicable_items` only means anything while the item is restricted;
|
|
109
|
+
// sending the empty list otherwise is what clears a restriction that was
|
|
110
|
+
// just lifted.
|
|
111
|
+
applicable_items: localData.itemApplicability === 'restricted'
|
|
112
|
+
? localData.applicableItems
|
|
113
|
+
: [],
|
|
114
|
+
description: trimOrUndefined(localData.description) ?? '',
|
|
105
115
|
enabled_for_checkout: true,
|
|
106
116
|
enabled_in_portal: true,
|
|
107
117
|
is_giftable: false,
|
|
@@ -111,41 +121,40 @@ const localDataToSaveInvoicingCatalogItemPayload = (localData, isCreate, lookup)
|
|
|
111
121
|
name: localData.name.trim(),
|
|
112
122
|
product_code: productCode,
|
|
113
123
|
type: localData.catalogType,
|
|
124
|
+
// The form no longer collects a unit; an item that already has one keeps it.
|
|
125
|
+
unit: trimOrUndefined(localData.unit) ?? DEFAULT_CATALOG_ITEM_UNIT,
|
|
114
126
|
};
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
if (localData.itemApplicability === 'restricted' &&
|
|
122
|
-
localData.applicableItems.length > 0) {
|
|
123
|
-
itemPayload.applicable_items = localData.applicableItems;
|
|
127
|
+
// The external id is assigned by the billing service and shown read-only, so
|
|
128
|
+
// it is echoed back only when the item already has one: sending the blank a
|
|
129
|
+
// create starts with would clear an id assigned in the meantime.
|
|
130
|
+
const externalId = trimOrUndefined(localData.externalId);
|
|
131
|
+
if (externalId != null) {
|
|
132
|
+
itemPayload.external_id = externalId;
|
|
124
133
|
}
|
|
125
134
|
const subFamilies = localData.subFamilies.length > 0
|
|
126
135
|
? localData.subFamilies
|
|
127
136
|
: [(0, invoicingCatalogItemFormHelper_1.blankInvoicingCatalogSubFamily)()];
|
|
128
|
-
itemPayload.
|
|
137
|
+
itemPayload.sub_families = isCreate
|
|
129
138
|
? subFamilies.map((subFamily) => buildSubFamilyPayload(localData, subFamily, false, lookup))
|
|
130
139
|
: subFamilies.map((subFamily) => {
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
//
|
|
140
|
-
//
|
|
141
|
-
//
|
|
142
|
-
// empty: an absent key on an update reads as "leave it alone", which
|
|
143
|
-
// would make a cleared mapping impossible to save — the same reason
|
|
144
|
-
// `description` and `invoice_notes` go through `trimOrNull`.
|
|
140
|
+
// Edits send the same body a create does. The narrower hand-built one
|
|
141
|
+
// this replaces had to be kept in step by hand and was not: it dropped
|
|
142
|
+
// `accounting_detail` once, and `billing_cycles`, `price`,
|
|
143
|
+
// `pricing_model`, `period`, the trial fields, `proration_type`,
|
|
144
|
+
// `free_quantity`, `is_taxable` and `tiers` were never added at all, so
|
|
145
|
+
// editing any of them looked saved and changed nothing.
|
|
146
|
+
const detail = buildSubFamilyPayload(localData, subFamily, true, lookup);
|
|
147
|
+
// `null` rather than omitted when empty: an absent key on an update
|
|
148
|
+
// reads as "leave it alone", which would make a cleared mapping
|
|
149
|
+
// impossible to save — the same reason `description` and
|
|
150
|
+
// `invoice_notes` are cleared rather than dropped above.
|
|
145
151
|
detail.accounting_detail =
|
|
146
152
|
(0, exports.buildAccountingDetail)(subFamily, lookup) ?? null;
|
|
147
|
-
|
|
148
|
-
|
|
153
|
+
// A price point added on this form has no id yet, and travels with a
|
|
154
|
+
// blank one. The update endpoint reads that as "create this one", so it
|
|
155
|
+
// lands in the same request as the edits around it rather than a second
|
|
156
|
+
// call that could fail on its own.
|
|
157
|
+
return { ...detail, id: subFamily.id.trim() };
|
|
149
158
|
});
|
|
150
159
|
return itemPayload;
|
|
151
160
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zeniai/client-epic-state",
|
|
3
|
-
"version": "5.2.
|
|
3
|
+
"version": "5.2.42-betaAK0",
|
|
4
4
|
"description": "Shared module between Web & Mobile containing required abstractions for state management, async network communication. ",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"module": "lib/esm/index.js",
|