@mercurjs/docs 2.3.8-canary.3 → 2.3.8-canary.4

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 (113) hide show
  1. package/content/learn/architecture.mdx +166 -263
  2. package/content/platform/attribute/concepts/attribute-types.mdx +73 -46
  3. package/content/platform/attribute/concepts/global-vs-inline.mdx +54 -32
  4. package/content/platform/attribute/concepts/variant-axes.mdx +54 -32
  5. package/content/platform/attribute/guides/attach-attributes-to-a-product.mdx +96 -53
  6. package/content/platform/attribute/guides/create-a-variant-axis.mdx +106 -57
  7. package/content/platform/attribute/guides/create-an-attribute.mdx +105 -56
  8. package/content/platform/attribute/overview.mdx +37 -33
  9. package/content/platform/attribute/reference/data-models.mdx +53 -38
  10. package/content/platform/attribute/reference/events.mdx +47 -24
  11. package/content/platform/attribute/reference/links.mdx +98 -27
  12. package/content/platform/attribute/reference/service.mdx +71 -32
  13. package/content/platform/attribute/reference/workflows.mdx +158 -28
  14. package/content/platform/catalog/concepts/master-products.mdx +39 -37
  15. package/content/platform/catalog/concepts/product-seller-allowlist.mdx +56 -35
  16. package/content/platform/catalog/concepts/status-lifecycle.mdx +55 -49
  17. package/content/platform/catalog/concepts/variants-categories-collections.mdx +55 -35
  18. package/content/platform/catalog/guides/allowlist-stores.mdx +100 -37
  19. package/content/platform/catalog/guides/create-a-master-product.mdx +124 -48
  20. package/content/platform/catalog/guides/publish-or-reject-a-product.mdx +120 -55
  21. package/content/platform/catalog/overview.mdx +39 -35
  22. package/content/platform/catalog/reference/data-models.mdx +154 -43
  23. package/content/platform/catalog/reference/events.mdx +41 -25
  24. package/content/platform/catalog/reference/links.mdx +196 -28
  25. package/content/platform/catalog/reference/service.mdx +57 -35
  26. package/content/platform/catalog/reference/workflows.mdx +124 -30
  27. package/content/platform/commission/concepts/order-commission-lines.mdx +54 -52
  28. package/content/platform/commission/concepts/rule-matching.mdx +53 -63
  29. package/content/platform/commission/concepts/rules-and-rates.mdx +61 -59
  30. package/content/platform/commission/guides/batch-update-rules.mdx +69 -41
  31. package/content/platform/commission/guides/create-a-rate.mdx +101 -53
  32. package/content/platform/commission/guides/refresh-order-commission-lines.mdx +104 -35
  33. package/content/platform/commission/overview.mdx +43 -35
  34. package/content/platform/commission/reference/data-models.mdx +59 -45
  35. package/content/platform/commission/reference/events.mdx +28 -34
  36. package/content/platform/commission/reference/links.mdx +42 -33
  37. package/content/platform/commission/reference/service.mdx +103 -28
  38. package/content/platform/commission/reference/workflows.mdx +101 -27
  39. package/content/platform/offer/concepts/pricing-and-inventory.mdx +63 -49
  40. package/content/platform/offer/concepts/shipping.mdx +28 -29
  41. package/content/platform/offer/concepts/what-is-an-offer.mdx +48 -45
  42. package/content/platform/offer/guides/bulk-create-offers.mdx +97 -61
  43. package/content/platform/offer/guides/create-an-offer.mdx +112 -60
  44. package/content/platform/offer/guides/manage-offer-inventory.mdx +78 -38
  45. package/content/platform/offer/overview.mdx +39 -32
  46. package/content/platform/offer/reference/data-models.mdx +77 -47
  47. package/content/platform/offer/reference/events.mdx +44 -18
  48. package/content/platform/offer/reference/links.mdx +92 -40
  49. package/content/platform/offer/reference/service.mdx +66 -31
  50. package/content/platform/offer/reference/workflows.mdx +146 -37
  51. package/content/platform/order-group/concepts/computed-totals.mdx +40 -37
  52. package/content/platform/order-group/concepts/order-splitting.mdx +41 -39
  53. package/content/platform/order-group/concepts/the-order-group.mdx +39 -32
  54. package/content/platform/order-group/guides/list-order-groups.mdx +96 -56
  55. package/content/platform/order-group/guides/retrieve-an-order-group.mdx +93 -42
  56. package/content/platform/order-group/guides/split-a-cart.mdx +115 -40
  57. package/content/platform/order-group/overview.mdx +42 -32
  58. package/content/platform/order-group/reference/data-models.mdx +48 -31
  59. package/content/platform/order-group/reference/events.mdx +29 -17
  60. package/content/platform/order-group/reference/links.mdx +90 -29
  61. package/content/platform/order-group/reference/service.mdx +55 -31
  62. package/content/platform/order-group/reference/workflows.mdx +83 -24
  63. package/content/platform/payout/concepts/account-lifecycle.mdx +64 -67
  64. package/content/platform/payout/concepts/accounts-and-onboarding.mdx +50 -40
  65. package/content/platform/payout/concepts/payout-pipeline.mdx +70 -73
  66. package/content/platform/payout/guides/create-a-payout-account.mdx +65 -41
  67. package/content/platform/payout/guides/process-a-provider-webhook.mdx +84 -38
  68. package/content/platform/payout/guides/start-provider-onboarding.mdx +90 -46
  69. package/content/platform/payout/overview.mdx +43 -34
  70. package/content/platform/payout/reference/data-models.mdx +63 -36
  71. package/content/platform/payout/reference/events.mdx +56 -30
  72. package/content/platform/payout/reference/links.mdx +52 -22
  73. package/content/platform/payout/reference/service.mdx +72 -33
  74. package/content/platform/payout/reference/workflows.mdx +80 -19
  75. package/content/platform/product-edit/concepts/change-actions.mdx +52 -45
  76. package/content/platform/product-edit/concepts/change-pipeline.mdx +60 -52
  77. package/content/platform/product-edit/concepts/status-and-auto-confirm.mdx +43 -46
  78. package/content/platform/product-edit/guides/confirm-or-decline-a-change.mdx +104 -56
  79. package/content/platform/product-edit/guides/edit-a-product.mdx +126 -46
  80. package/content/platform/product-edit/guides/request-a-revision.mdx +99 -39
  81. package/content/platform/product-edit/overview.mdx +41 -34
  82. package/content/platform/product-edit/reference/data-models.mdx +73 -47
  83. package/content/platform/product-edit/reference/events.mdx +39 -29
  84. package/content/platform/product-edit/reference/links.mdx +32 -24
  85. package/content/platform/product-edit/reference/service.mdx +45 -26
  86. package/content/platform/product-edit/reference/workflows.mdx +173 -31
  87. package/content/platform/review/concepts/product-vs-seller-reviews.mdx +42 -41
  88. package/content/platform/review/concepts/ratings-and-moderation.mdx +45 -44
  89. package/content/platform/review/concepts/the-review-model.mdx +52 -45
  90. package/content/platform/review/guides/compute-aggregate-ratings.mdx +62 -37
  91. package/content/platform/review/guides/create-a-review.mdx +109 -39
  92. package/content/platform/review/guides/moderate-a-review.mdx +83 -37
  93. package/content/platform/review/guides/respond-to-a-review.mdx +85 -40
  94. package/content/platform/review/overview.mdx +43 -33
  95. package/content/platform/review/reference/data-models.mdx +41 -27
  96. package/content/platform/review/reference/events.mdx +55 -37
  97. package/content/platform/review/reference/links.mdx +80 -22
  98. package/content/platform/review/reference/service.mdx +61 -33
  99. package/content/platform/review/reference/workflows.mdx +80 -24
  100. package/content/platform/store/concepts/lifecycle.mdx +68 -44
  101. package/content/platform/store/concepts/store-entity.mdx +45 -27
  102. package/content/platform/store/concepts/team.mdx +56 -35
  103. package/content/platform/store/guides/create-a-store.mdx +102 -35
  104. package/content/platform/store/guides/manage-the-team.mdx +122 -32
  105. package/content/platform/store/guides/moderate-a-store.mdx +107 -38
  106. package/content/platform/store/overview.mdx +41 -34
  107. package/content/platform/store/reference/data-models.mdx +132 -57
  108. package/content/platform/store/reference/events.mdx +42 -17
  109. package/content/platform/store/reference/links.mdx +67 -49
  110. package/content/platform/store/reference/service.mdx +105 -33
  111. package/content/platform/store/reference/workflows.mdx +304 -24
  112. package/llms.txt +111 -111
  113. package/package.json +1 -1
