@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.
Files changed (114) hide show
  1. package/content/learn/architecture.mdx +166 -263
  2. package/content/platform/attribute/concepts/attribute-types.mdx +73 -46
  3. package/content/platform/attribute/concepts/global-vs-inline.mdx +54 -32
  4. package/content/platform/attribute/concepts/variant-axes.mdx +54 -32
  5. package/content/platform/attribute/guides/attach-attributes-to-a-product.mdx +96 -53
  6. package/content/platform/attribute/guides/create-a-variant-axis.mdx +106 -57
  7. package/content/platform/attribute/guides/create-an-attribute.mdx +105 -56
  8. package/content/platform/attribute/overview.mdx +37 -33
  9. package/content/platform/attribute/reference/data-models.mdx +53 -38
  10. package/content/platform/attribute/reference/events.mdx +47 -24
  11. package/content/platform/attribute/reference/links.mdx +98 -27
  12. package/content/platform/attribute/reference/service.mdx +71 -32
  13. package/content/platform/attribute/reference/workflows.mdx +158 -28
  14. package/content/platform/catalog/concepts/master-products.mdx +39 -37
  15. package/content/platform/catalog/concepts/product-seller-allowlist.mdx +56 -35
  16. package/content/platform/catalog/concepts/status-lifecycle.mdx +55 -49
  17. package/content/platform/catalog/concepts/variants-categories-collections.mdx +55 -35
  18. package/content/platform/catalog/guides/allowlist-stores.mdx +100 -37
  19. package/content/platform/catalog/guides/create-a-master-product.mdx +124 -48
  20. package/content/platform/catalog/guides/publish-or-reject-a-product.mdx +120 -55
  21. package/content/platform/catalog/overview.mdx +39 -35
  22. package/content/platform/catalog/reference/data-models.mdx +154 -43
  23. package/content/platform/catalog/reference/events.mdx +41 -25
  24. package/content/platform/catalog/reference/links.mdx +196 -28
  25. package/content/platform/catalog/reference/service.mdx +57 -35
  26. package/content/platform/catalog/reference/workflows.mdx +124 -30
  27. package/content/platform/commission/concepts/order-commission-lines.mdx +54 -52
  28. package/content/platform/commission/concepts/rule-matching.mdx +53 -63
  29. package/content/platform/commission/concepts/rules-and-rates.mdx +61 -59
  30. package/content/platform/commission/guides/batch-update-rules.mdx +69 -41
  31. package/content/platform/commission/guides/create-a-rate.mdx +101 -53
  32. package/content/platform/commission/guides/refresh-order-commission-lines.mdx +104 -35
  33. package/content/platform/commission/overview.mdx +43 -35
  34. package/content/platform/commission/reference/data-models.mdx +59 -45
  35. package/content/platform/commission/reference/events.mdx +28 -34
  36. package/content/platform/commission/reference/links.mdx +42 -33
  37. package/content/platform/commission/reference/service.mdx +103 -28
  38. package/content/platform/commission/reference/workflows.mdx +101 -27
  39. package/content/platform/offer/concepts/pricing-and-inventory.mdx +63 -49
  40. package/content/platform/offer/concepts/shipping.mdx +28 -29
  41. package/content/platform/offer/concepts/what-is-an-offer.mdx +48 -45
  42. package/content/platform/offer/guides/bulk-create-offers.mdx +97 -61
  43. package/content/platform/offer/guides/create-an-offer.mdx +112 -60
  44. package/content/platform/offer/guides/manage-offer-inventory.mdx +78 -38
  45. package/content/platform/offer/overview.mdx +39 -32
  46. package/content/platform/offer/reference/data-models.mdx +77 -47
  47. package/content/platform/offer/reference/events.mdx +44 -18
  48. package/content/platform/offer/reference/links.mdx +92 -40
  49. package/content/platform/offer/reference/service.mdx +66 -31
  50. package/content/platform/offer/reference/workflows.mdx +146 -37
  51. package/content/platform/order-group/concepts/computed-totals.mdx +40 -37
  52. package/content/platform/order-group/concepts/order-splitting.mdx +41 -39
  53. package/content/platform/order-group/concepts/the-order-group.mdx +39 -32
  54. package/content/platform/order-group/guides/list-order-groups.mdx +96 -56
  55. package/content/platform/order-group/guides/retrieve-an-order-group.mdx +93 -42
  56. package/content/platform/order-group/guides/split-a-cart.mdx +115 -40
  57. package/content/platform/order-group/overview.mdx +42 -32
  58. package/content/platform/order-group/reference/data-models.mdx +48 -31
  59. package/content/platform/order-group/reference/events.mdx +29 -17
  60. package/content/platform/order-group/reference/links.mdx +90 -29
  61. package/content/platform/order-group/reference/service.mdx +55 -31
  62. package/content/platform/order-group/reference/workflows.mdx +83 -24
  63. package/content/platform/payout/concepts/account-lifecycle.mdx +64 -67
  64. package/content/platform/payout/concepts/accounts-and-onboarding.mdx +50 -40
  65. package/content/platform/payout/concepts/payout-pipeline.mdx +70 -73
  66. package/content/platform/payout/guides/create-a-payout-account.mdx +65 -41
  67. package/content/platform/payout/guides/process-a-provider-webhook.mdx +84 -38
  68. package/content/platform/payout/guides/start-provider-onboarding.mdx +90 -46
  69. package/content/platform/payout/overview.mdx +43 -34
  70. package/content/platform/payout/reference/data-models.mdx +63 -36
  71. package/content/platform/payout/reference/events.mdx +56 -30
  72. package/content/platform/payout/reference/links.mdx +52 -22
  73. package/content/platform/payout/reference/service.mdx +72 -33
  74. package/content/platform/payout/reference/workflows.mdx +80 -19
  75. package/content/platform/product-edit/concepts/change-actions.mdx +52 -45
  76. package/content/platform/product-edit/concepts/change-pipeline.mdx +60 -52
  77. package/content/platform/product-edit/concepts/status-and-auto-confirm.mdx +43 -46
  78. package/content/platform/product-edit/guides/confirm-or-decline-a-change.mdx +104 -56
  79. package/content/platform/product-edit/guides/edit-a-product.mdx +126 -46
  80. package/content/platform/product-edit/guides/request-a-revision.mdx +99 -39
  81. package/content/platform/product-edit/overview.mdx +41 -34
  82. package/content/platform/product-edit/reference/data-models.mdx +73 -47
  83. package/content/platform/product-edit/reference/events.mdx +39 -29
  84. package/content/platform/product-edit/reference/links.mdx +32 -24
  85. package/content/platform/product-edit/reference/service.mdx +45 -26
  86. package/content/platform/product-edit/reference/workflows.mdx +173 -31
  87. package/content/platform/review/concepts/product-vs-seller-reviews.mdx +42 -41
  88. package/content/platform/review/concepts/ratings-and-moderation.mdx +45 -44
  89. package/content/platform/review/concepts/the-review-model.mdx +52 -45
  90. package/content/platform/review/guides/compute-aggregate-ratings.mdx +62 -37
  91. package/content/platform/review/guides/create-a-review.mdx +109 -39
  92. package/content/platform/review/guides/moderate-a-review.mdx +83 -37
  93. package/content/platform/review/guides/respond-to-a-review.mdx +85 -40
  94. package/content/platform/review/overview.mdx +43 -33
  95. package/content/platform/review/reference/data-models.mdx +41 -27
  96. package/content/platform/review/reference/events.mdx +55 -37
  97. package/content/platform/review/reference/links.mdx +80 -22
  98. package/content/platform/review/reference/service.mdx +61 -33
  99. package/content/platform/review/reference/workflows.mdx +80 -24
  100. package/content/platform/store/concepts/lifecycle.mdx +68 -44
  101. package/content/platform/store/concepts/store-entity.mdx +45 -27
  102. package/content/platform/store/concepts/team.mdx +56 -35
  103. package/content/platform/store/guides/create-a-store.mdx +102 -35
  104. package/content/platform/store/guides/manage-the-team.mdx +122 -32
  105. package/content/platform/store/guides/moderate-a-store.mdx +107 -38
  106. package/content/platform/store/overview.mdx +41 -34
  107. package/content/platform/store/reference/data-models.mdx +132 -57
  108. package/content/platform/store/reference/events.mdx +42 -17
  109. package/content/platform/store/reference/links.mdx +67 -49
  110. package/content/platform/store/reference/service.mdx +105 -33
  111. package/content/platform/store/reference/workflows.mdx +304 -24
  112. package/content/references/panel-extensions/layouts.mdx +169 -0
  113. package/llms.txt +112 -111
  114. package/package.json +1 -1
