@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.
- 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/llms.txt +111 -111
- package/package.json +1 -1
|
@@ -1,76 +1,124 @@
|
|
|
1
1
|
---
|
|
2
|
-
title: "Confirm or
|
|
2
|
+
title: "Confirm, decline, or cancel a product change"
|
|
3
3
|
sidebarTitle: "Confirm or decline"
|
|
4
|
-
description: "Resolve a pending product change from server code."
|
|
4
|
+
description: "Resolve a pending product change from server code and react to the result."
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
Resolve a pending product change from your own code, for example in a custom review tool or an automated approval rule. Each resolution has its own workflow, so the status update, the product write, and the event always happen together.
|
|
8
|
+
|
|
9
|
+
## Before you begin
|
|
10
|
+
|
|
11
|
+
You need the following in place:
|
|
12
|
+
|
|
13
|
+
- A Mercur server with `@mercurjs/core` installed through `withMercur()`
|
|
14
|
+
- A change in `pending` status. Changes stay `pending` only while the `product_request` feature flag is on, the default
|
|
15
|
+
|
|
16
|
+
## Review and resolve a change
|
|
17
|
+
|
|
18
|
+
Find the pending change, then confirm or decline it.
|
|
19
|
+
|
|
20
|
+
<Steps>
|
|
21
|
+
<Step title="Find pending changes">
|
|
22
|
+
List the pending changes on a product with their actions, so a reviewer can see what each one does.
|
|
23
|
+
|
|
24
|
+
```ts
|
|
25
|
+
const query = container.resolve("query")
|
|
26
|
+
|
|
27
|
+
const { data: changes } = await query.graph({
|
|
28
|
+
entity: "product_change",
|
|
29
|
+
fields: ["id", "created_by", "external_note", "actions.*"],
|
|
30
|
+
filters: {
|
|
31
|
+
product_id: "prod_01JD8Q1234567",
|
|
32
|
+
status: "pending",
|
|
33
|
+
},
|
|
34
|
+
})
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Each action’s `details` holds the proposed value and the previous one, as described in [Change actions](/platform/product-edit/concepts/change-actions).
|
|
38
|
+
</Step>
|
|
39
|
+
<Step title="Confirm the change">
|
|
40
|
+
`confirmProductChangeWorkflow` sets the changes to `confirmed`, applies their actions to the product, and emits `product-change.confirmed`.
|
|
41
|
+
|
|
42
|
+
```ts title="src/api/admin/custom/changes/[id]/confirm/route.ts"
|
|
43
|
+
import type {
|
|
44
|
+
AuthenticatedMedusaRequest,
|
|
45
|
+
MedusaResponse,
|
|
46
|
+
} from "@medusajs/framework/http"
|
|
47
|
+
import { confirmProductChangeWorkflow } from "@mercurjs/core/workflows"
|
|
48
|
+
|
|
49
|
+
export async function POST(
|
|
50
|
+
req: AuthenticatedMedusaRequest,
|
|
51
|
+
res: MedusaResponse
|
|
52
|
+
) {
|
|
53
|
+
await confirmProductChangeWorkflow(req.scope).run({
|
|
54
|
+
input: {
|
|
55
|
+
ids: [req.params.id],
|
|
56
|
+
confirmed_by: req.auth_context.actor_id,
|
|
57
|
+
},
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
res.json({ id: req.params.id, status: "confirmed" })
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
The input takes an `ids` array. Pass `internal_note` or `external_note` to overwrite the change’s notes as you confirm it.
|
|
65
|
+
</Step>
|
|
66
|
+
<Step title="Or decline it">
|
|
67
|
+
`rejectProductChangeWorkflow` sets one change to `declined` and leaves the product untouched. Give the seller a reason in `declined_reason`.
|
|
68
|
+
|
|
69
|
+
```ts
|
|
70
|
+
import { rejectProductChangeWorkflow } from "@mercurjs/core/workflows"
|
|
71
|
+
|
|
72
|
+
await rejectProductChangeWorkflow(container).run({
|
|
73
|
+
input: {
|
|
74
|
+
id: "prodch_01JD8Q1234567",
|
|
75
|
+
declined_by: "user_01JD8Q1234567",
|
|
76
|
+
declined_reason: "Photos don't meet the image guidelines",
|
|
77
|
+
},
|
|
78
|
+
})
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
The workflow emits `product-change.declined` with the change id.
|
|
82
|
+
</Step>
|
|
83
|
+
</Steps>
|
|
10
84
|
|
|
11
|
-
##
|
|
12
|
-
|
|
13
|
-
`confirmProductChangeWorkflow` marks the changes `confirmed`, applies their
|
|
14
|
-
pending actions to the product, and emits `product-change.confirmed`.
|
|
15
|
-
|
|
16
|
-
```ts title="src/api/custom/confirm/route.ts"
|
|
17
|
-
import type { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
|
|
18
|
-
import { confirmProductChangeWorkflow } from "@mercurjs/core/workflows"
|
|
19
|
-
|
|
20
|
-
export async function POST(req: MedusaRequest, res: MedusaResponse) {
|
|
21
|
-
await confirmProductChangeWorkflow(req.scope).run({
|
|
22
|
-
input: {
|
|
23
|
-
ids: [req.params.id],
|
|
24
|
-
confirmed_by: req.auth_context?.actor_id,
|
|
25
|
-
},
|
|
26
|
-
})
|
|
27
|
-
|
|
28
|
-
res.sendStatus(200)
|
|
29
|
-
}
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
<Note>
|
|
33
|
-
Confirmation only applies actions that aren't already `applied`, so re-running
|
|
34
|
-
it never writes the same edit twice.
|
|
35
|
-
</Note>
|
|
36
|
-
|
|
37
|
-
## Decline a change
|
|
85
|
+
## Cancel a change
|
|
38
86
|
|
|
39
|
-
|
|
40
|
-
touching the product, and emits `product-change.declined`.
|
|
87
|
+
Cancel a change when it should be withdrawn rather than judged, for example when a seller retracts their own edit. Canceling frees the author to stage a new change on the product.
|
|
41
88
|
|
|
42
89
|
```ts
|
|
43
|
-
import {
|
|
90
|
+
import { cancelProductChangeWorkflow } from "@mercurjs/core/workflows"
|
|
44
91
|
|
|
45
|
-
await
|
|
92
|
+
await cancelProductChangeWorkflow(container).run({
|
|
46
93
|
input: {
|
|
47
|
-
id: "
|
|
48
|
-
|
|
49
|
-
declined_reason: "Images don't meet guidelines",
|
|
94
|
+
id: "prodch_01JD8Q1234567",
|
|
95
|
+
canceled_by: "sel_01JD8Q1234567",
|
|
50
96
|
},
|
|
51
97
|
})
|
|
52
98
|
```
|
|
53
99
|
|
|
54
|
-
|
|
100
|
+
The workflow sets `canceled_by` and `canceled_at`, then emits `product-change.canceled`.
|
|
55
101
|
|
|
56
|
-
|
|
57
|
-
retracting their own submission), use `cancelProductChangeWorkflow`:
|
|
102
|
+
## Confirm several changes at once
|
|
58
103
|
|
|
59
|
-
|
|
60
|
-
import { cancelProductChangeWorkflow } from "@mercurjs/core/workflows"
|
|
104
|
+
`confirmProductChangeWorkflow` loads every id in `ids` before it writes anything. If one id doesn’t exist, it throws a `NOT_FOUND` error. If one change isn’t `pending`, it throws `NOT_ALLOWED`, and none of the changes are confirmed.
|
|
61
105
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
106
|
+
## Handle a failed apply
|
|
107
|
+
|
|
108
|
+
Applying actions runs Medusa’s product workflows inside the confirm workflow. If one of them fails, for example because a variant was already deleted, the workflow compensates: it restores the change to `pending` and clears `confirmed_by` and `confirmed_at`. Fix the cause, then confirm again.
|
|
109
|
+
|
|
110
|
+
## Run your own logic on a resolution
|
|
111
|
+
|
|
112
|
+
Each resolution workflow has a hook that receives the change ids and the `additional_data` you passed in:
|
|
113
|
+
|
|
114
|
+
- **`confirmProductChangeWorkflow`**: `productChangeConfirmed`, with `{ ids, additional_data }`
|
|
115
|
+
- **`rejectProductChangeWorkflow`**: `productChangeRejected`, with `{ id, additional_data }`
|
|
116
|
+
- **`cancelProductChangeWorkflow`**: `productChangeCanceled`, with `{ id, additional_data }`
|
|
66
117
|
|
|
67
|
-
|
|
68
|
-
Confirm, decline, and cancel all require the change to be `pending`. Resolving
|
|
69
|
-
an already-resolved change fails validation. A change is resolved exactly once.
|
|
70
|
-
</Warning>
|
|
118
|
+
Use a hook when your logic must succeed or fail together with the resolution. For side effects that can run later, such as an email to the seller, subscribe to the events listed in the [Events reference](/platform/product-edit/reference/events).
|
|
71
119
|
|
|
72
|
-
##
|
|
120
|
+
## Next steps
|
|
73
121
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
[
|
|
122
|
+
- [Send a proposed product back for a revision](/platform/product-edit/guides/request-a-revision)
|
|
123
|
+
- [How actions apply on confirm](/platform/product-edit/concepts/change-actions#how-actions-apply)
|
|
124
|
+
- [Workflow inputs and hooks](/platform/product-edit/reference/workflows)
|
|
@@ -1,74 +1,154 @@
|
|
|
1
1
|
---
|
|
2
|
-
title: "
|
|
2
|
+
title: "Stage a product edit from server code"
|
|
3
3
|
sidebarTitle: "Edit a product"
|
|
4
|
-
description: "
|
|
4
|
+
description: "Route a product edit through the review pipeline from a custom API route, a script, or a bulk tool."
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
pipeline from your own server code, such as a custom API route or a bulk tool.
|
|
7
|
+
Stage a product edit as a `ProductChange` instead of writing to the product, so it goes through the same review as an edit from the Vendor panel. Use this when you build your own edit surface, such as a bulk editor or an import.
|
|
9
8
|
|
|
10
|
-
|
|
11
|
-
exposes high-level edit workflows that diff your update against the current
|
|
12
|
-
product and stage only the fields that actually changed.
|
|
9
|
+
## Before you begin
|
|
13
10
|
|
|
14
|
-
|
|
11
|
+
You need the following in place:
|
|
15
12
|
|
|
16
|
-
|
|
17
|
-
and
|
|
13
|
+
- A Mercur server with `@mercurjs/core` installed through `withMercur()`
|
|
14
|
+
- A product id, and the id of the actor making the edit, such as a seller id (`sel_…`)
|
|
18
15
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
16
|
+
## Stage a field edit
|
|
17
|
+
|
|
18
|
+
`productEditUpdateProductWorkflow` compares your update with the product and stages one action per field that differs.
|
|
19
|
+
|
|
20
|
+
<Steps>
|
|
21
|
+
<Step title="Create the route">
|
|
22
|
+
Add a route under `/admin` so Medusa authenticates the caller, and pass the update to the workflow. The workflow returns the staged change.
|
|
23
|
+
|
|
24
|
+
```ts title="src/api/admin/custom/products/[id]/edit/route.ts" {13-19}
|
|
25
|
+
import type {
|
|
26
|
+
AuthenticatedMedusaRequest,
|
|
27
|
+
MedusaResponse,
|
|
28
|
+
} from "@medusajs/framework/http"
|
|
29
|
+
import { productEditUpdateProductWorkflow } from "@mercurjs/core/workflows"
|
|
30
|
+
|
|
31
|
+
type Body = { title?: string; material?: string }
|
|
32
|
+
|
|
33
|
+
export async function POST(
|
|
34
|
+
req: AuthenticatedMedusaRequest<Body>,
|
|
35
|
+
res: MedusaResponse
|
|
36
|
+
) {
|
|
37
|
+
const { result } = await productEditUpdateProductWorkflow(req.scope).run({
|
|
38
|
+
input: {
|
|
39
|
+
product_id: req.params.id,
|
|
40
|
+
created_by: req.auth_context.actor_id,
|
|
41
|
+
update: req.body,
|
|
42
|
+
},
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
res.status(202).json({ product_change: result })
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
The route answers `202` because the edit isn’t on the product yet. With the default `product_request` flag on, `result.status` is `pending`.
|
|
50
|
+
</Step>
|
|
51
|
+
<Step title="Handle an open change">
|
|
52
|
+
If the same `created_by` already has a `pending` change on the product, the workflow throws a `NOT_ALLOWED` error before it stages anything. Medusa’s error handler turns it into a `400` response. Cancel the open change with `cancelProductChangeWorkflow`, or wait for an operator to resolve it.
|
|
53
|
+
</Step>
|
|
54
|
+
<Step title="Read the staged actions">
|
|
55
|
+
Load the change with its actions to show the seller what’s waiting for review.
|
|
56
|
+
|
|
57
|
+
```ts
|
|
58
|
+
const query = req.scope.resolve("query")
|
|
59
|
+
|
|
60
|
+
const { data: [change] } = await query.graph({
|
|
61
|
+
entity: "product_change",
|
|
62
|
+
fields: ["id", "status", "actions.action", "actions.details"],
|
|
63
|
+
filters: { id: result.id },
|
|
64
|
+
})
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Each `UPDATE` action carries `field`, `value`, and `previous_value` in `details`.
|
|
68
|
+
</Step>
|
|
69
|
+
</Steps>
|
|
70
|
+
|
|
71
|
+
If no field differs, the workflow still stages a change, with no actions. Compare the input yourself first when an empty change would confuse your reviewers.
|
|
72
|
+
|
|
73
|
+
## Stage variant and attribute edits
|
|
74
|
+
|
|
75
|
+
Variants and attributes have their own edit workflows, with the same pending check. `productEditUpdateVariantsWorkflow` takes a list of typed operations:
|
|
76
|
+
|
|
77
|
+
```ts
|
|
78
|
+
import { productEditUpdateVariantsWorkflow } from "@mercurjs/core/workflows"
|
|
22
79
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
}
|
|
80
|
+
await productEditUpdateVariantsWorkflow(container).run({
|
|
81
|
+
input: {
|
|
82
|
+
product_id: "prod_01JD8Q1234567",
|
|
83
|
+
created_by: "sel_01JD8Q1234567",
|
|
84
|
+
operations: [
|
|
85
|
+
{
|
|
86
|
+
type: "update",
|
|
87
|
+
variant_id: "variant_01JD8Q1234567",
|
|
88
|
+
fields: { sku: "LINEN-M" },
|
|
89
|
+
},
|
|
90
|
+
{ type: "remove", variant_id: "variant_01JD8Q1234568" },
|
|
91
|
+
],
|
|
92
|
+
},
|
|
93
|
+
})
|
|
34
94
|
```
|
|
35
95
|
|
|
36
|
-
|
|
37
|
-
If a product already has a `pending` change, staging another one is rejected.
|
|
38
|
-
Only one active change per product is allowed. Resolve or cancel the open
|
|
39
|
-
change first.
|
|
40
|
-
</Note>
|
|
96
|
+
Each operation becomes a `VARIANT_ADD`, `VARIANT_UPDATE`, or `VARIANT_REMOVE` action. An update whose fields all match the current variant stages nothing. For attributes, pass `add`, `remove`, and `update` arrays to `productEditUpdateAttributesWorkflow`, in the same shapes as the attribute batch.
|
|
41
97
|
|
|
42
98
|
## Stage actions directly
|
|
43
99
|
|
|
44
|
-
For
|
|
45
|
-
yourself with `stageProductChangeWorkflow`:
|
|
100
|
+
For a mixed change, build the actions yourself and pass them to `stageProductChangeWorkflow`. It skips the diff and the pending check, so check for an open change first.
|
|
46
101
|
|
|
47
102
|
```ts
|
|
103
|
+
import { ProductChangeActionType } from "@mercurjs/types"
|
|
48
104
|
import { stageProductChangeWorkflow } from "@mercurjs/core/workflows"
|
|
49
105
|
|
|
50
106
|
await stageProductChangeWorkflow(container).run({
|
|
51
107
|
input: {
|
|
52
|
-
product_id: "
|
|
53
|
-
created_by: "
|
|
108
|
+
product_id: "prod_01JD8Q1234567",
|
|
109
|
+
created_by: "sel_01JD8Q1234567",
|
|
110
|
+
external_note: "Spring catalog refresh",
|
|
54
111
|
actions: [
|
|
55
|
-
{
|
|
56
|
-
|
|
112
|
+
{
|
|
113
|
+
product_id: "prod_01JD8Q1234567",
|
|
114
|
+
action: ProductChangeActionType.UPDATE,
|
|
115
|
+
details: { field: "subtitle", value: "Washed linen" },
|
|
116
|
+
},
|
|
57
117
|
],
|
|
58
118
|
},
|
|
59
119
|
})
|
|
60
120
|
```
|
|
61
121
|
|
|
62
|
-
|
|
122
|
+
Every action needs its own `product_id`. The workflow fills in `product_change_id` for you.
|
|
123
|
+
|
|
124
|
+
## Apply an edit without review
|
|
125
|
+
|
|
126
|
+
Pass `auto_confirm: true` to `stageProductChangeWorkflow` or `productEditDeleteProductWorkflow` to confirm and apply the change in the same run. Use it for trusted sources, such as an operator tool. The change still lands in the trail as `confirmed`, and `product-change.confirmed` still fires.
|
|
127
|
+
|
|
128
|
+
## Attach custom data with additional_data
|
|
129
|
+
|
|
130
|
+
`productEditUpdateProductWorkflow` and `productEditUpdateAttributesWorkflow` accept `additional_data` and pass it to their `productChangeCreated` hook. Use the hook to store your own data on the change:
|
|
131
|
+
|
|
132
|
+
```ts title="src/workflows/hooks/product-change-created.ts"
|
|
133
|
+
import { productEditUpdateProductWorkflow } from "@mercurjs/core/workflows"
|
|
134
|
+
import { MercurModules } from "@mercurjs/types"
|
|
135
|
+
|
|
136
|
+
productEditUpdateProductWorkflow.hooks.productChangeCreated(
|
|
137
|
+
async ({ product_change, additional_data }, { container }) => {
|
|
138
|
+
const productEdit = container.resolve(MercurModules.PRODUCT_EDIT)
|
|
139
|
+
|
|
140
|
+
await productEdit.updateProductChanges({
|
|
141
|
+
id: product_change.id,
|
|
142
|
+
metadata: { ticket_id: additional_data?.ticket_id },
|
|
143
|
+
})
|
|
144
|
+
}
|
|
145
|
+
)
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
The hook runs after staging and auto-confirm, so `product_change` is the record the workflow returns.
|
|
63
149
|
|
|
64
|
-
|
|
65
|
-
applies immediately, with review on it stays `pending`. Pass `auto_confirm: true`
|
|
66
|
-
to `stageProductChangeWorkflow` to force immediate application regardless of the
|
|
67
|
-
review setting.
|
|
150
|
+
## Next steps
|
|
68
151
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
`productEditDeleteProductWorkflow`. Each stages the right action types for you.
|
|
73
|
-
See the [Workflows reference](/platform/product-edit/reference/workflows).
|
|
74
|
-
</Tip>
|
|
152
|
+
- [Confirm or decline the staged change](/platform/product-edit/guides/confirm-or-decline-a-change)
|
|
153
|
+
- [Every action type and its `details`](/platform/product-edit/concepts/change-actions)
|
|
154
|
+
- [Edit workflow inputs and hooks](/platform/product-edit/reference/workflows#edit-workflows)
|
|
@@ -1,54 +1,114 @@
|
|
|
1
1
|
---
|
|
2
|
-
title: "
|
|
2
|
+
title: "Send a proposed product back for a revision"
|
|
3
3
|
sidebarTitle: "Request a revision"
|
|
4
|
-
description: "
|
|
4
|
+
description: "Ask a seller to rework a proposed product, record the request, and notify the seller."
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
approving or rejecting it outright.
|
|
7
|
+
Ask a seller to rework a product they’ve proposed, instead of publishing or rejecting it. The request leaves the product `proposed`, adds a `CHANGE_REQUESTED` entry to its history, and emits an event you can use to notify the seller.
|
|
9
8
|
|
|
10
|
-
|
|
11
|
-
action in the audit trail carrying your message, and emits
|
|
12
|
-
`product.change-requested` so the vendor is notified. The product stays with the
|
|
13
|
-
vendor to revise and resubmit.
|
|
9
|
+
## Before you begin
|
|
14
10
|
|
|
15
|
-
|
|
11
|
+
You need the following in place:
|
|
16
12
|
|
|
17
|
-
|
|
18
|
-
|
|
13
|
+
- A Mercur server with `@mercurjs/core` installed through `withMercur()`
|
|
14
|
+
- A product with status `proposed`. The workflow fails for any other status
|
|
19
15
|
|
|
20
|
-
|
|
21
|
-
import type { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
|
|
22
|
-
import { requestProductChangeWorkflow } from "@mercurjs/core/workflows"
|
|
16
|
+
## Request and notify
|
|
23
17
|
|
|
24
|
-
|
|
25
|
-
await requestProductChangeWorkflow(req.scope).run({
|
|
26
|
-
input: {
|
|
27
|
-
product_id: req.params.id,
|
|
28
|
-
actor_id: req.auth_context?.actor_id,
|
|
29
|
-
message: "Please add a size guide and sharper photos.",
|
|
30
|
-
},
|
|
31
|
-
})
|
|
18
|
+
Run the workflow from an admin route, then notify the seller from a subscriber.
|
|
32
19
|
|
|
33
|
-
|
|
34
|
-
|
|
20
|
+
<Steps>
|
|
21
|
+
<Step title="Run the workflow">
|
|
22
|
+
`requestProductChangeWorkflow` checks the product status, records the audit change, and emits `product.change-requested`.
|
|
23
|
+
|
|
24
|
+
```ts title="src/api/admin/custom/products/[id]/revise/route.ts"
|
|
25
|
+
import type {
|
|
26
|
+
AuthenticatedMedusaRequest,
|
|
27
|
+
MedusaResponse,
|
|
28
|
+
} from "@medusajs/framework/http"
|
|
29
|
+
import { requestProductChangeWorkflow } from "@mercurjs/core/workflows"
|
|
30
|
+
|
|
31
|
+
export async function POST(
|
|
32
|
+
req: AuthenticatedMedusaRequest<{ message: string }>,
|
|
33
|
+
res: MedusaResponse
|
|
34
|
+
) {
|
|
35
|
+
await requestProductChangeWorkflow(req.scope).run({
|
|
36
|
+
input: {
|
|
37
|
+
product_id: req.params.id,
|
|
38
|
+
actor_id: req.auth_context.actor_id,
|
|
39
|
+
message: req.body.message,
|
|
40
|
+
},
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
res.json({ id: req.params.id })
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
The message is optional. Without it, the action’s `details.message` is `null`.
|
|
48
|
+
</Step>
|
|
49
|
+
<Step title="Notify the seller">
|
|
50
|
+
Subscribe to `product.change-requested`. The payload carries the product id, not a change id, plus the message and the operator’s id.
|
|
51
|
+
|
|
52
|
+
```ts title="src/subscribers/product-change-requested.ts"
|
|
53
|
+
import type { SubscriberArgs, SubscriberConfig } from "@medusajs/framework"
|
|
54
|
+
|
|
55
|
+
type Payload = { id: string; message?: string; actor_id?: string }
|
|
56
|
+
|
|
57
|
+
export default async function handler({
|
|
58
|
+
event,
|
|
59
|
+
container,
|
|
60
|
+
}: SubscriberArgs<Payload>) {
|
|
61
|
+
const logger = container.resolve("logger")
|
|
62
|
+
|
|
63
|
+
logger.info(
|
|
64
|
+
`Revision requested on ${event.data.id}: ${event.data.message ?? ""}`
|
|
65
|
+
)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export const config: SubscriberConfig = {
|
|
69
|
+
event: "product.change-requested",
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Replace the log line with your notification, for example a call to Medusa’s Notification module.
|
|
74
|
+
</Step>
|
|
75
|
+
</Steps>
|
|
76
|
+
|
|
77
|
+
## Where the request is stored
|
|
78
|
+
|
|
79
|
+
`requestProductChangeWorkflow` calls `recordProductAuditChangeWorkflow`, which stores the request as a change that starts out `confirmed`. The message is saved in two places:
|
|
80
|
+
|
|
81
|
+
- **`details.message`**: on the `CHANGE_REQUESTED` action
|
|
82
|
+
- **`external_note`**: on the change, where the seller-facing note lives
|
|
83
|
+
|
|
84
|
+
Read every revision request on a product by querying its actions:
|
|
85
|
+
|
|
86
|
+
```ts
|
|
87
|
+
const { data: requests } = await query.graph({
|
|
88
|
+
entity: "product_change_action",
|
|
89
|
+
fields: ["details", "product_change.created_by", "created_at"],
|
|
90
|
+
filters: {
|
|
91
|
+
product_id: "prod_01JD8Q1234567",
|
|
92
|
+
action: "CHANGE_REQUESTED",
|
|
93
|
+
},
|
|
94
|
+
})
|
|
35
95
|
```
|
|
36
96
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
97
|
+
`product_change.created_by` is the operator who asked.
|
|
98
|
+
|
|
99
|
+
## Publish or reject instead
|
|
100
|
+
|
|
101
|
+
When the product is ready or can’t be fixed, resolve it with a product workflow instead. Both require the product to be `proposed`, and both record a confirmed `STATUS_CHANGE` audit change:
|
|
102
|
+
|
|
103
|
+
- **`confirmProductsWorkflow`**: takes `product_ids`, sets them to `published`, and emits `product.published`
|
|
104
|
+
- **`rejectProductWorkflow`**: takes `product_id` and an optional `message`, sets it to `rejected`, and emits `product.rejected`
|
|
105
|
+
|
|
106
|
+
## Attach custom data with additional_data
|
|
41
107
|
|
|
42
|
-
|
|
108
|
+
`requestProductChangeWorkflow` passes `additional_data` to its `productChangeRequested` hook, along with `product_id` and `message`. Use it to store, for example, the checklist items the seller has to fix.
|
|
43
109
|
|
|
44
|
-
|
|
45
|
-
action's `details.message`, and the parent change's `external_note` (the
|
|
46
|
-
vendor-facing note). Because the audit change is created already `confirmed`, the
|
|
47
|
-
request is a permanent entry in the product's history.
|
|
110
|
+
## Next steps
|
|
48
111
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
operator who asked. See the
|
|
53
|
-
[Event reference](/platform/product-edit/reference/events).
|
|
54
|
-
</Tip>
|
|
112
|
+
- [How audit changes fit the trail](/platform/product-edit/concepts/change-pipeline#audit-changes)
|
|
113
|
+
- [Revision requests and statuses](/platform/product-edit/concepts/status-and-auto-confirm#revision-requests)
|
|
114
|
+
- [Event payloads](/platform/product-edit/reference/events)
|