@mercurjs/docs 2.3.8-canary.3 → 2.3.9-canary.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/content/learn/architecture.mdx +166 -263
- package/content/learn/introduction.mdx +9 -1
- package/content/learn/use-cases.mdx +70 -0
- package/content/platform/attribute/concepts/attribute-types.mdx +73 -46
- package/content/platform/attribute/concepts/global-vs-inline.mdx +54 -32
- package/content/platform/attribute/concepts/variant-axes.mdx +54 -32
- package/content/platform/attribute/guides/attach-attributes-to-a-product.mdx +96 -53
- package/content/platform/attribute/guides/create-a-variant-axis.mdx +106 -57
- package/content/platform/attribute/guides/create-an-attribute.mdx +105 -56
- package/content/platform/attribute/overview.mdx +37 -33
- package/content/platform/attribute/reference/data-models.mdx +53 -38
- package/content/platform/attribute/reference/events.mdx +47 -24
- package/content/platform/attribute/reference/links.mdx +98 -27
- package/content/platform/attribute/reference/service.mdx +71 -32
- package/content/platform/attribute/reference/workflows.mdx +158 -28
- package/content/platform/catalog/concepts/master-products.mdx +39 -37
- package/content/platform/catalog/concepts/product-seller-allowlist.mdx +56 -35
- package/content/platform/catalog/concepts/status-lifecycle.mdx +55 -49
- package/content/platform/catalog/concepts/variants-categories-collections.mdx +55 -35
- package/content/platform/catalog/guides/allowlist-stores.mdx +100 -37
- package/content/platform/catalog/guides/create-a-master-product.mdx +124 -48
- package/content/platform/catalog/guides/publish-or-reject-a-product.mdx +120 -55
- package/content/platform/catalog/overview.mdx +39 -35
- package/content/platform/catalog/reference/data-models.mdx +154 -43
- package/content/platform/catalog/reference/events.mdx +41 -25
- package/content/platform/catalog/reference/links.mdx +196 -28
- package/content/platform/catalog/reference/service.mdx +57 -35
- package/content/platform/catalog/reference/workflows.mdx +124 -30
- package/content/platform/commission/concepts/order-commission-lines.mdx +54 -52
- package/content/platform/commission/concepts/rule-matching.mdx +53 -63
- package/content/platform/commission/concepts/rules-and-rates.mdx +61 -59
- package/content/platform/commission/guides/batch-update-rules.mdx +69 -41
- package/content/platform/commission/guides/create-a-rate.mdx +101 -53
- package/content/platform/commission/guides/refresh-order-commission-lines.mdx +104 -35
- package/content/platform/commission/overview.mdx +43 -35
- package/content/platform/commission/reference/data-models.mdx +59 -45
- package/content/platform/commission/reference/events.mdx +28 -34
- package/content/platform/commission/reference/links.mdx +42 -33
- package/content/platform/commission/reference/service.mdx +103 -28
- package/content/platform/commission/reference/workflows.mdx +101 -27
- package/content/platform/offer/concepts/pricing-and-inventory.mdx +63 -49
- package/content/platform/offer/concepts/shipping.mdx +28 -29
- package/content/platform/offer/concepts/what-is-an-offer.mdx +48 -45
- package/content/platform/offer/guides/bulk-create-offers.mdx +97 -61
- package/content/platform/offer/guides/create-an-offer.mdx +112 -60
- package/content/platform/offer/guides/manage-offer-inventory.mdx +78 -38
- package/content/platform/offer/overview.mdx +39 -32
- package/content/platform/offer/reference/data-models.mdx +77 -47
- package/content/platform/offer/reference/events.mdx +44 -18
- package/content/platform/offer/reference/links.mdx +92 -40
- package/content/platform/offer/reference/service.mdx +66 -31
- package/content/platform/offer/reference/workflows.mdx +146 -37
- package/content/platform/order-group/concepts/computed-totals.mdx +40 -37
- package/content/platform/order-group/concepts/order-splitting.mdx +41 -39
- package/content/platform/order-group/concepts/the-order-group.mdx +39 -32
- package/content/platform/order-group/guides/list-order-groups.mdx +96 -56
- package/content/platform/order-group/guides/retrieve-an-order-group.mdx +93 -42
- package/content/platform/order-group/guides/split-a-cart.mdx +115 -40
- package/content/platform/order-group/overview.mdx +42 -32
- package/content/platform/order-group/reference/data-models.mdx +48 -31
- package/content/platform/order-group/reference/events.mdx +29 -17
- package/content/platform/order-group/reference/links.mdx +90 -29
- package/content/platform/order-group/reference/service.mdx +55 -31
- package/content/platform/order-group/reference/workflows.mdx +83 -24
- package/content/platform/payout/concepts/account-lifecycle.mdx +64 -67
- package/content/platform/payout/concepts/accounts-and-onboarding.mdx +50 -40
- package/content/platform/payout/concepts/payout-pipeline.mdx +70 -73
- package/content/platform/payout/guides/create-a-payout-account.mdx +65 -41
- package/content/platform/payout/guides/process-a-provider-webhook.mdx +84 -38
- package/content/platform/payout/guides/start-provider-onboarding.mdx +90 -46
- package/content/platform/payout/overview.mdx +43 -34
- package/content/platform/payout/reference/data-models.mdx +63 -36
- package/content/platform/payout/reference/events.mdx +56 -30
- package/content/platform/payout/reference/links.mdx +52 -22
- package/content/platform/payout/reference/service.mdx +72 -33
- package/content/platform/payout/reference/workflows.mdx +80 -19
- package/content/platform/product-edit/concepts/change-actions.mdx +52 -45
- package/content/platform/product-edit/concepts/change-pipeline.mdx +60 -52
- package/content/platform/product-edit/concepts/status-and-auto-confirm.mdx +43 -46
- package/content/platform/product-edit/guides/confirm-or-decline-a-change.mdx +104 -56
- package/content/platform/product-edit/guides/edit-a-product.mdx +126 -46
- package/content/platform/product-edit/guides/request-a-revision.mdx +99 -39
- package/content/platform/product-edit/overview.mdx +41 -34
- package/content/platform/product-edit/reference/data-models.mdx +73 -47
- package/content/platform/product-edit/reference/events.mdx +39 -29
- package/content/platform/product-edit/reference/links.mdx +32 -24
- package/content/platform/product-edit/reference/service.mdx +45 -26
- package/content/platform/product-edit/reference/workflows.mdx +173 -31
- package/content/platform/review/concepts/product-vs-seller-reviews.mdx +42 -41
- package/content/platform/review/concepts/ratings-and-moderation.mdx +45 -44
- package/content/platform/review/concepts/the-review-model.mdx +52 -45
- package/content/platform/review/guides/compute-aggregate-ratings.mdx +62 -37
- package/content/platform/review/guides/create-a-review.mdx +109 -39
- package/content/platform/review/guides/moderate-a-review.mdx +83 -37
- package/content/platform/review/guides/respond-to-a-review.mdx +85 -40
- package/content/platform/review/overview.mdx +43 -33
- package/content/platform/review/reference/data-models.mdx +41 -27
- package/content/platform/review/reference/events.mdx +55 -37
- package/content/platform/review/reference/links.mdx +80 -22
- package/content/platform/review/reference/service.mdx +61 -33
- package/content/platform/review/reference/workflows.mdx +80 -24
- package/content/platform/store/concepts/lifecycle.mdx +68 -44
- package/content/platform/store/concepts/store-entity.mdx +45 -27
- package/content/platform/store/concepts/team.mdx +56 -35
- package/content/platform/store/guides/create-a-store.mdx +102 -35
- package/content/platform/store/guides/manage-the-team.mdx +122 -32
- package/content/platform/store/guides/moderate-a-store.mdx +107 -38
- package/content/platform/store/overview.mdx +41 -34
- package/content/platform/store/reference/data-models.mdx +132 -57
- package/content/platform/store/reference/events.mdx +42 -17
- package/content/platform/store/reference/links.mdx +67 -49
- package/content/platform/store/reference/service.mdx +105 -33
- package/content/platform/store/reference/workflows.mdx +304 -24
- package/content/references/api/conventions.mdx +20 -0
- package/content/resources/integrations/magento2.mdx +162 -0
- package/content/resources/integrations/overview.mdx +10 -0
- package/content/resources/integrations/stripe-connect.mdx +1 -1
- package/content/resources/tutorials/custom-api-route.mdx +3 -2
- package/llms.txt +113 -111
- package/package.json +1 -1
|
@@ -1,66 +1,177 @@
|
|
|
1
1
|
---
|
|
2
|
-
title: "
|
|
2
|
+
title: "Catalog data models"
|
|
3
3
|
sidebarTitle: "Data models"
|
|
4
|
-
description: "
|
|
4
|
+
description: "Every field on the Medusa Product module models the shared catalog is built on, and the relations Mercur adds."
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
The
|
|
8
|
-
layer added by Mercur. This reference lists the models at the Mercur-relevant
|
|
9
|
-
level. It doesn't restate every Medusa product field. For the full module, see
|
|
10
|
-
the [Medusa Product module](https://docs.medusajs.com/resources/commerce-modules/product).
|
|
7
|
+
The catalog's data models belong to Medusa's Product module, version 2.22. Mercur adds no columns to them. It adds relations through [module links](/platform/catalog/reference/links), listed under each model. Every model also has `created_at`, `updated_at`, and `deleted_at` timestamps that Medusa manages.
|
|
11
8
|
|
|
12
9
|
## Product
|
|
13
10
|
|
|
14
|
-
Table `product`, id prefix `prod`.
|
|
15
|
-
store. The marketplace-relevant fields:
|
|
11
|
+
The shared master product. Table `product`, id prefix `prod`.
|
|
16
12
|
|
|
17
|
-
| Field | Type |
|
|
13
|
+
| Field | Type | Description |
|
|
18
14
|
| --- | --- | --- |
|
|
19
|
-
| `id` | text | Primary key |
|
|
20
|
-
| `title` | text |
|
|
21
|
-
| `handle` | text |
|
|
22
|
-
| `subtitle`
|
|
23
|
-
| `
|
|
24
|
-
| `
|
|
25
|
-
| `
|
|
15
|
+
| `id` | `text` | Primary key |
|
|
16
|
+
| `title` | `text` | Display title, searchable and translatable |
|
|
17
|
+
| `handle` | `text` | URL handle, unique among non-deleted products |
|
|
18
|
+
| `subtitle` | `text`, nullable | Subtitle, searchable and translatable |
|
|
19
|
+
| `description` | `text`, nullable | Description, searchable and translatable |
|
|
20
|
+
| `is_giftcard` | `boolean` | Whether the product is a gift card. Default `false` |
|
|
21
|
+
| `status` | `enum` | `draft`, `proposed`, `published`, or `rejected`. Default `draft` |
|
|
22
|
+
| `thumbnail` | `text`, nullable | Thumbnail image URL |
|
|
23
|
+
| `weight` | `float`, nullable | Weight |
|
|
24
|
+
| `length` | `float`, nullable | Length |
|
|
25
|
+
| `height` | `float`, nullable | Height |
|
|
26
|
+
| `width` | `float`, nullable | Width |
|
|
27
|
+
| `origin_country` | `text`, nullable | Country of origin |
|
|
28
|
+
| `hs_code` | `text`, nullable | Harmonized System code |
|
|
29
|
+
| `mid_code` | `text`, nullable | Manufacturer identification code |
|
|
30
|
+
| `material` | `text`, nullable | Material, translatable |
|
|
31
|
+
| `discountable` | `boolean` | Whether promotions can apply. Default `true` |
|
|
32
|
+
| `external_id` | `text`, nullable | Id in an external system |
|
|
33
|
+
| `metadata` | `json`, nullable | Custom key-value data |
|
|
34
|
+
|
|
35
|
+
Native relations:
|
|
36
|
+
|
|
37
|
+
- **`variants`**: `ProductVariant[]`, deleted with the product
|
|
38
|
+
- **`options`**: `ProductOption[]` through the `ProductProductOption` pivot
|
|
39
|
+
- **`images`**: `ProductImage[]`, deleted with the product
|
|
40
|
+
- **`categories`**: `ProductCategory[]` through `product_category_product`
|
|
41
|
+
- **`collection`**: `ProductCollection`, nullable
|
|
42
|
+
- **`type`**: `ProductType`, nullable
|
|
43
|
+
- **`tags`**: `ProductTag[]` through `product_tags`
|
|
44
|
+
|
|
45
|
+
Mercur relations:
|
|
46
|
+
|
|
47
|
+
- **`sellers`**: `Seller[]`, the store allowlist in `product_seller`
|
|
48
|
+
- **`product_attribute_values`**: attribute values attached to the product
|
|
49
|
+
- **`scoped_attributes`**: product-scoped attributes, read-only
|
|
50
|
+
- **`changes`**: `ProductChange[]`, the audit and edit history, read-only
|
|
51
|
+
- **`reviews`**: `Review[]`
|
|
26
52
|
|
|
27
|
-
|
|
28
|
-
`collection`, `tags`, `type`, `images` (native Medusa), plus Mercur's `sellers`
|
|
29
|
-
(the allowlist), `offers`, `scoped_attributes`, and `changes` (audit history).
|
|
53
|
+
## ProductVariant
|
|
30
54
|
|
|
31
|
-
|
|
55
|
+
One purchasable configuration of a product. Table `product_variant`, id prefix `variant`.
|
|
32
56
|
|
|
33
|
-
|
|
34
|
-
|
|
57
|
+
| Field | Type | Description |
|
|
58
|
+
| --- | --- | --- |
|
|
59
|
+
| `id` | `text` | Primary key |
|
|
60
|
+
| `title` | `text` | Display title, searchable and translatable |
|
|
61
|
+
| `sku` | `text`, nullable | Catalog SKU, unique among non-deleted variants |
|
|
62
|
+
| `barcode` | `text`, nullable | Barcode, unique |
|
|
63
|
+
| `ean` | `text`, nullable | EAN, unique |
|
|
64
|
+
| `upc` | `text`, nullable | UPC, unique |
|
|
65
|
+
| `allow_backorder` | `boolean` | Default `false` |
|
|
66
|
+
| `manage_inventory` | `boolean` | Model default `true`; Mercur's create workflow sends `false` |
|
|
67
|
+
| `hs_code` | `text`, nullable | Harmonized System code |
|
|
68
|
+
| `origin_country` | `text`, nullable | Country of origin |
|
|
69
|
+
| `mid_code` | `text`, nullable | Manufacturer identification code |
|
|
70
|
+
| `material` | `text`, nullable | Material, translatable |
|
|
71
|
+
| `weight` | `float`, nullable | Weight |
|
|
72
|
+
| `length` | `float`, nullable | Length |
|
|
73
|
+
| `height` | `float`, nullable | Height |
|
|
74
|
+
| `width` | `float`, nullable | Width |
|
|
75
|
+
| `thumbnail` | `text`, nullable | Variant thumbnail URL |
|
|
76
|
+
| `variant_rank` | `number`, nullable | Sort order. Default `0` |
|
|
77
|
+
| `metadata` | `json`, nullable | Custom key-value data |
|
|
78
|
+
|
|
79
|
+
Relations: `product` (nullable), `options` (`ProductOptionValue[]` through `product_variant_option`), and `images` (`ProductImage[]`). Mercur links offers to variants through `offer.variant_id`. Each store's price and stock live on the [offer](/platform/offer/reference/data-models), not here.
|
|
80
|
+
|
|
81
|
+
## ProductOption
|
|
82
|
+
|
|
83
|
+
An axis that variants vary on. Table `product_option`, id prefix `opt`.
|
|
84
|
+
|
|
85
|
+
| Field | Type | Description |
|
|
86
|
+
| --- | --- | --- |
|
|
87
|
+
| `id` | `text` | Primary key |
|
|
88
|
+
| `title` | `text` | Option name, such as `Size` |
|
|
89
|
+
| `is_exclusive` | `boolean` | `true` when the option belongs to one product. Default `false` |
|
|
90
|
+
| `metadata` | `json`, nullable | Custom key-value data |
|
|
35
91
|
|
|
36
|
-
|
|
37
|
-
| --- | --- |
|
|
38
|
-
| `product_id` | `product.id` |
|
|
39
|
-
| `seller_id` | `seller.id` |
|
|
92
|
+
Relations: `products` and `values` (`ProductOptionValue[]`). A variant-axis attribute mirrors one option through `product_attribute.product_option_id`.
|
|
40
93
|
|
|
41
|
-
|
|
42
|
-
product; a store not on it cannot.
|
|
94
|
+
## ProductOptionValue
|
|
43
95
|
|
|
44
|
-
|
|
96
|
+
One value of an option. Table `product_option_value`, id prefix `optval`.
|
|
97
|
+
|
|
98
|
+
| Field | Type | Description |
|
|
99
|
+
| --- | --- | --- |
|
|
100
|
+
| `id` | `text` | Primary key |
|
|
101
|
+
| `value` | `text` | The value, such as `M` |
|
|
102
|
+
| `rank` | `number`, nullable | Sort order |
|
|
103
|
+
| `metadata` | `json`, nullable | Custom key-value data |
|
|
104
|
+
|
|
105
|
+
Relations: `option` (nullable), `variants`, and `product_options`.
|
|
45
106
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
107
|
+
## ProductImage
|
|
108
|
+
|
|
109
|
+
A product image. Table `image`, id prefix `img`.
|
|
110
|
+
|
|
111
|
+
| Field | Type | Description |
|
|
112
|
+
| --- | --- | --- |
|
|
113
|
+
| `id` | `text` | Primary key |
|
|
114
|
+
| `url` | `text` | Image URL |
|
|
115
|
+
| `rank` | `number` | Sort order. Default `0` |
|
|
116
|
+
| `metadata` | `json`, nullable | Custom key-value data |
|
|
117
|
+
|
|
118
|
+
Relations: `product` and `variants`.
|
|
50
119
|
|
|
51
120
|
## ProductCategory
|
|
52
121
|
|
|
53
|
-
Table `product_category`, id prefix `pcat`.
|
|
54
|
-
|
|
55
|
-
|
|
122
|
+
A node in the category tree. Table `product_category`, id prefix `pcat`.
|
|
123
|
+
|
|
124
|
+
| Field | Type | Description |
|
|
125
|
+
| --- | --- | --- |
|
|
126
|
+
| `id` | `text` | Primary key |
|
|
127
|
+
| `name` | `text` | Display name, searchable and translatable |
|
|
128
|
+
| `description` | `text` | Description. Default `""` |
|
|
129
|
+
| `handle` | `text` | URL handle, unique |
|
|
130
|
+
| `mpath` | `text` | Materialized path of ancestor ids |
|
|
131
|
+
| `is_active` | `boolean` | Whether the category is live. Default `false` |
|
|
132
|
+
| `is_internal` | `boolean` | Hidden from the storefront. Default `false` |
|
|
133
|
+
| `rank` | `number` | Sort order among siblings. Default `0` |
|
|
134
|
+
| `external_id` | `text`, nullable | Id in an external system |
|
|
135
|
+
| `metadata` | `json`, nullable | Custom key-value data |
|
|
136
|
+
|
|
137
|
+
Relations: `parent_category` (nullable), `category_children` (deleted with the parent), and `products`. Mercur adds `sellers`, `media_images`, and the product attribute link.
|
|
56
138
|
|
|
57
139
|
## ProductCollection
|
|
58
140
|
|
|
59
|
-
Table `product_collection`, id prefix `pcol`.
|
|
60
|
-
|
|
141
|
+
A flat group of products. Table `product_collection`, id prefix `pcol`.
|
|
142
|
+
|
|
143
|
+
| Field | Type | Description |
|
|
144
|
+
| --- | --- | --- |
|
|
145
|
+
| `id` | `text` | Primary key |
|
|
146
|
+
| `title` | `text` | Display title, searchable and translatable |
|
|
147
|
+
| `handle` | `text` | URL handle, unique |
|
|
148
|
+
| `external_id` | `text`, nullable | Id in an external system |
|
|
149
|
+
| `metadata` | `json`, nullable | Custom key-value data |
|
|
150
|
+
|
|
151
|
+
Relations: `products`. Mercur adds `media_images`.
|
|
152
|
+
|
|
153
|
+
## ProductTag
|
|
154
|
+
|
|
155
|
+
A free-form label. Table `product_tag`, id prefix `ptag`.
|
|
156
|
+
|
|
157
|
+
| Field | Type | Description |
|
|
158
|
+
| --- | --- | --- |
|
|
159
|
+
| `id` | `text` | Primary key |
|
|
160
|
+
| `value` | `text` | The label, unique |
|
|
161
|
+
| `external_id` | `text`, nullable | Id in an external system |
|
|
162
|
+
| `metadata` | `json`, nullable | Custom key-value data |
|
|
163
|
+
|
|
164
|
+
Relations: `products`.
|
|
165
|
+
|
|
166
|
+
## ProductType
|
|
167
|
+
|
|
168
|
+
A single classification per product. Table `product_type`, id prefix `ptyp`.
|
|
169
|
+
|
|
170
|
+
| Field | Type | Description |
|
|
171
|
+
| --- | --- | --- |
|
|
172
|
+
| `id` | `text` | Primary key |
|
|
173
|
+
| `value` | `text` | The type name, unique |
|
|
174
|
+
| `external_id` | `text`, nullable | Id in an external system |
|
|
175
|
+
| `metadata` | `json`, nullable | Custom key-value data |
|
|
61
176
|
|
|
62
|
-
|
|
63
|
-
`ProductTag` and `ProductType` round out the native grouping models: free-form
|
|
64
|
-
labels and a single type classification per product, respectively. They carry
|
|
65
|
-
no Mercur-specific columns.
|
|
66
|
-
</Note>
|
|
177
|
+
Relations: `products`.
|
|
@@ -1,41 +1,57 @@
|
|
|
1
1
|
---
|
|
2
|
-
title: "
|
|
2
|
+
title: "Catalog events"
|
|
3
3
|
sidebarTitle: "Events"
|
|
4
|
-
description: "
|
|
4
|
+
description: "The events Mercur's product workflows emit, their payloads, and how to subscribe to them."
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
to them to run side effects instead of polling, such as sending notifications,
|
|
9
|
-
syncing external systems, or reindexing search.
|
|
7
|
+
Mercur's product workflows emit 4 events as products are created and reviewed. The names are exported as `ProductWorkflowEvents` from `@mercurjs/core/workflows`. Subscribe to them to notify stores, sync external systems, or reindex search.
|
|
10
8
|
|
|
11
|
-
|
|
9
|
+
## Product events
|
|
10
|
+
|
|
11
|
+
Each event is emitted once per product. Payloads come from the `emitEventStep` calls in `packages/core/src/workflows/product/workflows`.
|
|
12
|
+
|
|
13
|
+
| Event | Constant | Emitted when | Payload |
|
|
14
|
+
| --- | --- | --- | --- |
|
|
15
|
+
| `product.created` | `CREATED` | `createProductsWorkflow` creates a product | `{ id }` |
|
|
16
|
+
| `product.published` | `PUBLISHED` | `confirmProductsWorkflow` publishes a product | `{ id, internal_note? }` |
|
|
17
|
+
| `product.rejected` | `REJECTED` | `rejectProductWorkflow` rejects a product | `{ id, message? }` |
|
|
18
|
+
| `product.change-requested` | `CHANGE_REQUESTED` | `requestProductChangeWorkflow` asks for a revision | `{ id, message?, actor_id? }` |
|
|
19
|
+
|
|
20
|
+
<Warning>
|
|
21
|
+
Mercur's `createProductsWorkflow` runs Medusa's `createProductsWorkflow` as a step, which also emits `product.created` with `{ id }`. Expect 2 `product.created` events per product and make handlers idempotent.
|
|
22
|
+
</Warning>
|
|
23
|
+
|
|
24
|
+
## Subscribe to an event
|
|
25
|
+
|
|
26
|
+
Write a subscriber that listens for one of the events. This one logs the rejection reason for each rejected product.
|
|
27
|
+
|
|
28
|
+
```ts title="src/subscribers/product-rejected.ts"
|
|
12
29
|
import type { SubscriberArgs, SubscriberConfig } from "@medusajs/framework"
|
|
30
|
+
import { ProductWorkflowEvents } from "@mercurjs/core/workflows"
|
|
13
31
|
|
|
14
|
-
export default async function
|
|
32
|
+
export default async function productRejected({
|
|
15
33
|
event,
|
|
16
34
|
container,
|
|
17
|
-
}: SubscriberArgs<{ id: string }>) {
|
|
18
|
-
const
|
|
19
|
-
|
|
35
|
+
}: SubscriberArgs<{ id: string; message?: string }>) {
|
|
36
|
+
const logger = container.resolve("logger")
|
|
37
|
+
const reason = event.data.message ?? "no reason given"
|
|
38
|
+
|
|
39
|
+
logger.info(`Product ${event.data.id} rejected: ${reason}`)
|
|
20
40
|
}
|
|
21
41
|
|
|
22
42
|
export const config: SubscriberConfig = {
|
|
23
|
-
event:
|
|
43
|
+
event: ProductWorkflowEvents.REJECTED,
|
|
24
44
|
}
|
|
25
45
|
```
|
|
26
46
|
|
|
27
|
-
|
|
47
|
+
Medusa calls the handler once per rejected product with the payload above.
|
|
48
|
+
|
|
49
|
+
## Events from Medusa
|
|
50
|
+
|
|
51
|
+
Medusa's own product workflows emit their events alongside Mercur's. These matter for the catalog:
|
|
52
|
+
|
|
53
|
+
- **`product.updated`**: from Medusa's `updateProductsWorkflow`, which `assignProductsToCategoryWorkflow` and the admin product update both run
|
|
54
|
+
- **`product.deleted`**: from Medusa's `deleteProductsWorkflow`
|
|
55
|
+
- **`product-variant.created`**: from `createProductVariantsWorkflow`, which Mercur's create workflow runs for `variants`
|
|
28
56
|
|
|
29
|
-
|
|
30
|
-
| --- | --- | --- |
|
|
31
|
-
| `product.created` | A master product is created | `{ id }` |
|
|
32
|
-
| `product.published` | A proposed product is approved (`→ published`) | `{ id, internal_note? }` |
|
|
33
|
-
| `product.rejected` | A proposed product is rejected | `{ id, message? }` |
|
|
34
|
-
| `product.change-requested` | A revision is requested on a proposed product | `{ id, message?, actor_id? }` |
|
|
35
|
-
|
|
36
|
-
<Note>
|
|
37
|
-
These are the marketplace lifecycle events emitted by Mercur's product
|
|
38
|
-
workflows. Medusa's Product module also emits its own native events (e.g.
|
|
39
|
-
`product.updated`, `product-variant.created`) for lower-level changes. See the
|
|
40
|
-
[Medusa events reference](https://docs.medusajs.com/resources/events-reference).
|
|
41
|
-
</Note>
|
|
57
|
+
See the [Medusa events reference](https://docs.medusajs.com/resources/references/events) for the full list. Edits that stores submit through the product edit pipeline emit `product-change.*` events, listed in the [product edit events](/platform/product-edit/reference/events).
|
|
@@ -1,44 +1,212 @@
|
|
|
1
1
|
---
|
|
2
|
-
title: "
|
|
2
|
+
title: "Catalog module links"
|
|
3
3
|
sidebarTitle: "Links"
|
|
4
|
-
description: "
|
|
4
|
+
description: "Every Mercur link on Product module models: both sides, cardinality, source file, and how to query it."
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
Mercur connects Medusa's Product module to its own modules through module links defined in `packages/core/src/links`. Read across a link with `query.graph` and the link's alias. Read-only links have no pivot table: they resolve from a foreign key column on one side, and you change them by updating that column.
|
|
8
|
+
|
|
9
|
+
Resolve `query` from the container before you run the examples:
|
|
10
|
+
|
|
11
|
+
```ts
|
|
12
|
+
import { ContainerRegistrationKeys } from "@medusajs/framework/utils"
|
|
13
|
+
|
|
14
|
+
const query = container.resolve(ContainerRegistrationKeys.QUERY)
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Product and Seller
|
|
18
|
+
|
|
19
|
+
The store allowlist. A product with no rows is open to every store.
|
|
20
|
+
|
|
21
|
+
- **Sides**: `Product` (many) and `Seller` (many)
|
|
22
|
+
- **Storage**: pivot table `product_seller` with `product_id` and `seller_id`
|
|
23
|
+
- **Alias on product**: `sellers`
|
|
24
|
+
- **File**: `product-seller-link.ts`
|
|
25
|
+
|
|
26
|
+
```ts
|
|
27
|
+
const { data } = await query.graph({
|
|
28
|
+
entity: "product",
|
|
29
|
+
fields: ["id", "sellers.id", "sellers.name"],
|
|
30
|
+
filters: { id: "prod_01JD8Q1234567" },
|
|
31
|
+
})
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Write it with `linkSellersToProductWorkflow` or the `seller_ids` input of `createProductsWorkflow`.
|
|
35
|
+
|
|
36
|
+
## ProductCategory and Seller
|
|
37
|
+
|
|
38
|
+
Links stores to categories. No shipped route filters products by it.
|
|
39
|
+
|
|
40
|
+
- **Sides**: `ProductCategory` (many) and `Seller` (many)
|
|
41
|
+
- **Storage**: pivot table generated by Medusa
|
|
42
|
+
- **Alias on category**: `sellers`
|
|
43
|
+
- **File**: `category-seller-link.ts`
|
|
44
|
+
|
|
45
|
+
```ts
|
|
46
|
+
const { data } = await query.graph({
|
|
47
|
+
entity: "product_category",
|
|
48
|
+
fields: ["id", "sellers.id"],
|
|
49
|
+
filters: { id: "pcat_01JD8Q1234567" },
|
|
50
|
+
})
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Write it with `linkSellersToProductCategoryWorkflow`.
|
|
54
|
+
|
|
55
|
+
## Offer and Product
|
|
56
|
+
|
|
57
|
+
Each offer points at the master product it sells.
|
|
58
|
+
|
|
59
|
+
- **Sides**: `Offer` (one) to `Product` (one)
|
|
60
|
+
- **Storage**: read-only, through `offer.product_id`
|
|
61
|
+
- **Alias on offer**: `product`
|
|
62
|
+
- **File**: `offer-product-link.ts`
|
|
63
|
+
|
|
64
|
+
```ts
|
|
65
|
+
const { data } = await query.graph({
|
|
66
|
+
entity: "offer",
|
|
67
|
+
fields: ["id", "product.id", "product.title"],
|
|
68
|
+
filters: { product_id: "prod_01JD8Q1234567" },
|
|
69
|
+
})
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
To list a product's offers, query `offer` filtered by `product_id`, as above.
|
|
73
|
+
|
|
74
|
+
## Offer and ProductVariant
|
|
75
|
+
|
|
76
|
+
Each offer sells exactly one variant.
|
|
77
|
+
|
|
78
|
+
- **Sides**: `Offer` (one) to `ProductVariant` (one)
|
|
79
|
+
- **Storage**: read-only, through `offer.variant_id`
|
|
80
|
+
- **Alias on offer**: `product_variant`
|
|
81
|
+
- **File**: `offer-variant-link.ts`
|
|
82
|
+
|
|
83
|
+
```ts
|
|
84
|
+
const { data } = await query.graph({
|
|
85
|
+
entity: "offer",
|
|
86
|
+
fields: ["id", "product_variant.title", "product_variant.sku"],
|
|
87
|
+
filters: { variant_id: "variant_01JD8Q1234567" },
|
|
88
|
+
})
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## Product and ProductChange
|
|
92
|
+
|
|
93
|
+
The audit and edit history of a product, including every `PRODUCT_ADD` and `STATUS_CHANGE` record.
|
|
94
|
+
|
|
95
|
+
- **Sides**: `Product` (one) to `ProductChange` (many)
|
|
96
|
+
- **Storage**: read-only, through `product_change.product_id`
|
|
97
|
+
- **Alias on product**: `changes`
|
|
98
|
+
- **File**: `product-change-link.ts`
|
|
11
99
|
|
|
12
100
|
```ts
|
|
13
|
-
const { data
|
|
101
|
+
const { data } = await query.graph({
|
|
14
102
|
entity: "product",
|
|
15
|
-
fields: ["id", "
|
|
103
|
+
fields: ["id", "changes.status", "changes.created_by"],
|
|
104
|
+
filters: { id: "prod_01JD8Q1234567" },
|
|
16
105
|
})
|
|
17
106
|
```
|
|
18
107
|
|
|
19
|
-
##
|
|
108
|
+
## Product and ProductAttributeValue
|
|
20
109
|
|
|
21
|
-
|
|
22
|
-
| --- | --- |
|
|
23
|
-
| **Seller** | Many-to-many allowlist (`product_seller`) that controls which stores may sell a master product. Categories carry the same via `category_seller`. |
|
|
24
|
-
| **Offer** | A product has many offers (`offer.product_id`, read-only). Offers are how a store sells against the master product. |
|
|
25
|
-
| **Product change** | A product has many change records (`product.changes`, read-only): the immutable submission and review audit trail. |
|
|
110
|
+
The attribute values attached to a product.
|
|
26
111
|
|
|
27
|
-
|
|
112
|
+
- **Sides**: `Product` (many) and `ProductAttributeValue` (many)
|
|
113
|
+
- **Storage**: pivot table `product_attribute_value_link`
|
|
114
|
+
- **Alias on product**: `product_attribute_values`
|
|
115
|
+
- **File**: `product-attribute-value-pivot-link.ts`
|
|
28
116
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
117
|
+
```ts
|
|
118
|
+
const { data } = await query.graph({
|
|
119
|
+
entity: "product",
|
|
120
|
+
fields: [
|
|
121
|
+
"product_attribute_values.name",
|
|
122
|
+
"product_attribute_values.attribute.handle",
|
|
123
|
+
],
|
|
124
|
+
filters: { id: "prod_01JD8Q1234567" },
|
|
125
|
+
})
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## Product and scoped ProductAttribute
|
|
129
|
+
|
|
130
|
+
Attributes created inline for one product.
|
|
131
|
+
|
|
132
|
+
- **Sides**: `Product` (one) to `ProductAttribute` (many)
|
|
133
|
+
- **Storage**: read-only, through `product_attribute.product_id`
|
|
134
|
+
- **Alias on product**: `scoped_attributes`
|
|
135
|
+
- **File**: `product-attribute-product-link.ts`
|
|
136
|
+
|
|
137
|
+
```ts
|
|
138
|
+
const { data } = await query.graph({
|
|
139
|
+
entity: "product",
|
|
140
|
+
fields: ["scoped_attributes.name", "scoped_attributes.values.name"],
|
|
141
|
+
filters: { id: "prod_01JD8Q1234567" },
|
|
142
|
+
})
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
## ProductAttribute and ProductCategory
|
|
146
|
+
|
|
147
|
+
Assigns attributes to categories.
|
|
148
|
+
|
|
149
|
+
- **Sides**: `ProductAttribute` (many) and `ProductCategory` (many)
|
|
150
|
+
- **Storage**: pivot table `product_category_attribute`
|
|
151
|
+
- **Alias on attribute**: `categories`
|
|
152
|
+
- **File**: `product-attribute-category-link.ts`
|
|
153
|
+
|
|
154
|
+
```ts
|
|
155
|
+
const { data } = await query.graph({
|
|
156
|
+
entity: "product_attribute",
|
|
157
|
+
fields: ["id", "categories.id", "categories.name"],
|
|
158
|
+
})
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
## Attribute option mirrors
|
|
32
162
|
|
|
33
|
-
|
|
163
|
+
A variant-axis attribute mirrors a native option, and each of its values mirrors an option value. Both links are one-to-one and read-only:
|
|
34
164
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
165
|
+
- **`ProductAttribute` to `ProductOption`**: through `product_attribute.product_option_id`, in `product-attribute-option-mirror-link.ts`
|
|
166
|
+
- **`ProductAttributeValue` to `ProductOptionValue`**: through `product_attribute_value.product_option_value_id`, in `product-attribute-value-option-value-mirror-link.ts`
|
|
167
|
+
|
|
168
|
+
Read the ids from the attribute side:
|
|
169
|
+
|
|
170
|
+
```ts
|
|
171
|
+
const { data } = await query.graph({
|
|
172
|
+
entity: "product_attribute",
|
|
173
|
+
fields: ["id", "product_option_id", "values.product_option_value_id"],
|
|
174
|
+
filters: { is_variant_axis: true },
|
|
175
|
+
})
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
## Product and Review
|
|
179
|
+
|
|
180
|
+
Customer reviews of a product.
|
|
181
|
+
|
|
182
|
+
- **Sides**: `Product` (one) to `Review` (many)
|
|
183
|
+
- **Storage**: pivot table generated by Medusa
|
|
184
|
+
- **Alias on product**: `reviews`
|
|
185
|
+
- **File**: `product-review.ts`
|
|
186
|
+
|
|
187
|
+
```ts
|
|
188
|
+
const { data } = await query.graph({
|
|
189
|
+
entity: "product",
|
|
190
|
+
fields: ["id", "reviews.id"],
|
|
191
|
+
filters: { id: "prod_01JD8Q1234567" },
|
|
192
|
+
})
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
## ProductCategory and ProductCollection images
|
|
196
|
+
|
|
197
|
+
Categories and collections own gallery and icon images from Mercur's media module.
|
|
198
|
+
|
|
199
|
+
- **Sides**: `ProductCategory` (one) to `MediaImage` (many), and `ProductCollection` (one) to `MediaImage` (many)
|
|
200
|
+
- **Alias**: `media_images` on both
|
|
201
|
+
- **Files**: `media-product-category-link.ts` and `media-product-collection-link.ts`
|
|
202
|
+
|
|
203
|
+
```ts
|
|
204
|
+
const { data } = await query.graph({
|
|
205
|
+
entity: "product_category",
|
|
206
|
+
fields: ["id", "media_images.url", "media_images.is_thumbnail"],
|
|
207
|
+
})
|
|
208
|
+
```
|
|
38
209
|
|
|
39
|
-
<
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
The `media_images` alias is deliberately **not** the bare `images` alias. That
|
|
43
|
-
would shadow the native `Product.images` relation and break product queries.
|
|
44
|
-
</Note>
|
|
210
|
+
<Warning>
|
|
211
|
+
Don't define a link with the alias `images` on a category or collection. Both models live in the Product module, and that alias shadows `Product.images`, which breaks every product query.
|
|
212
|
+
</Warning>
|
|
@@ -1,52 +1,74 @@
|
|
|
1
1
|
---
|
|
2
|
-
title: "
|
|
2
|
+
title: "Product module service"
|
|
3
3
|
sidebarTitle: "Service"
|
|
4
|
-
description: "Medusa's Product module service
|
|
4
|
+
description: "Resolve Medusa's Product module service with Modules.PRODUCT and call its methods for catalog records."
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
The catalog
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
The catalog uses Medusa's Product module service. Mercur adds no catalog service of its own. Resolve it with `Modules.PRODUCT` to read and write products, variants, options, categories, collections, tags, and types from a subscriber, a scheduled job, or a custom step.
|
|
8
|
+
|
|
9
|
+
## Resolve the service
|
|
10
|
+
|
|
11
|
+
The service key comes from `@medusajs/framework/utils`, not `MercurModules`:
|
|
11
12
|
|
|
12
13
|
```ts
|
|
13
14
|
import { Modules } from "@medusajs/framework/utils"
|
|
14
15
|
|
|
15
|
-
const
|
|
16
|
+
const productService = container.resolve(Modules.PRODUCT)
|
|
16
17
|
|
|
17
|
-
const [products, count] = await
|
|
18
|
-
status: "published",
|
|
19
|
-
}
|
|
18
|
+
const [products, count] = await productService.listAndCountProducts(
|
|
19
|
+
{ status: "published" },
|
|
20
|
+
{ take: 20, select: ["id", "title"] }
|
|
21
|
+
)
|
|
20
22
|
```
|
|
21
23
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
service.
|
|
28
|
-
</Note>
|
|
24
|
+
`listAndCountProducts` returns a page of products and the total count.
|
|
25
|
+
|
|
26
|
+
<Warning>
|
|
27
|
+
The service writes rows directly. It doesn't link stores, record `ProductChange` entries, or emit Mercur's `product.*` events. Use the [catalog workflows](/platform/catalog/reference/workflows) for creates and reviews, and `query.graph` to read linked data such as `sellers`.
|
|
28
|
+
</Warning>
|
|
29
29
|
|
|
30
|
-
##
|
|
30
|
+
## Product methods
|
|
31
31
|
|
|
32
|
-
|
|
32
|
+
These methods act on `Product`:
|
|
33
33
|
|
|
34
|
-
| Method |
|
|
34
|
+
| Method | Signature |
|
|
35
35
|
| --- | --- |
|
|
36
|
-
| `
|
|
37
|
-
| `
|
|
38
|
-
| `
|
|
39
|
-
| `
|
|
40
|
-
| `
|
|
41
|
-
| `
|
|
36
|
+
| `retrieveProduct` | `(productId: string, config?) => Promise<ProductDTO>` |
|
|
37
|
+
| `listProducts` | `(filters?, config?) => Promise<ProductDTO[]>` |
|
|
38
|
+
| `listAndCountProducts` | `(filters?, config?) => Promise<[ProductDTO[], number]>` |
|
|
39
|
+
| `createProducts` | `(data: CreateProductDTO \| CreateProductDTO[]) => Promise<ProductDTO \| ProductDTO[]>` |
|
|
40
|
+
| `upsertProducts` | `(data: UpsertProductDTO \| UpsertProductDTO[]) => Promise<ProductDTO \| ProductDTO[]>` |
|
|
41
|
+
| `updateProducts` | `(id: string, data) => Promise<ProductDTO>` or `(selector, data) => Promise<ProductDTO[]>` |
|
|
42
|
+
| `deleteProducts` | `(productIds: string[]) => Promise<void>` |
|
|
43
|
+
| `softDeleteProducts` | `(productIds: string[], config?) => Promise<Record<string, string[]> \| void>` |
|
|
44
|
+
| `restoreProducts` | `(productIds: string[], config?) => Promise<Record<string, string[]> \| void>` |
|
|
42
45
|
|
|
43
|
-
|
|
44
|
-
`ProductCategory`, `ProductCollection`, `ProductTag`, and `ProductType` (e.g.
|
|
45
|
-
`createProductVariants`, `listProductCategories`, `updateProductCollections`).
|
|
46
|
+
`filters` takes `FilterableProductProps`, such as `id`, `status`, `handle`, or `q`. `config` takes `select`, `relations`, `skip`, `take`, and `order`.
|
|
46
47
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
48
|
+
## Methods for the other models
|
|
49
|
+
|
|
50
|
+
The same method set exists for each model in the module, named after it:
|
|
51
|
+
|
|
52
|
+
| Model | Example methods |
|
|
53
|
+
| --- | --- |
|
|
54
|
+
| `ProductVariant` | `retrieveProductVariant`, `listProductVariants`, `createProductVariants`, `updateProductVariants` |
|
|
55
|
+
| `ProductOption` | `listProductOptions`, `createProductOptions`, `upsertProductOptions`, `deleteProductOptions` |
|
|
56
|
+
| `ProductOptionValue` | `listProductOptionValues`, `createProductOptionValues`, `updateProductOptionValues` |
|
|
57
|
+
| `ProductCategory` | `retrieveProductCategory`, `listProductCategories`, `createProductCategories` |
|
|
58
|
+
| `ProductCollection` | `retrieveProductCollection`, `listProductCollections`, `updateProductCollections` |
|
|
59
|
+
| `ProductTag` | `listProductTags`, `createProductTags`, `upsertProductTags`, `deleteProductTags` |
|
|
60
|
+
| `ProductType` | `listProductTypes`, `createProductTypes`, `upsertProductTypes`, `deleteProductTypes` |
|
|
61
|
+
|
|
62
|
+
## Option and image methods
|
|
63
|
+
|
|
64
|
+
The service also has methods that manage shared options and variant images:
|
|
65
|
+
|
|
66
|
+
| Method | Does |
|
|
67
|
+
| --- | --- |
|
|
68
|
+
| `addProductOptionToProduct` | Attaches an existing option to a product, from `{ product_id, product_option_id }` or an array |
|
|
69
|
+
| `removeProductOptionFromProduct` | Detaches an option from a product |
|
|
70
|
+
| `updateProductOptionValuesOnProduct` | Changes which values of an option a product uses |
|
|
71
|
+
| `addImageToVariant` | Links a product image to a variant |
|
|
72
|
+
| `removeImageFromVariant` | Unlinks a product image from a variant |
|
|
73
|
+
|
|
74
|
+
Mercur's attribute workflows create and attach the options behind variant-axis attributes. Prefer those workflows over the option methods, so each attribute stays in sync with the option it mirrors.
|