@mercurjs/docs 2.3.8-canary.3 → 2.3.9-canary.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/content/learn/architecture.mdx +166 -263
- package/content/learn/introduction.mdx +9 -1
- package/content/learn/use-cases.mdx +70 -0
- package/content/platform/attribute/concepts/attribute-types.mdx +73 -46
- package/content/platform/attribute/concepts/global-vs-inline.mdx +54 -32
- package/content/platform/attribute/concepts/variant-axes.mdx +54 -32
- package/content/platform/attribute/guides/attach-attributes-to-a-product.mdx +96 -53
- package/content/platform/attribute/guides/create-a-variant-axis.mdx +106 -57
- package/content/platform/attribute/guides/create-an-attribute.mdx +105 -56
- package/content/platform/attribute/overview.mdx +37 -33
- package/content/platform/attribute/reference/data-models.mdx +53 -38
- package/content/platform/attribute/reference/events.mdx +47 -24
- package/content/platform/attribute/reference/links.mdx +98 -27
- package/content/platform/attribute/reference/service.mdx +71 -32
- package/content/platform/attribute/reference/workflows.mdx +158 -28
- package/content/platform/catalog/concepts/master-products.mdx +39 -37
- package/content/platform/catalog/concepts/product-seller-allowlist.mdx +56 -35
- package/content/platform/catalog/concepts/status-lifecycle.mdx +55 -49
- package/content/platform/catalog/concepts/variants-categories-collections.mdx +55 -35
- package/content/platform/catalog/guides/allowlist-stores.mdx +100 -37
- package/content/platform/catalog/guides/create-a-master-product.mdx +124 -48
- package/content/platform/catalog/guides/publish-or-reject-a-product.mdx +120 -55
- package/content/platform/catalog/overview.mdx +39 -35
- package/content/platform/catalog/reference/data-models.mdx +154 -43
- package/content/platform/catalog/reference/events.mdx +41 -25
- package/content/platform/catalog/reference/links.mdx +196 -28
- package/content/platform/catalog/reference/service.mdx +57 -35
- package/content/platform/catalog/reference/workflows.mdx +124 -30
- package/content/platform/commission/concepts/order-commission-lines.mdx +54 -52
- package/content/platform/commission/concepts/rule-matching.mdx +53 -63
- package/content/platform/commission/concepts/rules-and-rates.mdx +61 -59
- package/content/platform/commission/guides/batch-update-rules.mdx +69 -41
- package/content/platform/commission/guides/create-a-rate.mdx +101 -53
- package/content/platform/commission/guides/refresh-order-commission-lines.mdx +104 -35
- package/content/platform/commission/overview.mdx +43 -35
- package/content/platform/commission/reference/data-models.mdx +59 -45
- package/content/platform/commission/reference/events.mdx +28 -34
- package/content/platform/commission/reference/links.mdx +42 -33
- package/content/platform/commission/reference/service.mdx +103 -28
- package/content/platform/commission/reference/workflows.mdx +101 -27
- package/content/platform/offer/concepts/pricing-and-inventory.mdx +63 -49
- package/content/platform/offer/concepts/shipping.mdx +28 -29
- package/content/platform/offer/concepts/what-is-an-offer.mdx +48 -45
- package/content/platform/offer/guides/bulk-create-offers.mdx +97 -61
- package/content/platform/offer/guides/create-an-offer.mdx +112 -60
- package/content/platform/offer/guides/manage-offer-inventory.mdx +78 -38
- package/content/platform/offer/overview.mdx +39 -32
- package/content/platform/offer/reference/data-models.mdx +77 -47
- package/content/platform/offer/reference/events.mdx +44 -18
- package/content/platform/offer/reference/links.mdx +92 -40
- package/content/platform/offer/reference/service.mdx +66 -31
- package/content/platform/offer/reference/workflows.mdx +146 -37
- package/content/platform/order-group/concepts/computed-totals.mdx +40 -37
- package/content/platform/order-group/concepts/order-splitting.mdx +41 -39
- package/content/platform/order-group/concepts/the-order-group.mdx +39 -32
- package/content/platform/order-group/guides/list-order-groups.mdx +96 -56
- package/content/platform/order-group/guides/retrieve-an-order-group.mdx +93 -42
- package/content/platform/order-group/guides/split-a-cart.mdx +115 -40
- package/content/platform/order-group/overview.mdx +42 -32
- package/content/platform/order-group/reference/data-models.mdx +48 -31
- package/content/platform/order-group/reference/events.mdx +29 -17
- package/content/platform/order-group/reference/links.mdx +90 -29
- package/content/platform/order-group/reference/service.mdx +55 -31
- package/content/platform/order-group/reference/workflows.mdx +83 -24
- package/content/platform/payout/concepts/account-lifecycle.mdx +64 -67
- package/content/platform/payout/concepts/accounts-and-onboarding.mdx +50 -40
- package/content/platform/payout/concepts/payout-pipeline.mdx +70 -73
- package/content/platform/payout/guides/create-a-payout-account.mdx +65 -41
- package/content/platform/payout/guides/process-a-provider-webhook.mdx +84 -38
- package/content/platform/payout/guides/start-provider-onboarding.mdx +90 -46
- package/content/platform/payout/overview.mdx +43 -34
- package/content/platform/payout/reference/data-models.mdx +63 -36
- package/content/platform/payout/reference/events.mdx +56 -30
- package/content/platform/payout/reference/links.mdx +52 -22
- package/content/platform/payout/reference/service.mdx +72 -33
- package/content/platform/payout/reference/workflows.mdx +80 -19
- package/content/platform/product-edit/concepts/change-actions.mdx +52 -45
- package/content/platform/product-edit/concepts/change-pipeline.mdx +60 -52
- package/content/platform/product-edit/concepts/status-and-auto-confirm.mdx +43 -46
- package/content/platform/product-edit/guides/confirm-or-decline-a-change.mdx +104 -56
- package/content/platform/product-edit/guides/edit-a-product.mdx +126 -46
- package/content/platform/product-edit/guides/request-a-revision.mdx +99 -39
- package/content/platform/product-edit/overview.mdx +41 -34
- package/content/platform/product-edit/reference/data-models.mdx +73 -47
- package/content/platform/product-edit/reference/events.mdx +39 -29
- package/content/platform/product-edit/reference/links.mdx +32 -24
- package/content/platform/product-edit/reference/service.mdx +45 -26
- package/content/platform/product-edit/reference/workflows.mdx +173 -31
- package/content/platform/review/concepts/product-vs-seller-reviews.mdx +42 -41
- package/content/platform/review/concepts/ratings-and-moderation.mdx +45 -44
- package/content/platform/review/concepts/the-review-model.mdx +52 -45
- package/content/platform/review/guides/compute-aggregate-ratings.mdx +62 -37
- package/content/platform/review/guides/create-a-review.mdx +109 -39
- package/content/platform/review/guides/moderate-a-review.mdx +83 -37
- package/content/platform/review/guides/respond-to-a-review.mdx +85 -40
- package/content/platform/review/overview.mdx +43 -33
- package/content/platform/review/reference/data-models.mdx +41 -27
- package/content/platform/review/reference/events.mdx +55 -37
- package/content/platform/review/reference/links.mdx +80 -22
- package/content/platform/review/reference/service.mdx +61 -33
- package/content/platform/review/reference/workflows.mdx +80 -24
- package/content/platform/store/concepts/lifecycle.mdx +68 -44
- package/content/platform/store/concepts/store-entity.mdx +45 -27
- package/content/platform/store/concepts/team.mdx +56 -35
- package/content/platform/store/guides/create-a-store.mdx +102 -35
- package/content/platform/store/guides/manage-the-team.mdx +122 -32
- package/content/platform/store/guides/moderate-a-store.mdx +107 -38
- package/content/platform/store/overview.mdx +41 -34
- package/content/platform/store/reference/data-models.mdx +132 -57
- package/content/platform/store/reference/events.mdx +42 -17
- package/content/platform/store/reference/links.mdx +67 -49
- package/content/platform/store/reference/service.mdx +105 -33
- package/content/platform/store/reference/workflows.mdx +304 -24
- package/content/references/api/conventions.mdx +20 -0
- package/content/resources/integrations/magento2.mdx +162 -0
- package/content/resources/integrations/overview.mdx +10 -0
- package/content/resources/integrations/stripe-connect.mdx +1 -1
- package/content/resources/tutorials/custom-api-route.mdx +3 -2
- package/llms.txt +113 -111
- package/package.json +1 -1
|
@@ -1,22 +1,55 @@
|
|
|
1
1
|
---
|
|
2
|
-
title: "
|
|
2
|
+
title: "Offer events"
|
|
3
3
|
sidebarTitle: "Events"
|
|
4
|
-
description: "Events
|
|
4
|
+
description: "Events the offer workflows emit, with their payloads, for subscribers and side effects."
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
The
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
The offer and offer condition workflows emit events after they write. Subscribe to them to reindex search, sync an external system, or start a follow-up workflow instead of polling.
|
|
8
|
+
|
|
9
|
+
## Offer events
|
|
10
|
+
|
|
11
|
+
The offer workflows emit one event per affected offer. The names come from `OfferWorkflowEvents`.
|
|
12
|
+
|
|
13
|
+
| Event | Emitted when | Payload |
|
|
14
|
+
| --- | --- | --- |
|
|
15
|
+
| `offer.created` | `createOffersWorkflow` creates an offer | `{ id, product_id }` |
|
|
16
|
+
| `offer.updated` | `updateOffersWorkflow` updates an offer | `{ id, product_id }` |
|
|
17
|
+
| `offer.updated` | `batchOfferInventoryItemsWorkflow` changes an offer’s inventory links | `{ id }` |
|
|
18
|
+
| `offer.deleted` | `deleteOffersWorkflow` soft-deletes an offer | `{ id, product_id }` |
|
|
19
|
+
|
|
20
|
+
<Note>
|
|
21
|
+
`upsertOfferPricesWorkflow` emits no event of its own. When `updateOffersWorkflow` runs it, `offer.updated` covers the change. When you run it directly, use its `offerPricesUpserted` hook.
|
|
22
|
+
</Note>
|
|
23
|
+
|
|
24
|
+
## Offer condition events
|
|
25
|
+
|
|
26
|
+
The condition workflows emit one event per condition. The names come from `OfferConditionWorkflowEvents`.
|
|
27
|
+
|
|
28
|
+
| Event | Emitted when | Payload |
|
|
29
|
+
| --- | --- | --- |
|
|
30
|
+
| `offer_condition.created` | `createOfferConditionsWorkflow` creates a condition | `{ id }` |
|
|
31
|
+
| `offer_condition.updated` | `updateOfferConditionsWorkflow` updates a condition | `{ id }` |
|
|
32
|
+
| `offer_condition.deleted` | `deleteOfferConditionsWorkflow` deletes a condition | `{ id }` |
|
|
33
|
+
|
|
34
|
+
## Subscribe to an offer event
|
|
35
|
+
|
|
36
|
+
A subscriber receives the payload and the container. This one reads the new offer with its store.
|
|
10
37
|
|
|
11
38
|
```ts title="src/subscribers/offer-created.ts"
|
|
12
39
|
import type { SubscriberArgs, SubscriberConfig } from "@medusajs/framework"
|
|
40
|
+
import { ContainerRegistrationKeys } from "@medusajs/framework/utils"
|
|
13
41
|
|
|
14
42
|
export default async function offerCreatedHandler({
|
|
15
43
|
event,
|
|
16
44
|
container,
|
|
17
45
|
}: SubscriberArgs<{ id: string; product_id: string }>) {
|
|
18
|
-
const
|
|
19
|
-
|
|
46
|
+
const query = container.resolve(ContainerRegistrationKeys.QUERY)
|
|
47
|
+
const { data: [offer] } = await query.graph({
|
|
48
|
+
entity: "offer",
|
|
49
|
+
fields: ["id", "sku", "seller.name"],
|
|
50
|
+
filters: { id: event.data.id },
|
|
51
|
+
})
|
|
52
|
+
container.resolve("logger").info(`${offer.seller.name} listed ${offer.sku}`)
|
|
20
53
|
}
|
|
21
54
|
|
|
22
55
|
export const config: SubscriberConfig = {
|
|
@@ -24,16 +57,9 @@ export const config: SubscriberConfig = {
|
|
|
24
57
|
}
|
|
25
58
|
```
|
|
26
59
|
|
|
27
|
-
|
|
60
|
+
Payloads carry ids only, so query the offer for the fields you need. For an `offer.deleted` subscriber, read values from the `offersDeleted` hook instead, since the row is already soft-deleted.
|
|
28
61
|
|
|
29
|
-
|
|
30
|
-
| --- | --- | --- |
|
|
31
|
-
| `offer.created` | An offer is created | `{ id, product_id }` |
|
|
32
|
-
| `offer.updated` | An offer's row, prices, or inventory links change | `{ id, product_id }` |
|
|
33
|
-
| `offer.deleted` | An offer is deleted | `{ id, product_id }` |
|
|
62
|
+
## Related
|
|
34
63
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
offer. `batchOfferInventoryItemsWorkflow` emits `offer.updated` with a single
|
|
38
|
-
`{ id }` payload for the batched offer.
|
|
39
|
-
</Note>
|
|
64
|
+
- [Offer workflows](/platform/offer/reference/workflows)
|
|
65
|
+
- [Offer module links](/platform/offer/reference/links)
|
|
@@ -1,60 +1,112 @@
|
|
|
1
1
|
---
|
|
2
|
-
title: "
|
|
2
|
+
title: "Offer module links"
|
|
3
3
|
sidebarTitle: "Links"
|
|
4
|
-
description: "
|
|
4
|
+
description: "Every module link that connects offers to stores, the catalog, pricing, inventory, fulfillment, carts, and orders."
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
7
|
+
The Offer module never references another module directly. Eight module links in `packages/core/src/links/` connect the offer to the rest of the marketplace, and `query.graph` follows them by alias.
|
|
8
|
+
|
|
9
|
+
| Link | Other side | Kind | File |
|
|
10
|
+
| --- | --- | --- | --- |
|
|
11
|
+
| Offer to store | `Seller` | Read-only, on `seller_id` | `offer-seller-link.ts` |
|
|
12
|
+
| Offer to product | `Product` | Read-only, on `product_id` | `offer-product-link.ts` |
|
|
13
|
+
| Offer to variant | `ProductVariant` | Read-only, on `variant_id` | `offer-variant-link.ts` |
|
|
14
|
+
| Offer to shipping profile | `ShippingProfile` | Read-only, on `shipping_profile_id` | `offer-shipping-profile-link.ts` |
|
|
15
|
+
| Offer to price | `Price` | Writable, one offer to many prices | `offer-price-link.ts` |
|
|
16
|
+
| Offer to inventory item | `InventoryItem` | Writable, many to many | `offer-inventory-item-link.ts` |
|
|
17
|
+
| Cart line item to offer | Cart `LineItem` | Writable, many items to one offer | `cart-line-item-offer-link.ts` |
|
|
18
|
+
| Order line item to offer | `OrderLineItem` | Writable, many items to one offer | `order-line-item-offer-link.ts` |
|
|
19
|
+
|
|
20
|
+
## Offer to store
|
|
21
|
+
|
|
22
|
+
Each offer belongs to one store. The link reads `offer.seller_id`, so you write the store by setting that field, not through the link.
|
|
23
|
+
|
|
24
|
+
```ts
|
|
25
|
+
const { data: offers } = await query.graph({
|
|
26
|
+
entity: "offer",
|
|
27
|
+
fields: ["id", "sku", "seller.name"],
|
|
28
|
+
filters: { seller_id: "sel_01JD8Q1234567" },
|
|
29
|
+
})
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Offer to product and variant
|
|
33
|
+
|
|
34
|
+
Each offer points at one master product and one master variant. Both links are read-only and read `offer.product_id` and `offer.variant_id`.
|
|
35
|
+
|
|
36
|
+
```ts
|
|
37
|
+
const { data: offers } = await query.graph({
|
|
38
|
+
entity: "offer",
|
|
39
|
+
fields: ["id", "product.title", "product_variant.title"],
|
|
40
|
+
filters: { product_id: "prod_01JD8Q1234567" },
|
|
41
|
+
})
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
The variant alias is `product_variant`. Use `product_variant.price_set.id` to reach the shared price set that holds the offer’s prices.
|
|
45
|
+
|
|
46
|
+
## Offer to shipping profile
|
|
47
|
+
|
|
48
|
+
Each offer ships with one profile, read from `offer.shipping_profile_id`.
|
|
12
49
|
|
|
13
50
|
```ts
|
|
14
51
|
const { data: offers } = await query.graph({
|
|
15
52
|
entity: "offer",
|
|
16
|
-
fields: ["id", "
|
|
53
|
+
fields: ["id", "shipping_profile.id", "shipping_profile.name"],
|
|
17
54
|
})
|
|
18
55
|
```
|
|
19
56
|
|
|
20
|
-
##
|
|
57
|
+
## Offer to price
|
|
58
|
+
|
|
59
|
+
One offer has many prices. The link records which rows on the variant’s shared price set belong to the offer. The offer workflows create and dismiss these links, so don’t write them yourself.
|
|
60
|
+
|
|
61
|
+
```ts
|
|
62
|
+
const { data: offers } = await query.graph({
|
|
63
|
+
entity: "offer",
|
|
64
|
+
fields: [
|
|
65
|
+
"prices.amount",
|
|
66
|
+
"prices.currency_code",
|
|
67
|
+
"prices.min_quantity",
|
|
68
|
+
"prices.price_rules.attribute",
|
|
69
|
+
"prices.price_rules.value",
|
|
70
|
+
],
|
|
71
|
+
})
|
|
72
|
+
```
|
|
21
73
|
|
|
22
|
-
|
|
23
|
-
| --- | --- |
|
|
24
|
-
| **Product** | An offer points at one master product (`offer.product_id`, read-only). |
|
|
25
|
-
| **Product variant** | An offer points at one master variant (`offer.variant_id`, read-only). |
|
|
26
|
-
| **Seller** | An offer belongs to one store (`offer.seller_id`, read-only). |
|
|
74
|
+
Every row returned also carries an `offer_id` price rule with the offer’s id.
|
|
27
75
|
|
|
28
|
-
##
|
|
76
|
+
## Offer to inventory item
|
|
29
77
|
|
|
30
|
-
|
|
31
|
-
| --- | --- |
|
|
32
|
-
| **Price** | An offer has many prices: a writable list-link to the offer-owned rows on the master variant's shared price set. |
|
|
33
|
-
| **Inventory item** | An offer has many inventory items: a writable list-link (`offer_inventory_item`) whose pivot carries a `required_quantity` column. |
|
|
78
|
+
Offers and inventory items link many to many. The link table is `offer_inventory_item`, with an extra `required_quantity` integer column that defaults to `1`.
|
|
34
79
|
|
|
35
|
-
|
|
80
|
+
The link has three aliases:
|
|
36
81
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
82
|
+
- **`offer.inventory_items`**: the linked `InventoryItem` rows, without pivot columns
|
|
83
|
+
- **`offer.inventory_item_link`**: the pivot rows, with `required_quantity` and a nested `inventory_item`
|
|
84
|
+
- **`inventory_item.offers`**: the offers that draw on an inventory item
|
|
40
85
|
|
|
41
|
-
|
|
86
|
+
```ts
|
|
87
|
+
const { data: offers } = await query.graph({
|
|
88
|
+
entity: "offer",
|
|
89
|
+
fields: [
|
|
90
|
+
"inventory_item_link.required_quantity",
|
|
91
|
+
"inventory_item_link.inventory_item.sku",
|
|
92
|
+
],
|
|
93
|
+
})
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Cart and order line items to offer
|
|
97
|
+
|
|
98
|
+
Line items link to the offer that was bought. Both links set `isList: true` on the line item side, so one offer can back line items in any number of carts and orders.
|
|
99
|
+
|
|
100
|
+
```ts
|
|
101
|
+
const { data: [cart] } = await query.graph({
|
|
102
|
+
entity: "cart",
|
|
103
|
+
fields: ["items.id", "items.offer.id", "items.offer.seller_id"],
|
|
104
|
+
filters: { id: "cart_01JD8Q1234567" },
|
|
105
|
+
})
|
|
106
|
+
```
|
|
42
107
|
|
|
43
|
-
|
|
44
|
-
| --- | --- |
|
|
45
|
-
| **Cart line item** | A cart line item links to the offer it added; one offer can back many cart line items (list-link on the line-item side). |
|
|
46
|
-
| **Order line item** | An order line item links to the purchased offer; one offer can back many order line items across child orders (list-link on the line-item side). |
|
|
108
|
+
The alias is `offer` on both cart and order line items. At checkout, `mirrorLineItemOfferLinksToOrderStep` copies each cart line item’s link to the new order line item. A subscriber does the same for line items added later by order edits, exchanges, and claims.
|
|
47
109
|
|
|
48
|
-
|
|
49
|
-
Read-only links (Product, Variant, Seller, Shipping profile) resolve from a
|
|
50
|
-
field on the `offer` row and can't be written through the link itself. The
|
|
51
|
-
Price and Inventory-item links are writable and are managed by the offer
|
|
52
|
-
workflows.
|
|
53
|
-
</Note>
|
|
110
|
+
## Product to shipping profile
|
|
54
111
|
|
|
55
|
-
|
|
56
|
-
The cart- and order-line-item links are list-links **on the line-item side** so
|
|
57
|
-
the same offer can be added to many carts and placed on many orders. Without
|
|
58
|
-
that, Medusa would enforce a 1:1 line-item ↔ offer relationship and block
|
|
59
|
-
re-use.
|
|
60
|
-
</Tip>
|
|
112
|
+
`createOffersWorkflow` also writes Medusa’s own product to shipping profile link when the product has none. That link is Medusa’s, not Mercur’s. See [How an offer ships](/platform/offer/concepts/shipping).
|
|
@@ -1,53 +1,88 @@
|
|
|
1
1
|
---
|
|
2
|
-
title: "
|
|
2
|
+
title: "Offer module service"
|
|
3
3
|
sidebarTitle: "Service"
|
|
4
|
-
description: "
|
|
4
|
+
description: "Resolve the Offer module service and use its generated and custom methods."
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
The Offer module
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
The Offer module service reads and writes `Offer` and `OfferCondition` rows directly. Use it in subscribers, scheduled jobs, and your own steps when you don’t need what the workflows add on top.
|
|
8
|
+
|
|
9
|
+
## Resolve the service
|
|
10
|
+
|
|
11
|
+
Resolve the service from the container with `MercurModules.OFFER`.
|
|
10
12
|
|
|
11
13
|
```ts
|
|
12
14
|
import { MercurModules } from "@mercurjs/types"
|
|
13
15
|
|
|
14
16
|
const offerModuleService = container.resolve(MercurModules.OFFER)
|
|
15
17
|
|
|
16
|
-
const [offers, count] = await offerModuleService.listAndCountOffers(
|
|
17
|
-
seller_id: "
|
|
18
|
-
}
|
|
18
|
+
const [offers, count] = await offerModuleService.listAndCountOffers(
|
|
19
|
+
{ seller_id: "sel_01JD8Q1234567" },
|
|
20
|
+
{ take: 20 }
|
|
21
|
+
)
|
|
19
22
|
```
|
|
20
23
|
|
|
24
|
+
<Warning>
|
|
25
|
+
The service writes rows only. It doesn’t create prices, inventory items, or links, emit events, or roll back on failure. Use the [workflows](/platform/offer/reference/workflows) for anything that touches pricing, stock, or links.
|
|
26
|
+
</Warning>
|
|
27
|
+
|
|
21
28
|
## Generated methods
|
|
22
29
|
|
|
23
|
-
The `Offer
|
|
30
|
+
The service extends `MedusaService({ Offer, OfferCondition })`, which generates these methods for each model:
|
|
24
31
|
|
|
25
|
-
|
|
|
32
|
+
| Offer | OfferCondition |
|
|
26
33
|
| --- | --- |
|
|
27
|
-
| `
|
|
28
|
-
| `
|
|
29
|
-
| `
|
|
30
|
-
| `
|
|
31
|
-
| `updateOffers(data)` |
|
|
32
|
-
| `deleteOffers(ids)` |
|
|
34
|
+
| `retrieveOffer(id, config?)` | `retrieveOfferCondition(id, config?)` |
|
|
35
|
+
| `listOffers(filters?, config?)` | `listOfferConditions(filters?, config?)` |
|
|
36
|
+
| `listAndCountOffers(filters?, config?)` | `listAndCountOfferConditions(filters?, config?)` |
|
|
37
|
+
| `createOffers(data)` | `createOfferConditions(data)` |
|
|
38
|
+
| `updateOffers(data)` | `updateOfferConditions(data)` |
|
|
39
|
+
| `deleteOffers(ids)` | `deleteOfferConditions(ids)` |
|
|
40
|
+
| `softDeleteOffers(ids)` | `softDeleteOfferConditions(ids)` |
|
|
41
|
+
| `restoreOffers(ids)` | `restoreOfferConditions(ids)` |
|
|
33
42
|
|
|
34
|
-
##
|
|
43
|
+
## listOffers and listAndCountOffers with group_by_seller
|
|
35
44
|
|
|
36
|
-
|
|
37
|
-
set, the service collapses offers to one row per `(product, seller)` group and
|
|
38
|
-
populates `variant_count` and `offer_ids` on each returned offer.
|
|
45
|
+
Both offer list methods accept an extra `group_by_seller` filter. Without it they behave like the generated methods.
|
|
39
46
|
|
|
40
47
|
```ts
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
48
|
+
listOffers(
|
|
49
|
+
filters?: Record<string, unknown> & { group_by_seller?: boolean },
|
|
50
|
+
config?: FindConfig<OfferDTO>,
|
|
51
|
+
sharedContext?: Context
|
|
52
|
+
): Promise<OfferDTO[]>
|
|
53
|
+
|
|
54
|
+
listAndCountOffers(
|
|
55
|
+
filters?: Record<string, unknown> & { group_by_seller?: boolean },
|
|
56
|
+
config?: FindConfig<OfferDTO>,
|
|
57
|
+
sharedContext?: Context
|
|
58
|
+
): Promise<[OfferDTO[], number]>
|
|
45
59
|
```
|
|
46
60
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
61
|
+
With `group_by_seller: true`, the service returns one offer per `(product_id, seller_id)` group, the newest by `created_at`. Each returned offer gets `variant_count` and `offer_ids` for its group, and the count is the number of groups.
|
|
62
|
+
|
|
63
|
+
Grouped mode reads only the `product_id` and `seller_id` filters. Pagination uses `config.skip` and `config.take`, with `take` defaulting to 20.
|
|
64
|
+
|
|
65
|
+
## getStatistics
|
|
66
|
+
|
|
67
|
+
Counts offers, and the distinct stores and products behind them, as they stood at a point in time.
|
|
68
|
+
|
|
69
|
+
```ts
|
|
70
|
+
getStatistics(
|
|
71
|
+
input?: OfferStatisticsInput,
|
|
72
|
+
sharedContext?: Context
|
|
73
|
+
): Promise<OfferStatisticsOutput>
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
`input.at` defaults to now. `input.filters.seller_id` takes one id or a list. An offer counts when it was created at or before `at` and wasn’t deleted by then.
|
|
77
|
+
|
|
78
|
+
```ts
|
|
79
|
+
const { offers, sellers, products } =
|
|
80
|
+
await offerModuleService.getStatistics({ at: new Date("2026-10-01") })
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
The result is `{ offers, sellers, products }`, each a number.
|
|
84
|
+
|
|
85
|
+
## Related
|
|
86
|
+
|
|
87
|
+
- [Offer data models](/platform/offer/reference/data-models)
|
|
88
|
+
- [Offer workflows](/platform/offer/reference/workflows)
|
|
@@ -1,66 +1,175 @@
|
|
|
1
1
|
---
|
|
2
|
-
title: "
|
|
2
|
+
title: "Offer workflows"
|
|
3
3
|
sidebarTitle: "Workflows"
|
|
4
|
-
description: "
|
|
4
|
+
description: "Every offer and offer condition workflow, with its input, hooks, and events."
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
`@mercurjs/core/workflows` and run them against the Medusa container.
|
|
7
|
+
The Offer module ships eight workflows: five for offers and their prices and stock, and three for offer conditions. Import them from `@mercurjs/core/workflows` and run them with the Medusa container.
|
|
9
8
|
|
|
10
|
-
|
|
9
|
+
| Workflow | Id | Emits |
|
|
10
|
+
| --- | --- | --- |
|
|
11
|
+
| `createOffersWorkflow` | `create-offers` | `offer.created` |
|
|
12
|
+
| `updateOffersWorkflow` | `update-offers` | `offer.updated` |
|
|
13
|
+
| `upsertOfferPricesWorkflow` | `upsert-offer-prices` | None |
|
|
14
|
+
| `deleteOffersWorkflow` | `delete-offers` | `offer.deleted` |
|
|
15
|
+
| `batchOfferInventoryItemsWorkflow` | `batch-offer-inventory-items` | `offer.updated` |
|
|
16
|
+
| `createOfferConditionsWorkflow` | `create-offer-conditions` | `offer_condition.created` |
|
|
17
|
+
| `updateOfferConditionsWorkflow` | `update-offer-conditions` | `offer_condition.updated` |
|
|
18
|
+
| `deleteOfferConditionsWorkflow` | `delete-offer-conditions` | `offer_condition.deleted` |
|
|
19
|
+
|
|
20
|
+
## createOffersWorkflow
|
|
11
21
|
|
|
12
|
-
|
|
22
|
+
Creates offers with their inventory items, prices, and links. Returns the created `OfferDTO[]`.
|
|
23
|
+
|
|
24
|
+
| Input | Type | Description |
|
|
13
25
|
| --- | --- | --- |
|
|
14
|
-
| `
|
|
15
|
-
| `
|
|
16
|
-
|
|
17
|
-
|
|
26
|
+
| `offers` | `CreateOfferDTO[]` | One entry per offer |
|
|
27
|
+
| `additional_data` | `Record<string, unknown>` | Optional. Passed to `offersCreated` |
|
|
28
|
+
|
|
29
|
+
Each `CreateOfferDTO` requires `seller_id`, `created_by`, `sku`, `variant_id`, `shipping_profile_id`, `prices`, and at least one `inventory_items` entry. Optional fields are `ean`, `upc`, `manage_inventory`, `allow_backorder`, `leadtime_to_ship`, `condition_id`, and `metadata`.
|
|
30
|
+
|
|
31
|
+
An `inventory_items` entry takes `required_quantity` (default `1`), `sku`, `title`, and `stock_levels` (`location_id`, `stocked_quantity`). A price takes `amount`, `currency_code`, and optional `min_quantity`, `max_quantity`, and `rules`.
|
|
32
|
+
|
|
33
|
+
The run creates the inventory items and links them to the offer and to the store of the first entry. It adds prices to each variant’s price set with an `offer_id` rule, creating the price set first if the variant has none. It also links each master product to its first offer’s shipping profile when the product has no profile.
|
|
34
|
+
|
|
35
|
+
| Hook | Payload |
|
|
36
|
+
| --- | --- |
|
|
37
|
+
| `validate` | `{ input }` |
|
|
38
|
+
| `offersCreated` | `{ offers: OfferDTO[], additional_data }`, each offer with `prices` |
|
|
18
39
|
|
|
19
|
-
##
|
|
40
|
+
## updateOffersWorkflow
|
|
20
41
|
|
|
21
|
-
|
|
42
|
+
Updates offer rows and, for entries with a `prices` array, replaces that offer’s prices through `upsertOfferPricesWorkflow`. Returns the updated `OfferDTO[]`.
|
|
43
|
+
|
|
44
|
+
| Input | Type | Description |
|
|
45
|
+
| --- | --- | --- |
|
|
46
|
+
| `offers` | `UpdateOfferDTO[]` | Keyed by `id`. Every other field is optional |
|
|
47
|
+
| `updated_by` | `string` | Optional. The actor id, passed to both hooks |
|
|
48
|
+
| `additional_data` | `Record<string, unknown>` | Optional. Passed to `offersUpdated` |
|
|
22
49
|
|
|
23
|
-
|
|
50
|
+
`UpdateOfferDTO` accepts `sku`, `shipping_profile_id`, `manage_inventory`, `allow_backorder`, `leadtime_to_ship`, `condition_id`, `metadata`, and `prices`. Leave out `prices` to keep the current rows. A `condition_id` must point at an active condition.
|
|
51
|
+
|
|
52
|
+
| Hook | Payload |
|
|
24
53
|
| --- | --- |
|
|
25
|
-
| `
|
|
26
|
-
| `
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
| `batchOfferInventoryItemsWorkflow` | `validate`, `offerInventoryItemsBatched` |
|
|
54
|
+
| `validate` | `{ input }` |
|
|
55
|
+
| `offersUpdated` | `{ offers, previous_offers, updated_by, additional_data }` |
|
|
56
|
+
|
|
57
|
+
`previous_offers` holds the offer rows as they were before the write.
|
|
30
58
|
|
|
31
|
-
|
|
59
|
+
## upsertOfferPricesWorkflow
|
|
60
|
+
|
|
61
|
+
Replaces each listed offer’s prices without touching other offers on the same variant. Returns `{ offer_id, price_ids }[]` for the prices it created.
|
|
62
|
+
|
|
63
|
+
| Input | Type | Description |
|
|
64
|
+
| --- | --- | --- |
|
|
65
|
+
| `offers` | `{ id, prices: UpsertOfferPriceDTO[] }[]` | The full new price list per offer |
|
|
66
|
+
| `updated_by` | `string` | Optional. Passed to the hook |
|
|
67
|
+
|
|
68
|
+
A price with an `id` updates that row, a price without one is added, and owned prices left out are deleted with their links. An `id` that belongs to another offer fails the run with `Price … does not belong to offer …`.
|
|
32
69
|
|
|
33
70
|
| Hook | Payload |
|
|
34
71
|
| --- | --- |
|
|
35
|
-
| `
|
|
36
|
-
| `offersUpdated` | `{ offers: OfferDTO[], previous_offers: OfferDTO[], updated_by?, additional_data? }` — `previous_offers` is the row state before the write |
|
|
37
|
-
| `offerPricesUpserted` | `{ offers: Array<{ offer_id, previous_prices: OfferPriceDTO[], prices: OfferPriceDTO[], created: string[], updated: string[], deleted: string[] }>, updated_by: string \| null }` |
|
|
38
|
-
| `offersDeleted` | `{ ids: string[], offers: OfferDTO[], additional_data? }` — `offers` is a snapshot taken before deletion, including `prices` |
|
|
72
|
+
| `offerPricesUpserted` | `{ offers, updated_by }`, where `updated_by` is `string \| null` |
|
|
39
73
|
|
|
40
|
-
|
|
74
|
+
Each entry in `offers` has these fields:
|
|
41
75
|
|
|
42
|
-
|
|
76
|
+
- **`offer_id`**: the offer
|
|
77
|
+
- **`previous_prices`**: the offer’s prices before the write
|
|
78
|
+
- **`prices`**: the offer’s prices after the write
|
|
79
|
+
- **`created`**: ids of new prices
|
|
80
|
+
- **`updated`**: ids whose amount, currency, quantity bounds, or rules changed; an unchanged row resent with its `id` isn’t listed
|
|
81
|
+
- **`deleted`**: ids of removed prices
|
|
43
82
|
|
|
44
|
-
|
|
83
|
+
The hook also fires when `updateOffersWorkflow` runs the upsert, so one handler covers both entry points:
|
|
84
|
+
|
|
85
|
+
```ts title="src/workflows/hooks/offer-prices-upserted.ts"
|
|
45
86
|
import { upsertOfferPricesWorkflow } from "@mercurjs/core/workflows"
|
|
46
|
-
import { StepResponse } from "@medusajs/framework/workflows-sdk"
|
|
47
87
|
|
|
48
88
|
upsertOfferPricesWorkflow.hooks.offerPricesUpserted(
|
|
49
89
|
async ({ offers, updated_by }, { container }) => {
|
|
50
|
-
|
|
51
|
-
|
|
90
|
+
const logger = container.resolve("logger")
|
|
91
|
+
for (const { offer_id, updated, deleted } of offers) {
|
|
92
|
+
logger.info(`${offer_id} by ${updated_by}: ${updated.length} changed`)
|
|
93
|
+
logger.info(`${offer_id}: ${deleted.length} removed`)
|
|
52
94
|
}
|
|
53
|
-
return new StepResponse(undefined)
|
|
54
95
|
}
|
|
55
96
|
)
|
|
56
97
|
```
|
|
57
98
|
|
|
58
|
-
|
|
99
|
+
Deleted prices are gone for good, so this payload is the only place to read their old values.
|
|
100
|
+
|
|
101
|
+
## deleteOffersWorkflow
|
|
102
|
+
|
|
103
|
+
Soft-deletes offers. It has no step that removes their prices or inventory items.
|
|
104
|
+
|
|
105
|
+
| Input | Type | Description |
|
|
106
|
+
| --- | --- | --- |
|
|
107
|
+
| `ids` | `string[]` | The offers to delete |
|
|
108
|
+
| `additional_data` | `Record<string, unknown>` | Optional. Passed to `offersDeleted` |
|
|
109
|
+
|
|
110
|
+
| Hook | Payload |
|
|
111
|
+
| --- | --- |
|
|
112
|
+
| `offersDeleted` | `{ ids, offers, additional_data }` |
|
|
113
|
+
|
|
114
|
+
`offers` is a snapshot taken before the delete, with each offer’s `prices`.
|
|
115
|
+
|
|
116
|
+
## batchOfferInventoryItemsWorkflow
|
|
117
|
+
|
|
118
|
+
Creates, updates, and removes `offer ↔ inventory_item` links on one offer. Returns `{ created, updated, deleted }`.
|
|
119
|
+
|
|
120
|
+
| Input | Type | Description |
|
|
121
|
+
| --- | --- | --- |
|
|
122
|
+
| `offer_id` | `string` | The offer to change |
|
|
123
|
+
| `create` | `{ inventory_item_id, required_quantity? }[]` | Optional. Items to link, `required_quantity` defaults to `1` |
|
|
124
|
+
| `update` | `{ inventory_item_id, required_quantity }[]` | Optional. New quantities for linked items |
|
|
125
|
+
| `delete` | `string[]` | Optional. Inventory item ids to unlink |
|
|
126
|
+
| `additional_data` | `Record<string, unknown>` | Optional. Passed to `offerInventoryItemsBatched` |
|
|
127
|
+
|
|
128
|
+
The run fails when the offer doesn’t exist, an item in `create` doesn’t exist, an item in `delete` isn’t linked, or an id repeats within or across the lists.
|
|
129
|
+
|
|
130
|
+
| Hook | Payload |
|
|
131
|
+
| --- | --- |
|
|
132
|
+
| `validate` | `{ input }` |
|
|
133
|
+
| `offerInventoryItemsBatched` | `{ offer_id, result, additional_data }` |
|
|
134
|
+
|
|
135
|
+
## createOfferConditionsWorkflow
|
|
136
|
+
|
|
137
|
+
Creates offer conditions. Returns `OfferConditionDTO[]`.
|
|
138
|
+
|
|
139
|
+
| Input | Type | Description |
|
|
140
|
+
| --- | --- | --- |
|
|
141
|
+
| `offer_conditions` | `CreateOfferConditionDTO[]` | `code` and `label`, plus optional `is_active`, `rank`, and `metadata` |
|
|
142
|
+
|
|
143
|
+
| Hook | Payload |
|
|
144
|
+
| --- | --- |
|
|
145
|
+
| `offerConditionsCreated` | `{ offer_conditions }` |
|
|
146
|
+
|
|
147
|
+
## updateOfferConditionsWorkflow
|
|
148
|
+
|
|
149
|
+
Updates offer conditions by `id`. Returns `OfferConditionDTO[]`.
|
|
150
|
+
|
|
151
|
+
| Input | Type | Description |
|
|
152
|
+
| --- | --- | --- |
|
|
153
|
+
| `offer_conditions` | `UpdateOfferConditionDTO[]` | `id`, plus any of `code`, `label`, `is_active`, `rank`, and `metadata` |
|
|
154
|
+
|
|
155
|
+
| Hook | Payload |
|
|
156
|
+
| --- | --- |
|
|
157
|
+
| `offerConditionsUpdated` | `{ offer_conditions }` |
|
|
158
|
+
|
|
159
|
+
## deleteOfferConditionsWorkflow
|
|
160
|
+
|
|
161
|
+
Soft-deletes offer conditions. The run fails when an id doesn’t exist or when any offer still uses one of the conditions.
|
|
162
|
+
|
|
163
|
+
| Input | Type | Description |
|
|
164
|
+
| --- | --- | --- |
|
|
165
|
+
| `ids` | `string[]` | The conditions to delete |
|
|
166
|
+
|
|
167
|
+
| Hook | Payload |
|
|
168
|
+
| --- | --- |
|
|
169
|
+
| `offerConditionsDeleted` | `{ ids }` |
|
|
59
170
|
|
|
60
|
-
|
|
61
|
-
- **Inventory** items passed to `createOffersWorkflow` are created and linked to the offer (and the store) in the same run. Every offer needs at least one.
|
|
62
|
-
- Workflows run with compensation, so a failed create rolls back the inventory items, prices, and links it had already created.
|
|
171
|
+
## Related
|
|
63
172
|
|
|
64
|
-
|
|
65
|
-
[
|
|
66
|
-
an offer
|
|
173
|
+
- [Offer events](/platform/offer/reference/events)
|
|
174
|
+
- [Offer service methods](/platform/offer/reference/service)
|
|
175
|
+
- [Create an offer](/platform/offer/guides/create-an-offer)
|