@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.
Files changed (64) hide show
  1. package/dist/Attachment.js +2 -2
  2. package/dist/AttachmentBaseUrl.js +4 -4
  3. package/dist/AuthTokens.js +7 -7
  4. package/dist/Business.js +11 -10
  5. package/dist/BusinessIdentifier.js +3 -2
  6. package/dist/BusinessLink.js +3 -2
  7. package/dist/CartEditor.js +89 -81
  8. package/dist/Catalog.d.ts +19 -7
  9. package/dist/Catalog.js +121 -41
  10. package/dist/Constants.d.ts +1 -0
  11. package/dist/Constants.js +7 -5
  12. package/dist/ContainerNames.js +6 -6
  13. package/dist/Entity.js +4 -3
  14. package/dist/Enums.d.ts +1 -0
  15. package/dist/Enums.js +3 -2
  16. package/dist/Exceptions.js +2 -2
  17. package/dist/GeneralSettings.d.ts +5 -4
  18. package/dist/GeneralSettings.js +17 -15
  19. package/dist/HttpStatusCodes.js +7 -7
  20. package/dist/Ids.js +4 -5
  21. package/dist/Item.js +8 -5
  22. package/dist/LiveData.js +2 -2
  23. package/dist/Measures.js +3 -2
  24. package/dist/Message.js +1 -1
  25. package/dist/MockDatabase.js +6 -5
  26. package/dist/Model.js +166 -162
  27. package/dist/Order.js +10 -12
  28. package/dist/Package.d.ts +6 -2
  29. package/dist/Package.js +10 -5
  30. package/dist/PackageIdentifier.js +2 -2
  31. package/dist/Price.js +6 -5
  32. package/dist/PriceList.d.ts +13 -0
  33. package/dist/PriceList.js +34 -2
  34. package/dist/Product.d.ts +6 -2
  35. package/dist/Product.js +15 -10
  36. package/dist/Profile.js +15 -14
  37. package/dist/ProtoMap.js +189 -206
  38. package/dist/ServiceExceptions.js +6 -6
  39. package/dist/ServiceProxy.js +15 -14
  40. package/dist/ShoppingList.js +102 -100
  41. package/dist/SyncCache.js +76 -68
  42. package/dist/SyncKeys.js +5 -5
  43. package/dist/SyncState.js +14 -6
  44. package/dist/Tax.js +11 -10
  45. package/dist/TaxHelper.js +9 -8
  46. package/dist/TaxType.js +7 -6
  47. package/dist/UserProfile.js +7 -7
  48. package/dist/WorkingDays.js +21 -18
  49. package/dist/generated/BusinessSyncOutResponse.d.ts +4 -1
  50. package/dist/generated/Constants.d.ts +0 -4
  51. package/dist/generated/Constants.js +0 -4
  52. package/dist/generated/GeneralSettings.d.ts +5 -4
  53. package/dist/generated/GeneralSettings.js +8 -6
  54. package/dist/generated/Item.d.ts +4 -3
  55. package/dist/generated/Message.d.ts +5 -0
  56. package/dist/generated/OnlineStoreTheme.d.ts +33 -0
  57. package/dist/generated/OnlineStoreTheme.js +38 -0
  58. package/dist/generated/PriceList.d.ts +3 -2
  59. package/dist/generated/ProfileSyncOutRequest.d.ts +16 -15
  60. package/dist/generated/ProfileSyncOutResponse.d.ts +4 -6
  61. package/dist/generated/SalesOrderPayload.d.ts +6 -3
  62. package/dist/index.d.ts +4 -0
  63. package/dist/index.js +11 -8
  64. package/package.json +2 -2
@@ -1,7 +1,7 @@
1
1
  import Entity from "./Entity";
2
2
  import Ids from "./Ids";