@@ -1,83 +1,135 @@
1
1
  ---
2
- title: "Create an offer"
2
+ title: "Create an offer from your server code"
3
3
  sidebarTitle: "Create an offer"
4
- description: "Create a single offer programmatically with createOffersWorkflow."
4
+ description: "Run createOffersWorkflow to list a master variant for a store with a SKU, price, stock, and shipping profile."
5
5
  ---
6
6
 
7
- In this guide, you'll learn how to create an offer from your own server code,
8
- for example in a custom API route, a seed script, or an import flow.
7
+ Create an offer with `createOffersWorkflow` when you list products for a store outside the Vendor panel, for example in a custom route, a seed script, or an import job.
9
8
 
10
- Mercur exposes a `createOffersWorkflow` that creates the `Offer` record, its
11
- inventory items, and its price rows, and wires up every link in one run. Run it
12
- from any place that has access to the Medusa container.
9
+ ## Before you begin
13
10
 
14
- ## Run the workflow
11
+ You need these records in place:
15
12
 
16
- ```ts title="src/api/custom/route.ts"
17
- import type { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
18
- import { createOffersWorkflow } from "@mercurjs/core/workflows"
13
+ - **A store**: its `seller_id` and the id of a member to record as `created_by`
14
+ - **A master variant**: the `variant_id` the store will sell
15
+ - **A shipping profile**: one that belongs to the store
16
+ - **A stock location**: where the starting stock is held
17
+
18
+ ## Create the offer
19
+
20
+ <Steps>
21
+ <Step title="Add a route that runs the workflow">
22
+ Create an API route that runs `createOffersWorkflow` with one offer.
19
23
 
20
- export async function POST(req: MedusaRequest, res: MedusaResponse) {
21
- const { result } = await createOffersWorkflow(req.scope).run({
22
- input: {
23
- offers: [
24
- {
25
- seller_id: "sel_123",
26
- created_by: "mem_123",
27
- variant_id: "variant_123",
28
- shipping_profile_id: "sp_123",
29
- sku: "ACME-WIDGET-01",
30
- prices: [{ amount: 2500, currency_code: "usd" }],
31
- inventory_items: [
24
+ ```ts title="src/api/admin/custom/offers/route.ts"
25
+ import type { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
26
+ import { createOffersWorkflow } from "@mercurjs/core/workflows"
27
+
28
+ const stock = [{ location_id: "sloc_01JD8Q1234567", stocked_quantity: 50 }]
29
+
30
+ export async function POST(req: MedusaRequest, res: MedusaResponse) {
31
+ const { result } = await createOffersWorkflow(req.scope).run({
32
+ input: {
33
+ offers: [
32
34
  {
33
- stock_levels: [{ location_id: "sloc_1", stocked_quantity: 50 }],
35
+ seller_id: "sel_01JD8Q1234567",
36
+ created_by: "mem_01JD8Q1234567",
37
+ variant_id: "variant_01JD8Q1234567",
38
+ shipping_profile_id: "sp_01JD8Q1234567",
39
+ sku: "ACME-LS-M",
40
+ prices: [{ amount: 4700, currency_code: "eur" }],
41
+ inventory_items: [{ stock_levels: stock }],
34
42
  },
35
43
  ],
36
44
  },
45
+ })
46
+
47
+ res.status(201).json({ offer: result[0] })
48
+ }
49
+ ```
50
+
51
+ The workflow returns the created `Offer` rows. The one `inventory_items` entry becomes a new inventory item with 50 units at the location.
52
+ </Step>
53
+ <Step title="Know what the run wires up">
54
+ One run does more than insert the row:
55
+
56
+ - **Inventory**: creates an `InventoryItem` per entry, links it to the offer with its `required_quantity`, and links it to the store
57
+ - **Prices**: adds each price to the variant’s price set with an `offer_id` rule and links it to the offer
58
+ - **Barcodes**: copies `ean` and `upc` from the variant when you leave them out
59
+ - **Shipping**: links the master product to the shipping profile if the product has none
60
+ - **Event**: emits `offer.created` with `{ id, product_id }`
61
+ </Step>
62
+ <Step title="Read the offer back with its links">
63
+ Query the offer with its prices and stock to confirm the result.
64
+
65
+ ```ts
66
+ const { data: [offer] } = await query.graph({
67
+ entity: "offer",
68
+ fields: [
69
+ "id",
70
+ "sku",
71
+ "prices.amount",
72
+ "prices.currency_code",
73
+ "inventory_item_link.required_quantity",
74
+ "inventory_item_link.inventory_item.location_levels.*",
37
75
  ],
38
- },
39
- })
76
+ filters: { id: result[0].id },
77
+ })
78
+ ```
79
+
80
+ `prices` reads the offer’s own rows on the shared price set. `inventory_item_link` returns the pivot rows, so you get `required_quantity` with each item.
81
+ </Step>
82
+ </Steps>
83
+
84
+ ## Set selling terms and a condition
40
85
 
41
- res.status(201).json({ offer: result[0] })
86
+ The offer accepts optional fields beyond the required ones. Add them to the same entry:
87
+
88
+ ```ts
89
+ {
90
+ sku: "ACME-LS-M-RF",
91
+ condition_id: "ofcond_01JD8Q1234567",
92
+ manage_inventory: true,
93
+ allow_backorder: false,
94
+ leadtime_to_ship: 3,
95
+ metadata: { source: "erp" },
42
96
  }