@@ -1,85 +1,92 @@
1
1
  ---
2
- title: "Product Edit"
2
+ title: "Product edits"
3
3
  sidebarTitle: "Overview"
4
- description: "Route every product edit through a reviewable, immutable change pipeline."
4
+ description: "Review every seller edit to a shared product before it lands, and keep a record of who changed what."
5
5
  ---
6
6
 
7
- Use Mercur to keep a full audit trail of every product change and govern your
8
- master data before it changes.
7
+ Use Mercur to put seller edits to the shared catalog through review, and to keep a durable history of every change to a product.
9
8
 
10
- Master-data governance depends on knowing who changed what, and reviewing it
11
- before it takes effect. The Product Edit domain is that auditable change
12
- pipeline. Every edit becomes an immutable, attributed `ProductChange` record that
13
- enters an approval queue, so no change to a shared product is silently written.
14
- Each record carries typed actions and a status lifecycle, and the trail records
15
- who changed what and who approved it.
9
+ Products in Mercur form one shared catalog that every seller sells against, so a seller can’t write to a product directly. Each edit becomes a `ProductChange` record that holds typed actions, one per changed field, variant, or attribute. The change waits as `pending` until an operator confirms or declines it, or its author cancels it. Confirmed changes, together with publish approvals, rejections, and revision requests, form the product’s audit trail.
10
+
11
+ <img className="block dark:hidden" src="/images/figures/product-edit-overview-light.svg" alt="A product's Changes list read through product.changes: a pending UPDATE from the seller sits on top of confirmed PRODUCT_ADD, CHANGE_REQUESTED, UPDATE, and STATUS_CHANGE records that form the audit trail." />
12
+ <img className="hidden dark:block" src="/images/figures/product-edit-overview-dark.svg" alt="A product's Changes list read through product.changes: a pending UPDATE from the seller sits on top of confirmed PRODUCT_ADD, CHANGE_REQUESTED, UPDATE, and STATUS_CHANGE records that form the audit trail." />
16
13
 
