@aglyn/plugins-commerce 1.0.0-beta.291 → 1.0.0-beta.292

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 (92) hide show
  1. package/package.json +13 -13
  2. package/src/lib/components/cart-fulfillment.d.ts +3 -0
  3. package/src/lib/components/cart-fulfillment.js.map +1 -1
  4. package/src/lib/components/cart.js +14 -4
  5. package/src/lib/components/cart.js.map +1 -1
  6. package/src/lib/components/console/invoice-send.component.js +1 -1
  7. package/src/lib/components/console/invoice-send.component.js.map +1 -1
  8. package/src/lib/components/console/pos/pos-item-dialog.component.js +3 -2
  9. package/src/lib/components/console/pos/pos-item-dialog.component.js.map +1 -1
  10. package/src/lib/components/console/pos-kiosk-page.component.js +2 -1
  11. package/src/lib/components/console/pos-kiosk-page.component.js.map +1 -1
  12. package/src/lib/components/console/pos-page.component.js +1 -1
  13. package/src/lib/components/console/pos-page.component.js.map +1 -1
  14. package/src/lib/components/console/product-add-ons-fields.component.d.ts +41 -0
  15. package/src/lib/components/console/product-add-ons-fields.component.js +356 -0
  16. package/src/lib/components/console/product-add-ons-fields.component.js.map +1 -0
  17. package/src/lib/components/console/product-editor-dialog.component.js +31 -5
  18. package/src/lib/components/console/product-editor-dialog.component.js.map +1 -1
  19. package/src/lib/components/console/product-register-fields.component.d.ts +4 -5
  20. package/src/lib/components/console/product-register-fields.component.js +8 -197
  21. package/src/lib/components/console/product-register-fields.component.js.map +1 -1
  22. package/src/lib/components/console/products-hub-zone.component.js +6 -0
  23. package/src/lib/components/console/products-hub-zone.component.js.map +1 -1
  24. package/src/lib/components/console/quote-pay-link.component.d.ts +8 -1
  25. package/src/lib/components/console/quote-pay-link.component.js +29 -3
  26. package/src/lib/components/console/quote-pay-link.component.js.map +1 -1
  27. package/src/lib/components/console/quote-send.component.js +1 -4
  28. package/src/lib/components/console/quote-send.component.js.map +1 -1
  29. package/src/lib/components/product-add-ons.d.ts +28 -0
  30. package/src/lib/components/product-add-ons.js +136 -0
  31. package/src/lib/components/product-add-ons.js.map +1 -0
  32. package/src/lib/components/product-detail.d.ts +16 -0
  33. package/src/lib/components/product-detail.js +112 -19
  34. package/src/lib/components/product-detail.js.map +1 -1
  35. package/src/lib/components/product-grid.d.ts +8 -2
  36. package/src/lib/components/product-grid.js +6 -1
  37. package/src/lib/components/product-grid.js.map +1 -1
  38. package/src/lib/model/commerce-cart.d.ts +15 -3
  39. package/src/lib/model/commerce-cart.js +10 -3
  40. package/src/lib/model/commerce-cart.js.map +1 -1
  41. package/src/lib/model/commerce-invoices.d.ts +7 -0
  42. package/src/lib/model/commerce-invoices.js +11 -0
  43. package/src/lib/model/commerce-invoices.js.map +1 -1
  44. package/src/lib/model/commerce-orders.d.ts +6 -3
  45. package/src/lib/model/commerce-orders.js +3 -0
  46. package/src/lib/model/commerce-orders.js.map +1 -1
  47. package/src/lib/model/commerce-pos-kiosk.d.ts +1 -1
  48. package/src/lib/model/commerce-pos-kiosk.js +3 -2
  49. package/src/lib/model/commerce-pos-kiosk.js.map +1 -1
  50. package/src/lib/model/commerce-quote-email.d.ts +14 -0
  51. package/src/lib/model/commerce-quote-email.js +25 -9
  52. package/src/lib/model/commerce-quote-email.js.map +1 -1
  53. package/src/lib/model/commerce-quotes.js +1 -1
  54. package/src/lib/model/commerce-quotes.js.map +1 -1
  55. package/src/lib/model/commerce.d.ts +18 -0
  56. package/src/lib/model/commerce.js +26 -0
  57. package/src/lib/model/commerce.js.map +1 -1
  58. package/src/lib/model/product-modifiers.d.ts +74 -4
  59. package/src/lib/model/product-modifiers.js +131 -10
  60. package/src/lib/model/product-modifiers.js.map +1 -1
  61. package/src/lib/server/billing-webhook.js +64 -43
  62. package/src/lib/server/billing-webhook.js.map +1 -1
  63. package/src/lib/server/cart-checkout.js +14 -2
  64. package/src/lib/server/cart-checkout.js.map +1 -1
  65. package/src/lib/server/cart.d.ts +5 -0
  66. package/src/lib/server/cart.js +41 -4
  67. package/src/lib/server/cart.js.map +1 -1
  68. package/src/lib/server/catalog.d.ts +6 -0
  69. package/src/lib/server/catalog.js +2 -0
  70. package/src/lib/server/catalog.js.map +1 -1
  71. package/src/lib/server/checkout-extras.js +3 -1
  72. package/src/lib/server/checkout-extras.js.map +1 -1
  73. package/src/lib/server/checkout.js +28 -3
  74. package/src/lib/server/checkout.js.map +1 -1
  75. package/src/lib/server/local-fulfillment.js +5 -1
  76. package/src/lib/server/local-fulfillment.js.map +1 -1
  77. package/src/lib/server/pos-offline-sync.js +1 -1
  78. package/src/lib/server/pos-offline-sync.js.map +1 -1
  79. package/src/lib/server/pos-order.js +1 -1
  80. package/src/lib/server/pos-order.js.map +1 -1
  81. package/src/lib/server/product.d.ts +26 -0
  82. package/src/lib/server/product.js +18 -0
  83. package/src/lib/server/product.js.map +1 -1
  84. package/src/lib/server/products-write.js +5 -2
  85. package/src/lib/server/products-write.js.map +1 -1
  86. package/src/lib/server/quote-email.js.map +1 -1
  87. package/src/lib/utils/add-to-cart.d.ts +8 -0
  88. package/src/lib/utils/add-to-cart.js +10 -2
  89. package/src/lib/utils/add-to-cart.js.map +1 -1
  90. package/src/lib/utils/quote-email-content.d.ts +1 -0
  91. package/src/lib/utils/quote-email-content.js +7 -4
  92. package/src/lib/utils/quote-email-content.js.map +1 -1
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../libs/plugins/commerce/src/lib/server/pos-offline-sync.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport * as Aglyn from '@aglyn/aglyn/server'\nimport type { PluginApiHandler, PluginApiRequest } from '@aglyn/aglyn/server'\nimport recordCapturedContact from '@aglyn/aglyn/plugin-manager/record-captured-contact'\nimport { FieldValue } from 'firebase-admin/firestore'\nimport * as CommerceModel from '../model'\nimport {\n posOfflineFlagLabels,\n posOfflineSaleTotals,\n posOfflineSoldAt,\n posOfflineTotalsAddUp,\n POS_OFFLINE_CARD_INTENT_KIND,\n POS_OFFLINE_LATE_SYNC_MS,\n POS_OFFLINE_SYNC_BATCH_MAX,\n sanitizePosOfflineSale,\n type PosOfflineFlag,\n type PosOfflineKit,\n type PosOfflineKitCard,\n type PosOfflineOrderStamp,\n type PosOfflineRefusal,\n type PosOfflineSale,\n type PosOfflineSaleOutcome,\n type PosOfflineStockConflict,\n} from '../model/commerce-pos-offline'\nimport { ORDER_PAID_EVENT } from '../model/order-events'\nimport { holdsPosPermission } from '../model/plugin-permissions'\nimport { posMaxDiscountPct, posOfflineCardSettings } from '../plugin-config'\nimport { alertLowStockCrossing } from './low-stock'\nimport { notifyOrderBuyer } from './order-notifications'\nimport { raiseOrderEvent } from './order-events'\nimport { offlineFeeMonthKey } from './pos-fee-month'\nimport {\n authorizePosOps,\n defaultPosOpsDeps,\n posOpsBody,\n posOpsCleanId,\n resolvePosStaffAssertion,\n type PosOpsDeps,\n type PosOpsStaff,\n} from './pos-ops-gate'\nimport { posOfflineCardChargeRef, reportPosOfflineCardWrongAccount } from './pos-offline-card-reconcile'\nimport { posHardwareSaleFields } from './pos-hardware-sale-fields'\nimport { posOfflineCardOrderMoney, type PosOfflineCardSettled } from './pos-offline-card-order'\nimport { notifyPosSaleCompleted } from './pos-sale'\nimport { POS_CURRENCY, posCardApplicationFeeCents, posTerminalAvailable } from './pos-stripe'\nimport {\n capturePosOfflineCardIntent,\n ensurePosOfflineCardConfiguration,\n posMerchantAccount,\n posTerminalLocationId,\n retrievePosOfflineCardIntent,\n type PosOfflineCardIntent,\n} from './pos-terminal'\nimport { decrementVariantStock, type StockDecrementOutcome } from './reserve-stock'\n\n/*==========================================\n * THE OFFLINE REGISTER'S SYNC (AGL-3625): `/api/commerce/pos-offline-sync`.\n *\n * GET ?hostId= the offline kit: the store's in-person tax rate, the\n * discount ceiling, the shift rule, the registers and their\n * open shifts, and the receipt's head and foot\n * POST {hostId, sales[]} records each sale rung offline, once: cash,\n * and a card the Aglyn POS app's own reader stored and\n * Stripe has since been sent (AGL-4039), which this\n * captures with the platform fee\n *\n * The register's gate, unchanged (`authorizePosOps`): admin or editor on the\n * site, `managePos`, and the plan's `pos` entitlement — re-read at sync, so a\n * workspace that lost POS cannot sync into it. The plugin dispatcher has\n * already applied the security lockdown before this runs.\n *\n * ONE SALE, ONE ORDER, ONCE. The register's sale key is the order's id and\n * the id of a server-only record beside it (`posOfflineSales/{key}`), both\n * CREATED in one transaction. A sale sent twice — a connection that flapped\n * mid-request, a retry after an answer that never arrived — finds the record\n * and gets the first answer back, and a key that collides with any existing\n * order cannot overwrite it: `create` refuses.\n *\n * NEVER DROPPED. The customer has paid and left. So nothing the server finds\n * at sync refuses the sale: a changed price, short stock, a closed shift, a\n * register over the cap are each FLAGGED — on the order, its timeline and the\n * answer — and the sale is recorded as it happened. The only refusals are the\n * ones that protect somebody else: a sale signed for another member, another\n * site or another workspace stays on the device that rang it.\n *\n * STEPS AFTER THE COMMIT are recorded on the sync record as they finish, so a\n * request that dies between them is finished by the next sync of the same\n * sale rather than skipped (stock) or repeated (the receipt email).\n *=========================================*/\n\n/** The register's sync record: no customer data, only what dedupe and resume need. */\nexport interface PosOfflineSyncRecord {\n saleKey: string\n orderId: string\n number: number | null\n signedInUid: string\n registerId: string\n soldAtMs: number\n syncedAtMs: number\n status: 'recorded' | 'settled'\n flags: PosOfflineFlag[]\n stockConflicts: PosOfflineStockConflict[]\n /** Line indexes whose stock has been taken. */\n stockLines: number[]\n stockDone: boolean\n notified: boolean\n /** A card taken offline that was declined or never charged: no receipt and no \"paid\" for it. */\n unpaid?: boolean\n}\n\nexport interface PosOfflineAfterSale {\n hostId: string\n orderId: string\n order: CommerceModel.HostOrder\n email: string\n name: string\n}\n\nexport interface PosOfflineSyncDeps extends PosOpsDeps {\n /** The plan's take rate for one product type (AGL-2111). */\n feePct(org: Record<string, any> | null, productType: 'physical' | 'digital' | 'service'): number\n /** Whether the register at this creation rank is within the site's cap (AGL-1775). */\n registerWithinCap(org: Record<string, any> | null, hostId: string, rank: number): boolean\n /** Takes one line's units off the shelf, with its ledger row (AGL-2320). */\n decrementStock(input: {\n hostRef: FirebaseFirestore.DocumentReference\n hostId: string\n productId: string\n variantId: string\n quantity: number\n locationId?: string\n orderId: string\n }): Promise<StockDecrementOutcome>\n /** What a completed sale tells the rest of the workspace. Never throws. */\n afterSale(input: PosOfflineAfterSale): Promise<void>\n /** Cards taken offline on the app's own reader (AGL-4039); absent, the server takes none. */\n card?: PosOfflineCardDeps\n}\n\n/** The Stripe side of a card taken offline on the app's own reader. */\nexport interface PosOfflineCardDeps {\n /** Whether Terminal readers are offered at all, and the site's Location once one exists. */\n terminal(hostId: string): Promise<{ available: boolean; locationId: string | null }>\n /** The merchant's connected account, when it can take card payments. */\n merchantAccount(hostId: string, org: Record<string, any> | null): Promise<string | null>\n /** Turns Stripe's offline mode on for the site's readers; false when Stripe refused. */\n enableOffline(hostId: string): Promise<boolean>\n retrieve(paymentIntentId: string): Promise<{ ok: true; intent: PosOfflineCardIntent } | { ok: false; error: string }>\n /**\n * Captures with the platform fee. A failure carries Stripe's HTTP status\n * when Stripe answered; a request that never got an answer throws.\n */\n capture(\n paymentIntentId: string,\n applicationFeeCents: number,\n idempotencyKey: string,\n ): Promise<{ ok: true; intent: PosOfflineCardIntent } | { ok: false; error: string; status?: number }>\n /** Tells the operator a card was made out to another account. Never throws. */\n wrongAccount(input: {\n hostId: string\n orderId: string\n paymentIntentId: string\n status: string\n amountCents: number\n destination: string\n expected: string\n }): Promise<void>\n}\n\nexport function defaultPosOfflineCardDeps(): PosOfflineCardDeps {\n return {\n terminal: async (hostId) => ({\n available: posTerminalAvailable(),\n locationId: posTerminalAvailable() ? await posTerminalLocationId(hostId) : null,\n }),\n merchantAccount: posMerchantAccount,\n enableOffline: ensurePosOfflineCardConfiguration,\n retrieve: retrievePosOfflineCardIntent,\n capture: capturePosOfflineCardIntent,\n wrongAccount: reportPosOfflineCardWrongAccount,\n }\n}\n\nexport function defaultPosOfflineSyncDeps(): PosOfflineSyncDeps {\n const base = defaultPosOpsDeps()\n return {\n ...base,\n feePct: (org, productType) => Aglyn.resolveTransactionFeePct(org as any, productType),\n registerWithinCap: (org, hostId, rank) =>\n Aglyn.checkHostRegisterQuota(org as any, hostId, rank).allowed,\n decrementStock: async (input) => {\n const moved = await decrementVariantStock({\n firestore: base.firestore(),\n hostRef: input.hostRef,\n hostId: input.hostId,\n productId: input.productId,\n variantId: input.variantId,\n quantity: input.quantity,\n ...(input.locationId ? { locationId: input.locationId } : {}),\n ledger: { reason: 'sale', orderId: input.orderId },\n })\n if (moved.before && moved.after) alertLowStockCrossing(input.hostId, moved.before, moved.after)\n return moved\n },\n afterSale: async (input) => {\n const settle = async (label: string, work: () => Promise<unknown>) => {\n try {\n await work()\n } catch (error) {\n console.error(`[pos-offline-sync] ${label} failed`, input.orderId, error)\n }\n }\n if (input.email) {\n await settle('contact', () =>\n recordCapturedContact({\n orgId: '',\n hostId: input.hostId,\n identity: { email: input.email, ...(input.name ? { name: input.name } : {}) },\n surface: 'relationship',\n lifecycleFloor: 'customer',\n purchaseCents: Number(input.order.totals?.totalCents ?? 0),\n interaction: {\n source: 'order',\n refId: input.orderId,\n summary: `In-store purchase ($${(Number(input.order.totals?.totalCents ?? 0) / 100).toFixed(2)})`,\n },\n }),\n )\n }\n // The sale-completed listeners hear it too, told it was rung offline:\n // the cloud printers must not print a receipt or pop the drawer for a\n // sale the customer walked away with hours ago.\n await settle('sale completed', () =>\n notifyPosSaleCompleted({ hostId: input.hostId, orderId: input.orderId, order: input.order, offline: true }),\n )\n // The emailed receipt sends now, on sync (the printed one went offline).\n await settle('receipt', () =>\n notifyOrderBuyer({ hostId: input.hostId, orderId: input.orderId }, 'receipt', { email: input.email }),\n )\n await settle('paid event', () =>\n raiseOrderEvent(ORDER_PAID_EVENT, { hostId: input.hostId, orderId: input.orderId, key: 'paid' }),\n )\n },\n card: defaultPosOfflineCardDeps(),\n }\n}\n\ntype Outcome = { status: number; body: Record<string, unknown> }\n\nconst money = (cents: number) => `$${(Math.max(0, cents) / 100).toFixed(2)}`\n\n/** The store's in-person tax, as every register sale computes it. */\nexport function posInPersonTax(\n settings: CommerceModel.TaxSettings | undefined,\n):\n | { ok: true; pct: number | null; pricesIncludeTax: boolean; decision: CommerceModel.StorefrontTaxDecision }\n | { ok: false; reason: string } {\n const taxSettings = (settings ?? {}) as CommerceModel.TaxSettings\n const decision = CommerceModel.storefrontTaxDecision({ settings: taxSettings })\n if (decision.kind === 'undecided') {\n return { ok: false, reason: CommerceModel.STOREFRONT_TAX_UNDECIDED_MESSAGE }\n }\n const misconfigured = CommerceModel.storefrontTaxMisconfiguration(taxSettings, { inPerson: true })\n if (misconfigured) return { ok: false, reason: misconfigured }\n const rate =\n decision.kind === 'manual' ? CommerceModel.resolveTaxRate(taxSettings, taxSettings.origin ?? {}) : null\n return {\n ok: true,\n pct: rate && rate.pct > 0 ? rate.pct : null,\n pricesIncludeTax: taxSettings.pricesIncludeTax === true,\n decision,\n }\n}\n\ninterface SyncContext {\n deps: PosOfflineSyncDeps\n staff: PosOpsStaff\n config: Record<string, unknown>\n tax: ReturnType<typeof posInPersonTax>\n /** Registers by id, with their creation rank. */\n registers: Map<string, { rank: number; data: Record<string, any> }>\n}\n\nasync function readRegisters(staff: PosOpsStaff): Promise<SyncContext['registers']> {\n const docs = (await staff.hostRef.collection('registers').get()).docs\n .map((doc) => ({\n id: doc.id,\n data: (doc.data() ?? {}) as Record<string, any>,\n createdAtMs: Number(doc.get('createdAt')?.toMillis?.() ?? doc.get('createdAtMs') ?? 0),\n }))\n .sort((a, b) => a.createdAtMs - b.createdAtMs || a.id.localeCompare(b.id))\n return new Map(docs.map((doc, rank) => [doc.id, { rank, data: doc.data }]))\n}\n\nasync function readStoreTax(staff: PosOpsStaff): Promise<CommerceModel.TaxSettings | undefined> {\n const store = await staff.hostRef.collection('settings').doc('store').get()\n return (store.get('tax') ?? undefined) as CommerceModel.TaxSettings | undefined\n}\n\n/** The kit a register caches to sell offline. */\nexport async function buildPosOfflineKit(\n deps: PosOfflineSyncDeps,\n staff: PosOpsStaff,\n): Promise<PosOfflineKit> {\n const [registers, taxSettings, config, host, store] = await Promise.all([\n readRegisters(staff),\n readStoreTax(staff),\n staff.orgId ? deps.pluginConfig(staff.orgId, staff.hostId).catch(() => ({})) : Promise.resolve({}),\n staff.hostRef.get(),\n staff.hostRef.collection('settings').doc('store').get(),\n ])\n const tax = posInPersonTax(taxSettings)\n const card = await buildPosOfflineKitCard(deps, staff, config)\n return {\n v: 1,\n hostId: staff.hostId,\n orgId: staff.orgId,\n issuedAtMs: deps.now(),\n available: tax.ok,\n ...('reason' in tax ? { unavailableReason: tax.reason } : {}),\n tax: 'decision' in tax ? { pct: tax.pct, pricesIncludeTax: tax.pricesIncludeTax } : { pct: null, pricesIncludeTax: false },\n maxDiscountPct: posMaxDiscountPct(config),\n requireOpenShift: staff.settings.requireOpenShift,\n registers: [...registers.entries()]\n .filter(([, register]) => deps.registerWithinCap(staff.org, staff.hostId, register.rank))\n .map(([id, register]) => ({\n id,\n name: String(register.data['name'] ?? 'Register').slice(0, 120),\n ...(register.data['locationId'] ? { locationId: String(register.data['locationId']) } : {}),\n openShiftId: posOpsCleanId(register.data['openShiftId']) || null,\n })),\n ...(card ? { card } : {}),\n receipt: {\n name: String(host.get('displayName') ?? '') || 'Receipt',\n ...(typeof host.get('logoUrl') === 'string' && host.get('logoUrl') ? { logo: String(host.get('logoUrl')) } : {}),\n ...(staff.settings.receiptAddress ? { address: staff.settings.receiptAddress } : {}),\n ...(store.get('receiptFooter') ? { footer: String(store.get('receiptFooter')).slice(0, 500) } : {}),\n ...(staff.settings.returnPolicy ? { returnPolicy: staff.settings.returnPolicy } : {}),\n },\n }\n}\n\n/**\n * What the app needs to take a card offline on its own reader (AGL-4039), or\n * why it cannot. Absent where this server takes no offline cards.\n */\nasync function buildPosOfflineKitCard(\n deps: PosOfflineSyncDeps,\n staff: PosOpsStaff,\n config: Record<string, unknown>,\n): Promise<PosOfflineKitCard | undefined> {\n if (!deps.card) return undefined\n const settings = posOfflineCardSettings(config)\n const base = {\n destination: '',\n currency: POS_CURRENCY,\n maxSaleCents: settings.maxSaleCents,\n maxDeviceDailyCents: settings.maxDeviceDailyCents,\n }\n const off = (unavailableReason: string): PosOfflineKitCard => ({ ...base, available: false, unavailableReason })\n if (!settings.enabled) return off('Card sales offline are not turned on in this store’s point of sale settings.')\n const terminal = await deps.card.terminal(staff.hostId)\n if (!terminal.available) return off('Card readers are not set up for this store yet.')\n if (!terminal.locationId) return off('Connect a card reader once while online, then cards keep working offline.')\n const destination = await deps.card.merchantAccount(staff.hostId, staff.org)\n if (!destination) return off('Finish connecting Stripe to take cards.')\n if (!(await deps.card.enableOffline(staff.hostId))) {\n return off('Card sales offline could not be turned on for this store’s readers yet. Try again later.')\n }\n return { ...base, destination, available: true }\n}\n\n/** A card made out to a Stripe account that is not the store's. */\ninterface OfflineCardWrongAccount {\n destination: string\n expected: string\n status: string\n amountCents: number\n}\n\n/** What became of a card taken offline, as the sync settles it. */\ntype OfflineCardSettlement =\n | (PosOfflineCardSettled & { wrongAccount?: OfflineCardWrongAccount })\n | { kind: 'wait'; outcome: PosOfflineSaleOutcome }\n\n/** Statuses in which an intent holds the customer's money or has taken it. */\nconst INTENT_HOLDS_MONEY: ReadonlySet<string> = new Set(['requires_capture', 'processing', 'succeeded'])\n\n/**\n * How long after a capture that Stripe answered with a 5xx the sync waits\n * before reading \"still waiting for capture\" as the definite answer.\n */\nexport const POS_OFFLINE_CAPTURE_SETTLE_MS = 60_000\n\n/**\n * The capture's idempotency key (AGL-4039). Attempt 0 is the key every\n * capture used before attempts existed, so a retry of one already in flight\n * still finds it.\n *\n * WHY AN ATTEMPT. Stripe keeps the answer to an idempotent request for a day,\n * failures included, so a capture that failed once on a per-sale key would\n * fail the same way on every sync for a day, deep into the two days the\n * authorization lasts. The attempt moves on only when the previous one\n * DEFINITELY did not capture: Stripe refused it with a 4xx (not 409, a\n * request with the same key still running, and not 429, rate limited), or it\n * answered 5xx and the intent still waits for capture a minute later. A\n * request that got no answer at all throws before anything is recorded, so it\n * retries on the same key. Every sync reads the intent before it captures,\n * and Stripe captures an intent once, so a new key can never capture twice.\n */\nexport function posOfflineCaptureKey(hostId: string, saleKey: string, attempt: number): string {\n const base = `pos-offline-capture:${hostId}:${saleKey}`\n return attempt > 0 ? `${base}:${attempt}` : base\n}\n\n/** A capture Stripe refused for good: retrying the same key would replay the refusal. */\nexport function posOfflineCaptureRefused(status: number | undefined): boolean {\n return typeof status === 'number' && status >= 400 && status < 500 && status !== 409 && status !== 429\n}\n\n/**\n * Reads the card of a sale rung offline and settles it: captured with the\n * platform fee (the card tender's share of the take, by the one tender rule,\n * plus Stripe's processing cost, exactly as an online reader payment), or\n * unpaid when the bank declined it, Stripe never took it, or it was made out\n * to a Stripe account that is not the store's. Anything Stripe has not\n * finished yet waits on the register for the next sync.\n */\nasync function settleOfflineCard(\n context: SyncContext,\n sale: PosOfflineSale,\n takeFee: number,\n): Promise<OfflineCardSettlement> {\n const { deps, staff } = context\n const { saleKey } = sale\n const card = sale.card!\n const wait = (error: string): OfflineCardSettlement => ({ kind: 'wait', outcome: refused(saleKey, 'failed', error, true) })\n if (card.forward === 'stored') return wait('The card is still on this register. It syncs once Stripe has it.')\n if (!deps.card) return wait('Card sales taken offline cannot sync to this store yet.')\n const details = { ...(card.brand ? { brand: card.brand } : {}), ...(card.last4 ? { last4: card.last4 } : {}) }\n if (!card.paymentIntentId) {\n return {\n kind: 'unpaid',\n flag: card.forward === 'declined' ? 'card-declined' : 'card-not-charged',\n message: card.declineMessage || 'Stripe did not take the card.',\n ...details,\n }\n }\n const read = await deps.card.retrieve(card.paymentIntentId)\n if ('error' in read) return wait(`The card payment could not be checked yet: ${read.error}`)\n let intent = read.intent\n // The amount is NOT checked here: what Stripe took is what the customer\n // paid, and a difference from the server's total is flagged on the order\n // (`posOfflineCardOrderMoney`), never a reason to leave the sale unrecorded.\n if (\n intent.metadata['kind'] !== POS_OFFLINE_CARD_INTENT_KIND ||\n intent.metadata['saleKey'] !== saleKey ||\n intent.metadata['hostId'] !== staff.hostId ||\n intent.currency !== POS_CURRENCY\n ) {\n return { kind: 'wait', outcome: refused(saleKey, 'invalid', 'The card payment does not belong to this sale.', false) }\n }\n const found = { ...details, ...(intent.brand ? { brand: intent.brand } : {}), ...(intent.last4 ? { last4: intent.last4 } : {}) }\n // THE DESTINATION. The device made this intent out to the account its\n // offline kit named, and a kit can be stale (the store reconnected Stripe)\n // or a register tampered with. Money that holds or has moved is only ever\n // captured into, or recorded as paid to, the store's account as it is now.\n if (INTENT_HOLDS_MONEY.has(intent.status)) {\n const expected = await deps.card.merchantAccount(staff.hostId, staff.org)\n if (!expected) {\n return wait('This store’s Stripe account cannot take card payments right now. The card syncs once it can.')\n }\n if (intent.destination !== expected) {\n return {\n kind: 'unpaid',\n flag: 'card-not-charged',\n intentId: intent.id,\n message: 'The card was made out to a Stripe account that is not this store’s, so it was not charged here.',\n ...found,\n wrongAccount: { destination: intent.destination, expected, status: intent.status, amountCents: intent.amount },\n }\n }\n }\n const takeShareCents = CommerceModel.posTenderTakeCents('card_present', takeFee)\n const expectedFeeCents = posCardApplicationFeeCents({ takeShareCents, chargeCents: intent.amount })\n if (intent.status === 'requires_capture') {\n const chargeRef = posOfflineCardChargeRef(staff.hostRef, saleKey)\n const charge = ((await chargeRef.get()).data() ?? {}) as Record<string, unknown>\n let attempt = Math.max(0, Math.round(Number(charge['captureAttempt'] ?? 0)) || 0)\n if (\n charge['captureUncertainAttempt'] === attempt &&\n deps.now() - Number(charge['captureUncertainAtMs'] ?? 0) >= POS_OFFLINE_CAPTURE_SETTLE_MS\n ) {\n // The 5xx left the outcome unknown; the intent still waiting for\n // capture a minute later says it did not capture.\n attempt += 1\n await chargeRef.set(\n {\n captureAttempt: attempt,\n captureUncertainAttempt: FieldValue.delete(),\n captureUncertainAtMs: FieldValue.delete(),\n },\n { merge: true },\n )\n }\n const captured = await deps.card.capture(intent.id, expectedFeeCents, posOfflineCaptureKey(staff.hostId, saleKey, attempt))\n if ('error' in captured) {\n const stamp = { hostId: staff.hostId, saleKey, updatedAtMs: deps.now() }\n if (posOfflineCaptureRefused(captured.status)) {\n await chargeRef.set(\n {\n ...stamp,\n captureAttempt: attempt + 1,\n lastCaptureStatus: captured.status,\n lastCaptureError: captured.error.slice(0, 300),\n },\n { merge: true },\n )\n } else if (typeof captured.status === 'number' && captured.status >= 500) {\n await chargeRef.set(\n { ...stamp, captureUncertainAttempt: attempt, captureUncertainAtMs: deps.now() },\n { merge: true },\n )\n }\n return wait(`The card payment could not be captured yet: ${captured.error}`)\n }\n intent = captured.intent\n }\n if (intent.status === 'succeeded') {\n return {\n kind: 'captured',\n intentId: intent.id,\n amountCents: intent.amount,\n feeCents: intent.applicationFeeCents,\n expectedFeeCents,\n takeShareCents,\n livemode: intent.livemode,\n ...found,\n }\n }\n if (intent.status === 'requires_payment_method' || intent.status === 'canceled') {\n return {\n kind: 'unpaid',\n flag: 'card-declined',\n intentId: intent.id,\n message: intent.failureMessage || card.declineMessage || 'The card was declined.',\n ...found,\n }\n }\n return wait('The card payment is still processing. It syncs when Stripe finishes.')\n}\n\nconst refused = (\n saleKey: string,\n reason: PosOfflineRefusal,\n error: string,\n retry: boolean,\n): PosOfflineSaleOutcome => ({ saleKey, status: 'refused', reason, error, retry })\n\nconst replayOf = (record: PosOfflineSyncRecord): PosOfflineSaleOutcome => ({\n saleKey: record.saleKey,\n status: 'replayed',\n orderId: record.orderId,\n number: record.number,\n flags: record.flags,\n stockConflicts: record.stockConflicts,\n})\n\n/** The take the plan owes on the goods after the discount (AGL-2111, AGL-2256). */\nfunction takeFeeCents(\n deps: PosOfflineSyncDeps,\n staff: PosOpsStaff,\n lines: CommerceModel.OrderLineItem[],\n totals: CommerceModel.OrderTotals,\n): number {\n let applies = false\n const gross = lines.reduce((sum, line) => {\n const pct = deps.feePct(staff.org, (line.productType ?? 'physical') as 'physical' | 'digital' | 'service')\n if (pct > 0) applies = true\n return sum + Math.round((line.unitAmountCents * line.quantity * pct) / 100)\n }, 0)\n const charged = Math.max(0, totals.itemsCents - totals.discountCents)\n const scaled = totals.itemsCents > 0 ? Math.round((gross * charged) / totals.itemsCents) : 0\n return applies && charged > 0 ? Math.max(1, scaled) : scaled\n}\n\n/**\n * The steps after the commit, each recorded as it finishes. Run by the sync\n * that recorded the sale, and again by any later sync of it that finds them\n * unfinished.\n */\nasync function finishSale(\n context: SyncContext,\n recordRef: FirebaseFirestore.DocumentReference,\n orderRef: FirebaseFirestore.DocumentReference,\n record: PosOfflineSyncRecord,\n): Promise<PosOfflineSyncRecord> {\n const { deps, staff } = context\n const orderSnapshot = await orderRef.get()\n const order = (orderSnapshot.data() ?? {}) as CommerceModel.HostOrder & Record<string, any>\n let current = record\n if (!current.stockDone) {\n const conflicts = [...current.stockConflicts]\n const done = new Set(current.stockLines)\n const lines = (order.lineItems ?? []) as CommerceModel.OrderLineItem[]\n for (const [index, line] of lines.entries()) {\n if (done.has(index)) continue\n const moved = await deps.decrementStock({\n hostRef: staff.hostRef,\n hostId: staff.hostId,\n productId: line.productId,\n variantId: line.variantId ?? 'default',\n quantity: line.quantity,\n ...(order['locationId'] ? { locationId: String(order['locationId']) } : {}),\n orderId: orderRef.id,\n })\n // A failed commit leaves the line for the next sync to take, rather than\n // marking stock taken that never moved.\n if (moved.failed) continue\n // Untracked stock (or a product gone since) moves nothing and is short of nothing.\n const short = moved.before ? moved.applied - moved.requested : 0\n if (short > 0) {\n conflicts.push({\n productId: line.productId,\n ...(line.variantId ? { variantId: line.variantId } : {}),\n name: [line.name, line.variantLabel].filter(Boolean).join(' — '),\n requested: Math.abs(moved.requested),\n applied: Math.abs(moved.applied),\n shortUnits: short,\n })\n }\n done.add(index)\n await recordRef.update({ stockLines: [...done].sort((a, b) => a - b), stockConflicts: conflicts })\n }\n const stockDone = lines.every((_line, index) => done.has(index))\n const flags: PosOfflineFlag[] =\n conflicts.length && !current.flags.includes('stock-short') ? [...current.flags, 'stock-short'] : current.flags\n current = { ...current, stockLines: [...done], stockConflicts: conflicts, stockDone, flags }\n await recordRef.update({ stockDone, flags, stockConflicts: conflicts })\n if (conflicts.length && conflicts.length !== record.stockConflicts.length) {\n const fresh = (await orderRef.get()).data() as CommerceModel.HostOrder\n const stamp = fresh?.offline\n await orderRef.update({\n ...(stamp ? { offline: { ...stamp, flags, stockConflicts: conflicts } } : {}),\n timeline: CommerceModel.appendOrderEvent(\n fresh ?? {},\n 'offline-stock-short',\n conflicts\n .map((conflict) => `${conflict.shortUnits} of ${conflict.requested}× ${conflict.name} not in stock`)\n .join('; ')\n .slice(0, 500),\n deps.now(),\n ),\n })\n }\n if (!stockDone) return current\n }\n if (!current.notified && current.unpaid) {\n // A card that was never paid gets no receipt and no \"paid\": the order\n // waits, flagged, for the merchant to collect it another way.\n current = { ...current, notified: true, status: 'settled' }\n await recordRef.update({ notified: true, status: 'settled' })\n }\n if (!current.notified) {\n const email = String(order.customerEmail ?? '')\n await deps.afterSale({\n hostId: staff.hostId,\n orderId: orderRef.id,\n order: order as CommerceModel.HostOrder,\n email,\n name: String(order.customerName ?? ''),\n })\n current = { ...current, notified: true, status: 'settled' }\n await recordRef.update({ notified: true, status: 'settled' })\n }\n return current\n}\n\nasync function syncOne(context: SyncContext, raw: unknown): Promise<PosOfflineSaleOutcome> {\n const { deps, staff } = context\n const read = sanitizePosOfflineSale(raw)\n const rawKey = String((raw as Record<string, unknown> | null)?.['saleKey'] ?? '').slice(0, 64)\n if ('error' in read) return refused(rawKey, 'invalid', read.error, false)\n const sale = read.sale\n if (sale.hostId !== staff.hostId) {\n return refused(sale.saleKey, 'wrong-site', 'This sale was rung for another site.', false)\n }\n if (sale.signedInUid !== staff.uid) {\n return refused(\n sale.saleKey,\n 'wrong-staff',\n 'This sale was rung while another member was signed in. It syncs when they sign in on this register.',\n true,\n )\n }\n if (sale.orgId && staff.orgId && sale.orgId !== staff.orgId) {\n return refused(sale.saleKey, 'wrong-workspace', 'This sale was rung in another workspace.', false)\n }\n\n const firestore = deps.firestore()\n const hostRef = staff.hostRef\n const recordRef = hostRef.collection('posOfflineSales').doc(sale.saleKey)\n const orderRef = hostRef.collection('orders').doc(sale.saleKey)\n\n const existing = await recordRef.get()\n if (existing.exists) {\n const record = existing.data() as PosOfflineSyncRecord\n if (record.signedInUid !== staff.uid) {\n return refused(sale.saleKey, 'conflict', 'Another sale already used this key.', false)\n }\n return replayOf(record.status === 'settled' ? record : await finishSale(context, recordRef, orderRef, record))\n }\n\n const now = deps.now()\n const flags = new Set<PosOfflineFlag>()\n const soldAt = posOfflineSoldAt(sale.soldAtMs, now)\n if (soldAt.adjusted) flags.add('clock-adjusted')\n else if (now - soldAt.atMs > POS_OFFLINE_LATE_SYNC_MS) flags.add('late-sync')\n\n // The lines as sold, checked against the catalog as it stands now. The\n // price on the order is the one rung — that is what the customer paid —\n // and the catalog's is kept beside it where they differ.\n const productIds = [...new Set(sale.lines.map((line) => line.productId))]\n const snapshots = await Promise.all(productIds.map((id) => hostRef.collection('products').doc(id).get()))\n const products = new Map(\n snapshots.map((snapshot) => [\n snapshot.id,\n snapshot.exists ? CommerceModel.liftLegacyProduct(snapshot.data() as any) : null,\n ]),\n )\n const priceDrift: NonNullable<PosOfflineOrderStamp['priceDrift']> = []\n const lineItems: CommerceModel.OrderLineItem[] = sale.lines.map((line, index) => {\n const product = products.get(line.productId)\n if (!product) {\n flags.add('unknown-product')\n return {\n productId: line.productId,\n ...(line.variantId ? { variantId: line.variantId } : {}),\n name: line.name,\n ...(line.variantLabel ? { variantLabel: line.variantLabel } : {}),\n ...(line.sku ? { sku: line.sku } : {}),\n productType: line.productType ?? 'physical',\n quantity: line.quantity,\n unitAmountCents: line.unitAmountCents,\n }\n }\n const variant = product.variants.find((item) => item.id === line.variantId) ?? product.variants[0]\n const chosen = CommerceModel.resolveLineModifiers(product, line.modifiers)\n const currentCents =\n variant && CommerceModel.variantHasPrice(variant) && chosen.ok\n ? Math.round(Number(variant.priceUsd) * 100) + chosen.extraCents\n : null\n if (currentCents !== line.unitAmountCents) {\n flags.add('price-changed')\n priceDrift.push({ index, rungCents: line.unitAmountCents, currentCents: currentCents ?? 0 })\n }\n return {\n productId: line.productId,\n ...(variant && variant.id !== 'default' ? { variantId: variant.id } : {}),\n name: product.name || line.name,\n ...(line.variantLabel ? { variantLabel: line.variantLabel } : {}),\n ...(variant?.sku ? { sku: variant.sku } : line.sku ? { sku: line.sku } : {}),\n productType: product.type ?? line.productType ?? 'physical',\n quantity: line.quantity,\n unitAmountCents: line.unitAmountCents,\n ...(chosen.ok && chosen.modifiers.length ? { modifiers: chosen.modifiers } : {}),\n }\n })\n\n // The money: what the register rang, when its own arithmetic holds; the\n // arithmetic of its lines, with the tax it rang, when it does not.\n let totals: CommerceModel.OrderTotals\n if (posOfflineTotalsAddUp(sale)) {\n totals = CommerceModel.computeOrderTotals(lineItems, {\n discountCents: sale.totals.discountCents,\n taxCents: sale.totals.taxCents,\n })\n } else {\n flags.add('totals-restated')\n const restated = posOfflineSaleTotals({\n lines: sale.lines,\n discountPct: sale.discountPct,\n tax: { pct: null, pricesIncludeTax: false },\n })\n totals = CommerceModel.computeOrderTotals(lineItems, {\n discountCents: restated.discountCents,\n taxCents: Math.min(sale.totals.taxCents, restated.itemsCents - restated.discountCents),\n })\n }\n if ('decision' in context.tax) {\n const expected = posOfflineSaleTotals({\n lines: sale.lines,\n discountPct: sale.discountPct,\n tax: { pct: context.tax.pct, pricesIncludeTax: context.tax.pricesIncludeTax },\n })\n if (expected.taxCents !== totals.taxCents) flags.add('tax-differs')\n } else if (totals.taxCents > 0) {\n flags.add('tax-differs')\n }\n if (sale.discountPct > posMaxDiscountPct(context.config)) flags.add('discount-over-limit')\n // The cashier's own discount needs `posDiscount` (AGL-4047), as the online\n // register refuses it without. Offline the sale has already happened, so it\n // is recorded and flagged rather than dropped. Read for the member who was\n // signed in, whose membership the gate resolved, as the online check reads it.\n if (sale.discountPct > 0 && !holdsPosPermission(staff.membership.permissions, 'posDiscount')) {\n flags.add('discount-not-permitted')\n }\n const byCard = sale.tender === 'card'\n if (!byCard && sale.cashTenderedCents < totals.totalCents) flags.add('cash-short')\n\n // Where it was rung.\n const register = context.registers.get(sale.registerId)\n if (!register) flags.add('unknown-register')\n else if (!deps.registerWithinCap(staff.org, staff.hostId, register.rank)) flags.add('register-over-cap')\n let shiftId = ''\n if (sale.shiftId && register) {\n const shift = await hostRef\n .collection('registers')\n .doc(sale.registerId)\n .collection('shifts')\n .doc(sale.shiftId)\n .get()\n if (shift.exists) {\n shiftId = sale.shiftId\n if (shift.get('status') !== 'open') flags.add('shift-closed')\n }\n }\n if (!shiftId && staff.settings.requireOpenShift) flags.add('no-shift')\n\n // Who rang it: a PIN-switched cashier's assertion is read as of the moment\n // the cash was taken (it was valid then, and expiring since is the normal\n // case for a sale that waited to sync), and the member is re-checked NOW.\n let cashierId = staff.uid\n if (sale.cashierAssertion) {\n const memberUid = await resolvePosStaffAssertion(\n { ...deps, now: () => soldAt.atMs },\n hostRef,\n sale.cashierAssertion,\n { hostId: staff.hostId, registerId: sale.registerId, purpose: 'cashier' },\n )\n if (memberUid) cashierId = memberUid\n else flags.add('cashier-unverified')\n }\n\n // Cash carries no platform fee (AGL-4036): the plan's take is worked out\n // and then settled by the one predicate every tender answers to, so a sale\n // synced from the queue is charged exactly what the same sale rung online\n // is.\n const planTake = takeFeeCents(deps, staff, lineItems, totals)\n // A card taken offline settles with Stripe BEFORE the order is written: a\n // capture that fails leaves the sale on the register for the next sync,\n // and a repeat after a lost answer finds the first capture (AGL-4039).\n const settled = byCard ? await settleOfflineCard(context, sale, planTake) : null\n if (settled?.kind === 'wait') return settled.outcome\n // The card's money, decided once against the server's totals: the fee it\n // settled, the shortfall to invoice, and the order-level intent the refund\n // and the dispute lookup read.\n const cardMoney = settled\n ? posOfflineCardOrderMoney({\n settled,\n totals,\n planTakeCents: planTake,\n soldAtMs: soldAt.atMs,\n nowMs: now,\n cashierId,\n feeOrgId: staff.orgId ?? '',\n })\n : null\n for (const flag of cardMoney?.flags ?? []) flags.add(flag)\n const cashTake = CommerceModel.posTenderTakeCents('cash', planTake)\n totals = cardMoney ? cardMoney.totals : { ...totals, feeCents: cashTake }\n // What accrues to the org's month with the order: the cash take (none\n // since AGL-4036) or the part of a card's fee its payout did not carry.\n const invoiceCents = cardMoney ? cardMoney.invoiceCents : cashTake\n const changeCents = byCard ? 0 : Math.max(0, sale.cashTenderedCents - totals.totalCents)\n const flagList = [...flags]\n const stamp: PosOfflineOrderStamp = {\n saleKey: sale.saleKey,\n soldAtMs: soldAt.atMs,\n syncedAtMs: now,\n syncedBy: staff.uid,\n flags: flagList,\n ...(priceDrift.length ? { priceDrift } : {}),\n }\n const customer = sale.customer ?? {}\n const feeMonth = offlineFeeMonthKey(new Date(now))\n const feeRef = staff.orgId\n ? firestore.collection('orgs').doc(staff.orgId).collection('offlineFees').doc(feeMonth)\n : null\n const counterRef = hostRef.collection('counters').doc('orders')\n\n const committed = await firestore.runTransaction(async (transaction) => {\n const raced = await transaction.get(recordRef)\n if (raced.exists) return { raced: raced.data() as PosOfflineSyncRecord }\n const taken = await transaction.get(orderRef)\n if (taken.exists) return { collision: true as const }\n const counter = await transaction.get(counterRef)\n const number = Number(counter.get('next') ?? 1)\n transaction.set(counterRef, { next: number + 1 }, { merge: true })\n const cardName = settled\n ? [settled.brand ? settled.brand.toUpperCase() : 'Card', settled.last4 ? `•••• ${settled.last4}` : ''].filter(Boolean).join(' ')\n : ''\n const paidDetail = !settled\n ? `Cash — received ${money(sale.cashTenderedCents)}, change ${money(changeCents)}. ` +\n 'Rung while the register was offline.'\n : settled.kind === 'captured'\n ? `${cardName} — ${money(settled.amountCents)}, taken while the register was offline and charged when it reconnected.`\n : `${cardName} — ${money(totals.totalCents)} taken while the register was offline was not paid: ${settled.message}`.slice(0, 500)\n const timeline: CommerceModel.OrderTimelineEvent[] = [\n { atMs: soldAt.atMs, event: settled?.kind === 'unpaid' ? 'payment-failed' : 'paid', detail: paidDetail },\n {\n atMs: now,\n event: 'offline-synced',\n detail: flagList.length\n ? `Synced. Check: ${posOfflineFlagLabels(flagList).join('; ')}`.slice(0, 1000)\n : 'Synced.',\n },\n ]\n transaction.create(\n orderRef,\n CommerceModel.withOrderListFields(orderRef.id, {\n number,\n status: cardMoney ? cardMoney.status : 'paid',\n channel: 'pos',\n registerId: sale.registerId,\n cashierId,\n ...(shiftId ? { shiftId } : {}),\n ...(customer.name ? { customerName: customer.name } : {}),\n ...(customer.kind && customer.id ? { customerRecord: { kind: customer.kind, id: customer.id } } : {}),\n ...(sale.discountPct > 0 ? { discountPct: sale.discountPct, discountBy: cashierId } : {}),\n ...(sale.locationId ? { locationId: sale.locationId } : {}),\n // A card reader sold offline on the platform's own store (AGL-4038):\n // recorded once synced, with nobody attached, for the To attach list.\n ...posHardwareSaleFields({ hostId: staff.hostId, orgId: staff.orgId, lineItems, products }),\n lineItems,\n totals,\n taxMode: 'decision' in context.tax\n ? CommerceModel.storefrontTaxModeForDecision(context.tax.decision, totals.taxCents)\n : totals.taxCents > 0\n ? 'manual'\n : 'none',\n ...(cardMoney ? cardMoney.fields : cashTake > 0 ? { feeCollection: 'invoice' as const } : {}),\n payments: cardMoney\n ? [cardMoney.payment]\n : [\n {\n id: 'pay_cash',\n method: 'cash',\n amountCents: totals.totalCents,\n status: 'succeeded',\n atMs: soldAt.atMs,\n settledAtMs: soldAt.atMs,\n takeFeeCents: cashTake,\n feeCents: 0,\n cashierId,\n cashTenderedCents: sale.cashTenderedCents,\n changeCents,\n } satisfies CommerceModel.OrderPayment,\n ],\n customerEmail: customer.email || null,\n timeline,\n offline: stamp,\n createdAtMs: soldAt.atMs,\n createdAt: FieldValue.serverTimestamp(),\n }),\n )\n const record: PosOfflineSyncRecord = {\n saleKey: sale.saleKey,\n orderId: orderRef.id,\n number,\n signedInUid: staff.uid,\n registerId: sale.registerId,\n soldAtMs: soldAt.atMs,\n syncedAtMs: now,\n status: 'recorded',\n flags: flagList,\n stockConflicts: [],\n stockLines: [],\n stockDone: false,\n notified: false,\n ...(settled?.kind === 'unpaid' ? { unpaid: true } : {}),\n }\n transaction.create(recordRef, record)\n // The intent's record says its sale synced, in the same commit, so the\n // webhook and the unsynced-card sweep never report a sale that landed.\n if (settled?.intentId) {\n transaction.set(\n posOfflineCardChargeRef(hostRef, sale.saleKey),\n {\n hostId: staff.hostId,\n saleKey: sale.saleKey,\n paymentIntentId: settled.intentId,\n registerId: sale.registerId,\n synced: true,\n orderId: orderRef.id,\n syncedAtMs: now,\n updatedAtMs: now,\n alertDueAtMs: FieldValue.delete(),\n },\n { merge: true },\n )\n }\n // The fee no payout carried accrues with the order, in the same\n // transaction (AGL-2111): a card's share was netted from its payout by\n // the capture, and only a shortfall in that lands here.\n if (invoiceCents > 0 && feeRef) {\n transaction.set(\n feeRef,\n { month: feeMonth, feeCents: FieldValue.increment(invoiceCents), orders: FieldValue.increment(1) },\n { merge: true },\n )\n }\n return { record }\n })\n if ('collision' in committed) {\n return refused(sale.saleKey, 'conflict', 'Another order already has this sale’s key.', false)\n }\n if ('raced' in committed) {\n const record = committed.raced\n if (record.signedInUid !== staff.uid) {\n return refused(sale.saleKey, 'conflict', 'Another sale already used this key.', false)\n }\n return replayOf(record)\n }\n if (settled?.kind === 'unpaid' && settled.wrongAccount && settled.intentId) {\n await deps.card!.wrongAccount({\n hostId: staff.hostId,\n orderId: orderRef.id,\n paymentIntentId: settled.intentId,\n ...settled.wrongAccount,\n })\n }\n const finished = await finishSale(context, recordRef, orderRef, committed.record)\n return {\n saleKey: sale.saleKey,\n status: 'recorded',\n orderId: orderRef.id,\n number: finished.number,\n flags: finished.flags,\n stockConflicts: finished.stockConflicts,\n }\n}\n\nexport async function handlePosOfflineSync(\n deps: PosOfflineSyncDeps,\n req: PluginApiRequest,\n): Promise<Outcome> {\n if (req.method !== 'GET' && req.method !== 'POST') {\n return { status: 405, body: { error: 'Method not allowed' } }\n }\n const body =\n req.method === 'GET'\n ? Object.fromEntries(\n Object.entries(req.query ?? {}).map(([key, value]) => [key, Array.isArray(value) ? value[0] : value]),\n )\n : posOpsBody(req)\n const gate = await authorizePosOps(deps, req, body['hostId'], 'posSell')\n if ('error' in gate) return { status: gate.status, body: { error: gate.error } }\n const staff = gate.staff\n if (req.method === 'GET') {\n return { status: 200, body: { kit: await buildPosOfflineKit(deps, staff) } }\n }\n const sales = Array.isArray(body['sales']) ? (body['sales'] as unknown[]) : null\n if (!sales) return { status: 400, body: { error: 'Missing sales' } }\n if (sales.length > POS_OFFLINE_SYNC_BATCH_MAX) {\n return { status: 400, body: { error: `Send at most ${POS_OFFLINE_SYNC_BATCH_MAX} sales at a time` } }\n }\n if (sales.length === 0) return { status: 200, body: { results: [] } }\n const [registers, taxSettings, config] = await Promise.all([\n readRegisters(staff),\n readStoreTax(staff),\n staff.orgId ? deps.pluginConfig(staff.orgId, staff.hostId).catch(() => ({})) : Promise.resolve({}),\n ])\n const context: SyncContext = { deps, staff, config, tax: posInPersonTax(taxSettings), registers }\n const results: PosOfflineSaleOutcome[] = []\n // One at a time, in the order they were rung, so order numbers follow the\n // till and one sale's failure cannot take the rest with it.\n for (const sale of sales) {\n try {\n results.push(await syncOne(context, sale))\n } catch (error) {\n console.error('[pos-offline-sync] sale failed', error)\n const key = String((sale as Record<string, unknown> | null)?.['saleKey'] ?? '').slice(0, 64)\n results.push(refused(key, 'failed', 'The sale could not be synced yet. It stays on this register.', true))\n }\n }\n return { status: 200, body: { results } }\n}\n\nexport function createPosOfflineSyncHandler(\n deps: () => PosOfflineSyncDeps = defaultPosOfflineSyncDeps,\n): PluginApiHandler {\n return async (req, res) => {\n try {\n const outcome = await handlePosOfflineSync(deps(), req)\n return res.status(outcome.status).json(outcome.body)\n } catch (error) {\n console.error('[pos-offline-sync] failed', error)\n return res.status(500).json({ error: 'The offline sales could not be synced' })\n }\n }\n}\n\nexport const posOfflineSyncHandler = createPosOfflineSyncHandler()\n"],"names":["Aglyn","recordCapturedContact","FieldValue","CommerceModel","posOfflineFlagLabels","posOfflineSaleTotals","posOfflineSoldAt","posOfflineTotalsAddUp","POS_OFFLINE_CARD_INTENT_KIND","POS_OFFLINE_LATE_SYNC_MS","POS_OFFLINE_SYNC_BATCH_MAX","sanitizePosOfflineSale","ORDER_PAID_EVENT","holdsPosPermission","posMaxDiscountPct","posOfflineCardSettings","alertLowStockCrossing","notifyOrderBuyer","raiseOrderEvent","offlineFeeMonthKey","authorizePosOps","defaultPosOpsDeps","posOpsBody","posOpsCleanId","resolvePosStaffAssertion","posOfflineCardChargeRef","reportPosOfflineCardWrongAccount","posHardwareSaleFields","posOfflineCardOrderMoney","notifyPosSaleCompleted","POS_CURRENCY","posCardApplicationFeeCents","posTerminalAvailable","capturePosOfflineCardIntent","ensurePosOfflineCardConfiguration","posMerchantAccount","posTerminalLocationId","retrievePosOfflineCardIntent","decrementVariantStock","defaultPosOfflineCardDeps","terminal","hostId","available","locationId","merchantAccount","enableOffline","retrieve","capture","wrongAccount","defaultPosOfflineSyncDeps","base","feePct","org","productType","resolveTransactionFeePct","registerWithinCap","rank","checkHostRegisterQuota","allowed","decrementStock","input","moved","firestore","hostRef","productId","variantId","quantity","ledger","reason","orderId","before","after","afterSale","settle","label","work","error","console","email","orgId","identity","name","surface","lifecycleFloor","purchaseCents","Number","order","totals","totalCents","interaction","source","refId","summary","toFixed","offline","key","card","money","cents","Math","max","posInPersonTax","settings","taxSettings","decision","storefrontTaxDecision","kind","ok","STOREFRONT_TAX_UNDECIDED_MESSAGE","misconfigured","storefrontTaxMisconfiguration","inPerson","rate","resolveTaxRate","origin","pct","pricesIncludeTax","readRegisters","staff","docs","collection","get","map","doc","id","data","createdAtMs","toMillis","sort","a","b","localeCompare","Map","readStoreTax","store","undefined","buildPosOfflineKit","deps","host","registers","config","Promise","all","pluginConfig","catch","resolve","tax","buildPosOfflineKitCard","v","issuedAtMs","now","unavailableReason","maxDiscountPct","requireOpenShift","entries","filter","register","String","slice","openShiftId","receipt","logo","receiptAddress","address","footer","returnPolicy","destination","currency","maxSaleCents","maxDeviceDailyCents","off","enabled","INTENT_HOLDS_MONEY","Set","POS_OFFLINE_CAPTURE_SETTLE_MS","posOfflineCaptureKey","saleKey","attempt","posOfflineCaptureRefused","status","settleOfflineCard","context","sale","takeFee","wait","outcome","refused","forward","details","brand","last4","paymentIntentId","flag","message","declineMessage","read","intent","metadata","found","has","expected","intentId","amountCents","amount","takeShareCents","posTenderTakeCents","expectedFeeCents","chargeCents","charge","chargeRef","round","set","captureAttempt","captureUncertainAttempt","delete","captureUncertainAtMs","merge","captured","stamp","updatedAtMs","lastCaptureStatus","lastCaptureError","feeCents","applicationFeeCents","livemode","failureMessage","retry","replayOf","record","number","flags","stockConflicts","takeFeeCents","lines","applies","gross","reduce","sum","line","unitAmountCents","charged","itemsCents","discountCents","scaled","finishSale","recordRef","orderRef","orderSnapshot","current","stockDone","conflicts","done","stockLines","lineItems","index","failed","short","applied","requested","push","variantLabel","Boolean","join","abs","shortUnits","add","update","every","_line","length","includes","fresh","timeline","appendOrderEvent","conflict","notified","unpaid","customerEmail","customerName","syncOne","raw","rawKey","signedInUid","uid","existing","exists","soldAt","soldAtMs","adjusted","atMs","productIds","snapshots","products","snapshot","liftLegacyProduct","priceDrift","product","sku","variant","variants","find","item","chosen","resolveLineModifiers","modifiers","currentCents","variantHasPrice","priceUsd","extraCents","rungCents","type","computeOrderTotals","taxCents","restated","discountPct","min","membership","permissions","byCard","tender","cashTenderedCents","registerId","shiftId","shift","cashierId","cashierAssertion","memberUid","purpose","planTake","settled","cardMoney","planTakeCents","nowMs","feeOrgId","cashTake","invoiceCents","changeCents","flagList","syncedAtMs","syncedBy","customer","feeMonth","Date","feeRef","counterRef","committed","runTransaction","transaction","counter","raced","taken","collision","next","cardName","toUpperCase","paidDetail","event","detail","create","withOrderListFields","channel","customerRecord","discountBy","taxMode","storefrontTaxModeForDecision","fields","feeCollection","payments","payment","method","settledAtMs","createdAt","serverTimestamp","synced","alertDueAtMs","month","increment","orders","finished","handlePosOfflineSync","req","body","Object","fromEntries","query","value","Array","isArray","gate","kit","sales","results","createPosOfflineSyncHandler","res","json","posOfflineSyncHandler"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/commerce/src/lib/server/pos-offline-sync.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport * as Aglyn from '@aglyn/aglyn/server'\nimport type { PluginApiHandler, PluginApiRequest } from '@aglyn/aglyn/server'\nimport recordCapturedContact from '@aglyn/aglyn/plugin-manager/record-captured-contact'\nimport { FieldValue } from 'firebase-admin/firestore'\nimport * as CommerceModel from '../model'\nimport {\n posOfflineFlagLabels,\n posOfflineSaleTotals,\n posOfflineSoldAt,\n posOfflineTotalsAddUp,\n POS_OFFLINE_CARD_INTENT_KIND,\n POS_OFFLINE_LATE_SYNC_MS,\n POS_OFFLINE_SYNC_BATCH_MAX,\n sanitizePosOfflineSale,\n type PosOfflineFlag,\n type PosOfflineKit,\n type PosOfflineKitCard,\n type PosOfflineOrderStamp,\n type PosOfflineRefusal,\n type PosOfflineSale,\n type PosOfflineSaleOutcome,\n type PosOfflineStockConflict,\n} from '../model/commerce-pos-offline'\nimport { ORDER_PAID_EVENT } from '../model/order-events'\nimport { holdsPosPermission } from '../model/plugin-permissions'\nimport { posMaxDiscountPct, posOfflineCardSettings } from '../plugin-config'\nimport { alertLowStockCrossing } from './low-stock'\nimport { notifyOrderBuyer } from './order-notifications'\nimport { raiseOrderEvent } from './order-events'\nimport { offlineFeeMonthKey } from './pos-fee-month'\nimport {\n authorizePosOps,\n defaultPosOpsDeps,\n posOpsBody,\n posOpsCleanId,\n resolvePosStaffAssertion,\n type PosOpsDeps,\n type PosOpsStaff,\n} from './pos-ops-gate'\nimport { posOfflineCardChargeRef, reportPosOfflineCardWrongAccount } from './pos-offline-card-reconcile'\nimport { posHardwareSaleFields } from './pos-hardware-sale-fields'\nimport { posOfflineCardOrderMoney, type PosOfflineCardSettled } from './pos-offline-card-order'\nimport { notifyPosSaleCompleted } from './pos-sale'\nimport { POS_CURRENCY, posCardApplicationFeeCents, posTerminalAvailable } from './pos-stripe'\nimport {\n capturePosOfflineCardIntent,\n ensurePosOfflineCardConfiguration,\n posMerchantAccount,\n posTerminalLocationId,\n retrievePosOfflineCardIntent,\n type PosOfflineCardIntent,\n} from './pos-terminal'\nimport { decrementVariantStock, type StockDecrementOutcome } from './reserve-stock'\n\n/*==========================================\n * THE OFFLINE REGISTER'S SYNC (AGL-3625): `/api/commerce/pos-offline-sync`.\n *\n * GET ?hostId= the offline kit: the store's in-person tax rate, the\n * discount ceiling, the shift rule, the registers and their\n * open shifts, and the receipt's head and foot\n * POST {hostId, sales[]} records each sale rung offline, once: cash,\n * and a card the Aglyn POS app's own reader stored and\n * Stripe has since been sent (AGL-4039), which this\n * captures with the platform fee\n *\n * The register's gate, unchanged (`authorizePosOps`): admin or editor on the\n * site, `managePos`, and the plan's `pos` entitlement — re-read at sync, so a\n * workspace that lost POS cannot sync into it. The plugin dispatcher has\n * already applied the security lockdown before this runs.\n *\n * ONE SALE, ONE ORDER, ONCE. The register's sale key is the order's id and\n * the id of a server-only record beside it (`posOfflineSales/{key}`), both\n * CREATED in one transaction. A sale sent twice — a connection that flapped\n * mid-request, a retry after an answer that never arrived — finds the record\n * and gets the first answer back, and a key that collides with any existing\n * order cannot overwrite it: `create` refuses.\n *\n * NEVER DROPPED. The customer has paid and left. So nothing the server finds\n * at sync refuses the sale: a changed price, short stock, a closed shift, a\n * register over the cap are each FLAGGED — on the order, its timeline and the\n * answer — and the sale is recorded as it happened. The only refusals are the\n * ones that protect somebody else: a sale signed for another member, another\n * site or another workspace stays on the device that rang it.\n *\n * STEPS AFTER THE COMMIT are recorded on the sync record as they finish, so a\n * request that dies between them is finished by the next sync of the same\n * sale rather than skipped (stock) or repeated (the receipt email).\n *=========================================*/\n\n/** The register's sync record: no customer data, only what dedupe and resume need. */\nexport interface PosOfflineSyncRecord {\n saleKey: string\n orderId: string\n number: number | null\n signedInUid: string\n registerId: string\n soldAtMs: number\n syncedAtMs: number\n status: 'recorded' | 'settled'\n flags: PosOfflineFlag[]\n stockConflicts: PosOfflineStockConflict[]\n /** Line indexes whose stock has been taken. */\n stockLines: number[]\n stockDone: boolean\n notified: boolean\n /** A card taken offline that was declined or never charged: no receipt and no \"paid\" for it. */\n unpaid?: boolean\n}\n\nexport interface PosOfflineAfterSale {\n hostId: string\n orderId: string\n order: CommerceModel.HostOrder\n email: string\n name: string\n}\n\nexport interface PosOfflineSyncDeps extends PosOpsDeps {\n /** The plan's take rate for one product type (AGL-2111). */\n feePct(org: Record<string, any> | null, productType: 'physical' | 'digital' | 'service'): number\n /** Whether the register at this creation rank is within the site's cap (AGL-1775). */\n registerWithinCap(org: Record<string, any> | null, hostId: string, rank: number): boolean\n /** Takes one line's units off the shelf, with its ledger row (AGL-2320). */\n decrementStock(input: {\n hostRef: FirebaseFirestore.DocumentReference\n hostId: string\n productId: string\n variantId: string\n quantity: number\n locationId?: string\n orderId: string\n }): Promise<StockDecrementOutcome>\n /** What a completed sale tells the rest of the workspace. Never throws. */\n afterSale(input: PosOfflineAfterSale): Promise<void>\n /** Cards taken offline on the app's own reader (AGL-4039); absent, the server takes none. */\n card?: PosOfflineCardDeps\n}\n\n/** The Stripe side of a card taken offline on the app's own reader. */\nexport interface PosOfflineCardDeps {\n /** Whether Terminal readers are offered at all, and the site's Location once one exists. */\n terminal(hostId: string): Promise<{ available: boolean; locationId: string | null }>\n /** The merchant's connected account, when it can take card payments. */\n merchantAccount(hostId: string, org: Record<string, any> | null): Promise<string | null>\n /** Turns Stripe's offline mode on for the site's readers; false when Stripe refused. */\n enableOffline(hostId: string): Promise<boolean>\n retrieve(paymentIntentId: string): Promise<{ ok: true; intent: PosOfflineCardIntent } | { ok: false; error: string }>\n /**\n * Captures with the platform fee. A failure carries Stripe's HTTP status\n * when Stripe answered; a request that never got an answer throws.\n */\n capture(\n paymentIntentId: string,\n applicationFeeCents: number,\n idempotencyKey: string,\n ): Promise<{ ok: true; intent: PosOfflineCardIntent } | { ok: false; error: string; status?: number }>\n /** Tells the operator a card was made out to another account. Never throws. */\n wrongAccount(input: {\n hostId: string\n orderId: string\n paymentIntentId: string\n status: string\n amountCents: number\n destination: string\n expected: string\n }): Promise<void>\n}\n\nexport function defaultPosOfflineCardDeps(): PosOfflineCardDeps {\n return {\n terminal: async (hostId) => ({\n available: posTerminalAvailable(),\n locationId: posTerminalAvailable() ? await posTerminalLocationId(hostId) : null,\n }),\n merchantAccount: posMerchantAccount,\n enableOffline: ensurePosOfflineCardConfiguration,\n retrieve: retrievePosOfflineCardIntent,\n capture: capturePosOfflineCardIntent,\n wrongAccount: reportPosOfflineCardWrongAccount,\n }\n}\n\nexport function defaultPosOfflineSyncDeps(): PosOfflineSyncDeps {\n const base = defaultPosOpsDeps()\n return {\n ...base,\n feePct: (org, productType) => Aglyn.resolveTransactionFeePct(org as any, productType),\n registerWithinCap: (org, hostId, rank) =>\n Aglyn.checkHostRegisterQuota(org as any, hostId, rank).allowed,\n decrementStock: async (input) => {\n const moved = await decrementVariantStock({\n firestore: base.firestore(),\n hostRef: input.hostRef,\n hostId: input.hostId,\n productId: input.productId,\n variantId: input.variantId,\n quantity: input.quantity,\n ...(input.locationId ? { locationId: input.locationId } : {}),\n ledger: { reason: 'sale', orderId: input.orderId },\n })\n if (moved.before && moved.after) alertLowStockCrossing(input.hostId, moved.before, moved.after)\n return moved\n },\n afterSale: async (input) => {\n const settle = async (label: string, work: () => Promise<unknown>) => {\n try {\n await work()\n } catch (error) {\n console.error(`[pos-offline-sync] ${label} failed`, input.orderId, error)\n }\n }\n if (input.email) {\n await settle('contact', () =>\n recordCapturedContact({\n orgId: '',\n hostId: input.hostId,\n identity: { email: input.email, ...(input.name ? { name: input.name } : {}) },\n surface: 'relationship',\n lifecycleFloor: 'customer',\n purchaseCents: Number(input.order.totals?.totalCents ?? 0),\n interaction: {\n source: 'order',\n refId: input.orderId,\n summary: `In-store purchase ($${(Number(input.order.totals?.totalCents ?? 0) / 100).toFixed(2)})`,\n },\n }),\n )\n }\n // The sale-completed listeners hear it too, told it was rung offline:\n // the cloud printers must not print a receipt or pop the drawer for a\n // sale the customer walked away with hours ago.\n await settle('sale completed', () =>\n notifyPosSaleCompleted({ hostId: input.hostId, orderId: input.orderId, order: input.order, offline: true }),\n )\n // The emailed receipt sends now, on sync (the printed one went offline).\n await settle('receipt', () =>\n notifyOrderBuyer({ hostId: input.hostId, orderId: input.orderId }, 'receipt', { email: input.email }),\n )\n await settle('paid event', () =>\n raiseOrderEvent(ORDER_PAID_EVENT, { hostId: input.hostId, orderId: input.orderId, key: 'paid' }),\n )\n },\n card: defaultPosOfflineCardDeps(),\n }\n}\n\ntype Outcome = { status: number; body: Record<string, unknown> }\n\nconst money = (cents: number) => `$${(Math.max(0, cents) / 100).toFixed(2)}`\n\n/** The store's in-person tax, as every register sale computes it. */\nexport function posInPersonTax(\n settings: CommerceModel.TaxSettings | undefined,\n):\n | { ok: true; pct: number | null; pricesIncludeTax: boolean; decision: CommerceModel.StorefrontTaxDecision }\n | { ok: false; reason: string } {\n const taxSettings = (settings ?? {}) as CommerceModel.TaxSettings\n const decision = CommerceModel.storefrontTaxDecision({ settings: taxSettings })\n if (decision.kind === 'undecided') {\n return { ok: false, reason: CommerceModel.STOREFRONT_TAX_UNDECIDED_MESSAGE }\n }\n const misconfigured = CommerceModel.storefrontTaxMisconfiguration(taxSettings, { inPerson: true })\n if (misconfigured) return { ok: false, reason: misconfigured }\n const rate =\n decision.kind === 'manual' ? CommerceModel.resolveTaxRate(taxSettings, taxSettings.origin ?? {}) : null\n return {\n ok: true,\n pct: rate && rate.pct > 0 ? rate.pct : null,\n pricesIncludeTax: taxSettings.pricesIncludeTax === true,\n decision,\n }\n}\n\ninterface SyncContext {\n deps: PosOfflineSyncDeps\n staff: PosOpsStaff\n config: Record<string, unknown>\n tax: ReturnType<typeof posInPersonTax>\n /** Registers by id, with their creation rank. */\n registers: Map<string, { rank: number; data: Record<string, any> }>\n}\n\nasync function readRegisters(staff: PosOpsStaff): Promise<SyncContext['registers']> {\n const docs = (await staff.hostRef.collection('registers').get()).docs\n .map((doc) => ({\n id: doc.id,\n data: (doc.data() ?? {}) as Record<string, any>,\n createdAtMs: Number(doc.get('createdAt')?.toMillis?.() ?? doc.get('createdAtMs') ?? 0),\n }))\n .sort((a, b) => a.createdAtMs - b.createdAtMs || a.id.localeCompare(b.id))\n return new Map(docs.map((doc, rank) => [doc.id, { rank, data: doc.data }]))\n}\n\nasync function readStoreTax(staff: PosOpsStaff): Promise<CommerceModel.TaxSettings | undefined> {\n const store = await staff.hostRef.collection('settings').doc('store').get()\n return (store.get('tax') ?? undefined) as CommerceModel.TaxSettings | undefined\n}\n\n/** The kit a register caches to sell offline. */\nexport async function buildPosOfflineKit(\n deps: PosOfflineSyncDeps,\n staff: PosOpsStaff,\n): Promise<PosOfflineKit> {\n const [registers, taxSettings, config, host, store] = await Promise.all([\n readRegisters(staff),\n readStoreTax(staff),\n staff.orgId ? deps.pluginConfig(staff.orgId, staff.hostId).catch(() => ({})) : Promise.resolve({}),\n staff.hostRef.get(),\n staff.hostRef.collection('settings').doc('store').get(),\n ])\n const tax = posInPersonTax(taxSettings)\n const card = await buildPosOfflineKitCard(deps, staff, config)\n return {\n v: 1,\n hostId: staff.hostId,\n orgId: staff.orgId,\n issuedAtMs: deps.now(),\n available: tax.ok,\n ...('reason' in tax ? { unavailableReason: tax.reason } : {}),\n tax: 'decision' in tax ? { pct: tax.pct, pricesIncludeTax: tax.pricesIncludeTax } : { pct: null, pricesIncludeTax: false },\n maxDiscountPct: posMaxDiscountPct(config),\n requireOpenShift: staff.settings.requireOpenShift,\n registers: [...registers.entries()]\n .filter(([, register]) => deps.registerWithinCap(staff.org, staff.hostId, register.rank))\n .map(([id, register]) => ({\n id,\n name: String(register.data['name'] ?? 'Register').slice(0, 120),\n ...(register.data['locationId'] ? { locationId: String(register.data['locationId']) } : {}),\n openShiftId: posOpsCleanId(register.data['openShiftId']) || null,\n })),\n ...(card ? { card } : {}),\n receipt: {\n name: String(host.get('displayName') ?? '') || 'Receipt',\n ...(typeof host.get('logoUrl') === 'string' && host.get('logoUrl') ? { logo: String(host.get('logoUrl')) } : {}),\n ...(staff.settings.receiptAddress ? { address: staff.settings.receiptAddress } : {}),\n ...(store.get('receiptFooter') ? { footer: String(store.get('receiptFooter')).slice(0, 500) } : {}),\n ...(staff.settings.returnPolicy ? { returnPolicy: staff.settings.returnPolicy } : {}),\n },\n }\n}\n\n/**\n * What the app needs to take a card offline on its own reader (AGL-4039), or\n * why it cannot. Absent where this server takes no offline cards.\n */\nasync function buildPosOfflineKitCard(\n deps: PosOfflineSyncDeps,\n staff: PosOpsStaff,\n config: Record<string, unknown>,\n): Promise<PosOfflineKitCard | undefined> {\n if (!deps.card) return undefined\n const settings = posOfflineCardSettings(config)\n const base = {\n destination: '',\n currency: POS_CURRENCY,\n maxSaleCents: settings.maxSaleCents,\n maxDeviceDailyCents: settings.maxDeviceDailyCents,\n }\n const off = (unavailableReason: string): PosOfflineKitCard => ({ ...base, available: false, unavailableReason })\n if (!settings.enabled) return off('Card sales offline are not turned on in this store’s point of sale settings.')\n const terminal = await deps.card.terminal(staff.hostId)\n if (!terminal.available) return off('Card readers are not set up for this store yet.')\n if (!terminal.locationId) return off('Connect a card reader once while online, then cards keep working offline.')\n const destination = await deps.card.merchantAccount(staff.hostId, staff.org)\n if (!destination) return off('Finish connecting Stripe to take cards.')\n if (!(await deps.card.enableOffline(staff.hostId))) {\n return off('Card sales offline could not be turned on for this store’s readers yet. Try again later.')\n }\n return { ...base, destination, available: true }\n}\n\n/** A card made out to a Stripe account that is not the store's. */\ninterface OfflineCardWrongAccount {\n destination: string\n expected: string\n status: string\n amountCents: number\n}\n\n/** What became of a card taken offline, as the sync settles it. */\ntype OfflineCardSettlement =\n | (PosOfflineCardSettled & { wrongAccount?: OfflineCardWrongAccount })\n | { kind: 'wait'; outcome: PosOfflineSaleOutcome }\n\n/** Statuses in which an intent holds the customer's money or has taken it. */\nconst INTENT_HOLDS_MONEY: ReadonlySet<string> = new Set(['requires_capture', 'processing', 'succeeded'])\n\n/**\n * How long after a capture that Stripe answered with a 5xx the sync waits\n * before reading \"still waiting for capture\" as the definite answer.\n */\nexport const POS_OFFLINE_CAPTURE_SETTLE_MS = 60_000\n\n/**\n * The capture's idempotency key (AGL-4039). Attempt 0 is the key every\n * capture used before attempts existed, so a retry of one already in flight\n * still finds it.\n *\n * WHY AN ATTEMPT. Stripe keeps the answer to an idempotent request for a day,\n * failures included, so a capture that failed once on a per-sale key would\n * fail the same way on every sync for a day, deep into the two days the\n * authorization lasts. The attempt moves on only when the previous one\n * DEFINITELY did not capture: Stripe refused it with a 4xx (not 409, a\n * request with the same key still running, and not 429, rate limited), or it\n * answered 5xx and the intent still waits for capture a minute later. A\n * request that got no answer at all throws before anything is recorded, so it\n * retries on the same key. Every sync reads the intent before it captures,\n * and Stripe captures an intent once, so a new key can never capture twice.\n */\nexport function posOfflineCaptureKey(hostId: string, saleKey: string, attempt: number): string {\n const base = `pos-offline-capture:${hostId}:${saleKey}`\n return attempt > 0 ? `${base}:${attempt}` : base\n}\n\n/** A capture Stripe refused for good: retrying the same key would replay the refusal. */\nexport function posOfflineCaptureRefused(status: number | undefined): boolean {\n return typeof status === 'number' && status >= 400 && status < 500 && status !== 409 && status !== 429\n}\n\n/**\n * Reads the card of a sale rung offline and settles it: captured with the\n * platform fee (the card tender's share of the take, by the one tender rule,\n * plus Stripe's processing cost, exactly as an online reader payment), or\n * unpaid when the bank declined it, Stripe never took it, or it was made out\n * to a Stripe account that is not the store's. Anything Stripe has not\n * finished yet waits on the register for the next sync.\n */\nasync function settleOfflineCard(\n context: SyncContext,\n sale: PosOfflineSale,\n takeFee: number,\n): Promise<OfflineCardSettlement> {\n const { deps, staff } = context\n const { saleKey } = sale\n const card = sale.card!\n const wait = (error: string): OfflineCardSettlement => ({ kind: 'wait', outcome: refused(saleKey, 'failed', error, true) })\n if (card.forward === 'stored') return wait('The card is still on this register. It syncs once Stripe has it.')\n if (!deps.card) return wait('Card sales taken offline cannot sync to this store yet.')\n const details = { ...(card.brand ? { brand: card.brand } : {}), ...(card.last4 ? { last4: card.last4 } : {}) }\n if (!card.paymentIntentId) {\n return {\n kind: 'unpaid',\n flag: card.forward === 'declined' ? 'card-declined' : 'card-not-charged',\n message: card.declineMessage || 'Stripe did not take the card.',\n ...details,\n }\n }\n const read = await deps.card.retrieve(card.paymentIntentId)\n if ('error' in read) return wait(`The card payment could not be checked yet: ${read.error}`)\n let intent = read.intent\n // The amount is NOT checked here: what Stripe took is what the customer\n // paid, and a difference from the server's total is flagged on the order\n // (`posOfflineCardOrderMoney`), never a reason to leave the sale unrecorded.\n if (\n intent.metadata['kind'] !== POS_OFFLINE_CARD_INTENT_KIND ||\n intent.metadata['saleKey'] !== saleKey ||\n intent.metadata['hostId'] !== staff.hostId ||\n intent.currency !== POS_CURRENCY\n ) {\n return { kind: 'wait', outcome: refused(saleKey, 'invalid', 'The card payment does not belong to this sale.', false) }\n }\n const found = { ...details, ...(intent.brand ? { brand: intent.brand } : {}), ...(intent.last4 ? { last4: intent.last4 } : {}) }\n // THE DESTINATION. The device made this intent out to the account its\n // offline kit named, and a kit can be stale (the store reconnected Stripe)\n // or a register tampered with. Money that holds or has moved is only ever\n // captured into, or recorded as paid to, the store's account as it is now.\n if (INTENT_HOLDS_MONEY.has(intent.status)) {\n const expected = await deps.card.merchantAccount(staff.hostId, staff.org)\n if (!expected) {\n return wait('This store’s Stripe account cannot take card payments right now. The card syncs once it can.')\n }\n if (intent.destination !== expected) {\n return {\n kind: 'unpaid',\n flag: 'card-not-charged',\n intentId: intent.id,\n message: 'The card was made out to a Stripe account that is not this store’s, so it was not charged here.',\n ...found,\n wrongAccount: { destination: intent.destination, expected, status: intent.status, amountCents: intent.amount },\n }\n }\n }\n const takeShareCents = CommerceModel.posTenderTakeCents('card_present', takeFee)\n const expectedFeeCents = posCardApplicationFeeCents({ takeShareCents, chargeCents: intent.amount })\n if (intent.status === 'requires_capture') {\n const chargeRef = posOfflineCardChargeRef(staff.hostRef, saleKey)\n const charge = ((await chargeRef.get()).data() ?? {}) as Record<string, unknown>\n let attempt = Math.max(0, Math.round(Number(charge['captureAttempt'] ?? 0)) || 0)\n if (\n charge['captureUncertainAttempt'] === attempt &&\n deps.now() - Number(charge['captureUncertainAtMs'] ?? 0) >= POS_OFFLINE_CAPTURE_SETTLE_MS\n ) {\n // The 5xx left the outcome unknown; the intent still waiting for\n // capture a minute later says it did not capture.\n attempt += 1\n await chargeRef.set(\n {\n captureAttempt: attempt,\n captureUncertainAttempt: FieldValue.delete(),\n captureUncertainAtMs: FieldValue.delete(),\n },\n { merge: true },\n )\n }\n const captured = await deps.card.capture(intent.id, expectedFeeCents, posOfflineCaptureKey(staff.hostId, saleKey, attempt))\n if ('error' in captured) {\n const stamp = { hostId: staff.hostId, saleKey, updatedAtMs: deps.now() }\n if (posOfflineCaptureRefused(captured.status)) {\n await chargeRef.set(\n {\n ...stamp,\n captureAttempt: attempt + 1,\n lastCaptureStatus: captured.status,\n lastCaptureError: captured.error.slice(0, 300),\n },\n { merge: true },\n )\n } else if (typeof captured.status === 'number' && captured.status >= 500) {\n await chargeRef.set(\n { ...stamp, captureUncertainAttempt: attempt, captureUncertainAtMs: deps.now() },\n { merge: true },\n )\n }\n return wait(`The card payment could not be captured yet: ${captured.error}`)\n }\n intent = captured.intent\n }\n if (intent.status === 'succeeded') {\n return {\n kind: 'captured',\n intentId: intent.id,\n amountCents: intent.amount,\n feeCents: intent.applicationFeeCents,\n expectedFeeCents,\n takeShareCents,\n livemode: intent.livemode,\n ...found,\n }\n }\n if (intent.status === 'requires_payment_method' || intent.status === 'canceled') {\n return {\n kind: 'unpaid',\n flag: 'card-declined',\n intentId: intent.id,\n message: intent.failureMessage || card.declineMessage || 'The card was declined.',\n ...found,\n }\n }\n return wait('The card payment is still processing. It syncs when Stripe finishes.')\n}\n\nconst refused = (\n saleKey: string,\n reason: PosOfflineRefusal,\n error: string,\n retry: boolean,\n): PosOfflineSaleOutcome => ({ saleKey, status: 'refused', reason, error, retry })\n\nconst replayOf = (record: PosOfflineSyncRecord): PosOfflineSaleOutcome => ({\n saleKey: record.saleKey,\n status: 'replayed',\n orderId: record.orderId,\n number: record.number,\n flags: record.flags,\n stockConflicts: record.stockConflicts,\n})\n\n/** The take the plan owes on the goods after the discount (AGL-2111, AGL-2256). */\nfunction takeFeeCents(\n deps: PosOfflineSyncDeps,\n staff: PosOpsStaff,\n lines: CommerceModel.OrderLineItem[],\n totals: CommerceModel.OrderTotals,\n): number {\n let applies = false\n const gross = lines.reduce((sum, line) => {\n const pct = deps.feePct(staff.org, (line.productType ?? 'physical') as 'physical' | 'digital' | 'service')\n if (pct > 0) applies = true\n return sum + Math.round((line.unitAmountCents * line.quantity * pct) / 100)\n }, 0)\n const charged = Math.max(0, totals.itemsCents - totals.discountCents)\n const scaled = totals.itemsCents > 0 ? Math.round((gross * charged) / totals.itemsCents) : 0\n return applies && charged > 0 ? Math.max(1, scaled) : scaled\n}\n\n/**\n * The steps after the commit, each recorded as it finishes. Run by the sync\n * that recorded the sale, and again by any later sync of it that finds them\n * unfinished.\n */\nasync function finishSale(\n context: SyncContext,\n recordRef: FirebaseFirestore.DocumentReference,\n orderRef: FirebaseFirestore.DocumentReference,\n record: PosOfflineSyncRecord,\n): Promise<PosOfflineSyncRecord> {\n const { deps, staff } = context\n const orderSnapshot = await orderRef.get()\n const order = (orderSnapshot.data() ?? {}) as CommerceModel.HostOrder & Record<string, any>\n let current = record\n if (!current.stockDone) {\n const conflicts = [...current.stockConflicts]\n const done = new Set(current.stockLines)\n const lines = (order.lineItems ?? []) as CommerceModel.OrderLineItem[]\n for (const [index, line] of lines.entries()) {\n if (done.has(index)) continue\n const moved = await deps.decrementStock({\n hostRef: staff.hostRef,\n hostId: staff.hostId,\n productId: line.productId,\n variantId: line.variantId ?? 'default',\n quantity: line.quantity,\n ...(order['locationId'] ? { locationId: String(order['locationId']) } : {}),\n orderId: orderRef.id,\n })\n // A failed commit leaves the line for the next sync to take, rather than\n // marking stock taken that never moved.\n if (moved.failed) continue\n // Untracked stock (or a product gone since) moves nothing and is short of nothing.\n const short = moved.before ? moved.applied - moved.requested : 0\n if (short > 0) {\n conflicts.push({\n productId: line.productId,\n ...(line.variantId ? { variantId: line.variantId } : {}),\n name: [line.name, line.variantLabel].filter(Boolean).join(' — '),\n requested: Math.abs(moved.requested),\n applied: Math.abs(moved.applied),\n shortUnits: short,\n })\n }\n done.add(index)\n await recordRef.update({ stockLines: [...done].sort((a, b) => a - b), stockConflicts: conflicts })\n }\n const stockDone = lines.every((_line, index) => done.has(index))\n const flags: PosOfflineFlag[] =\n conflicts.length && !current.flags.includes('stock-short') ? [...current.flags, 'stock-short'] : current.flags\n current = { ...current, stockLines: [...done], stockConflicts: conflicts, stockDone, flags }\n await recordRef.update({ stockDone, flags, stockConflicts: conflicts })\n if (conflicts.length && conflicts.length !== record.stockConflicts.length) {\n const fresh = (await orderRef.get()).data() as CommerceModel.HostOrder\n const stamp = fresh?.offline\n await orderRef.update({\n ...(stamp ? { offline: { ...stamp, flags, stockConflicts: conflicts } } : {}),\n timeline: CommerceModel.appendOrderEvent(\n fresh ?? {},\n 'offline-stock-short',\n conflicts\n .map((conflict) => `${conflict.shortUnits} of ${conflict.requested}× ${conflict.name} not in stock`)\n .join('; ')\n .slice(0, 500),\n deps.now(),\n ),\n })\n }\n if (!stockDone) return current\n }\n if (!current.notified && current.unpaid) {\n // A card that was never paid gets no receipt and no \"paid\": the order\n // waits, flagged, for the merchant to collect it another way.\n current = { ...current, notified: true, status: 'settled' }\n await recordRef.update({ notified: true, status: 'settled' })\n }\n if (!current.notified) {\n const email = String(order.customerEmail ?? '')\n await deps.afterSale({\n hostId: staff.hostId,\n orderId: orderRef.id,\n order: order as CommerceModel.HostOrder,\n email,\n name: String(order.customerName ?? ''),\n })\n current = { ...current, notified: true, status: 'settled' }\n await recordRef.update({ notified: true, status: 'settled' })\n }\n return current\n}\n\nasync function syncOne(context: SyncContext, raw: unknown): Promise<PosOfflineSaleOutcome> {\n const { deps, staff } = context\n const read = sanitizePosOfflineSale(raw)\n const rawKey = String((raw as Record<string, unknown> | null)?.['saleKey'] ?? '').slice(0, 64)\n if ('error' in read) return refused(rawKey, 'invalid', read.error, false)\n const sale = read.sale\n if (sale.hostId !== staff.hostId) {\n return refused(sale.saleKey, 'wrong-site', 'This sale was rung for another site.', false)\n }\n if (sale.signedInUid !== staff.uid) {\n return refused(\n sale.saleKey,\n 'wrong-staff',\n 'This sale was rung while another member was signed in. It syncs when they sign in on this register.',\n true,\n )\n }\n if (sale.orgId && staff.orgId && sale.orgId !== staff.orgId) {\n return refused(sale.saleKey, 'wrong-workspace', 'This sale was rung in another workspace.', false)\n }\n\n const firestore = deps.firestore()\n const hostRef = staff.hostRef\n const recordRef = hostRef.collection('posOfflineSales').doc(sale.saleKey)\n const orderRef = hostRef.collection('orders').doc(sale.saleKey)\n\n const existing = await recordRef.get()\n if (existing.exists) {\n const record = existing.data() as PosOfflineSyncRecord\n if (record.signedInUid !== staff.uid) {\n return refused(sale.saleKey, 'conflict', 'Another sale already used this key.', false)\n }\n return replayOf(record.status === 'settled' ? record : await finishSale(context, recordRef, orderRef, record))\n }\n\n const now = deps.now()\n const flags = new Set<PosOfflineFlag>()\n const soldAt = posOfflineSoldAt(sale.soldAtMs, now)\n if (soldAt.adjusted) flags.add('clock-adjusted')\n else if (now - soldAt.atMs > POS_OFFLINE_LATE_SYNC_MS) flags.add('late-sync')\n\n // The lines as sold, checked against the catalog as it stands now. The\n // price on the order is the one rung — that is what the customer paid —\n // and the catalog's is kept beside it where they differ.\n const productIds = [...new Set(sale.lines.map((line) => line.productId))]\n const snapshots = await Promise.all(productIds.map((id) => hostRef.collection('products').doc(id).get()))\n const products = new Map(\n snapshots.map((snapshot) => [\n snapshot.id,\n snapshot.exists ? CommerceModel.liftLegacyProduct(snapshot.data() as any) : null,\n ]),\n )\n const priceDrift: NonNullable<PosOfflineOrderStamp['priceDrift']> = []\n const lineItems: CommerceModel.OrderLineItem[] = sale.lines.map((line, index) => {\n const product = products.get(line.productId)\n if (!product) {\n flags.add('unknown-product')\n return {\n productId: line.productId,\n ...(line.variantId ? { variantId: line.variantId } : {}),\n name: line.name,\n ...(line.variantLabel ? { variantLabel: line.variantLabel } : {}),\n ...(line.sku ? { sku: line.sku } : {}),\n productType: line.productType ?? 'physical',\n quantity: line.quantity,\n unitAmountCents: line.unitAmountCents,\n }\n }\n const variant = product.variants.find((item) => item.id === line.variantId) ?? product.variants[0]\n const chosen = CommerceModel.resolveLineModifiers(product, line.modifiers, variant?.id)\n const currentCents =\n variant && CommerceModel.variantHasPrice(variant) && chosen.ok\n ? Math.round(Number(variant.priceUsd) * 100) + chosen.extraCents\n : null\n if (currentCents !== line.unitAmountCents) {\n flags.add('price-changed')\n priceDrift.push({ index, rungCents: line.unitAmountCents, currentCents: currentCents ?? 0 })\n }\n return {\n productId: line.productId,\n ...(variant && variant.id !== 'default' ? { variantId: variant.id } : {}),\n name: product.name || line.name,\n ...(line.variantLabel ? { variantLabel: line.variantLabel } : {}),\n ...(variant?.sku ? { sku: variant.sku } : line.sku ? { sku: line.sku } : {}),\n productType: product.type ?? line.productType ?? 'physical',\n quantity: line.quantity,\n unitAmountCents: line.unitAmountCents,\n ...(chosen.ok && chosen.modifiers.length ? { modifiers: chosen.modifiers } : {}),\n }\n })\n\n // The money: what the register rang, when its own arithmetic holds; the\n // arithmetic of its lines, with the tax it rang, when it does not.\n let totals: CommerceModel.OrderTotals\n if (posOfflineTotalsAddUp(sale)) {\n totals = CommerceModel.computeOrderTotals(lineItems, {\n discountCents: sale.totals.discountCents,\n taxCents: sale.totals.taxCents,\n })\n } else {\n flags.add('totals-restated')\n const restated = posOfflineSaleTotals({\n lines: sale.lines,\n discountPct: sale.discountPct,\n tax: { pct: null, pricesIncludeTax: false },\n })\n totals = CommerceModel.computeOrderTotals(lineItems, {\n discountCents: restated.discountCents,\n taxCents: Math.min(sale.totals.taxCents, restated.itemsCents - restated.discountCents),\n })\n }\n if ('decision' in context.tax) {\n const expected = posOfflineSaleTotals({\n lines: sale.lines,\n discountPct: sale.discountPct,\n tax: { pct: context.tax.pct, pricesIncludeTax: context.tax.pricesIncludeTax },\n })\n if (expected.taxCents !== totals.taxCents) flags.add('tax-differs')\n } else if (totals.taxCents > 0) {\n flags.add('tax-differs')\n }\n if (sale.discountPct > posMaxDiscountPct(context.config)) flags.add('discount-over-limit')\n // The cashier's own discount needs `posDiscount` (AGL-4047), as the online\n // register refuses it without. Offline the sale has already happened, so it\n // is recorded and flagged rather than dropped. Read for the member who was\n // signed in, whose membership the gate resolved, as the online check reads it.\n if (sale.discountPct > 0 && !holdsPosPermission(staff.membership.permissions, 'posDiscount')) {\n flags.add('discount-not-permitted')\n }\n const byCard = sale.tender === 'card'\n if (!byCard && sale.cashTenderedCents < totals.totalCents) flags.add('cash-short')\n\n // Where it was rung.\n const register = context.registers.get(sale.registerId)\n if (!register) flags.add('unknown-register')\n else if (!deps.registerWithinCap(staff.org, staff.hostId, register.rank)) flags.add('register-over-cap')\n let shiftId = ''\n if (sale.shiftId && register) {\n const shift = await hostRef\n .collection('registers')\n .doc(sale.registerId)\n .collection('shifts')\n .doc(sale.shiftId)\n .get()\n if (shift.exists) {\n shiftId = sale.shiftId\n if (shift.get('status') !== 'open') flags.add('shift-closed')\n }\n }\n if (!shiftId && staff.settings.requireOpenShift) flags.add('no-shift')\n\n // Who rang it: a PIN-switched cashier's assertion is read as of the moment\n // the cash was taken (it was valid then, and expiring since is the normal\n // case for a sale that waited to sync), and the member is re-checked NOW.\n let cashierId = staff.uid\n if (sale.cashierAssertion) {\n const memberUid = await resolvePosStaffAssertion(\n { ...deps, now: () => soldAt.atMs },\n hostRef,\n sale.cashierAssertion,\n { hostId: staff.hostId, registerId: sale.registerId, purpose: 'cashier' },\n )\n if (memberUid) cashierId = memberUid\n else flags.add('cashier-unverified')\n }\n\n // Cash carries no platform fee (AGL-4036): the plan's take is worked out\n // and then settled by the one predicate every tender answers to, so a sale\n // synced from the queue is charged exactly what the same sale rung online\n // is.\n const planTake = takeFeeCents(deps, staff, lineItems, totals)\n // A card taken offline settles with Stripe BEFORE the order is written: a\n // capture that fails leaves the sale on the register for the next sync,\n // and a repeat after a lost answer finds the first capture (AGL-4039).\n const settled = byCard ? await settleOfflineCard(context, sale, planTake) : null\n if (settled?.kind === 'wait') return settled.outcome\n // The card's money, decided once against the server's totals: the fee it\n // settled, the shortfall to invoice, and the order-level intent the refund\n // and the dispute lookup read.\n const cardMoney = settled\n ? posOfflineCardOrderMoney({\n settled,\n totals,\n planTakeCents: planTake,\n soldAtMs: soldAt.atMs,\n nowMs: now,\n cashierId,\n feeOrgId: staff.orgId ?? '',\n })\n : null\n for (const flag of cardMoney?.flags ?? []) flags.add(flag)\n const cashTake = CommerceModel.posTenderTakeCents('cash', planTake)\n totals = cardMoney ? cardMoney.totals : { ...totals, feeCents: cashTake }\n // What accrues to the org's month with the order: the cash take (none\n // since AGL-4036) or the part of a card's fee its payout did not carry.\n const invoiceCents = cardMoney ? cardMoney.invoiceCents : cashTake\n const changeCents = byCard ? 0 : Math.max(0, sale.cashTenderedCents - totals.totalCents)\n const flagList = [...flags]\n const stamp: PosOfflineOrderStamp = {\n saleKey: sale.saleKey,\n soldAtMs: soldAt.atMs,\n syncedAtMs: now,\n syncedBy: staff.uid,\n flags: flagList,\n ...(priceDrift.length ? { priceDrift } : {}),\n }\n const customer = sale.customer ?? {}\n const feeMonth = offlineFeeMonthKey(new Date(now))\n const feeRef = staff.orgId\n ? firestore.collection('orgs').doc(staff.orgId).collection('offlineFees').doc(feeMonth)\n : null\n const counterRef = hostRef.collection('counters').doc('orders')\n\n const committed = await firestore.runTransaction(async (transaction) => {\n const raced = await transaction.get(recordRef)\n if (raced.exists) return { raced: raced.data() as PosOfflineSyncRecord }\n const taken = await transaction.get(orderRef)\n if (taken.exists) return { collision: true as const }\n const counter = await transaction.get(counterRef)\n const number = Number(counter.get('next') ?? 1)\n transaction.set(counterRef, { next: number + 1 }, { merge: true })\n const cardName = settled\n ? [settled.brand ? settled.brand.toUpperCase() : 'Card', settled.last4 ? `•••• ${settled.last4}` : ''].filter(Boolean).join(' ')\n : ''\n const paidDetail = !settled\n ? `Cash — received ${money(sale.cashTenderedCents)}, change ${money(changeCents)}. ` +\n 'Rung while the register was offline.'\n : settled.kind === 'captured'\n ? `${cardName} — ${money(settled.amountCents)}, taken while the register was offline and charged when it reconnected.`\n : `${cardName} — ${money(totals.totalCents)} taken while the register was offline was not paid: ${settled.message}`.slice(0, 500)\n const timeline: CommerceModel.OrderTimelineEvent[] = [\n { atMs: soldAt.atMs, event: settled?.kind === 'unpaid' ? 'payment-failed' : 'paid', detail: paidDetail },\n {\n atMs: now,\n event: 'offline-synced',\n detail: flagList.length\n ? `Synced. Check: ${posOfflineFlagLabels(flagList).join('; ')}`.slice(0, 1000)\n : 'Synced.',\n },\n ]\n transaction.create(\n orderRef,\n CommerceModel.withOrderListFields(orderRef.id, {\n number,\n status: cardMoney ? cardMoney.status : 'paid',\n channel: 'pos',\n registerId: sale.registerId,\n cashierId,\n ...(shiftId ? { shiftId } : {}),\n ...(customer.name ? { customerName: customer.name } : {}),\n ...(customer.kind && customer.id ? { customerRecord: { kind: customer.kind, id: customer.id } } : {}),\n ...(sale.discountPct > 0 ? { discountPct: sale.discountPct, discountBy: cashierId } : {}),\n ...(sale.locationId ? { locationId: sale.locationId } : {}),\n // A card reader sold offline on the platform's own store (AGL-4038):\n // recorded once synced, with nobody attached, for the To attach list.\n ...posHardwareSaleFields({ hostId: staff.hostId, orgId: staff.orgId, lineItems, products }),\n lineItems,\n totals,\n taxMode: 'decision' in context.tax\n ? CommerceModel.storefrontTaxModeForDecision(context.tax.decision, totals.taxCents)\n : totals.taxCents > 0\n ? 'manual'\n : 'none',\n ...(cardMoney ? cardMoney.fields : cashTake > 0 ? { feeCollection: 'invoice' as const } : {}),\n payments: cardMoney\n ? [cardMoney.payment]\n : [\n {\n id: 'pay_cash',\n method: 'cash',\n amountCents: totals.totalCents,\n status: 'succeeded',\n atMs: soldAt.atMs,\n settledAtMs: soldAt.atMs,\n takeFeeCents: cashTake,\n feeCents: 0,\n cashierId,\n cashTenderedCents: sale.cashTenderedCents,\n changeCents,\n } satisfies CommerceModel.OrderPayment,\n ],\n customerEmail: customer.email || null,\n timeline,\n offline: stamp,\n createdAtMs: soldAt.atMs,\n createdAt: FieldValue.serverTimestamp(),\n }),\n )\n const record: PosOfflineSyncRecord = {\n saleKey: sale.saleKey,\n orderId: orderRef.id,\n number,\n signedInUid: staff.uid,\n registerId: sale.registerId,\n soldAtMs: soldAt.atMs,\n syncedAtMs: now,\n status: 'recorded',\n flags: flagList,\n stockConflicts: [],\n stockLines: [],\n stockDone: false,\n notified: false,\n ...(settled?.kind === 'unpaid' ? { unpaid: true } : {}),\n }\n transaction.create(recordRef, record)\n // The intent's record says its sale synced, in the same commit, so the\n // webhook and the unsynced-card sweep never report a sale that landed.\n if (settled?.intentId) {\n transaction.set(\n posOfflineCardChargeRef(hostRef, sale.saleKey),\n {\n hostId: staff.hostId,\n saleKey: sale.saleKey,\n paymentIntentId: settled.intentId,\n registerId: sale.registerId,\n synced: true,\n orderId: orderRef.id,\n syncedAtMs: now,\n updatedAtMs: now,\n alertDueAtMs: FieldValue.delete(),\n },\n { merge: true },\n )\n }\n // The fee no payout carried accrues with the order, in the same\n // transaction (AGL-2111): a card's share was netted from its payout by\n // the capture, and only a shortfall in that lands here.\n if (invoiceCents > 0 && feeRef) {\n transaction.set(\n feeRef,\n { month: feeMonth, feeCents: FieldValue.increment(invoiceCents), orders: FieldValue.increment(1) },\n { merge: true },\n )\n }\n return { record }\n })\n if ('collision' in committed) {\n return refused(sale.saleKey, 'conflict', 'Another order already has this sale’s key.', false)\n }\n if ('raced' in committed) {\n const record = committed.raced\n if (record.signedInUid !== staff.uid) {\n return refused(sale.saleKey, 'conflict', 'Another sale already used this key.', false)\n }\n return replayOf(record)\n }\n if (settled?.kind === 'unpaid' && settled.wrongAccount && settled.intentId) {\n await deps.card!.wrongAccount({\n hostId: staff.hostId,\n orderId: orderRef.id,\n paymentIntentId: settled.intentId,\n ...settled.wrongAccount,\n })\n }\n const finished = await finishSale(context, recordRef, orderRef, committed.record)\n return {\n saleKey: sale.saleKey,\n status: 'recorded',\n orderId: orderRef.id,\n number: finished.number,\n flags: finished.flags,\n stockConflicts: finished.stockConflicts,\n }\n}\n\nexport async function handlePosOfflineSync(\n deps: PosOfflineSyncDeps,\n req: PluginApiRequest,\n): Promise<Outcome> {\n if (req.method !== 'GET' && req.method !== 'POST') {\n return { status: 405, body: { error: 'Method not allowed' } }\n }\n const body =\n req.method === 'GET'\n ? Object.fromEntries(\n Object.entries(req.query ?? {}).map(([key, value]) => [key, Array.isArray(value) ? value[0] : value]),\n )\n : posOpsBody(req)\n const gate = await authorizePosOps(deps, req, body['hostId'], 'posSell')\n if ('error' in gate) return { status: gate.status, body: { error: gate.error } }\n const staff = gate.staff\n if (req.method === 'GET') {\n return { status: 200, body: { kit: await buildPosOfflineKit(deps, staff) } }\n }\n const sales = Array.isArray(body['sales']) ? (body['sales'] as unknown[]) : null\n if (!sales) return { status: 400, body: { error: 'Missing sales' } }\n if (sales.length > POS_OFFLINE_SYNC_BATCH_MAX) {\n return { status: 400, body: { error: `Send at most ${POS_OFFLINE_SYNC_BATCH_MAX} sales at a time` } }\n }\n if (sales.length === 0) return { status: 200, body: { results: [] } }\n const [registers, taxSettings, config] = await Promise.all([\n readRegisters(staff),\n readStoreTax(staff),\n staff.orgId ? deps.pluginConfig(staff.orgId, staff.hostId).catch(() => ({})) : Promise.resolve({}),\n ])\n const context: SyncContext = { deps, staff, config, tax: posInPersonTax(taxSettings), registers }\n const results: PosOfflineSaleOutcome[] = []\n // One at a time, in the order they were rung, so order numbers follow the\n // till and one sale's failure cannot take the rest with it.\n for (const sale of sales) {\n try {\n results.push(await syncOne(context, sale))\n } catch (error) {\n console.error('[pos-offline-sync] sale failed', error)\n const key = String((sale as Record<string, unknown> | null)?.['saleKey'] ?? '').slice(0, 64)\n results.push(refused(key, 'failed', 'The sale could not be synced yet. It stays on this register.', true))\n }\n }\n return { status: 200, body: { results } }\n}\n\nexport function createPosOfflineSyncHandler(\n deps: () => PosOfflineSyncDeps = defaultPosOfflineSyncDeps,\n): PluginApiHandler {\n return async (req, res) => {\n try {\n const outcome = await handlePosOfflineSync(deps(), req)\n return res.status(outcome.status).json(outcome.body)\n } catch (error) {\n console.error('[pos-offline-sync] failed', error)\n return res.status(500).json({ error: 'The offline sales could not be synced' })\n }\n }\n}\n\nexport const posOfflineSyncHandler = createPosOfflineSyncHandler()\n"],"names":["Aglyn","recordCapturedContact","FieldValue","CommerceModel","posOfflineFlagLabels","posOfflineSaleTotals","posOfflineSoldAt","posOfflineTotalsAddUp","POS_OFFLINE_CARD_INTENT_KIND","POS_OFFLINE_LATE_SYNC_MS","POS_OFFLINE_SYNC_BATCH_MAX","sanitizePosOfflineSale","ORDER_PAID_EVENT","holdsPosPermission","posMaxDiscountPct","posOfflineCardSettings","alertLowStockCrossing","notifyOrderBuyer","raiseOrderEvent","offlineFeeMonthKey","authorizePosOps","defaultPosOpsDeps","posOpsBody","posOpsCleanId","resolvePosStaffAssertion","posOfflineCardChargeRef","reportPosOfflineCardWrongAccount","posHardwareSaleFields","posOfflineCardOrderMoney","notifyPosSaleCompleted","POS_CURRENCY","posCardApplicationFeeCents","posTerminalAvailable","capturePosOfflineCardIntent","ensurePosOfflineCardConfiguration","posMerchantAccount","posTerminalLocationId","retrievePosOfflineCardIntent","decrementVariantStock","defaultPosOfflineCardDeps","terminal","hostId","available","locationId","merchantAccount","enableOffline","retrieve","capture","wrongAccount","defaultPosOfflineSyncDeps","base","feePct","org","productType","resolveTransactionFeePct","registerWithinCap","rank","checkHostRegisterQuota","allowed","decrementStock","input","moved","firestore","hostRef","productId","variantId","quantity","ledger","reason","orderId","before","after","afterSale","settle","label","work","error","console","email","orgId","identity","name","surface","lifecycleFloor","purchaseCents","Number","order","totals","totalCents","interaction","source","refId","summary","toFixed","offline","key","card","money","cents","Math","max","posInPersonTax","settings","taxSettings","decision","storefrontTaxDecision","kind","ok","STOREFRONT_TAX_UNDECIDED_MESSAGE","misconfigured","storefrontTaxMisconfiguration","inPerson","rate","resolveTaxRate","origin","pct","pricesIncludeTax","readRegisters","staff","docs","collection","get","map","doc","id","data","createdAtMs","toMillis","sort","a","b","localeCompare","Map","readStoreTax","store","undefined","buildPosOfflineKit","deps","host","registers","config","Promise","all","pluginConfig","catch","resolve","tax","buildPosOfflineKitCard","v","issuedAtMs","now","unavailableReason","maxDiscountPct","requireOpenShift","entries","filter","register","String","slice","openShiftId","receipt","logo","receiptAddress","address","footer","returnPolicy","destination","currency","maxSaleCents","maxDeviceDailyCents","off","enabled","INTENT_HOLDS_MONEY","Set","POS_OFFLINE_CAPTURE_SETTLE_MS","posOfflineCaptureKey","saleKey","attempt","posOfflineCaptureRefused","status","settleOfflineCard","context","sale","takeFee","wait","outcome","refused","forward","details","brand","last4","paymentIntentId","flag","message","declineMessage","read","intent","metadata","found","has","expected","intentId","amountCents","amount","takeShareCents","posTenderTakeCents","expectedFeeCents","chargeCents","charge","chargeRef","round","set","captureAttempt","captureUncertainAttempt","delete","captureUncertainAtMs","merge","captured","stamp","updatedAtMs","lastCaptureStatus","lastCaptureError","feeCents","applicationFeeCents","livemode","failureMessage","retry","replayOf","record","number","flags","stockConflicts","takeFeeCents","lines","applies","gross","reduce","sum","line","unitAmountCents","charged","itemsCents","discountCents","scaled","finishSale","recordRef","orderRef","orderSnapshot","current","stockDone","conflicts","done","stockLines","lineItems","index","failed","short","applied","requested","push","variantLabel","Boolean","join","abs","shortUnits","add","update","every","_line","length","includes","fresh","timeline","appendOrderEvent","conflict","notified","unpaid","customerEmail","customerName","syncOne","raw","rawKey","signedInUid","uid","existing","exists","soldAt","soldAtMs","adjusted","atMs","productIds","snapshots","products","snapshot","liftLegacyProduct","priceDrift","product","sku","variant","variants","find","item","chosen","resolveLineModifiers","modifiers","currentCents","variantHasPrice","priceUsd","extraCents","rungCents","type","computeOrderTotals","taxCents","restated","discountPct","min","membership","permissions","byCard","tender","cashTenderedCents","registerId","shiftId","shift","cashierId","cashierAssertion","memberUid","purpose","planTake","settled","cardMoney","planTakeCents","nowMs","feeOrgId","cashTake","invoiceCents","changeCents","flagList","syncedAtMs","syncedBy","customer","feeMonth","Date","feeRef","counterRef","committed","runTransaction","transaction","counter","raced","taken","collision","next","cardName","toUpperCase","paidDetail","event","detail","create","withOrderListFields","channel","customerRecord","discountBy","taxMode","storefrontTaxModeForDecision","fields","feeCollection","payments","payment","method","settledAtMs","createdAt","serverTimestamp","synced","alertDueAtMs","month","increment","orders","finished","handlePosOfflineSync","req","body","Object","fromEntries","query","value","Array","isArray","gate","kit","sales","results","createPosOfflineSyncHandler","res","json","posOfflineSyncHandler"],"mappings":"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"}
@@ -302,7 +302,7 @@ import { posKioskPrincipal } from "./pos-kiosk-principal.js";
302
302
  const quantity = Math.max(1, Math.min(99, Math.round(Number((_raw_quantity = raw.quantity) != null ? _raw_quantity : 1))));
303
303
  // Modifiers are priced from the product, never from the register
304
304
  // (AGL-3607): the line names option ids and nothing else.
305
- const chosen = CommerceModel.resolveLineModifiers(product, raw.modifiers);
305
+ const chosen = CommerceModel.resolveLineModifiers(product, raw.modifiers, variant == null ? void 0 : variant.id);
306
306
  if (!chosen.ok) return res.status(400).json({
307
307
  error: chosen.error
308
308
  });