@tallyui/pos 2.0.0 → 3.0.0-next.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/index.d.ts +321 -61
- package/dist/index.js +1391 -185
- package/package.json +8 -8
- package/src/index.ts +15 -7
- package/src/order/index.ts +3 -0
- package/src/order/order-builder.ts +23 -7
- package/src/order/order-manager.ts +3 -1
- package/src/order/tax-figures.ts +23 -0
- package/src/order/types.ts +9 -3
- package/src/outbox/backend-not-found.ts +27 -0
- package/src/outbox/http-transport.ts +5 -4
- package/src/outbox/index.ts +2 -0
- package/src/outbox/logger.ts +3 -0
- package/src/outbox/order-outbox.ts +331 -27
- package/src/outbox/register-outbox.ts +250 -0
- package/src/outbox/types.test-d.ts +22 -0
- package/src/outbox/types.ts +17 -3
- package/src/outbox/use-order-outbox.ts +20 -6
- package/src/pos-order/__fixtures__/order-create-v3.json +97 -0
- package/src/pos-order/command.ts +99 -11
- package/src/pos-order/finalize.ts +145 -7
- package/src/pos-order/index.ts +2 -2
- package/src/pos-order/needs-attention.ts +9 -4
- package/src/pos-order/open.ts +100 -37
- package/src/pos-order/schema.ts +35 -5
- package/src/pos-order/types.ts +31 -5
- package/src/product/search-products.ts +5 -2
- package/src/receipt/build-receipt-data.ts +5 -3
- package/src/receipt/types.ts +1 -0
- package/src/register/closure-document.ts +7 -0
- package/src/register/index.ts +5 -3
- package/src/register/movement-input.ts +1 -1
- package/src/register/register-commands.ts +147 -0
- package/src/register/register-document.ts +26 -1
- package/src/register/session-store.ts +41 -12
- package/src/register/use-register-session.ts +46 -2
- package/src/sale/cart.ts +1 -0
- package/src/sale/use-sale.ts +97 -40
- package/src/store-settings/map-store-settings.ts +7 -2
- package/src/store-settings/use-store-settings.ts +22 -7
- package/src/tax/exact.ts +74 -11
- package/src/tax/index.ts +1 -1
- package/src/tax/tax-provider.tsx +26 -4
- package/src/tax/types.ts +6 -0
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
2
|
import { ReactNode } from 'react';
|
|
3
|
-
import { Money, TallyConnector, SyncContext, StoreSettingsChoice, StoreSettings, StoreSettingsChoices, ProductTraits, PaymentMethodKind, CommandServerRefs, CommandWarning, CommandError, ServerCapabilities,
|
|
3
|
+
import { Money, TaxRounding, TallyConnector, SyncContext, StoreSettingsChoice, StoreSettings, StoreSettingsChoices, ProductTraits, PaymentMethodKind, CommandServerRefs, CommandWarning, CommandError, ServerCapabilities, OrderCreateEnvelope, RegisterCommandType, RegisterCommandResult, VariantSummary, CommandResult, AnyCommandEnvelope, CommandEnvelope, OrderCreatePayload, RegisterCommandEnvelope } from '@tallyui/core';
|
|
4
4
|
export { getProductStock, withStockOverlay } from '@tallyui/core';
|
|
5
5
|
export { countFresh, readFresh, watchFresh } from '@tallyui/core/rxdb';
|
|
6
6
|
import * as rxdb from 'rxdb';
|
|
@@ -66,8 +66,10 @@ declare function ratePpmFromPercent(percent: number | string): number;
|
|
|
66
66
|
* Throws RangeError unless amount and rate are safe integers and rate is non-negative.
|
|
67
67
|
*/
|
|
68
68
|
declare function taxMicros(amountMinor: number, ratePpm: number, pricesIncludeTax: boolean): bigint;
|
|
69
|
-
/**
|
|
70
|
-
|
|
69
|
+
/** #287: `half_up` is Math.round's rule, Vendure's DefaultMoneyStrategy (default-money-strategy.js:19-21). */
|
|
70
|
+
type RoundingMode = 'half_away_from_zero' | 'half_up';
|
|
71
|
+
/** Rounds micro-minor-units to integer minor units, half away from zero unless `mode` says otherwise. */
|
|
72
|
+
declare function roundMicrosToMinor(micros: bigint, mode?: RoundingMode): number;
|
|
71
73
|
interface TaxLineInput {
|
|
72
74
|
unitPriceMinor: number;
|
|
73
75
|
quantity: number;
|
|
@@ -93,6 +95,16 @@ interface RateTaxLine {
|
|
|
93
95
|
netMinor: number;
|
|
94
96
|
amountMinor: number;
|
|
95
97
|
}
|
|
98
|
+
type TaxedLine = {
|
|
99
|
+
netMinor: number;
|
|
100
|
+
discountMinor?: number;
|
|
101
|
+
taxInclusive: boolean;
|
|
102
|
+
taxLines: readonly {
|
|
103
|
+
code?: string;
|
|
104
|
+
ratePpm: number;
|
|
105
|
+
taxMicros: string;
|
|
106
|
+
}[];
|
|
107
|
+
};
|
|
96
108
|
/**
|
|
97
109
|
* Groups each line's stacked tax rates (ADR-040: each independently taxes the line's full
|
|
98
110
|
* tax-free base) by `code`+`ratePpm`, floors each group's exact tax to minor units, then
|
|
@@ -100,17 +112,10 @@ interface RateTaxLine {
|
|
|
100
112
|
* so the rates sum to exactly `orderTaxMinor`. A line's `netMinor` is in its OWN tax mode
|
|
101
113
|
* (`taxInclusive`): an inclusive line's net already contains its tax, so its tax-free base is
|
|
102
114
|
* `netMinor − roundMicrosToMinor(Σ its taxMicros)`; an exclusive line's base is `netMinor` as is.
|
|
103
|
-
* Shared by a receipt's tax summary and a Z report's per-rate breakdown.
|
|
115
|
+
* Shared by a receipt's tax summary and a Z report's per-rate breakdown. That is `per_order`'s split; a `per_line_items` or
|
|
116
|
+
* `per_rate_group_items` order's rows are `roundedTaxByRate`'s (#287).
|
|
104
117
|
*/
|
|
105
|
-
declare function taxLinesByRate(lines: readonly
|
|
106
|
-
netMinor: number;
|
|
107
|
-
taxInclusive: boolean;
|
|
108
|
-
taxLines: readonly {
|
|
109
|
-
code?: string;
|
|
110
|
-
ratePpm: number;
|
|
111
|
-
taxMicros: string;
|
|
112
|
-
}[];
|
|
113
|
-
}[], orderTaxMinor: number, taxLabels?: Record<number, string>): RateTaxLine[];
|
|
118
|
+
declare function taxLinesByRate(lines: readonly TaxedLine[], orderTaxMinor: number, taxLabels?: Record<number, string>, rounding?: TaxRounding): RateTaxLine[];
|
|
114
119
|
|
|
115
120
|
/** Tax rates as integer parts per million (19% = 190000). */
|
|
116
121
|
type TaxRateMap = {
|
|
@@ -120,15 +125,24 @@ type TaxRateMap = {
|
|
|
120
125
|
interface TaxContext {
|
|
121
126
|
/** Tax rate for a tax class as integer parts per million (19% = 190000); the default class when omitted or unknown. */
|
|
122
127
|
getTaxRatePpm(taxClass?: string): number;
|
|
128
|
+
/** The backend's name for that class's rate, when mapped: a `per_rate_group_items` store groups by name and value (#287). */
|
|
129
|
+
getTaxRateCode?(taxClass?: string): string | undefined;
|
|
123
130
|
pricesIncludeTax: boolean;
|
|
131
|
+
/** The store's tax rounding strategy (#287); absent means `per_order`, half away from zero. */
|
|
132
|
+
rounding?: TaxRounding;
|
|
124
133
|
}
|
|
125
134
|
|
|
135
|
+
declare const taxLogger: Logger;
|
|
126
136
|
interface TaxProviderProps {
|
|
127
137
|
ratesPpm: Record<string, number>;
|
|
128
138
|
pricesIncludeTax: boolean;
|
|
139
|
+
/** `ServerCapabilities.taxRounding` (#287); a change restarts an idle sale under it. */
|
|
140
|
+
rounding?: TaxRounding;
|
|
141
|
+
/** Tax class → the backend's rate name, for `per_rate_group_items`'s grouping (#287). */
|
|
142
|
+
rateCodes?: Record<string, string>;
|
|
129
143
|
children: ReactNode;
|
|
130
144
|
}
|
|
131
|
-
declare function TaxProvider({ ratesPpm, pricesIncludeTax, children }: TaxProviderProps): react_jsx_runtime.JSX.Element;
|
|
145
|
+
declare function TaxProvider({ ratesPpm, pricesIncludeTax, rounding, rateCodes, children }: TaxProviderProps): react_jsx_runtime.JSX.Element;
|
|
132
146
|
declare function useTax(): TaxContext;
|
|
133
147
|
|
|
134
148
|
type StoreSettingsResolution = {
|
|
@@ -162,7 +176,11 @@ declare function resolveStoreSettings(options: ResolveStoreSettingsOptions, choi
|
|
|
162
176
|
|
|
163
177
|
/** The replication's context with the settings' opaque pricing context; the key is left out when the settings have none. */
|
|
164
178
|
declare function withPricingContext(context: SyncContext, settings: StoreSettings): SyncContext;
|
|
165
|
-
/**
|
|
179
|
+
/**
|
|
180
|
+
* `<TaxProvider>`'s props from the same settings, so its tax inclusivity and rounding match the store's. `custom`
|
|
181
|
+
* rounding passes none, the default (#287). An explicit `rounding` prop after the spread still wins, since JSX
|
|
182
|
+
* applies props in order; so `<TaxProvider {...taxProviderProps(s)} rounding={undefined}>` wipes the store's rounding.
|
|
183
|
+
*/
|
|
166
184
|
declare function taxProviderProps(settings: StoreSettings): Omit<TaxProviderProps, 'children'>;
|
|
167
185
|
|
|
168
186
|
type StoreSettingsState = {
|
|
@@ -187,6 +205,9 @@ type StoreSettingsState = {
|
|
|
187
205
|
* Resolves the store settings on mount, and again when `connector` or `context` changes by
|
|
188
206
|
* identity (a store switch), so memoise both; the last request wins. `loadChoice` and
|
|
189
207
|
* `saveChoice` are read when a request starts, so inline functions don't trigger a re-resolve.
|
|
208
|
+
* The ready settings carry the store's `taxRounding` from the capabilities, so `taxProviderProps` passes it on (#324).
|
|
209
|
+
* The capabilities are read beside the settings, before `ready`, so each resolve emits the settings once, with the
|
|
210
|
+
* rounding already known: no later change of `settings` holds a sale.
|
|
190
211
|
*
|
|
191
212
|
* ```tsx
|
|
192
213
|
* const store = useStoreSettings({ connector, context, loadChoice, saveChoice });
|
|
@@ -225,6 +246,8 @@ interface Order {
|
|
|
225
246
|
note: string;
|
|
226
247
|
currency: string;
|
|
227
248
|
pricesIncludeTax: boolean;
|
|
249
|
+
/** The store's tax rounding strategy the figures were computed with (#287), from the sale's tax context; absent is per_order. */
|
|
250
|
+
taxRounding?: TaxRounding;
|
|
228
251
|
subtotalMinor: number;
|
|
229
252
|
discountMinor: number;
|
|
230
253
|
taxMinor: number;
|
|
@@ -236,6 +259,12 @@ interface Order {
|
|
|
236
259
|
createdAt: string;
|
|
237
260
|
updatedAt: string;
|
|
238
261
|
}
|
|
262
|
+
/** An Order as it was stored and sent, whose customer id may have been left out. */
|
|
263
|
+
type SentOrder = Omit<Order, 'customer'> & {
|
|
264
|
+
customer: (Omit<CustomerSummary, 'id'> & {
|
|
265
|
+
id?: string;
|
|
266
|
+
}) | null;
|
|
267
|
+
};
|
|
239
268
|
/**
|
|
240
269
|
* Figures for showing the cart or a receipt in the store's display mode (ADR-063); never sent to the server.
|
|
241
270
|
* A parked order's saved draft may carry them; they are recomputed from the lines on resume, so never authoritative.
|
|
@@ -326,6 +355,7 @@ interface AddLineInput {
|
|
|
326
355
|
code?: string;
|
|
327
356
|
ratePpm: number;
|
|
328
357
|
}>;
|
|
358
|
+
taxClass?: string;
|
|
329
359
|
}
|
|
330
360
|
|
|
331
361
|
interface OrderBuilderOptions {
|
|
@@ -354,6 +384,29 @@ interface OrderBuilder {
|
|
|
354
384
|
}
|
|
355
385
|
declare function createOrderBuilder(options: OrderBuilderOptions): OrderBuilder;
|
|
356
386
|
|
|
387
|
+
/** A sent line; `discountMinor` is its `lines[].discountMinor` (line discounts + its order share), in its own mode. */
|
|
388
|
+
type BasketLine = {
|
|
389
|
+
unitPriceMinor: number;
|
|
390
|
+
quantity: number;
|
|
391
|
+
discountMinor?: number;
|
|
392
|
+
taxInclusive: boolean;
|
|
393
|
+
taxLines: Array<{
|
|
394
|
+
code?: string;
|
|
395
|
+
ratePpm: number;
|
|
396
|
+
}>;
|
|
397
|
+
};
|
|
398
|
+
/**
|
|
399
|
+
* The till's settlement figures for a sent basket under a store's strategy (#287), computed exactly as a sale computes
|
|
400
|
+
* them, so a backend can score its own orders against the till (vendurepos/app#38). Each discountMinor is applied as a
|
|
401
|
+
* fixed line discount in the line's own mode.
|
|
402
|
+
*/
|
|
403
|
+
declare function taxFiguresForBasket(currency: string, pricesIncludeTax: boolean, lines: readonly BasketLine[], rounding?: TaxRounding): {
|
|
404
|
+
subtotalMinor: number;
|
|
405
|
+
taxMinor: number;
|
|
406
|
+
totalMinor: number;
|
|
407
|
+
taxByRate: RateTaxLine[];
|
|
408
|
+
};
|
|
409
|
+
|
|
357
410
|
/**
|
|
358
411
|
* Splits an order discount across lines in proportion to each line's pre-order-discount amount,
|
|
359
412
|
* in its own tax mode (ADR-062), with largest-remainder rounding to the minor unit.
|
|
@@ -410,6 +463,7 @@ interface ReceiptData {
|
|
|
410
463
|
orderNumber: string;
|
|
411
464
|
date: string;
|
|
412
465
|
cashier?: string;
|
|
466
|
+
customer?: string;
|
|
413
467
|
register?: string;
|
|
414
468
|
};
|
|
415
469
|
lineItems: ReceiptLineItem[];
|
|
@@ -451,7 +505,7 @@ interface ReceiptConfig {
|
|
|
451
505
|
taxLabels?: Record<number, string>;
|
|
452
506
|
}
|
|
453
507
|
|
|
454
|
-
declare function buildReceiptData(order:
|
|
508
|
+
declare function buildReceiptData(order: SentOrder, config: ReceiptConfig): ReceiptData;
|
|
455
509
|
|
|
456
510
|
/**
|
|
457
511
|
* Filters product documents by a cashier's search term, through traits, so
|
|
@@ -474,6 +528,22 @@ declare function stockOverlay$(collection: RxCollection): Observable<Map<string,
|
|
|
474
528
|
declare function stockOverlayAsOf$(collection: RxCollection): Observable<string | undefined>;
|
|
475
529
|
|
|
476
530
|
type PosOrderSyncStatus = 'pending' | 'applied' | 'rejected';
|
|
531
|
+
type PosOrderLocalWarning = {
|
|
532
|
+
code: 'customer_omitted';
|
|
533
|
+
field: 'email' | 'id';
|
|
534
|
+
} | {
|
|
535
|
+
code: 'payment_reference_dropped';
|
|
536
|
+
paymentId: string;
|
|
537
|
+
};
|
|
538
|
+
/** The outbox's server-failure state for a pending order, kept so a restart restores it. */
|
|
539
|
+
interface PosOrderServerFailures {
|
|
540
|
+
/** The stuck clock's start in ms: the outbox's virtual start, which leaves out offline gaps. */
|
|
541
|
+
since: number;
|
|
542
|
+
/** The latest server-answered failure reason. */
|
|
543
|
+
reason: string;
|
|
544
|
+
/** The order failed when sent alone (as a probe or while isolated), so it is retried alone. */
|
|
545
|
+
isolated: boolean;
|
|
546
|
+
}
|
|
477
547
|
interface PosOrderLine {
|
|
478
548
|
id: string;
|
|
479
549
|
productId: string;
|
|
@@ -521,21 +591,28 @@ interface PosOrder {
|
|
|
521
591
|
registerId?: string;
|
|
522
592
|
/**
|
|
523
593
|
* The register session the sale was taken in (ADR-032): its closure counts this order.
|
|
524
|
-
*
|
|
594
|
+
* Sent at `order.create` version 3 as the payload's `sessionId` (the stamped or late session, one field).
|
|
525
595
|
*/
|
|
526
596
|
sessionId?: string;
|
|
527
597
|
/**
|
|
528
598
|
* Set only by `useSale`'s late-sale path (ADR-032): the session the sale was taken for, which
|
|
529
599
|
* refused the stamp after the money was taken. Such an order has no `sessionId`, so no closure
|
|
530
|
-
* counts it.
|
|
600
|
+
* counts it. Sent at `order.create` version 3 as the payload's `sessionId` (the stamped or late session, one field).
|
|
531
601
|
*/
|
|
532
602
|
lateSessionId?: string;
|
|
533
|
-
/**
|
|
603
|
+
/**
|
|
604
|
+
* The order.create version every attempt under this `commandId` goes out at: the outbox records it before the first
|
|
605
|
+
* send, and lowers it only on a downgrade (ADR-065 amendment). Absent means not sent yet (or requeued).
|
|
606
|
+
*/
|
|
607
|
+
sentVersion?: 1 | 2 | 3 | 4;
|
|
608
|
+
/** The version first tried, before the downgrade (the order's audit). */
|
|
609
|
+
downgradedFrom?: 1 | 2 | 3 | 4;
|
|
610
|
+
/** ADR-065: the receipt's display figures, in integer minor units of `currency` at `exponent`. */
|
|
534
611
|
display?: DisplayTotals & {
|
|
535
612
|
currency: string;
|
|
536
613
|
exponent: number;
|
|
537
614
|
};
|
|
538
|
-
/** ADR-065: tax by rate, named as `taxLinesByRate` names them (`amountMinor` is the tax).
|
|
615
|
+
/** ADR-065: tax by rate, named as `taxLinesByRate` names them (`amountMinor` is the tax). */
|
|
539
616
|
taxByRate?: Array<{
|
|
540
617
|
ratePpm: number;
|
|
541
618
|
code?: string;
|
|
@@ -544,11 +621,18 @@ interface PosOrder {
|
|
|
544
621
|
amountMinor: number;
|
|
545
622
|
grossMinor: number;
|
|
546
623
|
}>;
|
|
624
|
+
/**
|
|
625
|
+
* The tax rounding the figures were computed with (#287), frozen at finalize; the till's own record, never sent.
|
|
626
|
+
* Version 6's migration sets the default on every older row.
|
|
627
|
+
*/
|
|
628
|
+
taxRounding: TaxRounding;
|
|
547
629
|
cashierRef?: string;
|
|
548
630
|
syncStatus: PosOrderSyncStatus;
|
|
549
631
|
commandId: string;
|
|
550
632
|
serverRefs?: CommandServerRefs;
|
|
551
633
|
warnings?: CommandWarning[];
|
|
634
|
+
localWarnings?: PosOrderLocalWarning[];
|
|
635
|
+
serverFailures?: PosOrderServerFailures;
|
|
552
636
|
error?: CommandError;
|
|
553
637
|
updatedAt: string;
|
|
554
638
|
}
|
|
@@ -557,21 +641,38 @@ interface PosOrder {
|
|
|
557
641
|
declare function uuidv7(now?: number, randomBytes?: (n: number) => Uint8Array): string;
|
|
558
642
|
|
|
559
643
|
interface FinalizeOptions {
|
|
644
|
+
localWarnings?: PosOrderLocalWarning[];
|
|
560
645
|
registerId?: string;
|
|
561
646
|
cashierRef?: string;
|
|
562
647
|
now?: Date;
|
|
648
|
+
/** Tests only: replaces uuidv7. Every id it returns must be at most 255 characters with no NUL (order.create's bound). */
|
|
563
649
|
newId?: () => string;
|
|
564
650
|
/** The store's `order.create` capability (ADR-062); `undefined` is treated as 1. */
|
|
565
651
|
capabilities?: ServerCapabilities;
|
|
566
652
|
}
|
|
567
|
-
/**
|
|
653
|
+
/**
|
|
654
|
+
* Turns a fully paid builder Order into a pending PosOrder without mutating it. The PosOrder holds the sent form,
|
|
655
|
+
* frozen: line names and v3 discount labels are cut to PAYLOAD_STRING_MAX with NUL stripped (`cutText`), and a
|
|
656
|
+
* customer email or id the shape check would refuse is left out, so `toOrderCreateEnvelope` sends it unchanged.
|
|
657
|
+
*/
|
|
568
658
|
declare function finalizeOrder(order: Order, options?: FinalizeOptions): PosOrder;
|
|
569
659
|
|
|
660
|
+
declare class UnsupportedOrderVersionError extends Error {
|
|
661
|
+
readonly needed: number;
|
|
662
|
+
readonly supported: number;
|
|
663
|
+
readonly code = "UNSUPPORTED_ORDER_VERSION";
|
|
664
|
+
constructor(needed: number, supported: number);
|
|
665
|
+
}
|
|
570
666
|
/**
|
|
571
|
-
* Builds the ADR-038 order.create envelope for a PosOrder.
|
|
572
|
-
*
|
|
667
|
+
* Builds the ADR-038 order.create envelope for a PosOrder. It sends the stored order unchanged, so every resend of
|
|
668
|
+
* an order is byte-identical, and an order stored by an older till goes out exactly as that till sent it:
|
|
669
|
+
* `finalizeOrder` freezes the sent form (names and labels cut, an unsendable customer email or id left out).
|
|
670
|
+
* ADR-065's figures make version 3; otherwise a discounted order is version 2 (ADR-062), else version 1, byte-identical.
|
|
671
|
+
* A version-3 order goes as version 4 (#286) only when `maxVersion` is at least 4 and its `sentVersion` doesn't cap it.
|
|
573
672
|
*/
|
|
574
|
-
declare function toOrderCreateEnvelope(order: PosOrder, deviceId: string, attempt?: number
|
|
673
|
+
declare function toOrderCreateEnvelope(order: PosOrder, deviceId: string, attempt?: number, options?: {
|
|
674
|
+
maxVersion?: number;
|
|
675
|
+
}): OrderCreateEnvelope;
|
|
575
676
|
|
|
576
677
|
/**
|
|
577
678
|
* Version 1 adds the optional `sessionId` (ADR-032); nothing else changed from version 0.
|
|
@@ -579,6 +680,14 @@ declare function toOrderCreateEnvelope(order: PosOrder, deviceId: string, attemp
|
|
|
579
680
|
* sale), and ADR-065's `display` and `taxByRate`, declared ahead of the job that writes them so a
|
|
580
681
|
* till migrates once. Create the collection with `posOrderCollection()`, never with this schema
|
|
581
682
|
* alone: RxDB refuses a version above 0 without its migration strategies.
|
|
683
|
+
* Version 3 adds an index on `sessionId` (with its `maxLength`), and the optional `sentVersion` and `downgradedFrom` (the outbox's version fallback), and changes nothing else.
|
|
684
|
+
* Version 4 adds the optional `localWarnings` and `serverFailures` (the outbox's stuck clock start, latest reason and
|
|
685
|
+
* isolation, restored when an outbox starts), and changes nothing else.
|
|
686
|
+
* Version 5 lets `sentVersion` and `downgradedFrom` be 4 (order.create version 4, #286), and changes nothing else. Its
|
|
687
|
+
* migration sets a row's missing `sentVersion` to its content version, the version any earlier attempt went out at.
|
|
688
|
+
* Like version 4 (ADR-069), it is one-way: an older build shows no orders.
|
|
689
|
+
* Version 6 adds `taxRounding`, the strategy the sale's figures were computed with (#287; never sent), and changes
|
|
690
|
+
* nothing else. Its migration sets it on every older row. It is one-way like version 5.
|
|
582
691
|
*/
|
|
583
692
|
declare const posOrderSchema: RxJsonSchema<PosOrder>;
|
|
584
693
|
/**
|
|
@@ -604,8 +713,9 @@ declare function posOrderCollection(): {
|
|
|
604
713
|
declare const posOrdersLogger: Logger;
|
|
605
714
|
/**
|
|
606
715
|
* `addPosOrderCollection` stopped, before any further write, because its database is closing: it
|
|
607
|
-
* was called once `close()` had begun,
|
|
608
|
-
* `POS_ORDER_MIGRATION_CLOSE_WAIT_MS`). No order is lost: reopen the
|
|
716
|
+
* was called once `close()` had begun, the close cancelled its migration (RxDB 17.5), or the close
|
|
717
|
+
* stopped waiting for it (after `POS_ORDER_MIGRATION_CLOSE_WAIT_MS`). No order is lost: reopen the
|
|
718
|
+
* database and call it again.
|
|
609
719
|
*/
|
|
610
720
|
declare class PosOrderOpenClosedError extends Error {
|
|
611
721
|
readonly databaseName: string;
|
|
@@ -618,16 +728,20 @@ declare class PosOrderOpenClosedError extends Error {
|
|
|
618
728
|
* stopped and closes the collection, so the app can surface the error and open again; it never
|
|
619
729
|
* deletes an order.
|
|
620
730
|
*
|
|
621
|
-
* RxDB
|
|
731
|
+
* RxDB 17.5.0 trusts the status its last migration stored: a leftover `ERROR` rejects
|
|
622
732
|
* `migratePromise` at once while the migration keeps running (a close then interrupts it), and a
|
|
623
733
|
* `DONE` left before a rollback resolves it before the older version's new orders have moved. So the
|
|
624
|
-
* collection is added without `autoMigrate
|
|
625
|
-
* (
|
|
734
|
+
* collection is added without `autoMigrate` and the migration is started and awaited directly: RxDB
|
|
735
|
+
* 17.5's `startMigration()` ignores a leftover status (only `migratePromise()` trusts a leftover
|
|
736
|
+
* `DONE`), which lets a rolled-back or interrupted run migrate again and recovers its orders. The
|
|
737
|
+
* status is reset first so its record describes the new run, and a failed run's checkpoint is
|
|
738
|
+
* removed (RxDB bug 1); never an order or its storage.
|
|
626
739
|
*
|
|
627
|
-
* A close waits for the
|
|
628
|
-
* database's close has begun,
|
|
629
|
-
*
|
|
630
|
-
*
|
|
740
|
+
* A close waits for the open, up to `POS_ORDER_MIGRATION_CLOSE_WAIT_MS`, and cancels a running
|
|
741
|
+
* migration (RxDB 17.5). Called once the database's close has begun, when the close cancels its
|
|
742
|
+
* migration, or when the close stops waiting, it rejects with `PosOrderOpenClosedError`
|
|
743
|
+
* (`code: 'POS_ORDER_OPEN_CLOSED'`) before any further write: reopen and call it again. Any other
|
|
744
|
+
* rejection (DM4, a storage error) is a real failure.
|
|
631
745
|
*
|
|
632
746
|
* `closeWaitMs` is for tests only; the first open on a database sets it for that database's close.
|
|
633
747
|
*/
|
|
@@ -644,10 +758,14 @@ declare function getDeviceId(storage: {
|
|
|
644
758
|
} | null, key: string): string;
|
|
645
759
|
|
|
646
760
|
/**
|
|
647
|
-
* The orders a cashier must look at: rejected, applied with
|
|
648
|
-
* closed (`lateSessionId
|
|
761
|
+
* The orders a cashier must look at: rejected, applied with a known warning, taken after their
|
|
762
|
+
* session closed (`lateSessionId`) or with local warnings (whatever the sync status), or pending with a `commandId` in
|
|
763
|
+
* `stuckCommandIds` (the outbox's `OutboxState.stuck`: the store keeps failing it), newest first.
|
|
764
|
+
* A warning code this till version doesn't know is not shown here (`knownWarnings`). Never changes `orders`.
|
|
649
765
|
*/
|
|
650
|
-
declare function needsAttention(orders: PosOrder[]
|
|
766
|
+
declare function needsAttention(orders: PosOrder[], options?: {
|
|
767
|
+
stuckCommandIds?: readonly string[];
|
|
768
|
+
}): PosOrder[];
|
|
651
769
|
|
|
652
770
|
type SaleContent = Pick<PosOrder, 'totalMinor' | 'currency' | 'lines' | 'payments'>;
|
|
653
771
|
/**
|
|
@@ -877,6 +995,7 @@ type RegisterCounters = {
|
|
|
877
995
|
* same atomic write as its number, and `applied` once its period is in the perpetual totals.
|
|
878
996
|
*/
|
|
879
997
|
type RegisterBucket = Partial<RegisterCounters> & {
|
|
998
|
+
commands_since?: string;
|
|
880
999
|
closure_reservation?: {
|
|
881
1000
|
row: Closure;
|
|
882
1001
|
applied: boolean;
|
|
@@ -911,6 +1030,9 @@ declare function readBoundRegister(host: RegisterHost, storeKey: string): Promis
|
|
|
911
1030
|
id: string;
|
|
912
1031
|
name: string;
|
|
913
1032
|
} | null>;
|
|
1033
|
+
declare class RegisterIdInvalidError extends Error {
|
|
1034
|
+
constructor();
|
|
1035
|
+
}
|
|
914
1036
|
declare function bindRegister(host: RegisterHost, storeKey: string, register: {
|
|
915
1037
|
id: string | null;
|
|
916
1038
|
name: string | null;
|
|
@@ -944,10 +1066,17 @@ declare class RegisterSessionRequiredError extends Error {
|
|
|
944
1066
|
declare class RegisterSessionClosedError extends Error {
|
|
945
1067
|
constructor();
|
|
946
1068
|
}
|
|
1069
|
+
/** ADR-068 6a: movement amounts must be safe integers, positive for paid_in/paid_out and zero for no_sale. */
|
|
1070
|
+
declare class RegisterMovementAmountError extends Error {
|
|
1071
|
+
constructor(type: 'paid_in' | 'paid_out' | 'no_sale', amountMinor: number);
|
|
1072
|
+
}
|
|
1073
|
+
declare class RegisterMovementReasonError extends Error {
|
|
1074
|
+
constructor();
|
|
1075
|
+
}
|
|
947
1076
|
/**
|
|
948
1077
|
* The movement is recorded, on a session that closed while it was saved, and no closure is known
|
|
949
|
-
* to count it, so it
|
|
950
|
-
*
|
|
1078
|
+
* to count it, so it stays local on the till. The caller must not record it again; the server
|
|
1079
|
+
* refuses it once the closure exists (ADR-068 4). The message can be shown to a cashier as it is.
|
|
951
1080
|
*/
|
|
952
1081
|
declare class RegisterMovementStrandedError extends Error {
|
|
953
1082
|
readonly id: string;
|
|
@@ -975,9 +1104,9 @@ declare function openSession(sessions: RegisterSessionCollection, input: {
|
|
|
975
1104
|
day: number;
|
|
976
1105
|
};
|
|
977
1106
|
storeKey?: string | null;
|
|
978
|
-
}): Promise<rxdb.RxDocument<RegisterSession, {}>>;
|
|
979
|
-
declare const startCounting: (sessions: RegisterSessionCollection, id: string) => Promise<rxdb.RxDocument<RegisterSession, {}>>;
|
|
980
|
-
declare const backToSelling: (sessions: RegisterSessionCollection, id: string) => Promise<rxdb.RxDocument<RegisterSession, {}>>;
|
|
1107
|
+
}): Promise<rxdb.RxDocument<RegisterSession, {}, unknown>>;
|
|
1108
|
+
declare const startCounting: (sessions: RegisterSessionCollection, id: string) => Promise<rxdb.RxDocument<RegisterSession, {}, unknown>>;
|
|
1109
|
+
declare const backToSelling: (sessions: RegisterSessionCollection, id: string) => Promise<rxdb.RxDocument<RegisterSession, {}, unknown>>;
|
|
981
1110
|
/**
|
|
982
1111
|
* Closes the session with its counted tenders (minor units); `timezone` dates a session opened
|
|
983
1112
|
* without a business day. `approvedBy`, the manager who approved the count, is written in the same
|
|
@@ -988,7 +1117,7 @@ declare function closeSession(sessions: RegisterSessionCollection, id: string, i
|
|
|
988
1117
|
closedBy?: string;
|
|
989
1118
|
approvedBy?: string;
|
|
990
1119
|
timezone?: string;
|
|
991
|
-
}): Promise<rxdb.RxDocument<RegisterSession, {}>>;
|
|
1120
|
+
}): Promise<rxdb.RxDocument<RegisterSession, {}, unknown>>;
|
|
992
1121
|
/**
|
|
993
1122
|
* Records a movement on a session that is `open` or `counting`; a closed or missing one is refused.
|
|
994
1123
|
* A close that races the insert ends in `RegisterSessionClosedError` (removed) or
|
|
@@ -1000,14 +1129,14 @@ declare function recordMovement(sessions: RegisterSessionCollection, movements:
|
|
|
1000
1129
|
amountMinor: number;
|
|
1001
1130
|
reason: string;
|
|
1002
1131
|
actor: string;
|
|
1003
|
-
}): Promise<rxdb.RxDocument<CashMovement, {}>>;
|
|
1132
|
+
}): Promise<rxdb.RxDocument<CashMovement, {}, unknown>>;
|
|
1004
1133
|
/**
|
|
1005
1134
|
* Reverses a movement with a `void` row while its session is live; repeated or concurrent calls
|
|
1006
1135
|
* share one reversal. A close that races the writes ends in `RegisterMovementStrandedError` (kept;
|
|
1007
1136
|
* don't void it again). Without `closures`, nothing can prove the reversal counted, so a re-read
|
|
1008
1137
|
* that finds the session closed always ends in `RegisterMovementStrandedError`.
|
|
1009
1138
|
*/
|
|
1010
|
-
declare function voidMovement(sessions: RegisterSessionCollection, movements: CashMovementCollection, movementId: string, actor: string, closures?: ClosureCollection): Promise<rxdb.RxDocument<CashMovement, {}>>;
|
|
1139
|
+
declare function voidMovement(sessions: RegisterSessionCollection, movements: CashMovementCollection, movementId: string, actor: string, closures?: ClosureCollection): Promise<rxdb.RxDocument<CashMovement, {}, unknown>>;
|
|
1011
1140
|
/**
|
|
1012
1141
|
* Freezes a closed session's figures into its closure, once. The draft is reserved on the register
|
|
1013
1142
|
* document with its number (`mintClosureNumber`), so a failed insert or a restarted till reuses the
|
|
@@ -1039,7 +1168,7 @@ declare function writeClosure({ closures, register, storeKey, session, counted,
|
|
|
1039
1168
|
/** The app's version, stamped on the closure. */
|
|
1040
1169
|
softwareVersion: string;
|
|
1041
1170
|
timezone?: string;
|
|
1042
|
-
}): Promise<rxdb.RxDocument<Closure, {}>>;
|
|
1171
|
+
}): Promise<rxdb.RxDocument<Closure, {}, unknown>>;
|
|
1043
1172
|
|
|
1044
1173
|
/**
|
|
1045
1174
|
* Corrections layered onto a closure's recorded figures (ADR-032): late sales, late cash
|
|
@@ -1212,6 +1341,11 @@ declare function selectClosureRows(rows: readonly Closure[], scope: ClosureScope
|
|
|
1212
1341
|
declare function minorToDecimal(minor: number, exponent: number): string;
|
|
1213
1342
|
|
|
1214
1343
|
type Values = Record<string, unknown>;
|
|
1344
|
+
/**
|
|
1345
|
+
* The Z report's line when a session's sales used more than one tax rounding (#287; wording approved by the Front
|
|
1346
|
+
* desk, 2026-09-30). The closure template lives in the apps, so the document carries it ready to print.
|
|
1347
|
+
*/
|
|
1348
|
+
declare const TAX_ROUNDING_MIXED_NOTE = "This register's sales used more than one tax rounding method. Each sale's tax is as its receipt showed.";
|
|
1215
1349
|
type ClosureContext = {
|
|
1216
1350
|
store: Values;
|
|
1217
1351
|
currency: string;
|
|
@@ -1234,6 +1368,7 @@ type ClosureContext = {
|
|
|
1234
1368
|
declare function formatClosureDate(value: string | null | undefined, context: Pick<ClosureContext, 'timezone' | 'locale'>): Record<string, string>;
|
|
1235
1369
|
declare function buildClosureDocument(row: Closure, context: ClosureContext): {
|
|
1236
1370
|
closure: {
|
|
1371
|
+
tax_rounding_note?: string | undefined;
|
|
1237
1372
|
has_sales: boolean;
|
|
1238
1373
|
has_perpetual: boolean;
|
|
1239
1374
|
} & {
|
|
@@ -1344,6 +1479,7 @@ declare function buildClosureDocument(row: Closure, context: ClosureContext): {
|
|
|
1344
1479
|
};
|
|
1345
1480
|
declare function buildXReportDocument(session: RegisterSession, context: ClosureContext): {
|
|
1346
1481
|
closure: {
|
|
1482
|
+
tax_rounding_note?: string | undefined;
|
|
1347
1483
|
has_sales: boolean;
|
|
1348
1484
|
has_perpetual: boolean;
|
|
1349
1485
|
} & {
|
|
@@ -1486,6 +1622,48 @@ type RegisterFact = (Human & {
|
|
|
1486
1622
|
});
|
|
1487
1623
|
declare function recordRegisterFact(fact: RegisterFact): void;
|
|
1488
1624
|
|
|
1625
|
+
declare const registerCommandsLogger: Logger;
|
|
1626
|
+
interface RegisterCommand {
|
|
1627
|
+
key: string;
|
|
1628
|
+
registerId: string;
|
|
1629
|
+
seq: number;
|
|
1630
|
+
commandId: string;
|
|
1631
|
+
type: RegisterCommandType;
|
|
1632
|
+
version: number;
|
|
1633
|
+
payload: Record<string, unknown>;
|
|
1634
|
+
createdAt: string;
|
|
1635
|
+
syncStatus: 'pending' | 'applied' | 'rejected';
|
|
1636
|
+
error?: CommandError;
|
|
1637
|
+
result?: RegisterCommandResult;
|
|
1638
|
+
updatedAt: string;
|
|
1639
|
+
}
|
|
1640
|
+
type RegisterCommandCollection = RxCollection<RegisterCommand>;
|
|
1641
|
+
declare const registerCommandSchema: RxJsonSchema<RegisterCommand>;
|
|
1642
|
+
declare const registerCommandCollection: () => {
|
|
1643
|
+
schema: RxJsonSchema<RegisterCommand>;
|
|
1644
|
+
};
|
|
1645
|
+
type BuiltCommand = Pick<RegisterCommand, 'key' | 'type' | 'payload'> & {
|
|
1646
|
+
version: 1;
|
|
1647
|
+
};
|
|
1648
|
+
declare function sessionOpenCommand(s: RegisterSession): BuiltCommand;
|
|
1649
|
+
declare function sessionTransitionCommand(s: RegisterSession & {
|
|
1650
|
+
status_at: string;
|
|
1651
|
+
}): BuiltCommand;
|
|
1652
|
+
declare function movementCommand(m: CashMovement): BuiltCommand;
|
|
1653
|
+
declare function closureCommand(c: Closure): BuiltCommand;
|
|
1654
|
+
/** Appends missing facts in dependency order; the bytes of an existing key never change. */
|
|
1655
|
+
declare function reconcileRegisterCommands({ commands, sessions, movements, closures, host, storeKey, registerId, now, observed }: {
|
|
1656
|
+
commands: RegisterCommandCollection;
|
|
1657
|
+
sessions: RegisterSessionCollection;
|
|
1658
|
+
movements: CashMovementCollection;
|
|
1659
|
+
closures: ClosureCollection;
|
|
1660
|
+
host: RegisterHost;
|
|
1661
|
+
storeKey: string;
|
|
1662
|
+
registerId: string;
|
|
1663
|
+
now?: string;
|
|
1664
|
+
observed?: RegisterSession[];
|
|
1665
|
+
}): Promise<string[]>;
|
|
1666
|
+
|
|
1489
1667
|
/** A sale is at tender, so the till can't count or close under it. Its message can be shown to a cashier as it is. */
|
|
1490
1668
|
declare class RegisterTenderInProgressError extends Error {
|
|
1491
1669
|
constructor();
|
|
@@ -1509,6 +1687,9 @@ interface UseRegisterSessionOptions {
|
|
|
1509
1687
|
movements: CashMovementCollection | null;
|
|
1510
1688
|
/** `closures`; `null` while it opens. */
|
|
1511
1689
|
closures: ClosureCollection | null;
|
|
1690
|
+
/** `register_commands`, created with `registerCommandCollection()`; `null` while it opens. */
|
|
1691
|
+
commands?: RegisterCommandCollection | null;
|
|
1692
|
+
capabilities?: ServerCapabilities;
|
|
1512
1693
|
/** `pos_orders`: a session's sales are those whose `sessionId` is its id. */
|
|
1513
1694
|
orders: RxCollection<PosOrder> | null;
|
|
1514
1695
|
/** The register document's host, for the closure number and perpetual totals (`writeClosure`); `null` while it opens. */
|
|
@@ -1573,9 +1754,9 @@ declare function useRegisterSession(options: UseRegisterSessionOptions): {
|
|
|
1573
1754
|
openSession: (input: {
|
|
1574
1755
|
expectedFloatMinor: number | null;
|
|
1575
1756
|
countedFloatMinor: number;
|
|
1576
|
-
}) => Promise<rxdb.RxDocument<RegisterSession, {}>>;
|
|
1577
|
-
startCounting: () => Promise<rxdb.
|
|
1578
|
-
backToSelling: () => Promise<rxdb.
|
|
1757
|
+
}) => Promise<rxdb.RxDocument<RegisterSession, {}, unknown>>;
|
|
1758
|
+
startCounting: () => Promise<rxdb.RxDocumentBase<RegisterSession, {}, unknown> & RegisterSession & rxdb.ExtendObservables<RegisterSession> & rxdb.ExtendReactivity<RegisterSession, unknown>>;
|
|
1759
|
+
backToSelling: () => Promise<rxdb.RxDocumentBase<RegisterSession, {}, unknown> & RegisterSession & rxdb.ExtendObservables<RegisterSession> & rxdb.ExtendReactivity<RegisterSession, unknown>>;
|
|
1579
1760
|
/**
|
|
1580
1761
|
* `counted` maps each tender to its counted minor units. An interrupted close resumes with
|
|
1581
1762
|
* the count persisted on the session, not the one passed to the retry (WCPOS uses the retry's).
|
|
@@ -1590,13 +1771,13 @@ declare function useRegisterSession(options: UseRegisterSessionOptions): {
|
|
|
1590
1771
|
counted: Record<string, number>;
|
|
1591
1772
|
approvedBy?: string;
|
|
1592
1773
|
approvedByName?: string;
|
|
1593
|
-
}) => Promise<rxdb.RxDocument<Closure, {}>>;
|
|
1774
|
+
}) => Promise<rxdb.RxDocument<Closure, {}, unknown>>;
|
|
1594
1775
|
recordMovement: (input: {
|
|
1595
1776
|
type: "paid_in" | "paid_out" | "no_sale";
|
|
1596
1777
|
amountMinor: number;
|
|
1597
1778
|
reason: string;
|
|
1598
|
-
}) => Promise<rxdb.RxDocument<CashMovement, {}>>;
|
|
1599
|
-
voidMovement: (id: string) => Promise<rxdb.RxDocument<CashMovement, {}>>;
|
|
1779
|
+
}) => Promise<rxdb.RxDocument<CashMovement, {}, unknown>>;
|
|
1780
|
+
voidMovement: (id: string) => Promise<rxdb.RxDocument<CashMovement, {}, unknown>>;
|
|
1600
1781
|
};
|
|
1601
1782
|
};
|
|
1602
1783
|
|
|
@@ -1620,7 +1801,7 @@ type SaleStage = {
|
|
|
1620
1801
|
method: 'cash' | 'external';
|
|
1621
1802
|
} | {
|
|
1622
1803
|
kind: 'receipt';
|
|
1623
|
-
order:
|
|
1804
|
+
order: SentOrder;
|
|
1624
1805
|
posOrder: PosOrder;
|
|
1625
1806
|
};
|
|
1626
1807
|
/** TallyUI finalizeOrder's refusal below order.create v2 (c19a203), shown when the discount is applied; finalize stays the backstop. */
|
|
@@ -1668,6 +1849,8 @@ declare function useSale(settings: Pick<StoreSettings, 'currency'>, opts: {
|
|
|
1668
1849
|
/** A line's discount, or the order's without a line; returns the refusal to show, or null once applied. */
|
|
1669
1850
|
applyDiscount(lineId: string | null, discount: Discount): string | null;
|
|
1670
1851
|
removeDiscount(id: string): void;
|
|
1852
|
+
/** the picked customer reaches the server as order.create v3's customer.customerId */
|
|
1853
|
+
setCustomer(customer: CustomerSummary | null): void;
|
|
1671
1854
|
/**
|
|
1672
1855
|
* Pins `options.session` for this tender when given, else the rendered `session` option. Pass the
|
|
1673
1856
|
* session `useRegisterSession`'s `requireSaleSession()` returned: the rendered one can lag a session
|
|
@@ -1696,7 +1879,9 @@ declare function useSale(settings: Pick<StoreSettings, 'currency'>, opts: {
|
|
|
1696
1879
|
* and `createdAt`, no new stamp and no second late-sale fact) and hands it to `onSaleCompleted`
|
|
1697
1880
|
* again, so that must accept an order it already stored (as `useOrderOutbox.record` does). The
|
|
1698
1881
|
* pending completion is cleared once `onSaleCompleted` resolves, or by `newSale()`. A call while
|
|
1699
|
-
* another is in flight returns that call's promise, and a call on the receipt does nothing.
|
|
1882
|
+
* another is in flight returns that call's promise, and a call on the receipt does nothing. While
|
|
1883
|
+
* `cashierRef` or `registerId` is out of bounds (shown as `error`), a call does nothing either,
|
|
1884
|
+
* unless it's the Retry of a pending completion, which was built with the options as they were.
|
|
1700
1885
|
*/
|
|
1701
1886
|
complete: () => Promise<void>;
|
|
1702
1887
|
/**
|
|
@@ -1734,16 +1919,22 @@ type TransportOutcome = {
|
|
|
1734
1919
|
kind: 'refused';
|
|
1735
1920
|
status: number;
|
|
1736
1921
|
reason: string;
|
|
1737
|
-
}
|
|
1922
|
+
}
|
|
1923
|
+
/** `reason` is `network` when the store could not be reached, `timeout` when a sent request got no answer in time,
|
|
1924
|
+
* else what the store answered (`status_503`, `bad_body`, ...). The order outbox counts every reason but `network`. */
|
|
1925
|
+
| {
|
|
1738
1926
|
kind: 'retry';
|
|
1739
1927
|
reason: string;
|
|
1740
1928
|
retryAfterMs?: number;
|
|
1741
1929
|
};
|
|
1742
|
-
interface CommandTransport {
|
|
1743
|
-
send(batch:
|
|
1930
|
+
interface CommandTransport<E extends AnyCommandEnvelope = CommandEnvelope<OrderCreatePayload>> {
|
|
1931
|
+
send(batch: E[]): Promise<TransportOutcome>;
|
|
1744
1932
|
}
|
|
1745
1933
|
interface OutboxState {
|
|
1746
1934
|
pending: number;
|
|
1935
|
+
/** The order outbox's count of `pos_orders` the store refused (`syncStatus: 'rejected'`), read with `pending`. Each
|
|
1936
|
+
* stays until requeue() sends it again. The register outbox leaves it unset. */
|
|
1937
|
+
rejected?: number;
|
|
1747
1938
|
sending: boolean;
|
|
1748
1939
|
lastRetryReason?: string;
|
|
1749
1940
|
nextAttemptAt?: number;
|
|
@@ -1754,6 +1945,26 @@ interface OutboxState {
|
|
|
1754
1945
|
status: number;
|
|
1755
1946
|
reason: string;
|
|
1756
1947
|
};
|
|
1948
|
+
/** Set while the store (not offline) has kept failing some orders for 15 minutes, each on its own clock. Those
|
|
1949
|
+
* orders stay pending and keep retrying. `orders` has each order's own entry: its `since` is when its clock
|
|
1950
|
+
* would have started had there been no offline gaps (now minus its answered time; while offline, as of the
|
|
1951
|
+
* moment the clock paused), and its latest `reason`. `since` is the earliest of theirs; `reason` the latest. */
|
|
1952
|
+
stuck?: {
|
|
1953
|
+
commandIds: string[];
|
|
1954
|
+
since: number;
|
|
1955
|
+
reason: string;
|
|
1956
|
+
orders: {
|
|
1957
|
+
commandId: string;
|
|
1958
|
+
since: number;
|
|
1959
|
+
reason: string;
|
|
1960
|
+
}[];
|
|
1961
|
+
};
|
|
1962
|
+
/** Set after 3 consecutive 404 answers (counted across the outboxes sharing one BackendNotFound): the store address
|
|
1963
|
+
* may be wrong, or the store's plugin isn't installed or is switched off. The outbox keeps retrying; the next answer
|
|
1964
|
+
* that isn't a 404 clears it (offline changes nothing). `since` is when the first of those 404s arrived. */
|
|
1965
|
+
backendMissing?: {
|
|
1966
|
+
since: number;
|
|
1967
|
+
};
|
|
1757
1968
|
}
|
|
1758
1969
|
|
|
1759
1970
|
interface HttpTransportOptions {
|
|
@@ -1762,17 +1973,33 @@ interface HttpTransportOptions {
|
|
|
1762
1973
|
fetch?: typeof fetch;
|
|
1763
1974
|
timeoutMs?: number;
|
|
1764
1975
|
}
|
|
1765
|
-
declare function createHttpCommandTransport(options: HttpTransportOptions): CommandTransport
|
|
1976
|
+
declare function createHttpCommandTransport(options: HttpTransportOptions): CommandTransport<AnyCommandEnvelope>;
|
|
1977
|
+
|
|
1978
|
+
/** Counts consecutive 404s across every outbox given it, so a missing plugin shows once, whichever outbox meets it. */
|
|
1979
|
+
interface BackendNotFound {
|
|
1980
|
+
/** A 404 counts; any other answer from the store clears; offline (`network`) changes nothing. */
|
|
1981
|
+
record(outcome: TransportOutcome, at: number): void;
|
|
1982
|
+
backendMissing$: Observable<OutboxState['backendMissing']>;
|
|
1983
|
+
}
|
|
1984
|
+
declare function createBackendNotFound(): BackendNotFound;
|
|
1766
1985
|
|
|
1767
1986
|
interface OrderOutboxOptions {
|
|
1768
1987
|
collection: RxCollection<PosOrder>;
|
|
1769
|
-
transport: CommandTransport
|
|
1988
|
+
transport: CommandTransport<OrderCreateEnvelope>;
|
|
1770
1989
|
deviceId: string;
|
|
1990
|
+
getMaxOrderCreateVersion?: () => number | undefined | Promise<number | undefined>;
|
|
1991
|
+
refreshCapabilities?: () => Promise<void>;
|
|
1771
1992
|
batchSize?: number;
|
|
1772
1993
|
initialBackoffMs?: number;
|
|
1773
1994
|
maxBackoffMs?: number;
|
|
1774
1995
|
random?: () => number;
|
|
1775
1996
|
now?: () => number;
|
|
1997
|
+
/** Tests only: overrides ISOLATE_AFTER_ATTEMPTS. */
|
|
1998
|
+
isolateAfterAttempts?: number;
|
|
1999
|
+
/** Tests only: overrides STUCK_AFTER_MS. */
|
|
2000
|
+
stuckAfterMs?: number;
|
|
2001
|
+
/** Counts 404s toward OutboxState.backendMissing; pass the register outbox's too, so either one's 404s show one notice. */
|
|
2002
|
+
backendNotFound?: BackendNotFound;
|
|
1776
2003
|
}
|
|
1777
2004
|
interface OrderOutbox {
|
|
1778
2005
|
/** Sends pending orders until empty or retrying. Concurrent calls share one run. */
|
|
@@ -1788,8 +2015,34 @@ interface OrderOutbox {
|
|
|
1788
2015
|
}
|
|
1789
2016
|
declare function createOrderOutbox(options: OrderOutboxOptions): OrderOutbox;
|
|
1790
2017
|
|
|
1791
|
-
|
|
2018
|
+
interface RegisterOutboxOptions {
|
|
2019
|
+
collection: RxCollection<RegisterCommand>;
|
|
2020
|
+
transport: CommandTransport<RegisterCommandEnvelope>;
|
|
2021
|
+
deviceId: string;
|
|
2022
|
+
/** Checked at the start of every run; false sends nothing. */
|
|
2023
|
+
isEnabled?: () => boolean;
|
|
2024
|
+
/** Runs before marking, so a crash resends; a throw is logged and marking continues. */
|
|
2025
|
+
onResult?: (command: RegisterCommand, result: CommandResult) => Promise<void> | void;
|
|
2026
|
+
batchSize?: number;
|
|
2027
|
+
initialBackoffMs?: number;
|
|
2028
|
+
maxBackoffMs?: number;
|
|
2029
|
+
random?: () => number;
|
|
2030
|
+
now?: () => number;
|
|
2031
|
+
/** Tests only: overrides STUCK_AFTER_MS. */
|
|
2032
|
+
stuckAfterMs?: number;
|
|
2033
|
+
/** Counts 404s toward OutboxState.backendMissing; pass the order outbox's too, so either one's 404s show one notice. */
|
|
2034
|
+
backendNotFound?: BackendNotFound;
|
|
2035
|
+
}
|
|
2036
|
+
interface RegisterOutbox {
|
|
2037
|
+
flush(): Promise<void>;
|
|
2038
|
+
start(): void;
|
|
2039
|
+
stop(): void;
|
|
2040
|
+
state$: Observable<OutboxState>;
|
|
2041
|
+
}
|
|
2042
|
+
declare function createRegisterOutbox(options: RegisterOutboxOptions): RegisterOutbox;
|
|
2043
|
+
|
|
1792
2044
|
declare const outboxLogger: Logger;
|
|
2045
|
+
|
|
1793
2046
|
interface UseOrderOutboxOptions {
|
|
1794
2047
|
/** Which order store to use (medusapos: the backend's base URL); `null` means no store. A change reopens. */
|
|
1795
2048
|
storeKey: string | null;
|
|
@@ -1799,9 +2052,13 @@ interface UseOrderOutboxOptions {
|
|
|
1799
2052
|
close(): Promise<void>;
|
|
1800
2053
|
}>;
|
|
1801
2054
|
/** Builds the command transport for `storeKey` (the app's HTTP transport and auth headers). Read once per open. */
|
|
1802
|
-
transport(storeKey: string): CommandTransport
|
|
2055
|
+
transport(storeKey: string): CommandTransport<OrderCreateEnvelope>;
|
|
1803
2056
|
/** The device id sent on every command (see `getDeviceId`). A change reopens. */
|
|
1804
2057
|
deviceId: string;
|
|
2058
|
+
/** Presence is fixed when the store opens; calls use the latest function. Reopen to add or remove. */
|
|
2059
|
+
getMaxOrderCreateVersion?: () => number | undefined | Promise<number | undefined>;
|
|
2060
|
+
/** Presence is fixed when the store opens; calls use the latest function. Reopen to add or remove. */
|
|
2061
|
+
refreshCapabilities?: () => Promise<void>;
|
|
1805
2062
|
/** Called with `state.sending`, and with `false` on cleanup (medusapos: live-tab's `markBusy('outbox', …)`). */
|
|
1806
2063
|
onBusy?(busy: boolean): void;
|
|
1807
2064
|
/** Called when `open` rejects, even if the key has changed since (medusapos: reports storage worker failures). */
|
|
@@ -1837,6 +2094,9 @@ interface UseOrderOutboxResult {
|
|
|
1837
2094
|
* storage can wait forever, and TallyUI deliberately doesn't bound that close (#155).
|
|
1838
2095
|
*/
|
|
1839
2096
|
savesInFlight: number;
|
|
2097
|
+
/** The command ids in `state.stuck`: pending orders the store keeps failing. Pass them to `needsAttention` and
|
|
2098
|
+
* `OrdersList`. Empty when none, and before the current store is ready. */
|
|
2099
|
+
stuckCommandIds: readonly string[];
|
|
1840
2100
|
}
|
|
1841
2101
|
/** Opens the order store for `storeKey`, runs its outbox and watches the recent orders (lifted from medusapos/app, ADR-052). */
|
|
1842
2102
|
declare function useOrderOutbox(options: UseOrderOutboxOptions): UseOrderOutboxResult;
|
|
@@ -1987,4 +2247,4 @@ declare function planLegs(plan: TenderPlan, rowsSinceFrom: {
|
|
|
1987
2247
|
};
|
|
1988
2248
|
};
|
|
1989
2249
|
|
|
1990
|
-
export { type Actor, type AddLineInput, type AppliedDiscount, CartError, type CashMovement, type CashMovementCollection, type CatalogueEntry, type Closure, type ClosureCollection, type ClosureContext, type ClosureScope, type CommandTransport, type Correction, CurrencyProvider, type CurrencyProviderProps, type CustomerSummary, DISCOUNTS_UNSUPPORTED, type Discount, type DisplayLine, type DisplayTotals, type FinalizeOptions, type HttpTransportOptions, type LedgerRow, type LineItem, type LineTaxLine, type LogEntry, type LogLevel, type LogSink, type Logger, MAX_TENDER_MINOR, MICROS_PER_MINOR, type Movement$1 as Movement, type MovementType, type Order, type OrderBuilder, type OrderBuilderOptions, OrderContentMismatchError, type OrderManager, type OrderManagerOptions, type OrderOutbox, type OrderOutboxOptions, type OrderTaxTotals, type OutboxState, type ParkedOrderSummary, type Payment, type PaymentMethod, type PaymentTransport, type PlanLeg, type PosOrder, type PosOrderLine, PosOrderOpenClosedError, type PosOrderPayment, type PosOrderSyncStatus, type RateTaxLine, type ReceiptConfig, type ReceiptData, type ReceiptLineItem, type RecordedFigures, RegisterApprovalRequiredError, type RegisterBucket, RegisterCloseIncompleteError, type RegisterCounters, type RegisterDocument, type RegisterFact, type RegisterHost, RegisterMovementStrandedError, type RegisterSession, RegisterSessionAlreadyOpenError, RegisterSessionClosedError, type RegisterSessionCollection, RegisterSessionRequiredError, type RegisterStore, RegisterTenderInProgressError, type Repository, type ResolveStoreSettingsOptions, SALE_SAVING, type SaleStage, type SplitTab, type StoreSettingsResolution, type StoreSettingsState, type TaxContext, type TaxLineInput, TaxProvider, type TaxProviderProps, type TaxRateMap, type TenderAction, type TenderKey, type TenderLineId, type TenderPlan, type TenderState, type TenderView, type TransportOutcome, type UseOrderOutboxOptions, type UseOrderOutboxResult, type UseRegisterSessionOptions, activePlan, addEntryToCart, addPosOrderCollection, advancePerpetual, allocateOrderDiscount, appliedMinor, backToSelling, bindRegister, buildClosureDocument, buildReceiptData, buildXReportDocument, callbackSink, cashMovementSchema, catalogueEntries, changeMinor, clampClosureScope, closeNeedsApproval, closeSession, closureSchema, computeOrderTax, consoleSink, countVariance, createHttpCommandTransport, createLogger, createOrderBuilder, createOrderManager, createOrderOutbox, createRepository, denominationTotal, denominations, deriveExpected, deriveSettled, ensureRegister, evenSplitShareMinor, exportCsv, finalizeOrder, findEntryByCode, formatClosureDate, getBoundRegisterId, getDeviceId, initTenderState, initialTenderState, isServerDecimal, labelKeys, minorToDecimal, mintClosureNumber, mintUuid, movementFieldError, needsAttention, nextSaleCounter, normalizeAmount, observeRegister$, openSession, openSessionSelector, outboxLogger, overThreshold, parseMinor, planLegs, posOrderCollection, posOrderSchema, posOrdersLogger, quickTenderedAmounts, ratePpmFromPercent, readBoundRegister, readRegister, recordMovement, recordRegisterFact, registerFactsLogger, registerSessionCollection, registerSessionSchema, requireOpenSession, resolveStoreSettings, roundMicrosToMinor, saleLogger, sameSale, searchProducts, selectClosureRows, stampSession, startCounting, stockOverlay$, stockOverlayAsOf$, taxLinesByRate, taxMicros, taxProviderProps, tenderReducer, toOrderCreateEnvelope, unbindRegister, useCurrencyCode, useCurrencyFormatter, useOrderOutbox, useRegisterSession, useSale, useStoreSettings, useTax, uuidv7, validAmount, varianceText, variantPriceLabel, voidMovement, withPricingContext, writeClosure };
|
|
2250
|
+
export { type Actor, type AddLineInput, type AppliedDiscount, type BackendNotFound, type BasketLine, CartError, type CashMovement, type CashMovementCollection, type CatalogueEntry, type Closure, type ClosureCollection, type ClosureContext, type ClosureScope, type CommandTransport, type Correction, CurrencyProvider, type CurrencyProviderProps, type CustomerSummary, DISCOUNTS_UNSUPPORTED, type Discount, type DisplayLine, type DisplayTotals, type FinalizeOptions, type HttpTransportOptions, type LedgerRow, type LineItem, type LineTaxLine, type LogEntry, type LogLevel, type LogSink, type Logger, MAX_TENDER_MINOR, MICROS_PER_MINOR, type Movement$1 as Movement, type MovementType, type Order, type OrderBuilder, type OrderBuilderOptions, OrderContentMismatchError, type OrderManager, type OrderManagerOptions, type OrderOutbox, type OrderOutboxOptions, type OrderTaxTotals, type OutboxState, type ParkedOrderSummary, type Payment, type PaymentMethod, type PaymentTransport, type PlanLeg, type PosOrder, type PosOrderLine, type PosOrderLocalWarning, PosOrderOpenClosedError, type PosOrderPayment, type PosOrderServerFailures, type PosOrderSyncStatus, type RateTaxLine, type ReceiptConfig, type ReceiptData, type ReceiptLineItem, type RecordedFigures, RegisterApprovalRequiredError, type RegisterBucket, RegisterCloseIncompleteError, type RegisterCommand, type RegisterCommandCollection, type RegisterCounters, type RegisterDocument, type RegisterFact, type RegisterHost, RegisterIdInvalidError, RegisterMovementAmountError, RegisterMovementReasonError, RegisterMovementStrandedError, type RegisterOutbox, type RegisterOutboxOptions, type RegisterSession, RegisterSessionAlreadyOpenError, RegisterSessionClosedError, type RegisterSessionCollection, RegisterSessionRequiredError, type RegisterStore, RegisterTenderInProgressError, type Repository, type ResolveStoreSettingsOptions, SALE_SAVING, type SaleStage, type SentOrder, type SplitTab, type StoreSettingsResolution, type StoreSettingsState, TAX_ROUNDING_MIXED_NOTE, type TaxContext, type TaxLineInput, TaxProvider, type TaxProviderProps, type TaxRateMap, type TenderAction, type TenderKey, type TenderLineId, type TenderPlan, type TenderState, type TenderView, type TransportOutcome, UnsupportedOrderVersionError, type UseOrderOutboxOptions, type UseOrderOutboxResult, type UseRegisterSessionOptions, activePlan, addEntryToCart, addPosOrderCollection, advancePerpetual, allocateOrderDiscount, appliedMinor, backToSelling, bindRegister, buildClosureDocument, buildReceiptData, buildXReportDocument, callbackSink, cashMovementSchema, catalogueEntries, changeMinor, clampClosureScope, closeNeedsApproval, closeSession, closureCommand, closureSchema, computeOrderTax, consoleSink, countVariance, createBackendNotFound, createHttpCommandTransport, createLogger, createOrderBuilder, createOrderManager, createOrderOutbox, createRegisterOutbox, createRepository, denominationTotal, denominations, deriveExpected, deriveSettled, ensureRegister, evenSplitShareMinor, exportCsv, finalizeOrder, findEntryByCode, formatClosureDate, getBoundRegisterId, getDeviceId, initTenderState, initialTenderState, isServerDecimal, labelKeys, minorToDecimal, mintClosureNumber, mintUuid, movementCommand, movementFieldError, needsAttention, nextSaleCounter, normalizeAmount, observeRegister$, openSession, openSessionSelector, outboxLogger, overThreshold, parseMinor, planLegs, posOrderCollection, posOrderSchema, posOrdersLogger, quickTenderedAmounts, ratePpmFromPercent, readBoundRegister, readRegister, reconcileRegisterCommands, recordMovement, recordRegisterFact, registerCommandCollection, registerCommandSchema, registerCommandsLogger, registerFactsLogger, registerSessionCollection, registerSessionSchema, requireOpenSession, resolveStoreSettings, roundMicrosToMinor, saleLogger, sameSale, searchProducts, selectClosureRows, sessionOpenCommand, sessionTransitionCommand, stampSession, startCounting, stockOverlay$, stockOverlayAsOf$, taxFiguresForBasket, taxLinesByRate, taxLogger, taxMicros, taxProviderProps, tenderReducer, toOrderCreateEnvelope, unbindRegister, useCurrencyCode, useCurrencyFormatter, useOrderOutbox, useRegisterSession, useSale, useStoreSettings, useTax, uuidv7, validAmount, varianceText, variantPriceLabel, voidMovement, withPricingContext, writeClosure };
|