@fayz-ai/shop 0.14.2 → 0.16.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/{chunk-STRNMFC4.js → chunk-26YK5V6P.js} +35 -6
  2. package/dist/chunk-26YK5V6P.js.map +1 -0
  3. package/dist/{chunk-U6HV3LVP.js → chunk-EUFSFVGS.js} +67 -5
  4. package/dist/chunk-EUFSFVGS.js.map +1 -0
  5. package/dist/{chunk-3GERSY5F.js → chunk-UVQ6UUDN.js} +5 -2
  6. package/dist/chunk-UVQ6UUDN.js.map +1 -0
  7. package/dist/{chunk-2WACO2TG.js → chunk-WWQICATY.js} +3 -3
  8. package/dist/{chunk-2WACO2TG.js.map → chunk-WWQICATY.js.map} +1 -1
  9. package/dist/data/checkout-gateways.d.ts +83 -0
  10. package/dist/data/checkout-gateways.d.ts.map +1 -1
  11. package/dist/data/functions.d.ts +3 -1
  12. package/dist/data/functions.d.ts.map +1 -1
  13. package/dist/data/mock-provider.d.ts +4 -0
  14. package/dist/data/mock-provider.d.ts.map +1 -1
  15. package/dist/data/provider.d.ts +106 -2
  16. package/dist/data/provider.d.ts.map +1 -1
  17. package/dist/data/supabase-provider.d.ts +39 -2
  18. package/dist/data/supabase-provider.d.ts.map +1 -1
  19. package/dist/data/tables.d.ts +51 -0
  20. package/dist/data/tables.d.ts.map +1 -1
  21. package/dist/index.d.ts +2 -2
  22. package/dist/index.d.ts.map +1 -1
  23. package/dist/index.js +329 -47
  24. package/dist/index.js.map +1 -1
  25. package/dist/inventory/postgrest-double.d.ts +9 -0
  26. package/dist/inventory/postgrest-double.d.ts.map +1 -1
  27. package/dist/inventory/supabase-stock-store.d.ts.map +1 -1
  28. package/dist/inventory/vocabulary.d.ts.map +1 -1
  29. package/dist/inventory.js +1 -1
  30. package/dist/lib/commerce.d.ts.map +1 -1
  31. package/dist/lib/message-events.d.ts.map +1 -1
  32. package/dist/lib/order-events.d.ts +5 -0
  33. package/dist/lib/order-events.d.ts.map +1 -0
  34. package/dist/mock.js +2 -2
  35. package/dist/rules/adapter.d.ts +3 -0
  36. package/dist/rules/adapter.d.ts.map +1 -1
  37. package/dist/rules.js +2 -2
  38. package/dist/runtime.js +3 -3
  39. package/dist/types.d.ts +96 -1
  40. package/dist/types.d.ts.map +1 -1
  41. package/package.json +4 -4
  42. package/dist/chunk-3GERSY5F.js.map +0 -1
  43. package/dist/chunk-STRNMFC4.js.map +0 -1
  44. package/dist/chunk-U6HV3LVP.js.map +0 -1
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/rules/rule-types.ts","../src/rules/attributes.ts","../src/rules/evaluator.ts","../src/rules/adapter.ts","../src/rules/context.ts"],"names":["Registry"],"mappings":"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","file":"chunk-3GERSY5F.js","sourcesContent":["// ---------------------------------------------------------------------------\n// The rule model (spine §3 L2, milestone M6) — ONE rule engine, as data.\n//\n// Adopted nearly verbatim from Medusa's promotion module because it is correct\n// (spine I8): `rule(attribute, operator)` with values in a child table, three\n// rule sets (eligibility / target / buy), and an application_method whose six\n// integers and two rule sets express buy-N-get-M, tiered, BOGO, percentage and\n// fixed across `each | across | once`. \"Buy 3 pay 2\" is pure data, zero code —\n// see evaluator.test.ts (the acceptance test).\n//\n// Everything in this module is SERIALIZABLE AND ENGINE-INDEPENDENT (spine I11):\n// the portable artifact is the rule schema, never the evaluator. A promotion\n// here survives JSON round-tripping, rides in the Store Document's\n// `promotions` section, and is evaluated identically by the TypeScript\n// reference implementation (evaluator.ts) and the SQL function\n// (migrations/0033) — the TS implementation is the spec, the SQL mirrors it.\n//\n// This is a LEAF module: types and enum vocabularies only, so the storefront\n// validator, the document section, and the evaluator can all import it\n// without dragging in providers.\n// ---------------------------------------------------------------------------\n\n/** The 7 rule operators. This is the closed set — widening it is a model\n * change (TS union + DB CHECK + SQL evaluator), not an insert. */\nexport const RULE_OPERATORS = ['gte', 'lte', 'gt', 'lt', 'eq', 'ne', 'in'] as const\nexport type RuleOperator = (typeof RULE_OPERATORS)[number]\n\n/** A rule value is a JSON scalar. The DB stores each in its own\n * plg_shop_rule_values row (values-in-a-child-table, per the model). */\nexport type RuleValue = string | number | boolean\n\n/**\n * One rule: `attribute` names a row of the rule_attribute VOCABULARY (I9 —\n * never a free-form dotted path; an unknown attribute is an I2 hard error\n * naming the valid set), `operator` is one of the 7, `values` is the child\n * list (a single-element list for scalar operators).\n */\nexport interface PromotionRule {\n attribute: string\n operator: RuleOperator\n values: RuleValue[]\n}\n\nexport const APPLICATION_METHOD_TYPES = ['percentage', 'fixed'] as const\nexport type ApplicationMethodType = (typeof APPLICATION_METHOD_TYPES)[number]\n\nexport const APPLICATION_METHOD_TARGET_TYPES = ['order', 'items', 'shipping'] as const\nexport type ApplicationMethodTargetType = (typeof APPLICATION_METHOD_TARGET_TYPES)[number]\n\nexport const APPLICATION_METHOD_ALLOCATIONS = ['each', 'across', 'once'] as const\nexport type ApplicationMethodAllocation = (typeof APPLICATION_METHOD_ALLOCATIONS)[number]\n\n/**\n * HOW an eligible promotion applies. The six numbers + two rule sets that make\n * buy-N-pay-M pure data:\n *\n * - `type` percentage (value = percentage points) or fixed (value = amount).\n * - `targetType` order (spread across all items) | items | shipping.\n * - `allocation` (items only):\n * each — per unit of each eligible line, `maxQuantity` units per line;\n * across — one amount over the eligible subtotal, split proportionally;\n * once — one unit of the cheapest eligible line, once.\n * - `buyRulesMinQuantity` + `buyRules` — what must be in the cart, and how\n * many, for the promotion to fire (buyget). The promotion applies\n * floor(matched buy quantity / buyRulesMinQuantity) times.\n * - `applyToQuantity` — how many target units each firing discounts.\n * - `targetRules` — which items the discount lands on. A buyget whose target\n * is not in the cart but is pinned to one product id emits addFreeItem.\n */\nexport interface ApplicationMethod {\n type: ApplicationMethodType\n value: number\n targetType: ApplicationMethodTargetType\n allocation?: ApplicationMethodAllocation\n /** Unit cap: per line for `each`; total discounted/free units for buyget. */\n maxQuantity?: number | null\n /** Buyget: target units discounted per firing. */\n applyToQuantity?: number | null\n /** Buyget: matched buy quantity required per firing. Null/absent = standard. */\n buyRulesMinQuantity?: number | null\n /** What gets discounted (the `target` pivot). Absent = every item. */\n targetRules?: PromotionRule[]\n /** What must be bought (the `buy` pivot). Item-scoped attributes only. */\n buyRules?: PromotionRule[]\n}\n\nexport const PROMOTION_TYPES = ['standard', 'buyget'] as const\nexport type PromotionType = (typeof PROMOTION_TYPES)[number]\n\nexport const PROMOTION_STATUSES = ['draft', 'active', 'inactive'] as const\nexport type PromotionStatus = (typeof PROMOTION_STATUSES)[number]\n\n/**\n * A promotion: eligibility rules (the `eligibility` pivot) + one application\n * method. `code` promotions fire only when the shopper entered the code\n * (cart.codes); `isAutomatic` promotions fire whenever eligible.\n */\nexport interface Promotion {\n id: string\n /** Coupon code. Null/absent = automatic-only. Matched case-insensitively. */\n code?: string | null\n isAutomatic?: boolean\n /** Derivable (buyRulesMinQuantity ⇒ buyget) but stored for the admin UI. */\n type?: PromotionType\n status?: PromotionStatus\n campaignId?: string | null\n /** Does this promotion apply at all? (the `eligibility` pivot). AND across\n * rules; a rule's values are OR. Absent/empty = always eligible. */\n rules?: PromotionRule[]\n applicationMethod: ApplicationMethod\n startsAt?: string | null\n endsAt?: string | null\n usageLimit?: number | null\n timesUsed?: number\n}\n\n/** A campaign groups promotions for reporting/budgets. Parent row only in M6;\n * budget enforcement is a later milestone. */\nexport interface Campaign {\n id: string\n name: string\n startsAt?: string | null\n endsAt?: string | null\n}\n\n// --- The cart snapshot the engine evaluates against ---------------------------\n\n/** One cart line as the engine sees it. Prices are authoritative-catalog\n * prices; the engine never trusts a client-computed total. */\nexport interface RuleCartItem {\n /** Line identity actions refer back to. Defaults to productId. */\n id?: string\n productId: string\n quantity: number\n unitPrice: number\n categoryId?: string | null\n collectionId?: string | null\n}\n\n/**\n * The evaluation input. Serializable on purpose: the SAME snapshot shape is\n * the TS evaluator's input and the SQL function's `p_cart` jsonb, so the\n * preview a shopper sees and the revalidation place_order runs read one\n * contract.\n */\nexport interface RuleCartSnapshot {\n items: RuleCartItem[]\n /** Quoted shipping — what a shipping-target promotion discounts. */\n shippingTotal?: number | null\n /** Shipping region/zone identifier (the shipping.region attribute). */\n region?: string | null\n customer?: { email?: string | null; tags?: string[] }\n /** Promotion codes the shopper entered (the code attribute; case-insensitive). */\n codes?: string[]\n currency?: string\n}\n\n// --- Compute actions (the engine's output) ------------------------------------\n\n/** Discount one cart line by an absolute amount (already allocation-resolved). */\nexport interface AddItemDiscountAction {\n action: 'addItemDiscount'\n promotionId: string\n code?: string | null\n itemId: string\n amount: number\n}\n\n/** Discount the shipping total by an absolute amount. */\nexport interface AddShippingDiscountAction {\n action: 'addShippingDiscount'\n promotionId: string\n code?: string | null\n amount: number\n}\n\n/** Add `quantity` free units of a product the buyget promotion grants but the\n * cart does not contain. The cart applies it by adding the line at price 0. */\nexport interface AddFreeItemAction {\n action: 'addFreeItem'\n promotionId: string\n code?: string | null\n productId: string\n quantity: number\n}\n\n/**\n * The ordered action list computePromotionActions returns. Deterministic:\n * buyget promotions first (they consume target units), then by specificity\n * (total rules count DESC), then id — so replaying the same cart against the\n * same promotions always yields byte-identical actions.\n */\nexport type ComputeAction = AddItemDiscountAction | AddShippingDiscountAction | AddFreeItemAction\n\n/** Sum of a cart's line totals, cents-rounded — the number `cart.subtotal`\n * rules compare against. */\nexport function cartSubtotal(cart: RuleCartSnapshot): number {\n return roundCents(cart.items.reduce((sum, item) => sum + item.unitPrice * item.quantity, 0))\n}\n\n/** Cents rounding, the house money convention (commerce.ts round2). */\nexport function roundCents(value: number): number {\n return Math.round(value * 100) / 100\n}\n","import { Registry } from '@fayz-ai/core'\nimport type { RegistryMeta } from '@fayz-ai/core'\nimport { RULE_OPERATORS } from './rule-types'\nimport type { RuleOperator } from './rule-types'\n\n// ---------------------------------------------------------------------------\n// The rule attribute VOCABULARY — data, not a hardcoded list (spine I9).\n//\n// Medusa's promotion engine accepts any dotted path, its admin whitelist is a\n// literal array in the API layer, and the real bound is a hardcoded 50-entry\n// field list that silently decides which rules can ever match — a rule on an\n// unknown attribute evaluates against `undefined` and just fails. The fix is\n// to publish `rule_attribute(key, path, label, field_type, value_source,\n// operators[])` as ROWS: the admin form renders itself from the same data the\n// AI reads (describeStore), validateStore checks documents against it, the\n// evaluator refuses attributes outside it (I2, never a silent non-match), and\n// WIDENING the vocabulary is an insert — defineRuleContextProvider for code,\n// a plain INSERT for SQL.\n//\n// The TS registry here and the plg_shop_rule_attributes seed rows (migration\n// 0032) carry the SAME core vocabulary; migrations.test.ts cross-checks the\n// two so they cannot drift.\n// ---------------------------------------------------------------------------\n\nexport const RULE_ATTRIBUTE_FIELD_TYPES = ['text', 'number', 'boolean'] as const\nexport type RuleAttributeFieldType = (typeof RULE_ATTRIBUTE_FIELD_TYPES)[number]\n\n/**\n * One vocabulary row.\n *\n * `path` locates the value in the evaluation context built from the cart\n * snapshot. Paths under `items.` are ITEM-scoped: they evaluate per cart line\n * (scoping target/buy rules to lines; for eligibility, \"some line matches\").\n * Every other path is CART-scoped, walked from the snapshot root. A context\n * provider's attributes resolve by KEY from the provided context instead.\n *\n * `value_source` names the id list an admin form offers as values\n * ('products', 'categories', …) — a UI hint, never an evaluation input.\n */\nexport interface RuleAttribute {\n key: string\n path: string\n label: string\n fieldType: RuleAttributeFieldType\n /** Where an admin form fetches candidate values. Null = free entry. */\n valueSource?: string | null\n /** The operators this attribute supports — a subset of the 7. */\n operators: readonly RuleOperator[]\n}\n\nconst ID_OPERATORS: readonly RuleOperator[] = ['eq', 'ne', 'in']\nconst NUMBER_OPERATORS: readonly RuleOperator[] = ['gte', 'lte', 'gt', 'lt', 'eq', 'ne']\n\n/** Prefix that marks an attribute path as item-scoped. */\nexport const ITEM_PATH_PREFIX = 'items.'\n\n/**\n * The core vocabulary — what every Fayz-backed store starts with, mirrored as\n * the plg_shop_rule_attributes seed rows in migration 0032.\n */\nexport const CORE_RULE_ATTRIBUTES: readonly RuleAttribute[] = [\n {\n key: 'code',\n path: 'codes',\n label: 'Cupom informado',\n fieldType: 'text',\n valueSource: null,\n operators: ['eq', 'in'],\n },\n {\n key: 'cart.subtotal',\n path: 'subtotal',\n label: 'Subtotal do carrinho',\n fieldType: 'number',\n valueSource: null,\n operators: NUMBER_OPERATORS,\n },\n {\n key: 'item.quantity',\n path: 'items.quantity',\n label: 'Quantidade do item',\n fieldType: 'number',\n valueSource: null,\n operators: NUMBER_OPERATORS,\n },\n {\n key: 'product.id',\n path: 'items.product_id',\n label: 'Produto',\n fieldType: 'text',\n valueSource: 'products',\n operators: ID_OPERATORS,\n },\n {\n key: 'product.category_id',\n path: 'items.category_id',\n label: 'Categoria do produto',\n fieldType: 'text',\n valueSource: 'categories',\n operators: ID_OPERATORS,\n },\n {\n key: 'product.collection_id',\n path: 'items.collection_id',\n label: 'Coleção do produto',\n fieldType: 'text',\n valueSource: 'collections',\n operators: ID_OPERATORS,\n },\n {\n key: 'customer.email',\n path: 'customer.email',\n label: 'E-mail do cliente',\n fieldType: 'text',\n valueSource: null,\n operators: ID_OPERATORS,\n },\n {\n key: 'customer.tag',\n path: 'customer.tags',\n label: 'Tag do cliente',\n fieldType: 'text',\n valueSource: 'customer_tags',\n operators: ['eq', 'ne', 'in'],\n },\n {\n key: 'shipping.region',\n path: 'shipping.region',\n label: 'Região de entrega',\n fieldType: 'text',\n valueSource: 'regions',\n operators: ID_OPERATORS,\n },\n]\n\n// --- The registry -------------------------------------------------------------\n// Uniform Registry<T> meta envelope (M1), same as blocks/loaders/handles, so\n// `describeRuleAttributes()` feeds describeStore through the same parser every\n// other registry uses.\n\nexport const ruleAttributeRegistry = new Registry<RuleAttribute>('ruleAttribute')\n\nlet coreRegistered = false\n\n/** Idempotent — mirrors registerStorefrontBlocks: consumers call before reads. */\nexport function registerCoreRuleAttributes(): void {\n if (coreRegistered && ruleAttributeRegistry.has('code')) return\n for (const attribute of CORE_RULE_ATTRIBUTES) {\n ruleAttributeRegistry.register(attribute.key, attribute, {\n title: attribute.label,\n description: `Rule attribute (${attribute.fieldType}) — operators: ${attribute.operators.join(', ')}`,\n source: 'sdk',\n })\n }\n coreRegistered = true\n}\n\nexport interface RegisterRuleAttributeOptions {\n meta?: RegistryMeta\n /** Re-registering an existing key is an error unless the caller owns it\n * (module re-evaluation under HMR re-registers the same declaration). */\n replace?: boolean\n}\n\n/**\n * Widen the vocabulary with one attribute. Duplicate keys are an I2 hard\n * error naming the taken set — two providers silently fighting over one key\n * would make rules mean different things on different renders.\n */\nexport function registerRuleAttribute(attribute: RuleAttribute, options: RegisterRuleAttributeOptions = {}): void {\n registerCoreRuleAttributes()\n if (!attribute.key) throw new Error('registerRuleAttribute: attribute.key is required')\n if (!RULE_ATTRIBUTE_FIELD_TYPES.includes(attribute.fieldType)) {\n throw new Error(\n `registerRuleAttribute: \"${attribute.key}\" has unknown fieldType \"${attribute.fieldType}\". ` +\n `Valid field types: ${RULE_ATTRIBUTE_FIELD_TYPES.join(', ')}`,\n )\n }\n for (const op of attribute.operators) {\n if (!RULE_OPERATORS.includes(op)) {\n throw new Error(\n `registerRuleAttribute: \"${attribute.key}\" declares unknown operator \"${op}\". ` +\n `Valid operators: ${RULE_OPERATORS.join(', ')}`,\n )\n }\n }\n if (ruleAttributeRegistry.has(attribute.key) && !options.replace) {\n throw new Error(\n `registerRuleAttribute: attribute \"${attribute.key}\" is already registered. ` +\n `Registered attributes: ${listRuleAttributeKeys().join(', ')}`,\n )\n }\n ruleAttributeRegistry.register(attribute.key, attribute, {\n title: attribute.label,\n description: `Rule attribute (${attribute.fieldType}) — operators: ${attribute.operators.join(', ')}`,\n source: 'app',\n ...options.meta,\n })\n}\n\n/** The attribute for a key, or an I2 hard error naming the valid set — the\n * only lookup the evaluator and the validators may use. */\nexport function resolveRuleAttribute(key: string): RuleAttribute {\n registerCoreRuleAttributes()\n const attribute = ruleAttributeRegistry.get(key)\n if (!attribute) {\n throw new Error(`Unknown rule attribute \"${key}\". Registered attributes: ${listRuleAttributeKeys().join(', ')}`)\n }\n return attribute\n}\n\nexport function hasRuleAttribute(key: string): boolean {\n registerCoreRuleAttributes()\n return ruleAttributeRegistry.has(key)\n}\n\n/** Every attribute key, sorted — the valid set I2 errors name. */\nexport function listRuleAttributeKeys(): string[] {\n registerCoreRuleAttributes()\n return ruleAttributeRegistry.ids().sort()\n}\n\n/** Every vocabulary row, sorted by key — what describeStore publishes and the\n * admin promotion form renders itself from. */\nexport function listRuleAttributes(): RuleAttribute[] {\n registerCoreRuleAttributes()\n return ruleAttributeRegistry\n .list()\n .map((entry) => entry.value)\n .sort((a, b) => a.key.localeCompare(b.key))\n}\n\n/** True when the attribute evaluates per cart line. */\nexport function isItemScopedAttribute(attribute: RuleAttribute): boolean {\n return attribute.path.startsWith(ITEM_PATH_PREFIX)\n}\n\n/** Test seam — suites that widen the vocabulary must not leak into the next.\n * Core attributes re-register on the next read. */\nexport function clearRuleAttributes(): void {\n ruleAttributeRegistry.clear()\n coreRegistered = false\n}\n","import { isItemScopedAttribute, resolveRuleAttribute } from './attributes'\nimport type { RuleAttribute } from './attributes'\nimport type { RuleContext } from './context'\nimport {\n cartSubtotal,\n roundCents,\n} from './rule-types'\nimport type {\n ApplicationMethod,\n ComputeAction,\n Promotion,\n PromotionRule,\n RuleCartItem,\n RuleCartSnapshot,\n RuleValue,\n} from './rule-types'\n\n// ---------------------------------------------------------------------------\n// computePromotionActions — the TypeScript REFERENCE implementation of the\n// rule engine (spine §3 L2, M6). Pure: (cart snapshot, promotions, context) →\n// ordered actions. No provider, no clock (injected), no I/O — which is what\n// makes it exhaustively unit-testable and runnable in the storefront preview,\n// the mock provider and (later) a server, unchanged.\n//\n// THIS FILE IS THE SPEC. The SQL function shop_compute_promotion_actions\n// (packages/shop/migrations/0033_rule_engine_eval.sql) mirrors these\n// semantics for the trust boundary — place_order revalidates server-side so\n// the browser can never set what it pays. Change semantics HERE first, with\n// tests, then re-emit the SQL to match.\n//\n// Semantics, precisely:\n// - A promotion is eligible when its status/date/usage gates pass, its code\n// (if any) was entered, and ALL eligibility rules match (a rule's values\n// are OR; item-scoped eligibility rules match when SOME line matches).\n// - Unknown rule attributes are an I2 hard error naming the valid set —\n// never a silent non-match (the Medusa failure I9 exists to end).\n// - Buyget (buyRulesMinQuantity ≥ 1): the promotion fires\n// floor(matched buy qty / buyRulesMinQuantity) times, discounting\n// applyToQuantity target units per firing, cheapest-eligible-units first,\n// capped by maxQuantity. A target pinned to one product id that is absent\n// from the cart becomes addFreeItem instead.\n// - Standard: percentage|fixed over order (spread proportionally) | items\n// (each: per-unit per line, maxQuantity units per line; across: one amount\n// split proportionally over the eligible subtotal; once: one unit of the\n// cheapest eligible line) | shipping.\n// - Output order is deterministic: buyget before standard (they consume\n// units), then total rules count DESC (specific before generic), then id.\n// ---------------------------------------------------------------------------\n\nexport interface EvaluateOptions {\n /** Extra attribute values, keyed by attribute KEY — the validated output of\n * buildRuleContext (context providers). Wins over snapshot resolution. */\n context?: RuleContext\n /** ISO timestamp the date-window gates compare against. Default: now. */\n now?: string\n}\n\n// --- Attribute resolution -----------------------------------------------------\n\n/** items.* path suffix → RuleCartItem field. The vocabulary speaks the wire's\n * snake_case (it is also the SQL function's vocabulary); the TS snapshot is\n * camelCase. */\nconst ITEM_FIELDS: Record<string, keyof RuleCartItem> = {\n quantity: 'quantity',\n unit_price: 'unitPrice',\n product_id: 'productId',\n category_id: 'categoryId',\n collection_id: 'collectionId',\n}\n\nfunction itemValue(item: RuleCartItem, attribute: RuleAttribute): unknown {\n const field = ITEM_FIELDS[attribute.path.slice('items.'.length)]\n return field ? item[field] : undefined\n}\n\n/** Cart-scoped resolution: context (by key) wins, then a dot-walk of the\n * snapshot-derived root. Returns a flat list — array values (codes, tags)\n * flatten so `eq` means membership. */\nfunction cartValues(cart: RuleCartSnapshot, attribute: RuleAttribute, context: RuleContext): unknown[] {\n if (attribute.key in context) return flatten(context[attribute.key])\n const root: Record<string, unknown> = {\n subtotal: cartSubtotal(cart),\n codes: (cart.codes ?? []).map((code) => code.trim().toUpperCase()),\n customer: { email: cart.customer?.email ?? null, tags: cart.customer?.tags ?? [] },\n shipping: { region: cart.region ?? null, total: cart.shippingTotal ?? null },\n }\n let value: unknown = root\n for (const segment of attribute.path.split('.')) {\n if (value === null || typeof value !== 'object') return []\n value = (value as Record<string, unknown>)[segment]\n }\n return flatten(value)\n}\n\nfunction flatten(value: unknown): unknown[] {\n if (value === null || value === undefined) return []\n return Array.isArray(value) ? value.filter((v) => v !== null && v !== undefined) : [value]\n}\n\n// --- Operator matching --------------------------------------------------------\n\nfunction scalarEq(a: unknown, b: RuleValue): boolean {\n if (typeof a === 'string' && typeof b === 'string') return a.toUpperCase() === b.toUpperCase()\n return a === b\n}\n\nfunction numeric(op: (a: number, b: number) => boolean, values: unknown[], ruleValues: RuleValue[]): boolean {\n const bound = Number(ruleValues[0])\n if (!Number.isFinite(bound)) return false\n return values.some((value) => {\n const n = Number(value)\n return Number.isFinite(n) && op(n, bound)\n })\n}\n\n/** One rule against a flat value list. Values are OR; `ne` is a negative and\n * therefore holds only when NO value equals any rule value. */\nexport function matchRuleValues(rule: PromotionRule, values: unknown[]): boolean {\n switch (rule.operator) {\n case 'eq':\n case 'in':\n return values.some((value) => rule.values.some((rv) => scalarEq(value, rv)))\n case 'ne':\n return !values.some((value) => rule.values.some((rv) => scalarEq(value, rv)))\n case 'gt':\n return numeric((a, b) => a > b, values, rule.values)\n case 'gte':\n return numeric((a, b) => a >= b, values, rule.values)\n case 'lt':\n return numeric((a, b) => a < b, values, rule.values)\n case 'lte':\n return numeric((a, b) => a <= b, values, rule.values)\n }\n}\n\nfunction ruleMatchesCart(rule: PromotionRule, cart: RuleCartSnapshot, context: RuleContext): boolean {\n const attribute = resolveRuleAttribute(rule.attribute)\n if (isItemScopedAttribute(attribute) && !(attribute.key in context)) {\n // Item-scoped at cart level: \"some line matches\".\n return cart.items.some((item) => matchRuleValues(rule, flatten(itemValue(item, attribute))))\n }\n return matchRuleValues(rule, cartValues(cart, attribute, context))\n}\n\nfunction ruleMatchesItem(rule: PromotionRule, item: RuleCartItem, cart: RuleCartSnapshot, context: RuleContext): boolean {\n const attribute = resolveRuleAttribute(rule.attribute)\n if (isItemScopedAttribute(attribute) && !(attribute.key in context)) {\n return matchRuleValues(rule, flatten(itemValue(item, attribute)))\n }\n // A cart-scoped rule inside a target/buy set gates the whole set.\n return matchRuleValues(rule, cartValues(cart, attribute, context))\n}\n\n/** ALL rules match (AND). Empty/absent = match. */\nexport function rulesMatchCart(rules: PromotionRule[] | undefined, cart: RuleCartSnapshot, context: RuleContext = {}): boolean {\n return (rules ?? []).every((rule) => ruleMatchesCart(rule, cart, context))\n}\n\nfunction itemsMatching(rules: PromotionRule[] | undefined, cart: RuleCartSnapshot, context: RuleContext): RuleCartItem[] {\n const list = rules ?? []\n return cart.items.filter((item) => list.every((rule) => ruleMatchesItem(rule, item, cart, context)))\n}\n\n// --- Eligibility gates --------------------------------------------------------\n\nexport type PromotionGateReason =\n | 'inactive'\n | 'not_started'\n | 'expired'\n | 'usage_limit'\n | 'code_not_entered'\n | 'rules_not_matched'\n\n/**\n * Why a promotion does NOT fire against this cart, or null when it does.\n * Shared by the evaluator and the coupon-surface adapter so a rejected code\n * reports the same machine-readable reason everywhere.\n */\nexport function promotionGateReason(\n promotion: Promotion,\n cart: RuleCartSnapshot,\n options: EvaluateOptions = {},\n): PromotionGateReason | null {\n const now = options.now ?? new Date().toISOString()\n if (promotion.status !== undefined && promotion.status !== 'active') return 'inactive'\n if (promotion.startsAt && promotion.startsAt > now) return 'not_started'\n if (promotion.endsAt && promotion.endsAt < now) return 'expired'\n if (promotion.usageLimit != null && (promotion.timesUsed ?? 0) >= promotion.usageLimit) return 'usage_limit'\n if (promotion.code) {\n const entered = (cart.codes ?? []).map((code) => code.trim().toUpperCase())\n if (!entered.includes(promotion.code.trim().toUpperCase())) return 'code_not_entered'\n } else if (promotion.isAutomatic === false) {\n // No code AND not automatic: nothing can ever trigger it.\n return 'code_not_entered'\n }\n if (!rulesMatchCart(promotion.rules, cart, options.context ?? {})) return 'rules_not_matched'\n return null\n}\n\n// --- Money helpers ------------------------------------------------------------\n\nfunction clampPercent(value: number): number {\n return Math.min(Math.max(value, 0), 100)\n}\n\n/** Split `total` across weights, in cents, largest-remainder — the parts sum\n * to exactly `total` so a spread discount never loses or invents a cent. */\nexport function splitProportionally(total: number, weights: number[]): number[] {\n const sum = weights.reduce((acc, weight) => acc + weight, 0)\n if (sum <= 0 || total <= 0) return weights.map(() => 0)\n const cents = Math.round(total * 100)\n const raw = weights.map((weight) => (cents * weight) / sum)\n const floors = raw.map(Math.floor)\n let remainder = cents - floors.reduce((acc, value) => acc + value, 0)\n const order = raw\n .map((value, index) => ({ index, frac: value - Math.floor(value) }))\n .sort((a, b) => b.frac - a.frac || a.index - b.index)\n for (const { index } of order) {\n if (remainder <= 0) break\n floors[index] += 1\n remainder -= 1\n }\n return floors.map((value) => value / 100)\n}\n\n// --- Per-promotion application ------------------------------------------------\n\ninterface ItemLike {\n item: RuleCartItem\n id: string\n}\n\nfunction lineId(item: RuleCartItem): string {\n return item.id ?? item.productId\n}\n\nfunction unitDiscount(method: ApplicationMethod, unitPrice: number): number {\n if (method.type === 'percentage') return roundCents((unitPrice * clampPercent(method.value)) / 100)\n return Math.min(method.value, unitPrice)\n}\n\nfunction isBuyget(promotion: Promotion): boolean {\n const min = promotion.applicationMethod.buyRulesMinQuantity\n return promotion.type === 'buyget' || (min != null && min >= 1)\n}\n\nfunction applyBuyget(promotion: Promotion, cart: RuleCartSnapshot, context: RuleContext): ComputeAction[] {\n const method = promotion.applicationMethod\n const min = method.buyRulesMinQuantity ?? 1\n const per = method.applyToQuantity ?? 1\n const buyQty = itemsMatching(method.buyRules, cart, context).reduce((sum, item) => sum + item.quantity, 0)\n const times = Math.floor(buyQty / Math.max(min, 1))\n if (times <= 0 || per <= 0) return []\n\n let grantUnits = times * per\n if (method.maxQuantity != null) grantUnits = Math.min(grantUnits, method.maxQuantity)\n if (grantUnits <= 0) return []\n\n // Cheapest eligible units first — the shopper pays for the dearer ones.\n const targets: ItemLike[] = itemsMatching(method.targetRules, cart, context)\n .map((item) => ({ item, id: lineId(item) }))\n .sort((a, b) => a.item.unitPrice - b.item.unitPrice || a.id.localeCompare(b.id))\n\n if (targets.length === 0) {\n // Nothing to discount in the cart. A target pinned to exactly one product\n // id is a grant of something addable — the free-item case.\n const pins = (method.targetRules ?? [])\n .filter((rule) => rule.attribute === 'product.id' && (rule.operator === 'eq' || rule.operator === 'in'))\n .flatMap((rule) => rule.values)\n if (pins.length === 1 && typeof pins[0] === 'string') {\n return [{\n action: 'addFreeItem',\n promotionId: promotion.id,\n code: promotion.code ?? null,\n productId: pins[0],\n quantity: grantUnits,\n }]\n }\n return []\n }\n\n const actions: ComputeAction[] = []\n let remaining = grantUnits\n for (const target of targets) {\n if (remaining <= 0) break\n const units = Math.min(target.item.quantity, remaining)\n const amount = roundCents(unitDiscount(method, target.item.unitPrice) * units)\n remaining -= units\n if (amount <= 0) continue\n actions.push({\n action: 'addItemDiscount',\n promotionId: promotion.id,\n code: promotion.code ?? null,\n itemId: target.id,\n amount,\n })\n }\n return actions\n}\n\nfunction applyStandard(promotion: Promotion, cart: RuleCartSnapshot, context: RuleContext): ComputeAction[] {\n const method = promotion.applicationMethod\n const code = promotion.code ?? null\n\n if (method.targetType === 'shipping') {\n const shipping = cart.shippingTotal ?? 0\n if (shipping <= 0) return []\n const amount =\n method.type === 'percentage'\n ? roundCents((shipping * clampPercent(method.value)) / 100)\n : roundCents(Math.min(method.value, shipping))\n if (amount <= 0) return []\n return [{ action: 'addShippingDiscount', promotionId: promotion.id, code, amount }]\n }\n\n const eligible: ItemLike[] =\n method.targetType === 'order'\n ? cart.items.map((item) => ({ item, id: lineId(item) }))\n : itemsMatching(method.targetRules, cart, context).map((item) => ({ item, id: lineId(item) }))\n if (eligible.length === 0) return []\n\n const lineTotals = eligible.map(({ item }) => roundCents(item.unitPrice * item.quantity))\n const eligibleTotal = roundCents(lineTotals.reduce((sum, value) => sum + value, 0))\n const emit = (amounts: number[]): ComputeAction[] =>\n amounts.flatMap((amount, index) =>\n amount > 0\n ? [{ action: 'addItemDiscount' as const, promotionId: promotion.id, code, itemId: eligible[index].id, amount }]\n : [],\n )\n\n const allocation = method.targetType === 'order' ? 'across' : (method.allocation ?? 'across')\n\n if (allocation === 'each') {\n return emit(\n eligible.map(({ item }) => {\n const units = method.maxQuantity != null ? Math.min(item.quantity, method.maxQuantity) : item.quantity\n return roundCents(unitDiscount(method, item.unitPrice) * units)\n }),\n )\n }\n\n if (allocation === 'once') {\n // One unit of the cheapest eligible line, once.\n let cheapest = 0\n for (let i = 1; i < eligible.length; i++) {\n if (eligible[i].item.unitPrice < eligible[cheapest].item.unitPrice) cheapest = i\n }\n const amounts = eligible.map(() => 0)\n amounts[cheapest] = unitDiscount(method, eligible[cheapest].item.unitPrice)\n return emit(amounts)\n }\n\n // across (and order): one amount over the eligible subtotal, split\n // proportionally so the parts sum exactly.\n const total =\n method.type === 'percentage'\n ? roundCents((eligibleTotal * clampPercent(method.value)) / 100)\n : roundCents(Math.min(method.value, eligibleTotal))\n return emit(splitProportionally(total, lineTotals))\n}\n\n// --- Ordering -----------------------------------------------------------------\n\nfunction totalRulesCount(promotion: Promotion): number {\n return (\n (promotion.rules?.length ?? 0) +\n (promotion.applicationMethod.targetRules?.length ?? 0) +\n (promotion.applicationMethod.buyRules?.length ?? 0)\n )\n}\n\n/** Deterministic evaluation order: buyget first, specific before generic, id\n * as the final tiebreak. Exported so the SQL mirror's ORDER BY has a named\n * TS counterpart to test against. */\nexport function orderPromotions(promotions: Promotion[]): Promotion[] {\n return [...promotions].sort((a, b) => {\n const buyget = Number(isBuyget(b)) - Number(isBuyget(a))\n if (buyget !== 0) return buyget\n const specificity = totalRulesCount(b) - totalRulesCount(a)\n if (specificity !== 0) return specificity\n return a.id.localeCompare(b.id)\n })\n}\n\n// --- The engine ---------------------------------------------------------------\n\n/**\n * Evaluate promotions against a cart snapshot → the ordered compute-actions\n * the cart applies. Pure and deterministic (inject `now` for date gates).\n */\nexport function computePromotionActions(\n cart: RuleCartSnapshot,\n promotions: Promotion[],\n options: EvaluateOptions = {},\n): ComputeAction[] {\n const context = options.context ?? {}\n const actions: ComputeAction[] = []\n for (const promotion of orderPromotions(promotions)) {\n if (promotionGateReason(promotion, cart, options) !== null) continue\n actions.push(\n ...(isBuyget(promotion)\n ? applyBuyget(promotion, cart, context)\n : applyStandard(promotion, cart, context)),\n )\n }\n return actions\n}\n\n/** Total item+shipping discount an action list carries — the preview number a\n * cart shows. Free items carry no amount (they enter the cart at price 0). */\nexport function actionsDiscountTotal(actions: ComputeAction[]): number {\n return roundCents(\n actions.reduce((sum, action) => sum + ('amount' in action ? action.amount : 0), 0),\n )\n}\n","import { promotionGateReason } from './evaluator'\nimport type { EvaluateOptions } from './evaluator'\nimport type { Discount } from '../types'\nimport type { ApplicationMethod, Promotion, PromotionRule, RuleCartSnapshot } from './rule-types'\n\n// ---------------------------------------------------------------------------\n// The legacy-coupon adapter — how the EXISTING discount surface keeps working\n// while its internals retire onto the rule engine (M6 deliverable: \"a percent\n// code = promotion with one eligibility rule on code + percentage application\n// method\").\n//\n// Nothing here invents semantics: every plg_shop_discounts row (and every\n// StorefrontDiscountConfig `{code, percent}` entry) is EXPRESSIBLE in the rule\n// model, so the adapter is a pure translation:\n//\n// code → eligibility rule {code eq CODE} (method 'code')\n// minSubtotal → eligibility rule {cart.subtotal gte N}\n// percentage → applicationMethod {type percentage, value}\n// fixed_amount → applicationMethod {type fixed, value}\n// free_shipping → applicationMethod {type percentage, value 100, target shipping}\n// appliesTo products/categories → target rules {product.id/category_id in [...]}\n//\n// `buy_x_get_y` is the one legacy type that CANNOT ride: the legacy row has no\n// fields for X or Y (which is itself the evidence the old model was too\n// small). The adapter returns null for it; a store that wants buyget writes a\n// real promotion, where buy-3-pay-2 is six integers of data.\n//\n// `oncePerCustomer` does not translate either — it is a usage constraint on\n// (promotion, customer), not a cart predicate, and stays enforced where it\n// always was: server-side in shop_place_order.\n// ---------------------------------------------------------------------------\n\n/** The storefront config shape (`config.discounts` entries). */\nexport interface StorefrontPercentCode {\n code: string\n percent: number\n title?: string\n}\n\n/** `{code, percent}` — the simplest promotion there is. */\nexport function storefrontDiscountToPromotion(discount: StorefrontPercentCode): Promotion {\n const code = discount.code.trim().toUpperCase()\n return {\n id: `config-discount:${code}`,\n code,\n type: 'standard',\n status: 'active',\n rules: [{ attribute: 'code', operator: 'eq', values: [code] }],\n applicationMethod: { type: 'percentage', value: discount.percent, targetType: 'order' },\n }\n}\n\n/**\n * A provider Discount row as a Promotion, or null for the untranslatable\n * legacy `buy_x_get_y` (see header). Pure; tested against the existing\n * coupon fixtures in adapter.test.ts.\n */\nexport function discountToPromotion(discount: Discount): Promotion | null {\n if (discount.type === 'buy_x_get_y') return null\n\n const rules: PromotionRule[] = []\n const code = discount.code ? discount.code.trim().toUpperCase() : null\n if (discount.method !== 'automatic' && code) {\n rules.push({ attribute: 'code', operator: 'eq', values: [code] })\n }\n if (discount.minSubtotal != null) {\n rules.push({ attribute: 'cart.subtotal', operator: 'gte', values: [discount.minSubtotal] })\n }\n\n let targetRules: PromotionRule[] | undefined\n if (discount.appliesTo === 'products' && discount.productIds.length > 0) {\n targetRules = [{ attribute: 'product.id', operator: 'in', values: [...discount.productIds] }]\n } else if (discount.appliesTo === 'categories' && discount.categoryIds.length > 0) {\n targetRules = [{ attribute: 'product.category_id', operator: 'in', values: [...discount.categoryIds] }]\n }\n\n let applicationMethod: ApplicationMethod\n if (discount.type === 'free_shipping') {\n applicationMethod = { type: 'percentage', value: 100, targetType: 'shipping' }\n } else {\n applicationMethod = {\n type: discount.type === 'percentage' ? 'percentage' : 'fixed',\n value: discount.value,\n // A scoped legacy coupon discounts its eligible lines; an unscoped one\n // is order-wide. Same split 0027 makes in shop_place_order.\n targetType: targetRules ? 'items' : 'order',\n ...(targetRules ? { allocation: 'across' as const, targetRules } : {}),\n }\n }\n\n return {\n id: `discount:${discount.id}`,\n code,\n isAutomatic: discount.method === 'automatic',\n type: 'standard',\n status: discount.status === 'active' ? 'active' : 'inactive',\n rules,\n applicationMethod,\n startsAt: discount.startsAt || null,\n endsAt: discount.endsAt || null,\n usageLimit: discount.usageLimit,\n timesUsed: discount.timesUsed,\n }\n}\n\n// --- The percent-preview surface ----------------------------------------------\n\nexport interface CouponPreview {\n valid: boolean\n /** Percentage points, when the promotion is an order-wide percentage — the\n * only shape the client-side percent cart model can preview. */\n percent: number\n /** Machine-readable reason when invalid — the useDiscountValidator message\n * vocabulary: not_found | not_started | expired | usage_limit | inactive |\n * rules_not_matched | unsupported. */\n reason?: string\n}\n\n/**\n * The client-side coupon preview, engine-backed: find the promotion carrying\n * the code, gate it through the SAME eligibility path the evaluator runs, and\n * report the percent when (and only when) it is an order-wide percentage —\n * anything richer is previewed as compute-actions and always revalidated\n * server-side at place_order.\n */\nexport function previewCouponPercent(\n promotions: Promotion[],\n code: string,\n cart: RuleCartSnapshot = { items: [] },\n options: EvaluateOptions = {},\n): CouponPreview {\n const normalized = code.trim().toUpperCase()\n if (!normalized) return { valid: false, percent: 0, reason: 'not_found' }\n const promotion = promotions.find((p) => (p.code ?? '').trim().toUpperCase() === normalized)\n if (!promotion) return { valid: false, percent: 0, reason: 'not_found' }\n\n const reason = promotionGateReason(promotion, { ...cart, codes: [normalized] }, options)\n if (reason === 'inactive') return { valid: false, percent: 0, reason: 'not_found' }\n if (reason !== null) return { valid: false, percent: 0, reason }\n\n const method = promotion.applicationMethod\n if (method.type !== 'percentage' || method.targetType !== 'order' || method.buyRulesMinQuantity != null) {\n return { valid: false, percent: 0, reason: 'unsupported' }\n }\n return { valid: true, percent: method.value }\n}\n","import { Registry } from '@fayz-ai/core'\nimport type { RegistryMeta } from '@fayz-ai/core'\nimport { registerRuleAttribute, ruleAttributeRegistry } from './attributes'\nimport type { RuleAttribute, RuleAttributeFieldType } from './attributes'\nimport type { RuleCartSnapshot } from './rule-types'\n\n// ---------------------------------------------------------------------------\n// Context providers — how the vocabulary WIDENS (spine I9, the fix over\n// Medusa).\n//\n// A context provider is ~15 lines of validated code (Medusa's\n// setPromotionContext shape) after which every rule referencing the new\n// attribute is pure data. Two deliberate fixes over the original:\n//\n// 1. The result is VALIDATED against the provider's own declaration — never\n// z.record(z.any()). A provider may only return the attribute keys it\n// declared, each value type-checked against the declared field type.\n// A violation is an I2 hard error naming the declared set, because a\n// context value of the wrong shape makes every rule on it silently\n// non-matching — the exact failure mode I9 exists to end.\n//\n// 2. Registration REGISTERS THE ATTRIBUTES BACK into the vocabulary registry,\n// so the admin form, validateStore and describeStore discover them the\n// moment the provider exists. In Medusa the context can carry attributes\n// the admin whitelist has never heard of; here the two cannot diverge\n// because they are one write.\n//\n// Registry on the uniform meta envelope, same as blocks/loaders (M1).\n// ---------------------------------------------------------------------------\n\n/** What a provider contributes: one context value per declared attribute key.\n * A value may be a scalar of the declared field type or an array of them\n * (e.g. customer tags); null/undefined mean \"no value\" and are dropped. */\nexport type RuleContext = Record<string, unknown>\n\nexport interface RuleContextAttributeInput extends Omit<RuleAttribute, 'path'> {\n /** Optional — context attributes resolve by KEY from the provided context,\n * so the path is display metadata only. Defaults to `context.<key>`. */\n path?: string\n}\n\nexport interface RuleContextProvider {\n /** Attributes this provider contributes — registered into the vocabulary. */\n attributes: readonly RuleContextAttributeInput[]\n /** Compute the context values for one evaluation. Pure of side effects. */\n resolve(cart: RuleCartSnapshot): Promise<RuleContext> | RuleContext\n}\n\nexport const ruleContextRegistry = new Registry<RuleContextProvider>('ruleContext')\n\n/**\n * Register a context provider. Its attributes enter the vocabulary\n * immediately (duplicate keys are the I2 hard error registerRuleAttribute\n * throws), so discovery and evaluation can never disagree about what exists.\n */\nexport function defineRuleContextProvider(\n id: string,\n provider: RuleContextProvider,\n meta: RegistryMeta = {},\n): void {\n if (!id) throw new Error('defineRuleContextProvider: id is required')\n if (!provider.attributes.length) {\n throw new Error(`defineRuleContextProvider: \"${id}\" declares no attributes — a provider that contributes nothing cannot widen the vocabulary`)\n }\n const replacing = ruleContextRegistry.has(id)\n for (const input of provider.attributes) {\n registerRuleAttribute(\n { ...input, path: input.path ?? `context.${input.key}` },\n { meta: { source: 'app', ...meta }, replace: replacing },\n )\n }\n ruleContextRegistry.register(id, provider, {\n description: `Rule context provider — contributes: ${provider.attributes.map((a) => a.key).join(', ')}`,\n source: 'app',\n ...meta,\n })\n}\n\nfunction typeOk(value: unknown, fieldType: RuleAttributeFieldType): boolean {\n if (fieldType === 'number') return typeof value === 'number' && Number.isFinite(value)\n if (fieldType === 'boolean') return typeof value === 'boolean'\n return typeof value === 'string'\n}\n\n/**\n * Validate one provider's resolved context against its declaration (I2):\n * unknown keys name the declared set; wrong-typed values name the expected\n * type. Returns only the declared, typed, non-null entries.\n */\nexport function validateRuleContextResult(id: string, provider: RuleContextProvider, result: RuleContext): RuleContext {\n const declared = new Map(provider.attributes.map((attribute) => [attribute.key, attribute]))\n const validated: RuleContext = {}\n for (const [key, value] of Object.entries(result)) {\n const attribute = declared.get(key)\n if (!attribute) {\n throw new Error(\n `Rule context provider \"${id}\" returned undeclared attribute \"${key}\". ` +\n `Declared attributes: ${[...declared.keys()].sort().join(', ')}`,\n )\n }\n if (value === null || value === undefined) continue\n const values = Array.isArray(value) ? value : [value]\n for (const entry of values) {\n if (!typeOk(entry, attribute.fieldType)) {\n throw new Error(\n `Rule context provider \"${id}\" returned ${JSON.stringify(entry)} for \"${key}\", ` +\n `which is declared as ${attribute.fieldType}`,\n )\n }\n }\n validated[key] = value\n }\n return validated\n}\n\n/**\n * Run every registered provider against the cart and merge the validated\n * results into one context record, keyed by attribute key — the `context`\n * input of computePromotionActions. Key collisions cannot happen here: the\n * vocabulary registry already refused the second registration.\n */\nexport async function buildRuleContext(cart: RuleCartSnapshot): Promise<RuleContext> {\n const context: RuleContext = {}\n for (const entry of ruleContextRegistry.list()) {\n const result = await entry.value.resolve(cart)\n Object.assign(context, validateRuleContextResult(entry.id, entry.value, result))\n }\n return context\n}\n\n/** Test seam. Clearing providers does not un-register their vocabulary rows —\n * pair with clearRuleAttributes() in suite teardown. */\nexport function clearRuleContextProviders(): void {\n ruleContextRegistry.clear()\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/inventory/stock-store.ts","../src/inventory/stock-types.ts","../src/inventory/vocabulary.ts","../src/inventory/allocation.ts","../src/inventory/shopify-mapping.ts","../src/data/tables.ts","../src/inventory/supabase-stock-store.ts","../src/inventory/mock-stock-store.ts"],"names":["Registry","coreRegistered","error"],"mappings":"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","file":"chunk-STRNMFC4.js","sourcesContent":["import type {\n ListStockBalancesOptions,\n ListStockLotsOptions,\n ListStockMovementsOptions,\n RecordStockMovementInput,\n ReserveStockInput,\n StockAllocation,\n StockAllocationRequest,\n StockBalance,\n StockLocation,\n StockLot,\n StockMovement,\n StockReservation,\n StockReservationStatus,\n} from './stock-types'\n\n// ---------------------------------------------------------------------------\n// StockStore — the read/append surface over the inventory ledger, and the I5\n// storage indirection for it.\n//\n// Deliberately NOT wired into ShopProvider or shop_place_order. Allocation and\n// the place_order rewrite are §9 step 4 and they rewrite the SECURITY DEFINER\n// trust boundary; stock that can be allocated from the client is stock that can\n// be lied about. What ships here is the honest half: reading balances and lots,\n// and APPENDING movements — which is exactly the surface a receiving screen, a\n// CSV import and an ERP connector need, and §10 q4 names receiving as the first\n// thing a real store touches.\n//\n// Two implementations, the same shape as the M4 EntityStore: the Supabase one\n// over migrations 0034–0036, and an in-memory mock. Both run the SAME\n// validator (validateStockMovement) and the same ledger→balance arithmetic, so\n// a test that passes against the mock is asserting the real semantics and not a\n// convenient imitation.\n// ---------------------------------------------------------------------------\n\n/** Thrown when the ledger refuses a movement. Carries the input for logging. */\nexport class StockMovementError extends Error {\n constructor(\n message: string,\n readonly input: RecordStockMovementInput,\n ) {\n super(message)\n this.name = 'StockMovementError'\n }\n}\n\nexport interface StockStore {\n /** Every location, default first. Single-location stores see exactly one. */\n listLocations(): Promise<StockLocation[]>\n /** The tenant's default location, created on demand server-side. */\n getDefaultLocation(): Promise<StockLocation | null>\n\n /** Balance rows per (variant, lot, location). */\n listBalances(options?: ListStockBalancesOptions): Promise<StockBalance[]>\n /**\n * `available` for one variant, summed over sellable active locations — the\n * same number the 0036 mirror publishes to inventory_count, so a caller can\n * check the two agree without knowing how the mirror works.\n */\n getAvailable(variantId: string): Promise<number>\n\n listLots(options?: ListStockLotsOptions): Promise<StockLot[]>\n listMovements(options?: ListStockMovementsOptions): Promise<StockMovement[]>\n\n /**\n * Append to the ledger. Never an update: a mistake is corrected by an\n * opposing movement. An `idempotencyKey` that has already been used returns\n * the EXISTING movement rather than writing a second one, so a retry is safe.\n */\n recordMovement(input: RecordStockMovementInput): Promise<StockMovement>\n\n listReservations(variantId?: string): Promise<StockReservation[]>\n\n // --- §9 step 4: allocation and the reservation lifecycle --------------------\n // These three verbs did not exist while the ledger was read-only. The contract\n // suite carried them \"out of band\" through the harness with a comment saying\n // \"the port has no verb for this until §9 step 4 lands\". It has landed, so\n // they are promoted into the port and the harness keeps only the one thing a\n // port genuinely cannot expose — the raw row count behind it.\n\n /**\n * Which lots cover a quantity, in the strategy's picking order (§4).\n *\n * ALLOCATES ONLY: no movement, no hold, no balance write. That separation is\n * what lets shop_place_order decide what to do with the answer inside its own\n * transaction, and what lets a picking screen show the merchant what WOULD be\n * taken. Throws on a shortfall rather than answering short — backorder is\n * §10 q3, answered no.\n */\n allocate(request: Omit<StockAllocationRequest, 'tracking'>): Promise<StockAllocation[]>\n\n /**\n * Place a hold (§5). Add-to-cart NEVER calls this — carts are abandoned\n * constantly and reserving there converts an abandoned cart into a phantom\n * stockout. Checkout holds, payment consumes, the TTL releases.\n */\n reserve(input: ReserveStockInput): Promise<StockReservation>\n\n /**\n * Move a hold through its lifecycle. `consumed` is what payment confirmation\n * does; `released` is what a cancel or the sweeper does. Neither writes a\n * movement here — consuming a hold into a `sale` is the trust boundary's job\n * (shop_place_order / the order lifecycle trigger), not a client's.\n */\n setReservationStatus(reservationId: string, status: StockReservationStatus): Promise<void>\n\n /**\n * Release every hold whose TTL has passed, and answer how many. Runs\n * opportunistically at allocation time as well, so correctness never depends\n * on a scheduler being alive (§5).\n */\n sweepExpiredReservations(variantId?: string): Promise<number>\n}\n\n// --- Shared arithmetic --------------------------------------------------------\n// The ledger→balance reduction, written once so the mock cannot drift from the\n// SQL trigger it stands in for.\n\n/** Identity of a balance row. The `-` sentinel stands for \"no lot\". */\nexport function stockBalanceKey(variantId: string, locationId: string, lotId: string | null): string {\n return `${variantId}::${locationId}::${lotId ?? '-'}`\n}\n\n/**\n * Replay a ledger into balances — the operation that makes \"the balance is only\n * a cache\" a checkable claim rather than a slogan. Mirrors\n * `shop_rebuild_stock_balances()` in migration 0034.\n *\n * Held reservations subtract from `available` and nothing else; consumed and\n * released ones do not, because a consumed hold has already become a `sale`\n * movement and counting it twice is how phantom stockouts are made.\n */\nexport function replayStockLedger(\n movements: readonly Pick<StockMovement, 'tenantId' | 'variantId' | 'locationId' | 'lotId' | 'quantity'>[],\n reservations: readonly Pick<\n StockReservation,\n 'variantId' | 'locationId' | 'lotId' | 'quantity' | 'status'\n >[] = [],\n): Map<string, { tenantId: string; variantId: string; locationId: string; lotId: string | null; onHand: number; reserved: number }> {\n const balances = new Map<\n string,\n { tenantId: string; variantId: string; locationId: string; lotId: string | null; onHand: number; reserved: number }\n >()\n\n for (const movement of movements) {\n // A movement whose variant was deleted keeps its history but has no\n // balance to belong to — the balance CASCADEd away with the variant.\n if (!movement.variantId) continue\n const key = stockBalanceKey(movement.variantId, movement.locationId, movement.lotId)\n const row = balances.get(key) ?? {\n tenantId: movement.tenantId,\n variantId: movement.variantId,\n locationId: movement.locationId,\n lotId: movement.lotId,\n onHand: 0,\n reserved: 0,\n }\n row.onHand += movement.quantity\n balances.set(key, row)\n }\n\n for (const reservation of reservations) {\n if (reservation.status !== 'held') continue\n const key = stockBalanceKey(reservation.variantId, reservation.locationId, reservation.lotId)\n const row = balances.get(key)\n if (row) {\n row.reserved += reservation.quantity\n continue\n }\n // A hold placed before any receipt is legitimate (backorder). The row is\n // created so `available` reads -N rather than nothing at all.\n balances.set(key, {\n tenantId: '',\n variantId: reservation.variantId,\n locationId: reservation.locationId,\n lotId: reservation.lotId,\n onHand: 0,\n reserved: reservation.quantity,\n })\n }\n\n return balances\n}\n\n/**\n * What the 0036 mirror writes to `plg_shop_product_variants.inventory_count`:\n * available over SELLABLE ACTIVE locations, each row clamped at zero because\n * 0028 declares `CHECK (inventory_count >= 0)` while the ledger deliberately\n * permits a negative balance.\n */\nexport function mirroredInventoryCount(\n balances: readonly Pick<StockBalance, 'locationId' | 'onHand' | 'reserved'>[],\n sellableLocationIds: ReadonlySet<string>,\n): number {\n let total = 0\n for (const balance of balances) {\n if (!sellableLocationIds.has(balance.locationId)) continue\n total += Math.max(balance.onHand - balance.reserved, 0)\n }\n return Math.max(total, 0)\n}\n\n// --- Per-tenant store resolution (the promotion seam, I5) ---------------------\n\nconst storeOverrides = new Map<string, StockStore>()\n\n/**\n * Replace the stock store for one tenant — the seam an ERP connector uses when\n * `plg_shop_settings.stock_ownership = 'external'` and Bling, not our ledger,\n * owns the numbers. Callers are untouched; only the implementation changes.\n */\nexport function registerStockStore(tenantId: string, store: StockStore): void {\n storeOverrides.set(tenantId, store)\n}\n\nexport function getRegisteredStockStore(tenantId: string): StockStore | undefined {\n return storeOverrides.get(tenantId)\n}\n\n/** Resolve a tenant's stock store: the registered override, else the fallback. */\nexport function resolveStockStore(tenantId: string, fallback: () => StockStore): StockStore {\n return storeOverrides.get(tenantId) ?? fallback()\n}\n\n/** Test seam. */\nexport function clearStockStores(): void {\n storeOverrides.clear()\n}\n","// ---------------------------------------------------------------------------\n// The inventory model as serializable, engine-independent types.\n//\n// Stock stops being a number that is EDITED and becomes a number that is\n// DERIVED (docs/storefront/inventory-model.md §1):\n//\n// movements (append-only, signed) ──▶ balances (variant × lot × location)\n// │\n// reservations ─────┤\n// ▼\n// available = onHand − reserved\n//\n// These types mirror migrations 0034–0036 one-for-one. They carry no evaluator\n// and no transport: the portable artifact is the SHAPE, never the engine (I11),\n// which is what lets the same model be served by Postgres today, by a mock in\n// tests, and by a second backend later.\n// ---------------------------------------------------------------------------\n\n/**\n * The per-variant tracking policy — a closed set, deliberately (§3).\n *\n * Unlike movement kinds and reason codes, these are NOT rows: each value\n * changes what the engine does, so a tenant-invented mode would be data no\n * code could honour. Widening this is a model change — this union, the\n * `plg_shop_product_variants_tracking_check` CHECK, and the validator, together.\n *\n * - `none` — never depletes. Made-to-order, digital, services.\n * - `quantity` — one implicit lot. THE DEFAULT, so existing stores are unchanged.\n * - `lot` — batches with expiry; allocation is FEFO.\n * - `serial` — every unit is unique; a serial is a lot whose quantity is 1.\n */\nexport const STOCK_TRACKING_MODES = ['none', 'quantity', 'lot', 'serial'] as const\nexport type StockTrackingMode = (typeof STOCK_TRACKING_MODES)[number]\n\n/** The default every variant gets, so adopting the ledger costs a simple store nothing. */\nexport const DEFAULT_STOCK_TRACKING: StockTrackingMode = 'quantity'\n\n/** Closed set: allocation branches on it (transit stock cannot be promised). */\nexport const STOCK_LOCATION_KINDS = ['store', 'warehouse', 'transit', 'virtual'] as const\nexport type StockLocationKind = (typeof STOCK_LOCATION_KINDS)[number]\n\n/** Closed set: the §5 reservation lifecycle. */\nexport const STOCK_RESERVATION_STATUSES = ['held', 'consumed', 'released'] as const\nexport type StockReservationStatus = (typeof STOCK_RESERVATION_STATUSES)[number]\n\nexport const STOCK_LOT_STATUSES = ['active', 'quarantined', 'expired', 'consumed'] as const\nexport type StockLotStatus = (typeof STOCK_LOT_STATUSES)[number]\n\n/**\n * The sign contract a movement kind carries. It lives in the VOCABULARY ROW,\n * not in the validator, which is precisely what lets a tenant add a kind.\n */\nexport const STOCK_MOVEMENT_DIRECTIONS = ['in', 'out', 'either'] as const\nexport type StockMovementDirection = (typeof STOCK_MOVEMENT_DIRECTIONS)[number]\n\n/** One movement-kind vocabulary row (plg_shop_stock_movement_kinds). */\nexport interface StockMovementKind {\n key: string\n label: string\n direction: StockMovementDirection\n}\n\n/**\n * One reason-code vocabulary row (plg_shop_stock_reason_codes).\n *\n * `movementKind` null means \"valid for any kind\"; naming one makes the pair\n * checkable, so 'breakage' can never be recorded as a receipt.\n */\nexport interface StockReasonCode {\n key: string\n label: string\n movementKind: string | null\n}\n\n/**\n * `fulfillment_provider` when the merchant dispatches by hand — a decision, not\n * an absent one, which is why it is stored instead of inferred from \"no gateway\".\n */\nexport const OWN_FULFILLMENT = 'own'\n\n/** Where stock sits, and — since 0058 — what ships from there. */\nexport interface StockLocation {\n id: string\n tenantId: string\n code: string\n name: string\n kind: StockLocationKind\n /** Stock here can be promised to a shopper. Transit stock cannot. */\n isSellable: boolean\n isDefault: boolean\n status: 'active' | 'archived'\n /** 8 bare digits. The origin a freight gateway quotes from. */\n postalCode?: string | null\n city?: string | null\n state?: string | null\n street?: string | null\n number?: string | null\n district?: string | null\n complement?: string | null\n /** CNPJ/CPF, digits only — the carrier prints it on the label (0061). */\n document?: string | null\n phone?: string | null\n email?: string | null\n /**\n * Where THIS place drops its parcels, per carrier (0062):\n * `{ 'melhor-envio': { id: '49276', name: 'Jadlog Barra' } }`.\n *\n * Per carrier because the choice is: the same warehouse can drop one\n * carrier's parcels downtown and another's next door. Empty means \"the\n * nearest agency that accepts the service\", which is what dispatch does.\n */\n carrierAgencies?: Record<string, { id: string; name?: string }> | null\n /** Online orders may be dispatched from here. */\n shipsOnline?: boolean\n /** null follows the store, `OWN_FULFILLMENT` is by hand, else a connector id. */\n fulfillmentProvider?: string | null\n createdAt: string\n updatedAt: string\n}\n\nexport interface CreateStockLocationInput {\n name: string\n /** Stable handle for a CSV import or an ERP connector. Defaults to a slug of the name. */\n code?: string\n kind?: StockLocationKind\n isSellable?: boolean\n isDefault?: boolean\n status?: 'active' | 'archived'\n postalCode?: string | null\n city?: string | null\n state?: string | null\n street?: string | null\n number?: string | null\n district?: string | null\n complement?: string | null\n document?: string | null\n phone?: string | null\n email?: string | null\n carrierAgencies?: Record<string, { id: string; name?: string }> | null\n shipsOnline?: boolean\n fulfillmentProvider?: string | null\n}\n\nexport type UpdateStockLocationInput = Partial<CreateStockLocationInput>\n\n/** A batch. A serial-tracked unit is a lot whose quantity is 1. */\nexport interface StockLot {\n id: string\n tenantId: string\n variantId: string\n lotCode: string\n serial: string | null\n /** Calendar dates, not timestamps: FEFO order must not depend on a time zone. */\n manufacturedAt: string | null\n expiresAt: string | null\n receivedAt: string\n unitCost: number | null\n supplierRef: string | null\n status: StockLotStatus\n createdAt: string\n updatedAt: string\n}\n\n/**\n * One ledger row. Append-only and signed: a mistake is corrected by an\n * OPPOSING movement, never by an edit, which is what makes the ledger\n * auditable.\n */\nexport interface StockMovement {\n id: string\n tenantId: string\n /** Null only after the variant row was deleted — the history survives it. */\n variantId: string | null\n productId: string | null\n locationId: string\n lotId: string | null\n kind: string\n reasonCode: string | null\n /** Signed. The kind's `direction` decides which signs are legal. */\n quantity: number\n unitCost: number | null\n orderId: string | null\n reservationId: string | null\n idempotencyKey: string | null\n /** WHEN it happened, which is not when it was recorded. */\n occurredAt: string\n createdBy: string | null\n note: string | null\n createdAt: string\n}\n\n/** What a caller supplies to append to the ledger. */\nexport interface RecordStockMovementInput {\n variantId: string\n kind: string\n quantity: number\n reasonCode?: string | null\n /** Omitted ⇒ the tenant's default location, created on demand. */\n locationId?: string | null\n lotId?: string | null\n unitCost?: number | null\n orderId?: string | null\n reservationId?: string | null\n /** A retried write must not decrement twice. */\n idempotencyKey?: string | null\n occurredAt?: string\n note?: string | null\n}\n\n/**\n * The materialised answer per (variant, lot, location) — an index over the\n * ledger, not a second truth. `onHand` may be NEGATIVE: an over-recorded loss\n * must stay visible rather than be refused, because refusing it would make the\n * LEDGER lie. The clamp happens once, at the inventory_count mirror.\n *\n * ---------------------------------------------------------------------------\n * NAME MAPPING TO SHOPIFY, so a connector never needs a translation table.\n *\n * Shopify's quantity states are `available`, `committed`, `on_hand`,\n * `incoming`, plus `damaged`, `safety_stock` and `quality_control`. Ours:\n *\n * ours Shopify note\n * ──────────── ───────────── ──────────────────────────────────────────────\n * onHand on_hand identical\n * available available identical (onHand − reserved)\n * reserved committed SAME CONCEPT, different word. Ours is the\n * schema name (0034, landed and green); theirs\n * is published beside it by the\n * plg_shop_stock_levels view, which exposes both\n * `reserved` and `committed` columns. Renaming\n * the column instead would have churned an\n * already-green contract suite, the SQL\n * regression, the mirror and the mock for a\n * synonym.\n * (a non-sellable location's\n * balance) incoming we model it as a PLACE (`kind = 'transit'`,\n * `is_sellable = false`) rather than as a fourth\n * number, which is strictly more expressive:\n * two warehouses in transit are two rows.\n * (a `loss` movement with\n * reason `damaged`) damaged ours is a ledger entry with a reason code, so\n * it carries when, who and how many; Shopify's\n * is a bucket.\n * — safety_stock NOT MODELLED. Reorder point and low-stock\n * threshold are §8's \"forecasting\", deliberately\n * out of scope.\n * (a `lot` with status\n * 'quarantined') quality_control ours is a lot state, which is more precise:\n * it is a BATCH that is under inspection, not a\n * quantity.\n *\n * Where we go BEYOND Shopify we keep it: lots, `expiresAt`, FEFO and serial\n * tracking have no native Shopify equivalent (merchants buy apps for both), and\n * they are exactly what artorious and cristinarotondaro need. \"Padrão Shopify\"\n * means compatible-by-default, never limited-to. The machine-readable form of\n * this table is packages/shop/src/inventory/shopify-mapping.ts.\n */\nexport interface StockBalance {\n id: string\n tenantId: string\n variantId: string\n locationId: string\n lotId: string | null\n onHand: number\n reserved: number\n /** onHand − reserved. What the mirror publishes to the storefront. */\n available: number\n updatedAt: string\n}\n\n/** A hold. Add-to-cart does NOT reserve; checkout start does (§5). */\nexport interface StockReservation {\n id: string\n tenantId: string\n variantId: string\n locationId: string\n lotId: string | null\n quantity: number\n status: StockReservationStatus\n orderId: string | null\n cartToken: string | null\n /** The TTL that makes pix and boleto honest. */\n expiresAt: string | null\n idempotencyKey: string | null\n createdBy: string | null\n createdAt: string\n updatedAt: string\n}\n\n// --- Allocation (§4) ----------------------------------------------------------\n\n/**\n * One allocation-strategy vocabulary row\n * (plg_shop_stock_allocation_strategies). The three behavioural fields are the\n * ORDERING CONTRACT the engine reads — the same discipline as\n * StockMovementKind.direction. Widening the vocabulary is a row, not a fork.\n */\nexport interface StockAllocationStrategy {\n key: string\n label: string\n /** Order by expiry first, nulls last. FEFO. */\n expiresFirst: boolean\n /** 'asc' = oldest receipt first (FIFO), 'desc' = newest first (LIFO). */\n receivedOrder: 'asc' | 'desc'\n /** Nothing is picked automatically; the caller names the lot. */\n isManual: boolean\n}\n\n/** One (lot, location) position allocation may draw from. */\nexport interface StockAllocationCandidate {\n /** null for a quantity-tracked variant's one implicit lot. */\n lotId: string | null\n locationId: string\n /** onHand − reserved for this position. */\n available: number\n /** Calendar date, so FEFO order cannot depend on a reader's time zone. */\n expiresAt: string | null\n receivedAt: string\n /** Transit stock exists but cannot be promised. Default true. */\n isSellableLocation?: boolean\n /** Breaks ties so the answer is reproducible, not merely correct. */\n isDefaultLocation?: boolean\n}\n\nexport interface StockAllocationRequest {\n variantId: string\n quantity: number\n tracking: StockTrackingMode\n /** Narrow to one location. Omitted, the default location leads. */\n locationId?: string | null\n /** Narrow to one lot — required by the `manual` strategy. */\n lotId?: string | null\n /** Most specific first: explicit, then variant, then store, then derived. */\n strategy?: string | null\n variantStrategy?: string | null\n tenantStrategy?: string | null\n}\n\n/** One lot chosen, in picking order. They sum to the requested quantity. */\nexport interface StockAllocation {\n lotId: string | null\n locationId: string\n quantity: number\n}\n\n/** What a caller supplies to place a hold. Add-to-cart never calls this (§5). */\nexport interface ReserveStockInput {\n variantId: string\n quantity: number\n locationId: string\n lotId?: string | null\n orderId?: string | null\n cartToken?: string | null\n /** The TTL that makes pix and boleto honest. Omitted ⇒ a hold with no expiry. */\n ttlMinutes?: number | null\n /** A retried checkout must not hold the unit twice. */\n idempotencyKey?: string | null\n}\n\n// --- Read options -------------------------------------------------------------\n\nexport interface ListStockBalancesOptions {\n variantId?: string\n variantIds?: string[]\n locationId?: string\n lotId?: string\n /** Only rows a shopper could be promised (sellable, active locations). */\n sellableOnly?: boolean\n}\n\nexport interface ListStockLotsOptions {\n variantId?: string\n status?: StockLotStatus\n /** Lots expiring on or before this date — the \"what is about to go off\" read. */\n expiresBefore?: string\n /** FEFO order: expiresAt then receivedAt, nulls last. Default true. */\n fefo?: boolean\n limit?: number\n}\n\nexport interface ListStockMovementsOptions {\n variantId?: string\n lotId?: string\n orderId?: string\n kind?: string\n reasonCode?: string\n /** Newest first. Default true — \"why is this 3?\" reads backwards. */\n newestFirst?: boolean\n limit?: number\n}\n","import { Registry } from '@fayz-ai/core'\nimport type { RegistryMeta } from '@fayz-ai/core'\nimport type {\n RecordStockMovementInput,\n StockMovementKind,\n StockReasonCode,\n StockTrackingMode,\n} from './stock-types'\n\n// ---------------------------------------------------------------------------\n// The stock vocabulary — DATA, not a hardcoded list (spine I9), and ONE\n// validator shared with the SQL (spine: one validator, never two that drift).\n//\n// Movement kinds and reason codes ship as ROWS in\n// plg_shop_stock_movement_kinds / plg_shop_stock_reason_codes (migration 0034),\n// exactly as rule attributes do in 0032. An agency adding \"quebra de vitrine\"\n// as a loss reason writes an INSERT — and, in code, one\n// registerStockReasonCode call — never a migration and never a fork.\n//\n// The registries below are the TS MIRROR of the core seed rows.\n// migrations.test.ts cross-checks the two so they cannot drift: the admin form,\n// the AI and both validators must read one vocabulary or the point is lost.\n//\n// validateStockMovement() is the TypeScript half of the ONE specification whose\n// other half is shop_validate_stock_movement() in 0034. Every refusal is an I2\n// hard error naming the valid set — never a silent drop, because a silently\n// dropped movement is stock that vanishes.\n// ---------------------------------------------------------------------------\n\n/**\n * The core movement kinds — the SAME rows the 0034 seed inserts with\n * tenant_id NULL. `direction` is the sign contract:\n * in ⇒ quantity must be positive\n * out ⇒ quantity must be negative\n * either ⇒ any non-zero quantity\n */\nexport const CORE_STOCK_MOVEMENT_KINDS: readonly StockMovementKind[] = [\n { key: 'receipt', label: 'Entrada', direction: 'in' },\n { key: 'sale', label: 'Venda', direction: 'out' },\n { key: 'return', label: 'Devolução', direction: 'in' },\n { key: 'adjustment', label: 'Ajuste', direction: 'either' },\n { key: 'transfer_in', label: 'Transferência (entrada)', direction: 'in' },\n { key: 'transfer_out', label: 'Transferência (saída)', direction: 'out' },\n { key: 'loss', label: 'Perda', direction: 'out' },\n { key: 'count', label: 'Contagem', direction: 'either' },\n]\n\n/**\n * The core reason codes — the SAME rows migrations 0034 and 0038 seed with\n * tenant_id NULL. `movementKind` null = valid for any kind.\n *\n * The last five arrived with 0038 so that Shopify's CLOSED adjustment-reason\n * vocabulary maps onto ours without loss (founder: \"vamos padrão Shopify\").\n * They are a widening, which is exactly what I9 promises the vocabulary can\n * take: five INSERTs and five entries here, no migration to the ENGINE and no\n * fork. The mapping itself is published as code in shopify-mapping.ts, and\n * stock-store.test.ts fails if a Shopify reason has no target here.\n */\nexport const CORE_STOCK_REASON_CODES: readonly StockReasonCode[] = [\n // --- 0034 -----------------------------------------------------------------\n { key: 'opening_balance', label: 'Saldo inicial', movementKind: 'receipt' },\n { key: 'purchase', label: 'Compra', movementKind: 'receipt' },\n { key: 'production', label: 'Produção', movementKind: 'receipt' },\n { key: 'customer_order', label: 'Pedido do cliente', movementKind: 'sale' },\n { key: 'customer_return', label: 'Devolução do cliente', movementKind: 'return' },\n { key: 'cycle_count', label: 'Contagem de estoque', movementKind: 'count' },\n { key: 'breakage', label: 'Quebra', movementKind: 'loss' },\n { key: 'expiry', label: 'Vencimento', movementKind: 'loss' },\n { key: 'theft', label: 'Furto', movementKind: 'loss' },\n { key: 'correction', label: 'Correção', movementKind: 'adjustment' },\n { key: 'legacy_write', label: 'Escrita direta em inventory_count', movementKind: 'adjustment' },\n { key: 'transfer', label: 'Transferência entre locais', movementKind: null },\n // --- 0038, for Shopify parity ---------------------------------------------\n // `received` is wider than our `purchase`: stock can arrive from a transfer,\n // a production run or a supplier, and Shopify does not distinguish.\n { key: 'received', label: 'Recebimento', movementKind: 'receipt' },\n // Distinct from our `breakage`: Shopify's `damaged` covers arriving damaged,\n // not only breaking in the shop. Both are kept — a merchant who already uses\n // `breakage` is not renamed.\n { key: 'damaged', label: 'Avariado', movementKind: 'loss' },\n { key: 'quality_control', label: 'Controle de qualidade', movementKind: 'adjustment' },\n { key: 'promotion', label: 'Promoção / brinde', movementKind: 'loss' },\n { key: 'other', label: 'Outro', movementKind: null },\n]\n\n/** The reason 0035 stamps on every backfilled opening balance. */\nexport const OPENING_BALANCE_REASON = 'opening_balance'\n/** The reason 0036's I10 shim stamps on a translated direct inventory_count write. */\nexport const LEGACY_WRITE_REASON = 'legacy_write'\n\n// --- The registries -----------------------------------------------------------\n// Uniform Registry<T> meta envelope (M1), the same one blocks, loaders, handles\n// and rule attributes use, so describeStore reads all of them through one parser.\n\nexport const stockMovementKindRegistry = new Registry<StockMovementKind>('stockMovementKind')\nexport const stockReasonCodeRegistry = new Registry<StockReasonCode>('stockReasonCode')\n\nlet coreRegistered = false\n\n/** Idempotent — consumers call before reads, mirroring registerCoreRuleAttributes. */\nexport function registerCoreStockVocabulary(): void {\n if (coreRegistered && stockMovementKindRegistry.has('receipt')) return\n for (const kind of CORE_STOCK_MOVEMENT_KINDS) {\n stockMovementKindRegistry.register(kind.key, kind, {\n title: kind.label,\n description: `Stock movement kind (${kind.direction})`,\n source: 'sdk',\n })\n }\n for (const reason of CORE_STOCK_REASON_CODES) {\n stockReasonCodeRegistry.register(reason.key, reason, {\n title: reason.label,\n description: reason.movementKind\n ? `Stock reason code for \"${reason.movementKind}\" movements`\n : 'Stock reason code (any movement kind)',\n source: 'sdk',\n })\n }\n coreRegistered = true\n}\n\nexport interface RegisterStockVocabularyOptions {\n meta?: RegistryMeta\n /** Re-registering an existing key is an error unless the caller owns it. */\n replace?: boolean\n}\n\n/**\n * Widen the movement-kind vocabulary. The SQL half of this is one INSERT into\n * plg_shop_stock_movement_kinds with the tenant's id.\n */\nexport function registerStockMovementKind(\n kind: StockMovementKind,\n options: RegisterStockVocabularyOptions = {},\n): void {\n registerCoreStockVocabulary()\n if (!kind.key) throw new Error('registerStockMovementKind: kind.key is required')\n if (!['in', 'out', 'either'].includes(kind.direction)) {\n throw new Error(\n `registerStockMovementKind: \"${kind.key}\" has unknown direction \"${kind.direction}\". ` +\n 'Valid directions: in, out, either',\n )\n }\n if (stockMovementKindRegistry.has(kind.key) && !options.replace) {\n throw new Error(\n `registerStockMovementKind: kind \"${kind.key}\" is already registered. ` +\n `Registered kinds: ${listStockMovementKindKeys().join(', ')}`,\n )\n }\n stockMovementKindRegistry.register(kind.key, kind, {\n title: kind.label,\n description: `Stock movement kind (${kind.direction})`,\n source: 'app',\n ...options.meta,\n })\n}\n\n/** Widen the reason-code vocabulary — \"quebra de vitrine\" is a row, not a fork. */\nexport function registerStockReasonCode(\n reason: StockReasonCode,\n options: RegisterStockVocabularyOptions = {},\n): void {\n registerCoreStockVocabulary()\n if (!reason.key) throw new Error('registerStockReasonCode: reason.key is required')\n if (reason.movementKind && !stockMovementKindRegistry.has(reason.movementKind)) {\n throw new Error(\n `registerStockReasonCode: \"${reason.key}\" names unknown movement kind \"${reason.movementKind}\". ` +\n `Registered kinds: ${listStockMovementKindKeys().join(', ')}`,\n )\n }\n if (stockReasonCodeRegistry.has(reason.key) && !options.replace) {\n throw new Error(\n `registerStockReasonCode: reason \"${reason.key}\" is already registered. ` +\n `Registered reason codes: ${listStockReasonCodeKeys().join(', ')}`,\n )\n }\n stockReasonCodeRegistry.register(reason.key, reason, {\n title: reason.label,\n description: reason.movementKind\n ? `Stock reason code for \"${reason.movementKind}\" movements`\n : 'Stock reason code (any movement kind)',\n source: 'app',\n ...options.meta,\n })\n}\n\n/** The kind for a key, or an I2 hard error naming the valid set. */\nexport function resolveStockMovementKind(key: string): StockMovementKind {\n registerCoreStockVocabulary()\n const kind = stockMovementKindRegistry.get(key)\n if (!kind) {\n throw new Error(\n `Unknown stock movement kind \"${key}\". Registered kinds: ${listStockMovementKindKeys().join(', ')}`,\n )\n }\n return kind\n}\n\n/** The reason for a key, or an I2 hard error naming the valid set. */\nexport function resolveStockReasonCode(key: string): StockReasonCode {\n registerCoreStockVocabulary()\n const reason = stockReasonCodeRegistry.get(key)\n if (!reason) {\n throw new Error(\n `Unknown stock reason code \"${key}\". Registered reason codes: ${listStockReasonCodeKeys().join(', ')}`,\n )\n }\n return reason\n}\n\nexport function listStockMovementKindKeys(): string[] {\n registerCoreStockVocabulary()\n return stockMovementKindRegistry.ids().sort()\n}\n\nexport function listStockReasonCodeKeys(): string[] {\n registerCoreStockVocabulary()\n return stockReasonCodeRegistry.ids().sort()\n}\n\n/** Every kind row, sorted — what an admin form renders itself from. */\nexport function listStockMovementKinds(): StockMovementKind[] {\n registerCoreStockVocabulary()\n return stockMovementKindRegistry\n .list()\n .map((entry) => entry.value)\n .sort((a, b) => a.key.localeCompare(b.key))\n}\n\n/** Every reason row, sorted. Optionally narrowed to one kind, which is exactly\n * what the \"record a movement\" form needs once a kind is chosen. */\nexport function listStockReasonCodes(movementKind?: string): StockReasonCode[] {\n registerCoreStockVocabulary()\n const all = stockReasonCodeRegistry\n .list()\n .map((entry) => entry.value)\n .sort((a, b) => a.key.localeCompare(b.key))\n if (!movementKind) return all\n return all.filter((reason) => reason.movementKind === null || reason.movementKind === movementKind)\n}\n\n/** Test seam — a suite that widens the vocabulary must not leak into the next. */\nexport function clearStockVocabulary(): void {\n stockMovementKindRegistry.clear()\n stockReasonCodeRegistry.clear()\n coreRegistered = false\n}\n\n// --- THE ONE VALIDATOR --------------------------------------------------------\n\n/** What the validator needs to know about the variant a movement targets. */\nexport interface StockMovementVariantFacts {\n tracking: StockTrackingMode\n /** Present only for lot/serial checks. */\n lot?: { id: string; variantId: string; serial: string | null } | null\n /**\n * `on_hand` already recorded for (lot, location), needed ONLY by the serial\n * rule below. Per-movement `|quantity| = 1` is not enough: two `+1` receipts\n * on the same serial lot are each individually legal and together say the\n * one-of-one unit exists twice. The cumulative check is what closes that,\n * and it is the reason this fact has to be read before the write.\n */\n lotOnHand?: number\n}\n\n/**\n * Refuse anything the ledger must not accept. This is the TypeScript half of\n * the specification whose SQL half is `shop_validate_stock_movement()` in\n * migration 0034 — the two implement ONE ruleset, and\n * `packages/shop/src/inventory/migrations.test.ts` pins the error vocabulary of\n * both so they cannot quietly disagree.\n *\n * Throws with a message naming the valid set (I2). Never returns a \"soft\"\n * failure: a movement that is dropped instead of refused is stock that vanishes.\n */\nexport function validateStockMovement(\n input: RecordStockMovementInput,\n facts: StockMovementVariantFacts,\n): void {\n registerCoreStockVocabulary()\n\n if (!input.variantId) {\n throw new Error('stock movement: variantId is required (the ledger is keyed on the sellable unit)')\n }\n\n if (!Number.isInteger(input.quantity)) {\n throw new Error(`stock movement: quantity must be a whole number (got ${input.quantity})`)\n }\n if (input.quantity === 0) {\n throw new Error('stock movement: quantity must not be zero — a movement that moves nothing is not a movement')\n }\n\n // Kind, and the sign contract carried by its vocabulary row.\n const kind = resolveStockMovementKind(input.kind)\n if (kind.direction === 'in' && input.quantity < 0) {\n throw new Error(\n `stock movement: kind \"${kind.key}\" is an inbound movement and needs a positive quantity (got ${input.quantity})`,\n )\n }\n if (kind.direction === 'out' && input.quantity > 0) {\n throw new Error(\n `stock movement: kind \"${kind.key}\" is an outbound movement and needs a negative quantity (got ${input.quantity})`,\n )\n }\n\n if (input.reasonCode) {\n const reason = resolveStockReasonCode(input.reasonCode)\n if (reason.movementKind !== null && reason.movementKind !== kind.key) {\n throw new Error(\n `stock movement: reason \"${reason.key}\" belongs to kind \"${reason.movementKind}\", not \"${kind.key}\"`,\n )\n }\n }\n\n // Tracking policy (§3) — where a per-variant policy stops being a label.\n if (facts.tracking === 'none') {\n throw new Error(`stock movement: variant ${input.variantId} is tracking=none and holds no stock`)\n }\n\n if (facts.tracking === 'quantity') {\n if (input.lotId) {\n throw new Error(\n `stock movement: variant ${input.variantId} is tracking=quantity; it has one implicit lot, so lotId must be null`,\n )\n }\n return\n }\n\n // 'lot' | 'serial'\n if (!input.lotId) {\n throw new Error(`stock movement: variant ${input.variantId} is tracking=${facts.tracking}; lotId is required`)\n }\n const lot = facts.lot\n if (!lot) {\n throw new Error(`stock movement: lot ${input.lotId} not found`)\n }\n if (lot.variantId !== input.variantId) {\n throw new Error(\n `stock movement: lot ${lot.id} belongs to variant ${lot.variantId}, not ${input.variantId}`,\n )\n }\n if (facts.tracking === 'serial') {\n if (!lot.serial) {\n throw new Error(\n `stock movement: variant ${input.variantId} is tracking=serial; lot ${lot.id} carries no serial`,\n )\n }\n if (Math.abs(input.quantity) !== 1) {\n throw new Error(\n `stock movement: variant ${input.variantId} is tracking=serial; quantity must be 1 or -1 (got ${input.quantity})`,\n )\n }\n // A serial IS one physical unit. |quantity| = 1 per movement is necessary\n // and NOT sufficient: two +1 receipts on the same lot are each legal on\n // their own and together claim the one-of-one exists twice. The balance is\n // therefore bounded to {0, 1}, which is the only place \"serial\" stops being\n // a synonym for \"lot with a code\".\n if (facts.lotOnHand !== undefined) {\n const next = facts.lotOnHand + input.quantity\n if (next > 1 || next < 0) {\n throw new Error(\n `stock movement: variant ${input.variantId} is tracking=serial; lot ${lot.id} would go to ${next} on hand ` +\n `(from ${facts.lotOnHand}). A serial is one unit: its balance may only be 0 or 1.`,\n )\n }\n }\n }\n}\n","import { Registry } from '@fayz-ai/core'\nimport type { RegistryMeta } from '@fayz-ai/core'\nimport type {\n StockAllocation,\n StockAllocationCandidate,\n StockAllocationRequest,\n StockAllocationStrategy,\n} from './stock-types'\n\n// ---------------------------------------------------------------------------\n// ALLOCATION — the ordering over lots, as data (inventory-model.md §4).\n//\n// This file is the TypeScript half of ONE specification whose SQL half is\n// shop_allocate_stock() in migration 0038. Same discipline as\n// validateStockMovement()/shop_validate_stock_movement(): two implementations\n// of one ruleset, cross-checked by migrations.test.ts, never two rulesets.\n//\n// WHY THE STRATEGY IS A ROW AND NOT AN ENUM (I9).\n//\n// The naive encoding is `type Strategy = 'fefo' | 'fifo' | 'lifo' | 'manual'`\n// and a switch. That is the hardcoded enumeration the spine rejects: an agency\n// that wants \"oldest cost first\" needs a migration and a code fork.\n//\n// So a strategy is a ROW carrying the ORDERING CONTRACT, exactly as a movement\n// kind carries `direction`:\n//\n// expiresFirst order by expiry, nulls last (FEFO)\n// receivedOrder 'asc' oldest receipt | 'desc' newest (FIFO | LIFO)\n// isManual nothing is picked; the caller names it\n//\n// compareCandidates() below reads those three fields and nothing else, so\n// registering a row is genuinely all it takes — and the bound is honest,\n// because a strategy the comparator cannot express would be free text.\n//\n// WHAT THIS FILE IS NOT. It never touches storage, never writes a movement and\n// never places a hold. Allocation runs INSIDE shop_place_order in Postgres, for\n// the reason §4 gives: that function is already the SECURITY DEFINER trust\n// boundary, and stock that can be allocated from the client is stock that can be\n// lied about. What lives here is the pure ordering — the thing a preview screen,\n// a test and the SQL must all agree on.\n// ---------------------------------------------------------------------------\n\n/**\n * The core four (§4) — the SAME rows migration 0038 seeds with tenant_id NULL.\n * migrations.test.ts cross-checks the two so they cannot drift.\n */\nexport const CORE_STOCK_ALLOCATION_STRATEGIES: readonly StockAllocationStrategy[] = [\n {\n key: 'fefo',\n label: 'Vence primeiro (FEFO)',\n expiresFirst: true,\n receivedOrder: 'asc',\n isManual: false,\n },\n {\n key: 'fifo',\n label: 'Entrou primeiro (FIFO)',\n expiresFirst: false,\n receivedOrder: 'asc',\n isManual: false,\n },\n {\n key: 'lifo',\n label: 'Entrou por ultimo (LIFO)',\n expiresFirst: false,\n receivedOrder: 'desc',\n isManual: false,\n },\n { key: 'manual', label: 'Manual', expiresFirst: false, receivedOrder: 'asc', isManual: true },\n]\n\nexport const stockAllocationStrategyRegistry = new Registry<StockAllocationStrategy>(\n 'stockAllocationStrategy',\n)\n\nlet coreRegistered = false\n\n/** Idempotent — consumers call before reads, mirroring registerCoreStockVocabulary. */\nexport function registerCoreStockAllocationStrategies(): void {\n if (coreRegistered && stockAllocationStrategyRegistry.has('fefo')) return\n for (const strategy of CORE_STOCK_ALLOCATION_STRATEGIES) {\n stockAllocationStrategyRegistry.register(strategy.key, strategy, {\n title: strategy.label,\n description: describeStrategy(strategy),\n source: 'sdk',\n })\n }\n coreRegistered = true\n}\n\nfunction describeStrategy(strategy: StockAllocationStrategy): string {\n if (strategy.isManual) return 'Manual — the caller names the lot'\n const parts = [strategy.expiresFirst ? 'soonest expiry first' : 'ignores expiry']\n parts.push(strategy.receivedOrder === 'asc' ? 'oldest receipt first' : 'newest receipt first')\n return parts.join(', ')\n}\n\n/**\n * Widen the strategy vocabulary. The SQL half is one INSERT into\n * plg_shop_stock_allocation_strategies with the tenant's id.\n */\nexport function registerStockAllocationStrategy(\n strategy: StockAllocationStrategy,\n options: { meta?: RegistryMeta; replace?: boolean } = {},\n): void {\n registerCoreStockAllocationStrategies()\n if (!strategy.key) throw new Error('registerStockAllocationStrategy: strategy.key is required')\n if (strategy.receivedOrder !== 'asc' && strategy.receivedOrder !== 'desc') {\n throw new Error(\n `registerStockAllocationStrategy: \"${strategy.key}\" has unknown receivedOrder \"${strategy.receivedOrder}\". ` +\n 'Valid values: asc, desc',\n )\n }\n if (stockAllocationStrategyRegistry.has(strategy.key) && !options.replace) {\n throw new Error(\n `registerStockAllocationStrategy: strategy \"${strategy.key}\" is already registered. ` +\n `Registered strategies: ${listStockAllocationStrategyKeys().join(', ')}`,\n )\n }\n stockAllocationStrategyRegistry.register(strategy.key, strategy, {\n title: strategy.label,\n description: describeStrategy(strategy),\n source: 'app',\n ...options.meta,\n })\n}\n\n/** The strategy for a key, or an I2 hard error naming the valid set. */\nexport function resolveStockAllocationStrategy(key: string): StockAllocationStrategy {\n registerCoreStockAllocationStrategies()\n const strategy = stockAllocationStrategyRegistry.get(key)\n if (!strategy) {\n throw new Error(\n `shop_allocate_stock: unknown allocation strategy \"${key}\". ` +\n `Valid strategies: ${listStockAllocationStrategyKeys().join(', ')}`,\n )\n }\n return strategy\n}\n\nexport function listStockAllocationStrategyKeys(): string[] {\n registerCoreStockAllocationStrategies()\n return stockAllocationStrategyRegistry.ids().sort()\n}\n\nexport function listStockAllocationStrategies(): StockAllocationStrategy[] {\n registerCoreStockAllocationStrategies()\n return stockAllocationStrategyRegistry\n .list()\n .map((entry) => entry.value)\n .sort((a, b) => a.key.localeCompare(b.key))\n}\n\n/** Test seam — a suite that widens the vocabulary must not leak into the next. */\nexport function clearStockAllocationStrategies(): void {\n stockAllocationStrategyRegistry.clear()\n coreRegistered = false\n}\n\n/**\n * §4's \"default fefo when `expires_at` exists\", resolved most-specific first.\n * The SQL twin is shop_resolve_allocation_strategy() in 0038:\n *\n * 1. the explicit argument\n * 2. the variant's own override\n * 3. the store's default\n * 4. derived — 'fefo' when any candidate lot carries an expiry, else 'fifo'\n *\n * Rule 4 is what makes artorious work with no configuration: the moment a\n * smoked-meat lot has a best-before, picking becomes FEFO on its own. And it is\n * what makes the default cost pulse and figurinhas nothing: no expiry anywhere,\n * so FIFO, which over one implicit lot is a no-op.\n */\nexport function resolveAllocationStrategyKey(input: {\n explicit?: string | null\n variantDefault?: string | null\n tenantDefault?: string | null\n candidates?: readonly StockAllocationCandidate[]\n}): string {\n const named =\n trimmed(input.explicit) ?? trimmed(input.variantDefault) ?? trimmed(input.tenantDefault)\n if (named) {\n // I2: an unknown key is a hard error naming the valid set, never a silent\n // fall back to fefo. A typo that quietly picks a different batch is exactly\n // the failure a food store cannot detect.\n resolveStockAllocationStrategy(named)\n return named\n }\n const hasExpiry = (input.candidates ?? []).some((candidate) => candidate.expiresAt != null)\n return hasExpiry ? 'fefo' : 'fifo'\n}\n\nfunction trimmed(value: string | null | undefined): string | null {\n if (typeof value !== 'string') return null\n const next = value.trim()\n return next.length > 0 ? next : null\n}\n\n/**\n * The comparator, and the only place the strategy row's three fields are read.\n * Four terms, matching 0038's ORDER BY one for one:\n *\n * 1. under FEFO a lot with NO expiry never beats one that has a date — the\n * half a naive `ORDER BY expiresAt` gets wrong, and the half a food\n * store's recall depends on;\n * 2. under FEFO the soonest expiry leads;\n * 3. FIFO takes the oldest receipt, LIFO the newest;\n * 4. the default location leads, then the lot id, so the answer is\n * reproducible rather than merely correct.\n */\nexport function compareStockAllocationCandidates(\n strategy: StockAllocationStrategy,\n a: StockAllocationCandidate,\n b: StockAllocationCandidate,\n): number {\n if (strategy.expiresFirst) {\n const aNull = a.expiresAt == null ? 1 : 0\n const bNull = b.expiresAt == null ? 1 : 0\n if (aNull !== bNull) return aNull - bNull\n if (a.expiresAt != null && b.expiresAt != null && a.expiresAt !== b.expiresAt) {\n return a.expiresAt < b.expiresAt ? -1 : 1\n }\n }\n if (a.receivedAt !== b.receivedAt) {\n const older = a.receivedAt < b.receivedAt ? -1 : 1\n return strategy.receivedOrder === 'asc' ? older : -older\n }\n const aDefault = a.isDefaultLocation ? 0 : 1\n const bDefault = b.isDefaultLocation ? 0 : 1\n if (aDefault !== bDefault) return aDefault - bDefault\n return String(a.lotId ?? '').localeCompare(String(b.lotId ?? ''))\n}\n\n/** Thrown when nothing can cover the quantity. Carries both numbers. */\nexport class StockAllocationError extends Error {\n constructor(\n message: string,\n readonly requested: number,\n readonly available: number,\n ) {\n super(message)\n this.name = 'StockAllocationError'\n }\n}\n\n/**\n * Choose which lots cover a quantity, in the strategy's picking order.\n *\n * Mirrors shop_allocate_stock() exactly, including the two refusals:\n *\n * * a `manual` strategy with no lot named is an error, not a silent FEFO;\n * * a SHORTFALL is an error, not a partial answer. Backorder is §10 q3,\n * answered no — returning less than was asked for would let a caller write\n * an order it cannot ship and force it to re-derive the gap to notice.\n *\n * A `none`-tracked variant returns no rows and no error: it never depletes (§3),\n * so there is nothing to allocate and nothing to refuse.\n */\nexport function allocateStock(\n request: StockAllocationRequest,\n candidates: readonly StockAllocationCandidate[],\n): StockAllocation[] {\n if (!Number.isInteger(request.quantity) || request.quantity <= 0) {\n throw new Error(`shop_allocate_stock: quantity must be positive (got ${request.quantity})`)\n }\n if (request.tracking === 'none') return []\n\n const strategy = resolveStockAllocationStrategy(\n resolveAllocationStrategyKey({\n explicit: request.strategy,\n variantDefault: request.variantStrategy,\n tenantDefault: request.tenantStrategy,\n candidates,\n }),\n )\n\n if (request.tracking === 'quantity' && request.lotId) {\n throw new Error(\n `shop_allocate_stock: variant ${request.variantId} is tracking=quantity; it has one implicit lot, so lotId must be null`,\n )\n }\n if (request.tracking !== 'quantity' && strategy.isManual && !request.lotId) {\n throw new Error(\n `shop_allocate_stock: variant ${request.variantId} uses the \"manual\" strategy; name the lot to allocate from (lotId)`,\n )\n }\n\n const eligible = candidates\n .filter((candidate) => candidate.available > 0)\n .filter((candidate) => (request.locationId ? candidate.locationId === request.locationId : true))\n .filter((candidate) => (request.lotId ? candidate.lotId === request.lotId : true))\n // Stock in transit is REAL and the ledger says so; it simply cannot be\n // promised to a shopper.\n .filter((candidate) => candidate.isSellableLocation !== false)\n .filter((candidate) =>\n request.tracking === 'quantity' ? candidate.lotId == null : candidate.lotId != null,\n )\n .slice()\n .sort((a, b) => compareStockAllocationCandidates(strategy, a, b))\n\n const chosen: StockAllocation[] = []\n let remaining = request.quantity\n for (const candidate of eligible) {\n if (remaining <= 0) break\n const take = Math.min(remaining, candidate.available)\n // A serial IS one unit (§3), bounded to {0,1} by the ledger validator, so a\n // lot can only ever offer 1. The assertion says so out loud rather than\n // relying on the bound holding somewhere else.\n if (request.tracking === 'serial' && take !== 1) {\n throw new Error(\n `shop_allocate_stock: variant ${request.variantId} is tracking=serial; lot ${candidate.lotId} offered ${candidate.available} units and a serial is one unit`,\n )\n }\n chosen.push({ lotId: candidate.lotId, locationId: candidate.locationId, quantity: take })\n remaining -= take\n }\n\n if (remaining > 0) {\n const available = candidates\n .filter((candidate) => candidate.isSellableLocation !== false)\n .reduce((sum, candidate) => sum + Math.max(candidate.available, 0), 0)\n throw new StockAllocationError(\n `shop_allocate_stock: variant ${request.variantId} cannot cover ${request.quantity} unit(s); ${available} available`,\n request.quantity,\n available,\n )\n }\n\n return chosen\n}\n\n// --- Reservation TTLs (§5, §10 q2) -------------------------------------------\n\n/**\n * The core rows migration 0038 seeds into plg_shop_stock_reservation_ttls.\n * `null` is the fallback for any method, including an order that names none.\n *\n * §10 q2 asked whether the TTL is one number or one per payment method and\n * answered itself: \"30 minutes covers pix comfortably; boleto is days.\" Thirty\n * minutes is the window every Brazilian PSP quotes for pix; three days is the\n * ordinary boleto compensation window. Holding a unit that long is the cost of\n * accepting boleto, and pretending otherwise is what oversells.\n */\nexport const CORE_RESERVATION_TTL_MINUTES: readonly { paymentMethod: string | null; minutes: number }[] =\n [\n { paymentMethod: null, minutes: 30 },\n { paymentMethod: 'pix', minutes: 30 },\n { paymentMethod: 'credit_card', minutes: 30 },\n { paymentMethod: 'debit_card', minutes: 30 },\n { paymentMethod: 'cash', minutes: 30 },\n { paymentMethod: 'other', minutes: 30 },\n { paymentMethod: 'boleto', minutes: 4320 },\n ]\n\n/** The SQL twin is shop_reservation_ttl(): most specific row wins. */\nexport function resolveReservationTtlMinutes(\n paymentMethod?: string | null,\n overrides: readonly { paymentMethod: string | null; minutes: number }[] = [],\n): number {\n const rows = [...overrides, ...CORE_RESERVATION_TTL_MINUTES]\n const named = rows.find((row) => row.paymentMethod != null && row.paymentMethod === paymentMethod)\n if (named) return named.minutes\n const fallback = rows.find((row) => row.paymentMethod == null)\n return fallback?.minutes ?? 30\n}\n","import type { StockTrackingMode } from './stock-types'\n\n// ---------------------------------------------------------------------------\n// THE SHOPIFY MAPPING, AS CODE.\n//\n// Founder decision, 2026-08-03: \"vamos padrão Shopify\". Wherever we had a free\n// choice of shape, we took Shopify's, so a future connector maps 1:1 with no\n// translation table. Where the shape was NOT free — because materialising\n// Shopify's placeholder would be visible on our surfaces in a way it is not on\n// theirs — we kept the semantics and published the constant here instead of\n// leaving it as prose in a migration header.\n//\n// This file is deliberately data, not logic: it is what a connector reads, what\n// migrations.test.ts pins, and what a reviewer checks against Shopify's docs.\n// It has no dependency on the store, the ledger or Supabase.\n//\n// WHERE WE GO BEYOND SHOPIFY, WE KEEP IT. Shopify has no native lot/batch,\n// expiry or serial tracking — merchants buy third-party apps for both — and our\n// lot + expiresAt + FEFO + serial layer is exactly what artorious (smoked meat,\n// where expiry must drive picking and a recall must be answerable) and\n// cristinarotondaro (every piece one-of-one) need. \"Padrão Shopify\" means\n// compatible-by-default, never limited-to. Everything below that has no Shopify\n// counterpart says so explicitly rather than being quietly omitted.\n// ---------------------------------------------------------------------------\n\n/**\n * Shopify auto-creates one variant per option-less product, titled\n * \"Default Title\", under a single option axis named \"Title\". We carry the\n * SEMANTICS in `plg_shop_product_variants.is_default_variant` and the\n * PLACEHOLDER STRINGS nowhere — because in our schema `plg_shop_product_options`\n * is what renders the PDP picker and `plg_shop_product_variants.options` is what\n * names the order line, so materialising them would be visible in exactly the\n * two places the model must not touch. Shopify hides its placeholder in the\n * theme (`product.has_only_default_variant`); we never create it.\n *\n * A connector writes these constants; it does not read them from a row.\n */\nexport const SHOPIFY_DEFAULT_OPTION_NAME = 'Title'\nexport const SHOPIFY_DEFAULT_VARIANT_TITLE = 'Default Title'\n\n/**\n * `is_default_variant = true` ⟺ Shopify's `has_only_default_variant`. The whole\n * translation, in one predicate.\n */\nexport function toShopifyVariantTitle(isDefaultVariant: boolean, optionsLabel: string | null): string {\n return isDefaultVariant || !optionsLabel ? SHOPIFY_DEFAULT_VARIANT_TITLE : optionsLabel\n}\n\n/** The option axes Shopify would report for a product with no real ones. */\nexport function shopifyDefaultOptions(): { name: string; values: string[] }[] {\n return [{ name: SHOPIFY_DEFAULT_OPTION_NAME, values: [SHOPIFY_DEFAULT_VARIANT_TITLE] }]\n}\n\n// --- Quantity states ----------------------------------------------------------\n\n/**\n * Shopify's inventory quantity names → ours.\n *\n * `committed` is the only genuine synonym, and it is the one place we chose to\n * keep OUR word in the schema: `reserved` is what 0034 shipped, what the\n * contract suite, the SQL regression, the mirror and the mock all assert on, and\n * renaming a landed green column for a synonym is churn. Instead the\n * `plg_shop_stock_levels` view publishes BOTH — `reserved` and `committed` —\n * so the connector reads Shopify's name and nothing internal moved.\n */\nexport const SHOPIFY_QUANTITY_STATES: readonly {\n shopify: string\n ours: string | null\n note: string\n}[] = [\n { shopify: 'available', ours: 'available', note: 'identical (on_hand − reserved)' },\n { shopify: 'on_hand', ours: 'on_hand', note: 'identical' },\n {\n shopify: 'committed',\n ours: 'reserved',\n note: 'same concept; plg_shop_stock_levels publishes both names',\n },\n {\n shopify: 'incoming',\n ours: null,\n note: 'modelled as a PLACE — a balance in a location with is_sellable = false (kind \"transit\") — which is strictly more expressive than one number',\n },\n {\n shopify: 'damaged',\n ours: null,\n note: 'modelled as a `loss` movement with reason_code \"damaged\": carries when, who and how many, where Shopify has a bucket',\n },\n {\n shopify: 'quality_control',\n ours: null,\n note: 'modelled as a lot with status \"quarantined\" — a BATCH under inspection, not a quantity',\n },\n {\n shopify: 'safety_stock',\n ours: null,\n note: 'NOT MODELLED. Reorder point and low-stock threshold are forecasting, which inventory-model.md §8 puts out of scope on purpose',\n },\n]\n\n// --- Adjustment reasons -------------------------------------------------------\n\n/**\n * Shopify's adjustment-reason vocabulary is CLOSED. Ours is rows and stays open\n * (spine I9). Every Shopify reason must therefore land on one of ours without\n * loss, or a connector needs a translation table with holes in it. Migration\n * 0038 seeds the five rows that were missing.\n *\n * `null` means \"there is no movement to write\" and is not a gap — see the note.\n */\nexport const SHOPIFY_ADJUSTMENT_REASONS: readonly {\n shopify: string\n reasonCode: string | null\n movementKind: string | null\n note?: string\n}[] = [\n { shopify: 'correction', reasonCode: 'correction', movementKind: 'adjustment' },\n { shopify: 'cycle_count_available', reasonCode: 'cycle_count', movementKind: 'count' },\n { shopify: 'received', reasonCode: 'received', movementKind: 'receipt' },\n { shopify: 'damaged', reasonCode: 'damaged', movementKind: 'loss' },\n { shopify: 'quality_control', reasonCode: 'quality_control', movementKind: 'adjustment' },\n { shopify: 'promotion', reasonCode: 'promotion', movementKind: 'loss' },\n { shopify: 'movement_created', reasonCode: 'transfer', movementKind: 'transfer_out' },\n { shopify: 'movement_received', reasonCode: 'transfer', movementKind: 'transfer_in' },\n { shopify: 'movement_updated', reasonCode: 'transfer', movementKind: 'transfer_in' },\n { shopify: 'movement_canceled', reasonCode: 'correction', movementKind: 'adjustment' },\n { shopify: 'other', reasonCode: 'other', movementKind: null },\n {\n shopify: 'reservation_created',\n reasonCode: null,\n movementKind: null,\n note: 'NOT a movement. A hold is a row in plg_shop_stock_reservations with a status and a TTL — a lifecycle, not an entry in an append-only ledger. Recording it as a movement is what forces Shopify to have a reason code for it at all.',\n },\n]\n\n/** The reason code a Shopify adjustment reason maps to, or null when it is not a movement. */\nexport function shopifyReasonToStockReason(shopifyReason: string): string | null {\n const row = SHOPIFY_ADJUSTMENT_REASONS.find((entry) => entry.shopify === shopifyReason)\n if (!row) {\n throw new Error(\n `shopifyReasonToStockReason: unknown Shopify reason \"${shopifyReason}\". Known reasons: ${SHOPIFY_ADJUSTMENT_REASONS.map((entry) => entry.shopify).join(', ')}`,\n )\n }\n return row.reasonCode\n}\n\n// --- What Shopify cannot express ----------------------------------------------\n\n/**\n * Named so nobody \"simplifies\" them away in the name of Shopify parity. Each\n * one is a fleet requirement, not a luxury.\n */\nexport const BEYOND_SHOPIFY: readonly { feature: string; why: string }[] = [\n {\n feature: 'lot / batch tracking with expires_at',\n why: 'artorious sells smoked meat: FEFO picking and an answerable recall are regulatory, not optional. Shopify has no native lots.',\n },\n {\n feature: 'FEFO / FIFO / LIFO / manual allocation as data',\n why: 'Shopify allocates per location only. Which BATCH leaves the shelf is the question a food store actually asks.',\n },\n {\n feature: 'serial tracking bounded to {0,1} per lot',\n why: 'cristinarotondaro: every piece is one-of-one. Shopify has no native serials.',\n },\n {\n feature: 'per-variant tracking policy (none | quantity | lot | serial)',\n why: 'One model has to express all six fleet stores without a code fork, and the default must cost a simple store nothing.',\n },\n {\n feature: 'unit_cost on the lot and on the movement',\n why: 'Margin and CMV are derivable. Shopify keeps cost on the InventoryItem only, with no history.',\n },\n]\n\n/** Tracking modes that have no Shopify equivalent — kept deliberately. */\nexport const TRACKING_MODES_BEYOND_SHOPIFY: readonly StockTrackingMode[] = ['lot', 'serial']\n","// Physical table names for @fayz-ai/shop. Renamed to the plg_shop_* industry-pool\n// prefix (was shop_*) when the core DB moved into the public schema. RPC function\n// names (shop_place_order, shop_get_order, shop_resolve_customer) are unchanged.\nexport const T = {\n products: 'plg_shop_products',\n productImages: 'plg_shop_product_images',\n productOptions: 'plg_shop_product_options',\n productVariants: 'plg_shop_product_variants',\n categories: 'plg_shop_categories',\n orders: 'plg_shop_orders',\n orderItems: 'plg_shop_order_items',\n customers: 'plg_shop_customers',\n discounts: 'plg_shop_discounts',\n salesChannels: 'plg_shop_channels',\n // Collections, gift cards and customer groups/segments (migrations 0040-0043).\n // None of them touch the stock ledger or variants.\n collections: 'plg_shop_collections',\n collectionProducts: 'plg_shop_collection_products',\n giftCards: 'plg_shop_gift_cards',\n // Message customisations live in the automations table, not a shop-owned one:\n // the store's emails are events in the same catalog every other module writes\n // to. `plg_shop_email_templates` (0042) is the retired shape — plugin-automations\n // migration 0001 copied its rows over and nothing writes to it anymore.\n messageTemplates: 'plg_automations_templates',\n customerGroups: 'plg_shop_customer_groups',\n customerGroupMembers: 'plg_shop_customer_group_members',\n customerSegments: 'plg_shop_customer_segments',\n /** Public bucket the media library and product photos both live in. */\n mediaBucket: 'shop-images',\n // The inventory ledger (migrations 0034-0036). stockBalances is DERIVED: it is\n // read here and never written, because the only legitimate writers are 0034's\n // SECURITY DEFINER triggers — which is why that table deliberately has no RLS\n // write policy at all.\n stockMovementKinds: 'plg_shop_stock_movement_kinds',\n stockReasonCodes: 'plg_shop_stock_reason_codes',\n stockLocations: 'plg_shop_stock_locations',\n stockLots: 'plg_shop_stock_lots',\n stockMovements: 'plg_shop_stock_movements',\n stockReservations: 'plg_shop_stock_reservations',\n stockBalances: 'plg_shop_stock_balances',\n // Allocation and the reservation lifecycle (0038). The strategies and TTLs are\n // VOCABULARY — rows, not enums (I9) — so an agency widens either with an\n // INSERT. stockLevels is a VIEW: our (variant, lot, location) balances\n // projected to (variant, location), which is Shopify's InventoryLevel shape\n // and what a connector syncs.\n stockAllocationStrategies: 'plg_shop_stock_allocation_strategies',\n stockReservationTtls: 'plg_shop_stock_reservation_ttls',\n stockLevels: 'plg_shop_stock_levels',\n // Traceability (0039): which lot filled which order line. A join table, not a\n // jsonb column, because the query that matters runs BACKWARDS.\n orderItemLots: 'plg_shop_order_item_lots',\n // Tenant-level shop settings (0029). stock_ownership = 'external' means an\n // ERP owns the numbers and our ledger is not authoritative for that store.\n settings: 'plg_shop_settings',\n // Core, not plg_shop_*: a delivery zone is a fact about the business, and\n // fulfilment and financial read it too. Same reasoning as addresses and\n // orders — see migration 0021.\n shippingZones: 'shipping_zones',\n // The store-wide delivery fallback (0015). Core for the same reason zones\n // are: what a business charges to deliver is not a fact about a plugin.\n shippingSettings: 'shipping_settings',\n shippingProfiles: 'plg_shop_shipping_profiles',\n // plg_*, unlike the two above: which boxes a store keeps on the shelf is a\n // fact about running this shop, not about the business. Nothing outside the\n // freight quote reads it.\n shippingPackages: 'plg_shop_packages',\n} as const\n","import { getSupabaseClientOptional } from '@fayz-ai/core'\nimport { getShopTenantId } from '../data/tenant'\nimport { T } from '../data/tables'\nimport { StockMovementError, mirroredInventoryCount } from './stock-store'\nimport type { StockStore } from './stock-store'\nimport { validateStockMovement } from './vocabulary'\nimport type {\n ListStockBalancesOptions,\n ListStockLotsOptions,\n ListStockMovementsOptions,\n RecordStockMovementInput,\n ReserveStockInput,\n StockAllocation,\n StockAllocationRequest,\n StockBalance,\n StockLocation,\n StockLot,\n StockMovement,\n StockReservation,\n StockReservationStatus,\n StockTrackingMode,\n} from './stock-types'\n\n// ---------------------------------------------------------------------------\n// The Supabase StockStore — the canonical implementation of the port, over the\n// plg_shop_stock_* tables from migrations 0034-0036.\n//\n// House pattern is supabase-provider.ts, followed exactly: the same optional\n// global client, the same `getShopTenantId()` scoping, the same\n// `if (error) throw error` style, the same snake_case→camelCase row mappers.\n//\n// WHAT THIS FILE DOES NOT DO, and why that is the design rather than an\n// omission:\n//\n// * It never writes plg_shop_stock_balances. That table is DERIVED and 0034\n// deliberately gives it no RLS write policy at all — the only legitimate\n// writers are the SECURITY DEFINER triggers. A store that \"kept the balance\n// up to date\" would be a second truth, which is the exact thing the ledger\n// replaces.\n// * It never writes plg_shop_product_variants.inventory_count. That column is\n// 0036's mirror. Writing it would go through the I10 shim and translate\n// back into a movement — the long way round to the row we are already\n// inserting.\n// * It never allocates or reserves IN TYPESCRIPT. §9 step 4 has landed, so the\n// verbs exist — but every one of them is an RPC onto the SQL in 0038, for\n// the reason §4 gives: allocation runs inside the SECURITY DEFINER trust\n// boundary, because stock that can be allocated from the client is stock\n// that can be lied about. A second allocator here would race the first with\n// no lock between them.\n// * It never touches shop_place_order, which owns the checkout path outright.\n//\n// So the write surface is: APPEND A MOVEMENT (what a receiving screen, a CSV\n// import and an ERP connector need — §10 q4 names receiving as the first thing a\n// real store touches), plus three thin RPC wrappers over allocation and the\n// reservation lifecycle.\n//\n// THE VALIDATOR RUNS HERE TOO. 0034's shop_validate_stock_movement() is the\n// authority and it cannot be bypassed; validateStockMovement() runs first\n// anyway, for the same reason supabase-entity-store.ts gates writes that RLS\n// would also refuse: the client-side refusal names the valid set (I2) and costs\n// no round trip, while the server-side one is what makes it true.\n// ---------------------------------------------------------------------------\n\n/* eslint-disable @typescript-eslint/no-explicit-any */\n\nfunction getDb(): any {\n const supabase = getSupabaseClientOptional()\n if (!supabase) throw new Error('@fayz-ai/shop: Supabase client not initialized. Call setGlobalSupabaseClient() first.')\n return supabase\n}\n\n// Exported so the admin CRUD in supabase-provider.ts reads the row the same way\n// the ledger does — one mapper, two callers, nothing to drift.\nexport function rowToStockLocation(row: any): StockLocation {\n return {\n id: row.id,\n tenantId: row.tenant_id,\n code: row.code,\n name: row.name,\n kind: row.kind ?? 'store',\n isSellable: row.is_sellable ?? true,\n isDefault: row.is_default ?? false,\n status: row.status ?? 'active',\n postalCode: row.postal_code ?? null,\n city: row.city ?? null,\n state: row.state ?? null,\n street: row.street ?? null,\n number: row.number ?? null,\n district: row.district ?? null,\n complement: row.complement ?? null,\n document: row.document ?? null,\n phone: row.phone ?? null,\n email: row.email ?? null,\n carrierAgencies: row.carrier_agencies ?? null,\n shipsOnline: row.ships_online ?? true,\n fulfillmentProvider: row.fulfillment_provider ?? null,\n createdAt: row.created_at,\n updatedAt: row.updated_at,\n }\n}\n\nconst rowToLocation = rowToStockLocation\n\nfunction rowToLot(row: any): StockLot {\n return {\n id: row.id,\n tenantId: row.tenant_id,\n variantId: row.variant_id,\n lotCode: row.lot_code,\n serial: row.serial ?? null,\n manufacturedAt: row.manufactured_at ?? null,\n expiresAt: row.expires_at ?? null,\n receivedAt: row.received_at,\n // numeric(12,4) arrives as a string on the wire; Number() once, here, so no\n // caller ever adds a cost to a string and gets concatenation.\n unitCost: row.unit_cost == null ? null : Number(row.unit_cost),\n supplierRef: row.supplier_ref ?? null,\n status: row.status ?? 'active',\n createdAt: row.created_at,\n updatedAt: row.updated_at,\n }\n}\n\nfunction rowToMovement(row: any): StockMovement {\n return {\n id: row.id,\n tenantId: row.tenant_id,\n variantId: row.variant_id ?? null,\n productId: row.product_id ?? null,\n locationId: row.location_id,\n lotId: row.lot_id ?? null,\n kind: row.kind,\n reasonCode: row.reason_code ?? null,\n quantity: Number(row.quantity),\n unitCost: row.unit_cost == null ? null : Number(row.unit_cost),\n orderId: row.order_id ?? null,\n reservationId: row.reservation_id ?? null,\n idempotencyKey: row.idempotency_key ?? null,\n occurredAt: row.occurred_at,\n createdBy: row.created_by ?? null,\n note: row.note ?? null,\n createdAt: row.created_at,\n }\n}\n\nfunction rowToBalance(row: any): StockBalance {\n const onHand = Number(row.on_hand ?? 0)\n const reserved = Number(row.reserved ?? 0)\n return {\n id: row.id,\n tenantId: row.tenant_id,\n variantId: row.variant_id,\n locationId: row.location_id,\n lotId: row.lot_id ?? null,\n onHand,\n reserved,\n // `available` is a STORED generated column in 0034. It is recomputed here\n // rather than trusted, so a projection that omitted the column cannot make\n // `available` silently read 0 — the one number the storefront depends on.\n available: row.available == null ? onHand - reserved : Number(row.available),\n updatedAt: row.updated_at,\n }\n}\n\nfunction rowToReservation(row: any): StockReservation {\n return {\n id: row.id,\n tenantId: row.tenant_id,\n variantId: row.variant_id,\n locationId: row.location_id,\n lotId: row.lot_id ?? null,\n quantity: Number(row.quantity),\n status: row.status ?? 'held',\n orderId: row.order_id ?? null,\n cartToken: row.cart_token ?? null,\n expiresAt: row.expires_at ?? null,\n idempotencyKey: row.idempotency_key ?? null,\n createdBy: row.created_by ?? null,\n createdAt: row.created_at,\n updatedAt: row.updated_at,\n }\n}\n\n/** Postgres unique_violation — the idempotency index doing its job under a race. */\nconst UNIQUE_VIOLATION = '23505'\n\nexport class SupabaseStockStore implements StockStore {\n /**\n * An explicit tenant pins the store to one pool (what an ERP connector or a\n * background job needs); omitted, it resolves per call through the same\n * resolver every other plg_shop_* read uses.\n */\n constructor(private readonly explicitTenantId?: string) {}\n\n private tenantId(): string | undefined {\n return this.explicitTenantId ?? getShopTenantId()\n }\n\n private requireTenantId(operation: string): string {\n const tenantId = this.tenantId()\n if (!tenantId) {\n throw new Error(\n `@fayz-ai/shop: ${operation} requires a tenant id (configure the shop tenant resolver, or construct SupabaseStockStore with one).`,\n )\n }\n return tenantId\n }\n\n // --- reads ------------------------------------------------------------------\n\n async listLocations(): Promise<StockLocation[]> {\n const db = getDb()\n let q = db.from(T.stockLocations).select('*')\n const tenantId = this.tenantId()\n if (tenantId) q = q.eq('tenant_id', tenantId)\n // Default first, then a stable handle order — the shape a picker renders.\n q = q.order('is_default', { ascending: false }).order('code', { ascending: true })\n const { data, error } = await q\n if (error) throw error\n return (data ?? []).map(rowToLocation)\n }\n\n async getDefaultLocation(): Promise<StockLocation | null> {\n const db = getDb()\n let q = db.from(T.stockLocations).select('*').eq('is_default', true)\n const tenantId = this.tenantId()\n if (tenantId) q = q.eq('tenant_id', tenantId)\n const { data, error } = await q.maybeSingle()\n if (error) throw error\n return data ? rowToLocation(data) : null\n }\n\n /**\n * `sellableOnly` filters by an id set read from the locations table rather\n * than by an embedded `!inner` join. Locations are a handful of rows per\n * tenant, and the set is the same one mirroredInventoryCount() takes — so the\n * mock, this store and 0036's mirror all narrow \"sellable\" through one\n * definition instead of three query dialects.\n */\n async listBalances(options: ListStockBalancesOptions = {}): Promise<StockBalance[]> {\n const db = getDb()\n let q = db.from(T.stockBalances).select('*')\n const tenantId = this.tenantId()\n if (tenantId) q = q.eq('tenant_id', tenantId)\n if (options.variantId) q = q.eq('variant_id', options.variantId)\n if (options.variantIds) q = q.in('variant_id', options.variantIds)\n if (options.locationId) q = q.eq('location_id', options.locationId)\n if (options.lotId) q = q.eq('lot_id', options.lotId)\n if (options.sellableOnly) {\n const sellable = await this.sellableLocationIds()\n if (sellable.size === 0) return []\n q = q.in('location_id', [...sellable])\n }\n const { data, error } = await q\n if (error) throw error\n return (data ?? []).map(rowToBalance)\n }\n\n async getAvailable(variantId: string): Promise<number> {\n const [balances, sellable] = await Promise.all([\n this.listBalances({ variantId }),\n this.sellableLocationIds(),\n ])\n return mirroredInventoryCount(balances, sellable)\n }\n\n async listLots(options: ListStockLotsOptions = {}): Promise<StockLot[]> {\n const db = getDb()\n let q = db.from(T.stockLots).select('*')\n const tenantId = this.tenantId()\n if (tenantId) q = q.eq('tenant_id', tenantId)\n if (options.variantId) q = q.eq('variant_id', options.variantId)\n if (options.status) q = q.eq('status', options.status)\n if (options.expiresBefore) q = q.lte('expires_at', options.expiresBefore)\n if (options.fefo !== false) {\n // FEFO: soonest expiry first, then oldest received. nullsFirst:false is\n // load-bearing — Postgres sorts NULLs LAST on ASC by default but PostgREST\n // sends nullsfirst explicitly, and a lot with no expiry must never be\n // picked ahead of one that has a date.\n q = q\n .order('expires_at', { ascending: true, nullsFirst: false })\n .order('received_at', { ascending: true })\n }\n if (options.limit != null) q = q.limit(options.limit)\n const { data, error } = await q\n if (error) throw error\n return (data ?? []).map(rowToLot)\n }\n\n async listMovements(options: ListStockMovementsOptions = {}): Promise<StockMovement[]> {\n const db = getDb()\n let q = db.from(T.stockMovements).select('*')\n const tenantId = this.tenantId()\n if (tenantId) q = q.eq('tenant_id', tenantId)\n if (options.variantId) q = q.eq('variant_id', options.variantId)\n if (options.lotId) q = q.eq('lot_id', options.lotId)\n if (options.orderId) q = q.eq('order_id', options.orderId)\n if (options.kind) q = q.eq('kind', options.kind)\n if (options.reasonCode) q = q.eq('reason_code', options.reasonCode)\n const ascending = options.newestFirst === false\n // occurred_at is WHEN it happened and is routinely backdated, so it ties.\n // created_at breaks the tie, which is what makes \"the order I wrote them in\"\n // reproducible for a stock count entered in one batch.\n q = q.order('occurred_at', { ascending }).order('created_at', { ascending })\n if (options.limit != null) q = q.limit(options.limit)\n const { data, error } = await q\n if (error) throw error\n return (data ?? []).map(rowToMovement)\n }\n\n async listReservations(variantId?: string): Promise<StockReservation[]> {\n const db = getDb()\n let q = db.from(T.stockReservations).select('*')\n const tenantId = this.tenantId()\n if (tenantId) q = q.eq('tenant_id', tenantId)\n if (variantId) q = q.eq('variant_id', variantId)\n q = q.order('created_at', { ascending: true })\n const { data, error } = await q\n if (error) throw error\n return (data ?? []).map(rowToReservation)\n }\n\n // --- append -----------------------------------------------------------------\n\n /**\n * Append to the ledger. Never an update — a mistake is corrected by an\n * opposing movement.\n *\n * IDEMPOTENCY IS TWO MECHANISMS, not one, and the second is the real one:\n *\n * 1. A pre-read on (tenant_id, idempotency_key) returns the existing\n * movement. This is the ordinary retry — same caller, seconds later —\n * and it costs one cheap indexed SELECT.\n * 2. A `23505` on the INSERT is caught and re-read. This is the race the\n * pre-read cannot cover: two retries in flight at once both miss the\n * SELECT and both attempt the INSERT. 0034's partial unique index\n * plg_shop_stock_movements_idempotency_key is what turns the loser into\n * an error we can recognise instead of a second decrement.\n *\n * Without (2) a retried place_order under load decrements twice, which is\n * precisely the failure the idempotency key exists to prevent.\n */\n async recordMovement(input: RecordStockMovementInput): Promise<StockMovement> {\n const db = getDb()\n const tenantId = this.requireTenantId('recordMovement')\n\n if (input.idempotencyKey) {\n const existing = await this.findByIdempotencyKey(input.idempotencyKey)\n if (existing) return existing\n }\n\n const variant = await this.readVariant(input.variantId)\n if (!variant) {\n throw new StockMovementError(`stock movement: variant ${input.variantId} not found`, input)\n }\n if (variant.tenantId !== tenantId) {\n throw new StockMovementError(\n `stock movement: variant ${input.variantId} belongs to another store`,\n input,\n )\n }\n\n const lot = input.lotId ? await this.readLot(input.lotId) : null\n\n try {\n validateStockMovement(input, {\n tracking: variant.tracking,\n lot,\n // Summed over EVERY location: a serial is one physical unit in the whole\n // store, so moving it between locations must not let a second copy\n // appear. Same predicate as 0034's SELECT, and read only when it can\n // matter, so the quantity-tracked common case costs nothing.\n lotOnHand: lot && variant.tracking === 'serial' ? await this.lotOnHand(lot.id) : undefined,\n })\n } catch (error) {\n throw new StockMovementError((error as Error).message, input)\n }\n\n // An explicitly named location must belong to this store. Omitted, the\n // column is sent NULL and 0034's BEFORE INSERT trigger fills it with\n // shop_default_stock_location() — creating the tenant's default on demand,\n // under an ON CONFLICT that two concurrent first-movements cannot lose. We\n // deliberately do NOT reimplement that race here.\n let locationId = input.locationId ?? null\n if (locationId) {\n const known = await this.listLocations()\n if (!known.some((location) => location.id === locationId)) {\n throw new StockMovementError(\n `stock movement: location ${locationId} does not belong to store ${tenantId}`,\n input,\n )\n }\n } else {\n locationId = (await this.getDefaultLocation())?.id ?? null\n }\n\n const payload = {\n tenant_id: tenantId,\n variant_id: input.variantId,\n location_id: locationId,\n lot_id: input.lotId ?? null,\n kind: input.kind,\n reason_code: input.reasonCode ?? null,\n quantity: input.quantity,\n unit_cost: input.unitCost ?? null,\n order_id: input.orderId ?? null,\n reservation_id: input.reservationId ?? null,\n idempotency_key: input.idempotencyKey ?? null,\n ...(input.occurredAt !== undefined ? { occurred_at: input.occurredAt } : {}),\n note: input.note ?? null,\n // product_id is denormalised by the validator trigger from the variant,\n // so the ledger row still names its product after an ON DELETE SET NULL.\n }\n\n const { data, error } = await db.from(T.stockMovements).insert(payload).select().single()\n\n if (error) {\n if (error.code === UNIQUE_VIOLATION && input.idempotencyKey) {\n const winner = await this.findByIdempotencyKey(input.idempotencyKey)\n if (winner) return winner\n }\n // Every other failure is the ledger REFUSING the movement — the validator\n // trigger, a foreign key, RLS. It is raised, never swallowed: a movement\n // that is dropped instead of refused is stock that vanishes.\n throw new StockMovementError((error as Error).message ?? String(error), input)\n }\n return rowToMovement(data)\n }\n\n // --- allocation and the reservation lifecycle (§9 step 4) ---------------------\n //\n // All three go through an RPC rather than being computed here, and that is the\n // whole design rather than a shortcut. §4: allocation runs IN POSTGRES, inside\n // the SECURITY DEFINER trust boundary, because \"stock that can be allocated\n // from the client is stock that can be lied about\". A TypeScript\n // implementation that read balances and picked lots itself would be a second\n // allocator racing the first — two answers to \"which batch leaves the shelf\",\n // with no lock between them.\n //\n // The pure ordering in allocation.ts is the SPECIFICATION both sides share and\n // what the mock executes; this class calls the authority.\n\n async allocate(request: Omit<StockAllocationRequest, 'tracking'>): Promise<StockAllocation[]> {\n const db = getDb()\n const tenantId = this.requireTenantId('allocate')\n const { data, error } = await db.rpc('shop_allocate_stock', {\n p_tenant_id: tenantId,\n p_variant_id: request.variantId,\n p_quantity: request.quantity,\n p_location_id: request.locationId ?? null,\n p_strategy: request.strategy ?? null,\n p_lot_id: request.lotId ?? null,\n })\n if (error) throw new Error((error as Error).message ?? String(error))\n return (data ?? []).map((row: any) => ({\n lotId: row.lot_id ?? null,\n locationId: row.location_id,\n quantity: Number(row.quantity),\n }))\n }\n\n async reserve(input: ReserveStockInput): Promise<StockReservation> {\n const db = getDb()\n const tenantId = this.requireTenantId('reserve')\n const { data, error } = await db.rpc('shop_reserve_stock', {\n p_tenant_id: tenantId,\n p_variant_id: input.variantId,\n p_quantity: input.quantity,\n p_location_id: input.locationId,\n p_lot_id: input.lotId ?? null,\n p_order_id: input.orderId ?? null,\n p_cart_token: input.cartToken ?? null,\n // Postgres parses an ISO-8601 interval literal; minutes keeps the port's\n // surface a number rather than a Postgres dialect.\n p_ttl: input.ttlMinutes == null ? null : `${input.ttlMinutes} minutes`,\n p_idempotency_key: input.idempotencyKey ?? null,\n })\n if (error) throw new Error((error as Error).message ?? String(error))\n const id = typeof data === 'string' ? data : data?.[0]\n const rows = await this.listReservations(input.variantId)\n const created = rows.find((row) => row.id === id)\n if (!created) throw new Error(`shop_reserve_stock: reservation ${id} could not be read back`)\n return created\n }\n\n async setReservationStatus(reservationId: string, status: StockReservationStatus): Promise<void> {\n const db = getDb()\n let q = db.from(T.stockReservations).update({ status }).eq('id', reservationId)\n const tenantId = this.tenantId()\n if (tenantId) q = q.eq('tenant_id', tenantId)\n const { error } = await q\n if (error) throw error\n }\n\n async sweepExpiredReservations(variantId?: string): Promise<number> {\n const db = getDb()\n const tenantId = this.requireTenantId('sweepExpiredReservations')\n const { data, error } = await db.rpc('shop_sweep_stock_reservations', {\n p_tenant_id: tenantId,\n p_variant_id: variantId ?? null,\n })\n if (error) throw new Error((error as Error).message ?? String(error))\n return Number(Array.isArray(data) ? (data[0] ?? 0) : (data ?? 0))\n }\n\n // --- helpers ------------------------------------------------------------------\n\n private async findByIdempotencyKey(key: string): Promise<StockMovement | null> {\n const db = getDb()\n let q = db.from(T.stockMovements).select('*').eq('idempotency_key', key)\n const tenantId = this.tenantId()\n if (tenantId) q = q.eq('tenant_id', tenantId)\n const { data, error } = await q.maybeSingle()\n if (error) throw error\n return data ? rowToMovement(data) : null\n }\n\n /** The variant facts the validator needs — tracking is 0034's new column. */\n private async readVariant(\n variantId: string,\n ): Promise<{ id: string; tenantId: string; productId: string | null; tracking: StockTrackingMode } | null> {\n const { data, error } = await getDb()\n .from(T.productVariants)\n .select('id,tenant_id,product_id,tracking')\n .eq('id', variantId)\n .maybeSingle()\n if (error) throw error\n if (!data) return null\n return {\n id: data.id,\n tenantId: data.tenant_id,\n productId: data.product_id ?? null,\n tracking: (data.tracking ?? 'quantity') as StockTrackingMode,\n }\n }\n\n private async readLot(\n lotId: string,\n ): Promise<{ id: string; variantId: string; serial: string | null } | null> {\n const { data, error } = await getDb()\n .from(T.stockLots)\n .select('id,variant_id,serial')\n .eq('id', lotId)\n .maybeSingle()\n if (error) throw error\n if (!data) return null\n return { id: data.id, variantId: data.variant_id, serial: data.serial ?? null }\n }\n\n /** on_hand for one lot across every location — the serial-bound predicate. */\n private async lotOnHand(lotId: string): Promise<number> {\n const rows = await this.listBalances({ lotId })\n return rows.reduce((sum, row) => sum + row.onHand, 0)\n }\n\n private async sellableLocationIds(): Promise<Set<string>> {\n const locations = await this.listLocations()\n return new Set(\n locations.filter((location) => location.isSellable && location.status === 'active').map((l) => l.id),\n )\n }\n}\n\nexport function createSupabaseStockStore(tenantId?: string): StockStore {\n return new SupabaseStockStore(tenantId)\n}\n","import {\n StockMovementError,\n mirroredInventoryCount,\n replayStockLedger,\n stockBalanceKey,\n} from './stock-store'\nimport type { StockStore } from './stock-store'\nimport { validateStockMovement } from './vocabulary'\nimport { allocateStock } from './allocation'\nimport { DEFAULT_STOCK_TRACKING } from './stock-types'\nimport type {\n ListStockBalancesOptions,\n ListStockLotsOptions,\n ListStockMovementsOptions,\n RecordStockMovementInput,\n ReserveStockInput,\n StockAllocation,\n StockAllocationCandidate,\n StockAllocationRequest,\n StockBalance,\n StockLocation,\n StockLot,\n StockMovement,\n StockReservation,\n StockTrackingMode,\n} from './stock-types'\n\n// ---------------------------------------------------------------------------\n// The in-memory StockStore. It is not a stub: it runs the SAME validator and\n// the SAME ledger→balance arithmetic as the SQL, so the behavioural claims in\n// stock-store.test.ts (\"balance follows the ledger\", \"the mirror follows the\n// balance\", \"a movement can never be silently lost\") are asserted against the\n// real rules rather than a convenient imitation.\n//\n// What it deliberately does NOT model: RLS, concurrency, and the GRANT layer.\n// Those are properties of Postgres and are proven in\n// packages/shop/test/stock-ledger.regression.sql.\n// ---------------------------------------------------------------------------\n\nexport interface MockVariantSeed {\n id: string\n tenantId?: string\n productId?: string | null\n tracking?: StockTrackingMode\n /** Starting mirror value. The seed writes NO movement for it — the opening\n * balance is 0035's job and tests exercise it explicitly. */\n inventoryCount?: number\n}\n\nexport interface MockStockSeed {\n tenantId?: string\n variants?: MockVariantSeed[]\n locations?: Partial<StockLocation>[]\n lots?: Partial<StockLot>[]\n}\n\nconst TENANT = 'mock-tenant'\n\nlet counter = 0\nfunction nextId(prefix: string): string {\n counter += 1\n return `${prefix}-${String(counter).padStart(4, '0')}`\n}\n\nconst ISO = '2026-01-01T00:00:00.000Z'\n\n/**\n * An in-memory stock ledger. `variants` is the mirror surface: every recorded\n * movement updates `inventoryCount` through the same projection the 0036\n * trigger performs, so a test can assert the mirror without a database.\n */\nexport class MockStockStore implements StockStore {\n readonly tenantId: string\n private readonly locations: StockLocation[] = []\n private readonly lots: StockLot[] = []\n private readonly movements: StockMovement[] = []\n private readonly reservations: StockReservation[] = []\n private readonly variants = new Map<\n string,\n { id: string; tenantId: string; productId: string | null; tracking: StockTrackingMode; inventoryCount: number }\n >()\n\n constructor(seed: MockStockSeed = {}) {\n this.tenantId = seed.tenantId ?? TENANT\n\n const seededLocations = seed.locations ?? []\n if (!seededLocations.some((l) => l.isDefault)) {\n this.locations.push(this.makeLocation({ code: 'default', name: 'Estoque principal', isDefault: true }))\n }\n for (const location of seededLocations) this.locations.push(this.makeLocation(location))\n\n for (const variant of seed.variants ?? []) {\n this.variants.set(variant.id, {\n id: variant.id,\n tenantId: variant.tenantId ?? this.tenantId,\n productId: variant.productId ?? null,\n tracking: variant.tracking ?? DEFAULT_STOCK_TRACKING,\n inventoryCount: variant.inventoryCount ?? 0,\n })\n }\n\n for (const lot of seed.lots ?? []) {\n this.lots.push({\n id: lot.id ?? nextId('lot'),\n tenantId: lot.tenantId ?? this.tenantId,\n variantId: lot.variantId ?? '',\n lotCode: lot.lotCode ?? nextId('LOT'),\n serial: lot.serial ?? null,\n manufacturedAt: lot.manufacturedAt ?? null,\n expiresAt: lot.expiresAt ?? null,\n receivedAt: lot.receivedAt ?? ISO,\n unitCost: lot.unitCost ?? null,\n supplierRef: lot.supplierRef ?? null,\n status: lot.status ?? 'active',\n createdAt: ISO,\n updatedAt: ISO,\n })\n }\n }\n\n private makeLocation(partial: Partial<StockLocation>): StockLocation {\n return {\n id: partial.id ?? nextId('loc'),\n tenantId: partial.tenantId ?? this.tenantId,\n code: partial.code ?? nextId('code'),\n name: partial.name ?? 'Local',\n kind: partial.kind ?? 'store',\n isSellable: partial.isSellable ?? true,\n isDefault: partial.isDefault ?? false,\n status: partial.status ?? 'active',\n createdAt: ISO,\n updatedAt: ISO,\n }\n }\n\n // --- reads ------------------------------------------------------------------\n\n async listLocations(): Promise<StockLocation[]> {\n return [...this.locations].sort((a, b) => Number(b.isDefault) - Number(a.isDefault))\n }\n\n async getDefaultLocation(): Promise<StockLocation | null> {\n return this.locations.find((l) => l.isDefault) ?? null\n }\n\n async listBalances(options: ListStockBalancesOptions = {}): Promise<StockBalance[]> {\n const replayed = replayStockLedger(this.movements, this.reservations)\n const sellable = new Set(this.locations.filter((l) => l.isSellable && l.status === 'active').map((l) => l.id))\n const rows: StockBalance[] = []\n for (const [key, row] of replayed) {\n if (options.variantId && row.variantId !== options.variantId) continue\n if (options.variantIds && !options.variantIds.includes(row.variantId)) continue\n if (options.locationId && row.locationId !== options.locationId) continue\n if (options.lotId && row.lotId !== options.lotId) continue\n if (options.sellableOnly && !sellable.has(row.locationId)) continue\n rows.push({\n id: key,\n tenantId: row.tenantId || this.tenantId,\n variantId: row.variantId,\n locationId: row.locationId,\n lotId: row.lotId,\n onHand: row.onHand,\n reserved: row.reserved,\n available: row.onHand - row.reserved,\n updatedAt: ISO,\n })\n }\n return rows.sort((a, b) => a.id.localeCompare(b.id))\n }\n\n async getAvailable(variantId: string): Promise<number> {\n const balances = await this.listBalances({ variantId })\n return mirroredInventoryCount(balances, this.sellableLocationIds())\n }\n\n async listLots(options: ListStockLotsOptions = {}): Promise<StockLot[]> {\n let rows = [...this.lots]\n if (options.variantId) rows = rows.filter((l) => l.variantId === options.variantId)\n if (options.status) rows = rows.filter((l) => l.status === options.status)\n if (options.expiresBefore) {\n rows = rows.filter((l) => l.expiresAt !== null && l.expiresAt <= options.expiresBefore!)\n }\n if (options.fefo !== false) {\n // FEFO: soonest expiry first, nulls last (a lot with no expiry never\n // beats one that does), then oldest received.\n rows.sort((a, b) => {\n if (a.expiresAt === b.expiresAt) return a.receivedAt.localeCompare(b.receivedAt)\n if (a.expiresAt === null) return 1\n if (b.expiresAt === null) return -1\n return a.expiresAt.localeCompare(b.expiresAt)\n })\n }\n return options.limit != null ? rows.slice(0, options.limit) : rows\n }\n\n async listMovements(options: ListStockMovementsOptions = {}): Promise<StockMovement[]> {\n let rows = [...this.movements]\n if (options.variantId) rows = rows.filter((m) => m.variantId === options.variantId)\n if (options.lotId) rows = rows.filter((m) => m.lotId === options.lotId)\n if (options.orderId) rows = rows.filter((m) => m.orderId === options.orderId)\n if (options.kind) rows = rows.filter((m) => m.kind === options.kind)\n if (options.reasonCode) rows = rows.filter((m) => m.reasonCode === options.reasonCode)\n if (options.newestFirst !== false) rows.reverse()\n return options.limit != null ? rows.slice(0, options.limit) : rows\n }\n\n async listReservations(variantId?: string): Promise<StockReservation[]> {\n return variantId ? this.reservations.filter((r) => r.variantId === variantId) : [...this.reservations]\n }\n\n // --- append -----------------------------------------------------------------\n\n async recordMovement(input: RecordStockMovementInput): Promise<StockMovement> {\n // Idempotency first: a retried write must return the SAME movement, never\n // a second one. This is the property that lets place_order be retried.\n if (input.idempotencyKey) {\n const existing = this.movements.find((m) => m.idempotencyKey === input.idempotencyKey)\n if (existing) return existing\n }\n\n const variant = this.variants.get(input.variantId)\n if (!variant) {\n throw new StockMovementError(`stock movement: variant ${input.variantId} not found`, input)\n }\n\n const lot = input.lotId ? (this.lots.find((l) => l.id === input.lotId) ?? null) : null\n try {\n validateStockMovement(input, {\n tracking: variant.tracking,\n lot: lot ? { id: lot.id, variantId: lot.variantId, serial: lot.serial } : null,\n // Summed over EVERY location: a serial is one physical unit in the\n // whole store, so moving it between locations must not let a second\n // copy appear. Same predicate as the SQL validator's SELECT.\n lotOnHand: lot ? await this.lotOnHand(lot.id) : undefined,\n })\n } catch (error) {\n throw new StockMovementError((error as Error).message, input)\n }\n\n const locationId = input.locationId ?? (await this.getDefaultLocation())!.id\n if (!this.locations.some((l) => l.id === locationId)) {\n throw new StockMovementError(\n `stock movement: location ${locationId} does not belong to store ${this.tenantId}`,\n input,\n )\n }\n\n const movement: StockMovement = {\n id: nextId('mov'),\n tenantId: variant.tenantId,\n variantId: variant.id,\n productId: variant.productId,\n locationId,\n lotId: input.lotId ?? null,\n kind: input.kind,\n reasonCode: input.reasonCode ?? null,\n quantity: input.quantity,\n unitCost: input.unitCost ?? null,\n orderId: input.orderId ?? null,\n reservationId: input.reservationId ?? null,\n idempotencyKey: input.idempotencyKey ?? null,\n occurredAt: input.occurredAt ?? ISO,\n createdBy: null,\n note: input.note ?? null,\n createdAt: ISO,\n }\n this.movements.push(movement)\n await this.syncMirror(variant.id)\n return movement\n }\n\n // --- allocation and the reservation lifecycle (§9 step 4) ---------------------\n\n /**\n * Runs the SHARED allocateStock() over the mock's own balances and lots, so\n * \"fefo picked the right lot\" is asserted against the same ordering the SQL\n * executes rather than against a convenient imitation.\n */\n async allocate(request: Omit<StockAllocationRequest, 'tracking'>): Promise<StockAllocation[]> {\n const variant = this.variants.get(request.variantId)\n if (!variant) {\n throw new StockMovementError(`stock movement: variant ${request.variantId} not found`, {\n variantId: request.variantId,\n kind: 'sale',\n quantity: -request.quantity,\n })\n }\n // §5's opportunistic sweep, on the one variant we are about to read: an\n // expired hold must not block the transaction that would have released it.\n await this.sweepExpiredReservations(request.variantId)\n return allocateStock(\n { ...request, tracking: variant.tracking },\n await this.allocationCandidates(request.variantId, variant.tracking),\n )\n }\n\n private async allocationCandidates(\n variantId: string,\n tracking: StockTrackingMode,\n ): Promise<StockAllocationCandidate[]> {\n const balances = await this.listBalances({ variantId })\n const byId = new Map(this.locations.map((l) => [l.id, l]))\n return balances.map((balance) => {\n const lot = balance.lotId ? this.lots.find((l) => l.id === balance.lotId) : null\n const location = byId.get(balance.locationId)\n return {\n lotId: balance.lotId,\n locationId: balance.locationId,\n available: balance.onHand - balance.reserved,\n expiresAt: lot?.expiresAt ?? null,\n // A quantity-tracked variant's implicit lot has no receipt date of its\n // own; the ledger's own order stands in, and with one position it never\n // ties anything.\n receivedAt: lot?.receivedAt ?? ISO,\n isSellableLocation: (location?.isSellable ?? true) && (location?.status ?? 'active') === 'active',\n isDefaultLocation: location?.isDefault ?? false,\n }\n }).filter((candidate) => (tracking === 'quantity' ? candidate.lotId == null : true))\n }\n\n async reserve(input: ReserveStockInput): Promise<StockReservation> {\n if (input.idempotencyKey) {\n const existing = this.reservations.find((r) => r.idempotencyKey === input.idempotencyKey)\n if (existing) return existing\n }\n return this.addReservation({\n variantId: input.variantId,\n locationId: input.locationId,\n lotId: input.lotId ?? null,\n quantity: input.quantity,\n status: 'held',\n orderId: input.orderId ?? null,\n cartToken: input.cartToken ?? null,\n expiresAt:\n input.ttlMinutes == null\n ? null\n : new Date(Date.now() + input.ttlMinutes * 60_000).toISOString(),\n idempotencyKey: input.idempotencyKey ?? null,\n createdBy: null,\n })\n }\n\n async sweepExpiredReservations(variantId?: string): Promise<number> {\n const now = new Date().toISOString()\n let swept = 0\n for (const reservation of this.reservations) {\n if (reservation.status !== 'held') continue\n if (variantId && reservation.variantId !== variantId) continue\n if (!reservation.expiresAt || reservation.expiresAt > now) continue\n reservation.status = 'released'\n swept += 1\n await this.syncMirror(reservation.variantId)\n }\n return swept\n }\n\n // --- the parts a database would do for us ------------------------------------\n\n /** on_hand for one lot across every location — the serial-bound predicate. */\n private async lotOnHand(lotId: string): Promise<number> {\n const rows = await this.listBalances({ lotId })\n return rows.reduce((sum, row) => sum + row.onHand, 0)\n }\n\n private sellableLocationIds(): Set<string> {\n return new Set(this.locations.filter((l) => l.isSellable && l.status === 'active').map((l) => l.id))\n }\n\n /** The 0036 mirror, in memory. */\n private async syncMirror(variantId: string): Promise<void> {\n const variant = this.variants.get(variantId)\n if (!variant) return\n if (variant.tracking === 'none') return\n const balances = await this.listBalances({ variantId })\n if (balances.length === 0) return // never entered the ledger — leave it alone\n variant.inventoryCount = mirroredInventoryCount(balances, this.sellableLocationIds())\n }\n\n /** The mirror column, for assertions. */\n getInventoryCount(variantId: string): number | undefined {\n return this.variants.get(variantId)?.inventoryCount\n }\n\n /**\n * The 0036 I10 shim: a direct write to `inventory_count` is allowed, logged\n * and TRANSLATED into an adjustment movement carrying the DELTA, so the\n * ledger never diverges from the column every current writer still uses.\n * Returns the warning the SQL trigger would have raised.\n */\n async writeInventoryCountDirectly(variantId: string, value: number): Promise<string | null> {\n const variant = this.variants.get(variantId)\n if (!variant) throw new Error(`variant ${variantId} not found`)\n const delta = value - variant.inventoryCount\n if (delta === 0) return null\n if (variant.tracking === 'none') {\n variant.inventoryCount = value\n return null\n }\n if (variant.tracking === 'lot' || variant.tracking === 'serial') {\n throw new StockMovementError(\n `plg_shop_product_variants.inventory_count is a derived mirror: variant ${variantId} is ${variant.tracking}-tracked, so a direct write cannot say which lot it means.`,\n { variantId, kind: 'adjustment', quantity: delta },\n )\n }\n const warning = `DEPRECATED (I10): direct write to plg_shop_product_variants.inventory_count on variant ${variantId} (${variant.inventoryCount} -> ${value}). Translated to an adjustment movement; write plg_shop_stock_movements instead.`\n variant.inventoryCount = value\n await this.recordMovement({\n variantId,\n kind: 'adjustment',\n reasonCode: 'legacy_write',\n quantity: delta,\n })\n return warning\n }\n\n /** Rebuild balances by replaying the ledger — proof the balance is a cache. */\n async rebuildBalances(): Promise<StockBalance[]> {\n return this.listBalances()\n }\n\n /**\n * Seed a hold. The lifecycle itself is §9 step 4; this exists so\n * `available = onHand − reserved` is provable today.\n *\n * async, and the mirror is AWAITED rather than fired and forgotten: a\n * floating promise here meant `getInventoryCount()` read the pre-hold value\n * on the very next line, which is the assertion a reservation test makes.\n */\n async addReservation(\n reservation: Omit<StockReservation, 'id' | 'tenantId' | 'createdAt' | 'updatedAt'> & { id?: string },\n ): Promise<StockReservation> {\n const row: StockReservation = {\n // Spread FIRST: spreading last let an explicit `{ id: undefined }` win\n // over the generated id and produced a reservation with no identity.\n ...reservation,\n id: reservation.id ?? nextId('res'),\n tenantId: this.tenantId,\n createdAt: ISO,\n updatedAt: ISO,\n }\n this.reservations.push(row)\n await this.syncMirror(row.variantId)\n return row\n }\n\n /** Move a hold through its lifecycle, re-projecting the mirror. */\n async setReservationStatus(id: string, status: StockReservation['status']): Promise<void> {\n const row = this.reservations.find((r) => r.id === id)\n if (!row) throw new Error(`reservation ${id} not found`)\n row.status = status\n await this.syncMirror(row.variantId)\n }\n\n /** Every ledger row, in insertion order. Nothing is ever removed from it. */\n get ledger(): readonly StockMovement[] {\n return this.movements\n }\n\n /** The stock the variant was created with, unchanged by the mock. */\n getTracking(variantId: string): StockTrackingMode | undefined {\n return this.variants.get(variantId)?.tracking\n }\n\n /** Flip a variant's tracking policy (an admin action), for tests. */\n setTracking(variantId: string, tracking: StockTrackingMode): void {\n const variant = this.variants.get(variantId)\n if (!variant) throw new Error(`variant ${variantId} not found`)\n variant.tracking = tracking\n }\n\n /** The balance key helper, re-exported for assertions that group by it. */\n static key = stockBalanceKey\n}\n\nexport function createMockStockStore(seed: MockStockSeed = {}): MockStockStore {\n return new MockStockStore(seed)\n}\n"]}