@aglyn/plugins-commerce 1.0.0-beta.233 → 1.0.0-beta.235

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (135) hide show
  1. package/package.json +13 -13
  2. package/src/lib/components/cart.js +3 -2
  3. package/src/lib/components/cart.js.map +1 -1
  4. package/src/lib/components/console/commerce-analytics-card.component.js +3 -6
  5. package/src/lib/components/console/commerce-analytics-card.component.js.map +1 -1
  6. package/src/lib/components/console/commerce-glance-card.component.js +9 -3
  7. package/src/lib/components/console/commerce-glance-card.component.js.map +1 -1
  8. package/src/lib/components/console/customer-notifications-card.component.js +3 -1
  9. package/src/lib/components/console/customer-notifications-card.component.js.map +1 -1
  10. package/src/lib/components/console/discounts-card.component.js +2 -3
  11. package/src/lib/components/console/discounts-card.component.js.map +1 -1
  12. package/src/lib/components/console/host-coupons-card.component.js +3 -4
  13. package/src/lib/components/console/host-coupons-card.component.js.map +1 -1
  14. package/src/lib/components/console/local-delivery-settings-card.component.js +2 -2
  15. package/src/lib/components/console/local-delivery-settings-card.component.js.map +1 -1
  16. package/src/lib/components/console/locations-card.component.js +1 -1
  17. package/src/lib/components/console/locations-card.component.js.map +1 -1
  18. package/src/lib/components/console/member-posts-card.component.js +2 -2
  19. package/src/lib/components/console/member-posts-card.component.js.map +1 -1
  20. package/src/lib/components/console/order-detail-dialog.component.js +35 -30
  21. package/src/lib/components/console/order-detail-dialog.component.js.map +1 -1
  22. package/src/lib/components/console/order-fulfillment-panel.component.d.ts +11 -1
  23. package/src/lib/components/console/order-fulfillment-panel.component.js +75 -4
  24. package/src/lib/components/console/order-fulfillment-panel.component.js.map +1 -1
  25. package/src/lib/components/console/order-zones.d.ts +45 -1
  26. package/src/lib/components/console/order-zones.js +8 -0
  27. package/src/lib/components/console/order-zones.js.map +1 -1
  28. package/src/lib/components/console/pickup-delivery-queue-card.component.js +15 -1
  29. package/src/lib/components/console/pickup-delivery-queue-card.component.js.map +1 -1
  30. package/src/lib/components/console/pos-page.component.js +3 -2
  31. package/src/lib/components/console/pos-page.component.js.map +1 -1
  32. package/src/lib/components/console/product-editor-dialog.component.d.ts +2 -0
  33. package/src/lib/components/console/product-editor-dialog.component.js +30 -12
  34. package/src/lib/components/console/product-editor-dialog.component.js.map +1 -1
  35. package/src/lib/components/console/products-hub-card.component.js +13 -4
  36. package/src/lib/components/console/products-hub-card.component.js.map +1 -1
  37. package/src/lib/components/console/products-hub-zone.component.d.ts +2 -1
  38. package/src/lib/components/console/products-hub-zone.component.js +1 -1
  39. package/src/lib/components/console/products-hub-zone.component.js.map +1 -1
  40. package/src/lib/components/console/reviews-moderation-card.component.js +1 -2
  41. package/src/lib/components/console/reviews-moderation-card.component.js.map +1 -1
  42. package/src/lib/components/console/shipping-settings-card.component.js +2 -2
  43. package/src/lib/components/console/shipping-settings-card.component.js.map +1 -1
  44. package/src/lib/components/console/shippingeasy-card.component.js +1 -1
  45. package/src/lib/components/console/shippingeasy-card.component.js.map +1 -1
  46. package/src/lib/components/console/shipstation-card.component.js +1 -2
  47. package/src/lib/components/console/shipstation-card.component.js.map +1 -1
  48. package/src/lib/components/console/store-settings-card.component.js +3 -1
  49. package/src/lib/components/console/store-settings-card.component.js.map +1 -1
  50. package/src/lib/components/console/tax-settings-card.component.js +4 -1
  51. package/src/lib/components/console/tax-settings-card.component.js.map +1 -1
  52. package/src/lib/components/order-status.js +24 -1
  53. package/src/lib/components/order-status.js.map +1 -1
  54. package/src/lib/components/product-detail.js +30 -16
  55. package/src/lib/components/product-detail.js.map +1 -1
  56. package/src/lib/components/product-grid.d.ts +7 -0
  57. package/src/lib/components/product-grid.js +50 -7
  58. package/src/lib/components/product-grid.js.map +1 -1
  59. package/src/lib/components/related-products.js +1 -1
  60. package/src/lib/components/related-products.js.map +1 -1
  61. package/src/lib/components/wishlist.js +1 -1
  62. package/src/lib/components/wishlist.js.map +1 -1
  63. package/src/lib/declarations.server.js +11 -0
  64. package/src/lib/declarations.server.js.map +1 -1
  65. package/src/lib/model/commerce-orders.d.ts +8 -0
  66. package/src/lib/model/commerce-orders.js.map +1 -1
  67. package/src/lib/model/commerce.d.ts +21 -0
  68. package/src/lib/model/commerce.js +39 -1
  69. package/src/lib/model/commerce.js.map +1 -1
  70. package/src/lib/model/index.d.ts +1 -0
  71. package/src/lib/model/index.js +1 -0
  72. package/src/lib/model/index.js.map +1 -1
  73. package/src/lib/model/order-annotations.d.ts +54 -0
  74. package/src/lib/model/order-annotations.js +76 -0
  75. package/src/lib/model/order-annotations.js.map +1 -0
  76. package/src/lib/model/order-events.d.ts +2 -0
  77. package/src/lib/model/order-events.js.map +1 -1
  78. package/src/lib/model/order-fulfillment.d.ts +29 -0
  79. package/src/lib/model/order-fulfillment.js +63 -0
  80. package/src/lib/model/order-fulfillment.js.map +1 -1
  81. package/src/lib/model/order-local-fulfillment.d.ts +36 -0
  82. package/src/lib/model/order-local-fulfillment.js +19 -1
  83. package/src/lib/model/order-local-fulfillment.js.map +1 -1
  84. package/src/lib/model/order-status-view.d.ts +11 -0
  85. package/src/lib/model/order-status-view.js +20 -3
  86. package/src/lib/model/order-status-view.js.map +1 -1
  87. package/src/lib/model/product-write.js +8 -4
  88. package/src/lib/model/product-write.js.map +1 -1
  89. package/src/lib/server/api-v1/openapi.js +1 -0
  90. package/src/lib/server/api-v1/openapi.js.map +1 -1
  91. package/src/lib/server/api-v1/order-view.d.ts +1 -0
  92. package/src/lib/server/api-v1/order-view.js +10 -0
  93. package/src/lib/server/api-v1/order-view.js.map +1 -1
  94. package/src/lib/server/cart-checkout.js +9 -1
  95. package/src/lib/server/cart-checkout.js.map +1 -1
  96. package/src/lib/server/catalog.d.ts +6 -0
  97. package/src/lib/server/catalog.js +3 -1
  98. package/src/lib/server/catalog.js.map +1 -1
  99. package/src/lib/server/checkout.js +11 -1
  100. package/src/lib/server/checkout.js.map +1 -1
  101. package/src/lib/server/fulfill-order.d.ts +12 -0
  102. package/src/lib/server/fulfill-order.js +13 -0
  103. package/src/lib/server/fulfill-order.js.map +1 -1
  104. package/src/lib/server/local-delivery-records.d.ts +30 -0
  105. package/src/lib/server/local-delivery-records.js +284 -0
  106. package/src/lib/server/local-delivery-records.js.map +1 -0
  107. package/src/lib/server/order-annotations.d.ts +28 -0
  108. package/src/lib/server/order-annotations.js +143 -0
  109. package/src/lib/server/order-annotations.js.map +1 -0
  110. package/src/lib/server/order-events.js +2 -1
  111. package/src/lib/server/order-events.js.map +1 -1
  112. package/src/lib/server/order-notifications.js +39 -8
  113. package/src/lib/server/order-notifications.js.map +1 -1
  114. package/src/lib/server/pos-payment.js +9 -3
  115. package/src/lib/server/pos-payment.js.map +1 -1
  116. package/src/lib/server/product-catalog.js +6 -2
  117. package/src/lib/server/product-catalog.js.map +1 -1
  118. package/src/lib/server/product-drafts.d.ts +36 -9
  119. package/src/lib/server/product-drafts.js +24 -5
  120. package/src/lib/server/product-drafts.js.map +1 -1
  121. package/src/lib/server/product.d.ts +6 -1
  122. package/src/lib/server/product.js +1 -1
  123. package/src/lib/server/product.js.map +1 -1
  124. package/src/lib/server/products-write.d.ts +29 -0
  125. package/src/lib/server/products-write.js +7 -7
  126. package/src/lib/server/products-write.js.map +1 -1
  127. package/src/lib/server/shipment-records.js +2 -0
  128. package/src/lib/server/shipment-records.js.map +1 -1
  129. package/src/lib/server/site-page-resolver.d.ts +13 -0
  130. package/src/lib/server/site-page-resolver.js +138 -1
  131. package/src/lib/server/site-page-resolver.js.map +1 -1
  132. package/src/lib/server.js +5 -0
  133. package/src/lib/server.js.map +1 -1
  134. package/src/lib/utils/use-storefront-purchase-event.js +8 -2
  135. package/src/lib/utils/use-storefront-purchase-event.js.map +1 -1
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../libs/plugins/commerce/src/lib/model/order-status-view.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 type { HostOrder, OrderStatus } from './commerce-orders'\nimport { fulfillmentIsActive, fulfillmentLineQuantities } from './order-fulfillment'\nimport { carrierLabelFor, fulfillmentTrackingUrl } from './tracking-url'\nimport {\n ORDER_LOCAL_DELIVERY_STATUS_LABELS,\n ORDER_PICKUP_STATUS_LABELS,\n orderLocalDeliveryStatus,\n orderPickupStatus,\n type OrderLocalDeliveryStatus,\n type OrderPickupStatus,\n} from './order-local-fulfillment'\n\n/**\n * What the guest order-status page shows (AGL-3610): the ONE projection of an\n * order that leaves the server for a visitor holding a signed link.\n *\n * Allow-listed, never spread: an order document carries the buyer's email,\n * phone and addresses, Stripe ids, risk signals and the merchant's notes, and\n * a forwarded link must disclose no more than the email it arrived in. Every\n * field below is one the buyer's own receipt or shipping email already\n * states. New fields are added here deliberately, or not at all.\n */\nexport interface OrderStatusLine {\n name: string\n variantLabel: string | null\n quantity: number\n amountCents: number\n /** Units of this line in an active shipment. */\n shippedQuantity: number\n refunded: boolean\n}\n\nexport interface OrderStatusShipment {\n id: string\n atMs: number\n carrier: string | null\n trackingNumber: string | null\n trackingUrl: string | null\n lines: Array<{ name: string; quantity: number }>\n}\n\nexport interface OrderStatusStep {\n key:\n | 'placed'\n | 'paid'\n | 'shipped'\n | 'delivered'\n | 'cancelled'\n | 'refunded'\n // Pickup and the store's own delivery (AGL-3624).\n | 'ready_for_pickup'\n | 'picked_up'\n | 'out_for_delivery'\n label: string\n atMs: number | null\n done: boolean\n}\n\n/**\n * Something the buyer can do from the page — \"Request a return\" is the case\n * the order-processing lane (AGL-3611) adds through\n * `registerOrderStatusActions` on the server. The page renders each as a\n * button to `url`; it knows nothing of what the action is.\n */\nexport interface OrderStatusAction {\n id: string\n label: string\n url: string\n}\n\nexport interface OrderStatusView {\n storeName: string\n number: string\n status: OrderStatus\n /** The status as a buyer reads it. */\n statusLabel: string\n createdAtMs: number | null\n currency: string\n lines: OrderStatusLine[]\n totals: {\n itemsCents: number\n shippingCents: number\n taxCents: number\n discountCents: number\n totalCents: number\n refundedCents: number\n }\n /**\n * Optional lines the buyer took at checkout (AGL-3635) — \"Package\n * protection\" — by the name and price their receipt states. Optional so\n * an answer from before this field reads as none.\n */\n extras?: Array<{ label: string; amountCents: number }>\n shipments: OrderStatusShipment[]\n steps: OrderStatusStep[]\n actions: OrderStatusAction[]\n /**\n * Where to collect a pickup order (AGL-3624) — the location the buyer chose\n * and its hours, as the ready-for-pickup email states them.\n */\n pickup?: {\n locationName: string\n address: string | null\n hours: string | null\n instructions: string | null\n status: OrderPickupStatus\n statusLabel: string\n }\n /** When the store's own driver brings it (AGL-3624). */\n localDelivery?: {\n windowLabel: string | null\n status: OrderLocalDeliveryStatus\n statusLabel: string\n }\n}\n\nconst STATUS_LABELS: Record<OrderStatus, string> = {\n pending: 'Awaiting payment',\n paid: 'Confirmed',\n partially_fulfilled: 'Partly shipped',\n fulfilled: 'Shipped',\n delivered: 'Delivered',\n cancelled: 'Canceled',\n refunded: 'Refunded',\n}\n\nfunction eventAt(order: HostOrder, ...events: string[]): number | null {\n const found = (order.timeline ?? []).find((entry) =>\n events.includes(String(entry?.event ?? '')),\n )\n return found ? Number(found.atMs) || null : null\n}\n\n/** Builds the public projection. Pure; the caller has verified the link. */\nexport function buildOrderStatusView(input: {\n order: HostOrder\n orderId: string\n storeName: string\n currency?: string\n number: string\n actions?: OrderStatusAction[]\n}): OrderStatusView {\n const { order } = input\n const lineItems = order.lineItems ?? []\n const active = (order.fulfillments ?? []).filter(fulfillmentIsActive)\n // A handover is not a parcel (AGL-3624): it has no carrier or tracking,\n // and the pickup or delivery block below says what happened.\n const parcels = active.filter((fulfillment) => !fulfillment.handover)\n const shippedByLine = new Map<number, number>()\n for (const fulfillment of active.filter((entry) => entry.handover)) {\n for (const entry of fulfillmentLineQuantities(order, fulfillment)) {\n shippedByLine.set(entry.lineItemId, (shippedByLine.get(entry.lineItemId) ?? 0) + entry.quantity)\n }\n }\n const shipments: OrderStatusShipment[] = parcels\n .map((fulfillment) => {\n const quantities = fulfillmentLineQuantities(order, fulfillment)\n for (const entry of quantities) {\n shippedByLine.set(\n entry.lineItemId,\n (shippedByLine.get(entry.lineItemId) ?? 0) + entry.quantity,\n )\n }\n return {\n id: String(fulfillment.id ?? ''),\n atMs: Number(fulfillment.atMs) || 0,\n carrier: fulfillment.carrier ? carrierLabelFor(fulfillment.carrier) : null,\n trackingNumber: fulfillment.trackingNumber ?? null,\n trackingUrl: fulfillmentTrackingUrl(fulfillment),\n lines: quantities.map((entry) => ({\n name: String(lineItems[entry.lineItemId]?.name ?? 'Item'),\n quantity: entry.quantity,\n })),\n }\n })\n .sort((a, b) => a.atMs - b.atMs)\n const refundedLines = new Set(order.refundedLineItemIds ?? [])\n const lines: OrderStatusLine[] = lineItems.map((line, index) => ({\n name: String(line.name ?? 'Item'),\n variantLabel: line.variantLabel ?? null,\n quantity: Number(line.quantity) || 0,\n amountCents: (Number(line.unitAmountCents) || 0) * (Number(line.quantity) || 0),\n shippedQuantity: Math.min(Number(line.quantity) || 0, shippedByLine.get(index) ?? 0),\n refunded: refundedLines.has(index),\n }))\n const totals = order.totals\n const totalCents = totals?.totalCents ?? Number(order.amountCents ?? 0)\n const status = order.status\n const createdAtMs = Number(order.createdAtMs) || null\n const paidAtMs = eventAt(order, 'paid') ?? (status !== 'pending' ? createdAtMs : null)\n const firstShipAt = shipments[0]?.atMs ?? eventAt(order, 'fulfilled', 'partially_fulfilled')\n const deliveredAt = eventAt(order, 'delivered')\n const steps: OrderStatusStep[] = [\n { key: 'placed', label: 'Order placed', atMs: createdAtMs, done: true },\n {\n key: 'paid',\n label: 'Payment confirmed',\n atMs: paidAtMs,\n done: status !== 'pending' && paidAtMs !== null,\n },\n ]\n const pickup = order.fulfillmentMethod === 'pickup' ? order.pickup : undefined\n const delivery = order.fulfillmentMethod === 'local_delivery' ? order.localDelivery : undefined\n if (status === 'cancelled') {\n steps.push({ key: 'cancelled', label: 'Canceled', atMs: eventAt(order, 'cancelled'), done: true })\n } else if (pickup) {\n const pickupStatus = orderPickupStatus(pickup.status)\n steps.push(\n {\n key: 'ready_for_pickup',\n label: 'Ready for pickup',\n atMs: Number(pickup.readyAtMs) || null,\n done: pickupStatus !== 'preparing',\n },\n {\n key: 'picked_up',\n label: 'Picked up',\n atMs: Number(pickup.pickedUpAtMs) || null,\n done: pickupStatus === 'picked_up',\n },\n )\n } else if (delivery) {\n const deliveryStatus = orderLocalDeliveryStatus(delivery.status)\n steps.push(\n {\n key: 'out_for_delivery',\n label: 'Out for delivery',\n atMs: Number(delivery.outForDeliveryAtMs) || null,\n done: deliveryStatus === 'out_for_delivery' || deliveryStatus === 'delivered',\n },\n {\n key: 'delivered',\n label: 'Delivered',\n atMs: Number(delivery.deliveredAtMs) || deliveredAt,\n done: deliveryStatus === 'delivered' || status === 'delivered',\n },\n )\n } else {\n steps.push(\n {\n key: 'shipped',\n label: status === 'partially_fulfilled' ? 'Partly shipped' : 'Shipped',\n atMs: firstShipAt,\n done: ['partially_fulfilled', 'fulfilled', 'delivered'].includes(status) ||\n shipments.length > 0,\n },\n {\n key: 'delivered',\n label: 'Delivered',\n atMs: deliveredAt,\n done: status === 'delivered' || deliveredAt !== null,\n },\n )\n }\n if (status === 'refunded') {\n steps.push({ key: 'refunded', label: 'Refunded', atMs: eventAt(order, 'refund'), done: true })\n }\n return {\n storeName: input.storeName,\n number: input.number,\n status,\n statusLabel:\n pickup && status !== 'refunded' && status !== 'cancelled'\n ? ORDER_PICKUP_STATUS_LABELS[orderPickupStatus(pickup.status)]\n : delivery && status !== 'refunded' && status !== 'cancelled'\n ? ORDER_LOCAL_DELIVERY_STATUS_LABELS[orderLocalDeliveryStatus(delivery.status)]\n : (STATUS_LABELS[status] ?? 'Confirmed'),\n createdAtMs,\n currency: input.currency || 'USD',\n lines,\n totals: {\n itemsCents: totals?.itemsCents ?? lines.reduce((sum, line) => sum + line.amountCents, 0),\n shippingCents: totals?.shippingCents ?? 0,\n taxCents: totals?.taxCents ?? 0,\n discountCents: totals?.discountCents ?? 0,\n totalCents,\n refundedCents: Number(order.refundedCents ?? 0) || 0,\n },\n extras: (order.extras ?? [])\n .filter((extra) => extra && Number(extra.amountCents) > 0)\n .map((extra) => ({ label: String(extra.label || 'Extra'), amountCents: Number(extra.amountCents) })),\n shipments,\n steps,\n actions: input.actions ?? [],\n ...(pickup\n ? {\n pickup: {\n locationName: String(pickup.locationName ?? ''),\n address: pickup.address ?? null,\n hours: pickup.hours ?? null,\n instructions: pickup.instructions ?? null,\n status: orderPickupStatus(pickup.status),\n statusLabel: ORDER_PICKUP_STATUS_LABELS[orderPickupStatus(pickup.status)],\n },\n }\n : {}),\n ...(delivery\n ? {\n localDelivery: {\n windowLabel: delivery.windowLabel ?? null,\n status: orderLocalDeliveryStatus(delivery.status),\n statusLabel: ORDER_LOCAL_DELIVERY_STATUS_LABELS[orderLocalDeliveryStatus(delivery.status)],\n },\n }\n : {}),\n }\n}\n"],"names":["fulfillmentIsActive","fulfillmentLineQuantities","carrierLabelFor","fulfillmentTrackingUrl","ORDER_LOCAL_DELIVERY_STATUS_LABELS","ORDER_PICKUP_STATUS_LABELS","orderLocalDeliveryStatus","orderPickupStatus","STATUS_LABELS","pending","paid","partially_fulfilled","fulfilled","delivered","cancelled","refunded","eventAt","order","events","found","timeline","find","entry","includes","String","event","Number","atMs","buildOrderStatusView","input","pickup","delivery","shipments","lineItems","active","fulfillments","filter","parcels","fulfillment","handover","shippedByLine","Map","set","lineItemId","get","quantity","map","quantities","id","carrier","trackingNumber","trackingUrl","lines","name","sort","a","b","refundedLines","Set","refundedLineItemIds","line","index","variantLabel","amountCents","unitAmountCents","shippedQuantity","Math","min","has","totals","totalCents","status","createdAtMs","paidAtMs","firstShipAt","deliveredAt","steps","key","label","done","fulfillmentMethod","undefined","localDelivery","push","pickupStatus","readyAtMs","pickedUpAtMs","deliveryStatus","outForDeliveryAtMs","deliveredAtMs","length","storeName","number","statusLabel","currency","itemsCents","reduce","sum","shippingCents","taxCents","discountCents","refundedCents","extras","extra","actions","locationName","address","hours","instructions","windowLabel"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/commerce/src/lib/model/order-status-view.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 type { HostOrder, OrderStatus } from './commerce-orders'\nimport { fulfillmentIsActive, fulfillmentLineQuantities } from './order-fulfillment'\nimport { carrierLabelFor, fulfillmentTrackingUrl } from './tracking-url'\nimport {\n ORDER_COURIER_STATE_LABELS,\n ORDER_LOCAL_DELIVERY_STATUS_LABELS,\n orderCourierIsActive,\n ORDER_PICKUP_STATUS_LABELS,\n orderLocalDeliveryStatus,\n orderPickupStatus,\n type OrderLocalDeliveryStatus,\n type OrderPickupStatus,\n} from './order-local-fulfillment'\n\n/**\n * What the guest order-status page shows (AGL-3610): the ONE projection of an\n * order that leaves the server for a visitor holding a signed link.\n *\n * Allow-listed, never spread: an order document carries the buyer's email,\n * phone and addresses, Stripe ids, risk signals and the merchant's notes, and\n * a forwarded link must disclose no more than the email it arrived in. Every\n * field below is one the buyer's own receipt or shipping email already\n * states. New fields are added here deliberately, or not at all.\n */\nexport interface OrderStatusLine {\n name: string\n variantLabel: string | null\n quantity: number\n amountCents: number\n /** Units of this line in an active shipment. */\n shippedQuantity: number\n refunded: boolean\n}\n\nexport interface OrderStatusShipment {\n id: string\n atMs: number\n carrier: string | null\n trackingNumber: string | null\n trackingUrl: string | null\n lines: Array<{ name: string; quantity: number }>\n}\n\nexport interface OrderStatusStep {\n key:\n | 'placed'\n | 'paid'\n | 'shipped'\n | 'delivered'\n | 'cancelled'\n | 'refunded'\n // Pickup and the store's own delivery (AGL-3624).\n | 'ready_for_pickup'\n | 'picked_up'\n | 'out_for_delivery'\n label: string\n atMs: number | null\n done: boolean\n}\n\n/**\n * Something the buyer can do from the page — \"Request a return\" is the case\n * the order-processing lane (AGL-3611) adds through\n * `registerOrderStatusActions` on the server. The page renders each as a\n * button to `url`; it knows nothing of what the action is.\n */\nexport interface OrderStatusAction {\n id: string\n label: string\n url: string\n}\n\nexport interface OrderStatusView {\n storeName: string\n number: string\n status: OrderStatus\n /** The status as a buyer reads it. */\n statusLabel: string\n createdAtMs: number | null\n currency: string\n lines: OrderStatusLine[]\n totals: {\n itemsCents: number\n shippingCents: number\n taxCents: number\n discountCents: number\n totalCents: number\n refundedCents: number\n }\n /**\n * Optional lines the buyer took at checkout (AGL-3635) — \"Package\n * protection\" — by the name and price their receipt states. Optional so\n * an answer from before this field reads as none.\n */\n extras?: Array<{ label: string; amountCents: number }>\n shipments: OrderStatusShipment[]\n steps: OrderStatusStep[]\n actions: OrderStatusAction[]\n /**\n * Where to collect a pickup order (AGL-3624) — the location the buyer chose\n * and its hours, as the ready-for-pickup email states them.\n */\n pickup?: {\n locationName: string\n address: string | null\n hours: string | null\n instructions: string | null\n status: OrderPickupStatus\n statusLabel: string\n }\n /** When the store's own driver brings it (AGL-3624). */\n localDelivery?: {\n windowLabel: string | null\n status: OrderLocalDeliveryStatus\n statusLabel: string\n /**\n * The outside courier bringing it (AGL-3695), while one is: who, where\n * the run stands, the courier's own tracking page and the arrival\n * estimate. Optional so an answer from before this field reads as none.\n */\n courier?: {\n providerLabel: string\n stateLabel: string\n trackingUrl: string | null\n etaMs: number | null\n } | null\n }\n}\n\nconst STATUS_LABELS: Record<OrderStatus, string> = {\n pending: 'Awaiting payment',\n paid: 'Confirmed',\n partially_fulfilled: 'Partly shipped',\n fulfilled: 'Shipped',\n delivered: 'Delivered',\n cancelled: 'Canceled',\n refunded: 'Refunded',\n}\n\nfunction eventAt(order: HostOrder, ...events: string[]): number | null {\n const found = (order.timeline ?? []).find((entry) =>\n events.includes(String(entry?.event ?? '')),\n )\n return found ? Number(found.atMs) || null : null\n}\n\n/** Builds the public projection. Pure; the caller has verified the link. */\nexport function buildOrderStatusView(input: {\n order: HostOrder\n orderId: string\n storeName: string\n currency?: string\n number: string\n actions?: OrderStatusAction[]\n}): OrderStatusView {\n const { order } = input\n const lineItems = order.lineItems ?? []\n const active = (order.fulfillments ?? []).filter(fulfillmentIsActive)\n // A handover is not a parcel (AGL-3624): it has no carrier or tracking,\n // and the pickup or delivery block below says what happened.\n const parcels = active.filter((fulfillment) => !fulfillment.handover)\n const shippedByLine = new Map<number, number>()\n for (const fulfillment of active.filter((entry) => entry.handover)) {\n for (const entry of fulfillmentLineQuantities(order, fulfillment)) {\n shippedByLine.set(entry.lineItemId, (shippedByLine.get(entry.lineItemId) ?? 0) + entry.quantity)\n }\n }\n const shipments: OrderStatusShipment[] = parcels\n .map((fulfillment) => {\n const quantities = fulfillmentLineQuantities(order, fulfillment)\n for (const entry of quantities) {\n shippedByLine.set(\n entry.lineItemId,\n (shippedByLine.get(entry.lineItemId) ?? 0) + entry.quantity,\n )\n }\n return {\n id: String(fulfillment.id ?? ''),\n atMs: Number(fulfillment.atMs) || 0,\n carrier: fulfillment.carrier ? carrierLabelFor(fulfillment.carrier) : null,\n trackingNumber: fulfillment.trackingNumber ?? null,\n trackingUrl: fulfillmentTrackingUrl(fulfillment),\n lines: quantities.map((entry) => ({\n name: String(lineItems[entry.lineItemId]?.name ?? 'Item'),\n quantity: entry.quantity,\n })),\n }\n })\n .sort((a, b) => a.atMs - b.atMs)\n const refundedLines = new Set(order.refundedLineItemIds ?? [])\n const lines: OrderStatusLine[] = lineItems.map((line, index) => ({\n name: String(line.name ?? 'Item'),\n variantLabel: line.variantLabel ?? null,\n quantity: Number(line.quantity) || 0,\n amountCents: (Number(line.unitAmountCents) || 0) * (Number(line.quantity) || 0),\n shippedQuantity: Math.min(Number(line.quantity) || 0, shippedByLine.get(index) ?? 0),\n refunded: refundedLines.has(index),\n }))\n const totals = order.totals\n const totalCents = totals?.totalCents ?? Number(order.amountCents ?? 0)\n const status = order.status\n const createdAtMs = Number(order.createdAtMs) || null\n const paidAtMs = eventAt(order, 'paid') ?? (status !== 'pending' ? createdAtMs : null)\n const firstShipAt = shipments[0]?.atMs ?? eventAt(order, 'fulfilled', 'partially_fulfilled')\n const deliveredAt = eventAt(order, 'delivered')\n const steps: OrderStatusStep[] = [\n { key: 'placed', label: 'Order placed', atMs: createdAtMs, done: true },\n {\n key: 'paid',\n label: 'Payment confirmed',\n atMs: paidAtMs,\n done: status !== 'pending' && paidAtMs !== null,\n },\n ]\n const pickup = order.fulfillmentMethod === 'pickup' ? order.pickup : undefined\n const delivery = order.fulfillmentMethod === 'local_delivery' ? order.localDelivery : undefined\n if (status === 'cancelled') {\n steps.push({ key: 'cancelled', label: 'Canceled', atMs: eventAt(order, 'cancelled'), done: true })\n } else if (pickup) {\n const pickupStatus = orderPickupStatus(pickup.status)\n steps.push(\n {\n key: 'ready_for_pickup',\n label: 'Ready for pickup',\n atMs: Number(pickup.readyAtMs) || null,\n done: pickupStatus !== 'preparing',\n },\n {\n key: 'picked_up',\n label: 'Picked up',\n atMs: Number(pickup.pickedUpAtMs) || null,\n done: pickupStatus === 'picked_up',\n },\n )\n } else if (delivery) {\n const deliveryStatus = orderLocalDeliveryStatus(delivery.status)\n steps.push(\n {\n key: 'out_for_delivery',\n label: 'Out for delivery',\n atMs: Number(delivery.outForDeliveryAtMs) || null,\n done: deliveryStatus === 'out_for_delivery' || deliveryStatus === 'delivered',\n },\n {\n key: 'delivered',\n label: 'Delivered',\n atMs: Number(delivery.deliveredAtMs) || deliveredAt,\n done: deliveryStatus === 'delivered' || status === 'delivered',\n },\n )\n } else {\n steps.push(\n {\n key: 'shipped',\n label: status === 'partially_fulfilled' ? 'Partly shipped' : 'Shipped',\n atMs: firstShipAt,\n done: ['partially_fulfilled', 'fulfilled', 'delivered'].includes(status) ||\n shipments.length > 0,\n },\n {\n key: 'delivered',\n label: 'Delivered',\n atMs: deliveredAt,\n done: status === 'delivered' || deliveredAt !== null,\n },\n )\n }\n if (status === 'refunded') {\n steps.push({ key: 'refunded', label: 'Refunded', atMs: eventAt(order, 'refund'), done: true })\n }\n return {\n storeName: input.storeName,\n number: input.number,\n status,\n statusLabel:\n pickup && status !== 'refunded' && status !== 'cancelled'\n ? ORDER_PICKUP_STATUS_LABELS[orderPickupStatus(pickup.status)]\n : delivery && status !== 'refunded' && status !== 'cancelled'\n ? ORDER_LOCAL_DELIVERY_STATUS_LABELS[orderLocalDeliveryStatus(delivery.status)]\n : (STATUS_LABELS[status] ?? 'Confirmed'),\n createdAtMs,\n currency: input.currency || 'USD',\n lines,\n totals: {\n itemsCents: totals?.itemsCents ?? lines.reduce((sum, line) => sum + line.amountCents, 0),\n shippingCents: totals?.shippingCents ?? 0,\n taxCents: totals?.taxCents ?? 0,\n discountCents: totals?.discountCents ?? 0,\n totalCents,\n refundedCents: Number(order.refundedCents ?? 0) || 0,\n },\n extras: (order.extras ?? [])\n .filter((extra) => extra && Number(extra.amountCents) > 0)\n .map((extra) => ({ label: String(extra.label || 'Extra'), amountCents: Number(extra.amountCents) })),\n shipments,\n steps,\n actions: input.actions ?? [],\n ...(pickup\n ? {\n pickup: {\n locationName: String(pickup.locationName ?? ''),\n address: pickup.address ?? null,\n hours: pickup.hours ?? null,\n instructions: pickup.instructions ?? null,\n status: orderPickupStatus(pickup.status),\n statusLabel: ORDER_PICKUP_STATUS_LABELS[orderPickupStatus(pickup.status)],\n },\n }\n : {}),\n ...(delivery\n ? {\n localDelivery: {\n windowLabel: delivery.windowLabel ?? null,\n status: orderLocalDeliveryStatus(delivery.status),\n statusLabel: ORDER_LOCAL_DELIVERY_STATUS_LABELS[orderLocalDeliveryStatus(delivery.status)],\n ...(courierView(delivery.courier) ? { courier: courierView(delivery.courier) } : {}),\n },\n }\n : {}),\n }\n}\n\n/**\n * The courier as a buyer reads it: only a run still under way, only an https\n * tracking page, and no reference, fee or test flag (AGL-3695).\n */\nfunction courierView(\n courier: NonNullable<HostOrder['localDelivery']>['courier'],\n): NonNullable<NonNullable<OrderStatusView['localDelivery']>['courier']> | null {\n if (!courier || !orderCourierIsActive(courier)) return null\n const trackingUrl = /^https:\\/\\/[^\\s]+$/.test(String(courier.trackingUrl ?? '')) ? String(courier.trackingUrl) : null\n const etaMs = Number(courier.etaMs)\n return {\n providerLabel: String(courier.providerLabel || 'Courier'),\n stateLabel: ORDER_COURIER_STATE_LABELS[courier.state] ?? 'Courier requested',\n trackingUrl,\n etaMs: Number.isFinite(etaMs) && etaMs > 0 ? etaMs : null,\n }\n}\n"],"names":["fulfillmentIsActive","fulfillmentLineQuantities","carrierLabelFor","fulfillmentTrackingUrl","ORDER_COURIER_STATE_LABELS","ORDER_LOCAL_DELIVERY_STATUS_LABELS","orderCourierIsActive","ORDER_PICKUP_STATUS_LABELS","orderLocalDeliveryStatus","orderPickupStatus","STATUS_LABELS","pending","paid","partially_fulfilled","fulfilled","delivered","cancelled","refunded","eventAt","order","events","found","timeline","find","entry","includes","String","event","Number","atMs","buildOrderStatusView","input","pickup","delivery","shipments","lineItems","active","fulfillments","filter","parcels","fulfillment","handover","shippedByLine","Map","set","lineItemId","get","quantity","map","quantities","id","carrier","trackingNumber","trackingUrl","lines","name","sort","a","b","refundedLines","Set","refundedLineItemIds","line","index","variantLabel","amountCents","unitAmountCents","shippedQuantity","Math","min","has","totals","totalCents","status","createdAtMs","paidAtMs","firstShipAt","deliveredAt","steps","key","label","done","fulfillmentMethod","undefined","localDelivery","push","pickupStatus","readyAtMs","pickedUpAtMs","deliveryStatus","outForDeliveryAtMs","deliveredAtMs","length","storeName","number","statusLabel","currency","itemsCents","reduce","sum","shippingCents","taxCents","discountCents","refundedCents","extras","extra","actions","locationName","address","hours","instructions","windowLabel","courierView","courier","test","etaMs","providerLabel","stateLabel","state","isFinite"],"mappings":"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"}
@@ -15,7 +15,7 @@ import { _ as _object_without_properties_loose } from "@swc/helpers/_/_object_wi
15
15
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
16
  * See the License for the specific language governing permissions and
17
17
  * limitations under the License.
18
- */ import { adjustVariantInventory, commerceSlug, productSearchFields, productStockFields } from "./commerce.js";
18
+ */ import { adjustVariantInventory, commerceSlug, productSearchFields, productStockFields, variantHasPrice } from "./commerce.js";
19
19
  /**
20
20
  * The fields a product save writes, before its collection membership and its
21
21
  * timestamps: the product as edited, with
@@ -29,7 +29,7 @@ import { _ as _object_without_properties_loose } from "@swc/helpers/_/_object_wi
29
29
  * - a slug, the primary variant's price, the stock fields, the first image
30
30
  * and `updatedAtMs`.
31
31
  */ export function productSaveFields(current, nowMs) {
32
- var _ref, _ref1, _ref2;
32
+ var _ref, _ref1;
33
33
  var _current_mediaUrls;
34
34
  const { $id: _syntheticId, skus: _seededSkus, barcodes: _seededBarcodes } = current, currentFields = _object_without_properties_loose(current, [
35
35
  "$id",
@@ -42,12 +42,16 @@ import { _ as _object_without_properties_loose } from "@swc/helpers/_/_object_wi
42
42
  variants: current.variants
43
43
  }), {
44
44
  slug: current.slug || commerceSlug(current.name),
45
- priceUsd: (_ref = primaryVariant == null ? void 0 : primaryVariant.priceUsd) != null ? _ref : 0
45
+ // The legacy flat price, only where there is one (AGL-3676): an owner
46
+ // may save a product before pricing it, and an unpriced product is not
47
+ // a free one. `null` rather than absent, so an edit's full replace and
48
+ // the native save write the same field.
49
+ priceUsd: variantHasPrice(primaryVariant) ? primaryVariant.priceUsd : null
46
50
  }, productStockFields({
47
51
  variants: current.variants,
48
52
  oversellPolicy: current.oversellPolicy
49
53
  }), {
50
- imageUrl: (_ref1 = (_ref2 = (_current_mediaUrls = current.mediaUrls) == null ? void 0 : _current_mediaUrls[0]) != null ? _ref2 : current.imageUrl) != null ? _ref1 : null,
54
+ imageUrl: (_ref = (_ref1 = (_current_mediaUrls = current.mediaUrls) == null ? void 0 : _current_mediaUrls[0]) != null ? _ref1 : current.imageUrl) != null ? _ref : null,
51
55
  updatedAtMs: nowMs
52
56
  });
53
57
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../libs/plugins/commerce/src/lib/model/product-write.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 {\n adjustVariantInventory,\n commerceSlug,\n type HostProduct,\n type InventoryAdjustment,\n type InventoryAdjustmentReason,\n productSearchFields,\n productStockFields,\n type SmartCollectionRules,\n} from './commerce'\n\n/*\n * WHAT A PRODUCT SAVE AND A STOCK ADJUSTMENT WRITE, worked out once.\n *\n * The console's product editor and its Adjust stock dialog compute these\n * fields in the browser before their Firestore writes; the native apps save\n * through `commerce/products/save` and `commerce/products/stock`, which run\n * this same code on the server. One computation, so a product saved from a\n * phone carries exactly the search keys, stock verdict, price, image and\n * smart-collection membership the console would have written.\n */\n\n/** A product as the editor holds it: the stored fields plus keys the listener or a seed may carry. */\nexport type EditedProduct = HostProduct & {\n $id?: string\n skus?: string[]\n barcodes?: string[]\n stockHolds?: Record<string, unknown>\n}\n\n/**\n * The fields a product save writes, before its collection membership and its\n * timestamps: the product as edited, with\n *\n * - `$id` dropped (AGL-1374): the listener's synthetic key, never a field;\n * - the seed's `skus` / `barcodes` dropped (AGL-3321): `productSearchFields`\n * omits each when no variant has one, so a seeded copy would outlive the\n * last SKU;\n * - the trimmed name with the search keys derived from it, together, so the\n * two cannot drift (AGL-2501);\n * - a slug, the primary variant's price, the stock fields, the first image\n * and `updatedAtMs`.\n */\nexport function productSaveFields(current: EditedProduct, nowMs: number) {\n const {\n $id: _syntheticId,\n skus: _seededSkus,\n barcodes: _seededBarcodes,\n ...currentFields\n } = current\n const primaryVariant = current.variants[0]\n return {\n ...currentFields,\n ...productSearchFields({\n name: current.name.trim().slice(0, 120),\n variants: current.variants,\n }),\n slug: current.slug || commerceSlug(current.name),\n priceUsd: primaryVariant?.priceUsd ?? 0,\n ...productStockFields({\n variants: current.variants,\n oversellPolicy: current.oversellPolicy,\n }),\n imageUrl: current.mediaUrls?.[0] ?? current.imageUrl ?? null,\n updatedAtMs: nowMs,\n }\n}\n\n/** What the smart-collection rules are matched against: the saved name with the product's own facts. */\nexport function productMembershipInput(\n fields: { name: string },\n current: Pick<HostProduct, 'type' | 'tags' | 'categoryIds' | 'variants'>,\n) {\n return {\n name: fields.name,\n type: current.type,\n tags: current.tags,\n categoryIds: current.categoryIds,\n variants: current.variants,\n }\n}\n\n/**\n * An EDIT's replacing write, without its membership and server timestamp:\n * live stock holds are not the editor's to write (AGL-2356), so the seed's\n * are dropped and the stored ones carried; the product stays live\n * (`deletedAt: null`, AGL-3321).\n */\nexport function productEditWrite(\n fields: ReturnType<typeof productSaveFields>,\n liveHolds: Record<string, unknown> | null | undefined,\n) {\n const { stockHolds: _seededHolds, ...withoutHolds } = fields as typeof fields & {\n stockHolds?: Record<string, unknown>\n }\n return {\n ...withoutHolds,\n ...(liveHolds ? { stockHolds: liveHolds } : {}),\n deletedAt: null,\n }\n}\n\n/** A smart collection as `hosts/{hostId}/collections` stores it, as the membership rules read it. */\nexport function smartCollectionRulesOf(id: string, data: Record<string, unknown>): SmartCollectionRules {\n return {\n id,\n rules: (data['rules'] as SmartCollectionRules['rules']) ?? [],\n matchAll: data['matchAll'] as boolean | undefined,\n }\n}\n\n/** One stock adjustment, as the Adjust stock dialog asks for it. */\nexport interface StockAdjustment {\n variantId: string\n delta: number\n reason: InventoryAdjustmentReason\n locationId?: string\n}\n\n/**\n * The product update and the ledger row one stock adjustment writes: the\n * variants with the count moved, the stock fields that move with it\n * (AGL-3321), and the same `inventoryAdjustments` row the sale webhook writes\n * (AGL-281). A zero or non-numeric delta writes nothing.\n */\nexport function stockAdjustmentWrite(\n product: HostProduct,\n productId: string,\n adjustment: StockAdjustment,\n nowMs: number,\n): { update: Record<string, unknown>; ledger: InventoryAdjustment } | null {\n const delta = Math.round(Number(adjustment.delta))\n if (!delta) return null\n const variants = adjustVariantInventory(product, adjustment.variantId, delta, adjustment.locationId || undefined)\n return {\n update: {\n variants,\n ...productStockFields({ ...product, variants }),\n updatedAtMs: nowMs,\n },\n ledger: {\n productId,\n variantId: adjustment.variantId,\n delta,\n reason: adjustment.reason,\n ...(adjustment.locationId ? { locationId: adjustment.locationId } : {}),\n atMs: nowMs,\n },\n }\n}\n"],"names":["adjustVariantInventory","commerceSlug","productSearchFields","productStockFields","productSaveFields","current","nowMs","$id","_syntheticId","skus","_seededSkus","barcodes","_seededBarcodes","currentFields","primaryVariant","variants","name","trim","slice","slug","priceUsd","oversellPolicy","imageUrl","mediaUrls","updatedAtMs","productMembershipInput","fields","type","tags","categoryIds","productEditWrite","liveHolds","stockHolds","_seededHolds","withoutHolds","deletedAt","smartCollectionRulesOf","id","data","rules","matchAll","stockAdjustmentWrite","product","productId","adjustment","delta","Math","round","Number","variantId","locationId","undefined","update","ledger","reason","atMs"],"mappings":";;AAAA;;;;;;;;;;;;;;;CAeC,GAED,SACEA,sBAAsB,EACtBC,YAAY,EAIZC,mBAAmB,EACnBC,kBAAkB,QAEb,gBAAY;AAqBnB;;;;;;;;;;;;CAYC,GACD,OAAO,SAASC,kBAAkBC,OAAsB,EAAEC,KAAa;cAoBzDD;QAAAA;IAnBZ,MAAM,EACJE,KAAKC,YAAY,EACjBC,MAAMC,WAAW,EACjBC,UAAUC,eAAe,EAE1B,GAAGP,SADCQ,iDACDR;;;;;IACJ,MAAMS,iBAAiBT,QAAQU,QAAQ,CAAC,EAAE;IAC1C,OAAO,aACFF,eACAX,oBAAoB;QACrBc,MAAMX,QAAQW,IAAI,CAACC,IAAI,GAAGC,KAAK,CAAC,GAAG;QACnCH,UAAUV,QAAQU,QAAQ;IAC5B;QACAI,MAAMd,QAAQc,IAAI,IAAIlB,aAAaI,QAAQW,IAAI;QAC/CI,QAAQ,UAAEN,kCAAAA,eAAgBM,QAAQ,mBAAI;OACnCjB,mBAAmB;QACpBY,UAAUV,QAAQU,QAAQ;QAC1BM,gBAAgBhB,QAAQgB,cAAc;IACxC;QACAC,QAAQ,GAAEjB,kBAAAA,qBAAAA,QAAQkB,SAAS,qBAAjBlB,kBAAmB,CAAC,EAAE,oBAAIA,QAAQiB,QAAQ,YAA1CjB,QAA8C;QACxDmB,aAAalB;;AAEjB;AAEA,sGAAsG,GACtG,OAAO,SAASmB,uBACdC,MAAwB,EACxBrB,OAAwE;IAExE,OAAO;QACLW,MAAMU,OAAOV,IAAI;QACjBW,MAAMtB,QAAQsB,IAAI;QAClBC,MAAMvB,QAAQuB,IAAI;QAClBC,aAAaxB,QAAQwB,WAAW;QAChCd,UAAUV,QAAQU,QAAQ;IAC5B;AACF;AAEA;;;;;CAKC,GACD,OAAO,SAASe,iBACdJ,MAA4C,EAC5CK,SAAqD;IAErD,MAAM,EAAEC,YAAYC,YAAY,EAAmB,GAAGP,QAAjBQ,gDAAiBR;;;IAGtD,OAAO,aACFQ,cACCH,YAAY;QAAEC,YAAYD;IAAU,IAAI,CAAC;QAC7CI,WAAW;;AAEf;AAEA,mGAAmG,GACnG,OAAO,SAASC,uBAAuBC,EAAU,EAAEC,IAA6B;QAGpEA;IAFV,OAAO;QACLD;QACAE,KAAK,GAAGD,cAAAA,IAAI,CAAC,QAAQ,YAAbA,cAAmD,EAAE;QAC7DE,UAAUF,IAAI,CAAC,WAAW;IAC5B;AACF;AAUA;;;;;CAKC,GACD,OAAO,SAASG,qBACdC,OAAoB,EACpBC,SAAiB,EACjBC,UAA2B,EAC3BtC,KAAa;IAEb,MAAMuC,QAAQC,KAAKC,KAAK,CAACC,OAAOJ,WAAWC,KAAK;IAChD,IAAI,CAACA,OAAO,OAAO;IACnB,MAAM9B,WAAWf,uBAAuB0C,SAASE,WAAWK,SAAS,EAAEJ,OAAOD,WAAWM,UAAU,IAAIC;IACvG,OAAO;QACLC,QAAQ;YACNrC;WACGZ,mBAAmB,aAAKuC;YAAS3B;;YACpCS,aAAalB;;QAEf+C,QAAQ;YACNV;YACAM,WAAWL,WAAWK,SAAS;YAC/BJ;YACAS,QAAQV,WAAWU,MAAM;WACrBV,WAAWM,UAAU,GAAG;YAAEA,YAAYN,WAAWM,UAAU;QAAC,IAAI,CAAC;YACrEK,MAAMjD;;IAEV;AACF"}
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/commerce/src/lib/model/product-write.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 {\n adjustVariantInventory,\n commerceSlug,\n type HostProduct,\n type InventoryAdjustment,\n type InventoryAdjustmentReason,\n productSearchFields,\n productStockFields,\n type SmartCollectionRules,\n variantHasPrice,\n} from './commerce'\n\n/*\n * WHAT A PRODUCT SAVE AND A STOCK ADJUSTMENT WRITE, worked out once.\n *\n * The console's product editor and its Adjust stock dialog compute these\n * fields in the browser before their Firestore writes; the native apps save\n * through `commerce/products/save` and `commerce/products/stock`, which run\n * this same code on the server. One computation, so a product saved from a\n * phone carries exactly the search keys, stock verdict, price, image and\n * smart-collection membership the console would have written.\n */\n\n/** A product as the editor holds it: the stored fields plus keys the listener or a seed may carry. */\nexport type EditedProduct = HostProduct & {\n $id?: string\n skus?: string[]\n barcodes?: string[]\n stockHolds?: Record<string, unknown>\n}\n\n/**\n * The fields a product save writes, before its collection membership and its\n * timestamps: the product as edited, with\n *\n * - `$id` dropped (AGL-1374): the listener's synthetic key, never a field;\n * - the seed's `skus` / `barcodes` dropped (AGL-3321): `productSearchFields`\n * omits each when no variant has one, so a seeded copy would outlive the\n * last SKU;\n * - the trimmed name with the search keys derived from it, together, so the\n * two cannot drift (AGL-2501);\n * - a slug, the primary variant's price, the stock fields, the first image\n * and `updatedAtMs`.\n */\nexport function productSaveFields(current: EditedProduct, nowMs: number) {\n const {\n $id: _syntheticId,\n skus: _seededSkus,\n barcodes: _seededBarcodes,\n ...currentFields\n } = current\n const primaryVariant = current.variants[0]\n return {\n ...currentFields,\n ...productSearchFields({\n name: current.name.trim().slice(0, 120),\n variants: current.variants,\n }),\n slug: current.slug || commerceSlug(current.name),\n // The legacy flat price, only where there is one (AGL-3676): an owner\n // may save a product before pricing it, and an unpriced product is not\n // a free one. `null` rather than absent, so an edit's full replace and\n // the native save write the same field.\n priceUsd: variantHasPrice(primaryVariant) ? primaryVariant.priceUsd : null,\n ...productStockFields({\n variants: current.variants,\n oversellPolicy: current.oversellPolicy,\n }),\n imageUrl: current.mediaUrls?.[0] ?? current.imageUrl ?? null,\n updatedAtMs: nowMs,\n }\n}\n\n/** What the smart-collection rules are matched against: the saved name with the product's own facts. */\nexport function productMembershipInput(\n fields: { name: string },\n current: Pick<HostProduct, 'type' | 'tags' | 'categoryIds' | 'variants'>,\n) {\n return {\n name: fields.name,\n type: current.type,\n tags: current.tags,\n categoryIds: current.categoryIds,\n variants: current.variants,\n }\n}\n\n/**\n * An EDIT's replacing write, without its membership and server timestamp:\n * live stock holds are not the editor's to write (AGL-2356), so the seed's\n * are dropped and the stored ones carried; the product stays live\n * (`deletedAt: null`, AGL-3321).\n */\nexport function productEditWrite(\n fields: ReturnType<typeof productSaveFields>,\n liveHolds: Record<string, unknown> | null | undefined,\n) {\n const { stockHolds: _seededHolds, ...withoutHolds } = fields as typeof fields & {\n stockHolds?: Record<string, unknown>\n }\n return {\n ...withoutHolds,\n ...(liveHolds ? { stockHolds: liveHolds } : {}),\n deletedAt: null,\n }\n}\n\n/** A smart collection as `hosts/{hostId}/collections` stores it, as the membership rules read it. */\nexport function smartCollectionRulesOf(id: string, data: Record<string, unknown>): SmartCollectionRules {\n return {\n id,\n rules: (data['rules'] as SmartCollectionRules['rules']) ?? [],\n matchAll: data['matchAll'] as boolean | undefined,\n }\n}\n\n/** One stock adjustment, as the Adjust stock dialog asks for it. */\nexport interface StockAdjustment {\n variantId: string\n delta: number\n reason: InventoryAdjustmentReason\n locationId?: string\n}\n\n/**\n * The product update and the ledger row one stock adjustment writes: the\n * variants with the count moved, the stock fields that move with it\n * (AGL-3321), and the same `inventoryAdjustments` row the sale webhook writes\n * (AGL-281). A zero or non-numeric delta writes nothing.\n */\nexport function stockAdjustmentWrite(\n product: HostProduct,\n productId: string,\n adjustment: StockAdjustment,\n nowMs: number,\n): { update: Record<string, unknown>; ledger: InventoryAdjustment } | null {\n const delta = Math.round(Number(adjustment.delta))\n if (!delta) return null\n const variants = adjustVariantInventory(product, adjustment.variantId, delta, adjustment.locationId || undefined)\n return {\n update: {\n variants,\n ...productStockFields({ ...product, variants }),\n updatedAtMs: nowMs,\n },\n ledger: {\n productId,\n variantId: adjustment.variantId,\n delta,\n reason: adjustment.reason,\n ...(adjustment.locationId ? { locationId: adjustment.locationId } : {}),\n atMs: nowMs,\n },\n }\n}\n"],"names":["adjustVariantInventory","commerceSlug","productSearchFields","productStockFields","variantHasPrice","productSaveFields","current","nowMs","$id","_syntheticId","skus","_seededSkus","barcodes","_seededBarcodes","currentFields","primaryVariant","variants","name","trim","slice","slug","priceUsd","oversellPolicy","imageUrl","mediaUrls","updatedAtMs","productMembershipInput","fields","type","tags","categoryIds","productEditWrite","liveHolds","stockHolds","_seededHolds","withoutHolds","deletedAt","smartCollectionRulesOf","id","data","rules","matchAll","stockAdjustmentWrite","product","productId","adjustment","delta","Math","round","Number","variantId","locationId","undefined","update","ledger","reason","atMs"],"mappings":";;AAAA;;;;;;;;;;;;;;;CAeC,GAED,SACEA,sBAAsB,EACtBC,YAAY,EAIZC,mBAAmB,EACnBC,kBAAkB,EAElBC,eAAe,QACV,gBAAY;AAqBnB;;;;;;;;;;;;CAYC,GACD,OAAO,SAASC,kBAAkBC,OAAsB,EAAEC,KAAa;QAwBzDD;QAAAA;IAvBZ,MAAM,EACJE,KAAKC,YAAY,EACjBC,MAAMC,WAAW,EACjBC,UAAUC,eAAe,EAE1B,GAAGP,SADCQ,iDACDR;;;;;IACJ,MAAMS,iBAAiBT,QAAQU,QAAQ,CAAC,EAAE;IAC1C,OAAO,aACFF,eACAZ,oBAAoB;QACrBe,MAAMX,QAAQW,IAAI,CAACC,IAAI,GAAGC,KAAK,CAAC,GAAG;QACnCH,UAAUV,QAAQU,QAAQ;IAC5B;QACAI,MAAMd,QAAQc,IAAI,IAAInB,aAAaK,QAAQW,IAAI;QAC/C,sEAAsE;QACtE,uEAAuE;QACvE,uEAAuE;QACvE,wCAAwC;QACxCI,UAAUjB,gBAAgBW,kBAAkBA,eAAeM,QAAQ,GAAG;OACnElB,mBAAmB;QACpBa,UAAUV,QAAQU,QAAQ;QAC1BM,gBAAgBhB,QAAQgB,cAAc;IACxC;QACAC,QAAQ,GAAEjB,iBAAAA,qBAAAA,QAAQkB,SAAS,qBAAjBlB,kBAAmB,CAAC,EAAE,oBAAIA,QAAQiB,QAAQ,YAA1CjB,OAA8C;QACxDmB,aAAalB;;AAEjB;AAEA,sGAAsG,GACtG,OAAO,SAASmB,uBACdC,MAAwB,EACxBrB,OAAwE;IAExE,OAAO;QACLW,MAAMU,OAAOV,IAAI;QACjBW,MAAMtB,QAAQsB,IAAI;QAClBC,MAAMvB,QAAQuB,IAAI;QAClBC,aAAaxB,QAAQwB,WAAW;QAChCd,UAAUV,QAAQU,QAAQ;IAC5B;AACF;AAEA;;;;;CAKC,GACD,OAAO,SAASe,iBACdJ,MAA4C,EAC5CK,SAAqD;IAErD,MAAM,EAAEC,YAAYC,YAAY,EAAmB,GAAGP,QAAjBQ,gDAAiBR;;;IAGtD,OAAO,aACFQ,cACCH,YAAY;QAAEC,YAAYD;IAAU,IAAI,CAAC;QAC7CI,WAAW;;AAEf;AAEA,mGAAmG,GACnG,OAAO,SAASC,uBAAuBC,EAAU,EAAEC,IAA6B;QAGpEA;IAFV,OAAO;QACLD;QACAE,KAAK,GAAGD,cAAAA,IAAI,CAAC,QAAQ,YAAbA,cAAmD,EAAE;QAC7DE,UAAUF,IAAI,CAAC,WAAW;IAC5B;AACF;AAUA;;;;;CAKC,GACD,OAAO,SAASG,qBACdC,OAAoB,EACpBC,SAAiB,EACjBC,UAA2B,EAC3BtC,KAAa;IAEb,MAAMuC,QAAQC,KAAKC,KAAK,CAACC,OAAOJ,WAAWC,KAAK;IAChD,IAAI,CAACA,OAAO,OAAO;IACnB,MAAM9B,WAAWhB,uBAAuB2C,SAASE,WAAWK,SAAS,EAAEJ,OAAOD,WAAWM,UAAU,IAAIC;IACvG,OAAO;QACLC,QAAQ;YACNrC;WACGb,mBAAmB,aAAKwC;YAAS3B;;YACpCS,aAAalB;;QAEf+C,QAAQ;YACNV;YACAM,WAAWL,WAAWK,SAAS;YAC/BJ;YACAS,QAAQV,WAAWU,MAAM;WACrBV,WAAWM,UAAU,GAAG;YAAEA,YAAYN,WAAWM,UAAU;QAAC,IAAI,CAAC;YACrEK,MAAMjD;;IAEV;AACF"}
@@ -174,6 +174,7 @@ export const ORDERS_API_V1_DESCRIPTION = {
174
174
  }
175
175
  },
176
176
  refundedCents: int('Amount refunded so far.'),
177
+ livemode: bool('Whether real money moved: `false` for an order paid in Stripe test mode, such as a store rehearsal.'),
177
178
  disputed: bool('Whether a chargeback is open.'),
178
179
  taxMode: nullable(str('The tax regime: `stripe-automatic` (Stripe Tax, collected and remitted by the platform as marketplace facilitator), `manual` (your own rate) or `none`. `null` on an order from before it was recorded.')),
179
180
  taxEngine: nullable(objectOf('The tax service you connected, when one was asked for this sale’s tax: `provider` (`avalara` or `taxjar`), `providerLabel`, `status` (`quoted` when its tax was charged, `fallback` when it did not answer and your own rate was charged), `reason` (`timeout` or `error` on a fallback) and `sandbox`. `null` when no service was asked.')),
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../../libs/plugins/commerce/src/lib/server/api-v1/openapi.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 {\n type ApiV1ResourceDescription,\n booleanField as bool,\n integerField as int,\n nullableField as nullable,\n objectKindField as OBJECT_FIELD,\n openObjectField as objectOf,\n postalAddressField as ADDRESS,\n queryParam as q,\n RECORD_STAMPS as STAMPS,\n stringField as str,\n stringListField as strList,\n} from '@aglyn/tenant-data-admin/server/api-v1-description'\n\n/**\n * A site's products and orders as the customer API's OpenAPI document\n * describes them — what the console's builder turns into paths, tags and\n * schemas, and the MCP tools derive from. Transcribed unchanged from the\n * builder's own list when the resources moved here (AGL-3080);\n * `api-v1-openapi.spec.ts` holds the document to every endpoint the\n * documentation promises.\n */\nexport const PRODUCTS_API_V1_DESCRIPTION: ApiV1ResourceDescription = {\n tag: 'Products',\n description: 'A site’s catalog. Read-only over the API.',\n schemaName: 'Product',\n required: ['id', 'object', 'name'],\n fields: {\n id: str('Product id.'),\n object: OBJECT_FIELD('product'),\n name: str('Product name.'),\n slug: str('URL slug.'),\n description: nullable(str('Description.')),\n type: str('Product type.'),\n status: str('Publication status.'),\n tags: strList('Free-form tags.'),\n categoryIds: strList('Categories this product belongs to.'),\n mediaUrls: strList('Image URLs.'),\n options: { type: 'array', description: 'Option definitions, e.g. size.', items: { type: 'object', additionalProperties: true } },\n variants: { type: 'array', description: 'Purchasable variants.', items: { type: 'object', additionalProperties: true } },\n inventory: nullable(int('Stock on hand, when tracked.')),\n subscription: nullable(objectOf('Subscription terms, when the product is one.')),\n ...STAMPS,\n },\n ops: [\n { path: '/v1/sites/{siteId}/products', method: 'get', operationId: 'listProducts', summary: 'List products', list: true, returns: 'Product', description: 'Rows deleted since they were written are dropped after the read, so pages can come back short.', pathParams: [{ name: 'siteId', description: 'Site id.' }] },\n { path: '/v1/sites/{siteId}/products/{productId}', method: 'get', operationId: 'getProduct', summary: 'Retrieve a product', returns: 'Product', pathParams: [{ name: 'siteId', description: 'Site id.' }, { name: 'productId', description: 'Product id.' }] },\n ],\n}\n\nexport const ORDERS_API_V1_DESCRIPTION: ApiV1ResourceDescription = {\n tag: 'Orders',\n description: 'A site’s orders. Status moves are constrained.',\n schemaName: 'Order',\n required: ['id', 'object', 'number', 'status'],\n writable: ['status', 'carrier', 'trackingNumber', 'trackingUrl', 'lineItems', 'notify'],\n writeOnly: {\n status: { type: 'string', enum: ['fulfilled', 'delivered'], description: 'The status to move the order to.' },\n carrier: str('Free text, e.g. `UPS`. Trimmed to 40 characters. USPS, UPS, FedEx, DHL, Canada Post, Royal Mail and Australia Post get a tracking link.'),\n trackingNumber: str('Free text. Trimmed to 60 characters.'),\n trackingUrl: str('An https tracking link, for a carrier the link is not derived for.'),\n lineItems: {\n type: 'array',\n description:\n 'With `status: fulfilled`, the units this shipment covers: `[{ lineItemId, quantity }]`, where `lineItemId` is the line’s index. Omitted, the shipment covers everything still to ship. More than is left is `409 conflict` (`code: \"over_fulfilled\"`). Send an `Idempotency-Key` header so a retried partial shipment is recorded once.',\n items: { type: 'object', additionalProperties: true },\n },\n notify: bool('Whether the buyer is told about this shipment. Defaults to true.'),\n },\n writeRequired: ['status'],\n fields: {\n id: str('Order id.'),\n object: OBJECT_FIELD('order'),\n number: int('Human-facing order number.'),\n status: str('Order status.'),\n channel: str('`online` by default. Older orders carry no stored value, so `?channel=online` drops them after the read.'),\n currency: str('ISO 4217 code.'),\n customerEmail: nullable(str('Customer email.')),\n customerName: nullable(str('Customer name.')),\n lineItems: { type: 'array', description: 'What was bought.', items: { type: 'object', additionalProperties: true } },\n totals: objectOf('Money totals, in the smallest unit of `currency`. `extrasCents` is the optional lines the buyer added at checkout, inside `totalCents` and outside `itemsCents`.'),\n extras: { type: 'array', description: 'Optional lines the buyer added at checkout, such as package protection: `[{ id, pluginId, key, label, amountCents, quoteRef }]`. Empty when there were none.', items: { type: 'object', additionalProperties: true } },\n credits: { type: 'array', description: 'Store credit another plugin kept that paid part of the sale, such as rewards: `[{ providerId, pluginId, reference, label, last4, amountCents, appliedAs }]`. `appliedAs` is `discount` for a checkout redemption and `tender` for a register payment. Empty when there were none.', items: { type: 'object', additionalProperties: true } },\n refundedCents: int('Amount refunded so far.'),\n disputed: bool('Whether a chargeback is open.'),\n taxMode: nullable(str('The tax regime: `stripe-automatic` (Stripe Tax, collected and remitted by the platform as marketplace facilitator), `manual` (your own rate) or `none`. `null` on an order from before it was recorded.')),\n taxEngine: nullable(objectOf('The tax service you connected, when one was asked for this sale’s tax: `provider` (`avalara` or `taxjar`), `providerLabel`, `status` (`quoted` when its tax was charged, `fallback` when it did not answer and your own rate was charged), `reason` (`timeout` or `error` on a fallback) and `sandbox`. `null` when no service was asked.')),\n shippingAddress: ADDRESS(),\n couponCode: nullable(str('Coupon applied.')),\n fulfillments: { type: 'array', description: 'Shipments and deliveries.', items: { type: 'object', additionalProperties: true } },\n created: STAMPS.created,\n },\n ops: [\n { path: '/v1/sites/{siteId}/orders', method: 'get', operationId: 'listOrders', summary: 'List orders', list: true, returns: 'Order', filters: [q('channel', '`online` is a default rather than a stored value, so filtering on it drops older orders after the read.'), q('status', 'Order status.')], pathParams: [{ name: 'siteId', description: 'Site id.' }] },\n { path: '/v1/sites/{siteId}/orders/{orderId}', method: 'get', operationId: 'getOrder', summary: 'Retrieve an order', returns: 'Order', pathParams: [{ name: 'siteId', description: 'Site id.' }, { name: 'orderId', description: 'Order id.' }] },\n { path: '/v1/sites/{siteId}/orders/{orderId}', method: 'patch', operationId: 'updateOrder', summary: 'Move an order’s status', accepts: 'OrderWrite', returns: 'Order', description: 'An illegal transition is `409 conflict` (`code: \"order_transition\"`), and the message names the status it is in.', pathParams: [{ name: 'siteId', description: 'Site id.' }, { name: 'orderId', description: 'Order id.' }] },\n ],\n}\n"],"names":["booleanField","bool","integerField","int","nullableField","nullable","objectKindField","OBJECT_FIELD","openObjectField","objectOf","postalAddressField","ADDRESS","queryParam","q","RECORD_STAMPS","STAMPS","stringField","str","stringListField","strList","PRODUCTS_API_V1_DESCRIPTION","tag","description","schemaName","required","fields","id","object","name","slug","type","status","tags","categoryIds","mediaUrls","options","items","additionalProperties","variants","inventory","subscription","ops","path","method","operationId","summary","list","returns","pathParams","ORDERS_API_V1_DESCRIPTION","writable","writeOnly","enum","carrier","trackingNumber","trackingUrl","lineItems","notify","writeRequired","number","channel","currency","customerEmail","customerName","totals","extras","credits","refundedCents","disputed","taxMode","taxEngine","shippingAddress","couponCode","fulfillments","created","filters","accepts"],"mappings":";AAAA;;;;;;;;;;;;;;;CAeC,GAED,SAEEA,gBAAgBC,IAAI,EACpBC,gBAAgBC,GAAG,EACnBC,iBAAiBC,QAAQ,EACzBC,mBAAmBC,YAAY,EAC/BC,mBAAmBC,QAAQ,EAC3BC,sBAAsBC,OAAO,EAC7BC,cAAcC,CAAC,EACfC,iBAAiBC,MAAM,EACvBC,eAAeC,GAAG,EAClBC,mBAAmBC,OAAO,QACrB,qDAAoD;AAE3D;;;;;;;CAOC,GACD,OAAO,MAAMC,8BAAwD;IACnEC,KAAK;IACLC,aAAa;IACbC,YAAY;IACZC,UAAU;QAAC;QAAM;QAAU;KAAO;IAClCC,QAAQ;QACNC,IAAIT,IAAI;QACRU,QAAQpB,aAAa;QACrBqB,MAAMX,IAAI;QACVY,MAAMZ,IAAI;QACVK,aAAajB,SAASY,IAAI;QAC1Ba,MAAMb,IAAI;QACVc,QAAQd,IAAI;QACZe,MAAMb,QAAQ;QACdc,aAAad,QAAQ;QACrBe,WAAWf,QAAQ;QACnBgB,SAAS;YAAEL,MAAM;YAASR,aAAa;YAAkCc,OAAO;gBAAEN,MAAM;gBAAUO,sBAAsB;YAAK;QAAE;QAC/HC,UAAU;YAAER,MAAM;YAASR,aAAa;YAAyBc,OAAO;gBAAEN,MAAM;gBAAUO,sBAAsB;YAAK;QAAE;QACvHE,WAAWlC,SAASF,IAAI;QACxBqC,cAAcnC,SAASI,SAAS;OAC7BM;IAEL0B,KAAK;QACH;YAAEC,MAAM;YAA+BC,QAAQ;YAAOC,aAAa;YAAgBC,SAAS;YAAiBC,MAAM;YAAMC,SAAS;YAAWzB,aAAa;YAAkG0B,YAAY;gBAAC;oBAAEpB,MAAM;oBAAUN,aAAa;gBAAW;aAAE;QAAC;QACtT;YAAEoB,MAAM;YAA2CC,QAAQ;YAAOC,aAAa;YAAcC,SAAS;YAAsBE,SAAS;YAAWC,YAAY;gBAAC;oBAAEpB,MAAM;oBAAUN,aAAa;gBAAW;gBAAG;oBAAEM,MAAM;oBAAaN,aAAa;gBAAc;aAAE;QAAC;KAC9P;AACH,EAAC;AAED,OAAO,MAAM2B,4BAAsD;IACjE5B,KAAK;IACLC,aAAa;IACbC,YAAY;IACZC,UAAU;QAAC;QAAM;QAAU;QAAU;KAAS;IAC9C0B,UAAU;QAAC;QAAU;QAAW;QAAkB;QAAe;QAAa;KAAS;IACvFC,WAAW;QACTpB,QAAQ;YAAED,MAAM;YAAUsB,MAAM;gBAAC;gBAAa;aAAY;YAAE9B,aAAa;QAAmC;QAC5G+B,SAASpC,IAAI;QACbqC,gBAAgBrC,IAAI;QACpBsC,aAAatC,IAAI;QACjBuC,WAAW;YACT1B,MAAM;YACNR,aACE;YACFc,OAAO;gBAAEN,MAAM;gBAAUO,sBAAsB;YAAK;QACtD;QACAoB,QAAQxD,KAAK;IACf;IACAyD,eAAe;QAAC;KAAS;IACzBjC,QAAQ;QACNC,IAAIT,IAAI;QACRU,QAAQpB,aAAa;QACrBoD,QAAQxD,IAAI;QACZ4B,QAAQd,IAAI;QACZ2C,SAAS3C,IAAI;QACb4C,UAAU5C,IAAI;QACd6C,eAAezD,SAASY,IAAI;QAC5B8C,cAAc1D,SAASY,IAAI;QAC3BuC,WAAW;YAAE1B,MAAM;YAASR,aAAa;YAAoBc,OAAO;gBAAEN,MAAM;gBAAUO,sBAAsB;YAAK;QAAE;QACnH2B,QAAQvD,SAAS;QACjBwD,QAAQ;YAAEnC,MAAM;YAASR,aAAa;YAAgKc,OAAO;gBAAEN,MAAM;gBAAUO,sBAAsB;YAAK;QAAE;QAC5P6B,SAAS;YAAEpC,MAAM;YAASR,aAAa;YAAqRc,OAAO;gBAAEN,MAAM;gBAAUO,sBAAsB;YAAK;QAAE;QAClX8B,eAAehE,IAAI;QACnBiE,UAAUnE,KAAK;QACfoE,SAAShE,SAASY,IAAI;QACtBqD,WAAWjE,SAASI,SAAS;QAC7B8D,iBAAiB5D;QACjB6D,YAAYnE,SAASY,IAAI;QACzBwD,cAAc;YAAE3C,MAAM;YAASR,aAAa;YAA6Bc,OAAO;gBAAEN,MAAM;gBAAUO,sBAAsB;YAAK;QAAE;QAC/HqC,SAAS3D,OAAO2D,OAAO;IACzB;IACAjC,KAAK;QACH;YAAEC,MAAM;YAA6BC,QAAQ;YAAOC,aAAa;YAAcC,SAAS;YAAeC,MAAM;YAAMC,SAAS;YAAS4B,SAAS;gBAAC9D,EAAE,WAAW;gBAA4GA,EAAE,UAAU;aAAiB;YAAEmC,YAAY;gBAAC;oBAAEpB,MAAM;oBAAUN,aAAa;gBAAW;aAAE;QAAC;QACjW;YAAEoB,MAAM;YAAuCC,QAAQ;YAAOC,aAAa;YAAYC,SAAS;YAAqBE,SAAS;YAASC,YAAY;gBAAC;oBAAEpB,MAAM;oBAAUN,aAAa;gBAAW;gBAAG;oBAAEM,MAAM;oBAAWN,aAAa;gBAAY;aAAE;QAAC;QAChP;YAAEoB,MAAM;YAAuCC,QAAQ;YAASC,aAAa;YAAeC,SAAS;YAA0B+B,SAAS;YAAc7B,SAAS;YAASzB,aAAa;YAAoH0B,YAAY;gBAAC;oBAAEpB,MAAM;oBAAUN,aAAa;gBAAW;gBAAG;oBAAEM,MAAM;oBAAWN,aAAa;gBAAY;aAAE;QAAC;KACnZ;AACH,EAAC"}
1
+ {"version":3,"sources":["../../../../../../../../libs/plugins/commerce/src/lib/server/api-v1/openapi.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 {\n type ApiV1ResourceDescription,\n booleanField as bool,\n integerField as int,\n nullableField as nullable,\n objectKindField as OBJECT_FIELD,\n openObjectField as objectOf,\n postalAddressField as ADDRESS,\n queryParam as q,\n RECORD_STAMPS as STAMPS,\n stringField as str,\n stringListField as strList,\n} from '@aglyn/tenant-data-admin/server/api-v1-description'\n\n/**\n * A site's products and orders as the customer API's OpenAPI document\n * describes them — what the console's builder turns into paths, tags and\n * schemas, and the MCP tools derive from. Transcribed unchanged from the\n * builder's own list when the resources moved here (AGL-3080);\n * `api-v1-openapi.spec.ts` holds the document to every endpoint the\n * documentation promises.\n */\nexport const PRODUCTS_API_V1_DESCRIPTION: ApiV1ResourceDescription = {\n tag: 'Products',\n description: 'A site’s catalog. Read-only over the API.',\n schemaName: 'Product',\n required: ['id', 'object', 'name'],\n fields: {\n id: str('Product id.'),\n object: OBJECT_FIELD('product'),\n name: str('Product name.'),\n slug: str('URL slug.'),\n description: nullable(str('Description.')),\n type: str('Product type.'),\n status: str('Publication status.'),\n tags: strList('Free-form tags.'),\n categoryIds: strList('Categories this product belongs to.'),\n mediaUrls: strList('Image URLs.'),\n options: { type: 'array', description: 'Option definitions, e.g. size.', items: { type: 'object', additionalProperties: true } },\n variants: { type: 'array', description: 'Purchasable variants.', items: { type: 'object', additionalProperties: true } },\n inventory: nullable(int('Stock on hand, when tracked.')),\n subscription: nullable(objectOf('Subscription terms, when the product is one.')),\n ...STAMPS,\n },\n ops: [\n { path: '/v1/sites/{siteId}/products', method: 'get', operationId: 'listProducts', summary: 'List products', list: true, returns: 'Product', description: 'Rows deleted since they were written are dropped after the read, so pages can come back short.', pathParams: [{ name: 'siteId', description: 'Site id.' }] },\n { path: '/v1/sites/{siteId}/products/{productId}', method: 'get', operationId: 'getProduct', summary: 'Retrieve a product', returns: 'Product', pathParams: [{ name: 'siteId', description: 'Site id.' }, { name: 'productId', description: 'Product id.' }] },\n ],\n}\n\nexport const ORDERS_API_V1_DESCRIPTION: ApiV1ResourceDescription = {\n tag: 'Orders',\n description: 'A site’s orders. Status moves are constrained.',\n schemaName: 'Order',\n required: ['id', 'object', 'number', 'status'],\n writable: ['status', 'carrier', 'trackingNumber', 'trackingUrl', 'lineItems', 'notify'],\n writeOnly: {\n status: { type: 'string', enum: ['fulfilled', 'delivered'], description: 'The status to move the order to.' },\n carrier: str('Free text, e.g. `UPS`. Trimmed to 40 characters. USPS, UPS, FedEx, DHL, Canada Post, Royal Mail and Australia Post get a tracking link.'),\n trackingNumber: str('Free text. Trimmed to 60 characters.'),\n trackingUrl: str('An https tracking link, for a carrier the link is not derived for.'),\n lineItems: {\n type: 'array',\n description:\n 'With `status: fulfilled`, the units this shipment covers: `[{ lineItemId, quantity }]`, where `lineItemId` is the line’s index. Omitted, the shipment covers everything still to ship. More than is left is `409 conflict` (`code: \"over_fulfilled\"`). Send an `Idempotency-Key` header so a retried partial shipment is recorded once.',\n items: { type: 'object', additionalProperties: true },\n },\n notify: bool('Whether the buyer is told about this shipment. Defaults to true.'),\n },\n writeRequired: ['status'],\n fields: {\n id: str('Order id.'),\n object: OBJECT_FIELD('order'),\n number: int('Human-facing order number.'),\n status: str('Order status.'),\n channel: str('`online` by default. Older orders carry no stored value, so `?channel=online` drops them after the read.'),\n currency: str('ISO 4217 code.'),\n customerEmail: nullable(str('Customer email.')),\n customerName: nullable(str('Customer name.')),\n lineItems: { type: 'array', description: 'What was bought.', items: { type: 'object', additionalProperties: true } },\n totals: objectOf('Money totals, in the smallest unit of `currency`. `extrasCents` is the optional lines the buyer added at checkout, inside `totalCents` and outside `itemsCents`.'),\n extras: { type: 'array', description: 'Optional lines the buyer added at checkout, such as package protection: `[{ id, pluginId, key, label, amountCents, quoteRef }]`. Empty when there were none.', items: { type: 'object', additionalProperties: true } },\n credits: { type: 'array', description: 'Store credit another plugin kept that paid part of the sale, such as rewards: `[{ providerId, pluginId, reference, label, last4, amountCents, appliedAs }]`. `appliedAs` is `discount` for a checkout redemption and `tender` for a register payment. Empty when there were none.', items: { type: 'object', additionalProperties: true } },\n refundedCents: int('Amount refunded so far.'),\n livemode: bool('Whether real money moved: `false` for an order paid in Stripe test mode, such as a store rehearsal.'),\n disputed: bool('Whether a chargeback is open.'),\n taxMode: nullable(str('The tax regime: `stripe-automatic` (Stripe Tax, collected and remitted by the platform as marketplace facilitator), `manual` (your own rate) or `none`. `null` on an order from before it was recorded.')),\n taxEngine: nullable(objectOf('The tax service you connected, when one was asked for this sale’s tax: `provider` (`avalara` or `taxjar`), `providerLabel`, `status` (`quoted` when its tax was charged, `fallback` when it did not answer and your own rate was charged), `reason` (`timeout` or `error` on a fallback) and `sandbox`. `null` when no service was asked.')),\n shippingAddress: ADDRESS(),\n couponCode: nullable(str('Coupon applied.')),\n fulfillments: { type: 'array', description: 'Shipments and deliveries.', items: { type: 'object', additionalProperties: true } },\n created: STAMPS.created,\n },\n ops: [\n { path: '/v1/sites/{siteId}/orders', method: 'get', operationId: 'listOrders', summary: 'List orders', list: true, returns: 'Order', filters: [q('channel', '`online` is a default rather than a stored value, so filtering on it drops older orders after the read.'), q('status', 'Order status.')], pathParams: [{ name: 'siteId', description: 'Site id.' }] },\n { path: '/v1/sites/{siteId}/orders/{orderId}', method: 'get', operationId: 'getOrder', summary: 'Retrieve an order', returns: 'Order', pathParams: [{ name: 'siteId', description: 'Site id.' }, { name: 'orderId', description: 'Order id.' }] },\n { path: '/v1/sites/{siteId}/orders/{orderId}', method: 'patch', operationId: 'updateOrder', summary: 'Move an order’s status', accepts: 'OrderWrite', returns: 'Order', description: 'An illegal transition is `409 conflict` (`code: \"order_transition\"`), and the message names the status it is in.', pathParams: [{ name: 'siteId', description: 'Site id.' }, { name: 'orderId', description: 'Order id.' }] },\n ],\n}\n"],"names":["booleanField","bool","integerField","int","nullableField","nullable","objectKindField","OBJECT_FIELD","openObjectField","objectOf","postalAddressField","ADDRESS","queryParam","q","RECORD_STAMPS","STAMPS","stringField","str","stringListField","strList","PRODUCTS_API_V1_DESCRIPTION","tag","description","schemaName","required","fields","id","object","name","slug","type","status","tags","categoryIds","mediaUrls","options","items","additionalProperties","variants","inventory","subscription","ops","path","method","operationId","summary","list","returns","pathParams","ORDERS_API_V1_DESCRIPTION","writable","writeOnly","enum","carrier","trackingNumber","trackingUrl","lineItems","notify","writeRequired","number","channel","currency","customerEmail","customerName","totals","extras","credits","refundedCents","livemode","disputed","taxMode","taxEngine","shippingAddress","couponCode","fulfillments","created","filters","accepts"],"mappings":"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"}
@@ -56,6 +56,7 @@ export declare function orderViewFromData(id: string, data: Record<string, any>)
56
56
  appliedAs: "discount" | "tender";
57
57
  }[];
58
58
  refundedCents: number;
59
+ livemode: boolean;
59
60
  taxMode: string;
60
61
  taxEngine: import("../../model/commerce-tax-engine").OrderTaxEngineStamp;
61
62
  disputed: boolean;
@@ -17,6 +17,7 @@
17
17
  import { fulfillmentLineQuantities } from "../../model/order-fulfillment.js";
18
18
  import { fulfillmentTrackingUrl } from "../../model/tracking-url.js";
19
19
  import { orderTaxEngineView } from "../../model/commerce-tax-engine.js";
20
+ import { orderIsTestMode } from "../../model/commerce-orders.js";
20
21
  /**
21
22
  * The public order shape from an order's data (AGL-3611): the same object the
22
23
  * API returns, so an order event and an outbound webhook carry exactly what
@@ -72,6 +73,15 @@ import { orderTaxEngineView } from "../../model/commerce-tax-engine.js";
72
73
  // Money already handed back, for any reason. A chargeback lands here too,
73
74
  // so `refundedCents > 0` does not by itself mean the merchant chose it.
74
75
  refundedCents: Number((_data_refundedCents = data.refundedCents) != null ? _data_refundedCents : 0),
76
+ // Whether real money moved (AGL-3699): `false` for a Stripe test-mode
77
+ // order, so a subscriber that tells an outside service about a sale —
78
+ // a review invitation, a ledger — can leave a store's rehearsal out.
79
+ // The seller's own rule: the recorded fact, else the session id.
80
+ livemode: !orderIsTestMode({
81
+ livemode: data.livemode,
82
+ checkoutSessionId: data.checkoutSessionId,
83
+ $id: id
84
+ }),
75
85
  // Who the tax was charged under (AGL-3614): `stripe-automatic` tax is
76
86
  // Aglyn's to remit as marketplace facilitator and never reaches the
77
87
  // merchant's account, which a ledger posting the sale has to know. An
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../../libs/plugins/commerce/src/lib/server/api-v1/order-view.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 { serialize } from '@aglyn/tenant-data-admin/server/api-v1-kit'\nimport { fulfillmentLineQuantities } from '../../model/order-fulfillment'\nimport { fulfillmentTrackingUrl } from '../../model/tracking-url'\nimport { orderTaxEngineView } from '../../model/commerce-tax-engine'\n\n/**\n * The public order shape from an order's data (AGL-3611): the same object the\n * API returns, so an order event and an outbound webhook carry exactly what\n * `GET /v1/sites/{id}/orders/{id}` would.\n */\nexport function orderViewFromData(id: string, data: Record<string, any>) {\n const totals = (data.totals ?? {}) as Record<string, unknown>\n const legacyTotal = Number(data.amountCents ?? NaN)\n const totalCents = Number.isFinite(Number(totals.totalCents))\n ? Number(totals.totalCents)\n : Number.isFinite(legacyTotal)\n ? legacyTotal\n : null\n const legacyFee = Number(data.feeCents ?? NaN)\n return {\n id,\n object: 'order',\n number: typeof data.number === 'number' ? data.number : null,\n status: (data.status as string) ?? null,\n channel: (data.channel as string) ?? 'online',\n currency: 'usd',\n customerEmail: data.customerEmail ?? null,\n customerName: data.customerName ?? null,\n lineItems: serialize(data.lineItems ?? []),\n totals: {\n itemsCents: Number(totals.itemsCents ?? 0),\n shippingCents: Number(totals.shippingCents ?? 0),\n taxCents: Number(totals.taxCents ?? 0),\n discountCents: Number(totals.discountCents ?? 0),\n totalCents,\n // The Connect application fee. NOT subtracted from `totalCents` — it is\n // Aglyn's cut of a total the shopper already paid in full, so a client\n // that nets it out of revenue would understate what it collected.\n feeCents: Number.isFinite(Number(totals.feeCents))\n ? Number(totals.feeCents)\n : Number.isFinite(legacyFee)\n ? legacyFee\n : 0,\n // Optional lines the buyer added at checkout (AGL-3635), inside\n // `totalCents` and outside `itemsCents`.\n extrasCents: Number(totals.extrasCents ?? 0) || 0,\n },\n // What those lines were and whose offer each was: the fact a plugin that\n // insures a parcel reads off `order.paid` to know it was bought.\n extras: (Array.isArray(data.extras) ? data.extras : [])\n .filter((entry: Record<string, unknown>) => entry && typeof entry.id === 'string')\n .map((entry: Record<string, unknown>) => ({\n id: String(entry.id),\n pluginId: String(entry.pluginId ?? ''),\n key: String(entry.key ?? ''),\n label: String(entry.label ?? ''),\n amountCents: Number(entry.amountCents ?? 0) || 0,\n quoteRef: typeof entry.quoteRef === 'string' ? entry.quoteRef : null,\n })),\n // Store credit another plugin kept that paid part of the sale (AGL-3640):\n // a rewards redemption online comes off the goods (`discount`), one at\n // the register is one of its payments (`tender`). The provider reads its\n // own `reference` back off `order.paid` and `order.refunded`.\n credits: orderCreditsView(data),\n // Money already handed back, for any reason. A chargeback lands here too,\n // so `refundedCents > 0` does not by itself mean the merchant chose it.\n refundedCents: Number(data.refundedCents ?? 0),\n // Who the tax was charged under (AGL-3614): `stripe-automatic` tax is\n // Aglyn's to remit as marketplace facilitator and never reaches the\n // merchant's account, which a ledger posting the sale has to know. An\n // order from before the regime was recorded says `null`, never a guess.\n taxMode: typeof data.taxMode === 'string' && data.taxMode ? data.taxMode : null,\n // The merchant's own tax service (AGL-3631): which one priced the sale, or\n // that it was asked and the store's own rates were charged instead\n // (`status: 'fallback'`). `null` when no service was asked. The regime is\n // still `taxMode` — a service the merchant connected answers for tax the\n // merchant remits.\n taxEngine: orderTaxEngineView(data.taxEngine),\n disputed: Boolean(data.dispute),\n shippingAddress: serialize(data.shippingAddress) ?? null,\n couponCode: data.couponCode ?? null,\n // Shipment records — the half of fulfilment an integration can use\n // without a write (AGL-2460). `status` says an order is `fulfilled`; it\n // does not say which carrier took it or under what tracking number, so a\n // 3PL or accounting reconcile could see THAT an order shipped and never\n // WHICH shipment it was. The console's order dialog shows both, and an\n // order that has been shipped twice (a split shipment) is indistinguish-\n // able from one shipped once when only the status is published.\n //\n // `atMs` is a number of milliseconds, not a Firestore Timestamp, so\n // `serialize` passes it through untouched. It is republished as `at` in\n // ISO 8601 to match `created` and every other time this API emits: one\n // object publishing two time formats is a bug an integrator finds late,\n // in their own timezone conversion, and blames on their own code.\n fulfillments: (Array.isArray(data.fulfillments) ? data.fulfillments : []).map(\n (entry: Record<string, unknown>) => {\n const atMs = Number((entry ?? {}).atMs)\n return {\n id: (entry ?? {}).id ?? null,\n lineItemIds: Array.isArray((entry ?? {}).lineItemIds)\n ? (entry as { lineItemIds: unknown[] }).lineItemIds\n : [],\n // Units per line (AGL-3611); a fulfillment recorded before\n // quantities shipped every unit of the lines it names.\n lines: fulfillmentLineQuantities(\n { lineItems: Array.isArray(data.lineItems) ? data.lineItems : [] },\n (entry ?? {}) as never,\n ),\n status: (entry ?? {}).status === 'cancelled' ? 'cancelled' : 'active',\n carrier: (entry ?? {}).carrier ?? null,\n trackingNumber: (entry ?? {}).trackingNumber ?? null,\n trackingUrl: fulfillmentTrackingUrl((entry ?? {}) as never),\n labelUrl: (entry ?? {}).labelUrl ?? null,\n at: Number.isFinite(atMs) ? new Date(atMs).toISOString() : null,\n }\n },\n ),\n created: serialize(data.createdAt) ?? null,\n }\n}\n\n/** The plugin credits an order carries, online and at the register (AGL-3640). */\nexport function orderCreditsView(data: Record<string, any>): Array<{\n providerId: string\n pluginId: string\n reference: string\n label: string\n last4: string\n amountCents: number\n appliedAs: 'discount' | 'tender'\n}> {\n const online = (Array.isArray(data.credits) ? data.credits : [])\n .filter((entry: Record<string, unknown>) => entry && typeof entry.providerId === 'string')\n .map((entry: Record<string, unknown>) => ({\n providerId: String(entry.providerId),\n pluginId: String(entry.pluginId ?? String(entry.providerId).split('.')[0]),\n reference: String(entry.reference ?? ''),\n label: String(entry.label ?? ''),\n last4: String(entry.last4 ?? ''),\n amountCents: Math.max(0, Math.trunc(Number(entry.amountCents) || 0)),\n appliedAs: entry.appliedAs === 'tender' ? ('tender' as const) : ('discount' as const),\n }))\n const register = (Array.isArray(data.payments) ? data.payments : [])\n .filter(\n (payment: Record<string, unknown>) =>\n payment &&\n payment.method === 'credit' &&\n payment.status === 'succeeded' &&\n typeof payment.creditProviderId === 'string',\n )\n .map((payment: Record<string, unknown>) => ({\n providerId: String(payment.creditProviderId),\n pluginId: String(payment.creditProviderId).split('.')[0],\n reference: String(payment.creditReference ?? ''),\n label: String(payment.creditLabel ?? ''),\n last4: String(payment.last4 ?? ''),\n amountCents: Math.max(0, Math.trunc(Number(payment.amountCents) || 0)),\n appliedAs: 'tender' as const,\n }))\n return [...online, ...register].filter((credit) => credit.reference && credit.amountCents > 0)\n}\n"],"names":["serialize","fulfillmentLineQuantities","fulfillmentTrackingUrl","orderTaxEngineView","orderViewFromData","id","data","totals","legacyTotal","Number","amountCents","NaN","totalCents","isFinite","legacyFee","feeCents","object","number","status","channel","currency","customerEmail","customerName","lineItems","itemsCents","shippingCents","taxCents","discountCents","extrasCents","extras","Array","isArray","filter","entry","map","String","pluginId","key","label","quoteRef","credits","orderCreditsView","refundedCents","taxMode","taxEngine","disputed","Boolean","dispute","shippingAddress","couponCode","fulfillments","atMs","lineItemIds","lines","carrier","trackingNumber","trackingUrl","labelUrl","at","Date","toISOString","created","createdAt","online","providerId","split","reference","last4","Math","max","trunc","appliedAs","register","payments","payment","method","creditProviderId","creditReference","creditLabel","credit"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../../../../../libs/plugins/commerce/src/lib/server/api-v1/order-view.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 { serialize } from '@aglyn/tenant-data-admin/server/api-v1-kit'\nimport { fulfillmentLineQuantities } from '../../model/order-fulfillment'\nimport { fulfillmentTrackingUrl } from '../../model/tracking-url'\nimport { orderTaxEngineView } from '../../model/commerce-tax-engine'\nimport { orderIsTestMode } from '../../model/commerce-orders'\n\n/**\n * The public order shape from an order's data (AGL-3611): the same object the\n * API returns, so an order event and an outbound webhook carry exactly what\n * `GET /v1/sites/{id}/orders/{id}` would.\n */\nexport function orderViewFromData(id: string, data: Record<string, any>) {\n const totals = (data.totals ?? {}) as Record<string, unknown>\n const legacyTotal = Number(data.amountCents ?? NaN)\n const totalCents = Number.isFinite(Number(totals.totalCents))\n ? Number(totals.totalCents)\n : Number.isFinite(legacyTotal)\n ? legacyTotal\n : null\n const legacyFee = Number(data.feeCents ?? NaN)\n return {\n id,\n object: 'order',\n number: typeof data.number === 'number' ? data.number : null,\n status: (data.status as string) ?? null,\n channel: (data.channel as string) ?? 'online',\n currency: 'usd',\n customerEmail: data.customerEmail ?? null,\n customerName: data.customerName ?? null,\n lineItems: serialize(data.lineItems ?? []),\n totals: {\n itemsCents: Number(totals.itemsCents ?? 0),\n shippingCents: Number(totals.shippingCents ?? 0),\n taxCents: Number(totals.taxCents ?? 0),\n discountCents: Number(totals.discountCents ?? 0),\n totalCents,\n // The Connect application fee. NOT subtracted from `totalCents` — it is\n // Aglyn's cut of a total the shopper already paid in full, so a client\n // that nets it out of revenue would understate what it collected.\n feeCents: Number.isFinite(Number(totals.feeCents))\n ? Number(totals.feeCents)\n : Number.isFinite(legacyFee)\n ? legacyFee\n : 0,\n // Optional lines the buyer added at checkout (AGL-3635), inside\n // `totalCents` and outside `itemsCents`.\n extrasCents: Number(totals.extrasCents ?? 0) || 0,\n },\n // What those lines were and whose offer each was: the fact a plugin that\n // insures a parcel reads off `order.paid` to know it was bought.\n extras: (Array.isArray(data.extras) ? data.extras : [])\n .filter((entry: Record<string, unknown>) => entry && typeof entry.id === 'string')\n .map((entry: Record<string, unknown>) => ({\n id: String(entry.id),\n pluginId: String(entry.pluginId ?? ''),\n key: String(entry.key ?? ''),\n label: String(entry.label ?? ''),\n amountCents: Number(entry.amountCents ?? 0) || 0,\n quoteRef: typeof entry.quoteRef === 'string' ? entry.quoteRef : null,\n })),\n // Store credit another plugin kept that paid part of the sale (AGL-3640):\n // a rewards redemption online comes off the goods (`discount`), one at\n // the register is one of its payments (`tender`). The provider reads its\n // own `reference` back off `order.paid` and `order.refunded`.\n credits: orderCreditsView(data),\n // Money already handed back, for any reason. A chargeback lands here too,\n // so `refundedCents > 0` does not by itself mean the merchant chose it.\n refundedCents: Number(data.refundedCents ?? 0),\n // Whether real money moved (AGL-3699): `false` for a Stripe test-mode\n // order, so a subscriber that tells an outside service about a sale —\n // a review invitation, a ledger — can leave a store's rehearsal out.\n // The seller's own rule: the recorded fact, else the session id.\n livemode: !orderIsTestMode({\n livemode: data.livemode,\n checkoutSessionId: data.checkoutSessionId,\n $id: id,\n }),\n // Who the tax was charged under (AGL-3614): `stripe-automatic` tax is\n // Aglyn's to remit as marketplace facilitator and never reaches the\n // merchant's account, which a ledger posting the sale has to know. An\n // order from before the regime was recorded says `null`, never a guess.\n taxMode: typeof data.taxMode === 'string' && data.taxMode ? data.taxMode : null,\n // The merchant's own tax service (AGL-3631): which one priced the sale, or\n // that it was asked and the store's own rates were charged instead\n // (`status: 'fallback'`). `null` when no service was asked. The regime is\n // still `taxMode` — a service the merchant connected answers for tax the\n // merchant remits.\n taxEngine: orderTaxEngineView(data.taxEngine),\n disputed: Boolean(data.dispute),\n shippingAddress: serialize(data.shippingAddress) ?? null,\n couponCode: data.couponCode ?? null,\n // Shipment records — the half of fulfilment an integration can use\n // without a write (AGL-2460). `status` says an order is `fulfilled`; it\n // does not say which carrier took it or under what tracking number, so a\n // 3PL or accounting reconcile could see THAT an order shipped and never\n // WHICH shipment it was. The console's order dialog shows both, and an\n // order that has been shipped twice (a split shipment) is indistinguish-\n // able from one shipped once when only the status is published.\n //\n // `atMs` is a number of milliseconds, not a Firestore Timestamp, so\n // `serialize` passes it through untouched. It is republished as `at` in\n // ISO 8601 to match `created` and every other time this API emits: one\n // object publishing two time formats is a bug an integrator finds late,\n // in their own timezone conversion, and blames on their own code.\n fulfillments: (Array.isArray(data.fulfillments) ? data.fulfillments : []).map(\n (entry: Record<string, unknown>) => {\n const atMs = Number((entry ?? {}).atMs)\n return {\n id: (entry ?? {}).id ?? null,\n lineItemIds: Array.isArray((entry ?? {}).lineItemIds)\n ? (entry as { lineItemIds: unknown[] }).lineItemIds\n : [],\n // Units per line (AGL-3611); a fulfillment recorded before\n // quantities shipped every unit of the lines it names.\n lines: fulfillmentLineQuantities(\n { lineItems: Array.isArray(data.lineItems) ? data.lineItems : [] },\n (entry ?? {}) as never,\n ),\n status: (entry ?? {}).status === 'cancelled' ? 'cancelled' : 'active',\n carrier: (entry ?? {}).carrier ?? null,\n trackingNumber: (entry ?? {}).trackingNumber ?? null,\n trackingUrl: fulfillmentTrackingUrl((entry ?? {}) as never),\n labelUrl: (entry ?? {}).labelUrl ?? null,\n at: Number.isFinite(atMs) ? new Date(atMs).toISOString() : null,\n }\n },\n ),\n created: serialize(data.createdAt) ?? null,\n }\n}\n\n/** The plugin credits an order carries, online and at the register (AGL-3640). */\nexport function orderCreditsView(data: Record<string, any>): Array<{\n providerId: string\n pluginId: string\n reference: string\n label: string\n last4: string\n amountCents: number\n appliedAs: 'discount' | 'tender'\n}> {\n const online = (Array.isArray(data.credits) ? data.credits : [])\n .filter((entry: Record<string, unknown>) => entry && typeof entry.providerId === 'string')\n .map((entry: Record<string, unknown>) => ({\n providerId: String(entry.providerId),\n pluginId: String(entry.pluginId ?? String(entry.providerId).split('.')[0]),\n reference: String(entry.reference ?? ''),\n label: String(entry.label ?? ''),\n last4: String(entry.last4 ?? ''),\n amountCents: Math.max(0, Math.trunc(Number(entry.amountCents) || 0)),\n appliedAs: entry.appliedAs === 'tender' ? ('tender' as const) : ('discount' as const),\n }))\n const register = (Array.isArray(data.payments) ? data.payments : [])\n .filter(\n (payment: Record<string, unknown>) =>\n payment &&\n payment.method === 'credit' &&\n payment.status === 'succeeded' &&\n typeof payment.creditProviderId === 'string',\n )\n .map((payment: Record<string, unknown>) => ({\n providerId: String(payment.creditProviderId),\n pluginId: String(payment.creditProviderId).split('.')[0],\n reference: String(payment.creditReference ?? ''),\n label: String(payment.creditLabel ?? ''),\n last4: String(payment.last4 ?? ''),\n amountCents: Math.max(0, Math.trunc(Number(payment.amountCents) || 0)),\n appliedAs: 'tender' as const,\n }))\n return [...online, ...register].filter((credit) => credit.reference && credit.amountCents > 0)\n}\n"],"names":["serialize","fulfillmentLineQuantities","fulfillmentTrackingUrl","orderTaxEngineView","orderIsTestMode","orderViewFromData","id","data","totals","legacyTotal","Number","amountCents","NaN","totalCents","isFinite","legacyFee","feeCents","object","number","status","channel","currency","customerEmail","customerName","lineItems","itemsCents","shippingCents","taxCents","discountCents","extrasCents","extras","Array","isArray","filter","entry","map","String","pluginId","key","label","quoteRef","credits","orderCreditsView","refundedCents","livemode","checkoutSessionId","$id","taxMode","taxEngine","disputed","Boolean","dispute","shippingAddress","couponCode","fulfillments","atMs","lineItemIds","lines","carrier","trackingNumber","trackingUrl","labelUrl","at","Date","toISOString","created","createdAt","online","providerId","split","reference","last4","Math","max","trunc","appliedAs","register","payments","payment","method","creditProviderId","creditReference","creditLabel","credit"],"mappings":"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"}
@@ -16,6 +16,7 @@ import { _ as _extends } from "@swc/helpers/_/_extends";
16
16
  * limitations under the License.
17
17
  */ import { randomUUID } from "crypto";
18
18
  import { readSiteReturnHost, siteReturnUrl } from "@aglyn/aglyn/app-utils/site-return-url";
19
+ import { advertisingRequestFacts, recordAdvertisingOrderConsent } from "@aglyn/aglyn/plugin-manager/plugin-advertising-conversions";
19
20
  import * as Aglyn from "@aglyn/aglyn/server";
20
21
  import * as CommerceModel from "../model/index.js";
21
22
  import { checkoutRecoveryState } from "../model/checkout-recovery.js";
@@ -103,7 +104,7 @@ import { boundedCreditCents, checkoutCreditProviderForCode, encodeCheckoutCredit
103
104
  if (held) await held.release();
104
105
  };
105
106
  try {
106
- var _cartSnapshot_data, _ref1, _ref2, _storeSettings_get, _storeSettings_get1, _req_headers_referer, _req_headers_host, _ref3, _ref4;
107
+ var _cartSnapshot_data, _ref1, _ref2, _storeSettings_get, _storeSettings_get1, _req_headers_referer, _req_headers_host, _ref3, _ref4, _session_id;
107
108
  var _ownerOrg_org, _ownerOrg_org1, _ownerOrg_org2, _engineTax_quote;
108
109
  const firestore = firebaseAdmin.app().firestore();
109
110
  const hostRef = firestore.collection('hosts').doc(hostId);
@@ -1356,6 +1357,13 @@ import { boundedCreditCents, checkoutCreditProviderForCode, encodeCheckoutCredit
1356
1357
  items: startedItems
1357
1358
  });
1358
1359
  }
1360
+ // The shopper's advertising consent, for the order this session may
1361
+ // become (AGL-3694) — see `checkout.ts`. Never fails the checkout.
1362
+ await recordAdvertisingOrderConsent(_extends({
1363
+ hostId: String(hostId),
1364
+ orderKey: String((_session_id = session.id) != null ? _session_id : ''),
1365
+ wire: body.adConsent
1366
+ }, advertisingRequestFacts(req.headers)));
1359
1367
  await claim.record(200, payload);
1360
1368
  return res.status(200).json(payload);
1361
1369
  } catch (error) {