@mercurjs/docs 2.3.8-canary.2 → 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.
- package/content/learn/architecture.mdx +166 -263
- 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/panel-extensions/layouts.mdx +169 -0
- package/llms.txt +112 -111
- package/package.json +1 -1
|
@@ -1,70 +1,146 @@
|
|
|
1
1
|
---
|
|
2
|
-
title: "Create a master product"
|
|
2
|
+
title: "Create a master product from server code"
|
|
3
3
|
sidebarTitle: "Create a master product"
|
|
4
|
-
description: "
|
|
4
|
+
description: "Add a product to the shared catalog with Mercur's createProductsWorkflow, including attributes, variants, and an allowlist."
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
own server code, for example in a seed script, a custom API route, or an import
|
|
9
|
-
flow.
|
|
7
|
+
Run `createProductsWorkflow` to add a product to the shared catalog from a custom route, a seed script, or an import job. It creates the product and its variants, attaches attributes, links any stores you name, and records the submission.
|
|
10
8
|
|
|
11
|
-
|
|
12
|
-
attaches attributes and variants, records the submission for audit, and
|
|
13
|
-
optionally allowlists the submitting store. Run it from any place that has access
|
|
14
|
-
to the Medusa container.
|
|
9
|
+
## Before you begin
|
|
15
10
|
|
|
16
|
-
|
|
11
|
+
You need the following in place:
|
|
17
12
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
13
|
+
- A Mercur server with `@mercurjs/core` installed
|
|
14
|
+
- The ids of any [attributes](/platform/attribute/overview) and stores you want to reference
|
|
15
|
+
|
|
16
|
+
<Warning>
|
|
17
|
+
Import `createProductsWorkflow` from `@mercurjs/core/workflows`, not `@medusajs/medusa/core-flows`. Medusa's workflow has the same name but skips attributes, the allowlist, and the audit record.
|
|
18
|
+
</Warning>
|
|
19
|
+
|
|
20
|
+
## Create the product
|
|
21
|
+
|
|
22
|
+
<Steps>
|
|
23
|
+
<Step title="Add a route that runs the workflow">
|
|
24
|
+
Create an admin route. The workflow returns the created `ProductDTO` objects in input order.
|
|
25
|
+
|
|
26
|
+
```ts title="src/api/admin/supplier-products/route.ts"
|
|
27
|
+
import type {
|
|
28
|
+
AuthenticatedMedusaRequest,
|
|
29
|
+
MedusaResponse,
|
|
30
|
+
} from "@medusajs/framework/http"
|
|
31
|
+
import { createProductsWorkflow } from "@mercurjs/core/workflows"
|
|
32
|
+
|
|
33
|
+
export async function POST(
|
|
34
|
+
req: AuthenticatedMedusaRequest,
|
|
35
|
+
res: MedusaResponse
|
|
36
|
+
) {
|
|
37
|
+
const { result } = await createProductsWorkflow(req.scope).run({
|
|
38
|
+
input: {
|
|
39
|
+
products: [{ title: "Aeron Chair", status: "proposed" }],
|
|
40
|
+
created_by: req.auth_context.actor_id,
|
|
41
|
+
},
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
res.status(201).json({ product: result[0] })
|
|
45
|
+
}
|
|
46
|
+
```
|
|
21
47
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
48
|
+
`created_by` is required. Mercur stores it on the `PRODUCT_ADD` change as the submitter. Without a `status`, the product starts as `draft`.
|
|
49
|
+
</Step>
|
|
50
|
+
<Step title="Add attributes and variants">
|
|
51
|
+
Pass `attributes` instead of native `options`. A `multi_select` attribute with `is_variant_axis` becomes the option that variants bind to by title.
|
|
52
|
+
|
|
53
|
+
```ts
|
|
54
|
+
const product = {
|
|
55
|
+
title: "Aeron Chair",
|
|
56
|
+
status: "proposed" as const,
|
|
57
|
+
attributes: [
|
|
26
58
|
{
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
seller_ids: ["sel_123"],
|
|
59
|
+
id: "pattr_01JD8Q1234567",
|
|
60
|
+
value_ids: ["pattrval_01JD8Q1234567"],
|
|
30
61
|
},
|
|
62
|
+
{ title: "Size", is_variant_axis: true, values: ["B", "C"] },
|
|
31
63
|
],
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
```
|
|
64
|
+
variants: [
|
|
65
|
+
{ title: "Size B", options: { Size: "B" } },
|
|
66
|
+
{ title: "Size C", options: { Size: "C" } },
|
|
67
|
+
],
|
|
68
|
+
}
|
|
69
|
+
```
|
|
39
70
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
submitted the product for the audit trail.
|
|
45
|
-
</Note>
|
|
71
|
+
The first entry attaches a global attribute with a chosen value. The second creates a product-scoped axis. New variants get `manage_inventory: false` unless you set it, since stock lives on [offers](/platform/offer/overview).
|
|
72
|
+
</Step>
|
|
73
|
+
<Step title="Restrict the product to stores">
|
|
74
|
+
Add `seller_ids` to a product to put those stores on its [allowlist](/platform/catalog/concepts/product-seller-allowlist).
|
|
46
75
|
|
|
47
|
-
|
|
76
|
+
```ts
|
|
77
|
+
const product = {
|
|
78
|
+
title: "Aeron Chair",
|
|
79
|
+
seller_ids: ["sel_01JD8Q1234567", "sel_01JD8Q1234568"],
|
|
80
|
+
}
|
|
81
|
+
```
|
|
48
82
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
private until it's ready, or `status: "published"` from a trusted operator flow
|
|
53
|
-
to skip review. See the
|
|
54
|
-
[status lifecycle](/platform/catalog/concepts/status-lifecycle).
|
|
83
|
+
Only these 2 stores can see and sell the product once it's published. Leave `seller_ids` out to open it to every store.
|
|
84
|
+
</Step>
|
|
85
|
+
</Steps>
|
|
55
86
|
|
|
56
|
-
##
|
|
87
|
+
## Submit on behalf of a store
|
|
57
88
|
|
|
58
|
-
|
|
59
|
-
you persist marketplace-specific data alongside the product without forking the
|
|
60
|
-
workflow.
|
|
89
|
+
Pass the store's seller id as `created_by` to make the product a submission from that store. That's what the vendor API does, and it's how Mercur lets a store see its own `draft` and `proposed` products.
|
|
61
90
|
|
|
62
91
|
```ts
|
|
63
|
-
await createProductsWorkflow(
|
|
92
|
+
await createProductsWorkflow(container).run({
|
|
64
93
|
input: {
|
|
65
|
-
products: [{ title: "
|
|
66
|
-
created_by: "
|
|
67
|
-
additional_data: { source: "supplier-feed" },
|
|
94
|
+
products: [{ title: "Desk lamp", status: "proposed" }],
|
|
95
|
+
created_by: "sel_01JD8Q1234567",
|
|
68
96
|
},
|
|
69
97
|
})
|
|
70
98
|
```
|
|
99
|
+
|
|
100
|
+
The product appears in that store's product list as a proposed submission, and stays hidden from other stores until it's published.
|
|
101
|
+
|
|
102
|
+
## Validate input with the validate hook
|
|
103
|
+
|
|
104
|
+
The workflow runs a `validate` hook before it creates anything. Throw from the handler to stop the run.
|
|
105
|
+
|
|
106
|
+
```ts title="src/workflows/hooks/validate-products.ts"
|
|
107
|
+
import { createProductsWorkflow } from "@mercurjs/core/workflows"
|
|
108
|
+
import { MedusaError } from "@medusajs/framework/utils"
|
|
109
|
+
|
|
110
|
+
createProductsWorkflow.hooks.validate(async ({ products }) => {
|
|
111
|
+
const missing = products.find((p) => !p.description)
|
|
112
|
+
if (missing) {
|
|
113
|
+
throw new MedusaError(
|
|
114
|
+
MedusaError.Types.INVALID_DATA,
|
|
115
|
+
`Product "${missing.title}" needs a description`
|
|
116
|
+
)
|
|
117
|
+
}
|
|
118
|
+
})
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
The hook receives the raw `input` and its `products` array, so you can check any field before Medusa writes rows.
|
|
122
|
+
|
|
123
|
+
## Attach custom data with additional_data
|
|
124
|
+
|
|
125
|
+
Pass `additional_data` to carry extra values through the run. Mercur forwards it to Medusa's product and variant workflows and to the `productsCreated` hook.
|
|
126
|
+
|
|
127
|
+
```ts title="src/workflows/hooks/products-created.ts"
|
|
128
|
+
import { createProductsWorkflow } from "@mercurjs/core/workflows"
|
|
129
|
+
|
|
130
|
+
createProductsWorkflow.hooks.productsCreated(
|
|
131
|
+
async ({ products, additional_data }, { container }) => {
|
|
132
|
+
const logger = container.resolve("logger")
|
|
133
|
+
const source = additional_data?.source
|
|
134
|
+
|
|
135
|
+
logger.info(`Imported ${products.length} products from ${source}`)
|
|
136
|
+
}
|
|
137
|
+
)
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Call the workflow with `additional_data: { source: "supplier_feed" }` and the handler receives it next to the created products.
|
|
141
|
+
|
|
142
|
+
## Next steps
|
|
143
|
+
|
|
144
|
+
- [Publish or reject a product](/platform/catalog/guides/publish-or-reject-a-product) to review what you created
|
|
145
|
+
- [Allowlist stores](/platform/catalog/guides/allowlist-stores) to change the store list later
|
|
146
|
+
- [Create an offer](/platform/offer/guides/create-an-offer) so a store can sell the product
|
|
@@ -1,79 +1,144 @@
|
|
|
1
1
|
---
|
|
2
|
-
title: "Publish or reject a product"
|
|
2
|
+
title: "Publish or reject a proposed product"
|
|
3
3
|
sidebarTitle: "Publish or reject"
|
|
4
|
-
description: "
|
|
4
|
+
description: "Review a store's product submission from server code: publish it, reject it, or ask for a revision."
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
server code. Each transition has a dedicated workflow so the audit trail, events,
|
|
9
|
-
and side effects run consistently.
|
|
7
|
+
Run Mercur's review workflows to decide on a `proposed` product from a custom route, a subscriber, or a scheduled job. Each one checks the status, records who reviewed the product, and emits an event.
|
|
10
8
|
|
|
11
|
-
|
|
12
|
-
or ask the submitter for a revision. Every workflow validates that the product is
|
|
13
|
-
currently `proposed` before it runs.
|
|
9
|
+
## Before you begin
|
|
14
10
|
|
|
15
|
-
|
|
11
|
+
You need the following in place:
|
|
16
12
|
|
|
17
|
-
|
|
18
|
-
|
|
13
|
+
- A product in `proposed` status, such as a store submission
|
|
14
|
+
- The id of the operator or system acting, for the audit record
|
|
19
15
|
|
|
20
|
-
|
|
21
|
-
|
|
16
|
+
The workflows fail with a `NOT_ALLOWED` error for any product that isn't `proposed`.
|
|
17
|
+
|
|
18
|
+
## Review a product
|
|
19
|
+
|
|
20
|
+
<Steps>
|
|
21
|
+
<Step title="Publish it">
|
|
22
|
+
`confirmProductsWorkflow` publishes one or more products in a single run. `internal_note` is stored on each audit record for operators.
|
|
23
|
+
|
|
24
|
+
```ts title="src/api/admin/reviews/publish/route.ts"
|
|
25
|
+
import type {
|
|
26
|
+
AuthenticatedMedusaRequest,
|
|
27
|
+
MedusaResponse,
|
|
28
|
+
} from "@medusajs/framework/http"
|
|
29
|
+
import { confirmProductsWorkflow } from "@mercurjs/core/workflows"
|
|
30
|
+
|
|
31
|
+
type Body = { product_ids: string[] }
|
|
32
|
+
|
|
33
|
+
export async function POST(
|
|
34
|
+
req: AuthenticatedMedusaRequest<Body>,
|
|
35
|
+
res: MedusaResponse
|
|
36
|
+
) {
|
|
37
|
+
await confirmProductsWorkflow(req.scope).run({
|
|
38
|
+
input: {
|
|
39
|
+
product_ids: req.body.product_ids,
|
|
40
|
+
actor_id: req.auth_context.actor_id,
|
|
41
|
+
internal_note: "Checked against the supplier sheet",
|
|
42
|
+
},
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
res.json({ published: req.body.product_ids })
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
If any product isn't `proposed`, the run fails and none of them change.
|
|
50
|
+
</Step>
|
|
51
|
+
<Step title="Or reject it">
|
|
52
|
+
`rejectProductWorkflow` handles one product. `message` is stored as the change's `external_note`, the note the store can read.
|
|
53
|
+
|
|
54
|
+
```ts
|
|
55
|
+
import { rejectProductWorkflow } from "@mercurjs/core/workflows"
|
|
56
|
+
|
|
57
|
+
await rejectProductWorkflow(container).run({
|
|
58
|
+
input: {
|
|
59
|
+
product_id: "prod_01JD8Q1234567",
|
|
60
|
+
message: "Images don't match the description",
|
|
61
|
+
actor_id: "user_01JD8Q1234567",
|
|
62
|
+
},
|
|
63
|
+
})
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
The product moves to `rejected` and leaves the store's product list.
|
|
67
|
+
</Step>
|
|
68
|
+
<Step title="Or ask for a revision">
|
|
69
|
+
`requestProductChangeWorkflow` keeps the product `proposed` and records a `CHANGE_REQUESTED` action with your message.
|
|
70
|
+
|
|
71
|
+
```ts
|
|
72
|
+
import { requestProductChangeWorkflow } from "@mercurjs/core/workflows"
|
|
73
|
+
|
|
74
|
+
await requestProductChangeWorkflow(container).run({
|
|
75
|
+
input: {
|
|
76
|
+
product_id: "prod_01JD8Q1234567",
|
|
77
|
+
message: "Add a size B variant",
|
|
78
|
+
actor_id: "user_01JD8Q1234567",
|
|
79
|
+
},
|
|
80
|
+
})
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
The product stays in the review queue, and your message is stored as the change's `external_note`.
|
|
84
|
+
</Step>
|
|
85
|
+
</Steps>
|
|
86
|
+
|
|
87
|
+
## Run a review from a subscriber
|
|
88
|
+
|
|
89
|
+
Review products automatically by running a workflow when they're created. This subscriber publishes products from one trusted store.
|
|
90
|
+
|
|
91
|
+
```ts title="src/subscribers/auto-publish-trusted.ts"
|
|
92
|
+
import type { SubscriberArgs, SubscriberConfig } from "@medusajs/framework"
|
|
22
93
|
import { confirmProductsWorkflow } from "@mercurjs/core/workflows"
|
|
23
94
|
|
|
24
|
-
export async function
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
95
|
+
export default async function autoPublish({
|
|
96
|
+
event,
|
|
97
|
+
container,
|
|
98
|
+
}: SubscriberArgs<{ id: string }>) {
|
|
99
|
+
const query = container.resolve("query")
|
|
100
|
+
const { data: [action] } = await query.graph({
|
|
101
|
+
entity: "product_change_action",
|
|
102
|
+
fields: ["product_change.created_by"],
|
|
103
|
+
filters: { product_id: event.data.id, action: "PRODUCT_ADD" },
|
|
31
104
|
})
|
|
105
|
+
if (action?.product_change?.created_by !== "sel_01JD8Q1234567") return
|
|
32
106
|
|
|
33
|
-
|
|
107
|
+
await confirmProductsWorkflow(container).run({
|
|
108
|
+
input: { product_ids: [event.data.id], actor_id: "system" },
|
|
109
|
+
})
|
|
34
110
|
}
|
|
111
|
+
|
|
112
|
+
export const config: SubscriberConfig = { event: "product.created" }
|
|
35
113
|
```
|
|
36
114
|
|
|
37
|
-
|
|
115
|
+
The handler reads the `PRODUCT_ADD` record to find the submitter, then publishes. `product.created` fires twice for each product created through Mercur's workflow, so the second run fails the `proposed` check. Catch that error, or check the status first.
|
|
38
116
|
|
|
39
|
-
|
|
40
|
-
import { rejectProductWorkflow } from "@mercurjs/core/workflows"
|
|
117
|
+
## React to the outcome with hooks
|
|
41
118
|
|
|
42
|
-
|
|
43
|
-
input: {
|
|
44
|
-
product_id: "prod_123",
|
|
45
|
-
message: "Images don't match the description",
|
|
46
|
-
actor_id: "usr_123",
|
|
47
|
-
},
|
|
48
|
-
})
|
|
49
|
-
```
|
|
119
|
+
Each review workflow exposes a hook that runs after the status change. Use it for side effects that must happen inside the same run.
|
|
50
120
|
|
|
51
|
-
|
|
121
|
+
| Workflow | Hook | Hook input |
|
|
122
|
+
| --- | --- | --- |
|
|
123
|
+
| `confirmProductsWorkflow` | `productsConfirmed` | `product_ids`, `internal_note`, `additional_data` |
|
|
124
|
+
| `rejectProductWorkflow` | `productRejected` | `product_id`, `message`, `additional_data` |
|
|
125
|
+
| `requestProductChangeWorkflow` | `productChangeRequested` | `product_id`, `message`, `additional_data` |
|
|
52
126
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
`CHANGE_REQUESTED` action is recorded for the submitter to act on:
|
|
56
|
-
|
|
57
|
-
```ts
|
|
58
|
-
import { requestProductChangeWorkflow } from "@mercurjs/core/workflows"
|
|
127
|
+
```ts title="src/workflows/hooks/product-rejected.ts"
|
|
128
|
+
import { rejectProductWorkflow } from "@mercurjs/core/workflows"
|
|
59
129
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
})
|
|
130
|
+
rejectProductWorkflow.hooks.productRejected(
|
|
131
|
+
async ({ product_id, message }, { container }) => {
|
|
132
|
+
const logger = container.resolve("logger")
|
|
133
|
+
logger.info(`Rejected ${product_id}: ${message ?? "no reason"}`)
|
|
134
|
+
}
|
|
135
|
+
)
|
|
67
136
|
```
|
|
68
137
|
|
|
69
|
-
|
|
70
|
-
These workflows require the product to be `proposed`. Running them against a
|
|
71
|
-
`draft`, `published`, or `rejected` product fails validation rather than
|
|
72
|
-
forcing the transition.
|
|
73
|
-
</Warning>
|
|
138
|
+
For work that can run later, subscribe to `product.published`, `product.rejected`, or `product.change-requested` instead.
|
|
74
139
|
|
|
75
|
-
##
|
|
140
|
+
## Next steps
|
|
76
141
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
[
|
|
142
|
+
- [Event reference](/platform/catalog/reference/events) for payloads
|
|
143
|
+
- [Status lifecycle](/platform/catalog/concepts/status-lifecycle) for every transition
|
|
144
|
+
- [Request a revision](/platform/product-edit/guides/request-a-revision) for edits to published products
|
|
@@ -1,90 +1,94 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: "Catalog"
|
|
3
3
|
sidebarTitle: "Overview"
|
|
4
|
-
description: "
|
|
4
|
+
description: "Keep one shared product catalog, decide which stores may sell each product, and review what stores submit."
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
Use Mercur to
|
|
8
|
-
allowed to sell what.
|
|
7
|
+
Use Mercur to keep one product catalog for the whole marketplace and control which stores can sell each product.
|
|
9
8
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
categories, collections, tags, and types. A store never owns a product. It sells
|
|
15
|
-
against a shared master product through an [offer](/platform/offer/overview).
|
|
9
|
+
The catalog is Medusa's Product module (`Modules.PRODUCT`) with a marketplace layer on top. Products, variants, categories, collections, tags, and types are native Medusa models. Mercur adds the `product_seller` allowlist, a review flow over the product `status`, an audit record for every submission, and its own product workflows. No store owns a product: a store sells a master product through an [offer](/platform/offer/overview) that carries its own SKU, price, and stock.
|
|
10
|
+
|
|
11
|
+
<img className="block dark:hidden" src="/images/figures/catalog-overview-light.svg" alt="A product_seller link table behind a Products list of the shared catalog: each master product shows its status, the stores on its allowlist, and its offer count, with the Linen shirt restricted to ACME highlighted." />
|
|
12
|
+
<img className="hidden dark:block" src="/images/figures/catalog-overview-dark.svg" alt="A product_seller link table behind a Products list of the shared catalog: each master product shows its status, the stores on its allowlist, and its offer count, with the Linen shirt restricted to ACME highlighted." />
|
|
16
13
|
|
|
17
14
|
<Note>
|
|
18
|
-
|
|
19
|
-
variants, categories, and collections are Medusa's native `Product` module.
|
|
20
|
-
Mercur adds the `product_seller` allowlist (which stores may sell a product),
|
|
21
|
-
the `draft` → `proposed` → `published` / `rejected` status lifecycle, and its
|
|
22
|
-
own product workflows. Products are **shared master products**, not
|
|
23
|
-
seller-owned, and a store sells one via an [offer](/platform/offer/overview).
|
|
15
|
+
There's no Mercur catalog module. Resolve the service with `Modules.PRODUCT` from `@medusajs/framework/utils`, and run Mercur's product workflows from `@mercurjs/core/workflows`. Mercur's `createProductsWorkflow` has the same name as Medusa's, so check the import path.
|
|
24
16
|
</Note>
|
|
25
17
|
|
|
26
18
|
## Key features
|
|
27
19
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
- **
|
|
31
|
-
- **
|
|
32
|
-
- **
|
|
33
|
-
- **
|
|
20
|
+
The catalog layer covers how products enter the marketplace and who can sell them:
|
|
21
|
+
|
|
22
|
+
- **Shared master products**: one `Product` record per item, sold by any number of stores through offers
|
|
23
|
+
- **Store allowlist**: the `product_seller` link restricts a product to the stores on it; a product with no rows is open to every store
|
|
24
|
+
- **Review flow**: vendor submissions start as `proposed` and an operator publishes or rejects them
|
|
25
|
+
- **Submission audit**: every create and review step writes a confirmed `ProductChange` record with the actor who did it
|
|
26
|
+
- **Attribute-driven variants**: products take Mercur attributes on create, and variant-axis attributes become the options variants bind to
|
|
27
|
+
- **Native structure**: variants, categories, collections, tags, and types come straight from Medusa's Product module
|
|
28
|
+
|
|
29
|
+
## How it works
|
|
30
|
+
|
|
31
|
+
A product moves through the catalog in a fixed order:
|
|
32
|
+
|
|
33
|
+
1. A store or an operator runs `createProductsWorkflow`. Vendor submissions default to `proposed`.
|
|
34
|
+
2. The workflow records a `PRODUCT_ADD` change with the creator as `created_by`, and links any `seller_ids` to the product.
|
|
35
|
+
3. An operator runs `confirmProductsWorkflow`, `rejectProductWorkflow`, or `requestProductChangeWorkflow` on the proposed product.
|
|
36
|
+
4. Once `published`, the product shows in the Store API and to every store its allowlist permits.
|
|
37
|
+
5. Each store that sells it creates an offer against one of its variants.
|
|
34
38
|
|
|
35
39
|
## Get started
|
|
36
40
|
|
|
37
|
-
Learn how the
|
|
41
|
+
Learn how master products, the allowlist, and statuses fit together.
|
|
38
42
|
|
|
39
43
|
<CardGroup cols={2}>
|
|
40
44
|
<Card title="Master products" icon="box" href="/platform/catalog/concepts/master-products">
|
|
41
|
-
|
|
45
|
+
Why products belong to the catalog and how Mercur records who submitted them.
|
|
42
46
|
</Card>
|
|
43
47
|
<Card title="The store allowlist" icon="user-lock" href="/platform/catalog/concepts/product-seller-allowlist">
|
|
44
|
-
|
|
48
|
+
How the `product_seller` link decides which stores see and sell a product.
|
|
45
49
|
</Card>
|
|
46
50
|
<Card title="Status lifecycle" icon="arrows-rotate" href="/platform/catalog/concepts/status-lifecycle">
|
|
47
|
-
|
|
51
|
+
The four statuses and the workflow behind each transition.
|
|
48
52
|
</Card>
|
|
49
53
|
<Card title="Variants & categories" icon="sitemap" href="/platform/catalog/concepts/variants-categories-collections">
|
|
50
|
-
|
|
54
|
+
The Medusa models that structure the catalog, and where Mercur extends them.
|
|
51
55
|
</Card>
|
|
52
56
|
</CardGroup>
|
|
53
57
|
|
|
54
58
|
## Examples
|
|
55
59
|
|
|
56
|
-
Build against the
|
|
60
|
+
Build against the catalog in your own server code.
|
|
57
61
|
|
|
58
62
|
<CardGroup cols={2}>
|
|
59
63
|
<Card title="Create a master product" icon="plus" href="/platform/catalog/guides/create-a-master-product">
|
|
60
|
-
Run `createProductsWorkflow`
|
|
64
|
+
Run `createProductsWorkflow` with attributes, variants, and an allowlist.
|
|
61
65
|
</Card>
|
|
62
66
|
<Card title="Allowlist stores" icon="user-plus" href="/platform/catalog/guides/allowlist-stores">
|
|
63
|
-
|
|
67
|
+
Restrict a product to chosen stores, or open it to all of them again.
|
|
64
68
|
</Card>
|
|
65
69
|
<Card title="Publish or reject" icon="gavel" href="/platform/catalog/guides/publish-or-reject-a-product">
|
|
66
|
-
|
|
70
|
+
Review a proposed product from a route or a subscriber.
|
|
67
71
|
</Card>
|
|
68
72
|
</CardGroup>
|
|
69
73
|
|
|
70
74
|
## Resources
|
|
71
75
|
|
|
72
|
-
|
|
76
|
+
The data models, links, workflows, service methods, and events behind the catalog.
|
|
73
77
|
|
|
74
78
|
<CardGroup cols={2}>
|
|
75
79
|
<Card title="Data models" icon="table" href="/platform/catalog/reference/data-models">
|
|
76
|
-
|
|
80
|
+
`Product`, `ProductVariant`, `ProductCategory`, and the other Product module models.
|
|
77
81
|
</Card>
|
|
78
82
|
<Card title="Links" icon="link" href="/platform/catalog/reference/links">
|
|
79
|
-
How
|
|
83
|
+
How products and categories link to stores, offers, attributes, changes, and media.
|
|
80
84
|
</Card>
|
|
81
85
|
<Card title="Workflows" icon="diagram-project" href="/platform/catalog/reference/workflows">
|
|
82
|
-
Mercur's
|
|
86
|
+
Mercur's create, review, allowlist, and category workflows with their hooks.
|
|
83
87
|
</Card>
|
|
84
88
|
<Card title="Service" icon="gear" href="/platform/catalog/reference/service">
|
|
85
89
|
Medusa's Product module service, resolved with `Modules.PRODUCT`.
|
|
86
90
|
</Card>
|
|
87
91
|
<Card title="Events" icon="bell" href="/platform/catalog/reference/events">
|
|
88
|
-
|
|
92
|
+
The events Mercur emits when products are created and reviewed.
|
|
89
93
|
</Card>
|
|
90
94
|
</CardGroup>
|