@peabody-soft/mbs-model-ts 1.7.0 → 1.8.0
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/dist/Attachment.js +2 -2
- package/dist/AttachmentBaseUrl.js +4 -4
- package/dist/AuthTokens.js +7 -7
- package/dist/Business.js +11 -10
- package/dist/BusinessIdentifier.js +3 -2
- package/dist/BusinessLink.js +3 -2
- package/dist/CartEditor.js +89 -81
- package/dist/Catalog.d.ts +19 -7
- package/dist/Catalog.js +121 -41
- package/dist/Constants.d.ts +1 -0
- package/dist/Constants.js +7 -5
- package/dist/ContainerNames.js +6 -6
- package/dist/Entity.js +4 -3
- package/dist/Enums.d.ts +1 -0
- package/dist/Enums.js +3 -2
- package/dist/Exceptions.js +2 -2
- package/dist/GeneralSettings.d.ts +5 -4
- package/dist/GeneralSettings.js +17 -15
- package/dist/HttpStatusCodes.js +7 -7
- package/dist/Ids.js +4 -5
- package/dist/Item.js +8 -5
- package/dist/LiveData.js +2 -2
- package/dist/Measures.js +3 -2
- package/dist/Message.js +1 -1
- package/dist/MockDatabase.js +6 -5
- package/dist/Model.js +166 -162
- package/dist/Order.js +10 -12
- package/dist/Package.d.ts +6 -2
- package/dist/Package.js +10 -5
- package/dist/PackageIdentifier.js +2 -2
- package/dist/Price.js +6 -5
- package/dist/PriceList.d.ts +13 -0
- package/dist/PriceList.js +34 -2
- package/dist/Product.d.ts +6 -2
- package/dist/Product.js +15 -10
- package/dist/Profile.js +15 -14
- package/dist/ProtoMap.js +189 -206
- package/dist/ServiceExceptions.js +6 -6
- package/dist/ServiceProxy.js +15 -14
- package/dist/ShoppingList.js +102 -100
- package/dist/SyncCache.js +76 -68
- package/dist/SyncKeys.js +5 -5
- package/dist/SyncState.js +14 -6
- package/dist/Tax.js +11 -10
- package/dist/TaxHelper.js +9 -8
- package/dist/TaxType.js +7 -6
- package/dist/UserProfile.js +7 -7
- package/dist/WorkingDays.js +21 -18
- package/dist/generated/BusinessSyncOutResponse.d.ts +4 -1
- package/dist/generated/Constants.d.ts +0 -4
- package/dist/generated/Constants.js +0 -4
- package/dist/generated/GeneralSettings.d.ts +5 -4
- package/dist/generated/GeneralSettings.js +8 -6
- package/dist/generated/Item.d.ts +4 -3
- package/dist/generated/Message.d.ts +5 -0
- package/dist/generated/OnlineStoreTheme.d.ts +33 -0
- package/dist/generated/OnlineStoreTheme.js +38 -0
- package/dist/generated/PriceList.d.ts +3 -2
- package/dist/generated/ProfileSyncOutRequest.d.ts +16 -15
- package/dist/generated/ProfileSyncOutResponse.d.ts +4 -6
- package/dist/generated/SalesOrderPayload.d.ts +6 -3
- package/dist/index.d.ts +4 -0
- package/dist/index.js +11 -8
- package/package.json +2 -2
package/dist/Catalog.js
CHANGED
|
@@ -1,51 +1,70 @@
|
|
|
1
1
|
import { Constants as proto_Constants } from "./generated/Constants";
|
|
2
|
-
import {
|
|
2
|
+
import { DEFAULT_SALE_PRICE_LIST_ID } from "./Constants";
|
|
3
|
+
import { EntityHeader, UpdatableEntity } from "./Entity";
|
|
3
4
|
import ProductCategory from "./ProductCategory";
|
|
4
5
|
// The catalog of a business: its categories and the products in them. Not an entity and not on the
|
|
5
|
-
// wire. The sync layer
|
|
6
|
+
// wire. The sync layer builds it from the flat item entities of a sync response, as the merchant
|
|
7
|
+
// app's Catalog is built.
|
|
8
|
+
//
|
|
9
|
+
// The constructor links what it is given: a package to its product, a price to its price list and a
|
|
10
|
+
// price list to its parent. It does not depend on any price list being present. Packages are priced
|
|
11
|
+
// from currentSalePriceListId, so setting that id decides which packages and products are buyable.
|
|
6
12
|
class Catalog {
|
|
7
|
-
constructor(categories, products,
|
|
8
|
-
|
|
13
|
+
constructor(categories, products, packages, priceLists, prices) {
|
|
14
|
+
// Every package that has a product, by that product's id, whether it can be bought or not
|
|
15
|
+
this._candidatePackages = new Map();
|
|
16
|
+
this._priceListMap = new Map();
|
|
17
|
+
this._currentSalePriceListId = DEFAULT_SALE_PRICE_LIST_ID;
|
|
18
|
+
// What a customer can reach, rebuilt by applySalePriceList
|
|
19
|
+
this._categories = Object.freeze([]);
|
|
20
|
+
this._products = Object.freeze([]);
|
|
9
21
|
this._categoryMap = new Map();
|
|
10
22
|
this._productMap = new Map();
|
|
11
23
|
this._packageMap = new Map();
|
|
24
|
+
this._allCategories = categories;
|
|
25
|
+
this._allProducts = products;
|
|
12
26
|
// A dummy category for products whose parentItemId is Constants.ENTITY_ID_NULL. Its id is also
|
|
13
27
|
// ENTITY_ID_NULL, so getCategory(product.productCategoryId) resolves it like a real category.
|
|
14
|
-
// It is left out
|
|
15
|
-
|
|
16
|
-
//
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
if (!product.isBuyable)
|
|
28
|
+
// It is left out like any other category when it has no buyable products.
|
|
29
|
+
this._uncategorized = new ProductCategory(new EntityHeader(BigInt(proto_Constants.ENTITY_ID_NULL), 0n, 0n, 0n, 0n), "", null, BigInt(proto_Constants.ENTITY_ID_NULL), false);
|
|
30
|
+
// A package with no product is dropped
|
|
31
|
+
const productsById = new Map();
|
|
32
|
+
for (const product of products)
|
|
33
|
+
productsById.set(product.id, product);
|
|
34
|
+
for (const pkg of packages) {
|
|
35
|
+
const product = productsById.get(pkg.productId);
|
|
36
|
+
if (product === undefined)
|
|
24
37
|
continue;
|
|
25
|
-
|
|
26
|
-
this.
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
productsByCategory.set(product.productCategoryId, [product]);
|
|
38
|
+
UpdatableEntity.setParent(pkg, product);
|
|
39
|
+
const productPackages = this._candidatePackages.get(product.id);
|
|
40
|
+
if (productPackages === undefined)
|
|
41
|
+
this._candidatePackages.set(product.id, [pkg]);
|
|
30
42
|
else
|
|
31
|
-
|
|
32
|
-
for (const pkg of product.packages)
|
|
33
|
-
this._packageMap.set(pkg.id, pkg);
|
|
43
|
+
productPackages.push(pkg);
|
|
34
44
|
}
|
|
35
|
-
|
|
36
|
-
//
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
45
|
+
// The first of two price lists with one id is kept. A price with no price list is dropped, and a
|
|
46
|
+
// list holds one price per package, the first to arrive.
|
|
47
|
+
for (const priceList of priceLists)
|
|
48
|
+
if (!this._priceListMap.has(priceList.id))
|
|
49
|
+
this._priceListMap.set(priceList.id, priceList);
|
|
50
|
+
const pricesByList = new Map();
|
|
51
|
+
for (const price of prices) {
|
|
52
|
+
if (!this._priceListMap.has(price.priceListId))
|
|
42
53
|
continue;
|
|
43
|
-
|
|
44
|
-
|
|
54
|
+
const listPrices = pricesByList.get(price.priceListId);
|
|
55
|
+
if (listPrices === undefined)
|
|
56
|
+
pricesByList.set(price.priceListId, new Map([[price.packageId, price]]));
|
|
57
|
+
else if (!listPrices.has(price.packageId))
|
|
58
|
+
listPrices.set(price.packageId, price);
|
|
45
59
|
}
|
|
46
|
-
|
|
47
|
-
for (const
|
|
48
|
-
|
|
60
|
+
// A parent that was not read, or a list that names itself, leaves the list without a parent
|
|
61
|
+
for (const priceList of this._priceListMap.values()) {
|
|
62
|
+
const updatable = priceList;
|
|
63
|
+
updatable.prices = pricesByList.get(priceList.id) ?? new Map();
|
|
64
|
+
const parent = this._priceListMap.get(priceList.parentPriceListId) ?? null;
|
|
65
|
+
updatable.parentPriceList = parent === priceList ? null : parent;
|
|
66
|
+
}
|
|
67
|
+
this.applySalePriceList();
|
|
49
68
|
}
|
|
50
69
|
// In no particular order - a UI orders these for display
|
|
51
70
|
get categories() {
|
|
@@ -55,11 +74,22 @@ class Catalog {
|
|
|
55
74
|
get products() {
|
|
56
75
|
return this._products;
|
|
57
76
|
}
|
|
58
|
-
// The
|
|
59
|
-
//
|
|
60
|
-
//
|
|
77
|
+
// The id of the price list packages are priced from, DEFAULT_SALE_PRICE_LIST_ID until the sync
|
|
78
|
+
// layer sets it. Setting it prices every package from that list, or from its parent where the list
|
|
79
|
+
// has no price, and keeps the packages, products and categories that can then be bought. An id
|
|
80
|
+
// that names no list prices nothing, so the catalog holds nothing.
|
|
81
|
+
get currentSalePriceListId() {
|
|
82
|
+
return this._currentSalePriceListId;
|
|
83
|
+
}
|
|
84
|
+
set currentSalePriceListId(value) {
|
|
85
|
+
this._currentSalePriceListId = value;
|
|
86
|
+
this.applySalePriceList();
|
|
87
|
+
}
|
|
88
|
+
// The price list packages are priced from (MeraBills Online for a storefront). Null when the
|
|
89
|
+
// catalog has no list with the current id, as before the catalog has arrived. A shopping list
|
|
90
|
+
// built from this catalog carries its id, or MERABILLS_ONLINE_PRICE_LIST_ID while it is null.
|
|
61
91
|
get priceList() {
|
|
62
|
-
return this.
|
|
92
|
+
return this._priceListMap.get(this._currentSalePriceListId) ?? null;
|
|
63
93
|
}
|
|
64
94
|
get productCount() {
|
|
65
95
|
return this._products.length;
|
|
@@ -75,7 +105,8 @@ class Catalog {
|
|
|
75
105
|
return this._productMap.get(productId) ?? null;
|
|
76
106
|
}
|
|
77
107
|
// Null for the four reserved package ids a shopping list uses for its charge and summary lines
|
|
78
|
-
// (see Ids.isReservedPackageId), and for a package
|
|
108
|
+
// (see Ids.isReservedPackageId), and for a package that is not buyable or whose product is not
|
|
109
|
+
// buyable
|
|
79
110
|
getPackage(packageId) {
|
|
80
111
|
return this._packageMap.get(packageId) ?? null;
|
|
81
112
|
}
|
|
@@ -90,7 +121,7 @@ class Catalog {
|
|
|
90
121
|
return byPrice;
|
|
91
122
|
return Catalog.compareIds(left, right);
|
|
92
123
|
}
|
|
93
|
-
// Display comparator for items generally: case insensitive, numeric, id
|
|
124
|
+
// Display comparator for items generally: case insensitive, numeric, ties broken by id
|
|
94
125
|
static compareByName(left, right) {
|
|
95
126
|
const byName = Catalog.compareNames(left.name, right.name);
|
|
96
127
|
if (byName !== 0)
|
|
@@ -111,7 +142,56 @@ class Catalog {
|
|
|
111
142
|
return -1;
|
|
112
143
|
return left.offerPrice - right.offerPrice;
|
|
113
144
|
}
|
|
145
|
+
// Prices the packages from the current sale price list, then rebuilds what a customer can reach.
|
|
146
|
+
// Only a buyable product is part of a catalog: enabled, not sold only offline, with at least one
|
|
147
|
+
// buyable package. Any other is left out of products, getProduct and its categories, and its
|
|
148
|
+
// packages are unreachable by id. A category with no buyable products is left out too, the dummy
|
|
149
|
+
// one included.
|
|
150
|
+
applySalePriceList() {
|
|
151
|
+
const salePriceList = this.priceList;
|
|
152
|
+
const buyableProducts = [];
|
|
153
|
+
const productMap = new Map();
|
|
154
|
+
const packageMap = new Map();
|
|
155
|
+
const productsByCategory = new Map();
|
|
156
|
+
for (const product of this._allProducts) {
|
|
157
|
+
const candidates = this._candidatePackages.get(product.id) ?? [];
|
|
158
|
+
for (const pkg of candidates) {
|
|
159
|
+
const price = salePriceList === null ? null : salePriceList.getPrice(pkg.id);
|
|
160
|
+
pkg.price = price;
|
|
161
|
+
if (price !== null)
|
|
162
|
+
UpdatableEntity.setParent(price, pkg);
|
|
163
|
+
}
|
|
164
|
+
// The product keeps only the buyable ones, and is judged by them
|
|
165
|
+
product.packages = candidates;
|
|
166
|
+
if (!product.isBuyable)
|
|
167
|
+
continue;
|
|
168
|
+
buyableProducts.push(product);
|
|
169
|
+
productMap.set(product.id, product);
|
|
170
|
+
const productsOfCategory = productsByCategory.get(product.productCategoryId);
|
|
171
|
+
if (productsOfCategory === undefined)
|
|
172
|
+
productsByCategory.set(product.productCategoryId, [product]);
|
|
173
|
+
else
|
|
174
|
+
productsOfCategory.push(product);
|
|
175
|
+
for (const pkg of product.packages)
|
|
176
|
+
packageMap.set(pkg.id, pkg);
|
|
177
|
+
}
|
|
178
|
+
const nonEmptyCategories = [];
|
|
179
|
+
const categoryMap = new Map();
|
|
180
|
+
for (const category of [...this._allCategories, this._uncategorized]) {
|
|
181
|
+
const categoryProducts = productsByCategory.get(category.id);
|
|
182
|
+
category.products = Object.freeze(categoryProducts ?? []);
|
|
183
|
+
if (categoryProducts === undefined)
|
|
184
|
+
continue;
|
|
185
|
+
nonEmptyCategories.push(category);
|
|
186
|
+
categoryMap.set(category.id, category);
|
|
187
|
+
}
|
|
188
|
+
this._products = Object.freeze(buyableProducts);
|
|
189
|
+
this._categories = Object.freeze(nonEmptyCategories);
|
|
190
|
+
this._productMap = productMap;
|
|
191
|
+
this._packageMap = packageMap;
|
|
192
|
+
this._categoryMap = categoryMap;
|
|
193
|
+
}
|
|
114
194
|
}
|
|
115
195
|
// A business whose catalog has not arrived yet
|
|
116
|
-
Catalog.EMPTY = new Catalog(
|
|
196
|
+
Catalog.EMPTY = new Catalog([], [], [], [], []);
|
|
117
197
|
export default Catalog;
|
package/dist/Constants.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export { Constants } from "./generated/Constants";
|
|
2
2
|
export declare function buildPriceListId(priceList: number): bigint;
|
|
3
|
+
export declare const DEFAULT_SALE_PRICE_LIST_ID: bigint;
|
|
3
4
|
export declare const MERABILLS_ONLINE_PRICE_LIST_ID: bigint;
|
|
4
5
|
export declare const MAX_SCALED_AMOUNT: bigint;
|
|
5
6
|
export declare function amountFromScaledValue(scaledAmount: bigint): number;
|
package/dist/Constants.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Constants as proto_Constants } from "./generated/Constants";
|
|
2
2
|
import Ids from "./Ids";
|
|
3
3
|
// The protocol's limits and scaling factors, and the wire-to-UI value conversions.
|
|
4
|
-
// Limits are re-exported so
|
|
4
|
+
// Limits are re-exported from the generated code so each Java constant is defined in one place.
|
|
5
5
|
export { Constants } from "./generated/Constants";
|
|
6
6
|
// The id of a built-in price list, from the low part Constants carries for it:
|
|
7
7
|
// DEFAULT_SALE_PRICE_LIST, DEFAULT_PURCHASE_PRICE_LIST, MERABILLS_ONLINE_PRICE_LIST,
|
|
@@ -11,12 +11,14 @@ export { Constants } from "./generated/Constants";
|
|
|
11
11
|
export function buildPriceListId(priceList) {
|
|
12
12
|
return Ids.buildId(proto_Constants.PRICE_LIST_ENTITY_TYPE_ID, BigInt(priceList));
|
|
13
13
|
}
|
|
14
|
-
// The
|
|
15
|
-
|
|
16
|
-
//
|
|
14
|
+
// The list a catalog sells from until another is set, as in the merchant app's Catalog
|
|
15
|
+
export const DEFAULT_SALE_PRICE_LIST_ID = buildPriceListId(proto_Constants.DEFAULT_SALE_PRICE_LIST);
|
|
16
|
+
// The list a storefront sells from. A business prices a package in several lists (over the counter,
|
|
17
|
+
// on a marketplace), and a response can carry any of its public ones. ProtoMap.buildBusiness sets
|
|
18
|
+
// the catalog to sell from this one, and a package with no price in it is priced from its parent.
|
|
17
19
|
export const MERABILLS_ONLINE_PRICE_LIST_ID = buildPriceListId(proto_Constants.MERABILLS_ONLINE_PRICE_LIST);
|
|
18
20
|
// The largest amount the protocol can carry (Java Transaction.MAX_AMOUNT). Kept here because it does
|
|
19
|
-
// not fit a protobuf enum value.
|
|
21
|
+
// not fit a protobuf enum value. It is 2^44 - 1, well below 2^53, so an amount is safe as a number.
|
|
20
22
|
export const MAX_SCALED_AMOUNT = 17592186044415n;
|
|
21
23
|
// An amount, price or discount travels multiplied by Constants.AMOUNT_FACTOR, bounded by
|
|
22
24
|
// MAX_SCALED_AMOUNT, so it's safe as a number even though it arrives as an int64.
|
package/dist/ContainerNames.js
CHANGED
|
@@ -9,12 +9,12 @@ export function forBusiness(nameSuffix) {
|
|
|
9
9
|
export function forProfile(nameSuffix) {
|
|
10
10
|
return `${VERSION_PREFIX}${PROFILE_PREFIX}${nameSuffix}`;
|
|
11
11
|
}
|
|
12
|
-
// Every name carries the layout version of its contents, so
|
|
13
|
-
// needs no migration
|
|
14
|
-
//
|
|
15
|
-
//
|
|
12
|
+
// Every name carries the layout version of its contents, so changing how a container is stored
|
|
13
|
+
// needs no migration. The new version uses names nothing has written, and the old containers stay
|
|
14
|
+
// unused until deleted. Bump it whenever a stored blob changes meaning. Callers cannot be relied
|
|
15
|
+
// on to clear a store, and a container read with the wrong meaning is worse than no cache.
|
|
16
16
|
const VERSION_PREFIX = "V1.";
|
|
17
|
-
//
|
|
18
|
-
// business id would put a profile and a catalog in one container
|
|
17
|
+
// This library does not control either id space. Without a prefix, a subject id equal to a
|
|
18
|
+
// business id would put a profile and a catalog in one container.
|
|
19
19
|
const BUSINESS_PREFIX = "b.";
|
|
20
20
|
const PROFILE_PREFIX = "p.";
|
package/dist/Entity.js
CHANGED
|
@@ -35,8 +35,9 @@ class Entity {
|
|
|
35
35
|
get updateType() {
|
|
36
36
|
return Number(this._header.updateNumber & Entity.UPDATE_TYPE_MASK);
|
|
37
37
|
}
|
|
38
|
-
// A deleted entity is a tombstone: it carries what the entity was when deleted. A UI never
|
|
39
|
-
// one, except an order whose message is deleted, which is an order the business has
|
|
38
|
+
// A deleted entity is a tombstone: it carries what the entity was when deleted. A UI is never
|
|
39
|
+
// given one, except an order whose message is deleted, which is an order the business has
|
|
40
|
+
// completed.
|
|
40
41
|
get isDeleted() {
|
|
41
42
|
return this.updateType === proto_Constants.UPDATE_TYPE_DELETED;
|
|
42
43
|
}
|
|
@@ -52,7 +53,7 @@ class Entity {
|
|
|
52
53
|
Entity.UPDATE_TYPE_MASK = BigInt(proto_Constants.UPDATE_TYPE_MASK);
|
|
53
54
|
export default Entity;
|
|
54
55
|
// The fields of the Entity message every entity carries. Passed as one value because they are all
|
|
55
|
-
// bigint
|
|
56
|
+
// bigint, so swapping two at a call site would not be a type error.
|
|
56
57
|
export class EntityHeader {
|
|
57
58
|
constructor(id, createdAt, updatedAt, updateNumber, updateInfo) {
|
|
58
59
|
this.id = id;
|
package/dist/Enums.d.ts
CHANGED
|
@@ -5,6 +5,7 @@ export { BusinessIdentifierType } from "./generated/BusinessIdentifierType";
|
|
|
5
5
|
export { DimensionUnit } from "./generated/DimensionUnit";
|
|
6
6
|
export { FulfillmentMode } from "./generated/FulfillmentMode";
|
|
7
7
|
export { MessageType } from "./generated/MessageType";
|
|
8
|
+
export { OnlineStoreTheme } from "./generated/OnlineStoreTheme";
|
|
8
9
|
export { OrderStatus } from "./generated/OrderStatus";
|
|
9
10
|
export { PackageIdentifierType } from "./generated/PackageIdentifierType";
|
|
10
11
|
export { PaymentMethod } from "./generated/PaymentMethod";
|
package/dist/Enums.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
// Every enum of the protocol, re-exported so
|
|
2
|
-
//
|
|
1
|
+
// Every enum of the protocol, re-exported so each Java-defined numeric id is defined in one place.
|
|
2
|
+
// The list also records which enums are part of the library's surface.
|
|
3
3
|
// TaxType is the exception: the protocol does not carry it, so it is defined in TaxType.ts.
|
|
4
4
|
export { AddressType } from "./generated/AddressType";
|
|
5
5
|
export { AttachmentType } from "./generated/AttachmentType";
|
|
@@ -8,6 +8,7 @@ export { BusinessIdentifierType } from "./generated/BusinessIdentifierType";
|
|
|
8
8
|
export { DimensionUnit } from "./generated/DimensionUnit";
|
|
9
9
|
export { FulfillmentMode } from "./generated/FulfillmentMode";
|
|
10
10
|
export { MessageType } from "./generated/MessageType";
|
|
11
|
+
export { OnlineStoreTheme } from "./generated/OnlineStoreTheme";
|
|
11
12
|
export { OrderStatus } from "./generated/OrderStatus";
|
|
12
13
|
export { PackageIdentifierType } from "./generated/PackageIdentifierType";
|
|
13
14
|
export { PaymentMethod } from "./generated/PaymentMethod";
|
package/dist/Exceptions.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
// The two
|
|
2
|
-
//
|
|
1
|
+
// The two programming errors this library throws. A UI does not recover from either. A sync
|
|
2
|
+
// failure is a rejected promise (see ServiceExceptions.ts).
|
|
3
3
|
// A caller passed something this library cannot work with
|
|
4
4
|
export class InvalidArgumentException extends Error {
|
|
5
5
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { FulfillmentMode as proto_FulfillmentMode } from "./generated/FulfillmentMode";
|
|
2
|
+
import { OnlineStoreTheme as proto_OnlineStoreTheme } from "./generated/OnlineStoreTheme";
|
|
2
3
|
import { AddressList } from "./Address";
|
|
3
4
|
import BusinessIdentifier from "./BusinessIdentifier";
|
|
4
5
|
import LatLong from "./LatLong";
|
|
@@ -22,10 +23,10 @@ export default class GeneralSettings {
|
|
|
22
23
|
private readonly _primaryStoreLocation;
|
|
23
24
|
private readonly _googleBusinessCategoryId;
|
|
24
25
|
private readonly _googleBusinessCategoryName;
|
|
25
|
-
private readonly
|
|
26
|
+
private readonly _onlineStoreTheme;
|
|
26
27
|
private readonly _onlineShoppingEnabled;
|
|
27
|
-
static fromScaledValues(paymentMethodDetails: ReadonlyArray<PaymentMethodDetail>, customerServicePhone: string | null, isWhatsAppSupported: boolean, isSmsSupported: boolean, businessAddresses: AddressList, businessIdentifiers: ReadonlyArray<BusinessIdentifier>, fulfillmentModes: ReadonlyArray<proto_FulfillmentMode>, scaledDeliveryCharge: bigint, deliveryChargeTaxes: ReadonlyArray<Tax>, scaledFreeDeliveryTotal: bigint, deliveryRadiusKilometres: number, storeTagline: string | null, workingDays: WorkingDays, primaryStoreLocation: LatLong | null, googleBusinessCategoryId: string | null, googleBusinessCategoryName: string | null,
|
|
28
|
-
constructor(_paymentMethodDetails: ReadonlyArray<PaymentMethodDetail>, _customerServicePhone: string | null, _isWhatsAppSupported: boolean, _isSmsSupported: boolean, _businessAddresses: AddressList, _businessIdentifiers: ReadonlyArray<BusinessIdentifier>, _fulfillmentModes: ReadonlyArray<proto_FulfillmentMode>, _deliveryCharge: number, _deliveryChargeTaxes: ReadonlyArray<Tax>, _freeDeliveryTotal: number, _deliveryRadiusKilometres: number, _storeTagline: string | null, _workingDays: WorkingDays, _primaryStoreLocation: LatLong | null, _googleBusinessCategoryId: string | null, _googleBusinessCategoryName: string | null,
|
|
28
|
+
static fromScaledValues(paymentMethodDetails: ReadonlyArray<PaymentMethodDetail>, customerServicePhone: string | null, isWhatsAppSupported: boolean, isSmsSupported: boolean, businessAddresses: AddressList, businessIdentifiers: ReadonlyArray<BusinessIdentifier>, fulfillmentModes: ReadonlyArray<proto_FulfillmentMode>, scaledDeliveryCharge: bigint, deliveryChargeTaxes: ReadonlyArray<Tax>, scaledFreeDeliveryTotal: bigint, deliveryRadiusKilometres: number, storeTagline: string | null, workingDays: WorkingDays, primaryStoreLocation: LatLong | null, googleBusinessCategoryId: string | null, googleBusinessCategoryName: string | null, onlineStoreTheme: proto_OnlineStoreTheme | null, onlineShoppingEnabled: boolean): GeneralSettings;
|
|
29
|
+
constructor(_paymentMethodDetails: ReadonlyArray<PaymentMethodDetail>, _customerServicePhone: string | null, _isWhatsAppSupported: boolean, _isSmsSupported: boolean, _businessAddresses: AddressList, _businessIdentifiers: ReadonlyArray<BusinessIdentifier>, _fulfillmentModes: ReadonlyArray<proto_FulfillmentMode>, _deliveryCharge: number, _deliveryChargeTaxes: ReadonlyArray<Tax>, _freeDeliveryTotal: number, _deliveryRadiusKilometres: number, _storeTagline: string | null, _workingDays: WorkingDays, _primaryStoreLocation: LatLong | null, _googleBusinessCategoryId: string | null, _googleBusinessCategoryName: string | null, _onlineStoreTheme: proto_OnlineStoreTheme | null, _onlineShoppingEnabled: boolean);
|
|
29
30
|
get paymentMethodDetails(): ReadonlyArray<PaymentMethodDetail>;
|
|
30
31
|
get customerServicePhone(): string | null;
|
|
31
32
|
get isWhatsAppSupported(): boolean;
|
|
@@ -42,7 +43,7 @@ export default class GeneralSettings {
|
|
|
42
43
|
get primaryStoreLocation(): LatLong | null;
|
|
43
44
|
get googleBusinessCategoryId(): string | null;
|
|
44
45
|
get googleBusinessCategoryName(): string | null;
|
|
45
|
-
get
|
|
46
|
+
get onlineStoreTheme(): proto_OnlineStoreTheme | null;
|
|
46
47
|
get onlineShoppingEnabled(): boolean;
|
|
47
48
|
static readonly EMPTY: GeneralSettings;
|
|
48
49
|
}
|
package/dist/GeneralSettings.js
CHANGED
|
@@ -7,10 +7,10 @@ import WorkingDays from "./WorkingDays";
|
|
|
7
7
|
// merchant-only fields are absent.
|
|
8
8
|
class GeneralSettings {
|
|
9
9
|
// For ProtoMap, which sees amounts still multiplied by Constants.AMOUNT_FACTOR.
|
|
10
|
-
static fromScaledValues(paymentMethodDetails, customerServicePhone, isWhatsAppSupported, isSmsSupported, businessAddresses, businessIdentifiers, fulfillmentModes, scaledDeliveryCharge, deliveryChargeTaxes, scaledFreeDeliveryTotal, deliveryRadiusKilometres, storeTagline, workingDays, primaryStoreLocation, googleBusinessCategoryId, googleBusinessCategoryName,
|
|
11
|
-
return new GeneralSettings(paymentMethodDetails, customerServicePhone, isWhatsAppSupported, isSmsSupported, businessAddresses, businessIdentifiers, fulfillmentModes, amountFromScaledValue(scaledDeliveryCharge), deliveryChargeTaxes, amountFromScaledValue(scaledFreeDeliveryTotal), deliveryRadiusKilometres, storeTagline, workingDays, primaryStoreLocation, googleBusinessCategoryId, googleBusinessCategoryName,
|
|
10
|
+
static fromScaledValues(paymentMethodDetails, customerServicePhone, isWhatsAppSupported, isSmsSupported, businessAddresses, businessIdentifiers, fulfillmentModes, scaledDeliveryCharge, deliveryChargeTaxes, scaledFreeDeliveryTotal, deliveryRadiusKilometres, storeTagline, workingDays, primaryStoreLocation, googleBusinessCategoryId, googleBusinessCategoryName, onlineStoreTheme, onlineShoppingEnabled) {
|
|
11
|
+
return new GeneralSettings(paymentMethodDetails, customerServicePhone, isWhatsAppSupported, isSmsSupported, businessAddresses, businessIdentifiers, fulfillmentModes, amountFromScaledValue(scaledDeliveryCharge), deliveryChargeTaxes, amountFromScaledValue(scaledFreeDeliveryTotal), deliveryRadiusKilometres, storeTagline, workingDays, primaryStoreLocation, googleBusinessCategoryId, googleBusinessCategoryName, onlineStoreTheme, onlineShoppingEnabled);
|
|
12
12
|
}
|
|
13
|
-
constructor(_paymentMethodDetails, _customerServicePhone, _isWhatsAppSupported, _isSmsSupported, _businessAddresses, _businessIdentifiers, _fulfillmentModes, _deliveryCharge, _deliveryChargeTaxes, _freeDeliveryTotal, _deliveryRadiusKilometres, _storeTagline, _workingDays, _primaryStoreLocation, _googleBusinessCategoryId, _googleBusinessCategoryName,
|
|
13
|
+
constructor(_paymentMethodDetails, _customerServicePhone, _isWhatsAppSupported, _isSmsSupported, _businessAddresses, _businessIdentifiers, _fulfillmentModes, _deliveryCharge, _deliveryChargeTaxes, _freeDeliveryTotal, _deliveryRadiusKilometres, _storeTagline, _workingDays, _primaryStoreLocation, _googleBusinessCategoryId, _googleBusinessCategoryName, _onlineStoreTheme, _onlineShoppingEnabled) {
|
|
14
14
|
this._paymentMethodDetails = _paymentMethodDetails;
|
|
15
15
|
this._customerServicePhone = _customerServicePhone;
|
|
16
16
|
this._isWhatsAppSupported = _isWhatsAppSupported;
|
|
@@ -27,7 +27,7 @@ class GeneralSettings {
|
|
|
27
27
|
this._primaryStoreLocation = _primaryStoreLocation;
|
|
28
28
|
this._googleBusinessCategoryId = _googleBusinessCategoryId;
|
|
29
29
|
this._googleBusinessCategoryName = _googleBusinessCategoryName;
|
|
30
|
-
this.
|
|
30
|
+
this._onlineStoreTheme = _onlineStoreTheme;
|
|
31
31
|
this._onlineShoppingEnabled = _onlineShoppingEnabled;
|
|
32
32
|
}
|
|
33
33
|
get paymentMethodDetails() {
|
|
@@ -49,7 +49,8 @@ class GeneralSettings {
|
|
|
49
49
|
get businessIdentifiers() {
|
|
50
50
|
return this._businessIdentifiers;
|
|
51
51
|
}
|
|
52
|
-
// The modes this business offers, as a list rather than Java's bit mask.
|
|
52
|
+
// The modes this business offers, as a list rather than Java's bit mask. An empty list means the
|
|
53
|
+
// business has published none, and CartEditor then offers no delivery.
|
|
53
54
|
get fulfillmentModes() {
|
|
54
55
|
return this._fulfillmentModes;
|
|
55
56
|
}
|
|
@@ -92,16 +93,17 @@ class GeneralSettings {
|
|
|
92
93
|
get googleBusinessCategoryName() {
|
|
93
94
|
return this._googleBusinessCategoryName;
|
|
94
95
|
}
|
|
95
|
-
// Which of the storefront's
|
|
96
|
-
//
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
//
|
|
102
|
-
//
|
|
103
|
-
//
|
|
104
|
-
//
|
|
96
|
+
// Which of the storefront's themes this business picked. Null when none was picked. A theme that a
|
|
97
|
+
// newer app added, which this version of the library has no entry for, is returned as it came: a
|
|
98
|
+
// number that is not a member of the enum. A caller that maps themes must have a fallback.
|
|
99
|
+
get onlineStoreTheme() {
|
|
100
|
+
return this._onlineStoreTheme;
|
|
101
|
+
}
|
|
102
|
+
// Whether the business has turned online shopping on. The server sends the business either way.
|
|
103
|
+
// Model throws a NotFoundException, after reading everything it was sent, when the settings it
|
|
104
|
+
// holds do not say shopping is on or it holds none (see Model.setBusinessId). Also false for
|
|
105
|
+
// GeneralSettings.EMPTY. A business with online shopping off is closed. A business with nothing
|
|
106
|
+
// to buy is open.
|
|
105
107
|
get onlineShoppingEnabled() {
|
|
106
108
|
return this._onlineShoppingEnabled;
|
|
107
109
|
}
|
package/dist/HttpStatusCodes.js
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import { BadRequestException, ConflictException, InternalServerException, MethodNotAllowedException, NotFoundException, ServiceException, ServiceUnavailableException, UnauthorizedException, UpgradeRequiredException, } from "./ServiceExceptions";
|
|
2
|
-
//
|
|
3
|
-
// Java's HttpStatusCodes and exception package, so
|
|
4
|
-
//
|
|
2
|
+
// Maps a status code to its exception. The classes are in ServiceExceptions.ts. The split matches
|
|
3
|
+
// Java's HttpStatusCodes and exception package, so each client has one table of what each code
|
|
4
|
+
// means.
|
|
5
5
|
// Everything from 300 up (Java's HttpURLConnection.HTTP_MULT_CHOICE). A redirect counts as a
|
|
6
|
-
// failure because this client follows none, so
|
|
6
|
+
// failure because this client follows none, so the call did not happen.
|
|
7
7
|
export function isError(httpStatusCode) {
|
|
8
8
|
return httpStatusCode >= LOWEST_ERROR_STATUS_CODE;
|
|
9
9
|
}
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
//
|
|
10
|
+
// Throws the exception for a status code, and does nothing for a code that is not an error. A code
|
|
11
|
+
// with no class throws a ServiceException carrying the code, so an unlisted error code cannot pass
|
|
12
|
+
// as a success.
|
|
13
13
|
export function throwOnError(httpStatusCode, message) {
|
|
14
14
|
if (!isError(httpStatusCode))
|
|
15
15
|
return;
|
package/dist/Ids.js
CHANGED
|
@@ -22,8 +22,7 @@ class Ids {
|
|
|
22
22
|
}
|
|
23
23
|
// A fresh id for an entity this client creates (Java Entity.getRandomLongForId): random, never
|
|
24
24
|
// in 0-255, which every entity type reserves. Only the low part is drawn, as buildId masks the
|
|
25
|
-
// rest off. It uses the cryptographic generator because a Math.random id is guessable
|
|
26
|
-
// message id can be handed to a caller.
|
|
25
|
+
// rest off. It uses the cryptographic generator because a Math.random id is guessable.
|
|
27
26
|
static getNewId(entityTypeId) {
|
|
28
27
|
for (;;) {
|
|
29
28
|
const bytes = new Uint8Array(8);
|
|
@@ -55,9 +54,9 @@ class Ids {
|
|
|
55
54
|
static toOrderNumberString(orderId) {
|
|
56
55
|
return (orderId & Ids.UNSIGNED_32_BIT_MASK).toString(16).toUpperCase();
|
|
57
56
|
}
|
|
58
|
-
//
|
|
59
|
-
//
|
|
60
|
-
//
|
|
57
|
+
// The blob in which a container keeps the sync cursor of one entity type. Its eTag is the cursor
|
|
58
|
+
// the next read sends, and the blob holds no other data. The store keeps the greater eTag, so a
|
|
59
|
+
// stale or repeated page, or a write from another tab, cannot lower the cursor. It is written in
|
|
61
60
|
// the batch of the page that moved it, so it cannot get ahead of the entities it describes. key
|
|
62
61
|
// is one of SyncKeys.
|
|
63
62
|
static getSyncCursorBlobId(key) {
|
package/dist/Item.js
CHANGED
|
@@ -2,7 +2,8 @@ import Entity from "./Entity";
|
|
|
2
2
|
// Something a business buys or sells, or a category or price of one. Mirrors the protocol's Item, embedded in each of the five real types.
|
|
3
3
|
// parentItemId is the wire parent (a product's category). Entity.parent is the sync layer's containment parent (the business).
|
|
4
4
|
export default class Item extends Entity {
|
|
5
|
-
// Public because ProductCategory and PriceList
|
|
5
|
+
// Public because ProductCategory and PriceList declare no constructor and inherit this one. The
|
|
6
|
+
// class is still abstract.
|
|
6
7
|
constructor(header, _name, _description, _parentItemId, _isDisabled) {
|
|
7
8
|
super(header);
|
|
8
9
|
this._name = _name;
|
|
@@ -16,13 +17,15 @@ export default class Item extends Entity {
|
|
|
16
17
|
get description() {
|
|
17
18
|
return this._description;
|
|
18
19
|
}
|
|
19
|
-
// Constants.ENTITY_ID_NULL when this item has no parent (e.g. an uncategorized product, or a price
|
|
20
|
+
// Constants.ENTITY_ID_NULL when this item has no parent (e.g. an uncategorized product, or a price
|
|
21
|
+
// list with no parent list).
|
|
20
22
|
get parentItemId() {
|
|
21
23
|
return this._parentItemId;
|
|
22
24
|
}
|
|
23
|
-
// True when the business has disabled this item.
|
|
24
|
-
// change after an item exists, so
|
|
25
|
-
// not guarantee that every package of an otherwise visible product is
|
|
25
|
+
// True when the business has disabled this item. The server filters out draft and non-public
|
|
26
|
+
// products, but this flag can change after an item exists, so a disabled item is sent flagged. A
|
|
27
|
+
// product-level check does not guarantee that every package of an otherwise visible product is
|
|
28
|
+
// enabled.
|
|
26
29
|
get isDisabled() {
|
|
27
30
|
return this._isDisabled;
|
|
28
31
|
}
|
package/dist/LiveData.js
CHANGED
|
@@ -29,7 +29,7 @@ class LiveData {
|
|
|
29
29
|
// Returns the function that stops this observer observing. Observing twice with the same function only observes once.
|
|
30
30
|
observe(observer, notifyImmediately = false) {
|
|
31
31
|
if (!this._observers.has(observer)) {
|
|
32
|
-
// NOT_NOTIFIED means the immediate notification below has something to deliver; otherwise a new observer only
|
|
32
|
+
// NOT_NOTIFIED means the immediate notification below has something to deliver; otherwise a new observer receives only future changes (not sticky, unlike Android LiveData).
|
|
33
33
|
this._observers.set(observer, notifyImmediately ? LiveData.NOT_NOTIFIED : this._version);
|
|
34
34
|
}
|
|
35
35
|
if (notifyImmediately)
|
|
@@ -54,7 +54,7 @@ class LiveData {
|
|
|
54
54
|
if (this._isNotificationStale)
|
|
55
55
|
break; // Start again with the newer value
|
|
56
56
|
if (this._observers.get(observer) === this._version)
|
|
57
|
-
continue; // Already
|
|
57
|
+
continue; // Already notified
|
|
58
58
|
this._observers.set(observer, this._version);
|
|
59
59
|
observer(this._holder);
|
|
60
60
|
}
|
package/dist/Measures.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
// How big a package is and how much it weighs,
|
|
2
|
-
//
|
|
1
|
+
// How big a package is and how much it weighs, in one file since a UI showing one usually shows both.
|
|
2
|
+
// Neither is an entity; both are values on Package.
|
|
3
|
+
// Units are the generated proto enums. A value is shown in the unit the merchant entered it in, and nothing here converts between units.
|
|
3
4
|
export class Dimensions {
|
|
4
5
|
constructor(_unit, _length, _width, _height) {
|
|
5
6
|
this._unit = _unit;
|
package/dist/Message.js
CHANGED
|
@@ -12,7 +12,7 @@ export default class Message extends Entity {
|
|
|
12
12
|
}
|
|
13
13
|
// The customer's party id at the business (BusinessLink.partyId), not the id of their User. A
|
|
14
14
|
// business sync out sends a customer only messages from their own party. senderName and
|
|
15
|
-
// senderPhone
|
|
15
|
+
// senderPhone identify the sender without resolving the party.
|
|
16
16
|
get senderId() {
|
|
17
17
|
return this._senderId;
|
|
18
18
|
}
|
package/dist/MockDatabase.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
// A Database that holds everything in a Map, for tests and training mode
|
|
2
|
-
// network.
|
|
1
|
+
// A Database that holds everything in a Map, for tests and training mode, which run with no
|
|
2
|
+
// network. It enforces the eTag rule (a write applies only if its eTag is greater), so a test can
|
|
3
|
+
// check that a sync is idempotent.
|
|
3
4
|
export default class MockDatabase {
|
|
4
5
|
constructor() {
|
|
5
6
|
this._containers = new Map();
|
|
@@ -18,14 +19,14 @@ export default class MockDatabase {
|
|
|
18
19
|
async deleteContainer(name) {
|
|
19
20
|
this._containers.delete(name);
|
|
20
21
|
}
|
|
21
|
-
//
|
|
22
|
+
// Lets a test inspect what was stored without going through a sync
|
|
22
23
|
getContainerIfPresent(name) {
|
|
23
24
|
return this._containers.get(name) ?? null;
|
|
24
25
|
}
|
|
25
26
|
}
|
|
26
27
|
export class MockContainer {
|
|
27
28
|
constructor() {
|
|
28
|
-
//
|
|
29
|
+
// A test sets this just before the write that should fail. That write clears it.
|
|
29
30
|
this.failNextWrite = false;
|
|
30
31
|
// How many times write has been called, so that a test can show a sync did not write per entity
|
|
31
32
|
this.writeCount = 0;
|
|
@@ -56,7 +57,7 @@ export class MockContainer {
|
|
|
56
57
|
this._blobs.delete(id);
|
|
57
58
|
return appliedCount;
|
|
58
59
|
}
|
|
59
|
-
//
|
|
60
|
+
// The greater eTag wins. A write always applies when the incoming blob or the stored one has no eTag.
|
|
60
61
|
static isNewer(incoming, stored) {
|
|
61
62
|
if (stored === null)
|
|
62
63
|
return true;
|