17
14
  <Note>
18
- **Product Edit ≠ product ownership.** Products are the shared master catalog,
19
- and sellers sell against them through offers. Product Edit governs *changes* to
20
- a product. It never makes a store the owner of one.
15
+ The Product Edit module (`MercurModules.PRODUCT_EDIT`) governs changes to a product. It never makes a store the owner of one. Products live in Medusa’s Product module, and sellers sell them through [offers](/platform/offer/overview).
21
16
  </Note>
22
17
 
18
+ ## How it works
19
+
20
+ A seller edit moves through the pipeline in five steps:
21
+
22
+ 1. A vendor route runs an edit workflow such as `productEditUpdateProductWorkflow`, which diffs the input against the product and stages a `pending` change with one action per difference.
23
+ 2. `autoConfirmProductChangeWorkflow` runs right after staging. With the `product_request` feature flag on, the default, the change stays `pending`.
24
+ 3. An operator confirms or declines the change, or the seller cancels it.
25
+ 4. On confirm, `applyProductChangeActionsWorkflow` writes the actions to the product through Medusa’s product workflows and marks each action `applied`.
26
+ 5. Product creation, publish approvals, rejections, and revision requests are recorded as changes that start out `confirmed`, so the trail is complete without a review.
27
+
23
28
  ## Key features
24
29
 
25
- - **Immutable audit trail:** every edit is an attributed `ProductChange` that is never rewritten, only appended to and resolved.
26
- - **Typed change actions:** each edit is expressed as `ProductChangeAction` rows such as `UPDATE`, `VARIANT_*`, `ATTRIBUTE_*`, `STATUS_CHANGE`, or `PRODUCT_ADD/DELETE`.
27
- - **Approval queue:** a change moves `pending` → `confirmed` / `declined` / `canceled`, each through a dedicated workflow.
28
- - **Deferred application:** actions are applied to the product only on confirmation, then marked `applied`.
29
- - **Auto-confirm:** when review is off, staged changes confirm and apply immediately.
30
- - **Revision requests:** operators send a submission back to the vendor as a recorded `CHANGE_REQUESTED` audit action.
31
- - **One active change per product:** a product can't have two pending changes at once.
30
+ The module covers the full path of an edit, from diff to audit record:
31
+
32
+ - **Field-level diffs**: the edit workflows stage only the fields, variants, and attributes that differ from the current product
33
+ - **Typed actions**: each change holds `ProductChangeAction` rows such as `UPDATE`, `VARIANT_ADD`, `ATTRIBUTE_UPDATE`, or `PRODUCT_DELETE`
34
+ - **Review queue**: a change moves from `pending` to `confirmed`, `declined`, or `canceled`, each through its own workflow and event
35
+ - **Deferred writes**: actions touch the product only when the change is confirmed
36
+ - **Auto-confirm**: turn off the `product_request` flag, or pass `auto_confirm`, to apply a change in the same run
37
+ - **Revision requests**: an operator sends a proposed product back to its seller with a recorded `CHANGE_REQUESTED` action
38
+ - **One open change per author**: the edit workflows reject a new change while the same author has one pending on the product
32
39
 
33
40
  ## Get started
34
41
 
35
- Learn how the domain fits together:
42
+ Learn how changes, actions, and statuses fit together.
36
43
 
37
44
  <CardGroup cols={2}>
38
45
  <Card title="The change pipeline" icon="clock-rotate-left" href="/platform/product-edit/concepts/change-pipeline">
39
- The `ProductChange` record, immutability, and the audit trail.
46
+ How an edit becomes a `ProductChange` and how the audit trail builds up.
40
47
  </Card>
41
48
  <Card title="Change actions" icon="list-check" href="/platform/product-edit/concepts/change-actions">
42
- The typed actions that make up a change and their `details`.
49
+ The action types, the `details` each one carries, and how they apply.
43
50
  </Card>
44
51
  <Card title="Status & auto-confirm" icon="arrows-rotate" href="/platform/product-edit/concepts/status-and-auto-confirm">
