@peabody-soft/mbs-model-ts 1.8.0 → 1.9.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/Business.js CHANGED
@@ -57,7 +57,8 @@ export default class Business extends Entity {
57
57
  this._orders = value;
58
58
  }
59
59
  // What this customer has picked and not yet ordered. Null until something is put in a cart, and
60
- // null again once the cart is ordered. The cart message itself stays, with no list. Only the list
60
+ // null again once the cart is ordered. The cart message itself stays, with no list, or with a list
61
+ // that has no items (the merchant app stores one), which is null here too. Only the list
61
62
  // is exposed here. The fulfillment mode is chosen when an order is placed and is stored on the
62
63
  // Order (see defaultFulfillmentMode).
63
64
  get cart() {
package/dist/Catalog.js CHANGED
@@ -75,9 +75,9 @@ class Catalog {
75
75
  return this._products;
76
76
  }
77
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.
78
+ // layer sets it. Setting it prices every package from that list (see PriceList.getPrice), and keeps
79
+ // the packages, products and categories that can then be bought. An id that names no list prices
80
+ // nothing, so the catalog holds nothing.
81
81
  get currentSalePriceListId() {
82
82
  return this._currentSalePriceListId;
83
83
  }
package/dist/Constants.js CHANGED
@@ -15,7 +15,8 @@ export function buildPriceListId(priceList) {
15
15
  export const DEFAULT_SALE_PRICE_LIST_ID = buildPriceListId(proto_Constants.DEFAULT_SALE_PRICE_LIST);
16
16
  // The list a storefront sells from. A business prices a package in several lists (over the counter,
17
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.
18
+ // the catalog to sell from this one, which can take the amounts of a package from its parent (see
19
+ // PriceList.getPrice).
19
20
  export const MERABILLS_ONLINE_PRICE_LIST_ID = buildPriceListId(proto_Constants.MERABILLS_ONLINE_PRICE_LIST);
20
21
  // The largest amount the protocol can carry (Java Transaction.MAX_AMOUNT). Kept here because it does
21
22
  // not fit a protobuf enum value. It is 2^44 - 1, well below 2^53, so an amount is safe as a number.
package/dist/Entity.d.ts CHANGED
@@ -10,6 +10,7 @@ export default abstract class Entity {
10
10
  get isDeleted(): boolean;
11
11
  get parent(): Entity | null;
12
12
  protected set parent(value: Entity | null);
13
+ protected get header(): EntityHeader;
13
14
  private static readonly UPDATE_TYPE_MASK;
14
15
  private readonly _header;
15
16
  private _parent;
package/dist/Entity.js CHANGED
@@ -49,6 +49,10 @@ class Entity {
49
49
  set parent(value) {
50
50
  this._parent = value;
51
51
  }
52
+ // For a subclass that builds a variant of itself, which has the same id and update number
53
+ get header() {
54
+ return this._header;
55
+ }
52
56
  }
53
57
  Entity.UPDATE_TYPE_MASK = BigInt(proto_Constants.UPDATE_TYPE_MASK);
54
58
  export default Entity;
package/dist/Package.js CHANGED
@@ -39,8 +39,9 @@ export default class Package extends Item {
39
39
  return this._photos;
40
40
  }
41
41
  // What this package costs, with the taxes of its product included (Product.taxes). It is the
42
- // price in the catalog's sale price list, or in that list's parent. Null when neither has one, so
43
- // the package cannot be bought.
42
+ // price in the catalog's sale price list, which can follow that list's parent (see
43
+ // PriceList.getPrice). It is disabled when the package is not in the list. Null when the list gives
44
+ // the package no price, so the package cannot be bought.
44
45
  get price() {
45
46
  return this._price;
46
47
  }
package/dist/Price.d.ts CHANGED
@@ -4,10 +4,13 @@ export default class Price extends Item {
4
4
  private readonly _packageId;
5
5
  private readonly _regularPrice;
6
6
  private readonly _offerPrice;
7
- static fromScaledValues(header: EntityHeader, name: string, description: string | null, priceListId: bigint, isDisabled: boolean, packageId: bigint, scaledRegularPrice: bigint, scaledOfferPrice: bigint): Price;
8
- constructor(header: EntityHeader, name: string, description: string | null, priceListId: bigint, isDisabled: boolean, _packageId: bigint, _regularPrice: number, _offerPrice: number);
7
+ private readonly _isPlaceholder;
8
+ static fromScaledValues(header: EntityHeader, name: string, description: string | null, priceListId: bigint, isDisabled: boolean, packageId: bigint, scaledRegularPrice: bigint, scaledOfferPrice: bigint, isPlaceholder?: boolean): Price;
9
+ constructor(header: EntityHeader, name: string, description: string | null, priceListId: bigint, isDisabled: boolean, _packageId: bigint, _regularPrice: number, _offerPrice: number, _isPlaceholder?: boolean);
9
10
  get packageId(): bigint;
10
11
  get priceListId(): bigint;
12
+ get isPlaceholder(): boolean;
13
+ withIsDisabled(isDisabled: boolean): Price;
11
14
  get regularPrice(): number;
12
15
  get offerPrice(): number;
13
16
  get finalPrice(): number;
package/dist/Price.js CHANGED
@@ -9,14 +9,15 @@ import Item from "./Item";
9
9
  // stores a price without taxes, so a cart takes them out (CartEditor).
10
10
  export default class Price extends Item {
11
11
  // For ProtoMap, which sees prices still multiplied by Constants.AMOUNT_FACTOR.
12
- static fromScaledValues(header, name, description, priceListId, isDisabled, packageId, scaledRegularPrice, scaledOfferPrice) {
13
- return new Price(header, name, description, priceListId, isDisabled, packageId, amountFromScaledValue(scaledRegularPrice), amountFromScaledValue(scaledOfferPrice));
12
+ static fromScaledValues(header, name, description, priceListId, isDisabled, packageId, scaledRegularPrice, scaledOfferPrice, isPlaceholder = false) {
13
+ return new Price(header, name, description, priceListId, isDisabled, packageId, amountFromScaledValue(scaledRegularPrice), amountFromScaledValue(scaledOfferPrice), isPlaceholder);
14
14
  }
15
- constructor(header, name, description, priceListId, isDisabled, _packageId, _regularPrice, _offerPrice) {
15
+ constructor(header, name, description, priceListId, isDisabled, _packageId, _regularPrice, _offerPrice, _isPlaceholder = false) {
16
16
  super(header, name, description, priceListId, isDisabled);
17
17
  this._packageId = _packageId;
18
18
  this._regularPrice = _regularPrice;
19
19
  this._offerPrice = _offerPrice;
20
+ this._isPlaceholder = _isPlaceholder;
20
21
  }
21
22
  // The package this price applies to. Nothing nests on the wire, so this is how the catalog finds
22
23
  // the price of a package.
@@ -27,6 +28,20 @@ export default class Price extends Item {
27
28
  get priceListId() {
28
29
  return this.parentItemId;
29
30
  }
31
+ // True when this price has no amounts of its own: the package has the amounts of the price the
32
+ // list's parent has for it. regularPrice and offerPrice are a copy of those, which can be out of
33
+ // date, so they are never used. isDisabled says whether the package is in this list. See
34
+ // PriceList.getPrice, which resolves it. A UI never sees one, because Package.price is never a
35
+ // placeholder.
36
+ get isPlaceholder() {
37
+ return this._isPlaceholder;
38
+ }
39
+ // This price with another enabled state: the same entity, amounts and list. For PriceList.getPrice,
40
+ // which returns a price that is not the one the list holds when the package's state in the list
41
+ // differs from the state of the price that gives its amounts.
42
+ withIsDisabled(isDisabled) {
43
+ return new Price(this.header, this.name, this.description, this.priceListId, isDisabled, this._packageId, this._regularPrice, this._offerPrice, this._isPlaceholder);
44
+ }
30
45
  get regularPrice() {
31
46
  return this._regularPrice;
32
47
  }
package/dist/PriceList.js CHANGED
@@ -1,15 +1,15 @@
1
1
  import Item from "./Item";
2
2
  // A named list of prices, at most one per package. A list can have a parent, named by its
3
- // parentItemId. A package with no price in the list is priced from the parent. Catalog sets the
4
- // prices and the parent, once every price list and price has been read.
3
+ // parentItemId, and gets the amounts of some packages from it. See getPrice for the rules. Catalog
4
+ // sets the prices and the parent, once every price list and price has been read.
5
5
  export default class PriceList extends Item {
6
6
  constructor() {
7
7
  super(...arguments);
8
8
  this._parentPriceList = null;
9
9
  this._prices = new Map();
10
10
  }
11
- // The id of the list whose prices are used for the packages this list has no price for, or
12
- // Constants.ENTITY_ID_NULL when this list has no parent
11
+ // The id of the list whose prices give the amounts of the packages this list takes them from it
12
+ // for, or Constants.ENTITY_ID_NULL when this list has no parent
13
13
  get parentPriceListId() {
14
14
  return this.parentItemId;
15
15
  }
@@ -28,10 +28,26 @@ export default class PriceList extends Item {
28
28
  set prices(value) {
29
29
  this._prices = value;
30
30
  }
31
- // The price of a package: this list's own, else the parent's own, else null. Only the parent is
32
- // consulted, not its parent. A price is returned even when disabled, so a package with a disabled
33
- // price here is not priced from the parent.
31
+ // The price of a package in this list, or null when the list gives it none. Only the parent is
32
+ // consulted, not its parent.
33
+ // - A list follows its parent. A package the list has no price for has the parent's price, so its
34
+ // amounts and whether it is enabled.
35
+ // - A price of this list replaces the parent's price, both amounts and enabled state.
36
+ // - A placeholder replaces only the enabled state. The amounts are the parent's. The placeholder
37
+ // holds a copy of them that can be out of date, so it is never used.
38
+ // - The package is in the list when the price returned is enabled.
39
+ // - A placeholder gives no price when the parent has none.
40
+ // The price returned is the one that gives the amounts when it already has the right state, and a
41
+ // copy of it with the right state (withIsDisabled) otherwise.
34
42
  getPrice(packageId) {
35
- return this._prices.get(packageId) ?? this._parentPriceList?._prices.get(packageId) ?? null;
43
+ const ownPrice = this._prices.get(packageId);
44
+ if (ownPrice !== undefined && !ownPrice.isPlaceholder)
45
+ return ownPrice;
46
+ const parentPrice = this._parentPriceList?._prices.get(packageId);
47
+ if (parentPrice === undefined)
48
+ return null;
49
+ if (ownPrice === undefined || ownPrice.isDisabled === parentPrice.isDisabled)
50
+ return parentPrice;
51
+ return parentPrice.withIsDisabled(ownPrice.isDisabled);
36
52
  }
37
53
  }
package/dist/ProtoMap.js CHANGED
@@ -737,7 +737,7 @@ export function buildBusiness(response) {
737
737
  productCategories.push(productCategory);
738
738
  }
739
739
  // A storefront sells from the MeraBills Online list, picked by id because a response can carry
740
- // other public lists. A package with no price in that list is priced from the list's parent.
740
+ // other public lists. That list can follow its parent (see PriceList.getPrice).
741
741
  const catalog = new Catalog(productCategories, products, packages, priceLists, prices);
742
742
  catalog.currentSalePriceListId = MERABILLS_ONLINE_PRICE_LIST_ID;
743
743
  updatableBusiness.catalog = catalog;
@@ -761,7 +761,8 @@ export function buildBusiness(response) {
761
761
  let defaultFulfillmentMode = proto_FulfillmentMode.DELIVERY;
762
762
  if (partyPreferences !== null && partyPreferences.payload.oneofKind === "partyPreferences") {
763
763
  // An emptied cart is a message whose payload has no shopping list, which is what placing an order
764
- // leaves. The cart is null then, the same as for a customer who never had one.
764
+ // leaves. A list with no items is the same: the merchant app stores its cart whether or not it holds
765
+ // anything. The cart is null then, the same as for a customer who never had one.
765
766
  cart = cartShoppingListFromProto(partyPreferences.payload.partyPreferences, countryCode);
766
767
  // Taken as the message has it. DELIVERY comes only from the fallback above, even when the message
767
768
  // holds UNSPECIFIED_FULFILLMENT_MODE.
@@ -824,8 +825,9 @@ export function buildShoppingList(message, countryCode) {
824
825
  // fulfillment mode. An existing message is amended, since its payload can carry fields a storefront
825
826
  // cannot read, and dropping them would overwrite that data. Its id is kept, as the server holds the
826
827
  // preferences under it. The derived id is used only when there is nothing to amend.
827
- // A list with no items is pushed as no list, as Java serializeShoppingList returns null below one
828
- // item. An emptied cart and a cart never created look the same on the wire.
828
+ // A list with no items is pushed as no list. The merchant app stores its cart with no items too, and
829
+ // both read as no cart (cartShoppingListFromProto), so an emptied cart, a cart with no items and a cart
830
+ // never created are one state to a storefront.
829
831
  // fulfillmentMode null keeps the existing value, the ordinary case: a storefront treats that field
830
832
  // as the merchant app's data and leaves it alone. createOrder sets it to the mode of the order that
831
833
  // emptied the cart.
@@ -925,11 +927,18 @@ function inPackageIdOrder(list) {
925
927
  deletedItems: [...list.deletedItems].sort(byPackageId),
926
928
  });
927
929
  }
928
- // The list inside a cart message, or absent for an emptied cart
930
+ // The list inside a cart message, or absent for an emptied cart. A list with no items is absent too,
931
+ // as the merchant app stores its cart whether or not it holds anything (see cartShoppingListFromProto)
929
932
  function getCartShoppingList(message) {
930
- return message.payload.oneofKind === "partyPreferences"
931
- ? message.payload.partyPreferences.shoppingList
932
- : undefined;
933
+ if (message.payload.oneofKind !== "partyPreferences")
934
+ return undefined;
935
+ const shoppingList = message.payload.partyPreferences.shoppingList;
936
+ return shoppingList === undefined || holdsNothing(shoppingList) ? undefined : shoppingList;
937
+ }
938
+ // Whether a list on the wire has no items. The items are what is judged, not the counts, as in
939
+ // pushedShoppingList
940
+ function holdsNothing(shoppingList) {
941
+ return shoppingList.existingItems.length === 0 && shoppingList.deletedItems.length === 0;
933
942
  }
934
943
  // A shopping list to push, or absent when it holds nothing - see buildPartyPreferencesMessage.
935
944
  // Emptiness is judged by the items sent, not the counts, for the reason given at shoppingListToProto.
@@ -1226,7 +1235,7 @@ function priceListFromProto(priceListProto) {
1226
1235
  function priceFromProto(priceProto) {
1227
1236
  const item = getItem(priceProto.item);
1228
1237
  const entity = getEntity(item.entity);
1229
- return Price.fromScaledValues(toEntityHeader(entity), item.name, stringFromProto(item.description), item.parentItemId, item.isDisabled, priceProto.packageId, priceProto.regularPrice, priceProto.offerPrice);
1238
+ return Price.fromScaledValues(toEntityHeader(entity), item.name, stringFromProto(item.description), item.parentItemId, item.isDisabled, priceProto.packageId, priceProto.regularPrice, priceProto.offerPrice, priceProto.isPlaceholder);
1230
1239
  }
1231
1240
  function packageFromProto(packageProto, photos) {
1232
1241
  const item = getItem(packageProto.item);
@@ -1246,12 +1255,16 @@ function orderFromProto(message, salesOrder, countryCode) {
1246
1255
  return Order.fromScaledValues(toEntityHeader(entity), message.senderId, message.status, stringFromProto(message.senderName), stringFromProto(message.senderPhone), salesOrder.status, salesOrder.fulfillmentMode, shoppingListFromProto(salesOrder.shoppingList, countryCode), salesOrder.shoppingListTotal, salesOrder.shoppingListCount, salesOrder.deliveryAddressId, salesOrder.deliveryAddress === undefined ? null : addressFromProto(salesOrder.deliveryAddress), salesOrder.creationDate, stringFromProto(salesOrder.note), salesOrder.paymentReceived);
1247
1256
  }
1248
1257
  // The one part of a cart message a customer is shown. Null when the cart holds nothing, so a cart
1249
- // emptied by placing an order and one never created both give null. The rest of the message is set by
1250
- // the server or read elsewhere: who sent it, how far the business has read it, and the fulfillment
1251
- // mode, which buildBusiness reads as defaultFulfillmentMode.
1258
+ // emptied by placing an order, one never created, and a list with no items (the merchant app stores
1259
+ // its cart even when it is empty, with the properties of the list) all give null. The properties of
1260
+ // a list with no items are not read: a cart is built again from its items. The rest of the message
1261
+ // is set by the server or read elsewhere: who sent it, how far the business has read it, and the
1262
+ // fulfillment mode, which buildBusiness reads as defaultFulfillmentMode.
1252
1263
  function cartShoppingListFromProto(partyPreferences, countryCode) {
1253
1264
  const shoppingList = partyPreferences.shoppingList;
1254
- return shoppingList === undefined ? null : shoppingListFromProto(shoppingList, countryCode);
1265
+ return shoppingList === undefined || holdsNothing(shoppingList)
1266
+ ? null
1267
+ : shoppingListFromProto(shoppingList, countryCode);
1255
1268
  }
1256
1269
  // #endregion
1257
1270
  // #region Photos
@@ -60,8 +60,8 @@ class ShoppingList {
60
60
  }
61
61
  // The id of the price list the cart is priced from. A storefront names
62
62
  // Constants.MERABILLS_ONLINE_PRICE_LIST_ID, the list in Catalog.priceList, for every line, including
63
- // one priced from that list's parent. CartEditor falls back to that id when the catalog has no
64
- // price list.
63
+ // one with the amounts of that list's parent. CartEditor falls back to that id when the catalog has
64
+ // no price list.
65
65
  get priceListId() {
66
66
  return this._priceListId;
67
67
  }
@@ -38,6 +38,15 @@ export interface Price {
38
38
  * @generated from protobuf field: int64 offerPrice = 4
39
39
  */
40
40
  offerPrice: bigint;
41
+ /**
42
+ * True when this price has no amounts of its own: the package has the amounts of the price that
43
+ * the parent of the price list has for it. regularPrice and offerPrice are a copy of those that
44
+ * can be out of date, so a client must not use them. Item.isDisabled says whether the package is
45
+ * in this price list. See PriceList.
46
+ *
47
+ * @generated from protobuf field: bool isPlaceholder = 5
48
+ */
49
+ isPlaceholder: boolean;
41
50
  }
42
51
  declare class Price$Type extends MessageType<Price> {
43
52
  constructor();
@@ -10,7 +10,8 @@ class Price$Type extends MessageType {
10
10
  { no: 1, name: "item", kind: "message", T: () => Item },
11
11
  { no: 2, name: "packageId", kind: "scalar", T: 3 /*ScalarType.INT64*/, L: 0 /*LongType.BIGINT*/ },
12
12
  { no: 3, name: "regularPrice", kind: "scalar", T: 3 /*ScalarType.INT64*/, L: 0 /*LongType.BIGINT*/ },
13
- { no: 4, name: "offerPrice", kind: "scalar", T: 3 /*ScalarType.INT64*/, L: 0 /*LongType.BIGINT*/ }
13
+ { no: 4, name: "offerPrice", kind: "scalar", T: 3 /*ScalarType.INT64*/, L: 0 /*LongType.BIGINT*/ },
14
+ { no: 5, name: "isPlaceholder", kind: "scalar", T: 8 /*ScalarType.BOOL*/ }
14
15
  ]);
15
16
  }
16
17
  create(value) {
@@ -18,6 +19,7 @@ class Price$Type extends MessageType {
18
19
  message.packageId = 0n;
19
20
  message.regularPrice = 0n;
20
21
  message.offerPrice = 0n;
22
+ message.isPlaceholder = false;
21
23
  if (value !== undefined)
22
24
  reflectionMergePartial(this, message, value);
23
25
  return message;
@@ -39,6 +41,9 @@ class Price$Type extends MessageType {
39
41
  case /* int64 offerPrice */ 4:
40
42
  message.offerPrice = reader.int64().toBigInt();
41
43
  break;
44
+ case /* bool isPlaceholder */ 5:
45
+ message.isPlaceholder = reader.bool();
46
+ break;
42
47
  default:
43
48
  let u = options.readUnknownField;
44
49
  if (u === "throw")
@@ -63,6 +68,9 @@ class Price$Type extends MessageType {
63
68
  /* int64 offerPrice = 4; */
64
69
  if (message.offerPrice !== 0n)
65
70
  writer.tag(4, WireType.Varint).int64(message.offerPrice);
71
+ /* bool isPlaceholder = 5; */
72
+ if (message.isPlaceholder !== false)
73
+ writer.tag(5, WireType.Varint).bool(message.isPlaceholder);
66
74
  let u = options.writeUnknownFields;
67
75
  if (u !== false)
68
76
  (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer);
@@ -7,8 +7,18 @@ import { MessageType } from "@protobuf-ts/runtime";
7
7
  import { Item } from "./Item";
8
8
  /**
9
9
  * A named list of prices. Item.parentItemId is the id of its parent price list, or 0 when it has
10
- * none. A package that has no price in a list is priced from the parent list. Contains
11
- * nothing - its prices are the Price entities whose Item.parentItemId is this price list's id.
10
+ * none. Contains nothing - its prices are the Price entities whose Item.parentItemId is this price
11
+ * list's id.
12
+ *
13
+ * A list with a parent follows it. Only the parent is consulted, never its parent.
14
+ * - A package with no price in the list has the parent's price: its amounts, and it is in the list
15
+ * when that price is enabled.
16
+ * - A price of the list replaces the parent's price, both the amounts and whether it is enabled.
17
+ * - A placeholder (Price.isPlaceholder) replaces only whether the package is enabled. The amounts
18
+ * are the parent's price: the placeholder holds a copy that can be out of date, so a client must
19
+ * not use it.
20
+ * - A placeholder is no price at all when the parent has no price for the package.
21
+ *
12
22
  * Excluded: isPurchasePriceList - a purchase price list is never sent, so the flag would always be
13
23
  * false.
14
24
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@peabody-soft/mbs-model-ts",
3
- "version": "1.8.0",
3
+ "version": "1.9.0",
4
4
  "description": "MeraBills storefront client-side model (TypeScript)",
5
5
  "author": "Peabody Soft",
6
6
  "license": "AGPL-3.0-or-later",
@@ -49,7 +49,7 @@
49
49
  "typescript": "^7.0.2"
50
50
  },
51
51
  "dependencies": {
52
- "@peabody-soft/mbs-protos": "^1.12.1",
52
+ "@peabody-soft/mbs-protos": "^1.13.0",
53
53
  "@protobuf-ts/runtime": "^2.11.1"
54
54
  }
55
55
  }