@peabody-soft/mbs-model-ts 1.5.0 → 1.7.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 (52) hide show
  1. package/dist/Address.js +8 -11
  2. package/dist/Attachment.js +7 -8
  3. package/dist/AttachmentBaseUrl.js +10 -10
  4. package/dist/AuthTokens.js +12 -7
  5. package/dist/Business.js +19 -19
  6. package/dist/BusinessIdentifier.js +2 -2
  7. package/dist/BusinessLink.js +16 -16
  8. package/dist/BusinessProfile.js +4 -4
  9. package/dist/CartEditor.js +137 -166
  10. package/dist/Catalog.js +19 -25
  11. package/dist/Constants.js +10 -10
  12. package/dist/ContainerNames.js +10 -11
  13. package/dist/Entity.js +11 -9
  14. package/dist/Enums.js +2 -2
  15. package/dist/Exceptions.js +2 -2
  16. package/dist/GeneralSettings.js +23 -21
  17. package/dist/HttpStatusCodes.js +10 -11
  18. package/dist/Ids.js +24 -30
  19. package/dist/Item.js +6 -7
  20. package/dist/LatLong.js +3 -3
  21. package/dist/LiveData.js +6 -6
  22. package/dist/Measures.js +2 -2
  23. package/dist/Message.js +6 -6
  24. package/dist/MockDatabase.js +2 -2
  25. package/dist/Model.js +294 -365
  26. package/dist/Order.js +20 -17
  27. package/dist/Package.js +7 -8
  28. package/dist/PackageIdentifier.js +2 -2
  29. package/dist/PaymentMethodDetail.js +4 -4
  30. package/dist/Photo.js +6 -7
  31. package/dist/Price.js +8 -9
  32. package/dist/PriceList.js +2 -2
  33. package/dist/Product.js +17 -22
  34. package/dist/ProductCategory.js +3 -4
  35. package/dist/Profile.js +26 -27
  36. package/dist/ProtoMap.d.ts +3 -0
  37. package/dist/ProtoMap.js +288 -294
  38. package/dist/Routes.js +3 -3
  39. package/dist/ServiceExceptions.js +11 -12
  40. package/dist/ServiceProxy.js +19 -23
  41. package/dist/ShoppingList.js +112 -130
  42. package/dist/SyncCache.d.ts +3 -0
  43. package/dist/SyncCache.js +92 -75
  44. package/dist/SyncKeys.js +8 -8
  45. package/dist/SyncState.js +5 -5
  46. package/dist/Tax.js +16 -21
  47. package/dist/TaxHelper.js +22 -30
  48. package/dist/TaxType.js +8 -9
  49. package/dist/UserProfile.js +4 -4
  50. package/dist/WorkingDays.js +15 -15
  51. package/dist/index.js +9 -9
  52. package/package.json +1 -1
package/dist/Routes.js CHANGED
@@ -1,7 +1,7 @@
1
1
  // The last path segment of each call this library makes, appended to the base url a ServiceProxy is
2
- // built with. Named after the server method the segment reaches, in the kebab case Java Routes.java
3
- // uses, and prefixed to keep the storefront methods clear of the merchant ones that share the host.
4
- // No route takes an id: every one of these calls says everything it needs to in its request message.
2
+ // built with. Named after the server method, in the kebab case Java Routes.java uses, with a prefix
3
+ // that keeps storefront methods apart from the merchant ones on the same host.
4
+ // No route takes an id: the request message carries everything.
5
5
  class Routes {
6
6
  }
7
7
  Routes.BUSINESS_SYNC_OUT = "shop-business-sync-out";
@@ -1,11 +1,11 @@
1
- // What a Service call fails with. Unlike the two in Exceptions.ts these are not programming errors:
2
- // a caller is expected to catch them and decide what to do - sign in again, retry, or show the shop
3
- // it already has cached. They mirror Java com.merabills.repository.client.exception so that the two
4
- // clients of this server describe the same failure the same way.
5
- // Each carries the status code rather than being told apart by a string, and the derived classes
6
- // exist so a caller can catch the one case it can actually handle without testing a number.
1
+ // What a Service call fails with. Unlike the two in Exceptions.ts these are not programming errors.
2
+ // A caller is expected to catch them and decide what to do: sign in again, retry, or show the shop
3
+ // it has cached. They mirror Java com.merabills.repository.client.exception, so both clients of
4
+ // this server describe a failure the same way.
5
+ // Each carries the status code. The derived classes let a caller catch the one case it can handle
6
+ // without testing a number.
7
7
  // A call reached the server and the server refused it. Thrown directly for a status code none of the
