@fayz-ai/shop 0.9.0 → 0.11.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 (163) hide show
  1. package/README.md +18 -1
  2. package/dist/admin.d.ts +2 -0
  3. package/dist/admin.d.ts.map +1 -0
  4. package/dist/catalog.d.ts +1 -3
  5. package/dist/catalog.d.ts.map +1 -1
  6. package/dist/catalog.js +2 -2
  7. package/dist/{chunk-IYQHKMLB.js → chunk-2WACO2TG.js} +4 -4
  8. package/dist/chunk-2WACO2TG.js.map +1 -0
  9. package/dist/chunk-3GERSY5F.js +526 -0
  10. package/dist/chunk-3GERSY5F.js.map +1 -0
  11. package/dist/chunk-6BO2TMZ5.js +93 -0
  12. package/dist/chunk-6BO2TMZ5.js.map +1 -0
  13. package/dist/chunk-FQPGE3YN.js +3 -0
  14. package/dist/{chunk-JR25TISO.js.map → chunk-FQPGE3YN.js.map} +1 -1
  15. package/dist/{chunk-CTKZ6O2J.js → chunk-IKNGCRUM.js} +41 -15
  16. package/dist/chunk-IKNGCRUM.js.map +1 -0
  17. package/dist/chunk-KLDS7IPU.js +17 -0
  18. package/dist/chunk-KLDS7IPU.js.map +1 -0
  19. package/dist/chunk-NWW3WJ7Y.js +136 -0
  20. package/dist/chunk-NWW3WJ7Y.js.map +1 -0
  21. package/dist/chunk-STRNMFC4.js +1438 -0
  22. package/dist/chunk-STRNMFC4.js.map +1 -0
  23. package/dist/chunk-U6HV3LVP.js +1170 -0
  24. package/dist/chunk-U6HV3LVP.js.map +1 -0
  25. package/dist/client.d.ts +2 -0
  26. package/dist/client.d.ts.map +1 -0
  27. package/dist/data/customer-search.d.ts +3 -0
  28. package/dist/data/customer-search.d.ts.map +1 -0
  29. package/dist/data/functions.d.ts +9 -0
  30. package/dist/data/functions.d.ts.map +1 -0
  31. package/dist/data/media.d.ts +37 -0
  32. package/dist/data/media.d.ts.map +1 -0
  33. package/dist/{mock-catalog.d.ts → data/mock-catalog.d.ts} +21 -1
  34. package/dist/data/mock-catalog.d.ts.map +1 -0
  35. package/dist/data/mock-provider.d.ts +165 -0
  36. package/dist/data/mock-provider.d.ts.map +1 -0
  37. package/dist/data/mock.d.ts +3 -0
  38. package/dist/data/mock.d.ts.map +1 -0
  39. package/dist/data/provider.d.ts +335 -0
  40. package/dist/data/provider.d.ts.map +1 -0
  41. package/dist/data/supabase-provider.d.ts +208 -0
  42. package/dist/data/supabase-provider.d.ts.map +1 -0
  43. package/dist/data/tables.d.ts +38 -0
  44. package/dist/data/tables.d.ts.map +1 -0
  45. package/dist/data/tenant.d.ts.map +1 -0
  46. package/dist/entities/entity-reconcile.d.ts +32 -0
  47. package/dist/entities/entity-reconcile.d.ts.map +1 -0
  48. package/dist/entities/entity-sql.d.ts +50 -0
  49. package/dist/entities/entity-sql.d.ts.map +1 -0
  50. package/dist/entities/entity-store.d.ts +58 -0
  51. package/dist/entities/entity-store.d.ts.map +1 -0
  52. package/dist/entities/index.d.ts +10 -0
  53. package/dist/entities/index.d.ts.map +1 -0
  54. package/dist/entities/mock-entity-store.d.ts +17 -0
  55. package/dist/entities/mock-entity-store.d.ts.map +1 -0
  56. package/dist/entities/supabase-entity-store.d.ts +8 -0
  57. package/dist/entities/supabase-entity-store.d.ts.map +1 -0
  58. package/dist/entities.js +159 -0
  59. package/dist/entities.js.map +1 -0
  60. package/dist/index.d.ts +28 -12
  61. package/dist/index.d.ts.map +1 -1
  62. package/dist/index.js +1558 -40
  63. package/dist/index.js.map +1 -1
  64. package/dist/inventory/allocation.d.ts +98 -0
  65. package/dist/inventory/allocation.d.ts.map +1 -0
  66. package/dist/inventory/index.d.ts +13 -0
  67. package/dist/inventory/index.d.ts.map +1 -0
  68. package/dist/inventory/mock-stock-store.d.ts +89 -0
  69. package/dist/inventory/mock-stock-store.d.ts.map +1 -0
  70. package/dist/inventory/postgrest-double.d.ts +65 -0
  71. package/dist/inventory/postgrest-double.d.ts.map +1 -0
  72. package/dist/inventory/shopify-mapping.d.ts +67 -0
  73. package/dist/inventory/shopify-mapping.d.ts.map +1 -0
  74. package/dist/inventory/stock-contract.d.ts +46 -0
  75. package/dist/inventory/stock-contract.d.ts.map +1 -0
  76. package/dist/inventory/stock-store.d.ts +96 -0
  77. package/dist/inventory/stock-store.d.ts.map +1 -0
  78. package/dist/inventory/stock-types.d.ts +351 -0
  79. package/dist/inventory/stock-types.d.ts.map +1 -0
  80. package/dist/inventory/supabase-stock-store.d.ts +60 -0
  81. package/dist/inventory/supabase-stock-store.d.ts.map +1 -0
  82. package/dist/inventory/vocabulary.d.ts +86 -0
  83. package/dist/inventory/vocabulary.d.ts.map +1 -0
  84. package/dist/inventory.js +4 -0
  85. package/dist/inventory.js.map +1 -0
  86. package/dist/lib/catalog.d.ts +4 -0
  87. package/dist/lib/catalog.d.ts.map +1 -0
  88. package/dist/{commerce.d.ts → lib/commerce.d.ts} +2 -2
  89. package/dist/lib/commerce.d.ts.map +1 -0
  90. package/dist/lib/document.d.ts +13 -0
  91. package/dist/lib/document.d.ts.map +1 -0
  92. package/dist/lib/emails.d.ts +50 -0
  93. package/dist/lib/emails.d.ts.map +1 -0
  94. package/dist/lib/message-events.d.ts +4 -0
  95. package/dist/lib/message-events.d.ts.map +1 -0
  96. package/dist/lib/runtime.d.ts +5 -0
  97. package/dist/lib/runtime.d.ts.map +1 -0
  98. package/dist/lib/variant-matrix.d.ts +120 -0
  99. package/dist/lib/variant-matrix.d.ts.map +1 -0
  100. package/dist/mock.d.ts +1 -2
  101. package/dist/mock.d.ts.map +1 -1
  102. package/dist/mock.js +4 -2
  103. package/dist/provider.d.ts +1 -88
  104. package/dist/provider.d.ts.map +1 -1
  105. package/dist/providers/checkout.d.ts +129 -0
  106. package/dist/providers/checkout.d.ts.map +1 -0
  107. package/dist/providers/index.d.ts +5 -0
  108. package/dist/providers/index.d.ts.map +1 -0
  109. package/dist/providers/shipping.d.ts +43 -0
  110. package/dist/providers/shipping.d.ts.map +1 -0
  111. package/dist/providers.js +70 -0
  112. package/dist/providers.js.map +1 -0
  113. package/dist/rules/adapter.d.ts +36 -0
  114. package/dist/rules/adapter.d.ts.map +1 -0
  115. package/dist/rules/attributes.d.ts +64 -0
  116. package/dist/rules/attributes.d.ts.map +1 -0
  117. package/dist/rules/context.d.ts +43 -0
  118. package/dist/rules/context.d.ts.map +1 -0
  119. package/dist/rules/evaluator.d.ts +37 -0
  120. package/dist/rules/evaluator.d.ts.map +1 -0
  121. package/dist/rules/index.d.ts +13 -0
  122. package/dist/rules/index.d.ts.map +1 -0
  123. package/dist/rules/prices.d.ts +25 -0
  124. package/dist/rules/prices.d.ts.map +1 -0
  125. package/dist/rules/rule-types.d.ts +160 -0
  126. package/dist/rules/rule-types.d.ts.map +1 -0
  127. package/dist/rules.js +22 -0
  128. package/dist/rules.js.map +1 -0
  129. package/dist/runtime.d.ts +1 -4
  130. package/dist/runtime.d.ts.map +1 -1
  131. package/dist/runtime.js +5 -3
  132. package/dist/shipping/label-provider.d.ts +43 -0
  133. package/dist/shipping/label-provider.d.ts.map +1 -0
  134. package/dist/types.d.ts +688 -0
  135. package/dist/types.d.ts.map +1 -1
  136. package/package.json +30 -20
  137. package/dist/chunk-CTKZ6O2J.js.map +0 -1
  138. package/dist/chunk-IYQHKMLB.js.map +0 -1
  139. package/dist/chunk-JR25TISO.js +0 -3
  140. package/dist/chunk-SVTCJ6HP.js +0 -440
  141. package/dist/chunk-SVTCJ6HP.js.map +0 -1
  142. package/dist/commerce.d.ts.map +0 -1
  143. package/dist/mock-catalog.d.ts.map +0 -1
  144. package/dist/mock-provider.d.ts +0 -60
  145. package/dist/mock-provider.d.ts.map +0 -1
  146. package/dist/supabase-provider.d.ts +0 -56
  147. package/dist/supabase-provider.d.ts.map +0 -1
  148. package/dist/tables.d.ts +0 -12
  149. package/dist/tables.d.ts.map +0 -1
  150. package/dist/tenant.d.ts.map +0 -1
  151. package/src/catalog.ts +0 -3
  152. package/src/commerce.ts +0 -120
  153. package/src/index.ts +0 -40
  154. package/src/mock-catalog.ts +0 -266
  155. package/src/mock-provider.ts +0 -480
  156. package/src/mock.ts +0 -2
  157. package/src/provider.ts +0 -117
  158. package/src/runtime.ts +0 -20
  159. package/src/supabase-provider.ts +0 -754
  160. package/src/tables.ts +0 -17
  161. package/src/tenant.ts +0 -21
  162. package/src/types.ts +0 -623
  163. /package/dist/{tenant.d.ts → data/tenant.d.ts} +0 -0