43
97
  ```
44
98
 
45
- <Note>
46
- Each offer must reference an existing master `variant_id` and include at least
47
- one `inventory_items` entry. The SKU must be unique within the store.
48
- Reusing an existing `(seller_id, sku)` pair is rejected.
49
- </Note>
99
+ `condition_id` must point at an active offer condition. `manage_inventory` defaults to `true`, `allow_backorder` to `false`, and `leadtime_to_ship` to `null`.
50
100
 
51
- ## What the workflow wires up
101
+ ## Attach custom data with additional_data
52
102
 
53
- A single run does more than insert a row:
103
+ Pass `additional_data` to the run to hand extra values to the `offersCreated` hook. Use it to store data from your own module next to the offer.
54
104
 
55
- - Creates a new `InventoryItem` per `inventory_items` entry (with any `stock_levels`) and links each to the offer. Remember, inventory links to the **offer**, not the variant.
56
- - Writes each price onto the master variant's shared price set, stamped with an `offer_id` rule, and links the price rows to the offer.
57
- - Links the offer to its store, product, variant, and shipping profile.
58
- - Emits `offer.created`.
105
+ ```ts title="src/workflows/hooks/offers-created.ts"
106
+ import { createOffersWorkflow } from "@mercurjs/core/workflows"
59
107
 