8
- // classes below names, so an unrecognised failure is still a ServiceException and not something else.
8
+ // classes below names, so an unrecognised failure is still a ServiceException.
9
9
  export class ServiceException extends Error {
10
10
  constructor(httpStatusCode, message) {
11
11
  super(message);
@@ -70,9 +70,9 @@ export class ServiceUnavailableException extends ServiceException {
70
70
  }
71
71
  }
72
72
  ServiceUnavailableException.STATUS_CODE = 503;
73
- // The call never got an answer at all - offline, DNS, TLS, a refused connection or one cut short.
74
- // Not a ServiceException, because there is no status code and the server may well have acted on the
75
- // request: a caller that retries has to be prepared for the first attempt to have succeeded.
73
+ // The call never got an answer: offline, DNS, TLS, a refused connection or one cut short.
74
+ // Not a ServiceException, since there is no status code and the server may have acted on the
75
+ // request. A caller that retries must be prepared for the first attempt to have succeeded.
76
76
  export class NetworkConnectionException extends Error {
77
77
  constructor(message, reason) {
78
78
  super(message);
@@ -80,5 +80,4 @@ export class NetworkConnectionException extends Error {
80
80
  this.name = new.target.name;
81
81
  }
82
82
  }
83
- // Which status code each of these stands for is HttpStatusCodes.ts, the way Java keeps the classes
84
- // in its exception package and the mapping in HttpStatusCodes.
83
+ // The status code each of these stands for is mapped in HttpStatusCodes.ts.
@@ -12,14 +12,13 @@ import { buildAuthTokensBytes } from "./ProtoMap";
12
12
  import Routes from "./Routes";
13
13
  import { NetworkConnectionException } from "./ServiceExceptions";
14
14
  // A Service that reaches a real MeraBills server. Every call is one POST to the route named after
15
- // it, carrying its request message encoded, and answering with the response message encoded - so
16
- // this class translates nothing and reads no field of either.
17
- // One call is one page: a sync out is paged, and the loop belongs to whoever is caching what comes
18
- // back, so that each page is written as it arrives - see Model.businessSyncIn.
15
+ // it, with the encoded request message as the body and the encoded response message as the answer.
16
+ // This class translates nothing and reads no field of either.
17
+ // One call is one page: the loop over a paged sync out belongs to whoever caches what comes back,
18
+ // so each page is written as it arrives (see Model.businessSyncIn).
19
19
  export default class ServiceProxy {
20
- // The base url is everything before the route, with or without a trailing slash and with or
21
- // without a path of its own - both are joined onto rather than replaced, so a server reached
22
- // under a prefix works without the caller having to know how a route is built.
20
+ // The base url is everything before the route, with or without a trailing slash or a path of its
21
+ // own. The route is joined onto it, so a server reached under a prefix works.
23
22
  constructor(serviceBaseUrl, fetchFunction = null) {
24
23
  this._serviceBaseUrl = serviceBaseUrl.replace(TRAILING_SLASHES, "");
25
24
  this._fetchFunction = fetchFunction;
@@ -40,8 +39,8 @@ export default class ServiceProxy {
40
39
  async updateUser(user, authTokens) {
41
40
  return this.post(Routes.UPDATE_USER, proto_User, user, proto_ProfileSyncOutResponse, authTokens);
42
41
  }
43
- // One call: encode, send, turn a refusal into the exception it stands for, decode. The message
44
- // types are passed in rather than named per call so that every route is sent and read identically.
42
+ // One call: encode, send, turn a refusal into its exception, decode. The message types are
43
+ // passed in so every route is sent and read the same way.
45
44
  async post(route, requestType, request, responseType, authTokens) {
46
45
  const response = await this.send(route, requestType.toBinary(request), authTokens);
47
46
  // The body of a failure is not a response message, so it has to be dealt with before decoding
@@ -51,8 +50,8 @@ export default class ServiceProxy {
51
50
  }
52
51
  async send(route, body, authTokens) {
53
52
  const headers = { [CONTENT_TYPE_HEADER]: CONTENT_TYPE_PROTOBUF };
54
- // An anonymous visitor is given no header at all rather than an empty one - the protocol has no
55
- // anonymous form of the message, so the absence is what says nobody is claiming to be calling.
53
+ // An anonymous visitor gets no header, not an empty one. The protocol has no anonymous form of
54
+ // the message, so the absence says nobody claims to be calling.
56
55
  if (authTokens !== null)
57
56
  headers[AUTHORIZATION_HEADER] = toBase64(buildAuthTokensBytes(authTokens));
58
57
  try {
@@ -63,14 +62,13 @@ export default class ServiceProxy {
63
62
  });
64
63
  }
65
64
  catch (reason) {
66
- // fetch rejects only when no answer arrived at all; a server that refused the call answers
67
- // with a status code and resolves. So the request may still have been acted on, and a caller
68
- // that retries has to be prepared for the first attempt to have succeeded.
65
+ // fetch rejects only when no answer arrived. A refusal resolves with a status code. See
66
+ // NetworkConnectionException for what a caller must expect.
69
67
  throw new NetworkConnectionException(`The call to ${route} could not be completed`, reason);
70
68
  }
71
69
  }
72
- // Resolved per call rather than at construction, so a platform that installs fetch after this
73
- // object is built still works and an absent one fails where it is actually needed.
70
+ // Resolved per call, not at construction, so a platform that installs fetch after this object is
71
+ // built still works and an absent fetch fails where it is needed.
74
72
  getFetchFunction() {
75
73
  const supplied = this._fetchFunction;
76
74
  if (supplied !== null)
@@ -82,9 +80,8 @@ export default class ServiceProxy {
82
80
  return platform.bind(globalThis);
83
81
  }
84
82
  // What the response says went wrong, if anything. This protocol has no error message of its own
85
- // yet, so the body of a failure can be anything at all - a stack trace, an HTML page - and only
86
- // the start of one is worth carrying; a body that cannot be read says nothing the status code
87
- // does not already.
83
+ // yet, so a failure body can be anything (a stack trace, an HTML page) and only its start is
84
+ // worth carrying. A body that cannot be read adds nothing to the status code.
88
85
  static async readErrorMessage(route, response) {
89
86
  let body;
90
87
  try {
@@ -99,11 +96,10 @@ export default class ServiceProxy {
99
96
  }
100
97
  }
101
98
  // A header carries text, so the encoded credentials travel as base64. No scheme prefix: the server
102
- // reads the whole value as one encoded AuthTokens, exactly as the Java client sends the whole value
103
- // as one serialized AuthTokens.
99
+ // reads the whole value as one encoded AuthTokens, as the Java client sends it.
104
100
  function toBase64(bytes) {
105
- // Built a character at a time rather than by spreading the array into String.fromCharCode, which
106
- // passes it as an argument list and overflows the stack once it is long enough
101
+ // Built a character at a time, since spreading the array into String.fromCharCode passes it as an
102
+ // argument list and overflows the stack when long enough
107
103
  let binary = "";
108
104
  for (const byte of bytes)
109
105
  binary += String.fromCharCode(byte);
@@ -7,25 +7,22 @@ import { TaxAmount } from "./Tax";
7
7
  import TaxHelper from "./TaxHelper";
8
8
  // A list of packages being bought: a shopping cart, or what an order contains.
9
9
  //
10
- // Every unit price on a list is WITHOUT taxes, and its taxes are worked out on top (see
11
- // ShoppingListItem). What a customer is shown is the other thing: the same prices WITH the taxes,
12
- // which is what the catalog gave them, so each item offers both. Everything that adds up - the
13
- // line totals, the list's total, a discount of a line or of the list - already has the taxes in
14
- // it. The taxes inside a total are available by type (computedTaxes).
10
+ // Every unit price on a list is WITHOUT taxes, with the taxes worked out on top (see
11
+ // ShoppingListItem). Each item also offers the price WITH taxes that a customer is shown.
12
+ // Everything that adds up (line totals, the list's total, a discount) already has its taxes in it.
13
+ // The taxes inside a total are available by type (computedTaxes).
15
14
  //
16
- // Wire totals are authoritative: totalAmount is the number to display. The computed ones (below)
17
- // reproduce Java's arithmetic to the unit for the items that are here, so they agree with the wire
18
- // whenever the items were sent - but a list can arrive without them, and then only the wire total
19
- // covers the whole list.
15
+ // Wire totals are authoritative: totalAmount is the number to display. The computed ones reproduce
16
+ // Java's arithmetic to the unit for the items that are here, so they match the wire whenever the
17
+ // items were sent. A list can arrive without its items, and then only the wire total covers it.
20
18
  class ShoppingList {
21
- // Only ProtoMap calls this: it is the one place that sees a total still multiplied by
22
- // Constants.AMOUNT_FACTOR.
19
+ // For ProtoMap, which sees totals still multiplied by Constants.AMOUNT_FACTOR.
23
20
  static fromScaledValues(isFinal, autoAddDeliveryCharge, autoAddServiceCharge, inventoryLocationId, priceListId, scaledTotalAmountBeforeDiscount, scaledDiscount, existingItemCount, deletedItemCount, existingItems, deletedItems) {
24
21
  return new ShoppingList(isFinal, autoAddDeliveryCharge, autoAddServiceCharge, inventoryLocationId, priceListId, amountFromScaledValue(scaledTotalAmountBeforeDiscount), amountFromScaledValue(scaledDiscount), existingItemCount, deletedItemCount, existingItems, deletedItems);
25
22
  }
26
23
  // What these items come to, taxes included and each item's own discount taken off: the sum of
27
- // their finalPrice, which is what a list of them has as totalAmountBeforeDiscount. Added up in
28
- // scaled values and unscaled once, so no rounding creeps in from adding unscaled numbers.
24
+ // their finalPrice, which a list of them has as totalAmountBeforeDiscount. Added in scaled values
25
+ // and unscaled once, so adding unscaled numbers adds no rounding error.
29
26
  static sumOfFinalPrices(items) {
30
27
  return amountFromScaledValue(sumOf(items, (pricing) => pricing.finalPrice));
31
28
  }
@@ -42,8 +39,8 @@ class ShoppingList {
42
39
  this._existingItems = _existingItems;
43
40
  this._deletedItems = _deletedItems;
44
41
  }
45
- // True once the customer is done editing, after which the business may edit it instead. Only a
46
- // final list can have deleted items, and this is never unset.
42
+ // True once the customer is done editing, after which the business may edit it. Only a final
43
+ // list can have deleted items. It is never unset.
47
44
  get isFinal() {
48
45
  return this._isFinal;
49
46
  }
@@ -69,21 +66,21 @@ class ShoppingList {
69
66
  get totalAmountBeforeDiscount() {
70
67
  return this._totalAmountBeforeDiscount;
71
68
  }
72
- // The discount on the whole list, as opposed to a discount on one line of it. It comes off a
73
- // total that already has its taxes in it.
69
+ // The discount on the whole list, not on one line. It comes off a total that already has its
70
+ // taxes in it.
74
71
  get discount() {
75
72
  return this._discount;
76
73
  }
77
- // What the customer owes, taxes included. Derived rather than sent, but derived from the
78
- // authoritative totals above, so this is the number to display. Never less than zero, which is
79
- // Java ShoppingList.getTotalAmount: a discount greater than the total leaves nothing to pay, not
80
- // a refund. Worked out in scaled values, so it is not a floating point difference.
74
+ // What the customer owes, taxes included. Derived from the authoritative totals above, so it is
75
+ // the number to display. Never less than zero, as in Java ShoppingList.getTotalAmount: a discount
76
+ // greater than the total leaves nothing to pay. Worked out in scaled values, so there is no
77
+ // floating point error.
81
78
  get totalAmount() {
82
79
  const total = amountToScaledValue(this._totalAmountBeforeDiscount) - amountToScaledValue(this._discount);
83
80
  return total > 0n ? amountFromScaledValue(total) : 0;
84
81
  }
85
- // Read from the wire and not from the length of the array below, because the protocol sends the
86
- // count even when it does not send the items themselves
82
+ // Read from the wire because the protocol sends the count even when it sends no items, so the
83
+ // array length below can differ
87
84
  get existingItemCount() {
88
85
  return this._existingItemCount;
89
86
  }
@@ -97,25 +94,24 @@ class ShoppingList {
97
94
  return this._existingItems;
98
95
  }
99
96
  // The items that were in this list when it was made final and have since been deleted or
100
- // replaced. Always empty until then.
97
+ // replaced. Empty until the list is final.
101
98
  get deletedItems() {
102
99
  return this._deletedItems;
103
100
  }
104
- // The existing items that are packages bought from the catalog, in the order of the list - which
105
- // is shoppingListOrder and not the order they were sent in, the server sending them by package id.
106
- // The charge lines are not here (chargeItems), and neither are the items taken out since the
107
- // list was made final (removedItems).
101
+ // The existing items that are packages bought from the catalog, in the order of the list
102
+ // (shoppingListOrder). The server sends them by package id, so that differs from the order
103
+ // received. The charge lines are in chargeItems, and the items taken out since the list was made
104
+ // final are in removedItems.
108
105
  get goodsItems() {
109
106
  return this.derived.goods;
110
107
  }
111
- // The existing items that are charge lines - the delivery charge and the service charge - in the
108
+ // The existing items that are charge lines (the delivery charge and the service charge), in the
112
109
  // order of the list. The other two reserved ids, for a discount and a total, are summary lines a
113
- // list does not carry as items (the discount is discount, the total is totalAmount).
110
+ // list does not carry as items. They are discount and totalAmount.
114
111
  get chargeItems() {
115
112
  return this.derived.charges;
116
113
  }
117
- // The delivery charge, which is an item like any other and priced like one, or null when this
118
- // list has none
114
+ // The delivery charge, an item priced like any other, or null when this list has none
119
115
  get deliveryCharge() {
120
116
  return (this._existingItems.find((item) => item.reservedPackage === proto_ReservedPackage.DELIVERY_CHARGE_PACKAGE) ?? null);
121
117
  }
@@ -124,20 +120,18 @@ class ShoppingList {
124
120
  return (this._existingItems.find((item) => item.reservedPackage === proto_ReservedPackage.SERVICE_CHARGE_PACKAGE) ?? null);
125
121
  }
126
122
  // The items the business took out of the list after the customer made it final, or replaced with
127
- // a changed one, as the customer ordered them - deletedItems in the order of the list. A deleted
128
- // charge line is among them, and tells itself apart by reservedPackage.
123
+ // a changed one, as the customer ordered them. It is deletedItems in the order of the list. A
124
+ // deleted charge line is among them, and is told apart by reservedPackage.
129
125
  get removedItems() {
130
126
  return this.derived.removed;
131
127
  }
132
- // What the business has done to one line of this list since the customer made it final, which is
133
- // what ShoppingListItem.change says of it - except for a list the storefront wrote before it set
134
- // isOriginal. CartEditor did not, up to 1.4.0: every line of such an order is flagged as made
135
- // after the list was final, which on the merchant app's reading is a line the merchant added. But
136
- // no change the business can make leaves a final list with no line of the customer's in it and
137
- // nothing removed (adding a line leaves the others original; taking one out leaves it in
138
- // deletedItems; changing one leaves the old line there too), so a final list with nothing
139
- // original, nothing modified and nothing deleted is read as untouched. Show a list's marks with
140
- // this and not with ShoppingListItem.change.
128
+ // What the business has done to one line of this list since the customer made it final. It is
129
+ // ShoppingListItem.change, except for a legacy order, written by a storefront that did not set
130
+ // isOriginal (CartEditor, up to 1.4.0). Every line of such an order reads as added by the
131
+ // business. A final list with no original, modified or deleted line is taken to be one, since no
132
+ // change the business can make leaves that (adding a line keeps the others original, taking one
133
+ // out leaves it in deletedItems, changing one leaves the old line there too).
134
+ // Show a list's marks with this, not ShoppingListItem.change.
141
135
  changeOf(item) {
142
136
  const change = item.change;
143
137
  if (change !== ItemChange.ADDED)
@@ -160,14 +154,12 @@ class ShoppingList {
160
154
  return amountFromScaledValue(sumOf(this._existingItems, (pricing) => pricing.totalTax));
161
155
  }
162
156
  // The taxes inside computedTotal, one entry per TYPE of tax, in the order of the types, added up
163
- // over every item. A type none of the items has is not here. An amount can be zero - a type that
164
- // applies to nothing but free items, which Java ShoppingList.getTotalTaxesByTaxCode leaves out
165
- // altogether (JavaVectorsTest pins that) - and a bill shows no line for it, so it reads the same.
166
- // The taxes are not changed by the discount of an item or of the list: those come off a price that
167
- // has its taxes in it.
168
- //
169
- // Worked out once, like what derived holds, but apart from it: this prices the items, which the
170
- // lines of a list do not need, and one that cannot be priced is no reason for a list not to list them.
157
+ // over every item. A type none of the items has is not here. An amount can be zero, for a type
158
+ // that applies only to free items. Java ShoppingList.getTotalTaxesByTaxCode leaves such a type
159
+ // out (JavaVectorsTest pins that), and a bill shows no line for it.
160
+ // A discount does not change the taxes: it comes off a price that already has them in it.
161
+ // Worked out once, like derived but kept apart: this prices the items, and an item that cannot be
162
+ // priced must not stop the list's lines being listed.
171
163
  get computedTaxes() {
172
164
  const existing = TAXES.get(this);
173
165
  if (existing !== undefined)
@@ -185,24 +177,24 @@ class ShoppingList {
185
177
  TAXES.set(this, created);
186
178
  return created;
187
179
  }
188
- // What the existing items come to at their regular prices, with their taxes and with none of
189
- // their own discounts taken off: the total as it would have been had nothing been on offer.
190
- // Compared with totalAmount it gives what the customer saved. Same caveat as computedTotal.
180
+ // What the existing items come to at their regular prices, with their taxes and none of their own
181
+ // discounts taken off: the total had nothing been on offer. Compared with totalAmount it gives
182
+ // what the customer saved. Same caveat as computedTotal.
191
183
  get computedTotalWithoutDiscounts() {
192
184
  return amountFromScaledValue(sumOf(this._existingItems, (pricing) => pricing.finalPriceWithoutDiscounts));
193
185
  }
194
186
  // What the offers on the items, the discounts on them and the discount on the list took off,
195
- // taxes included, which is what a bill shows as savings - and a delivery charge that was waived,
196
- // being an item with a price discount like any other, as the merchant app's total row has it. So
197
- // a basket can save more once delivery is in the list than it did without. Zero when nothing was
198
- // taken off, and never negative. Same caveat as computedTotal: it needs the items.
187
+ // taxes included, shown on a bill as savings. A waived delivery charge counts too, being an item
188
+ // with a price discount like any other, as in the merchant app's total row. So a basket can save
189
+ // more once delivery is in the list. Zero when nothing was taken off, never negative. Same caveat
190
+ // as computedTotal: it needs the items.
199
191
  get savings() {
200
192
  const saved = sumOf(this._existingItems, (pricing) => pricing.finalPriceWithoutDiscounts) -
201
193
  amountToScaledValue(this.totalAmount);
202
194
  return saved > 0n ? amountFromScaledValue(saved) : 0;
203
195
  }
204
- // Worked out once for this list, which never changes, so that what a UI reads is the same array
205
- // every time - a dependency that holds still - and a list is not sorted at every render
196
+ // Worked out once, since the list never changes, so a UI reads the same array every time (a
197
+ // stable dependency) and the list is not sorted at every render
206
198
  get derived() {
207
199
  const existing = DERIVED.get(this);
208
200
  if (existing !== undefined)
@@ -227,24 +219,18 @@ ShoppingList.EMPTY = new ShoppingList(false, false, false, 0, MERABILLS_ONLINE_P
227
219
  export default ShoppingList;
228
220
  // One line of a shopping list: the package being bought, how much of it, and at what price.
229
221
  //
230
- // THE PRICE IS STORED WITHOUT TAXES. unitPrice and priceDiscount are the prices before taxes, and
231
- // taxes are worked out on top of them - exactly as Java ShoppingListItem does and as
232
- // ShoppingListItem.proto says. The prices a customer sees (a Price in the catalog, the unit price
233
- // in a cart) INCLUDE taxes, so a price has its taxes taken out on its way into an item
234
- // (CartEditor does this, as Java ShoppingListItem.setDetailsFromTaxInclusivePrices does), and the
235
- // *WithTaxes members below put them back for display. Show those, never unitPrice: unitPrice is
236
- // what is stored, not what anyone is charged for a unit.
222
+ // THE PRICE IS STORED WITHOUT TAXES: unitPrice and priceDiscount are before taxes, which are worked
223
+ // out on top, as in Java ShoppingListItem and ShoppingListItem.proto. A customer sees prices WITH
224
+ // taxes, so CartEditor takes them out on the way in and the *WithTaxes members put them back. Show
225
+ // those, never unitPrice, which is not what a customer is charged for a unit.
237
226
  //
238
- // The arithmetic is Java's to the unit, and rounds in the same places: the tax base (the unit price
239
- // multiplied by the quantity) is rounded to a whole amount, then each tax on it is rounded on its
240
- // own, half up. See TaxHelper. So are its limits: Java refuses an item for an amount of
241
- // Transaction.MAX_AMOUNT or more (validateAmount), and so does this - by throwing an
242
- // InvalidArgumentException when the item is priced, which is when something first reads a price
243
- // off it - for each of the taxes, the final price and the other prices it works out (see
244
- // validatePricing).
227
+ // The arithmetic is Java's to the unit and rounds in the same places: the tax base (unit price
228
+ // times quantity) to a whole amount, then each tax on its own, half up (see TaxHelper). An amount
229
+ // of Transaction.MAX_AMOUNT or more throws an InvalidArgumentException when something first reads
230
+ // a price off the item, as in Java (see validatePricing).
245
231
  export class ShoppingListItem {
246
- // Only ProtoMap calls this: it is the one place that sees a quantity multiplied by
247
- // Constants.QUANTITY_FACTOR and prices multiplied by Constants.AMOUNT_FACTOR.
232
+ // For ProtoMap, which sees a quantity multiplied by Constants.QUANTITY_FACTOR and prices
233
+ // multiplied by Constants.AMOUNT_FACTOR.
248
234
  static fromScaledValues(packageId, scaledQuantity, scaledUnitPrice, shoppingListOrder, isOriginal, isModified, isStockKeepingRequired, scaledPriceDiscount, scaledDiscount, taxes, expiryMinutes, description) {
249
235
  return new ShoppingListItem(packageId, quantityFromScaledValue(scaledQuantity), amountFromScaledValue(scaledUnitPrice), shoppingListOrder, isOriginal, isModified, isStockKeepingRequired, amountFromScaledValue(scaledPriceDiscount), amountFromScaledValue(scaledDiscount), taxes, expiryMinutes, description);
250
236
  }
@@ -262,8 +248,8 @@ export class ShoppingListItem {
262
248
  this._expiryMinutes = _expiryMinutes;
263
249
  this._description = _description;
264
250
  }
265
- // The package being bought. Four ids are reserved for charge and summary lines rather than
266
- // catalog packages and will not be found in the catalog - see isReservedPackage below.
251
+ // The package being bought. Four ids are reserved for charge and summary lines and are not in the
252
+ // catalog. See isReservedPackage.
267
253
  get packageId() {
268
254
  return this._packageId;
269
255
  }
@@ -281,8 +267,8 @@ export class ShoppingListItem {
281
267
  get quantity() {
282
268
  return this._quantity;
283
269
  }
284
- // What one unit costs WITHOUT taxes, which is the offer price when the package is on offer. This
285
- // is what is stored, not what a customer is charged: see unitPriceWithTaxes.
270
+ // What one unit costs WITHOUT taxes, the offer price when the package is on offer. This is what is
271
+ // stored. For what a customer is charged see unitPriceWithTaxes.
286
272
  get unitPrice() {
287
273
  return this._unitPrice;
288
274
  }
@@ -291,22 +277,21 @@ export class ShoppingListItem {
291
277
  return this._shoppingListOrder;
292
278
  }
293
279
  // True when this line was created before the list was made final. Everything a customer puts in
294
- // a cart is, which is what separates it from what the business adds or changes afterwards.
280
+ // a cart is, which separates it from what the business adds or changes afterwards.
295
281
  get isOriginal() {
296
282
  return this._isOriginal;
297
283
  }
298
- // True when this line has been deleted or replaced since the list was made final. An original
299
- // line that is also modified has been deleted (and is in ShoppingList.deletedItems); one that is
300
- // not original but is modified has replaced an original one; one that is neither has been added
301
- // by the business.
284
+ // True when this line has been deleted or replaced since the list was made final. Original and
285
+ // modified: deleted (and in ShoppingList.deletedItems). Not original but modified: replaced an
286
+ // original one. Neither: added by the business.
302
287
  get isModified() {
303
288
  return this._isModified;
304
289
  }
305
290
  // What the business has done to this line since the customer made the list final, read off
306
- // isOriginal and isModified the way the merchant app does: a "+" beside a line it added, an "x"
307
- // beside one it changed, and the old line listed apart as removed. This is the line's own flags
308
- // and nothing more; to show a list use ShoppingList.changeOf, which also knows an order written
309
- // before 1.5.0 flagged its lines wrongly.
291
+ // isOriginal and isModified as the merchant app does: a "+" beside a line it added, an "x" beside
292
+ // one it changed, and the old line listed apart as removed. This reads the line's own flags only.
293
+ // To show a list use ShoppingList.changeOf, which also knows that an order written before 1.5.0
294
+ // flagged its lines wrongly.
310
295
  get change() {
311
296
  if (this._isOriginal)
312
297
  return this._isModified ? ItemChange.REMOVED : ItemChange.NONE;
@@ -315,9 +300,9 @@ export class ShoppingListItem {
315
300
  get isStockKeepingRequired() {
316
301
  return this._isStockKeepingRequired;
317
302
  }
318
- // What the offer takes off the unit price - the regular price of the package less its offer
319
- // price, both WITHOUT taxes - and not a discount on this line. So the regular price of one unit,
320
- // without taxes, is unitPrice + priceDiscount.
303
+ // What the offer takes off the unit price: the package's regular price less its offer price, both
304
+ // WITHOUT taxes. It is not a discount on this line (see discount). The regular price of one unit
305
+ // without taxes is unitPrice + priceDiscount.
321
306
  get priceDiscount() {
322
307
  return this._priceDiscount;
323
308
  }
@@ -341,20 +326,20 @@ export class ShoppingListItem {
341
326
  return amountFromScaledValue(pricingOf(this).regularUnitPrice);
342
327
  }
343
328
  // What one unit costs a customer: unitPrice with the taxes on it, the offer price when the
344
- // package is on offer. This is the price a customer saw when they added it, and the one to show
345
- // wherever the item is shown. Each tax is rounded, so a price with taxes is not always one that
346
- // can be reached; when it is not, this is the nearest that can, which differs from what the
347
- // catalog said by a thousandth or so (Java ShoppingListItem.setDetailsFromTaxInclusivePrices).
329
+ // package is on offer. It is the price the customer saw when adding it, and the one to show
330
+ // wherever the item is shown. Each tax is rounded, so not every price with taxes can be reached.
331
+ // When it cannot, this is the nearest that can, which differs from the catalog's by a thousandth
332
+ // or so (Java ShoppingListItem.setDetailsFromTaxInclusivePrices).
348
333
  get unitPriceWithTaxes() {
349
334
  return amountFromScaledValue(pricingOf(this).unitPriceWithTaxes);
350
335
  }
351
- // The same for the regular price of one unit, which is what is struck through when this line is
352
- // on offer (priceDiscount greater than zero)
336
+ // The same for the regular price of one unit, struck through when this line is on offer
337
+ // (priceDiscount greater than zero)
353
338
  get regularUnitPriceWithTaxes() {
354
339
  return amountFromScaledValue(pricingOf(this).regularUnitPriceWithTaxes);
355
340
  }
356
- // What this line costs before its taxes and before its discount - the tax base: the unit price
357
- // multiplied by the quantity, rounded to a whole amount as Java does
341
+ // What this line costs before its taxes and discount, the tax base: the unit price times the
342
+ // quantity, rounded to a whole amount as Java does
358
343
  get amount() {
359
344
  return amountFromScaledValue(pricingOf(this).taxBase);
360
345
  }
@@ -367,19 +352,19 @@ export class ShoppingListItem {
367
352
  get totalTax() {
368
353
  return amountFromScaledValue(pricingOf(this).totalTax);
369
354
  }
370
- // What this line adds to the list: what it costs, plus the tax on it, less its discount, and not
371
- // less than zero. This is the definition ShoppingListItem.proto gives, and it includes taxes.
355
+ // What this line adds to the list: its cost plus the tax on it, less its discount, and not less
356
+ // than zero. This is the definition in ShoppingListItem.proto, and it includes taxes.
372
357
  get finalPrice() {
373
358
  return amountFromScaledValue(pricingOf(this).finalPrice);
374
359
  }
375
- // What this line would add at the regular price of the package, taxes included, with none of its
376
- // own discount taken off: the price a customer would have paid had nothing been on offer. Strike
377
- // it through beside finalPrice when it is greater (Java calculateFinalPriceWithoutDiscounts).
360
+ // What this line would add at the package's regular price, taxes included, with none of its own
361
+ // discount taken off: what a customer would have paid had nothing been on offer. Strike it
362
+ // through beside finalPrice when it is greater (Java calculateFinalPriceWithoutDiscounts).
378
363
  get finalPriceWithoutDiscounts() {
379
364
  return amountFromScaledValue(pricingOf(this).finalPriceWithoutDiscounts);
380
365
  }
381
- // True when this line costs less than it would at the regular price with none of its discount:
382
- // finalPriceWithoutDiscounts is then worth striking through beside finalPrice
366
+ // True when this line costs less than at the regular price with none of its discount, so
367
+ // finalPriceWithoutDiscounts is worth striking through beside finalPrice
383
368
  get isDiscounted() {
384
369
  const pricing = pricingOf(this);
385
370
  return pricing.finalPriceWithoutDiscounts > pricing.finalPrice;
@@ -408,8 +393,8 @@ export var ItemChange;
408
393
  // ShoppingList.deletedItems.
409
394
  ItemChange[ItemChange["REMOVED"] = 3] = "REMOVED";
410
395
  })(ItemChange || (ItemChange = {}));
411
- // Kept beside the item rather than in it: an item is a value, and two equal ones should compare
412
- // equal whether or not either has been priced yet.
396
+ // Kept beside the item so it stays a plain value: two equal items compare equal whether or not
397
+ // either has been priced.
413
398
  const PRICINGS = new WeakMap();
414
399
  const DERIVED = new WeakMap();
415
400
  // The taxes of a list by type, which only a list whose items can be priced has
@@ -444,23 +429,20 @@ function pricingOf(item) {
444
429
  return created;
445
430
  }
446
431
  // Throws an InvalidArgumentException unless every amount the item works out is one the protocol can
447
- // carry. CartEditor calls it before it keeps a line; everything else finds out when it first reads
448
- // a price off the item.
432
+ // carry. CartEditor calls it before keeping a line. Anything else finds out when it first reads a
433
+ // price off the item.
449
434
  export function validatePricing(item) {
450
435
  pricingOf(item);
451
436
  }
452
- // Java Transaction.validateAmount, which refuses an amount of MAX_AMOUNT or more, applied where Java
453
- // applies it to an item: each of its taxes and its final price when it works them out
454
- // (recalculate), and the results of calculateFinalPriceWithoutDiscounts, calculateUnitPriceWithTaxes
455
- // and calculateUnitRegularPriceWithTaxes. They are checked together, once, where the item is priced,
456
- // so that however it was made - a line of a cart being edited, or one that came from the wire - it
457
- // answers as Java does and no number that is not an amount of the protocol is ever handed out.
458
- //
459
- // They are not independent of each other. finalPriceWithoutDiscounts is what the item costs at its
460
- // regular price, which is never less than its offer price, and each tax and the final price (a
461
- // discount comes off that alone) are parts of it; likewise the regular unit price with taxes is
462
- // never less than the unit price with taxes. So no tax and no final price is over the limit unless
463
- // that cost is, which the vectors of test/JavaVectors.ts show item by item.
437
+ // Java Transaction.validateAmount refuses an amount of MAX_AMOUNT or more. This applies it to the
438
+ // amounts Java checks on an item: each tax and the final price (recalculate), and the results of
439
+ // calculateFinalPriceWithoutDiscounts, calculateUnitPriceWithTaxes and
440
+ // calculateUnitRegularPriceWithTaxes. They are checked together, once, where the item is priced, so
441
+ // an item answers as Java does however it was made (a cart line or the wire) and hands out no
442
+ // number the protocol cannot carry.
443
+ // The amounts are not independent. finalPriceWithoutDiscounts is the cost at the regular price,
444
+ // never less than the offer price, and each tax and the final price are parts of it. So none of
445
+ // them is over the limit unless that cost is (test/JavaVectors.ts shows this item by item).
464
446
  function validateAmounts(pricing) {
465
447
  const amounts = [
466
448
  ...pricing.taxes.map((tax) => ["tax", tax]),
@@ -473,20 +455,20 @@ function validateAmounts(pricing) {
473
455
  if (amount >= MAX_SCALED_AMOUNT)
474
456
  throw new InvalidArgumentException(`The ${name} of an item, ${amountFromScaledValue(amount)}, is more than the ${amountFromScaledValue(MAX_SCALED_AMOUNT - 1n)} the protocol can carry`);
475
457
  }
476
- // A scaled amount multiplied by a quantity, rounded to a whole amount, to the letter of Java:
458
+ // A scaled amount times a quantity, rounded to a whole amount as Java does:
477
459
  // Math.round(((double) amount) * NumberHelper.intToFloat(scaledQuantity, QUANTITY_FACTOR)). The
478
- // quantity is a FLOAT there, so it is one here - fround - and a quantity such as 0.7 is not quite
479
- // 0.7 in either, which matters only where the product lands exactly on a half.
460
+ // quantity is a FLOAT there, so it is one here (fround), and a quantity like 0.7 is not quite 0.7
461
+ // in either. That matters only where the product lands on a half.
480
462
  function scaledProduct(scaledAmount, quantity) {
481
463
  const scaledQuantity = quantityToScaledValue(quantity);
482
464
  const floatQuantity = Math.fround(Math.fround(scaledQuantity) / proto_Constants.QUANTITY_FACTOR);
483
465
  const product = Math.round(Number(scaledAmount) * floatQuantity);
484
466
  return Number.isFinite(product) ? BigInt(product) : 0n;
485
467
  }
486
- // A copy in the order of the list. Java sorts the existing items by shoppingListOrder before it
487
- // shows a list (ShoppingListAdapter), and an order arrives with its items in the order of their
488
- // package ids. The removed items are in package id order in the merchant app, which has no better
489
- // order for them; this library puts them in the order they had, which is the customer's.
468
+ // A copy in the order of the list. Java sorts the existing items by shoppingListOrder before
469
+ // showing a list (ShoppingListAdapter), and an order arrives with its items in package id order.
470
+ // The merchant app shows the removed items in package id order, having no better one. This library
471
+ // uses the order they had, which is the customer's.
490
472
  function sortedByOrder(items) {
491
473
  return [...items].sort((left, right) => left.shoppingListOrder - right.shoppingListOrder);
492
474
  }
@@ -11,13 +11,16 @@ export default class SyncCache {
11
11
  readBusinessCursors(containerName: string): Promise<SyncState>;
12
12
  writeBusiness(containerName: string, response: proto_BusinessSyncOutResponse): Promise<number>;
13
13
  storeBusiness(containerName: string, response: proto_BusinessSyncOutResponse): Promise<number>;
14
+ readDeletedBusiness(containerName: string): Promise<proto_BusinessSyncOutResponse | null>;
14
15
  readProfile(containerName: string): Promise<proto_ProfileSyncOutResponse | null>;
16
+ readDeletedProfile(containerName: string): Promise<proto_ProfileSyncOutResponse | null>;
15
17
  readProfileCursors(containerName: string): Promise<SyncState>;
16
18
  writeProfile(containerName: string, response: proto_ProfileSyncOutResponse): Promise<number>;
17
19
  storeProfile(containerName: string, response: proto_ProfileSyncOutResponse): Promise<number>;
18
20
  readAuthTokens(containerName: string): Promise<AuthTokens | null>;
19
21
  writeAuthTokens(containerName: string, authTokens: AuthTokens): Promise<void>;
20
22
  readOrders(containerName: string): Promise<ReadonlyArray<proto_Message>>;
23
+ readDeletedOrders(containerName: string): Promise<ReadonlyArray<proto_Message>>;
21
24
  readMessageUpdateNumber(containerName: string): Promise<bigint>;
22
25
  writeOrder(containerName: string, order: proto_Message): Promise<number>;
23
26
  readPartyPreferences(containerName: string): Promise<proto_Message | null>;