3
- // A file attached to an entity: a photo of a business, product or package. Not public. A UI is given
4
- // a Photo (attachment type and url) built from it.
3
+ // A file attached to an entity: a photo of a business, product or package. Not exported from
4
+ // index.ts: a UI is given a Photo (attachment type and url) built from it.
5
5
  export default class Attachment extends Entity {
6
6
  constructor(header, _attachmentType, _owningEntityId, _fileHash, _fileExtension, _fileName) {
7
7
  super(header);
@@ -1,14 +1,14 @@
1
1
  import { Constants as proto_Constants } from "./generated/Constants";
2
2
  import { InvalidArgumentException } from "./Exceptions";
3
3
  // Where a business's attachment files live, the signature that reads them, and when both expire.
4
- // The signature has no getter, only createFileUrl exposes it. It is safe in a url because it is
5
- // scoped, read-only and short-lived, unlike AuthTokens' userToken and linkToken.
4
+ // The signature has no getter, and only createFileUrl exposes it. A signature in a url is safe
5
+ // because it is scoped, read-only and short-lived, unlike AuthTokens' userToken and linkToken.
6
6
  export default class AttachmentBaseUrl {
7
7
  constructor(_resourceUrl, _sasToken, expiry) {
8
8
  this._resourceUrl = _resourceUrl;
9
9
  this._sasToken = _sasToken;
10
- // The protobuf promises no trailing slash and no query string. Checked, since either would
11
- // silently build a url that fetches nothing.
10
+ // The protobuf specifies no trailing slash and no query string. Either would build a url that
11
+ // fetches nothing, so both are checked.
12
12
  if (_resourceUrl.length === 0 || _resourceUrl.endsWith("/"))
13
13
  throw new InvalidArgumentException(`Not a container url: ${_resourceUrl}`);
14
14
  if (_resourceUrl.includes("?"))
@@ -1,6 +1,6 @@
1
- // The credentials a request is signed with: the fields of the AuthTokens message, nothing derived.
2
- // A class of its own because it is held and cached. ProtoMap turns one into the message a Service
3
- // is handed. Every value is a bearer credential, so never log one.
1
+ // The credentials a request is signed with: the fields of the AuthTokens message, with nothing
2
+ // derived. It is a class because it is held and cached. ProtoMap converts one into the message
3
+ // passed to a Service. Every value is a bearer credential, so never log one.
4
4
  export default class AuthTokens {
5
5
  constructor(_authenticationMethod, _authenticationToken, _userToken, _linkToken) {
6
6
  this._authenticationMethod = _authenticationMethod;
@@ -13,13 +13,13 @@ export default class AuthTokens {
13
13
  get authenticationMethod() {
14
14
  return this._authenticationMethod;
15
15
  }
16
- // Who is calling, as their identity provider states it. Empty when read back from the cache, which
17
- // keeps no copy. See ProtoMap.authTokensToBlob.
16
+ // The identity provider's token for the caller. Empty when read back from the cache, which stores
17
+ // no copy. See ProtoMap.authTokensToBlob.
18
18
  get authenticationToken() {
19
19
  return this._authenticationToken;
20
20
  }
21
- // The MeraBills user, issued by this server. Null until the first profile sync out, which is the
22
- // call that returns one.
21
+ // Identifies the MeraBills user. This server issues it, so it is null until the first profile
22
+ // sync out returns one.
23
23
  get userToken() {
24
24
  return this._userToken;
25
25
  }
package/dist/Business.js CHANGED
@@ -48,8 +48,8 @@ export default class Business extends Entity {
48
48
  this._photos = value;
49
49
  }
50
50
  // The orders this customer has placed, open and completed (fulfilled or cancelled). Order.status
51
- // says how far the business got, and Order.isDeleted that it has completed the order. The library
52
- // does not sort them.
51
+ // is the order's progress, and Order.isDeleted is true once the business has completed it. The
52
+ // library does not sort them.
53
53
  get orders() {
54
54
  return this._orders;
55
55
  }
@@ -57,8 +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 it is ordered, although the message carrying the cart outlives that. Only the
61
- // list is exposed. The fulfillment mode is chosen when an order is placed and is a field of the
60
+ // null again once the cart is ordered. The cart message itself stays, with no list. Only the list
61
+ // is exposed here. The fulfillment mode is chosen when an order is placed and is stored on the
62
62
  // Order (see defaultFulfillmentMode).
63
63
  get cart() {
64
64
  return this._cart;
@@ -66,9 +66,9 @@ export default class Business extends Entity {
66
66
  set cart(value) {
67
67
  this._cart = value;
68
68
  }
69
- // What to preselect for the next order. It is the mode in the party preferences message, which
70
- // createOrder stamps with the mode of the order just placed, so it is the last order's choice.
71
- // DELIVERY when there is no party preferences message.
69
+ // The fulfillment mode to preselect for the next order: the mode of the last order placed, which
70
+ // Model.createOrder writes to the party preferences message. DELIVERY when there is no party
71
+ // preferences message.
72
72
  get defaultFulfillmentMode() {
73
73
  return this._defaultFulfillmentMode;
74
74
  }
@@ -84,12 +84,13 @@ export default class Business extends Entity {
84
84
  }
85
85
  // A scratch copy of the cart to edit, priced from this business's catalog and settings (see
86
86
  // CartEditor). It reads catalog, settings and cart once, so a Business republished mid-edit needs
87
- // a new editor. Nothing it does touches this business until its built list reaches
88
- // Model.updateCartLocalOnly.
87
+ // a new editor. Editing does not change this business. The cart changes only when the editor's
88
+ // built list is passed to Model.updateCartLocalOnly.
89
89
  createCartEditor() {
90
90
  return new CartEditor(this._catalog, this._settings, this._cart);
91
91
  }
92
- // Where this business's photo files live. Renewed on its own schedule.
92
+ // Where this business's photo files live. The server renews it, and it is tracked by its expiry,
93
+ // not by an entity cursor.
93
94
  get attachmentBaseUrl() {
94
95
  return this._attachmentBaseUrl;
95
96
  }
@@ -1,5 +1,6 @@
1
- // An identifier of a business, such as a GSTIN, and its kind. A pair rather than a map, as in
2
- // PackageIdentifier: Java identifies kinds by string id, so the numbering is protocol-only.
1
+ // An identifier of a business, such as a GSTIN, and its kind. It is a pair of type and value, not a
2
+ // map keyed by kind, as in PackageIdentifier. Java identifies kinds by string id, so the enum
3
+ // numbering exists only in the protocol.
3
4
  export default class BusinessIdentifier {
4
5
  constructor(_type, _value) {
5
6
  this._type = _type;
@@ -44,8 +44,9 @@ class BusinessLink extends Entity {
44
44
  get isExpirySet() {
45
45
  return this._expiry !== BusinessLink.NO_EXPIRY;
46
46
  }
47
- // True once past expiry by more than Constants.MAX_CLOCK_SKEW. Applied the opposite way to
48
- // AttachmentBaseUrl.hasExpired, so a false positive cannot lock out an entitled customer.
47
+ // True once expiry is more than Constants.MAX_CLOCK_SKEW in the past. The skew delays expiry
48
+ // here, whereas AttachmentBaseUrl.hasExpired advances it. A false positive here would lock out an
49
+ // entitled customer.
49
50
  get hasExpired() {
50
51
  if (!this.isExpirySet)
51
52
  return false;
@@ -6,33 +6,36 @@ import { InvalidArgumentException } from "./Exceptions";
6
6
  import Ids from "./Ids";
7
7
  import ShoppingList, { ShoppingListItem, validatePricing } from "./ShoppingList";
8
8
  import TaxHelper from "./TaxHelper";
9
- // Builds the cart a customer is filling, in the terms a UI has to hand: packages, quantities as
10
- // the customer means them (not multiplied by Constants.QUANTITY_FACTOR), prices from the catalog.
11
- // Get one from Business.createCartEditor, edit it, then hand build() to Model.updateCartLocalOnly.
9
+ // Builds the cart a customer is filling. It works in packages, quantities as the customer enters
10
+ // them (not multiplied by Constants.QUANTITY_FACTOR) and prices from the catalog. Get one from
11
+ // Business.createCartEditor, edit it, then pass build() to Model.updateCartLocalOnly.
12
12
  //
13
- // Mutable, and the only mutable thing in this object model. It is a scratch copy: editing it
14
- // changes no Business, store or server until a built list is handed to Model. It is also a
15
- // snapshot, read once at construction, so a sync mid-edit does not move prices under the customer.
16
- // Make a fresh one when the Business republishes.
13
+ // Mutable, and the only mutable class in this object model. It is a scratch copy: editing it
14
+ // changes no Business, store or server until a built list is passed to Model. It is also a
15
+ // snapshot, read once at construction, so a sync during an edit does not change the prices. Make
16
+ // a new one when the Business is republished.
17
17
  //
18
- // EVERY PRICE HERE INCLUDES TAXES: a package's includes its product's taxes, the delivery charge
19
- // the taxes the settings name for it, so a customer pays what the catalog shows. A shopping list
20
- // item stores a price WITHOUT taxes, so building one takes them out, as Java
21
- // ShoppingListItem.setDetailsFromTaxInclusivePrices does (to the unit, see TaxHelper). Taxes are
22
- // shown as what a total includes (taxes), never added to it.
18
+ // EVERY PRICE HERE INCLUDES TAXES. A package's price includes its product's taxes, and the
19
+ // delivery charge includes the taxes the settings name for it, so a customer pays what the
20
+ // catalog shows. A shopping list item stores a price WITHOUT taxes, so building one takes them
21
+ // out, as Java ShoppingListItem.setDetailsFromTaxInclusivePrices does (with the same amounts to
22
+ // the unit, see TaxHelper). A total includes its taxes. They are reported separately and never
23
+ // added to it.
23
24
  class CartEditor {
24
- // Business.createCartEditor is how a UI gets one. It takes the catalog, settings and cart, not
25
- // the Business, so a caller can build a cart against ones that are not currently published.
25
+ // It takes the catalog, settings and cart instead of the Business, so a caller can build a cart
26
+ // against ones that are not the currently published ones.
26
27
  //
27
- // Every line is priced off the catalog, not off what the cart held: the merchant may have
28
- // repriced since, and an order freezes the price when it is placed. A line goes to
29
- // unavailableItems, for a UI to tell the customer about, when the catalog can no longer price it
30
- // (package withdrawn, disabled or without a price) or setQuantity would refuse it (a cart that
31
- // was read is not known to be one this class could have made): a zero or invalid quantity, a
32
- // fraction of a package not sold loose, more than the wire or the protocol carries at today's
33
- // price, or a line past the most a cart holds (the first lines are kept, in pick order).
34
- // The charge lines of the starting list (see ShoppingListItem.isReservedPackage) are dropped, not
35
- // listed, since build() puts back what the settings now say.
28
+ // Every line is priced from the catalog, not from the price the cart held: the merchant may have
29
+ // repriced since, and an order freezes the price when it is placed.
30
+ //
31
+ // A line goes to unavailableItems, for a UI to show the customer, when the catalog cannot price
32
+ // it (package withdrawn, disabled or without a price). A cart that was read back is not known to
33
+ // be one this class could have made, so a line also goes there when setQuantity would reject it:
34
+ // a zero or invalid quantity, a fraction of a package not sold loose, a quantity too large for
35
+ // the wire, a quantity whose amount at today's price is too large for the protocol, or a line
36
+ // past the most a cart holds (the first lines are kept, in pick order).
37
+ // The charge lines of the starting list (see ShoppingListItem.isReservedPackage) are dropped and
38
+ // not listed, because build() adds the charges the settings give.
36
39
  constructor(_catalog, _settings, cart) {
37
40
  this._catalog = _catalog;
38
41
  this._settings = _settings;
@@ -40,8 +43,8 @@ class CartEditor {
40
43
  // shoppingListOrder
41
44
  this._quantities = new Map();
42
45
  this._inventoryLocationId = cart?.inventoryLocationId ?? 0;
43
- // goodsItems is in the order the customer picked. existingItems is not, the server sending it
44
- // by package id, so a cart read back would reorder itself
46
+ // goodsItems is in the order the customer picked. existingItems is in the order the server
47
+ // sends it, by package id, so reading from it would reorder the cart
45
48
  const unavailableItems = [];
46
49
  for (const item of cart?.goodsItems ?? []) {
47
50
  if (item.isReservedPackage)
@@ -63,8 +66,8 @@ class CartEditor {
63
66
  }
64
67
  return Object.freeze(lines);
65
68
  }
66
- // The lines of the starting cart that the catalog can no longer price (see the constructor).
67
- // Empty for a cart built from nothing, and no edit adds to it.
69
+ // The lines of the starting cart that could not be kept (see the constructor). Empty for a cart
70
+ // built from nothing, and no edit adds to it.
68
71
  get unavailableItems() {
69
72
  return this._unavailableItems;
70
73
  }
@@ -86,11 +89,12 @@ class CartEditor {
86
89
  getQuantity(item) {
87
90
  return this._quantities.get(item.id) ?? 0;
88
91
  }
89
- // Sets how much of one package the cart holds; zero takes it out. Refuses a package a customer
90
- // cannot buy (disabled, or priced in no list this storefront sells from), a fractional quantity
91
- // of a package that is not sold loose, and a quantity past what the wire can carry. Sold loose is
92
- // read from the catalog's package, since the Package passed in may predate a catalog change.
93
- // Returns this, so edits chain.
92
+ // Sets how much of one package the cart holds; zero removes it. Throws InvalidArgumentException
93
+ // for a package a customer cannot buy (disabled, or priced in no list this storefront sells
94
+ // from), a fractional quantity of a package that is not sold loose, and a quantity past what the
95
+ // wire can carry. See checkQuantity for the full list. Whether a package is sold loose comes from
96
+ // the catalog's package, because the Package passed in may be older than the catalog. Returns
97
+ // this, so edits chain.
94
98
  setQuantity(item, quantity) {
95
99
  const wireQuantity = CartEditor.toWireQuantity(quantity);
96
100
  // Less than half a thousandth rounds to zero on the wire, a line that costs nothing, so it
@@ -102,8 +106,8 @@ class CartEditor {
102
106
  this._quantities.set(item.id, this.checkQuantity(item.id, quantity, wireQuantity));
103
107
  return this;
104
108
  }
105
- // Adds to what the cart holds of this package, as a quantity stepper does. A negative amount
106
- // takes away, down to zero at the lowest.
109
+ // Adds to the quantity of this package in the cart, as a quantity stepper does. A negative
110
+ // amount subtracts, to a minimum of zero.
107
111
  add(item, quantity = 1) {
108
112
  return this.setQuantity(item, Math.max(0, this.getQuantity(item) + quantity));
109
113
  }
@@ -114,8 +118,8 @@ class CartEditor {
114
118
  this._quantities.clear();
115
119
  return this;
116
120
  }
117
- // What the packages come to, taxes included and offers taken off: the sum of the lines' final
118
- // prices, which a customer sees as the price of the cart. Delivery is not in it.
121
+ // The sum of the lines' final prices: taxes included, offers taken off, delivery not included. A
122
+ // customer sees it as the price of the cart.
119
123
  get itemsTotal() {
120
124
  return ShoppingList.sumOfFinalPrices(this.lines.map((line) => line.item));
121
125
  }
@@ -170,27 +174,29 @@ class CartEditor {
170
174
  return remaining > 0n ? amountFromScaledValue(remaining) : 0;
171
175
  }
172
176
  // The tax inside deliveryCharge, from GeneralSettings.deliveryChargeTaxes. Those are the
173
- // business's own, unrelated to a product's. Included in the charge, not added to it. Zero when
177
+ // business's own, unrelated to a product's, and are already included in the charge. Zero when
174
178
  // delivery is free.
175
179
  get deliveryTax() {
176
180
  return this.buildDeliveryChargeItem(this.getDeliveryCharge(this.itemsTotal), 0)?.totalTax ?? 0;
177
181
  }
178
- // What the cart comes to, taxes included. Delivery is left out unless asked for: a cart page
179
- // leaves it out and a checkout page puts it in, as in build(). It is the total of the list
180
- // build() answers, and so of an order placed from it.
182
+ // What the cart comes to, taxes included. Delivery is included only when withDeliveryCharge is
183
+ // true: a cart page leaves it out and a checkout page includes it, as in build(). It is the total
184
+ // of the list build() returns, and so of an order placed from it.
181
185
  total(withDeliveryCharge = false) {
182
186
  return this.build(withDeliveryCharge).totalAmount;
183
187
  }
184
- // The cart as a list, to write (Model.updateCartLocalOnly, Model.updateCart, Model.createOrder)
185
- // and to SHOW: every price, total and tax a cart or checkout page displays can be read off it.
188
+ // The cart as a list, to write (Model.updateCartLocalOnly or Model.updateCart) and to SHOW: every
189
+ // price, total and tax a cart or checkout page displays can be read off it. Model.createOrder
190
+ // takes no list: it orders the cart as stored.
186
191
  // Each line stores its price without taxes and is flagged original, as Java ShoppingList.addItem
187
- // does before a list is final. The list comes back not final: Model.createOrder makes it so.
192
+ // does before a list is final. The list is not final: Model.createOrder sends it final.
188
193
  //
189
194
  // withDeliveryCharge adds the delivery charge as a line (ReservedPackage.DELIVERY_CHARGE_PACKAGE)
190
- // and sets ShoppingList.autoAddDeliveryCharge. A cart page leaves it out (the default), a checkout
191
- // page that has settled on delivery asks for it. The line is added even when the charge is
192
- // waived: at zero, with the regular charge (less its taxes) as its priceDiscount, so a bill can
193
- // show FREE with the original struck through (Java ShoppingCartViewModel.setDeliveryCharge).
195
+ // and sets ShoppingList.autoAddDeliveryCharge. A cart page leaves it out (the default). A
196
+ // checkout page whose customer has chosen delivery passes true. The line is added even when the
197
+ // charge is waived: at zero, with the regular charge (less its taxes) as its priceDiscount, so a
198
+ // bill can show FREE with the original struck through (Java
199
+ // ShoppingCartViewModel.setDeliveryCharge).
194
200
  // An empty cart gets no line: a list holding only a delivery charge is not an order, and Java
195
201
  // removes that line when it is all that is left.
196
202
  build(withDeliveryCharge = false) {
@@ -203,8 +209,8 @@ class CartEditor {
203
209
  if (delivery !== null)
204
210
  items.push(delivery);
205
211
  // The total is the sum of the final prices of the lines sent, tax included, as ShoppingList.proto
206
- // defines it. There is no list-wide discount: an offer comes off a line, and a storefront gives
207
- // no other.
212
+ // defines it. There is no list-wide discount: an offer comes off a line, and a storefront
213
+ // applies no other.
208
214
  return new ShoppingList(false, delivery !== null, false, this._inventoryLocationId, this._catalog.priceList?.id ?? MERABILLS_ONLINE_PRICE_LIST_ID, ShoppingList.sumOfFinalPrices(items), 0, items.length, 0, Object.freeze(items), Object.freeze([]));
209
215
  }
210
216
  // The delivery charge as a line: one unit at what it costs now (charge: the regular charge, or
@@ -236,13 +242,14 @@ class CartEditor {
236
242
  return charge; // Never free, at any total
237
243
  return itemsTotal >= freeDeliveryTotal ? 0 : charge;
238
244
  }
239
- // The package this id names, and null unless a customer could actually buy it right now
245
+ // The package with this id, or null unless a customer can buy it
240
246
  getBuyablePackage(packageId) {
241
247
  const item = this._catalog.getPackage(packageId);
242
248
  return item !== null && item.isBuyable ? item : null;
243
249
  }
244
- // Keeps a line of a cart that was read, as setQuantity would, and says whether it could. A line
245
- // that cannot be kept goes to unavailableItems, as it is not the caller's mistake.
250
+ // Keeps a line of a cart that was read, as setQuantity would, and returns whether it could. A
251
+ // line that cannot be kept goes to unavailableItems instead of throwing, because it is not a
252
+ // caller's error.
246
253
  tryKeep(item) {
247
254
  try {
248
255
  const wireQuantity = CartEditor.toWireQuantity(item.quantity);
@@ -258,18 +265,18 @@ class CartEditor {
258
265
  }
259
266
  }
260
267
  // A quantity travels as an int32 multiplied by Constants.QUANTITY_FACTOR (Java rounds to it with
261
- // NumberHelper.floatToInt), so the least is a thousandth. This converts a quantity to that. What
262
- // is kept is the quantity the wire holds, so getQuantity never reports one the order will not
263
- // have.
268
+ // NumberHelper.floatToInt), so the smallest quantity is a thousandth. This converts a quantity
269
+ // to that. The editor stores the quantity the wire holds, so getQuantity reports the quantity the
270
+ // order will have.
264
271
  static toWireQuantity(quantity) {
265
272
  if (!Number.isFinite(quantity) || quantity < 0)
266
273
  throw new InvalidArgumentException(`${quantity} is not a quantity`);
267
274
  return Math.round(quantity * proto_Constants.QUANTITY_FACTOR);
268
275
  }
269
- // The quantity to keep for this much of a package, as the wire holds it, or an
270
- // InvalidArgumentException saying why the cart cannot have it: the catalog does not sell the
271
- // package, it is not sold loose and the quantity is a fraction, the quantity or what it comes to
272
- // is more than the protocol carries, or the cart is full. All of it is read off the catalog's
276
+ // Returns the quantity to store for this much of a package, as the wire holds it. Throws
277
+ // InvalidArgumentException when the cart cannot have it: the catalog does not sell the package,
278
+ // it is not sold loose and the quantity is a fraction, the quantity or its amount is more than
279
+ // the protocol carries, or the cart is full. Everything is checked against the catalog's
273
280
  // package, which the order is priced from.
274
281
  checkQuantity(packageId, quantity, wireQuantity) {
275
282
  const buyable = this.getBuyablePackage(packageId);
@@ -277,19 +284,19 @@ class CartEditor {
277
284
  throw new InvalidArgumentException(`Package ${Ids.toStorageHexString(packageId)} cannot be bought from this catalog`);
278
285
  if (!buyable.isSoldLoose && !Number.isInteger(quantity))
279
286
  throw new InvalidArgumentException(`${buyable.name} is not sold loose, so it cannot be bought ${quantity} at a time`);
280
- // The most of one package a cart can hold. Refused here so it does not wrap silently on the wire
287
+ // The most of one package a cart can hold. Rejected here so it cannot overflow on the wire
281
288
  if (wireQuantity > CartEditor.MAX_WIRE_QUANTITY)
282
289
  throw new InvalidArgumentException(`${quantity} is more than the ${CartEditor.MAX_WIRE_QUANTITY / proto_Constants.QUANTITY_FACTOR} the protocol can carry`);
283
- // A list holds at most Constants.SHOPPING_LIST_MAX_ITEMS items, and the delivery charge is one
284
- // (Java ShoppingList.addItem counts it like any other, and a server refuses a list one line
285
- // over). Where the business delivers, one place is kept for it: a cart filled to the limit
286
- // could not be ordered for delivery.
290
+ // A list holds at most Constants.SHOPPING_LIST_MAX_ITEMS items, and the delivery charge counts
291
+ // as one (Java ShoppingList.addItem counts it like any other, and a server rejects a list one
292
+ // line over). Where the business delivers, one place is reserved for it: a cart filled to the
293
+ // limit could not be ordered for delivery.
287
294
  const maxLines = proto_Constants.SHOPPING_LIST_MAX_ITEMS - (this.isDeliveryOffered ? 1 : 0);
288
295
  if (!this._quantities.has(packageId) && this._quantities.size >= maxLines)
289
296
  throw new InvalidArgumentException(`A cart holds at most ${maxLines} lines`);
290
- // Java refuses an amount past Transaction.MAX_AMOUNT (validateAmount) in the item that comes to
291
- // it, and so does pricing an item here. It is priced before the line is kept, so the failure
292
- // does not wait until a list is written.
297
+ // Java rejects an amount past Transaction.MAX_AMOUNT (validateAmount) in the item that reaches
298
+ // it, and so does pricing an item here. The line is priced before it is kept, so the error is
299
+ // raised here and not when a list is written.
293
300
  const stored = wireQuantity / proto_Constants.QUANTITY_FACTOR;
294
301
  try {
295
302
  validatePricing(new CartLine(buyable, this._catalog.getProduct(buyable.productId), stored).item);
@@ -310,7 +317,7 @@ export default CartEditor;
310
317
  // holds, rebuilt on every read of CartEditor.lines.
311
318
  //
312
319
  // Everything is read off item, the line as a shopping list would store it, so the cart page shows
313
- // what the order will and no price is worked out twice.
320
+ // the same prices as the order and no price is computed twice.
314
321
  export class CartLine {
315
322
  constructor(_package, _product, _quantity) {
316
323
  this._package = _package;
@@ -321,7 +328,8 @@ export class CartLine {
321
328
  get package() {
322
329
  return this._package;
323
330
  }
324
- // Null when the package's product is not in the catalog, which a buyable package never has
331
+ // Null when the package's product is not in the catalog. A buyable package always has its
332
+ // product there.
325
333
  get product() {
326
334
  return this._product;
327
335
  }
@@ -329,7 +337,7 @@ export class CartLine {
329
337
  return this._quantity;
330
338
  }
331
339
  // This line as a shopping list holds it: priced without taxes, with the taxes beside it. Show
332
- // this to show the line as an order does. Its place in the list means nothing here.
340
+ // this to show the line as an order does. Its shoppingListOrder is always 0 and means nothing.
333
341
  get item() {
334
342
  this._item ?? (this._item = this.toShoppingListItem(0));
335
343
  return this._item;
@@ -340,8 +348,8 @@ export class CartLine {
340
348
  return includedTaxes(this._product?.taxes ?? []);
341
349
  }
342
350
  // What one unit costs, taxes included: the offer price where the package is on offer, else the
343
- // regular price. This is the catalog's price to within tax rounding (a thousandth or two, which
344
- // no one is shown). A buyable package always has a price, so nothing is guessed.
351
+ // regular price. This is the catalog's price to within tax rounding (a thousandth or two, too
352
+ // small to show). A buyable package always has a price, so no default price is used.
345
353
  get unitPrice() {
346
354
  return this.item.unitPriceWithTaxes;
347
355
  }
@@ -372,8 +380,8 @@ export class CartLine {
372
380
  get finalPrice() {
373
381
  return this.item.finalPrice;
374
382
  }
375
- // This line as the protocol carries it. CartEditor.build calls it and assigns the order. Taxes
376
- // come out of the price on the way (see buildItem).
383
+ // This line as the protocol carries it. CartEditor.build calls it with the line's position as
384
+ // shoppingListOrder. Taxes are taken out of the price here (see buildItem).
377
385
  toShoppingListItem(shoppingListOrder) {
378
386
  const price = this._package.price;
379
387
  return buildItem(this._package.id, this._quantity, price?.finalPrice ?? 0, price?.regularPrice ?? 0, this._product?.taxes ?? [], shoppingListOrder);
@@ -384,13 +392,13 @@ export class CartLine {
384
392
  function includedTaxes(taxes) {
385
393
  return Object.freeze(taxes.filter((tax) => Number.isFinite(tax.rate) && tax.rate > 0));
386
394
  }
387
- // Where a price with taxes in it becomes a shopping list item, and so where CartEditor takes a tax
388
- // out of a price, as Java ShoppingListItem.setDetailsFromTaxInclusivePrices does. The offer price
389
- // and the regular price each have their taxes removed, and the price discount is the difference of
390
- // those two, which is not the difference of the prices with taxes. The rest is as Java's
391
- // ProductSelectionCard leaves a new item: no discount of its own, no description (that is for
392
- // lines with no package), no stock keeping or expiry (a storefront is not told). It is flagged
393
- // original, as in build.
395
+ // Builds a shopping list item from prices that include taxes, so this is where CartEditor takes a
396
+ // tax out of a price, as Java ShoppingListItem.setDetailsFromTaxInclusivePrices does. The offer
397
+ // price and the regular price each have their taxes removed, and the price discount is the
398
+ // difference of those two, which is not the difference of the prices with taxes. The other fields
399
+ // are as Java's ProductSelectionCard sets them on a new item: no discount besides the price
400
+ // discount, no description (that is for lines with no package), no stock keeping or expiry (a
401
+ // storefront is not sent them). The item is flagged original, as in build.
394
402
  // A price is a whole number of thousandths and each tax is rounded, so some prices cannot be
395
403
  // reached. The item then has the nearest price (TaxHelper.removeTaxes).
396
404
  function buildItem(packageId, quantity, offerPriceWithTaxes, regularPriceWithTaxes, taxes, shoppingListOrder) {
package/dist/Catalog.d.ts CHANGED
@@ -1,13 +1,15 @@
1
1
  import Item from "./Item";
2
2
  import Package from "./Package";
3
+ import Price from "./Price";
3
4
  import PriceList from "./PriceList";
4
5
  import Product from "./Product";
5
6
  import ProductCategory from "./ProductCategory";
6
7
  export default class Catalog {
7
- private readonly _priceList;
8
- constructor(categories: ReadonlyArray<ProductCategory>, products: ReadonlyArray<Product>, _priceList: PriceList | null);
8
+ constructor(categories: ReadonlyArray<ProductCategory>, products: ReadonlyArray<Product>, packages: ReadonlyArray<Package>, priceLists: ReadonlyArray<PriceList>, prices: ReadonlyArray<Price>);
9
9
  get categories(): ReadonlyArray<ProductCategory>;
10
10
  get products(): ReadonlyArray<Product>;
11
+ get currentSalePriceListId(): bigint;
12
+ private set currentSalePriceListId(value);
11
13
  get priceList(): PriceList | null;
12
14
  get productCount(): number;
13
15
  get packageCount(): number;
@@ -19,10 +21,20 @@ export default class Catalog {
19
21
  private static compareNames;
20
22
  private static compareIds;
21
23
  private static comparePrices;
24
+ private applySalePriceList;
22
25
  static readonly EMPTY: Catalog;
23
- private readonly _categories;
24
- private readonly _products;
25
- private readonly _categoryMap;
26
- private readonly _productMap;
27
- private readonly _packageMap;
26
+ private readonly _allCategories;
27
+ private readonly _allProducts;
28
+ private readonly _uncategorized;
29
+ private readonly _candidatePackages;
30
+ private readonly _priceListMap;
31
+ private _currentSalePriceListId;
32
+ private _categories;
33
+ private _products;
34
+ private _categoryMap;
35
+ private _productMap;
36
+ private _packageMap;
37
+ }
38
+ export interface UpdatableCatalog {
39
+ set currentSalePriceListId(value: bigint);
28
40
  }