45
- The status lifecycle, revision requests, and auto-confirm.
52
+ The status lifecycle, revision requests, and when a change skips review.
46
53
  </Card>
47
54
  </CardGroup>
48
55
 
49
56
  ## Examples
50
57
 
51
- Build against the Product Edit domain in your own code:
58
+ Build against the change pipeline in your own server code.
52
59
 
53
60
  <CardGroup cols={2}>
54
61
  <Card title="Edit a product" icon="pen" href="/platform/product-edit/guides/edit-a-product">
55
- Stage a product change from server code.
62
+ Stage field, variant, and attribute changes from a custom route.
56
63
  </Card>
57
64
  <Card title="Confirm or decline" icon="gavel" href="/platform/product-edit/guides/confirm-or-decline-a-change">
58
- Resolve a pending change in code.
65
+ Resolve a pending change and react to the result.
59
66
  </Card>
60
67
  <Card title="Request a revision" icon="rotate-left" href="/platform/product-edit/guides/request-a-revision">
61
- Send a submission back to the vendor.
68
+ Send a proposed product back to its seller with a message.
62
69
  </Card>
63
70
  </CardGroup>
64
71
 
65
72
  ## Resources
66
73
 
67
- Data models, workflows, service methods, and events for the Product Edit domain:
74
+ The data models, links, workflows, service methods, and events behind product edits.
68
75
 
69
76
  <CardGroup cols={2}>
70
77
  <Card title="Data models" icon="table" href="/platform/product-edit/reference/data-models">
71
- The `ProductChange` and `ProductChangeAction` entities.
78
+ The `ProductChange` and `ProductChangeAction` models and their enums.
72
79
  </Card>
73
80
  <Card title="Links" icon="link" href="/platform/product-edit/reference/links">
74
- How the Product Edit domain links to other modules.
81
+ The read-only `product.changes` link to Medusa’s Product module.
75
82
  </Card>
76
83
  <Card title="Workflows" icon="diagram-project" href="/platform/product-edit/reference/workflows">
77
- Create, confirm, decline, cancel, and stage workflows.
84
+ Staging, resolution, apply, edit, and audit workflows with their hooks.
78
85
  </Card>
79
86
  <Card title="Service" icon="gear" href="/platform/product-edit/reference/service">
80
- Module service methods for working with records directly.
87
+ Generated methods on the Product Edit module service.
81
88
  </Card>
82
89
  <Card title="Events" icon="bell" href="/platform/product-edit/reference/events">
83
- Events emitted as changes move through the pipeline.
90
+ The `product-change.*` events and the revision request event.
84
91
  </Card>
85
92
  </CardGroup>
@@ -1,65 +1,91 @@
1
1
  ---
2
- title: "Data models"
2
+ title: "Product Edit data models"
3
3
  sidebarTitle: "Data models"
4
- description: "The data models owned by the Product Edit domain."
4
+ description: "Fields, enums, indexes, and relations of the ProductChange and ProductChangeAction models."
5
5
  ---
6
6
 
7
- The Product Edit domain is owned by the **Product Edit module**
8
- (`MercurModules.PRODUCT_EDIT`). This reference lists its data models and their
9
- fields.
7
+ The Product Edit module, resolved with `MercurModules.PRODUCT_EDIT` (`"product_edit"`), owns two data models: `ProductChange` and `ProductChangeAction`. Both are defined in `packages/core/src/modules/product-edit/models`. Every model also gets `created_at`, `updated_at`, and `deleted_at` from Medusa.
10
8
 
11
9
  ## ProductChange
12
10
 
13
- Table `product_change`, id prefix `prodch`. A single reviewable edit to one
14
- product.
11
+ A single reviewable edit to one product. Table `product_change`, id prefix `prodch`.
15
12
 
16
- | Field | Type | Notes |
13
+ | Field | Type | Description |
17
14
  | --- | --- | --- |
