@aglyn/plugins-commerce 1.0.0-beta.277 → 1.0.0-beta.282
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +17 -14
- package/src/lib/components/commerce-console-page.js +33 -1
- package/src/lib/components/commerce-console-page.js.map +1 -1
- package/src/lib/components/console/payments-settings-card.component.js +1 -1
- package/src/lib/components/console/payments-settings-card.component.js.map +1 -1
- package/src/lib/components/console/pos/pos-hardware-api.d.ts +122 -0
- package/src/lib/components/console/pos/pos-hardware-api.js +90 -0
- package/src/lib/components/console/pos/pos-hardware-api.js.map +1 -0
- package/src/lib/components/console/pos/pos-reader-splash-api.d.ts +58 -0
- package/src/lib/components/console/pos/pos-reader-splash-api.js +101 -0
- package/src/lib/components/console/pos/pos-reader-splash-api.js.map +1 -0
- package/src/lib/components/console/pos-devices-card.component.js +5 -0
- package/src/lib/components/console/pos-devices-card.component.js.map +1 -1
- package/src/lib/components/console/pos-hardware-card.component.d.ts +26 -0
- package/src/lib/components/console/pos-hardware-card.component.js +456 -0
- package/src/lib/components/console/pos-hardware-card.component.js.map +1 -0
- package/src/lib/components/console/pos-hardware-orders-staff-page.component.d.ts +14 -0
- package/src/lib/components/console/pos-hardware-orders-staff-page.component.js +519 -0
- package/src/lib/components/console/pos-hardware-orders-staff-page.component.js.map +1 -0
- package/src/lib/components/console/pos-ops/pos-check-in-dialog.component.d.ts +27 -0
- package/src/lib/components/console/pos-ops/pos-check-in-dialog.component.js +444 -0
- package/src/lib/components/console/pos-ops/pos-check-in-dialog.component.js.map +1 -0
- package/src/lib/components/console/pos-ops/pos-check-in-zone.component.d.ts +9 -0
- package/src/lib/components/console/pos-ops/pos-check-in-zone.component.js +37 -0
- package/src/lib/components/console/pos-ops/pos-check-in-zone.component.js.map +1 -0
- package/src/lib/components/console/pos-ops/pos-check-ins-card.component.d.ts +24 -0
- package/src/lib/components/console/pos-ops/pos-check-ins-card.component.js +346 -0
- package/src/lib/components/console/pos-ops/pos-check-ins-card.component.js.map +1 -0
- package/src/lib/components/console/pos-ops/pos-customer-lookup.component.d.ts +5 -1
- package/src/lib/components/console/pos-ops/pos-customer-lookup.component.js +83 -4
- package/src/lib/components/console/pos-ops/pos-customer-lookup.component.js.map +1 -1
- package/src/lib/components/console/pos-ops/pos-day-totals-card.component.d.ts +35 -0
- package/src/lib/components/console/pos-ops/pos-day-totals-card.component.js +546 -0
- package/src/lib/components/console/pos-ops/pos-day-totals-card.component.js.map +1 -0
- package/src/lib/components/console/pos-ops/pos-operations-bar.component.d.ts +2 -1
- package/src/lib/components/console/pos-ops/pos-operations-bar.component.js +20 -1
- package/src/lib/components/console/pos-ops/pos-operations-bar.component.js.map +1 -1
- package/src/lib/components/console/pos-ops/pos-ops-api.d.ts +1 -1
- package/src/lib/components/console/pos-ops/pos-ops-api.js.map +1 -1
- package/src/lib/components/console/pos-page.component.js +6 -3
- package/src/lib/components/console/pos-page.component.js.map +1 -1
- package/src/lib/components/console/pos-reader-splash-setting.component.d.ts +22 -0
- package/src/lib/components/console/pos-reader-splash-setting.component.js +275 -0
- package/src/lib/components/console/pos-reader-splash-setting.component.js.map +1 -0
- package/src/lib/components/console/pos-reader-splash-staff-page.component.d.ts +14 -0
- package/src/lib/components/console/pos-reader-splash-staff-page.component.js +301 -0
- package/src/lib/components/console/pos-reader-splash-staff-page.component.js.map +1 -0
- package/src/lib/components/console/pos-zones.d.ts +37 -3
- package/src/lib/components/console/pos-zones.js +11 -1
- package/src/lib/components/console/pos-zones.js.map +1 -1
- package/src/lib/components/console/product-check-in-fields.component.d.ts +20 -0
- package/src/lib/components/console/product-check-in-fields.component.js +172 -0
- package/src/lib/components/console/product-check-in-fields.component.js.map +1 -0
- package/src/lib/components/console/product-editor-dialog.component.js +48 -9
- package/src/lib/components/console/product-editor-dialog.component.js.map +1 -1
- package/src/lib/components/console/product-register-fields.component.d.ts +12 -0
- package/src/lib/components/console/product-register-fields.component.js +39 -1
- package/src/lib/components/console/product-register-fields.component.js.map +1 -1
- package/src/lib/components/console/products-hub-card.component.js +21 -6
- package/src/lib/components/console/products-hub-card.component.js.map +1 -1
- package/src/lib/components/console/reservations-card.component.js +239 -70
- package/src/lib/components/console/reservations-card.component.js.map +1 -1
- package/src/lib/components/reservation-widget.js +16 -0
- package/src/lib/components/reservation-widget.js.map +1 -1
- package/src/lib/constants/operator-alerts.d.ts +6 -0
- package/src/lib/constants/operator-alerts.js +20 -1
- package/src/lib/constants/operator-alerts.js.map +1 -1
- package/src/lib/declarations.console-server.js +7 -0
- package/src/lib/declarations.console-server.js.map +1 -1
- package/src/lib/declarations.d.ts +26 -0
- package/src/lib/declarations.js +34 -0
- package/src/lib/declarations.js.map +1 -0
- package/src/lib/model/commerce-check-in-product.d.ts +37 -0
- package/src/lib/model/commerce-check-in-product.js +49 -0
- package/src/lib/model/commerce-check-in-product.js.map +1 -0
- package/src/lib/model/commerce-check-in.d.ts +187 -0
- package/src/lib/model/commerce-check-in.js +468 -0
- package/src/lib/model/commerce-check-in.js.map +1 -0
- package/src/lib/model/commerce-pos-day-report.d.ts +209 -0
- package/src/lib/model/commerce-pos-day-report.js +572 -0
- package/src/lib/model/commerce-pos-day-report.js.map +1 -0
- package/src/lib/model/commerce-pos-offline.d.ts +65 -3
- package/src/lib/model/commerce-pos-offline.js +104 -7
- package/src/lib/model/commerce-pos-offline.js.map +1 -1
- package/src/lib/model/commerce-pos-ops.d.ts +3 -0
- package/src/lib/model/commerce-pos-ops.js +1 -0
- package/src/lib/model/commerce-pos-ops.js.map +1 -1
- package/src/lib/model/commerce-pos.d.ts +43 -3
- package/src/lib/model/commerce-pos.js +73 -5
- package/src/lib/model/commerce-pos.js.map +1 -1
- package/src/lib/model/commerce-reservations.d.ts +73 -7
- package/src/lib/model/commerce-reservations.js +90 -10
- package/src/lib/model/commerce-reservations.js.map +1 -1
- package/src/lib/model/commerce.d.ts +40 -0
- package/src/lib/model/commerce.js +22 -4
- package/src/lib/model/commerce.js.map +1 -1
- package/src/lib/model/index.d.ts +1 -0
- package/src/lib/model/index.js +1 -0
- package/src/lib/model/index.js.map +1 -1
- package/src/lib/model/plugin-permissions.d.ts +1 -1
- package/src/lib/model/plugin-permissions.js +2 -1
- package/src/lib/model/plugin-permissions.js.map +1 -1
- package/src/lib/model/pos-hardware.d.ts +288 -0
- package/src/lib/model/pos-hardware.js +499 -0
- package/src/lib/model/pos-hardware.js.map +1 -0
- package/src/lib/model/pos-offline-card-limits.d.ts +20 -0
- package/src/lib/model/pos-offline-card-limits.js +28 -0
- package/src/lib/model/pos-offline-card-limits.js.map +1 -0
- package/src/lib/model/pos-reader-splash.d.ts +114 -0
- package/src/lib/model/pos-reader-splash.js +141 -0
- package/src/lib/model/pos-reader-splash.js.map +1 -0
- package/src/lib/model/product-write.d.ts +6 -0
- package/src/lib/plugin-config.d.ts +10 -0
- package/src/lib/plugin-config.js +40 -0
- package/src/lib/plugin-config.js.map +1 -1
- package/src/lib/plugin.js +28 -0
- package/src/lib/plugin.js.map +1 -1
- package/src/lib/server/cart-checkout.js +3 -1
- package/src/lib/server/cart-checkout.js.map +1 -1
- package/src/lib/server/cart.js +12 -1
- package/src/lib/server/cart.js.map +1 -1
- package/src/lib/server/catalog.js +49 -12
- package/src/lib/server/catalog.js.map +1 -1
- package/src/lib/server/checkout-extras.js +1 -1
- package/src/lib/server/checkout-extras.js.map +1 -1
- package/src/lib/server/checkout.js +7 -0
- package/src/lib/server/checkout.js.map +1 -1
- package/src/lib/server/person-eraser.d.ts +8 -1
- package/src/lib/server/person-eraser.js +23 -1
- package/src/lib/server/person-eraser.js.map +1 -1
- package/src/lib/server/pos-check-in.d.ts +49 -0
- package/src/lib/server/pos-check-in.js +652 -0
- package/src/lib/server/pos-check-in.js.map +1 -0
- package/src/lib/server/pos-day-report.d.ts +46 -0
- package/src/lib/server/pos-day-report.js +299 -0
- package/src/lib/server/pos-day-report.js.map +1 -0
- package/src/lib/server/pos-hardware-admin.d.ts +50 -0
- package/src/lib/server/pos-hardware-admin.js +366 -0
- package/src/lib/server/pos-hardware-admin.js.map +1 -0
- package/src/lib/server/pos-hardware-notify.d.ts +27 -0
- package/src/lib/server/pos-hardware-notify.js +142 -0
- package/src/lib/server/pos-hardware-notify.js.map +1 -0
- package/src/lib/server/pos-hardware-user-eraser.d.ts +26 -0
- package/src/lib/server/pos-hardware-user-eraser.js +67 -0
- package/src/lib/server/pos-hardware-user-eraser.js.map +1 -0
- package/src/lib/server/pos-hardware-webhook.d.ts +31 -0
- package/src/lib/server/pos-hardware-webhook.js +176 -0
- package/src/lib/server/pos-hardware-webhook.js.map +1 -0
- package/src/lib/server/pos-hardware.d.ts +58 -0
- package/src/lib/server/pos-hardware.js +234 -0
- package/src/lib/server/pos-hardware.js.map +1 -0
- package/src/lib/server/pos-offline-card-reconcile.d.ts +38 -0
- package/src/lib/server/pos-offline-card-reconcile.js +58 -0
- package/src/lib/server/pos-offline-card-reconcile.js.map +1 -0
- package/src/lib/server/pos-offline-sync.d.ts +32 -0
- package/src/lib/server/pos-offline-sync.js +191 -15
- package/src/lib/server/pos-offline-sync.js.map +1 -1
- package/src/lib/server/pos-ops-routes.d.ts +4 -2
- package/src/lib/server/pos-ops-routes.js +14 -2
- package/src/lib/server/pos-ops-routes.js.map +1 -1
- package/src/lib/server/pos-order.js +39 -51
- package/src/lib/server/pos-order.js.map +1 -1
- package/src/lib/server/pos-payment.js +8 -4
- package/src/lib/server/pos-payment.js.map +1 -1
- package/src/lib/server/pos-reader-splash-admin.d.ts +36 -0
- package/src/lib/server/pos-reader-splash-admin.js +295 -0
- package/src/lib/server/pos-reader-splash-admin.js.map +1 -0
- package/src/lib/server/pos-reader-splash-render.d.ts +40 -0
- package/src/lib/server/pos-reader-splash-render.js +138 -0
- package/src/lib/server/pos-reader-splash-render.js.map +1 -0
- package/src/lib/server/pos-reader-splash-server.d.ts +32 -0
- package/src/lib/server/pos-reader-splash-server.js +32 -0
- package/src/lib/server/pos-reader-splash-server.js.map +1 -0
- package/src/lib/server/pos-reader-splash.d.ts +105 -0
- package/src/lib/server/pos-reader-splash.js +548 -0
- package/src/lib/server/pos-reader-splash.js.map +1 -0
- package/src/lib/server/pos-sale.d.ts +2 -1
- package/src/lib/server/pos-sale.js +3 -1
- package/src/lib/server/pos-sale.js.map +1 -1
- package/src/lib/server/pos-terminal.d.ts +67 -4
- package/src/lib/server/pos-terminal.js +239 -32
- package/src/lib/server/pos-terminal.js.map +1 -1
- package/src/lib/server/process-restock.js +4 -0
- package/src/lib/server/process-restock.js.map +1 -1
- package/src/lib/server/product-card.js +2 -2
- package/src/lib/server/product-card.js.map +1 -1
- package/src/lib/server/product-catalog.d.ts +2 -0
- package/src/lib/server/product-catalog.js +11 -4
- package/src/lib/server/product-catalog.js.map +1 -1
- package/src/lib/server/product.js +3 -1
- package/src/lib/server/product.js.map +1 -1
- package/src/lib/server/related.js +8 -1
- package/src/lib/server/related.js.map +1 -1
- package/src/lib/server/reservation-availability-query.d.ts +35 -0
- package/src/lib/server/reservation-availability-query.js +34 -0
- package/src/lib/server/reservation-availability-query.js.map +1 -0
- package/src/lib/server/reservation-availability.d.ts +21 -0
- package/src/lib/server/reservation-availability.js +64 -10
- package/src/lib/server/reservation-availability.js.map +1 -1
- package/src/lib/server/reservation-walk-in.d.ts +28 -0
- package/src/lib/server/reservation-walk-in.js +166 -0
- package/src/lib/server/reservation-walk-in.js.map +1 -0
- package/src/lib/server/reserve.js +13 -10
- package/src/lib/server/reserve.js.map +1 -1
- package/src/lib/server/reviews.js +3 -1
- package/src/lib/server/reviews.js.map +1 -1
- package/src/lib/server/site-page-resolver.js +5 -2
- package/src/lib/server/site-page-resolver.js.map +1 -1
- package/src/lib/server.js +26 -0
- package/src/lib/server.js.map +1 -1
- package/src/lib/testing/fake-firestore.js +72 -0
- package/src/lib/testing/fake-firestore.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_LATE_SYNC_MS,\n POS_OFFLINE_SYNC_BATCH_MAX,\n sanitizePosOfflineSale,\n type PosOfflineFlag,\n type PosOfflineKit,\n type PosOfflineOrderStamp,\n type PosOfflineRefusal,\n type PosOfflineSaleOutcome,\n type PosOfflineStockConflict,\n} from '../model/commerce-pos-offline'\nimport { ORDER_PAID_EVENT } from '../model/order-events'\nimport { posMaxDiscountPct } 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 { notifyPosSaleCompleted } from './pos-sale'\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 cash sale rung offline, once\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}\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}\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 }\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 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 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\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) {\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 if (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 const takeFee = takeFeeCents(deps, staff, lineItems, totals)\n totals = { ...totals, feeCents: takeFee }\n const changeCents = 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 paidDetail =\n `Cash — received ${money(sale.cashTenderedCents)}, change ${money(changeCents)}. ` +\n 'Rung while the register was offline.'\n const timeline: CommerceModel.OrderTimelineEvent[] = [\n { atMs: soldAt.atMs, event: '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: '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 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 ...(takeFee > 0 ? { feeCollection: 'invoice' as const } : {}),\n payments: [\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: takeFee,\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 }\n transaction.create(recordRef, record)\n // The take accrues with the order, in the same transaction (AGL-2111).\n if (takeFee > 0 && feeRef) {\n transaction.set(\n feeRef,\n { month: feeMonth, feeCents: FieldValue.increment(takeFee), 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 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'])\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_LATE_SYNC_MS","POS_OFFLINE_SYNC_BATCH_MAX","sanitizePosOfflineSale","ORDER_PAID_EVENT","posMaxDiscountPct","alertLowStockCrossing","notifyOrderBuyer","raiseOrderEvent","offlineFeeMonthKey","authorizePosOps","defaultPosOpsDeps","posOpsBody","posOpsCleanId","resolvePosStaffAssertion","notifyPosSaleCompleted","decrementVariantStock","defaultPosOfflineSyncDeps","base","feePct","org","productType","resolveTransactionFeePct","registerWithinCap","hostId","rank","checkHostRegisterQuota","allowed","decrementStock","input","moved","firestore","hostRef","productId","variantId","quantity","locationId","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","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","v","issuedAtMs","now","available","unavailableReason","maxDiscountPct","requireOpenShift","entries","filter","register","String","slice","openShiftId","receipt","logo","receiptAddress","address","footer","returnPolicy","refused","saleKey","retry","status","replayOf","record","number","flags","stockConflicts","takeFeeCents","lines","applies","gross","reduce","sum","line","round","unitAmountCents","charged","itemsCents","discountCents","scaled","finishSale","context","recordRef","orderRef","orderSnapshot","current","stockDone","conflicts","done","Set","stockLines","lineItems","index","has","failed","short","applied","requested","push","variantLabel","Boolean","join","abs","shortUnits","add","update","every","_line","length","includes","fresh","stamp","timeline","appendOrderEvent","conflict","notified","customerEmail","customerName","syncOne","raw","sale","read","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","expected","cashTenderedCents","registerId","shiftId","shift","cashierId","cashierAssertion","memberUid","purpose","takeFee","feeCents","changeCents","flagList","syncedAtMs","syncedBy","customer","feeMonth","Date","feeRef","counterRef","committed","runTransaction","transaction","counter","raced","taken","collision","set","next","merge","paidDetail","event","detail","create","withOrderListFields","channel","customerRecord","discountBy","taxMode","storefrontTaxModeForDecision","feeCollection","payments","method","amountCents","settledAtMs","createdAt","serverTimestamp","month","increment","orders","finished","handlePosOfflineSync","req","body","Object","fromEntries","query","value","Array","isArray","gate","kit","sales","results","createPosOfflineSyncHandler","res","outcome","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 { 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 { 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 capture(\n paymentIntentId: string,\n applicationFeeCents: number,\n idempotencyKey: string,\n ): Promise<{ ok: true; intent: PosOfflineCardIntent } | { ok: false; error: string }>\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 }\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/** What became of a card taken offline, as the sync settles it. */\ntype OfflineCardSettlement =\n | {\n kind: 'captured'\n intentId: string\n feeCents: number\n takeShareCents: number\n livemode: boolean\n brand?: string\n last4?: string\n }\n | { kind: 'unpaid'; flag: 'card-declined' | 'card-not-charged'; intentId?: string; message: string; brand?: string; last4?: string }\n | { kind: 'wait'; outcome: PosOfflineSaleOutcome }\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 or Stripe never took it. Anything Stripe\n * has not 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 if (\n intent.metadata['kind'] !== POS_OFFLINE_CARD_INTENT_KIND ||\n intent.metadata['saleKey'] !== saleKey ||\n intent.metadata['hostId'] !== staff.hostId ||\n intent.amount !== sale.totals.totalCents ||\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 const takeShareCents = CommerceModel.posTenderTakeCents('card_present', takeFee)\n if (intent.status === 'requires_capture') {\n const feeCents = posCardApplicationFeeCents({ takeShareCents, chargeCents: intent.amount })\n const captured = await deps.card.capture(intent.id, feeCents, `pos-offline-capture:${staff.hostId}:${saleKey}`)\n if ('error' in captured) return wait(`The card payment could not be captured yet: ${captured.error}`)\n intent = captured.intent\n }\n if (intent.status === 'succeeded') {\n return {\n kind: 'captured',\n intentId: intent.id,\n feeCents: intent.applicationFeeCents,\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 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 // The offline register takes cash only, and cash carries no platform fee\n // (AGL-4036): the plan's take is worked out and then settled by the one\n // predicate every tender answers to, so a sale synced from the queue is\n // charged exactly what the same sale rung online 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 if (settled?.kind === 'unpaid') flags.add(settled.flag)\n const takeFee = byCard ? (settled?.kind === 'captured' ? settled.takeShareCents : 0) : CommerceModel.posTenderTakeCents('cash', planTake)\n totals = { ...totals, feeCents: takeFee }\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(totals.totalCents)}, 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: settled?.kind === 'unpaid' ? 'pending' : '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 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 ...(takeFee > 0 ? { feeCollection: settled ? ('payout' as const) : ('invoice' as const) } : {}),\n payments: settled\n ? [\n {\n id: 'pay_card_offline',\n method: 'card_present',\n amountCents: totals.totalCents,\n status: settled.kind === 'captured' ? 'succeeded' : 'failed',\n atMs: soldAt.atMs,\n ...(settled.kind === 'captured'\n ? { settledAtMs: now, takeFeeCents: settled.takeShareCents, feeCents: settled.feeCents, livemode: settled.livemode }\n : { takeFeeCents: 0, feeCents: 0, failureMessage: settled.message.slice(0, 300) }),\n ...(settled.intentId ? { paymentIntentId: settled.intentId } : {}),\n ...(settled.brand ? { cardBrand: settled.brand } : {}),\n ...(settled.last4 ? { last4: settled.last4 } : {}),\n cashierId,\n } satisfies CommerceModel.OrderPayment,\n ]\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: takeFee,\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 take accrues with the order, in the same transaction (AGL-2111).\n // A card's share was netted from its payout by the capture instead.\n if (takeFee > 0 && feeRef && !settled) {\n transaction.set(\n feeRef,\n { month: feeMonth, feeCents: FieldValue.increment(takeFee), 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 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'])\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","posMaxDiscountPct","posOfflineCardSettings","alertLowStockCrossing","notifyOrderBuyer","raiseOrderEvent","offlineFeeMonthKey","authorizePosOps","defaultPosOpsDeps","posOpsBody","posOpsCleanId","resolvePosStaffAssertion","notifyPosSaleCompleted","POS_CURRENCY","posCardApplicationFeeCents","posTerminalAvailable","capturePosOfflineCardIntent","ensurePosOfflineCardConfiguration","posMerchantAccount","posTerminalLocationId","retrievePosOfflineCardIntent","decrementVariantStock","defaultPosOfflineCardDeps","terminal","hostId","available","locationId","merchantAccount","enableOffline","retrieve","capture","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","settleOfflineCard","context","sale","takeFee","saleKey","wait","outcome","refused","forward","details","brand","last4","paymentIntentId","flag","message","declineMessage","read","intent","metadata","amount","found","takeShareCents","posTenderTakeCents","status","feeCents","chargeCents","captured","intentId","applicationFeeCents","livemode","failureMessage","retry","replayOf","record","number","flags","stockConflicts","takeFeeCents","lines","applies","gross","reduce","sum","line","round","unitAmountCents","charged","itemsCents","discountCents","scaled","finishSale","recordRef","orderRef","orderSnapshot","current","stockDone","conflicts","done","Set","stockLines","lineItems","index","has","failed","short","applied","requested","push","variantLabel","Boolean","join","abs","shortUnits","add","update","every","_line","length","includes","fresh","stamp","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","expected","byCard","tender","cashTenderedCents","registerId","shiftId","shift","cashierId","cashierAssertion","memberUid","purpose","planTake","settled","changeCents","flagList","syncedAtMs","syncedBy","customer","feeMonth","Date","feeRef","counterRef","committed","runTransaction","transaction","counter","raced","taken","collision","set","next","merge","cardName","toUpperCase","paidDetail","event","detail","create","withOrderListFields","channel","customerRecord","discountBy","taxMode","storefrontTaxModeForDecision","feeCollection","payments","method","amountCents","settledAtMs","cardBrand","createdAt","serverTimestamp","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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"}
|
|
@@ -16,7 +16,9 @@
|
|
|
16
16
|
*/
|
|
17
17
|
/**
|
|
18
18
|
* The register's operations routes (AGL-3609): shifts and the drawer, staff
|
|
19
|
-
* PINs, the customer lookup and returns,
|
|
20
|
-
*
|
|
19
|
+
* PINs, the customer lookup and returns, the day totals (AGL-4037), the
|
|
20
|
+
* check-in (AGL-4043), plus the
|
|
21
|
+
* sale printing. Console-auth, so they register on the console surface with
|
|
22
|
+
* the sale route itself.
|
|
21
23
|
*/
|
|
22
24
|
export declare function registerPosOpsRoutes(): void;
|
|
@@ -14,20 +14,32 @@
|
|
|
14
14
|
* See the License for the specific language governing permissions and
|
|
15
15
|
* limitations under the License.
|
|
16
16
|
*/ import { registerPluginApiRoute } from "@aglyn/aglyn/server";
|
|
17
|
+
import { posCheckInHandler, registerPosSaleCheckIns } from "./pos-check-in.js";
|
|
17
18
|
import { posCustomerHandler } from "./pos-customer.js";
|
|
19
|
+
import { posDayReportHandler } from "./pos-day-report.js";
|
|
18
20
|
import { registerPosSalePrinting } from "./pos-print.js";
|
|
19
21
|
import { posReturnHandler } from "./pos-return.js";
|
|
20
22
|
import { posShiftHandler } from "./pos-shift.js";
|
|
21
23
|
import { posStaffPinHandler } from "./pos-staff-pin.js";
|
|
22
24
|
/**
|
|
23
25
|
* The register's operations routes (AGL-3609): shifts and the drawer, staff
|
|
24
|
-
* PINs, the customer lookup and returns,
|
|
25
|
-
*
|
|
26
|
+
* PINs, the customer lookup and returns, the day totals (AGL-4037), the
|
|
27
|
+
* check-in (AGL-4043), plus the
|
|
28
|
+
* sale printing. Console-auth, so they register on the console surface with
|
|
29
|
+
* the sale route itself.
|
|
26
30
|
*/ export function registerPosOpsRoutes() {
|
|
27
31
|
registerPluginApiRoute('commerce/pos-shift', posShiftHandler);
|
|
28
32
|
registerPluginApiRoute('commerce/pos-staff-pin', posStaffPinHandler);
|
|
29
33
|
registerPluginApiRoute('commerce/pos-customer', posCustomerHandler);
|
|
30
34
|
registerPluginApiRoute('commerce/pos-return', posReturnHandler);
|
|
35
|
+
// One day's or one event's totals across every register, shift and cashier
|
|
36
|
+
// (AGL-4037): a GET, read with the same gate as the shift reports.
|
|
37
|
+
registerPluginApiRoute('commerce/pos-day-report', posDayReportHandler);
|
|
38
|
+
// Check-in at the window (AGL-4043): who is due today, checking them in
|
|
39
|
+
// with a wristband, and the day's list; a register sale's check-ins are
|
|
40
|
+
// written the moment it completes, through the same seam as its printing.
|
|
41
|
+
registerPluginApiRoute('commerce/pos-check-in', posCheckInHandler);
|
|
42
|
+
registerPosSaleCheckIns();
|
|
31
43
|
// A completed sale's receipt and drawer kick on the register's cloud
|
|
32
44
|
// printers (AGL-3619), through the sale-completed seam every path fires.
|
|
33
45
|
registerPosSalePrinting();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/commerce/src/lib/server/pos-ops-routes.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 { registerPluginApiRoute } from '@aglyn/aglyn/server'\nimport { posCustomerHandler } from './pos-customer'\nimport { registerPosSalePrinting } from './pos-print'\nimport { posReturnHandler } from './pos-return'\nimport { posShiftHandler } from './pos-shift'\nimport { posStaffPinHandler } from './pos-staff-pin'\n\n/**\n * The register's operations routes (AGL-3609): shifts and the drawer, staff\n * PINs, the customer lookup and returns, plus the sale printing. Console-auth
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/commerce/src/lib/server/pos-ops-routes.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 { registerPluginApiRoute } from '@aglyn/aglyn/server'\nimport { posCheckInHandler, registerPosSaleCheckIns } from './pos-check-in'\nimport { posCustomerHandler } from './pos-customer'\nimport { posDayReportHandler } from './pos-day-report'\nimport { registerPosSalePrinting } from './pos-print'\nimport { posReturnHandler } from './pos-return'\nimport { posShiftHandler } from './pos-shift'\nimport { posStaffPinHandler } from './pos-staff-pin'\n\n/**\n * The register's operations routes (AGL-3609): shifts and the drawer, staff\n * PINs, the customer lookup and returns, the day totals (AGL-4037), the\n * check-in (AGL-4043), plus the\n * sale printing. Console-auth, so they register on the console surface with\n * the sale route itself.\n */\nexport function registerPosOpsRoutes(): void {\n registerPluginApiRoute('commerce/pos-shift', posShiftHandler)\n registerPluginApiRoute('commerce/pos-staff-pin', posStaffPinHandler)\n registerPluginApiRoute('commerce/pos-customer', posCustomerHandler)\n registerPluginApiRoute('commerce/pos-return', posReturnHandler)\n // One day's or one event's totals across every register, shift and cashier\n // (AGL-4037): a GET, read with the same gate as the shift reports.\n registerPluginApiRoute('commerce/pos-day-report', posDayReportHandler)\n // Check-in at the window (AGL-4043): who is due today, checking them in\n // with a wristband, and the day's list; a register sale's check-ins are\n // written the moment it completes, through the same seam as its printing.\n registerPluginApiRoute('commerce/pos-check-in', posCheckInHandler)\n registerPosSaleCheckIns()\n // A completed sale's receipt and drawer kick on the register's cloud\n // printers (AGL-3619), through the sale-completed seam every path fires.\n registerPosSalePrinting()\n}\n"],"names":["registerPluginApiRoute","posCheckInHandler","registerPosSaleCheckIns","posCustomerHandler","posDayReportHandler","registerPosSalePrinting","posReturnHandler","posShiftHandler","posStaffPinHandler","registerPosOpsRoutes"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED,SAASA,sBAAsB,QAAQ,sBAAqB;AAC5D,SAASC,iBAAiB,EAAEC,uBAAuB,QAAQ,oBAAgB;AAC3E,SAASC,kBAAkB,QAAQ,oBAAgB;AACnD,SAASC,mBAAmB,QAAQ,sBAAkB;AACtD,SAASC,uBAAuB,QAAQ,iBAAa;AACrD,SAASC,gBAAgB,QAAQ,kBAAc;AAC/C,SAASC,eAAe,QAAQ,iBAAa;AAC7C,SAASC,kBAAkB,QAAQ,qBAAiB;AAEpD;;;;;;CAMC,GACD,OAAO,SAASC;IACdT,uBAAuB,sBAAsBO;IAC7CP,uBAAuB,0BAA0BQ;IACjDR,uBAAuB,yBAAyBG;IAChDH,uBAAuB,uBAAuBM;IAC9C,2EAA2E;IAC3E,mEAAmE;IACnEN,uBAAuB,2BAA2BI;IAClD,wEAAwE;IACxE,wEAAwE;IACxE,0EAA0E;IAC1EJ,uBAAuB,yBAAyBC;IAChDC;IACA,qEAAqE;IACrE,yEAAyE;IACzEG;AACF"}
|
|
@@ -556,64 +556,52 @@ import { posKioskPrincipal } from "./pos-kiosk-principal.js";
|
|
|
556
556
|
taxEngine: engineTax.stamp
|
|
557
557
|
} : {};
|
|
558
558
|
/*==========================================
|
|
559
|
-
*
|
|
559
|
+
* WHICH TENDERS OWE THE TAKE (AGL-2111, AGL-4036).
|
|
560
560
|
*
|
|
561
|
-
*
|
|
562
|
-
*
|
|
563
|
-
*
|
|
564
|
-
*
|
|
565
|
-
*
|
|
566
|
-
*
|
|
567
|
-
* that zeroes the fee.
|
|
561
|
+
* AGL-2111 made the take follow the SALE: cash and room charges never
|
|
562
|
+
* touch Stripe, so they had been recording no fee at all, which left a
|
|
563
|
+
* merchant one tap away from paying nothing by ringing every sale as
|
|
564
|
+
* cash. It closed that by accruing their fee to the org's month
|
|
565
|
+
* (`offlineFees` below), swept onto the org's own Aglyn invoice by
|
|
566
|
+
* `report-usage`.
|
|
568
567
|
*
|
|
569
|
-
*
|
|
570
|
-
*
|
|
568
|
+
* AGL-4036 narrows it, by tender, through one predicate
|
|
569
|
+
* (`CommerceModel.paymentCarriesPlatformFee`):
|
|
571
570
|
*
|
|
572
|
-
*
|
|
573
|
-
*
|
|
574
|
-
*
|
|
575
|
-
*
|
|
576
|
-
*
|
|
577
|
-
*
|
|
578
|
-
*
|
|
571
|
+
* - CASH carries no fee. In-person rivals charge nothing on cash, and an
|
|
572
|
+
* event till that invoices its operator for the cash they took is not
|
|
573
|
+
* a till they will pick. A merchant steering sales to cash is no
|
|
574
|
+
* longer an avoidance route, because cash is meant to be free.
|
|
575
|
+
* - GIFT CARD carries no fee: the take was charged when the card was
|
|
576
|
+
* sold, and charging it again when it is spent charges twice. A gift
|
|
577
|
+
* card is only reachable on an open sale (`pos-payment.ts`).
|
|
578
|
+
* - A ROOM CHARGE (`folio`) keeps the take, accrued to the invoice: it is
|
|
579
|
+
* new revenue booked to a stay, with no payout to net it from.
|
|
580
|
+
* - A CARD keeps the take, netted from its payout as always.
|
|
579
581
|
*
|
|
580
|
-
*
|
|
581
|
-
*
|
|
582
|
-
*
|
|
583
|
-
* basis on a cash sale is LOWER (no Stripe fee at all), so the same
|
|
584
|
-
* percentage is more profitable there, not less justified.
|
|
585
|
-
*
|
|
586
|
-
* And any gap between tenders is itself the defect: a cheaper cash rate
|
|
587
|
-
* would still pay a merchant to steer customers to cash. Parity is the
|
|
588
|
-
* only rate at which the tender stops being a pricing lever.
|
|
589
|
-
*
|
|
590
|
-
* ## What changes is COLLECTION, not the rate
|
|
591
|
-
*
|
|
592
|
-
* There is still no charge to net it from, so the fee is ACCRUED against
|
|
593
|
-
* the org's month (`offlineFees` below) and swept onto the
|
|
594
|
-
* org's own monthly Aglyn invoice by `report-usage` — option 2 on
|
|
595
|
-
* AGL-2111, the `aglyn_metered_usage` meter that already carries every
|
|
596
|
-
* other overage. `feeCollection` on the order says which of the two routes
|
|
597
|
-
* this sale's fee took, so a merchant reconciling a payout can tell "short
|
|
598
|
-
* by the fee" from "billed for it next month".
|
|
582
|
+
* `feeCollection` on the order says which of the two routes a sale's fee
|
|
583
|
+
* took, so a merchant reconciling a payout can tell "short by the fee"
|
|
584
|
+
* from "billed for it next month". A sale whose fee is 0 records none.
|
|
599
585
|
*
|
|
600
586
|
* `feeCents` does not enter `totalCents` (see `computeOrderTotals`), so
|
|
601
|
-
* the shopper pays exactly
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
//
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
//
|
|
608
|
-
//
|
|
609
|
-
//
|
|
610
|
-
//
|
|
587
|
+
* the shopper pays exactly the same on every tender.
|
|
588
|
+
*=========================================*/ // The take THIS tender owes when it pays the whole sale. An open sale
|
|
589
|
+
// keeps the whole take on `posTakeFeeCents` and settles what its tenders
|
|
590
|
+
// owe when the balance reaches zero (`posCompletionPatch`).
|
|
591
|
+
const singleTenderMethod = payment === 'cash' ? 'cash' : payment === 'folio' ? 'folio' : payment === 'link' ? 'card_link' : null;
|
|
592
|
+
const tenderTakeFeeCents = singleTenderMethod ? CommerceModel.posTenderTakeCents(singleTenderMethod, takeFeeCents) : takeFeeCents;
|
|
593
|
+
// STRIPE'S CARD COST, ON THE CARD TENDER ONLY (AGL-2152). The take above
|
|
594
|
+
// is the platform's advertised cut. This term is different in kind: it is
|
|
595
|
+
// not a rate, it is the 2.9% + 30¢ Stripe debits from the PLATFORM's
|
|
596
|
+
// balance when a destination charge settles. Cash and folio never reach
|
|
597
|
+
// Stripe, so there is no such cost to pass through and adding one would
|
|
598
|
+
// invoice a merchant for a fee nobody paid.
|
|
611
599
|
//
|
|
612
600
|
// The base is exactly what the card runs for: the goods after the
|
|
613
601
|
// cashier's discount, plus the origin tax. A till knows both — there is no
|
|
614
602
|
// shopper-chosen shipping and no Stripe Tax to wait on — so unlike the
|
|
615
603
|
// online doors this one recovers the cost exactly.
|
|
616
|
-
const feeCents = payment === 'link' ?
|
|
604
|
+
const feeCents = payment === 'link' ? tenderTakeFeeCents + Aglyn.saleProcessingCostCents(chargedItemsCents + taxCents) : tenderTakeFeeCents;
|
|
617
605
|
const totals = CommerceModel.computeOrderTotals(lineItems, {
|
|
618
606
|
discountCents,
|
|
619
607
|
taxCents,
|
|
@@ -868,9 +856,9 @@ import { posKioskPrincipal } from "./pos-kiosk-principal.js";
|
|
|
868
856
|
} : {}, {
|
|
869
857
|
lineItems,
|
|
870
858
|
// `feeCents` is what the merchant's payout is short by, and it
|
|
871
|
-
// belongs on the document that records the charge.
|
|
872
|
-
//
|
|
873
|
-
//
|
|
859
|
+
// belongs on the document that records the charge. The card is the
|
|
860
|
+
// tender whose fee is netted from the payout rather than invoiced,
|
|
861
|
+
// and `feeCollection` says so.
|
|
874
862
|
totals: cardTotals,
|
|
875
863
|
// WHICH TAX THIS SALE CARRIED (AGL-2451), from the decision resolved
|
|
876
864
|
// above. The register never reaches `stripe-automatic` — AGL-2145
|
|
@@ -1072,7 +1060,7 @@ import { posKioskPrincipal } from "./pos-kiosk-principal.js";
|
|
|
1072
1060
|
status: 'succeeded',
|
|
1073
1061
|
atMs: paidEvent.atMs,
|
|
1074
1062
|
settledAtMs: paidEvent.atMs,
|
|
1075
|
-
takeFeeCents,
|
|
1063
|
+
takeFeeCents: tenderTakeFeeCents,
|
|
1076
1064
|
feeCents: 0,
|
|
1077
1065
|
cashierId: saleStamp.cashierId
|
|
1078
1066
|
}, payment === 'cash' ? {
|