60
- ## Attach custom data
108
+ createOffersWorkflow.hooks.offersCreated(
109
+ async ({ offers, additional_data }, { container }) => {
110
+ const logger = container.resolve("logger")
111
+ for (const offer of offers) {
112
+ logger.info(`${offer.sku}: ${additional_data?.source ?? "api"}`)
113
+ }
114
+ }
115
+ )
116
+ ```
61
117
 
62
- The workflow accepts an `additional_data` payload that is passed to its
63
- `offersCreated` hook, letting you persist marketplace-specific data alongside the
64
- offer without forking the workflow.
118
+ The hook receives the created offers with their `prices` loaded. Run the workflow with `additional_data: { source: "erp" }` to see the value in the handler.
65
119
 
66
- ```ts
67
- await createOffersWorkflow(req.scope).run({
68
- input: {
69
- offers: [
70
- {
71
- seller_id: "sel_123",
72
- created_by: "mem_123",
73
- variant_id: "variant_123",
74
- shipping_profile_id: "sp_123",
75
- sku: "ACME-WIDGET-01",
76
- prices: [{ amount: 2500, currency_code: "usd" }],
77
- inventory_items: [{ stock_levels: [{ location_id: "sloc_1", stocked_quantity: 50 }] }],
78
- },
79
- ],
80
- additional_data: { source: "csv-import" },
81
- },
82
- })
83
- ```
120
+ ## Handle a failed run
121
+
122
+ The workflow validates the whole input and rolls back on failure. Compensation removes the inventory items, prices, and links it already created.
123
+
124
+ A run fails when:
125
+
126
+ - **A variant doesn’t exist**: `Variant with id … was not found`
127
+ - **An offer has no inventory items**: `Offer must have at least one inventory item`
128
+ - **A condition is unknown or inactive**: `Offer condition … is not active` or not found
129
+ - **A SKU is taken in the store**: the `(seller_id, sku)` unique index rejects the insert
130
+
131
+ ## Next steps
132
+
133
+ - [Bulk-create offers](/platform/offer/guides/bulk-create-offers)
134
+ - [Manage offer inventory](/platform/offer/guides/manage-offer-inventory)
135
+ - [createOffersWorkflow reference](/platform/offer/reference/workflows)
@@ -1,53 +1,93 @@
1
1
  ---
2
- title: "Manage offer inventory"
2
+ title: "Change the inventory items behind an offer"
3
3
  sidebarTitle: "Manage offer inventory"
4
- description: "Attach, update, and detach inventory items on an existing offer."
4
+ description: "Use batchOfferInventoryItemsWorkflow to link, relink, and unlink existing inventory items on an offer."
5
5
  ---
6
6
 
7
- In this guide, you'll learn how to change the inventory items linked to an
8
- existing offer from your own server code.
7
+ Run `batchOfferInventoryItemsWorkflow` when an existing offer needs different stock behind it, for example to turn it into a kit or to swap a component. The workflow changes the `offer ↔ inventory_item` links and their `required_quantity` in one run.
9
8
 
10
- Because inventory links to the **offer** (not the variant), the set of inventory
11
- items backing an offer is managed through the `offer ↔ inventory_item` link.
12
- `batchOfferInventoryItemsWorkflow` applies creates, updates, and deletes to that
13
- link in a single run.
9
+ ## Before you begin
14
10
 
15
- ## Run the batch workflow
11
+ You need these records in place:
16
12
 
17
- ```ts title="src/api/custom/offer-inventory/route.ts"
18
- import type { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
19
- import { batchOfferInventoryItemsWorkflow } from "@mercurjs/core/workflows"
13
+ - **An offer**: the `offer_id` whose stock you change
14
+ - **Existing inventory items**: the workflow links items that already exist and doesn’t create new ones
20
15
 
21
- export async function POST(req: MedusaRequest, res: MedusaResponse) {
22
- const { result } = await batchOfferInventoryItemsWorkflow(req.scope).run({
23
- input: {
24
- offer_id: "offer_123",
25
- create: [{ inventory_item_id: "iitem_new", required_quantity: 2 }],
26
- update: [{ inventory_item_id: "iitem_existing", required_quantity: 5 }],
27
- delete: ["iitem_stale"],
28
- },
29
- })
30
-
31
- res.json(result)
32
- }
33
- ```
16
+ ## Change the links
17
+
18
+ <Steps>
19
+ <Step title="Add a route that runs the batch">
20
+ Pass the offer and the three optional lists: `create`, `update`, and `delete`.
21
+
22
+ ```ts title="src/api/admin/custom/offers/[id]/inventory/route.ts"
23
+ import type { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
24
+ import { batchOfferInventoryItemsWorkflow } from "@mercurjs/core/workflows"
25
+
26
+ export async function POST(req: MedusaRequest, res: MedusaResponse) {
27
+ const { result } = await batchOfferInventoryItemsWorkflow(req.scope).run({
28
+ input: {
29
+ offer_id: req.params.id,
30
+ create: [
31
+ { inventory_item_id: "iitem_01JD8Q1234567", required_quantity: 2 },
32
+ ],
33
+ update: [
34
+ { inventory_item_id: "iitem_01JD8Q1234568", required_quantity: 1 },
35
+ ],
36
+ delete: ["iitem_01JD8Q1234569"],
37
+ },
38
+ })
39
+
40
+ res.json(result)
41
+ }
42
+ ```
43
+
44
+ `create` links a new item, `update` changes the `required_quantity` of a linked item, and `delete` unlinks an item. The inventory item itself stays in place after an unlink.
45
+ </Step>
46
+ <Step title="Read the result">
47
+ The workflow returns what changed:
48
+
49
+ - **`created`**: the link definitions it added
50
+ - **`updated`**: the link definitions it rewrote
51
+ - **`deleted`**: the inventory item ids it unlinked
52
+
53
+ It also emits `offer.updated` with `{ id }` for the offer.
54
+ </Step>
55
+ </Steps>
34
56
 
35
- The result reports the links that were `created`, `updated`, and `deleted`.
57
+ ## Rules the batch enforces
36
58
 
37
- ## Rules the workflow enforces
59
+ The workflow checks the whole payload before it writes anything. Any broken rule fails the run:
38
60
 
39
- - Items in `create` must **not** already be linked to the offer; items in `update` and `delete` **must** already be linked.
40
- - An `inventory_item_id` can't appear in more than one section, and can't be duplicated within a section.
41
- - `create` items must reference existing `InventoryItem` records; `required_quantity` defaults to `1`.
42
- - Deleting an item that isn't linked to the offer surfaces a 404 rather than silently no-op'ing.
61
+ - **Unknown offer**: `Offer with id: … was not found`
62
+ - **Unknown item in `create`**: the inventory item must exist
63
+ - **Item not linked, in `delete`**: fails with not found instead of a silent no-op
64
+ - **Same id twice**: an id can appear once per list and in only one of the three lists
65
+
66
+ `required_quantity` defaults to `1` in `create` and is required in `update`.
43
67
 
44
68
  <Note>
45
- This workflow manages the **links** between an offer and existing inventory
46
- items and their `required_quantity`. To create brand-new inventory items with
47
- starting stock as part of listing an offer, pass `inventory_items` to
48
- [`createOffersWorkflow`](/platform/offer/guides/create-an-offer) instead.
69
+ The workflow doesn’t check that an item in `create` is already linked or that an item in `update` is linked, and it doesn’t check which store owns an item. Read the offer’s `inventory_item_link` and the item’s store first when your input comes from a seller.
49
70
  </Note>
50
71
 
51
- The workflow emits `offer.updated` and exposes an `offerInventoryItemsBatched`
52
- hook carrying the `offer_id`, the batch result, and any `additional_data` you
53
- passed in.
72
+ ## Run code after the batch
73
+
74
+ The workflow exposes an `offerInventoryItemsBatched` hook. It receives the `offer_id`, the `result`, and any `additional_data` you passed.
75
+
76
+ ```ts title="src/workflows/hooks/offer-inventory-batched.ts"
77
+ import { batchOfferInventoryItemsWorkflow } from "@mercurjs/core/workflows"
78
+
79
+ batchOfferInventoryItemsWorkflow.hooks.offerInventoryItemsBatched(
80
+ async ({ offer_id, result }, { container }) => {
81
+ const logger = container.resolve("logger")
82
+ logger.info(`${offer_id}: ${result.deleted.length} items unlinked`)
83
+ }
84
+ )
85
+ ```
86
+
87
+ A `validate` hook runs first with the raw `input`, so you can reject a batch with your own rules before any link changes.
88
+
89
+ ## Next steps
90
+
91
+ - [How offer stock works](/platform/offer/concepts/pricing-and-inventory)
92
+ - [The offer to inventory item link](/platform/offer/reference/links)
93
+ - [batchOfferInventoryItemsWorkflow reference](/platform/offer/reference/workflows)
@@ -1,60 +1,67 @@
1
1
  ---
2
- title: "Offer"
2
+ title: "Offers"
3
3
  sidebarTitle: "Overview"
4
- description: "How a store sells against the shared master catalog, with its own SKU, price, inventory, and shipping."
4
+ description: "Let every store sell a shared catalog product with its own SKU, price, stock, condition, and shipping profile."
5
5
  ---
6
6
 
7
- Use offers to let each store sell against Mercur's shared master catalog on its
8
- own terms.
7
+ Use offers to let several stores sell the same catalog product, each on its own terms.
9
8
 
10
- The offer is the central concept of the marketplace. Products live in a single
11
- **master catalog** that no store owns; an offer is the record that connects a
12
- store to a master product variant. It carries everything that makes that listing
13
- the store's own: its SKU, its price, its inventory, and its shipping profile.
14
- Cart and order line items link back to the exact offer that was purchased, so the
15
- whole order lifecycle knows which store fulfills and gets paid.
9
+ Products in Mercur live in one shared master catalog that no store owns. An offer is the record that connects a store to one master variant. It carries everything that makes the listing the store’s own: its SKU, price, inventory, condition, and shipping profile. The Offer module holds the offer and its conditions, and links reach out to the store, the catalog, pricing, inventory, fulfillment, and the cart and order line items that buy it.
10
+
11
+ <img className="block dark:hidden" src="/images/figures/offer-overview-light.svg" alt="The Linen shirt master product sits behind an Offers list for its M variant: ACME, GLOBEX, and INITECH each list the same variant with their own SKU, condition, price, and stock." />
12
+ <img className="hidden dark:block" src="/images/figures/offer-overview-dark.svg" alt="The Linen shirt master product sits behind an Offers list for its M variant: ACME, GLOBEX, and INITECH each list the same variant with their own SKU, condition, price, and stock." />
16
13
 
17
14
  <Note>
18
- **Products are shared; offers are owned.** A store never owns a product. It
19
- publishes an offer against a master product variant. Two stores selling the
20
- same product each have their own offer, with their own SKU, price, and stock.
15
+ The data model is `Offer` (id prefix `offer`), resolved with `MercurModules.OFFER` from `@mercurjs/types`. A store never owns a product. Two stores selling the same variant each hold their own offer.
21
16
  </Note>
22
17
 
23
18
  ## Key features
24
19
 
25
- - **Per-store listings:** one offer per store per variant, each with its own SKU (unique within a store).
26
- - **Offer-scoped pricing:** prices live on the master variant's shared price set, scoped by an `offer_id` rule so every store prices independently.
27
- - **Offer-scoped inventory:** inventory items link to the **offer**, not the variant, so stock never leaks between stores.
28
- - **Per-offer shipping:** each offer points at the store's own shipping profile.
29
- - **Order attribution:** cart and order line items link to the purchased offer, driving fulfillment, commission, and payouts.
30
- - **Bulk operations:** create and update many offers, and batch an offer's inventory links, in a single workflow run.
20
+ The Offer module covers everything a store needs to sell a catalog variant:
21
+
22
+ - **Per-store listings**: an offer points at one master variant, with a SKU that’s unique within its store
23
+ - **Offer-scoped pricing**: price rows sit on the variant’s shared price set, each tagged with an `offer_id` price rule
24
+ - **Offer-scoped inventory**: inventory items link to the offer, not the variant, so stock never mixes between stores
25
+ - **Conditions**: operators define conditions such as `new` or `refurbished`, and stores list offers under the active ones
26
+ - **Per-offer shipping**: each offer names the store’s own shipping profile
27
+ - **Order attribution**: cart and order line items link to the offer that was bought
28
+ - **Hooks and events**: every workflow exposes hooks and emits `offer.*` events for your own side effects
29
+
30
+ ## How it works
31
+
32
+ An offer moves through the marketplace in four steps:
33
+
34
+ 1. A store runs `createOffersWorkflow` against a master variant, with a SKU, prices, inventory items, and a shipping profile.
35
+ 2. The workflow writes the prices onto the variant’s price set with an `offer_id` rule and creates inventory items linked to the offer.
36
+ 3. When a shopper adds the offer to a cart, the line item links to the offer and pricing resolves the offer’s own rows.
37
+ 4. At checkout the order line item keeps the offer link, so the right store fulfills the item and gets paid.
31
38
 
32
39
  ## Get started
33
40
 
34
- Learn how the domain fits together:
41
+ Learn how offers, prices, stock, and shipping fit together.
35
42
 
36
43
  <CardGroup cols={2}>
37
44
  <Card title="What is an offer" icon="tag" href="/platform/offer/concepts/what-is-an-offer">
38
- The offer record, how it points at a master variant, and its SKU.
45
+ The offer record, its SKU rules, and offer conditions.
39
46
  </Card>
40
47
  <Card title="Pricing & inventory" icon="coins" href="/platform/offer/concepts/pricing-and-inventory">
41
- Offer-scoped prices on the shared price set and offer-linked inventory.
48
+ Prices on the shared price set and stock linked to the offer.
42
49
  </Card>
43
50
  <Card title="Shipping" icon="truck" href="/platform/offer/concepts/shipping">
44
- How an offer carries its own shipping profile.
51
+ How an offer ships with its store’s shipping profile.
45
52
  </Card>
46
53
  </CardGroup>
47
54
 
48
55
  ## Examples
49
56
 
50
- Build against the Offer domain in your own code:
57
+ Build against offers in your own server code.
51
58
 
52
59
  <CardGroup cols={2}>
53
60
  <Card title="Create an offer" icon="plus" href="/platform/offer/guides/create-an-offer">
54
- Run `createOffersWorkflow` with a price and inventory.
61
+ Run `createOffersWorkflow` from an API route.
55
62
  </Card>
56
63
  <Card title="Bulk-create offers" icon="layer-group" href="/platform/offer/guides/bulk-create-offers">
57
- List many offers against the catalog in one run.
64
+ List a store’s catalog in a single run.
58
65
  </Card>
59
66
  <Card title="Manage offer inventory" icon="boxes-stacked" href="/platform/offer/guides/manage-offer-inventory">
60
67
  Attach, update, and detach inventory items on an offer.
@@ -63,22 +70,22 @@ Build against the Offer domain in your own code:
63
70
 
64
71
  ## Resources
65
72
 
66
- Data models, workflows, service methods, and events for the Offer domain:
73
+ The data models, links, workflows, service methods, and events behind offers.
67
74
 
68
75
  <CardGroup cols={2}>
69
76
  <Card title="Data models" icon="table" href="/platform/offer/reference/data-models">
70
- The `Offer` entity and its fields.
77
+ `Offer` and `OfferCondition`, field by field.
71
78
  </Card>
72
79
  <Card title="Links" icon="link" href="/platform/offer/reference/links">
73
- How the Offer domain links to other modules.
80
+ How offers link to stores, catalog, pricing, inventory, and orders.
74
81
  </Card>
75
82
  <Card title="Workflows" icon="diagram-project" href="/platform/offer/reference/workflows">
76
- Create, update, delete, and inventory-batch workflows.
83
+ Offer, price, inventory, and condition workflows with their hooks.
77
84
  </Card>
78
85
  <Card title="Service" icon="gear" href="/platform/offer/reference/service">
79
- Module service methods for working with records directly.
86
+ Module service methods for reading and writing records directly.
80
87
  </Card>
81
88
  <Card title="Events" icon="bell" href="/platform/offer/reference/events">
82
- Events emitted as offers change.
89
+ Events emitted as offers and conditions change.
83
90
  </Card>
84
91
  </CardGroup>
@@ -1,56 +1,86 @@
1
1
  ---
2
- title: "Data models"
2
+ title: "Offer data models"
3
3
  sidebarTitle: "Data models"
4
- description: "The data models owned by the Offer domain."
4
+ description: "Every field of the Offer and OfferCondition data models in the Offer module."
5
5
  ---
6
6
 
7
- The Offer domain is owned by the **Offer module**. This reference lists its data
8
- model and fields. For the full module overview, see the
9
- [Offer overview](/platform/offer/overview).
7
+ The Offer module owns two data models: `Offer` and `OfferCondition`. Prices, inventory, the store, and catalog records aren’t columns on these models. They attach through [module links](/platform/offer/reference/links).
10
8
 
11
9
  ## Offer
12
10
 
13
- Table `offer`, id prefix `offer`. A store's listing against a master product
14
- variant. Price and inventory are attached through links rather than stored on the
15
- row.
11
+ A store’s listing of one master product variant. Table `offer`, id prefix `offer`.
16
12
 
17
- | Field | Type | Notes |
13
+ | Field | Type | Description |
18
14
  | --- | --- | --- |
19
- | `id` | text | Primary key |
20
- | `seller_id` | text | The store that owns the offer (read-only link) |
21
- | `variant_id` | text | The master `ProductVariant` the offer points at |
22
- | `product_id` | text | The master `Product` the variant belongs to |
23
- | `shipping_profile_id` | text | The store's shipping profile for this offer |
24
- | `sku` | text | Searchable; unique within a store (see below) |
25
- | `ean` | text | Nullable, searchable; snapshotted off the variant when omitted |
26
- | `upc` | text | Nullable, searchable; snapshotted off the variant when omitted |
27
- | `created_by` | text | The member that created the offer |
28
- | `variant_count` | number | Computed; only set when grouping by store |
29
- | `metadata` | json | Nullable |
30
-
31
- Uniqueness: `(seller_id, sku)` is unique while `deleted_at IS NULL`, so a store
32
- can't reuse a SKU across its live offers, but different stores may share SKU
33
- strings. Indexes also cover `variant_id`, `product_id`, `seller_id`,
34
- `shipping_profile_id`, `ean`, and `upc`.
35
-
36
- ## Linked data
37
-
38
- The offer's price ladder, inventory, and related records aren't columns on the
39
- `offer` table. They're joined through links and only present when requested:
40
-
41
- | Relation | Source |
42
- | --- | --- |
43
- | `prices` | Offer-owned rows on the master variant's shared price set (`offer ↔ price` list-link) |
44
- | `inventory_items` | `offer ↔ inventory_item` list-link; each row carries `required_quantity` |
45
- | `seller` | Read-only `offer ↔ seller` link |
46
- | `product` / `product_variant` | Read-only `offer ↔ product` / `offer ↔ variant` links |
47
- | `shipping_profile` | Read-only `offer ↔ shipping_profile` link |
48
-
49
- <Note>
50
- `variant_count` (and the companion `offer_ids` list) are computed only when
51
- offers are listed grouped by store (`group_by_seller`). On ungrouped reads they
52
- are absent.
53
- </Note>
54
-
55
- See the [Links reference](/platform/offer/reference/links) for the full set of
56
- module links.
15
+ | `id` | `text` | Primary key, prefixed `offer_` |
16
+ | `seller_id` | `text` | The store that owns the offer |
17
+ | `variant_id` | `text` | The master `ProductVariant` the offer sells |
18
+ | `product_id` | `text` | The master `Product`, read from the variant on create |
19
+ | `shipping_profile_id` | `text` | The store’s shipping profile for this offer |
20
+ | `sku` | `text` | Searchable. Unique per store among live offers |
21
+ | `ean` | `text \| null` | Searchable. Copied from the variant when omitted on create |
22
+ | `upc` | `text \| null` | Searchable. Copied from the variant when omitted on create |
23
+ | `manage_inventory` | `boolean` | Default `true`. Whether stock is checked for the offer |
24
+ | `allow_backorder` | `boolean` | Default `false`. Whether the offer sells past its stock |
25
+ | `leadtime_to_ship` | `number \| null` | Lead time before the offer ships |
26
+ | `created_by` | `text` | The member who created the offer |
27
+ | `condition_id` | `text \| null` | Foreign key to `OfferCondition` |
28
+ | `variant_count` | `number` | Computed, not stored. Set only by `group_by_seller` reads |
29
+ | `metadata` | `json \| null` | Custom key-value data |
30
+ | `created_at` | `timestamp` | Set on create |
31
+ | `updated_at` | `timestamp` | Set on every write |
32
+ | `deleted_at` | `timestamp \| null` | Set when the offer is soft-deleted |
33
+
34
+ `OfferDTO` also has `offer_ids`, the list of offer ids in a `group_by_seller` group. It isn’t a model field.
35
+
36
+ ### Offer relations
37
+
38
+ The offer has one relation inside the module:
39
+
40
+ - **`condition`**: belongs to one `OfferCondition`, nullable
41
+
42
+ ### Offer indexes
43
+
44
+ Every index applies only to rows where `deleted_at IS NULL`:
45
+
46
+ | Index | Columns | Notes |
47
+ | --- | --- | --- |
48
+ | `IDX_offer_seller_sku_unique` | `seller_id`, `sku` | Unique |
49
+ | `IDX_offer_variant_id` | `variant_id` | |
50
+ | `IDX_offer_product_id` | `product_id` | |
51
+ | `IDX_offer_seller_id` | `seller_id` | |
52
+ | `IDX_offer_shipping_profile_id` | `shipping_profile_id` | |
53
+ | `IDX_offer_ean` | `ean` | Only where `ean IS NOT NULL` |
54
+ | `IDX_offer_upc` | `upc` | Only where `upc IS NOT NULL` |
55
+ | `IDX_offer_condition_id` | `condition_id` | Only where `condition_id IS NOT NULL` |
56
+ | `IDX_offer_seller_id_created_at` | `seller_id`, `created_at` | |
57
+ | `IDX_offer_product_id_seller_id_created_at` | `product_id`, `seller_id`, `created_at` | |
58
+
59
+ ## OfferCondition
60
+
61
+ A physical condition an offer can be listed under, such as `new` or `refurbished`. Operators manage the list. Id prefix `ofcond`.
62
+
63
+ | Field | Type | Description |
64
+ | --- | --- | --- |
65
+ | `id` | `text` | Primary key, prefixed `ofcond_` |
66
+ | `code` | `text` | Searchable. Unique among live conditions |
67
+ | `label` | `text` | Searchable. Display name |
68
+ | `is_active` | `boolean` | Default `true`. Inactive conditions can’t be assigned |
69
+ | `rank` | `number` | Default `0`. Sort order of the list |
70
+ | `metadata` | `json \| null` | Custom key-value data |
71
+ | `created_at` | `timestamp` | Set on create |
72
+ | `updated_at` | `timestamp` | Set on every write |
73
+ | `deleted_at` | `timestamp \| null` | Set when the condition is soft-deleted |
74
+
75
+ ### OfferCondition relations
76
+
77
+ The condition has one relation inside the module:
78
+
79
+ - **`offers`**: has many `Offer` records, mapped by `condition`
80
+
81
+ The index `IDX_offer_condition_code_unique` keeps `code` unique while `deleted_at IS NULL`.
82
+
83
+ ## Related
84
+
85
+ - [Links to other modules](/platform/offer/reference/links)
86
+ - [What is an offer](/platform/offer/concepts/what-is-an-offer)