@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.
Files changed (44) hide show
  1. package/dist/index.d.ts +321 -61
  2. package/dist/index.js +1391 -185
  3. package/package.json +8 -8
  4. package/src/index.ts +15 -7
  5. package/src/order/index.ts +3 -0
  6. package/src/order/order-builder.ts +23 -7
  7. package/src/order/order-manager.ts +3 -1
  8. package/src/order/tax-figures.ts +23 -0
  9. package/src/order/types.ts +9 -3
  10. package/src/outbox/backend-not-found.ts +27 -0
  11. package/src/outbox/http-transport.ts +5 -4
  12. package/src/outbox/index.ts +2 -0
  13. package/src/outbox/logger.ts +3 -0
  14. package/src/outbox/order-outbox.ts +331 -27
  15. package/src/outbox/register-outbox.ts +250 -0
  16. package/src/outbox/types.test-d.ts +22 -0
  17. package/src/outbox/types.ts +17 -3
  18. package/src/outbox/use-order-outbox.ts +20 -6
  19. package/src/pos-order/__fixtures__/order-create-v3.json +97 -0
  20. package/src/pos-order/command.ts +99 -11
  21. package/src/pos-order/finalize.ts +145 -7
  22. package/src/pos-order/index.ts +2 -2
  23. package/src/pos-order/needs-attention.ts +9 -4
  24. package/src/pos-order/open.ts +100 -37
  25. package/src/pos-order/schema.ts +35 -5
  26. package/src/pos-order/types.ts +31 -5
  27. package/src/product/search-products.ts +5 -2
  28. package/src/receipt/build-receipt-data.ts +5 -3
  29. package/src/receipt/types.ts +1 -0
  30. package/src/register/closure-document.ts +7 -0
  31. package/src/register/index.ts +5 -3
  32. package/src/register/movement-input.ts +1 -1
  33. package/src/register/register-commands.ts +147 -0
  34. package/src/register/register-document.ts +26 -1
  35. package/src/register/session-store.ts +41 -12
  36. package/src/register/use-register-session.ts +46 -2
  37. package/src/sale/cart.ts +1 -0
  38. package/src/sale/use-sale.ts +97 -40
  39. package/src/store-settings/map-store-settings.ts +7 -2
  40. package/src/store-settings/use-store-settings.ts +22 -7
  41. package/src/tax/exact.ts +74 -11
  42. package/src/tax/index.ts +1 -1
  43. package/src/tax/tax-provider.tsx +26 -4
  44. 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, CommandEnvelope, OrderCreatePayload, VariantSummary, CommandResult } from '@tallyui/core';
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
- /** Rounds micro-minor-units to integer minor units, half away from zero. */
70
- declare function roundMicrosToMinor(micros: bigint): number;
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
- /** `<TaxProvider>`'s props from the same settings, so its tax inclusivity matches the connector's. */
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: Order, config: ReceiptConfig): ReceiptData;
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
- * Local only: `toOrderCreateEnvelope` never sends it.
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. Local only: `toOrderCreateEnvelope` never sends 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
- /** ADR-065: the receipt's display figures, in integer minor units of `currency` at `exponent`. Not written yet. */
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). Not written yet. */
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
- /** Turns a fully paid builder Order into a pending PosOrder without mutating it. */
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
- * A discounted order is version 2 (ADR-062); a discount-free one stays version 1, byte-identical.
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): CommandEnvelope<OrderCreatePayload>;
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, or the close stopped waiting for it (after
608
- * `POS_ORDER_MIGRATION_CLOSE_WAIT_MS`). No order is lost: reopen the database and call it again.
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 16.21 trusts the status its last migration stored: a leftover `ERROR` rejects
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`, the status and a failed run's checkpoint are reset
625
- * (never an order or its storage), and the migration itself is awaited.
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 whole open, up to `POS_ORDER_MIGRATION_CLOSE_WAIT_MS`. Called once the
628
- * database's close has begun, or when the close stops waiting, it rejects with
629
- * `PosOrderOpenClosedError` (`code: 'POS_ORDER_OPEN_CLOSED'`) before any further write: reopen and
630
- * call it again. Any other rejection (DM4, a storage error) is a real failure.
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 warnings, or taken after their session
648
- * closed (`lateSessionId`, whatever the sync status), newest first. Never changes `orders`.
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[]): 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 was kept rather than deleted. The caller must not record it again; job c's
950
- * server resolves stranded movements. The message can be shown to a cashier as it is.
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.RxDocument<RegisterSession, {}>>;
1578
- backToSelling: () => Promise<rxdb.RxDocument<RegisterSession, {}>>;
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: 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: CommandEnvelope<OrderCreatePayload>[]): Promise<TransportOutcome>;
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
- /** Logs a retried order found stored under another `commandId` (warn), and a content mismatch (error). */
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 };