@@ -0,0 +1,98 @@
1
+ import { Registry } from '@fayz-ai/core';
2
+ import type { RegistryMeta } from '@fayz-ai/core';
3
+ import type { StockAllocation, StockAllocationCandidate, StockAllocationRequest, StockAllocationStrategy } from './stock-types';
4
+ /**
5
+ * The core four (§4) — the SAME rows migration 0038 seeds with tenant_id NULL.
6
+ * migrations.test.ts cross-checks the two so they cannot drift.
7
+ */
8
+ export declare const CORE_STOCK_ALLOCATION_STRATEGIES: readonly StockAllocationStrategy[];
9
+ export declare const stockAllocationStrategyRegistry: Registry<StockAllocationStrategy>;
10
+ /** Idempotent — consumers call before reads, mirroring registerCoreStockVocabulary. */
11
+ export declare function registerCoreStockAllocationStrategies(): void;
12
+ /**
13
+ * Widen the strategy vocabulary. The SQL half is one INSERT into
14
+ * plg_shop_stock_allocation_strategies with the tenant's id.
15
+ */
16
+ export declare function registerStockAllocationStrategy(strategy: StockAllocationStrategy, options?: {
17
+ meta?: RegistryMeta;
18
+ replace?: boolean;
19
+ }): void;
20
+ /** The strategy for a key, or an I2 hard error naming the valid set. */
21
+ export declare function resolveStockAllocationStrategy(key: string): StockAllocationStrategy;
22
+ export declare function listStockAllocationStrategyKeys(): string[];
23
+ export declare function listStockAllocationStrategies(): StockAllocationStrategy[];
24
+ /** Test seam — a suite that widens the vocabulary must not leak into the next. */
25
+ export declare function clearStockAllocationStrategies(): void;
26
+ /**
27
+ * §4's "default fefo when `expires_at` exists", resolved most-specific first.
28
+ * The SQL twin is shop_resolve_allocation_strategy() in 0038:
29
+ *
30
+ * 1. the explicit argument
31
+ * 2. the variant's own override
32
+ * 3. the store's default
33
+ * 4. derived — 'fefo' when any candidate lot carries an expiry, else 'fifo'
34
+ *
35
+ * Rule 4 is what makes artorious work with no configuration: the moment a
36
+ * smoked-meat lot has a best-before, picking becomes FEFO on its own. And it is
37
+ * what makes the default cost pulse and figurinhas nothing: no expiry anywhere,
38
+ * so FIFO, which over one implicit lot is a no-op.
39
+ */
40
+ export declare function resolveAllocationStrategyKey(input: {
41
+ explicit?: string | null;
42
+ variantDefault?: string | null;
43
+ tenantDefault?: string | null;
44
+ candidates?: readonly StockAllocationCandidate[];
45
+ }): string;
46
+ /**
47
+ * The comparator, and the only place the strategy row's three fields are read.
48
+ * Four terms, matching 0038's ORDER BY one for one:
49
+ *
50
+ * 1. under FEFO a lot with NO expiry never beats one that has a date — the
51
+ * half a naive `ORDER BY expiresAt` gets wrong, and the half a food
52
+ * store's recall depends on;
53
+ * 2. under FEFO the soonest expiry leads;
54
+ * 3. FIFO takes the oldest receipt, LIFO the newest;
55
+ * 4. the default location leads, then the lot id, so the answer is
56
+ * reproducible rather than merely correct.
57
+ */
58
+ export declare function compareStockAllocationCandidates(strategy: StockAllocationStrategy, a: StockAllocationCandidate, b: StockAllocationCandidate): number;
59
+ /** Thrown when nothing can cover the quantity. Carries both numbers. */
60
+ export declare class StockAllocationError extends Error {
61
+ readonly requested: number;
62
+ readonly available: number;
63
+ constructor(message: string, requested: number, available: number);
64
+ }
65
+ /**
66
+ * Choose which lots cover a quantity, in the strategy's picking order.
67
+ *
68
+ * Mirrors shop_allocate_stock() exactly, including the two refusals:
69
+ *
70
+ * * a `manual` strategy with no lot named is an error, not a silent FEFO;
71
+ * * a SHORTFALL is an error, not a partial answer. Backorder is §10 q3,
72
+ * answered no — returning less than was asked for would let a caller write
73
+ * an order it cannot ship and force it to re-derive the gap to notice.
74
+ *
75
+ * A `none`-tracked variant returns no rows and no error: it never depletes (§3),
76
+ * so there is nothing to allocate and nothing to refuse.
77
+ */
78
+ export declare function allocateStock(request: StockAllocationRequest, candidates: readonly StockAllocationCandidate[]): StockAllocation[];
79
+ /**
80
+ * The core rows migration 0038 seeds into plg_shop_stock_reservation_ttls.
81
+ * `null` is the fallback for any method, including an order that names none.
82
+ *
83
+ * §10 q2 asked whether the TTL is one number or one per payment method and
84
+ * answered itself: "30 minutes covers pix comfortably; boleto is days." Thirty
85
+ * minutes is the window every Brazilian PSP quotes for pix; three days is the
86
+ * ordinary boleto compensation window. Holding a unit that long is the cost of
87
+ * accepting boleto, and pretending otherwise is what oversells.
88
+ */
89
+ export declare const CORE_RESERVATION_TTL_MINUTES: readonly {
90
+ paymentMethod: string | null;
91
+ minutes: number;
92
+ }[];
93
+ /** The SQL twin is shop_reservation_ttl(): most specific row wins. */
94
+ export declare function resolveReservationTtlMinutes(paymentMethod?: string | null, overrides?: readonly {
95
+ paymentMethod: string | null;
96
+ minutes: number;
97
+ }[]): number;
98
+ //# sourceMappingURL=allocation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"allocation.d.ts","sourceRoot":"","sources":["../../src/inventory/allocation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AACxC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AACjD,OAAO,KAAK,EACV,eAAe,EACf,wBAAwB,EACxB,sBAAsB,EACtB,uBAAuB,EACxB,MAAM,eAAe,CAAA;AAmCtB;;;GAGG;AACH,eAAO,MAAM,gCAAgC,EAAE,SAAS,uBAAuB,EAuB9E,CAAA;AAED,eAAO,MAAM,+BAA+B,mCAE3C,CAAA;AAID,uFAAuF;AACvF,wBAAgB,qCAAqC,IAAI,IAAI,CAU5D;AASD;;;GAGG;AACH,wBAAgB,+BAA+B,CAC7C,QAAQ,EAAE,uBAAuB,EACjC,OAAO,GAAE;IAAE,IAAI,CAAC,EAAE,YAAY,CAAC;IAAC,OAAO,CAAC,EAAE,OAAO,CAAA;CAAO,GACvD,IAAI,CAqBN;AAED,wEAAwE;AACxE,wBAAgB,8BAA8B,CAAC,GAAG,EAAE,MAAM,GAAG,uBAAuB,CAUnF;AAED,wBAAgB,+BAA+B,IAAI,MAAM,EAAE,CAG1D;AAED,wBAAgB,6BAA6B,IAAI,uBAAuB,EAAE,CAMzE;AAED,kFAAkF;AAClF,wBAAgB,8BAA8B,IAAI,IAAI,CAGrD;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,4BAA4B,CAAC,KAAK,EAAE;IAClD,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC9B,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC7B,UAAU,CAAC,EAAE,SAAS,wBAAwB,EAAE,CAAA;CACjD,GAAG,MAAM,CAYT;AAQD;;;;;;;;;;;GAWG;AACH,wBAAgB,gCAAgC,CAC9C,QAAQ,EAAE,uBAAuB,EACjC,CAAC,EAAE,wBAAwB,EAC3B,CAAC,EAAE,wBAAwB,GAC1B,MAAM,CAiBR;AAED,wEAAwE;AACxE,qBAAa,oBAAqB,SAAQ,KAAK;IAG3C,QAAQ,CAAC,SAAS,EAAE,MAAM;IAC1B,QAAQ,CAAC,SAAS,EAAE,MAAM;gBAF1B,OAAO,EAAE,MAAM,EACN,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM;CAK7B;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,aAAa,CAC3B,OAAO,EAAE,sBAAsB,EAC/B,UAAU,EAAE,SAAS,wBAAwB,EAAE,GAC9C,eAAe,EAAE,CAoEnB;AAID;;;;;;;;;GASG;AACH,eAAO,MAAM,4BAA4B,EAAE,SAAS;IAAE,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,EASlG,CAAA;AAEH,sEAAsE;AACtE,wBAAgB,4BAA4B,CAC1C,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,EAC7B,SAAS,GAAE,SAAS;IAAE,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,EAAO,GAC3E,MAAM,CAMR"}
@@ -0,0 +1,13 @@
1
+ export { StockMovementError, stockBalanceKey, replayStockLedger, mirroredInventoryCount } from './stock-store';
2
+ export { registerStockStore, getRegisteredStockStore, resolveStockStore, clearStockStores, } from './stock-store';
3
+ export type { StockStore } from './stock-store';
4
+ export { STOCK_TRACKING_MODES, DEFAULT_STOCK_TRACKING, STOCK_LOCATION_KINDS, STOCK_RESERVATION_STATUSES, STOCK_LOT_STATUSES, STOCK_MOVEMENT_DIRECTIONS, OWN_FULFILLMENT, } from './stock-types';
5
+ export type { StockTrackingMode, StockLocationKind, StockReservationStatus, StockLotStatus, StockMovementDirection, StockMovementKind, StockReasonCode, StockLocation, CreateStockLocationInput, UpdateStockLocationInput, StockLot, StockMovement, StockBalance, StockReservation, RecordStockMovementInput, ListStockBalancesOptions, ListStockLotsOptions, ListStockMovementsOptions, StockAllocationStrategy, StockAllocationCandidate, StockAllocationRequest, StockAllocation, ReserveStockInput, } from './stock-types';
6
+ export { CORE_STOCK_MOVEMENT_KINDS, CORE_STOCK_REASON_CODES, OPENING_BALANCE_REASON, LEGACY_WRITE_REASON, stockMovementKindRegistry, stockReasonCodeRegistry, registerCoreStockVocabulary, registerStockMovementKind, registerStockReasonCode, resolveStockMovementKind, resolveStockReasonCode, listStockMovementKindKeys, listStockReasonCodeKeys, listStockMovementKinds, listStockReasonCodes, clearStockVocabulary, validateStockMovement, } from './vocabulary';
7
+ export type { RegisterStockVocabularyOptions, StockMovementVariantFacts } from './vocabulary';
8
+ export { CORE_STOCK_ALLOCATION_STRATEGIES, CORE_RESERVATION_TTL_MINUTES, StockAllocationError, stockAllocationStrategyRegistry, registerCoreStockAllocationStrategies, registerStockAllocationStrategy, resolveStockAllocationStrategy, resolveAllocationStrategyKey, listStockAllocationStrategyKeys, listStockAllocationStrategies, clearStockAllocationStrategies, compareStockAllocationCandidates, allocateStock, resolveReservationTtlMinutes, } from './allocation';
9
+ export { SHOPIFY_DEFAULT_OPTION_NAME, SHOPIFY_DEFAULT_VARIANT_TITLE, SHOPIFY_QUANTITY_STATES, SHOPIFY_ADJUSTMENT_REASONS, BEYOND_SHOPIFY, TRACKING_MODES_BEYOND_SHOPIFY, toShopifyVariantTitle, shopifyDefaultOptions, shopifyReasonToStockReason, } from './shopify-mapping';
10
+ export { SupabaseStockStore, createSupabaseStockStore } from './supabase-stock-store';
11
+ export { MockStockStore, createMockStockStore } from './mock-stock-store';
12
+ export type { MockStockSeed, MockVariantSeed } from './mock-stock-store';
13
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/inventory/index.ts"],"names":[],"mappings":"AAyBA,OAAO,EAAE,kBAAkB,EAAE,eAAe,EAAE,iBAAiB,EAAE,sBAAsB,EAAE,MAAM,eAAe,CAAA;AAC9G,OAAO,EACL,kBAAkB,EAClB,uBAAuB,EACvB,iBAAiB,EACjB,gBAAgB,GACjB,MAAM,eAAe,CAAA;AACtB,YAAY,EAAE,UAAU,EAAE,MAAM,eAAe,CAAA;AAE/C,OAAO,EACL,oBAAoB,EACpB,sBAAsB,EACtB,oBAAoB,EACpB,0BAA0B,EAC1B,kBAAkB,EAClB,yBAAyB,EACzB,eAAe,GAChB,MAAM,eAAe,CAAA;AACtB,YAAY,EACV,iBAAiB,EACjB,iBAAiB,EACjB,sBAAsB,EACtB,cAAc,EACd,sBAAsB,EACtB,iBAAiB,EACjB,eAAe,EACf,aAAa,EACb,wBAAwB,EACxB,wBAAwB,EACxB,QAAQ,EACR,aAAa,EACb,YAAY,EACZ,gBAAgB,EAChB,wBAAwB,EACxB,wBAAwB,EACxB,oBAAoB,EACpB,yBAAyB,EACzB,uBAAuB,EACvB,wBAAwB,EACxB,sBAAsB,EACtB,eAAe,EACf,iBAAiB,GAClB,MAAM,eAAe,CAAA;AAEtB,OAAO,EACL,yBAAyB,EACzB,uBAAuB,EACvB,sBAAsB,EACtB,mBAAmB,EACnB,yBAAyB,EACzB,uBAAuB,EACvB,2BAA2B,EAC3B,yBAAyB,EACzB,uBAAuB,EACvB,wBAAwB,EACxB,sBAAsB,EACtB,yBAAyB,EACzB,uBAAuB,EACvB,sBAAsB,EACtB,oBAAoB,EACpB,oBAAoB,EACpB,qBAAqB,GACtB,MAAM,cAAc,CAAA;AACrB,YAAY,EAAE,8BAA8B,EAAE,yBAAyB,EAAE,MAAM,cAAc,CAAA;AAE7F,OAAO,EACL,gCAAgC,EAChC,4BAA4B,EAC5B,oBAAoB,EACpB,+BAA+B,EAC/B,qCAAqC,EACrC,+BAA+B,EAC/B,8BAA8B,EAC9B,4BAA4B,EAC5B,+BAA+B,EAC/B,6BAA6B,EAC7B,8BAA8B,EAC9B,gCAAgC,EAChC,aAAa,EACb,4BAA4B,GAC7B,MAAM,cAAc,CAAA;AAIrB,OAAO,EACL,2BAA2B,EAC3B,6BAA6B,EAC7B,uBAAuB,EACvB,0BAA0B,EAC1B,cAAc,EACd,6BAA6B,EAC7B,qBAAqB,EACrB,qBAAqB,EACrB,0BAA0B,GAC3B,MAAM,mBAAmB,CAAA;AAE1B,OAAO,EAAE,kBAAkB,EAAE,wBAAwB,EAAE,MAAM,wBAAwB,CAAA;AACrF,OAAO,EAAE,cAAc,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAA;AACzE,YAAY,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAA"}
@@ -0,0 +1,89 @@
1
+ import { stockBalanceKey } from './stock-store';
2
+ import type { StockStore } from './stock-store';
3
+ import type { ListStockBalancesOptions, ListStockLotsOptions, ListStockMovementsOptions, RecordStockMovementInput, ReserveStockInput, StockAllocation, StockAllocationRequest, StockBalance, StockLocation, StockLot, StockMovement, StockReservation, StockTrackingMode } from './stock-types';
4
+ export interface MockVariantSeed {
5
+ id: string;
6
+ tenantId?: string;
7
+ productId?: string | null;
8
+ tracking?: StockTrackingMode;
9
+ /** Starting mirror value. The seed writes NO movement for it — the opening
10
+ * balance is 0035's job and tests exercise it explicitly. */
11
+ inventoryCount?: number;
12
+ }
13
+ export interface MockStockSeed {
14
+ tenantId?: string;
15
+ variants?: MockVariantSeed[];
16
+ locations?: Partial<StockLocation>[];
17
+ lots?: Partial<StockLot>[];
18
+ }
19
+ /**
20
+ * An in-memory stock ledger. `variants` is the mirror surface: every recorded
21
+ * movement updates `inventoryCount` through the same projection the 0036
22
+ * trigger performs, so a test can assert the mirror without a database.
23
+ */
24
+ export declare class MockStockStore implements StockStore {
25
+ readonly tenantId: string;
26
+ private readonly locations;
27
+ private readonly lots;
28
+ private readonly movements;
29
+ private readonly reservations;
30
+ private readonly variants;
31
+ constructor(seed?: MockStockSeed);
32
+ private makeLocation;
33
+ listLocations(): Promise<StockLocation[]>;
34
+ getDefaultLocation(): Promise<StockLocation | null>;
35
+ listBalances(options?: ListStockBalancesOptions): Promise<StockBalance[]>;
36
+ getAvailable(variantId: string): Promise<number>;
37
+ listLots(options?: ListStockLotsOptions): Promise<StockLot[]>;
38
+ listMovements(options?: ListStockMovementsOptions): Promise<StockMovement[]>;
39
+ listReservations(variantId?: string): Promise<StockReservation[]>;
40
+ recordMovement(input: RecordStockMovementInput): Promise<StockMovement>;
41
+ /**
42
+ * Runs the SHARED allocateStock() over the mock's own balances and lots, so
43
+ * "fefo picked the right lot" is asserted against the same ordering the SQL
44
+ * executes rather than against a convenient imitation.
45
+ */
46
+ allocate(request: Omit<StockAllocationRequest, 'tracking'>): Promise<StockAllocation[]>;
47
+ private allocationCandidates;
48
+ reserve(input: ReserveStockInput): Promise<StockReservation>;
49
+ sweepExpiredReservations(variantId?: string): Promise<number>;
50
+ /** on_hand for one lot across every location — the serial-bound predicate. */
51
+ private lotOnHand;
52
+ private sellableLocationIds;
53
+ /** The 0036 mirror, in memory. */
54
+ private syncMirror;
55
+ /** The mirror column, for assertions. */
56
+ getInventoryCount(variantId: string): number | undefined;
57
+ /**
58
+ * The 0036 I10 shim: a direct write to `inventory_count` is allowed, logged
59
+ * and TRANSLATED into an adjustment movement carrying the DELTA, so the
60
+ * ledger never diverges from the column every current writer still uses.
61
+ * Returns the warning the SQL trigger would have raised.
62
+ */
63
+ writeInventoryCountDirectly(variantId: string, value: number): Promise<string | null>;
64
+ /** Rebuild balances by replaying the ledger — proof the balance is a cache. */
65
+ rebuildBalances(): Promise<StockBalance[]>;
66
+ /**
67
+ * Seed a hold. The lifecycle itself is §9 step 4; this exists so
68
+ * `available = onHand − reserved` is provable today.
69
+ *
70
+ * async, and the mirror is AWAITED rather than fired and forgotten: a
71
+ * floating promise here meant `getInventoryCount()` read the pre-hold value
72
+ * on the very next line, which is the assertion a reservation test makes.
73
+ */
74
+ addReservation(reservation: Omit<StockReservation, 'id' | 'tenantId' | 'createdAt' | 'updatedAt'> & {
75
+ id?: string;
76
+ }): Promise<StockReservation>;
77
+ /** Move a hold through its lifecycle, re-projecting the mirror. */
78
+ setReservationStatus(id: string, status: StockReservation['status']): Promise<void>;
79
+ /** Every ledger row, in insertion order. Nothing is ever removed from it. */
80
+ get ledger(): readonly StockMovement[];
81
+ /** The stock the variant was created with, unchanged by the mock. */
82
+ getTracking(variantId: string): StockTrackingMode | undefined;
83
+ /** Flip a variant's tracking policy (an admin action), for tests. */
84
+ setTracking(variantId: string, tracking: StockTrackingMode): void;
85
+ /** The balance key helper, re-exported for assertions that group by it. */
86
+ static key: typeof stockBalanceKey;
87
+ }
88
+ export declare function createMockStockStore(seed?: MockStockSeed): MockStockStore;
89
+ //# sourceMappingURL=mock-stock-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mock-stock-store.d.ts","sourceRoot":"","sources":["../../src/inventory/mock-stock-store.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,65 @@
1
+ import { stockBalanceKey } from './stock-store';
2
+ type Row = Record<string, any>;
3
+ /** The subset of PostgREST error shapes the store branches on. */
4
+ export interface PostgrestErrorShape {
5
+ code: string;
6
+ message: string;
7
+ }
8
+ /**
9
+ * The database. Tables hold snake_case rows exactly as Postgres would; the
10
+ * balance table is a VIEW over the ledger, computed on read, because 0034's
11
+ * balances are derived and a double that let them be set independently would
12
+ * make "balances follow the ledger" untestable by construction.
13
+ */
14
+ export declare class PostgrestDouble {
15
+ readonly tables: Record<string, Row[]>;
16
+ /** Every statement the double served — the assertion seam for "no second write". */
17
+ readonly log: string[];
18
+ constructor(seed?: {
19
+ variants?: Row[];
20
+ locations?: Row[];
21
+ lots?: Row[];
22
+ reservations?: Row[];
23
+ });
24
+ /** The client object handed to setGlobalSupabaseClient(). */
25
+ get client(): {
26
+ from: (table: string) => any;
27
+ rpc: (fn: string, args: Row) => any;
28
+ };
29
+ private callRpc;
30
+ private allocate;
31
+ private reserveRpc;
32
+ private sweep;
33
+ /**
34
+ * plg_shop_stock_balances, materialised on demand. Mirrors 0034's
35
+ * shop_rebuild_stock_balances(): SUM(movements) per (variant, location, lot),
36
+ * plus the SUM of HELD reservations as `reserved`, plus the generated
37
+ * `available = on_hand - reserved`.
38
+ */
39
+ private balanceRows;
40
+ private tenantOf;
41
+ private rowsFor;
42
+ /** What the ledger holds right now, for "nothing was written" assertions. */
43
+ get ledgerSize(): number;
44
+ private builder;
45
+ /**
46
+ * The write path, with exactly the three server-side behaviours the store
47
+ * DEPENDS on and therefore must be tested against:
48
+ *
49
+ * 1. the partial unique index on (tenant_id, idempotency_key) → 23505,
50
+ * 2. shop_default_stock_location() filling a NULL location_id, creating the
51
+ * tenant's default on demand,
52
+ * 3. the validator denormalising product_id from the variant.
53
+ *
54
+ * Everything else 0034 does is Postgres's job and is proven in SQL.
55
+ */
56
+ private insertRow;
57
+ /** shop_default_stock_location(): found, or created on demand. */
58
+ private defaultLocationId;
59
+ addReservation(row: Row): Row;
60
+ setReservationStatus(id: string, status: string): void;
61
+ /** The balance key helper, so assertions group the same way both sides do. */
62
+ static key: typeof stockBalanceKey;
63
+ }
64
+ export {};
65
+ //# sourceMappingURL=postgrest-double.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"postgrest-double.d.ts","sourceRoot":"","sources":["../../src/inventory/postgrest-double.ts"],"names":[],"mappings":"AAAA,OAAO,EAAqB,eAAe,EAAE,MAAM,eAAe,CAAA;AAgDlE,KAAK,GAAG,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;AAc9B,kEAAkE;AAClE,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,MAAM,CAAA;CAChB;AAqBD;;;;;GAKG;AACH,qBAAa,eAAe;IAC1B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,CAMrC;IAED,oFAAoF;IACpF,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,CAAK;gBAEf,IAAI,GAAE;QAAE,QAAQ,CAAC,EAAE,GAAG,EAAE,CAAC;QAAC,SAAS,CAAC,EAAE,GAAG,EAAE,CAAC;QAAC,IAAI,CAAC,EAAE,GAAG,EAAE,CAAC;QAAC,YAAY,CAAC,EAAE,GAAG,EAAE,CAAA;KAAO;IAOlG,6DAA6D;IAC7D,IAAI,MAAM,IAAI;QAAE,IAAI,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,GAAG,CAAC;QAAC,GAAG,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,KAAK,GAAG,CAAA;KAAE,CAKlF;IAcD,OAAO,CAAC,OAAO;IAiBf,OAAO,CAAC,QAAQ;IAyChB,OAAO,CAAC,UAAU;IA4BlB,OAAO,CAAC,KAAK;IAeb;;;;;OAKG;IACH,OAAO,CAAC,WAAW;IAiCnB,OAAO,CAAC,QAAQ;IAIhB,OAAO,CAAC,OAAO;IAOf,6EAA6E;IAC7E,IAAI,UAAU,IAAI,MAAM,CAEvB;IAID,OAAO,CAAC,OAAO;IAiIf;;;;;;;;;;OAUG;IACH,OAAO,CAAC,SAAS;IA0DjB,kEAAkE;IAClE,OAAO,CAAC,iBAAiB;IAsBzB,cAAc,CAAC,GAAG,EAAE,GAAG,GAAG,GAAG;IAa7B,oBAAoB,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI;IAMtD,8EAA8E;IAC9E,MAAM,CAAC,GAAG,yBAAkB;CAC7B"}
@@ -0,0 +1,67 @@
1
+ import type { StockTrackingMode } from './stock-types';
2
+ /**
3
+ * Shopify auto-creates one variant per option-less product, titled
4
+ * "Default Title", under a single option axis named "Title". We carry the
5
+ * SEMANTICS in `plg_shop_product_variants.is_default_variant` and the
6
+ * PLACEHOLDER STRINGS nowhere — because in our schema `plg_shop_product_options`
7
+ * is what renders the PDP picker and `plg_shop_product_variants.options` is what
8
+ * names the order line, so materialising them would be visible in exactly the
9
+ * two places the model must not touch. Shopify hides its placeholder in the
10
+ * theme (`product.has_only_default_variant`); we never create it.
11
+ *
12
+ * A connector writes these constants; it does not read them from a row.
13
+ */
14
+ export declare const SHOPIFY_DEFAULT_OPTION_NAME = "Title";
15
+ export declare const SHOPIFY_DEFAULT_VARIANT_TITLE = "Default Title";
16
+ /**
17
+ * `is_default_variant = true` ⟺ Shopify's `has_only_default_variant`. The whole
18
+ * translation, in one predicate.
19
+ */
20
+ export declare function toShopifyVariantTitle(isDefaultVariant: boolean, optionsLabel: string | null): string;
21
+ /** The option axes Shopify would report for a product with no real ones. */
22
+ export declare function shopifyDefaultOptions(): {
23
+ name: string;
24
+ values: string[];
25
+ }[];
26
+ /**
27
+ * Shopify's inventory quantity names → ours.
28
+ *
29
+ * `committed` is the only genuine synonym, and it is the one place we chose to
30
+ * keep OUR word in the schema: `reserved` is what 0034 shipped, what the
31
+ * contract suite, the SQL regression, the mirror and the mock all assert on, and
32
+ * renaming a landed green column for a synonym is churn. Instead the
33
+ * `plg_shop_stock_levels` view publishes BOTH — `reserved` and `committed` —
34
+ * so the connector reads Shopify's name and nothing internal moved.
35
+ */
36
+ export declare const SHOPIFY_QUANTITY_STATES: readonly {
37
+ shopify: string;
38
+ ours: string | null;
39
+ note: string;
40
+ }[];
41
+ /**
42
+ * Shopify's adjustment-reason vocabulary is CLOSED. Ours is rows and stays open
43
+ * (spine I9). Every Shopify reason must therefore land on one of ours without
44
+ * loss, or a connector needs a translation table with holes in it. Migration
45
+ * 0038 seeds the five rows that were missing.
46
+ *
47
+ * `null` means "there is no movement to write" and is not a gap — see the note.
48
+ */
49
+ export declare const SHOPIFY_ADJUSTMENT_REASONS: readonly {
50
+ shopify: string;
51
+ reasonCode: string | null;
52
+ movementKind: string | null;
53
+ note?: string;
54
+ }[];
55
+ /** The reason code a Shopify adjustment reason maps to, or null when it is not a movement. */
56
+ export declare function shopifyReasonToStockReason(shopifyReason: string): string | null;
57
+ /**
58
+ * Named so nobody "simplifies" them away in the name of Shopify parity. Each
59
+ * one is a fleet requirement, not a luxury.
60
+ */
61
+ export declare const BEYOND_SHOPIFY: readonly {
62
+ feature: string;
63
+ why: string;
64
+ }[];
65
+ /** Tracking modes that have no Shopify equivalent — kept deliberately. */
66
+ export declare const TRACKING_MODES_BEYOND_SHOPIFY: readonly StockTrackingMode[];
67
+ //# sourceMappingURL=shopify-mapping.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shopify-mapping.d.ts","sourceRoot":"","sources":["../../src/inventory/shopify-mapping.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAA;AAyBtD;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,2BAA2B,UAAU,CAAA;AAClD,eAAO,MAAM,6BAA6B,kBAAkB,CAAA;AAE5D;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,gBAAgB,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAEpG;AAED,4EAA4E;AAC5E,wBAAgB,qBAAqB,IAAI;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,EAAE,CAAA;CAAE,EAAE,CAE5E;AAID;;;;;;;;;GASG;AACH,eAAO,MAAM,uBAAuB,EAAE,SAAS;IAC7C,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;IACnB,IAAI,EAAE,MAAM,CAAA;CACb,EA4BA,CAAA;AAID;;;;;;;GAOG;AACH,eAAO,MAAM,0BAA0B,EAAE,SAAS;IAChD,OAAO,EAAE,MAAM,CAAA;IACf,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,IAAI,CAAC,EAAE,MAAM,CAAA;CACd,EAkBA,CAAA;AAED,8FAA8F;AAC9F,wBAAgB,0BAA0B,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAQ/E;AAID;;;GAGG;AACH,eAAO,MAAM,cAAc,EAAE,SAAS;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,EAqBrE,CAAA;AAED,0EAA0E;AAC1E,eAAO,MAAM,6BAA6B,EAAE,SAAS,iBAAiB,EAAsB,CAAA"}
@@ -0,0 +1,46 @@
1
+ import type { StockStore } from './stock-store';
2
+ import type { StockTrackingMode } from './stock-types';
3
+ export interface StockContractVariantSeed {
4
+ id: string;
5
+ productId?: string;
6
+ tracking?: StockTrackingMode;
7
+ }
8
+ export interface StockContractLocationSeed {
9
+ id: string;
10
+ code: string;
11
+ isSellable?: boolean;
12
+ isDefault?: boolean;
13
+ status?: 'active' | 'archived';
14
+ }
15
+ export interface StockContractLotSeed {
16
+ id: string;
17
+ variantId: string;
18
+ lotCode: string;
19
+ serial?: string | null;
20
+ expiresAt?: string | null;
21
+ receivedAt?: string;
22
+ }
23
+ export interface StockContractSeed {
24
+ tenantId: string;
25
+ variants: StockContractVariantSeed[];
26
+ locations: StockContractLocationSeed[];
27
+ lots: StockContractLotSeed[];
28
+ }
29
+ export interface StockStoreHarness {
30
+ store: StockStore;
31
+ /** Rows in the ledger, read behind the port. */
32
+ ledgerSize(): Promise<number>;
33
+ }
34
+ export type StockHarnessFactory = (seed: StockContractSeed) => Promise<StockStoreHarness>;
35
+ /** One seed, shared by both implementations, covering all four tracking modes. */
36
+ export declare function stockContractSeed(): StockContractSeed;
37
+ /**
38
+ * Run the whole contract against one implementation.
39
+ *
40
+ * Everything here goes through the StockStore interface. Where a test needs
41
+ * something the port does not expose (a reservation, the raw row count), it
42
+ * goes through the harness — and the harness is the only place the two
43
+ * implementations are allowed to differ.
44
+ */
45
+ export declare function describeStockStoreContract(name: string, createHarness: StockHarnessFactory): void;
46
+ //# sourceMappingURL=stock-contract.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stock-contract.d.ts","sourceRoot":"","sources":["../../src/inventory/stock-contract.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAA;AAC/C,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAA;AAwCtD,MAAM,WAAW,wBAAwB;IACvC,EAAE,EAAE,MAAM,CAAA;IACV,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,QAAQ,CAAC,EAAE,iBAAiB,CAAA;CAC7B;AAED,MAAM,WAAW,yBAAyB;IACxC,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,MAAM,CAAC,EAAE,QAAQ,GAAG,UAAU,CAAA;CAC/B;AAED,MAAM,WAAW,oBAAoB;IACnC,EAAE,EAAE,MAAM,CAAA;IACV,SAAS,EAAE,MAAM,CAAA;IACjB,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACtB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,MAAM,CAAA;IAChB,QAAQ,EAAE,wBAAwB,EAAE,CAAA;IACpC,SAAS,EAAE,yBAAyB,EAAE,CAAA;IACtC,IAAI,EAAE,oBAAoB,EAAE,CAAA;CAC7B;AAED,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,UAAU,CAAA;IACjB,gDAAgD;IAChD,UAAU,IAAI,OAAO,CAAC,MAAM,CAAC,CAAA;CAC9B;AAED,MAAM,MAAM,mBAAmB,GAAG,CAAC,IAAI,EAAE,iBAAiB,KAAK,OAAO,CAAC,iBAAiB,CAAC,CAAA;AAIzF,kFAAkF;AAClF,wBAAgB,iBAAiB,IAAI,iBAAiB,CA2BrD;AAED;;;;;;;GAOG;AACH,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,MAAM,EAAE,aAAa,EAAE,mBAAmB,GAAG,IAAI,CAyfjG"}
@@ -0,0 +1,96 @@
1
+ import type { ListStockBalancesOptions, ListStockLotsOptions, ListStockMovementsOptions, RecordStockMovementInput, ReserveStockInput, StockAllocation, StockAllocationRequest, StockBalance, StockLocation, StockLot, StockMovement, StockReservation, StockReservationStatus } from './stock-types';
2
+ /** Thrown when the ledger refuses a movement. Carries the input for logging. */
3
+ export declare class StockMovementError extends Error {
4
+ readonly input: RecordStockMovementInput;
5
+ constructor(message: string, input: RecordStockMovementInput);
6
+ }
7
+ export interface StockStore {
8
+ /** Every location, default first. Single-location stores see exactly one. */
9
+ listLocations(): Promise<StockLocation[]>;
10
+ /** The tenant's default location, created on demand server-side. */
11
+ getDefaultLocation(): Promise<StockLocation | null>;
12
+ /** Balance rows per (variant, lot, location). */
13
+ listBalances(options?: ListStockBalancesOptions): Promise<StockBalance[]>;
14
+ /**
15
+ * `available` for one variant, summed over sellable active locations — the
16
+ * same number the 0036 mirror publishes to inventory_count, so a caller can
17
+ * check the two agree without knowing how the mirror works.
18
+ */
19
+ getAvailable(variantId: string): Promise<number>;
20
+ listLots(options?: ListStockLotsOptions): Promise<StockLot[]>;
21
+ listMovements(options?: ListStockMovementsOptions): Promise<StockMovement[]>;
22
+ /**
23
+ * Append to the ledger. Never an update: a mistake is corrected by an
24
+ * opposing movement. An `idempotencyKey` that has already been used returns
25
+ * the EXISTING movement rather than writing a second one, so a retry is safe.
26
+ */
27
+ recordMovement(input: RecordStockMovementInput): Promise<StockMovement>;
28
+ listReservations(variantId?: string): Promise<StockReservation[]>;
29
+ /**
30
+ * Which lots cover a quantity, in the strategy's picking order (§4).
31
+ *
32
+ * ALLOCATES ONLY: no movement, no hold, no balance write. That separation is
33
+ * what lets shop_place_order decide what to do with the answer inside its own
34
+ * transaction, and what lets a picking screen show the merchant what WOULD be
35
+ * taken. Throws on a shortfall rather than answering short — backorder is
36
+ * §10 q3, answered no.
37
+ */
38
+ allocate(request: Omit<StockAllocationRequest, 'tracking'>): Promise<StockAllocation[]>;
39
+ /**
40
+ * Place a hold (§5). Add-to-cart NEVER calls this — carts are abandoned
41
+ * constantly and reserving there converts an abandoned cart into a phantom
42
+ * stockout. Checkout holds, payment consumes, the TTL releases.
43
+ */
44
+ reserve(input: ReserveStockInput): Promise<StockReservation>;
45
+ /**
46
+ * Move a hold through its lifecycle. `consumed` is what payment confirmation
47
+ * does; `released` is what a cancel or the sweeper does. Neither writes a
48
+ * movement here — consuming a hold into a `sale` is the trust boundary's job
49
+ * (shop_place_order / the order lifecycle trigger), not a client's.
50
+ */
51
+ setReservationStatus(reservationId: string, status: StockReservationStatus): Promise<void>;
52
+ /**
53
+ * Release every hold whose TTL has passed, and answer how many. Runs
54
+ * opportunistically at allocation time as well, so correctness never depends
55
+ * on a scheduler being alive (§5).
56
+ */
57
+ sweepExpiredReservations(variantId?: string): Promise<number>;
58
+ }
59
+ /** Identity of a balance row. The `-` sentinel stands for "no lot". */
60
+ export declare function stockBalanceKey(variantId: string, locationId: string, lotId: string | null): string;
61
+ /**
62
+ * Replay a ledger into balances — the operation that makes "the balance is only
63
+ * a cache" a checkable claim rather than a slogan. Mirrors
64
+ * `shop_rebuild_stock_balances()` in migration 0034.
65
+ *
66
+ * Held reservations subtract from `available` and nothing else; consumed and
67
+ * released ones do not, because a consumed hold has already become a `sale`
68
+ * movement and counting it twice is how phantom stockouts are made.
69
+ */
70
+ export declare function replayStockLedger(movements: readonly Pick<StockMovement, 'tenantId' | 'variantId' | 'locationId' | 'lotId' | 'quantity'>[], reservations?: readonly Pick<StockReservation, 'variantId' | 'locationId' | 'lotId' | 'quantity' | 'status'>[]): Map<string, {
71
+ tenantId: string;
72
+ variantId: string;
73
+ locationId: string;
74
+ lotId: string | null;
75
+ onHand: number;
76
+ reserved: number;
77
+ }>;
78
+ /**
79
+ * What the 0036 mirror writes to `plg_shop_product_variants.inventory_count`:
80
+ * available over SELLABLE ACTIVE locations, each row clamped at zero because
81
+ * 0028 declares `CHECK (inventory_count >= 0)` while the ledger deliberately
82
+ * permits a negative balance.
83
+ */
84
+ export declare function mirroredInventoryCount(balances: readonly Pick<StockBalance, 'locationId' | 'onHand' | 'reserved'>[], sellableLocationIds: ReadonlySet<string>): number;
85
+ /**
86
+ * Replace the stock store for one tenant — the seam an ERP connector uses when
87
+ * `plg_shop_settings.stock_ownership = 'external'` and Bling, not our ledger,
88
+ * owns the numbers. Callers are untouched; only the implementation changes.
89
+ */
90
+ export declare function registerStockStore(tenantId: string, store: StockStore): void;
91
+ export declare function getRegisteredStockStore(tenantId: string): StockStore | undefined;
92
+ /** Resolve a tenant's stock store: the registered override, else the fallback. */
93
+ export declare function resolveStockStore(tenantId: string, fallback: () => StockStore): StockStore;
94
+ /** Test seam. */
95
+ export declare function clearStockStores(): void;
96
+ //# sourceMappingURL=stock-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stock-store.d.ts","sourceRoot":"","sources":["../../src/inventory/stock-store.ts"],"names":[],"mappings":"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"}