@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,49 +1,191 @@
|
|
|
1
1
|
---
|
|
2
|
-
title: "
|
|
2
|
+
title: "Product Edit workflows"
|
|
3
3
|
sidebarTitle: "Workflows"
|
|
4
|
-
description: "
|
|
4
|
+
description: "Inputs, hooks, and events of every product change workflow, from staging to audit records."
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
This
|
|
8
|
-
`@mercurjs/core/workflows` and run them against the Medusa container.
|
|
7
|
+
This page lists the workflows that stage, resolve, apply, and record product changes. Import them from `@mercurjs/core/workflows` and run them with the Medusa container, for example `confirmProductChangeWorkflow(container).run({ input })`. Each section gives the workflow id, the input, the hooks, and the events.
|
|
9
8
|
|
|
10
|
-
##
|
|
9
|
+
## Edit workflows
|
|
11
10
|
|
|
12
|
-
|
|
11
|
+
These workflows load the current product, diff your input against it, check that the author has no pending change on the product, and stage the result through `stageProductChangeWorkflow`. They return the staged `ProductChangeDTO`.
|
|
12
|
+
|
|
13
|
+
### productEditUpdateProductWorkflow
|
|
14
|
+
|
|
15
|
+
Id `product-edit-update-product`. Stages an `UPDATE` action per changed product field, and a `STATUS_CHANGE` action when `status` differs.
|
|
16
|
+
|
|
17
|
+
| Input | Type | Description |
|
|
13
18
|
| --- | --- | --- |
|
|
14
|
-
| `
|
|
15
|
-
| `
|
|
16
|
-
| `
|
|
17
|
-
| `
|
|
18
|
-
| `cancelProductChangeWorkflow` | `{ id, canceled_by? }` | Move a `pending` change to `canceled` |
|
|
19
|
-
| `autoConfirmProductChangeWorkflow` | `{ change_id, confirmed_by?, force? }` | Confirm when review is off, or when `force` is set |
|
|
19
|
+
| `product_id` | `string` | The product to edit |
|
|
20
|
+
| `created_by` | `string`, optional | The author. Scopes the pending check |
|
|
21
|
+
| `update` | `Record<string, unknown>` | Proposed values. Keys outside the diffed field list are ignored |
|
|
22
|
+
| `additional_data` | `Record<string, unknown>`, optional | Passed to the `productChangeCreated` hook |
|
|
20
23
|
|
|
21
|
-
|
|
24
|
+
Hooks: `validate` with `{ input }`, and `productChangeCreated` with `{ product_change, additional_data }`.
|
|
25
|
+
|
|
26
|
+
### productEditUpdateVariantsWorkflow
|
|
27
|
+
|
|
28
|
+
Id `product-edit-update-variants`. Stages `VARIANT_ADD`, `VARIANT_UPDATE`, and `VARIANT_REMOVE` actions.
|
|
29
|
+
|
|
30
|
+
| Input | Type | Description |
|
|
31
|
+
| --- | --- | --- |
|
|
32
|
+
| `product_id` | `string` | The product to edit |
|
|
33
|
+
| `created_by` | `string`, optional | The author. Scopes the pending check |
|
|
34
|
+
| `operations` | `ProductEditVariantOperation[]` | `{ type: "add", variant }`, `{ type: "update", variant_id, fields }`, or `{ type: "remove", variant_id }` |
|
|
35
|
+
|
|
36
|
+
An update stages only the fields that differ, and `manage_inventory` is never staged. No hooks.
|
|
37
|
+
|
|
38
|
+
### productEditUpdateAttributesWorkflow
|
|
39
|
+
|
|
40
|
+
Id `product-edit-update-attributes`. Stages `ATTRIBUTE_ADD`, `ATTRIBUTE_UPDATE`, and `ATTRIBUTE_REMOVE` actions, skipping entries that wouldn’t change the product.
|
|
41
|
+
|
|
42
|
+
| Input | Type | Description |
|
|
43
|
+
| --- | --- | --- |
|
|
44
|
+
| `product_id` | `string` | The product to edit |
|
|
45
|
+
| `created_by` | `string`, optional | The author. Scopes the pending check |
|
|
46
|
+
| `add` | `ProductAttributeBatchAdd[]`, optional | Attributes to attach |
|
|
47
|
+
| `remove` | `string[]`, optional | Attribute ids to detach |
|
|
48
|
+
| `update` | `ProductAttributeBatchUpdate[]`, optional | Changes to attached attributes |
|
|
49
|
+
| `additional_data` | `Record<string, unknown>`, optional | Passed to the `productChangeCreated` hook |
|
|
50
|
+
|
|
51
|
+
Hooks: `validate` with `{ input }`, and `productChangeCreated` with `{ product_change, additional_data }`.
|
|
52
|
+
|
|
53
|
+
### productEditDeleteProductWorkflow
|
|
54
|
+
|
|
55
|
+
Id `product-edit-delete-product`. Stages a single `PRODUCT_DELETE` action.
|
|
56
|
+
|
|
57
|
+
| Input | Type | Description |
|
|
58
|
+
| --- | --- | --- |
|
|
59
|
+
| `product_id` | `string` | The product to delete |
|
|
60
|
+
| `created_by` | `string`, optional | The author. Scopes the pending check |
|
|
61
|
+
| `auto_confirm` | `boolean`, optional | Defaults to `true` when the product is a `draft`, else `false` |
|
|
62
|
+
|
|
63
|
+
No hooks. On confirm, the product and its offers are deleted.
|
|
64
|
+
|
|
65
|
+
## Staging and resolution workflows
|
|
66
|
+
|
|
67
|
+
These workflows create a change and move it through its statuses.
|
|
68
|
+
|
|
69
|
+
### stageProductChangeWorkflow
|
|
70
|
+
|
|
71
|
+
Id `stage-product-change`. Creates one `pending` change with the given actions, emits `product-change.created`, then runs `autoConfirmProductChangeWorkflow`. It doesn’t check for an open change.
|
|
72
|
+
|
|
73
|
+
| Input | Type | Description |
|
|
74
|
+
| --- | --- | --- |
|
|
75
|
+
| `product_id` | `string` | The product the change targets |
|
|
76
|
+
| `actions` | `Omit<CreateProductChangeActionDTO, "product_change_id">[]` | The actions to stage |
|
|
77
|
+
| `created_by` | `string`, optional | The author. Also used as `confirmed_by` on auto-confirm |
|
|
78
|
+
| `internal_note` | `string`, optional | Note for operators |
|
|
79
|
+
| `external_note` | `string`, optional | Note for the seller |
|
|
80
|
+
| `auto_confirm` | `boolean`, optional | Confirm and apply in the same run |
|
|
81
|
+
|
|
82
|
+
No hooks. Returns the created `ProductChangeDTO`, without its actions.
|
|
83
|
+
|
|
84
|
+
### createProductChangeWorkflow
|
|
85
|
+
|
|
86
|
+
Id `create-product-change`. Creates `pending` changes without actions, after checking that none of the products has a pending change from the same author. When the batch spans several authors, the check covers every pending change on those products.
|
|
87
|
+
|
|
88
|
+
| Input | Type | Description |
|
|
89
|
+
| --- | --- | --- |
|
|
90
|
+
| `changes` | `CreateProductChangeDTO[]` | `product_id`, plus optional notes, `created_by`, and `metadata` |
|
|
91
|
+
| `additional_data` | `Record<string, unknown>`, optional | Passed to the `productChangeCreated` hook |
|
|
92
|
+
|
|
93
|
+
Hooks: `validate` with `{ input }`, and `productChangeCreated` with `{ changes, additional_data }`. Emits `product-change.created` per change.
|
|
22
94
|
|
|
23
|
-
|
|
95
|
+
### autoConfirmProductChangeWorkflow
|
|
96
|
+
|
|
97
|
+
Id `auto-confirm-product-change`. Runs `confirmProductChangeWorkflow` when `force` is `true` or the `product_request` feature flag is off. Otherwise it does nothing.
|
|
98
|
+
|
|
99
|
+
| Input | Type | Description |
|
|
100
|
+
| --- | --- | --- |
|
|
101
|
+
| `change_id` | `string` | The change to confirm |
|
|
102
|
+
| `confirmed_by` | `string`, optional | Stamped on the change |
|
|
103
|
+
| `force` | `boolean`, optional | Confirm regardless of the flag |
|
|
104
|
+
|
|
105
|
+
### confirmProductChangeWorkflow
|
|
106
|
+
|
|
107
|
+
Id `confirm-product-change`. Sets every change in `ids` to `confirmed`, runs `applyProductChangeActionsWorkflow`, and emits `product-change.confirmed` per change. Fails if any change is missing or not `pending`.
|
|
108
|
+
|
|
109
|
+
| Input | Type | Description |
|
|
24
110
|
| --- | --- | --- |
|
|
25
|
-
| `
|
|
26
|
-
| `
|
|
111
|
+
| `ids` | `string[]` | The changes to confirm |
|
|
112
|
+
| `confirmed_by` | `string`, optional | Stamped on each change |
|
|
113
|
+
| `internal_note` | `string`, optional | Overwrites the operator note |
|
|
114
|
+
| `external_note` | `string`, optional | Overwrites the seller note |
|
|
115
|
+
| `additional_data` | `Record<string, unknown>`, optional | Passed to the `productChangeConfirmed` hook |
|
|
27
116
|
|
|
28
|
-
|
|
117
|
+
Hooks: `validate` with `{ input }`, and `productChangeConfirmed` with `{ ids, additional_data }`.
|
|
29
118
|
|
|
30
|
-
|
|
119
|
+
### rejectProductChangeWorkflow
|
|
31
120
|
|
|
32
|
-
|
|
121
|
+
Id `reject-product-change`. Sets one `pending` change to `declined` and emits `product-change.declined`.
|
|
122
|
+
|
|
123
|
+
| Input | Type | Description |
|
|
33
124
|
| --- | --- | --- |
|
|
34
|
-
| `
|
|
35
|
-
| `
|
|
36
|
-
| `
|
|
37
|
-
| `
|
|
125
|
+
| `id` | `string` | The change to decline |
|
|
126
|
+
| `declined_by` | `string`, optional | Stamped on the change |
|
|
127
|
+
| `declined_reason` | `string`, optional | Reason for the seller |
|
|
128
|
+
| `additional_data` | `Record<string, unknown>`, optional | Passed to the `productChangeRejected` hook |
|
|
129
|
+
|
|
130
|
+
Hooks: `validate` with `{ input }`, and `productChangeRejected` with `{ id, additional_data }`.
|
|
38
131
|
|
|
39
|
-
|
|
132
|
+
### cancelProductChangeWorkflow
|
|
40
133
|
|
|
41
|
-
|
|
134
|
+
Id `cancel-product-change`. Sets one `pending` change to `canceled` and emits `product-change.canceled`.
|
|
135
|
+
|
|
136
|
+
| Input | Type | Description |
|
|
42
137
|
| --- | --- | --- |
|
|
43
|
-
| `
|
|
44
|
-
| `
|
|
138
|
+
| `id` | `string` | The change to cancel |
|
|
139
|
+
| `canceled_by` | `string`, optional | Stamped on the change |
|
|
140
|
+
| `additional_data` | `Record<string, unknown>`, optional | Passed to the `productChangeCanceled` hook |
|
|
141
|
+
|
|
142
|
+
Hooks: `validate` with `{ input }`, and `productChangeCanceled` with `{ id, additional_data }`.
|
|
143
|
+
|
|
144
|
+
## Apply workflows
|
|
145
|
+
|
|
146
|
+
`confirmProductChangeWorkflow` runs these for you, so you rarely call them yourself.
|
|
147
|
+
|
|
148
|
+
### applyProductChangeActionsWorkflow
|
|
149
|
+
|
|
150
|
+
Id `apply-product-change-actions`. Input `{ change_ids: string[] }`. Loads the unapplied actions of the given changes, groups them by type, runs Medusa’s product and variant workflows, deletes the offers of deleted products, and marks the handled actions `applied`. No hooks, no events of its own.
|
|
151
|
+
|
|
152
|
+
### applyProductAttributeChangeActionsWorkflow
|
|
153
|
+
|
|
154
|
+
Id `apply-product-attribute-change-actions`. Input `{ product_id, add, remove, update }`. Runs `createAndLinkProductAttributesToProductWorkflow` when any array is non-empty. No hooks.
|
|
155
|
+
|
|
156
|
+
## Audit workflows
|
|
157
|
+
|
|
158
|
+
These workflows record history that doesn’t need review.
|
|
159
|
+
|
|
160
|
+
### recordProductAuditChangeWorkflow
|
|
161
|
+
|
|
162
|
+
Id `record-product-audit-change`. Creates changes that start `confirmed`, with `actor_id` as both `created_by` and `confirmed_by`, and stores every action with `applied: true`. It doesn’t touch the product and emits no event.
|
|
163
|
+
|
|
164
|
+
| Input | Type | Description |
|
|
165
|
+
| --- | --- | --- |
|
|
166
|
+
| `actor_id` | `string`, optional | The actor to stamp |
|
|
167
|
+
| `changes` | `array` | Each entry has `product_id`, `actions`, and optional `internal_note` and `external_note` |
|
|
168
|
+
|
|
169
|
+
### requestProductChangeWorkflow
|
|
170
|
+
|
|
171
|
+
Id `mercur-request-product-change`. Checks that the product is `proposed`, records a `CHANGE_REQUESTED` audit change with the message as `external_note`, and emits `product.change-requested`.
|
|
172
|
+
|
|
173
|
+
| Input | Type | Description |
|
|
174
|
+
| --- | --- | --- |
|
|
175
|
+
| `product_id` | `string` | The proposed product |
|
|
176
|
+
| `message` | `string`, optional | What the seller should change |
|
|
177
|
+
| `actor_id` | `string`, optional | The operator making the request |
|
|
178
|
+
| `additional_data` | `Record<string, unknown>`, optional | Passed to the hook |
|
|
179
|
+
|
|
180
|
+
Hooks: `productChangeRequested` with `{ product_id, message, additional_data }`.
|
|
181
|
+
|
|
182
|
+
## Steps
|
|
183
|
+
|
|
184
|
+
The module’s steps are exported from `@mercurjs/core/workflows` for your own workflows:
|
|
185
|
+
|
|
186
|
+
- **Create**: `createProductChangesStep`, `createProductChangeActionsStep`, `addProductChangeActionStep`
|
|
187
|
+
- **Resolve**: `confirmProductChangesStep`, `declineProductChangeStep`, `cancelProductChangeStep`
|
|
188
|
+
- **Update**: `updateProductChangeActionsStep`, `applyVariantImageLinksStep`
|
|
189
|
+
- **Validate**: `validateNoPendingProductChangeStep`, `validateProductChangeIsPendingStep`, `confirmProductChangeValidationStep`
|
|
45
190
|
|
|
46
|
-
|
|
47
|
-
[Service reference](/platform/product-edit/reference/service). To run side effects
|
|
48
|
-
when a change resolves, see the
|
|
49
|
-
[Event reference](/platform/product-edit/reference/events).
|
|
191
|
+
The create, resolve, and `updateProductChangeActionsStep` steps compensate by deleting or restoring what they wrote. `applyVariantImageLinksStep` has no compensation, and the validation steps write nothing.
|
|
@@ -1,63 +1,64 @@
|
|
|
1
1
|
---
|
|
2
|
-
title: "Product
|
|
2
|
+
title: "Product reviews and store reviews"
|
|
3
3
|
sidebarTitle: "Product vs seller reviews"
|
|
4
|
-
description: "
|
|
4
|
+
description: "How the reference field picks a review's target, and the four links that anchor every review."
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
anchored to the rest of the marketplace.
|
|
7
|
+
A customer can rate the product they bought, the store that sold it, or both. Mercur uses one `Review` model for both cases and sets `reference` to `product` or `seller` to say which one a review rates. The target itself is stored as a module link, written when the review is created, alongside links to the order and the customer.
|
|
9
8
|
|
|
10
|
-
##
|
|
9
|
+
## The reference field picks the link
|
|
11
10
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
11
|
+
You pass `reference` and `reference_id` to `createReviewWorkflow`. The workflow stores `reference` on the review, then writes one link from `reference_id` to the review:
|
|
12
|
+
|
|
13
|
+
| `reference` | `reference_id` is | Link written |
|
|
14
|
+
| --- | --- | --- |
|
|
15
|
+
| `product` | A product id, such as `prod_01JD8Q1234567` | Product to review |
|
|
16
|
+
| `seller` | A seller id, such as `sel_01JD8Q1234567` | Seller to review |
|
|
17
|
+
|
|
18
|
+
`reference_id` isn’t stored on the review. To read the target back, follow the relation that matches `reference`:
|
|
16
19
|
|
|
17
20
|
```ts
|
|
18
|
-
|
|
19
|
-
|
|
21
|
+
const { data: [review] } = await query.graph({
|
|
22
|
+
entity: "review",
|
|
23
|
+
fields: ["id", "reference", "rating", "product.title", "seller.name"],
|
|
24
|
+
filters: { id: "rev_01JD8Q1234567" },
|
|
25
|
+
})
|
|
20
26
|
|
|
21
|
-
|
|
22
|
-
|
|
27
|
+
const target = review.reference === "product"
|
|
28
|
+
? review.product
|
|
29
|
+
: review.seller
|
|
23
30
|
```
|
|
24
31
|
|
|
25
|
-
|
|
26
|
-
`reference: "product"` links the review to a product, and `reference: "seller"`
|
|
27
|
-
links it to a seller. Reading a review back, you follow the matching relation
|
|
28
|
-
(`review.product` or `review.seller`) to reach its target.
|
|
32
|
+
Only one of `review.product` and `review.seller` is set on any review.
|
|
29
33
|
|
|
30
34
|
<Note>
|
|
31
|
-
Products are
|
|
32
|
-
rates the master product; a **seller** review rates the store. They are
|
|
33
|
-
independent. A customer can leave both for the same order.
|
|
35
|
+
Products are Mercur’s shared master catalog, not owned by a store. A product review rates the master product, so it has no link to the store that sold it. Only seller reviews are linked to a seller.
|
|
34
36
|
</Note>
|
|
35
37
|
|
|
36
|
-
##
|
|
38
|
+
## Every review links to its order and customer
|
|
37
39
|
|
|
38
|
-
|
|
39
|
-
customer who wrote it. Creating a review writes two more links, one to the
|
|
40
|
-
`Order` and one to the `Customer`, so a review always has a verifiable purchase
|
|
41
|
-
behind it.
|
|
40
|
+
Apart from its target, each review is linked to the order that earned it and the customer who wrote it. The `create-review` step writes both links in the same step that creates the record. These links back the ownership checks across the APIs:
|
|
42
41
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
entity: "customer_customer_review_review",
|
|
47
|
-
fields: ["review.*"],
|
|
48
|
-
filters: { customer_id: "cus_123" },
|
|
49
|
-
})
|
|
50
|
-
```
|
|
42
|
+
- **Store API**: a customer can read, edit, and delete only reviews linked to their customer id
|
|
43
|
+
- **Vendor API**: a store can list, read, and answer only reviews linked to its seller id
|
|
44
|
+
- **Validation**: duplicate checks look up existing reviews through the order link
|
|
51
45
|
|
|
52
|
-
##
|
|
46
|
+
## Stores see only seller reviews
|
|
53
47
|
|
|
54
|
-
Because
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
48
|
+
Because product reviews carry no seller link, the Vendor API never returns them. `GET /vendor/reviews` filters on the seller link, and answering a review checks the same link. A store can’t answer a review of a product it sells: only an operator can, through the Admin API.
|
|
49
|
+
|
|
50
|
+
## One review per target per order
|
|
51
|
+
|
|
52
|
+
`createReviewWorkflow` rejects a second review of the same target from the same order. It loads every review linked to `order_id` and fails with “Review already exists” when one has the same `reference` and the same target id. It also fails with “Order not found!” when the order doesn’t belong to `customer_id`.
|
|
58
53
|
|
|
59
54
|
<Tip>
|
|
60
|
-
|
|
61
|
-
example, a customer may review the master product *and* the store that
|
|
62
|
-
fulfilled it from the same order.
|
|
55
|
+
One order can back several reviews, one per distinct target. A customer can rate the master product and the store from the same order, as order `#1001` does in the overview figure.
|
|
63
56
|
</Tip>
|
|
57
|
+
|
|
58
|
+
The workflow doesn’t check that `reference_id` appears in the order. A customer with any order can review any product or seller id. Add that check in your own route if your marketplace needs it.
|
|
59
|
+
|
|
60
|
+
## Related
|
|
61
|
+
|
|
62
|
+
- [The four review links and their query.graph aliases](/platform/review/reference/links)
|
|
63
|
+
- [Respond to a review as a store](/platform/review/guides/respond-to-a-review)
|
|
64
|
+
- [What a review stores](/platform/review/concepts/the-review-model)
|
|
@@ -1,64 +1,65 @@
|
|
|
1
1
|
---
|
|
2
|
-
title: "
|
|
2
|
+
title: "How reviews are moderated and rated"
|
|
3
3
|
sidebarTitle: "Ratings & moderation"
|
|
4
|
-
description: "The review status
|
|
4
|
+
description: "The review status values, who can change them, how a store answers, and how average ratings are computed."
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
per-product and per-seller averages are computed.
|
|
7
|
+
A review reaches shoppers only after an operator approves it, and a store can add one public answer. Moderation is a change to the review’s `status` field, an answer is a write to its `seller_note`, and removal is a soft delete. Average ratings aren’t stored anywhere: the Review module service computes them from the review rows each time you ask.
|
|
9
8
|
|
|
10
|
-
## Status
|
|
9
|
+
## Status values
|
|
11
10
|
|
|
12
|
-
A review
|
|
13
|
-
moves through three statuses:
|
|
14
|
-
|
|
15
|
-
```
|
|
16
|
-
┌──────────┐ publish ┌────────────┐
|
|
17
|
-
│ pending │───────────►│ published │
|
|
18
|
-
└────┬─────┘ └────────────┘
|
|
19
|
-
│ reject
|
|
20
|
-
▼
|
|
21
|
-
┌────────────┐
|
|
22
|
-
│ rejected │
|
|
23
|
-
└────────────┘
|
|
24
|
-
```
|
|
11
|
+
A review’s `status` holds one of three values. Every review starts as `pending`, and only `updateReviewWorkflow` changes it.
|
|
25
12
|
|
|
26
13
|
| Status | Meaning |
|
|
27
14
|
| --- | --- |
|
|
28
|
-
| `pending` | Submitted
|
|
29
|
-
| `published` | Approved
|
|
30
|
-
| `rejected` | Declined by
|
|
15
|
+
| `pending` | Submitted and waiting for an operator. The default |
|
|
16
|
+
| `published` | Approved for display |
|
|
17
|
+
| `rejected` | Declined by an operator |
|
|
18
|
+
|
|
19
|
+
<img className="block dark:hidden" src="/images/figures/review-moderation-light.svg" alt="Behind, a review record. In front, the changes each workflow makes: updateReviewWorkflow moves status between pending, published, and rejected in any direction, respondReviewWorkflow sets seller_note once, and deleteReviewWorkflow sets deleted_at." />
|
|
20
|
+
<img className="hidden dark:block" src="/images/figures/review-moderation-dark.svg" alt="Behind, a review record. In front, the changes each workflow makes: updateReviewWorkflow moves status between pending, published, and rejected in any direction, respondReviewWorkflow sets seller_note once, and deleteReviewWorkflow sets deleted_at." />
|
|
21
|
+
|
|
22
|
+
The workflow enforces no order between statuses. An operator can publish a rejected review or move a published one back to `pending`. Who can set the status depends on the API:
|
|
23
|
+
|
|
24
|
+
- **Admin API**: `POST /admin/reviews/:id` sets `status`, `rating`, and `customer_note`
|
|
25
|
+
- **Vendor API**: a store can’t change `status` or `rating`; the respond route ignores both fields
|
|
26
|
+
- **Store API**: a customer can edit `rating` and `customer_note`, but not `status`
|
|
31
27
|
|
|
32
|
-
|
|
33
|
-
`
|
|
34
|
-
|
|
28
|
+
<Warning>
|
|
29
|
+
A customer edit doesn’t reset a published review to `pending`. If edited reviews should go back through moderation, wrap the Store route or `updateReviewWorkflow` and set the status yourself.
|
|
30
|
+
</Warning>
|
|
35
31
|
|
|
36
|
-
## Store
|
|
32
|
+
## Store answers
|
|
37
33
|
|
|
38
|
-
A store
|
|
39
|
-
lives in the `seller_note` field and is added through a dedicated respond flow,
|
|
40
|
-
which refuses to overwrite an existing response.
|
|
34
|
+
A store answers a review by writing `seller_note` through `respondReviewWorkflow`. The workflow refuses a second answer: it fails when `seller_note` is already set, so the first answer stays until someone clears it with `updateReviewWorkflow`. Answering never touches `status`, so a store can answer a `pending` review before it’s published.
|
|
41
35
|
|
|
42
|
-
|
|
43
|
-
Responding is separate from moderation. A store adds its `seller_note`, while
|
|
44
|
-
the `status` transition (`published` / `rejected`) stays an operator decision.
|
|
45
|
-
</Note>
|
|
36
|
+
Over HTTP, a store answers with `POST /vendor/reviews/:id`, which only accepts reviews linked to that store, so only seller reviews. An operator answers any review with `POST /admin/reviews/:id/respond`.
|
|
46
37
|
|
|
47
|
-
##
|
|
38
|
+
## Deletion is soft
|
|
48
39
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
40
|
+
`deleteReviewWorkflow` soft deletes the review: it sets `deleted_at` and keeps the row and its links. `query.graph` and the APIs stop returning it, and the workflow restores it if a later step fails. Operators delete with `DELETE /admin/reviews/:id`, and customers delete their own reviews with `DELETE /store/reviews/:id`.
|
|
41
|
+
|
|
42
|
+
## Average ratings are computed on read
|
|
43
|
+
|
|
44
|
+
The service’s rating helpers run an `AVG(rating)` query over the review table, joined through the product or seller link table. There’s no stored average to keep in sync. The helpers apply no filters beyond the target, which affects what counts:
|
|
45
|
+
|
|
46
|
+
- **Every status counts**: `pending` and `rejected` reviews are part of the average
|
|
47
|
+
- **Soft deleted reviews count**: the query doesn’t check `deleted_at`, and the link rows stay in place
|
|
48
|
+
- **No reviews**: `getAvgRating` returns `null`
|
|
53
49
|
|
|
54
50
|
```ts
|
|
55
|
-
const
|
|
51
|
+
const reviewService = container.resolve(MercurModules.REVIEW)
|
|
56
52
|
|
|
57
|
-
const
|
|
53
|
+
const average = await reviewService.getAvgRating(
|
|
54
|
+
"seller",
|
|
55
|
+
"sel_01JD8Q1234567"
|
|
56
|
+
)
|
|
58
57
|
```
|
|
59
58
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
59
|
+
This returns the store’s average as a numeric string, such as `"4.5000000000000000"`, or `null`. When only published reviews should count, compute the average from `query.graph` results instead, as shown in [Compute aggregate ratings](/platform/review/guides/compute-aggregate-ratings).
|
|
60
|
+
|
|
61
|
+
## Related
|
|
62
|
+
|
|
63
|
+
- [Moderate a review from server code](/platform/review/guides/moderate-a-review)
|
|
64
|
+
- [Respond to a review](/platform/review/guides/respond-to-a-review)
|
|
65
|
+
- [Rating helper signatures](/platform/review/reference/service)
|
|
@@ -1,56 +1,63 @@
|
|
|
1
1
|
---
|
|
2
|
-
title: "
|
|
2
|
+
title: "What a review stores"
|
|
3
3
|
sidebarTitle: "The review model"
|
|
4
|
-
description: "The
|
|
4
|
+
description: "The fields of a review: its target type, rating, the customer's note, the store's answer, and its status."
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
A review is one customer’s rating of one product or one store, earned by an order. Mercur stores it as a single `Review` record (table `review`, id prefix `rev`) in the Review module. Knowing its fields tells you what a review can hold, what a store can add to it, and what an operator changes when moderating it.
|
|
8
8
|
|
|
9
|
-
##
|
|
9
|
+
## One record per rating
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
11
|
+
Every review has the same shape, whatever it rates. The record holds the score and the text around it, while the product, store, order, and customer it belongs to live in module links rather than columns.
|
|
12
|
+
|
|
13
|
+
| Field | Holds |
|
|
14
|
+
| --- | --- |
|
|
15
|
+
| `id` | The review id, such as `rev_01JD8Q1234567` |
|
|
16
|
+
| `display_id` | An auto-incrementing number for people to read |
|
|
17
|
+
| `reference` | The target type: `product` or `seller` |
|
|
18
|
+
| `rating` | The score as an integer |
|
|
19
|
+
| `customer_note` | The customer’s text, or `null` |
|
|
20
|
+
| `seller_note` | The store’s answer, or `null` |
|
|
21
|
+
| `status` | `pending`, `published`, or `rejected` |
|
|
22
|
+
|
|
23
|
+
A review has no `product_id` or `seller_id` column. The `reference` value tells you which link to follow, as covered in [Product vs seller reviews](/platform/review/concepts/product-vs-seller-reviews).
|
|
24
|
+
|
|
25
|
+
## Ratings are integers from 1 to 5 at the API
|
|
26
|
+
|
|
27
|
+
The `rating` column is an integer with no range check in the model itself. The Store and Admin API validators accept whole numbers from 1 to 5, so every review created over HTTP falls in that range:
|
|
28
|
+
|
|
29
|
+
- **Store API**: `rating` is required on create and update, and must be an integer from 1 to 5
|
|
30
|
+
- **Admin API**: `rating` is optional on update, with the same 1 to 5 range
|
|
31
|
+
- **Workflows**: `createReviewWorkflow` and `updateReviewWorkflow` store whatever number you pass
|
|
32
|
+
|
|
33
|
+
<Warning>
|
|
34
|
+
When you run the workflows from your own code, validate `rating` yourself. A value outside 1 to 5 is stored as is and skews every average that includes it.
|
|
35
|
+
</Warning>
|
|
36
|
+
|
|
37
|
+
## Two notes, two authors
|
|
38
|
+
|
|
39
|
+
A review keeps the two sides of the conversation in separate fields. The customer writes `customer_note` when they submit or edit the review, and the store writes `seller_note` once, through `respondReviewWorkflow`. Both are nullable text fields marked searchable, so a `q` search in the Admin and Vendor APIs matches their content.
|
|
15
40
|
|
|
16
41
|
```ts
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
})
|
|
42
|
+
const review = {
|
|
43
|
+
id: "rev_01JD8Q1234567",
|
|
44
|
+
display_id: 1042,
|
|
45
|
+
reference: "product",
|
|
46
|
+
rating: 4,
|
|
47
|
+
customer_note: "Fits well, the colour is darker than shown.",
|
|
48
|
+
seller_note: "Thanks, we've updated the product photos.",
|
|
49
|
+
status: "published",
|
|
50
|
+
}
|
|
27
51
|
```
|
|
28
52
|
|
|
29
|
-
|
|
53
|
+
This is the shape `query.graph` returns for a review’s own fields. The Store API caps `customer_note` at 300 characters, the Admin API at 1000, and both APIs cap `seller_note` at 300.
|
|
30
54
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
<Note>
|
|
41
|
-
There is no separate table for product reviews and seller reviews. A single
|
|
42
|
-
`Review` row is discriminated by its `reference` field. See
|
|
43
|
-
[Product vs seller reviews](/platform/review/concepts/product-vs-seller-reviews).
|
|
44
|
-
</Note>
|
|
45
|
-
|
|
46
|
-
## Notes
|
|
47
|
-
|
|
48
|
-
A review separates the two sides of the conversation into two nullable text
|
|
49
|
-
fields. `customer_note` is written by the customer when they submit the review;
|
|
50
|
-
`seller_note` is the store's single response, added later through the respond
|
|
51
|
-
flow. Both are searchable so operators can find reviews by their content.
|
|
52
|
-
|
|
53
|
-
<Tip>
|
|
54
|
-
A store can respond **once**. The respond flow refuses to overwrite an existing
|
|
55
|
-
`seller_note`. To change a response, clear it first.
|
|
56
|
-
</Tip>
|
|
55
|
+
## Status starts at pending
|
|
56
|
+
|
|
57
|
+
Every new review gets `status: "pending"`, the model default. An operator then sets it to `published` or `rejected` with `updateReviewWorkflow`. Nothing in core hides a pending review from a storefront for you, since core has no public review listing: your own route decides which statuses to show. See [Ratings and moderation](/platform/review/concepts/ratings-and-moderation) for the full set of changes.
|
|
58
|
+
|
|
59
|
+
## Related
|
|
60
|
+
|
|
61
|
+
- [How reviews pick their target](/platform/review/concepts/product-vs-seller-reviews)
|
|
62
|
+
- [Every field and index of the Review model](/platform/review/reference/data-models)
|
|
63
|
+
- [Create a review from a custom route](/platform/review/guides/create-a-review)
|