18
- | `id` | text | Primary key |
19
- | `product_id` | text | The product this change targets (indexed) |
20
- | `status` | enum | `ProductChangeStatus`, default `pending` (indexed) |
21
- | `internal_note` | text | Nullable; operator-only note |
22
- | `external_note` | text | Nullable; vendor-facing note |
23
- | `created_by` | text | Nullable; actor who staged the change |
24
- | `confirmed_by` / `confirmed_at` | text / dateTime | Nullable; set on confirm |
25
- | `declined_by` / `declined_at` | text / dateTime | Nullable; set on decline |
26
- | `declined_reason` | text | Nullable |
27
- | `canceled_by` / `canceled_at` | text / dateTime | Nullable; set on cancel |
28
- | `requires_action_by` / `requires_action_at` | text / dateTime | Nullable |
29
- | `requires_action_reason` | text | Nullable |
30
- | `metadata` | json | Nullable |
31
-
32
- Relations: `actions` (one-to-many `ProductChangeAction`, cascade-deleted with the
33
- change).
34
-
35
- <Note>
36
- The `status` enum has four values: `pending`, `confirmed`, `declined`, and
37
- `canceled`. A revision request is recorded as a `CHANGE_REQUESTED` audit action
38
- rather than a distinct status.
39
- </Note>
15
+ | `id` | `text` | Primary key, prefixed `prodch` |
16
+ | `product_id` | `text` | The product the change targets |
17
+ | `status` | `enum` | A `ProductChangeStatus` value. Default `pending` |
18
+ | `internal_note` | `text`, nullable | Note for operators |
19
+ | `external_note` | `text`, nullable | Note shown to the seller. Holds the message of a revision request |
20
+ | `created_by` | `text`, nullable | Actor who staged the change: a seller id from vendor routes, a user id from admin routes |
21
+ | `confirmed_by` | `text`, nullable | Actor who confirmed the change |
22
+ | `confirmed_at` | `dateTime`, nullable | When the change was confirmed |
23
+ | `declined_by` | `text`, nullable | Actor who declined the change |
24
+ | `declined_at` | `dateTime`, nullable | When the change was declined |
25
+ | `declined_reason` | `text`, nullable | Reason given on decline |
26
+ | `canceled_by` | `text`, nullable | Actor who canceled the change |
27
+ | `canceled_at` | `dateTime`, nullable | When the change was canceled |
28
+ | `requires_action_by` | `text`, nullable | Reserved. No workflow writes it |
29
+ | `requires_action_at` | `dateTime`, nullable | Reserved. No workflow writes it |
30
+ | `requires_action_reason` | `text`, nullable | Reserved. No workflow writes it |
31
+ | `metadata` | `json`, nullable | Custom key-value data |
32
+
33
+ Relations:
34
+
35
+ - **`actions`**: has many `ProductChangeAction`. The model cascades deletes to its actions
36
+
37
+ Indexes on `status`, on `product_id`, and on `(product_id, status)`, all excluding soft-deleted rows.
40
38
 
41
39
  ## ProductChangeAction
42
40
 
43
- Table `product_change_action`, id prefix `prodchact`. A single typed operation
44
- inside a change.
41
+ A single typed operation inside a change. Table `product_change_action`, id prefix `prodchact`.
45
42
 
46
- | Field | Type | Notes |
43
+ | Field | Type | Description |
47
44
  | --- | --- | --- |
48
- | `id` | text | Primary key |
49
- | `product_id` | text | The product the action targets (indexed) |
50
- | `product_change_id` | text | Nullable FK to the parent change (`ON DELETE SET NULL`, indexed) |
51
- | `ordering` | autoincrement | Deterministic apply order (indexed) |
52
- | `action` | text | A `ProductChangeActionType` value |
53
- | `details` | json | Default `{}`; the operation payload |
54
- | `internal_note` | text | Nullable |
55
- | `applied` | boolean | Default `false`; set `true` once written to the product |
45
+ | `id` | `text` | Primary key, prefixed `prodchact` |
46
+ | `product_id` | `text` | The product the action targets |
47
+ | `product_change_id` | `text`, nullable | The parent change. The foreign key is `ON DELETE SET NULL` |
48
+ | `ordering` | `autoincrement` | Insertion order across all actions |
49
+ | `action` | `text` | A `ProductChangeActionType` value |
50
+ | `details` | `json` | The operation payload. Default `{}` |
51
+ | `reference` | `text`, nullable | Free-form type of a related record. Not set by Mercur |
52
+ | `reference_id` | `text`, nullable | Free-form id of a related record. Not set by Mercur |
53
+ | `internal_note` | `text`, nullable | Note for operators |
54
+ | `applied` | `boolean` | `true` once the action is written to the product. Default `false` |
55
+
56
+ Relations:
57
+
58
+ - **`product_change`**: belongs to `ProductChange`, nullable
59
+
60
+ Indexes on `product_change_id`, `product_id`, `(reference, reference_id)`, and `ordering`, all excluding soft-deleted rows.
61
+
62
+ ## ProductChangeStatus
63
+
64
+ The values of `ProductChange.status`, exported from `@mercurjs/types`:
65
+
66
+ | Value | Meaning |
67
+ | --- | --- |
68
+ | `pending` | Waiting for review |
69
+ | `confirmed` | Approved and applied, or recorded as an audit change |
70
+ | `declined` | Rejected by an operator |
71
+ | `canceled` | Withdrawn before review |
56
72
 
57
- ## Enums
73
+ ## ProductChangeActionType
58
74
 
59
- `ProductChangeStatus`: `pending`, `confirmed`, `declined`, `canceled`.
75
+ The values of `ProductChangeAction.action`, exported from `@mercurjs/types`:
60
76
 
61
- `ProductChangeActionType`: `UPDATE`, `STATUS_CHANGE`, `VARIANT_ADD`,
62
- `VARIANT_UPDATE`, `VARIANT_REMOVE`, `ATTRIBUTE_ADD`, `ATTRIBUTE_UPDATE`,
63
- `ATTRIBUTE_REMOVE`, `PRODUCT_ADD`, `PRODUCT_DELETE`, `CHANGE_REQUESTED`.
77
+ | Value | Applied on confirm |
78
+ | --- | --- |
79
+ | `UPDATE` | Yes |
80
+ | `STATUS_CHANGE` | Yes |
81
+ | `VARIANT_ADD` | Yes |
82
+ | `VARIANT_UPDATE` | Yes |
83
+ | `VARIANT_REMOVE` | Yes |
84
+ | `ATTRIBUTE_ADD` | Yes |
85
+ | `ATTRIBUTE_UPDATE` | Yes |
86
+ | `ATTRIBUTE_REMOVE` | Yes |
87
+ | `PRODUCT_DELETE` | Yes |
88
+ | `PRODUCT_ADD` | No, audit only |
89
+ | `CHANGE_REQUESTED` | No, audit only |
64
90
 
65
- Both enums are exported from `@mercurjs/types`.
91
+ The `details` shape for each type is listed in [Change actions](/platform/product-edit/concepts/change-actions#action-types). The matching DTOs are `ProductChangeDTO` and `ProductChangeActionDTO`, and the create inputs are `CreateProductChangeDTO` and `CreateProductChangeActionDTO`, all from `@mercurjs/types`.
@@ -1,22 +1,52 @@
1
1
  ---
2
- title: "Event reference"
2
+ title: "Product Edit events"
3
3
  sidebarTitle: "Events"
4
- description: "Events emitted by the Product Edit domain, for subscribers and side effects."
4
+ description: "Events emitted as product changes are staged and resolved, and when an operator requests a revision."
5
5
  ---
6
6
 
7
- The Product Edit domain emits events as changes move through the pipeline.
8
- Subscribe to them to run side effects instead of polling. Use them to notify a
9
- vendor, sync external systems, or kick off follow-up workflows.
7
+ Product change workflows emit events after they commit, so you can run side effects, such as notifying a seller or reindexing search, without polling. The change events are in `ProductChangeWorkflowEvents`, and the revision request event is in `ProductWorkflowEvents`, both exported from `@mercurjs/core/workflows`.
8
+
9
+ ## Change events
10
+
11
+ Each change event carries the id of one change. A workflow that handles several changes emits one event per change.
12
+
13
+ | Event | Emitted when | Payload |
14
+ | --- | --- | --- |
15
+ | `product-change.created` | `stageProductChangeWorkflow` or `createProductChangeWorkflow` creates a change | `{ id }` |
16
+ | `product-change.confirmed` | `confirmProductChangeWorkflow` confirms a change, including auto-confirm | `{ id }` |
17
+ | `product-change.declined` | `rejectProductChangeWorkflow` declines a change | `{ id }` |
18
+ | `product-change.canceled` | `cancelProductChangeWorkflow` cancels a change | `{ id }` |
19
+
20
+ Audit changes from `recordProductAuditChangeWorkflow` emit no change event. An auto-confirmed change emits `product-change.created` and then `product-change.confirmed`.
21
+
22
+ ## Revision request event
23
+
24
+ The revision request event comes from `requestProductChangeWorkflow`. Its `id` is the product id, because the request is recorded on the product rather than resolving a change.
25
+
26
+ | Event | Emitted when | Payload |
27
+ | --- | --- | --- |
28
+ | `product.change-requested` | An operator requests a revision on a `proposed` product | `{ id, message, actor_id }` |
29
+
30
+ ## Subscribe to an event
31
+
32
+ This subscriber loads a confirmed change with its actions:
10
33
 
11
34
  ```ts title="src/subscribers/product-change-confirmed.ts"
12
35
  import type { SubscriberArgs, SubscriberConfig } from "@medusajs/framework"
13
36
 
14
- export default async function productChangeConfirmedHandler({
37
+ export default async function handler({
15
38
  event,
16
39
  container,
17
40
  }: SubscriberArgs<{ id: string }>) {
18
- const changeId = event.data.id
19
- // ...notify the vendor, sync search, etc.
41
+ const query = container.resolve("query")
42
+
43
+ const { data: [change] } = await query.graph({
44
+ entity: "product_change",
45
+ fields: ["product_id", "created_by", "actions.action"],
46
+ filters: { id: event.data.id },
47
+ })
48
+
49
+ // notify change.created_by that change.product_id was updated
20
50
  }
21
51
 
22
52
  export const config: SubscriberConfig = {
@@ -24,24 +54,4 @@ export const config: SubscriberConfig = {
24
54
  }
25
55
  ```
26
56
 
27
- ## Change events
28
-
29
- | Event | Emitted when | Payload |
30
- | --- | --- | --- |
31
- | `product-change.created` | A change is staged | `{ id }` |
32
- | `product-change.confirmed` | A change is confirmed | `{ id }` |
33
- | `product-change.declined` | A change is declined | `{ id }` |
34
- | `product-change.canceled` | A change is canceled | `{ id }` |
35
-
36
- ## Revision events
37
-
38
- | Event | Emitted when | Payload |
39
- | --- | --- | --- |
40
- | `product.change-requested` | An operator requests a revision | `{ id, message, actor_id }` |
41
-
42
- <Note>
43
- `product.change-requested` carries the **product** id (not a change id) along
44
- with the operator's `message` and `actor_id`, because a revision request is
45
- recorded against the product's audit trail rather than resolving a pending
46
- change.
47
- </Note>
57
+ `created_by` tells you who to notify: a seller id for edits from the Vendor panel. To react to declines with the reason, read `declined_reason` the same way from a `product-change.declined` subscriber.
@@ -1,39 +1,47 @@
1
1
  ---
2
- title: "Links to other modules"
2
+ title: "Product Edit links"
3
3
  sidebarTitle: "Links"
4
- description: "How the Product Edit domain links to other modules across the marketplace."
4
+ description: "The read-only link that exposes a product's change history as product.changes."
5
5
  ---
6
6
 
7
- Modules in Mercur never reference each other directly. They connect through
8
- **module links**. Once a link is defined, you retrieve related records with
9
- `query.graph` using the link alias.
7
+ Mercur modules never import each other. They connect through module links, which you read with `query.graph`. The Product Edit module has one link, to Medusa’s Product module.
10
8
 
11
- ## Product
9
+ ## Product to product changes
12
10
 
13
- The Product Edit domain links to the Product module so a product's change
14
- history hangs off the product itself.
11
+ A read-only link that lists a product’s changes under the `changes` alias. It’s defined in `packages/core/src/links/product-change-link.ts`.
15
12
 
16
- | Linked module | Relationship |
17
- | --- | --- |
18
- | **Product** | A product has many changes (`product_change.product_id`, read-only). Exposed as `product.changes`. |
13
+ | Side | Module | Linkable | Key |
14
+ | --- | --- | --- | --- |
15
+ | Source | Product (`Modules.PRODUCT`) | `product` | `id` |
16
+ | Target | Product Edit (`MercurModules.PRODUCT_EDIT`) | `product_change` | `product_id` |
17
+
18
+ - **Cardinality**: one product to many changes
19
+ - **Alias**: `changes`, read as `product.changes`
20
+ - **Storage**: no pivot table. The link reads the `product_id` column on `product_change`
21
+ - **Writes**: none. The link is `readOnly`, so you create changes with the [workflows](/platform/product-edit/reference/workflows), never through the link
22
+
23
+ Read a product’s history with its actions:
19
24
 
20
25
  ```ts
21
- const { data: products } = await query.graph({
26
+ const query = container.resolve("query")
27
+
28
+ const { data: [product] } = await query.graph({
22
29
  entity: "product",
23
- fields: ["id", "title", "changes.status", "changes.actions.action"],
30
+ fields: [
31
+ "id",
32
+ "title",
33
+ "changes.id",
34
+ "changes.status",
35
+ "changes.created_by",
36
+ "changes.actions.action",
37
+ "changes.actions.details",
38
+ ],
39
+ filters: { id: "prod_01JD8Q1234567" },
24
40
  })
25
41
  ```
26
42
 
27
- <Note>
28
- The link is **read-only** and has no pivot table. The FK lives directly on the
29
- `product_change` row. It exists so you read the audit trail through
30
- `product.changes`. You can't write a change through the link itself. Stage
31
- changes with the [workflows](/platform/product-edit/reference/workflows).
32
- </Note>
43
+ `product.changes` returns every change on the product, including audit changes and resolved ones. To filter by status, query the `product_change` entity with a `product_id` filter instead, because `query.graph` can’t filter on linked records.
33
44
 
34
- ## Actor references
45
+ ## Actor fields
35
46
 
36
- The `created_by`, `confirmed_by`, `declined_by`, and `canceled_by` fields hold
37
- actor ids (the dashboard user who staged or resolved the change). They are plain
38
- text references for the audit trail, not module links, so they aren't resolved
39
- through `query.graph`.
47
+ `created_by`, `confirmed_by`, `declined_by`, and `canceled_by` hold actor ids as plain text: a seller id when the change came from a vendor route, a user id when it came from an admin route. They aren’t module links, so `query.graph` can’t expand them. Resolve the seller or user yourself when you need their details.
@@ -1,43 +1,62 @@
1
1
  ---
2
- title: "Service reference"
2
+ title: "Product Edit module service"
3
3
  sidebarTitle: "Service"
4
- description: "The Product Edit module service methods for working with records directly."
4
+ description: "Resolve the Product Edit module service and call its generated methods for changes and actions."
5
5
  ---
6
6
 
7
- The Product Edit module exposes a service you can resolve from the Medusa
8
- container to read and write records directly, without going through a workflow.
9
- Use it inside custom services, subscribers, or scheduled jobs.
7
+ The Product Edit module service, `ProductChangeModuleService`, reads and writes `ProductChange` and `ProductChangeAction` records directly. It’s a plain `MedusaService` with generated methods only, and no custom methods. Use it in subscribers, scheduled jobs, and workflow steps.
8
+
9
+ ## Resolve the service
10
+
11
+ Resolve the service from the Medusa container with `MercurModules.PRODUCT_EDIT`:
10
12
 
11
13
  ```ts
12
14
  import { MercurModules } from "@mercurjs/types"
13
15
 
14
- const productChangeModuleService = container.resolve(MercurModules.PRODUCT_EDIT)
16
+ const productEdit = container.resolve(MercurModules.PRODUCT_EDIT)
15
17
 
16
- const [changes, count] = await productChangeModuleService.listAndCountProductChanges({
17
- status: "pending",
18
- })
18
+ const [changes, count] = await productEdit.listAndCountProductChanges(
19
+ { product_id: "prod_01JD8Q1234567", status: "pending" },
20
+ { relations: ["actions"], take: 20 }
21
+ )
19
22
  ```
20
23
 
21
- ## Generated methods
24
+ This returns the first 20 pending changes on a product, with their actions, and the total count. For a typed handle, import the class with `import type { ProductChangeModuleService } from "@mercurjs/core/modules/product-edit"` and pass it as the `resolve` generic.
25
+
26
+ ## ProductChange methods
27
+
28
+ Medusa generates these methods for the `ProductChange` model:
29
+
30
+ | Method | Signature | Description |
31
+ | --- | --- | --- |
32
+ | `retrieveProductChange` | `(id, config?)` | Retrieve one change. Throws if it doesn’t exist |
33
+ | `listProductChanges` | `(filters?, config?)` | List changes matching the filters |
34
+ | `listAndCountProductChanges` | `(filters?, config?)` | List changes and return `[changes, count]` |
35
+ | `createProductChanges` | `(data)` | Create one change or an array of them |
36
+ | `updateProductChanges` | `(data)` | Update by `id`, or pass `{ selector, data }` |
37
+ | `deleteProductChanges` | `(ids)` | Delete changes. The model cascades deletes to their actions |
38
+ | `softDeleteProductChanges` | `(ids, config?)` | Soft-delete changes |
39
+ | `restoreProductChanges` | `(ids, config?)` | Restore soft-deleted changes |
40
+
41
+ ## ProductChangeAction methods
42
+
43
+ The same set exists for `ProductChangeAction`:
22
44
 
23
- Each data model gets a standard set of auto-generated methods. For
24
- `ProductChange`:
45
+ | Method | Signature | Description |
46
+ | --- | --- | --- |
47
+ | `retrieveProductChangeAction` | `(id, config?)` | Retrieve one action |
48
+ | `listProductChangeActions` | `(filters?, config?)` | List actions matching the filters |
49
+ | `listAndCountProductChangeActions` | `(filters?, config?)` | List actions and return `[actions, count]` |
50
+ | `createProductChangeActions` | `(data)` | Create one action or an array of them |
51
+ | `updateProductChangeActions` | `(data)` | Update by `id`, or pass `{ selector, data }` |
52
+ | `deleteProductChangeActions` | `(ids)` | Delete actions |
53
+ | `softDeleteProductChangeActions` | `(ids, config?)` | Soft-delete actions |
54
+ | `restoreProductChangeActions` | `(ids, config?)` | Restore soft-deleted actions |
25
55
 
26
- | Method | Description |
27
- | --- | --- |
28
- | `createProductChanges(data)` | Create one or more changes |
29
- | `retrieveProductChange(id, config?)` | Retrieve a change by id |
30
- | `listProductChanges(filters?, config?)` | List changes matching filters |
31
- | `listAndCountProductChanges(filters?, config?)` | List changes with a total count |
32
- | `updateProductChanges(data)` | Update one or more changes |
33
- | `deleteProductChanges(ids)` | Delete one or more changes |
56
+ ## When to use a workflow instead
34
57
 
35
- The same set exists for `ProductChangeAction`: `createProductChangeActions`,
36
- `listProductChangeActions`, `updateProductChangeActions`, and so on.
58
+ The service writes rows and nothing else. Setting `status` to `confirmed` through `updateProductChanges` doesn’t apply the actions, stamp `confirmed_at`, emit `product-change.confirmed`, or run the hooks.
37
59
 
38
60
  <Warning>
39
- Prefer [workflows](/platform/product-edit/reference/workflows) for anything with
40
- side effects (confirming, declining, applying actions). The service writes
41
- records directly and does **not** apply actions to the product, emit events, or
42
- run compensation.
61
+ Use the [workflows](/platform/product-edit/reference/workflows) for any status change. Keep the service for reads and for fields with no side effects, such as `metadata`.
43
62
  </Warning>