@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,59 +1,62 @@
|
|
|
1
1
|
---
|
|
2
|
-
title: "
|
|
2
|
+
title: "How order group totals are computed"
|
|
3
3
|
sidebarTitle: "Computed totals"
|
|
4
|
-
description: "Why
|
|
4
|
+
description: "Why seller_count and total are summed from the child orders at read time, and how child order statuses are derived."
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
and why they aren't persisted.
|
|
7
|
+
An order group never stores its own total. The `seller_count` and `total` fields are computed from the child orders each time you read the group, and the read workflows derive each child order’s payment and fulfillment status the same way. This keeps the group in step with its orders after edits, returns, and refunds, with no denormalized column to keep in sync.
|
|
9
8
|
|
|
10
|
-
## Computed
|
|
9
|
+
## Computed fields hold no value
|
|
11
10
|
|
|
12
|
-
|
|
13
|
-
value in the `order_group` row.
|
|
11
|
+
Both fields are declared with `.computed()` on the model, so the `order_group` table has no column for them:
|
|
14
12
|
|
|
15
13
|
```ts
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
seller_count: model.number().computed(),
|
|
19
|
-
total: model.bigNumber().computed(),
|
|
20
|
-
})
|
|
14
|
+
seller_count: model.number().computed(),
|
|
15
|
+
total: model.bigNumber().computed(),
|
|
21
16
|
```
|
|
22
17
|
|
|
23
|
-
`
|
|
24
|
-
`total` is the sum of those child orders' current totals. Both are resolved
|
|
25
|
-
by aggregating across the group's linked orders each time the group is read,
|
|
26
|
-
rather than being written once at creation.
|
|
18
|
+
You can’t write them through `createOrderGroups` or `updateOrderGroups`. They only exist on the records the Seller module reads back.
|
|
27
19
|
|
|
28
|
-
|
|
29
|
-
Storing these values would immediately go stale: child orders can be refunded,
|
|
30
|
-
returned, or canceled after the group is created, changing both the total and,
|
|
31
|
-
effectively, the active seller set.
|
|
32
|
-
</Note>
|
|
20
|
+
## How the Seller module fills them
|
|
33
21
|
|
|
34
|
-
|
|
22
|
+
The Seller module reads groups through a custom `OrderGroupRepository`. Its query joins each group to its child orders and sums them:
|
|
35
23
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
them up:
|
|
24
|
+
- **`seller_count`**: the number of distinct sellers linked to the group’s orders through `order_order_seller_seller`
|
|
25
|
+
- **`total`**: the sum of `current_order_total` from each order’s summary at its current version, or `0` for a group with no orders
|
|
39
26
|
|
|
40
|
-
|
|
41
|
-
- `total`: the sum of each child order's current order total
|
|
27
|
+
Every list, count, and retrieve call on order groups runs through this repository, including `query.graph` on the `order_group` entity. Deleted orders and deleted links are left out.
|
|
42
28
|
|
|
43
29
|
```ts
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
|
|
30
|
+
const sellerModuleService = container.resolve(MercurModules.SELLER)
|
|
31
|
+
|
|
32
|
+
const group = await sellerModuleService.retrieveOrderGroup(
|
|
33
|
+
"og_01JD8Q1234567"
|
|
34
|
+
)
|
|
47
35
|
```
|
|
48
36
|
|
|
37
|
+
For a group with a 47.00 order from ACME and a 40.00 order from GLOBEX, `group.seller_count` is `2` and `group.total` is `87`. The repository converts `total` to a JavaScript `number`, which matches the `OrderGroupDTO` type.
|
|
38
|
+
|
|
39
|
+
## Filters pick groups, not orders
|
|
40
|
+
|
|
41
|
+
Order-level filters such as `seller_id`, `status`, `payment_status`, and `sales_channel_id` decide which groups match. They don’t trim the sums. A group that matches `seller_id: "sel_01JD8Q1234567"` still reports the `seller_count` and `total` of every order in it.
|
|
42
|
+
|
|
49
43
|
<Tip>
|
|
50
|
-
|
|
51
|
-
child orders and currencies contribute to the aggregate.
|
|
44
|
+
To show one seller’s share of a group, read the group’s `orders` and sum the ones that belong to that seller. The list workflow’s `sellerId` input trims `orders` for you.
|
|
52
45
|
</Tip>
|
|
53
46
|
|
|
54
|
-
##
|
|
47
|
+
## Child order statuses
|
|
48
|
+
|
|
49
|
+
`getOrderGroupDetailWorkflow` and `getOrderGroupsListWorkflow` set `payment_status` and `fulfillment_status` on every child order they return. Medusa’s `Order` model doesn’t store these, so the workflows compute them from the order’s data:
|
|
50
|
+
|
|
51
|
+
| Status | Derived from | Possible values |
|
|
52
|
+
| --- | --- | --- |
|
|
53
|
+
| `payment_status` | The order’s payment collections: its own once the payment is split, the cart’s shared one before | `not_paid`, `awaiting`, `authorized`, `partially_authorized`, `captured`, `partially_captured`, `refunded`, `partially_refunded`, `canceled`, `requires_action` |
|
|
54
|
+
| `fulfillment_status` | The `packed_at`, `shipped_at`, `delivered_at`, and `canceled_at` timestamps of its fulfillments, plus unfulfilled item quantities | `not_fulfilled`, `fulfilled`, `partially_fulfilled`, `shipped`, `partially_shipped`, `delivered`, `partially_delivered`, `canceled` |
|
|
55
|
+
|
|
56
|
+
The group has no status of its own. Read the child statuses to show where each seller’s part of the purchase stands.
|
|
57
|
+
|
|
58
|
+
## Related
|
|
55
59
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
of every seller's slice without any denormalized status column.
|
|
60
|
+
- [Retrieve a group with its child order statuses](/platform/order-group/guides/retrieve-an-order-group)
|
|
61
|
+
- [Filters the repository understands](/platform/order-group/reference/service#filters-and-sorting)
|
|
62
|
+
- [The `OrderGroup` fields](/platform/order-group/reference/data-models)
|
|
@@ -1,61 +1,63 @@
|
|
|
1
1
|
---
|
|
2
|
-
title: "
|
|
2
|
+
title: "How checkout splits a cart into orders"
|
|
3
3
|
sidebarTitle: "Order splitting"
|
|
4
|
-
description: "How
|
|
4
|
+
description: "How completeCartWithSplitOrdersWorkflow turns one multi-seller cart into one order per seller under a group."
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
per-seller orders at checkout.
|
|
7
|
+
Order splitting is what happens when a shopper completes a cart that holds offers from several sellers. Mercur replaces Medusa’s single-order checkout with `completeCartWithSplitOrdersWorkflow`, which creates one order per seller and wraps them in an order group. Understanding the split tells you where each seller’s items, shipping, promotions, and payment end up.
|
|
9
8
|
|
|
10
|
-
##
|
|
9
|
+
## Items follow the offer’s seller
|
|
11
10
|
|
|
12
|
-
|
|
13
|
-
fulfills, ships, and settles independently, so a single combined order would be
|
|
14
|
-
impossible to operate. Instead, on completion the cart is divided along seller
|
|
15
|
-
boundaries. Every seller with items in the cart gets its own `Order`, and all of
|
|
16
|
-
those orders are attached to one parent `OrderGroup`.
|
|
11
|
+
The workflow groups line items by `item.offer.seller_id`. Sellers sell against a shared master catalog through offers, so the offer, not the product, ties a line item to a seller. Every seller with at least one item gets one child order.
|
|
17
12
|
|
|
18
|
-
The split
|
|
19
|
-
|
|
20
|
-
|
|
13
|
+
The split enforces two rules before it creates anything:
|
|
14
|
+
|
|
15
|
+
- **Every item has a seller**: `validateSellerCartItemsStep` throws `INVALID_DATA` when any line item has no offer with a `seller_id`
|
|
16
|
+
- **Every seller’s shipping is covered**: `validateSellerCartShippingStep` throws when items that require shipping have no matching shipping method
|
|
17
|
+
|
|
18
|
+
Each child order gets the seller’s line items plus the shipping methods whose shipping option belongs to that seller. Addresses, email, currency, locale, region, and sales channel are copied from the cart.
|
|
19
|
+
|
|
20
|
+
## Promotions stay with their seller
|
|
21
|
+
|
|
22
|
+
Promotion adjustments are split by scope. A promotion linked to a seller only keeps its adjustments on that seller’s items and shipping, and the workflow links the promotion to that seller’s child order. A marketplace promotion with no seller keeps its adjustments on every order it touched, and the workflow links it to no order.
|
|
23
|
+
|
|
24
|
+
## What the split creates
|
|
25
|
+
|
|
26
|
+
The workflow creates everything for the group in one run:
|
|
27
|
+
|
|
28
|
+
1. Create the `OrderGroup` with the cart’s `customer_id` and `cart_id`.
|
|
29
|
+
2. Create one `Order` per seller, with status `pending` unless the `setOrderStatus` hook says otherwise.
|
|
30
|
+
3. Link each order to the group, its seller, and the cart, and link new customers to the sellers they bought from.
|
|
31
|
+
4. Mirror each cart line item’s offer link onto the new order line item.
|
|
32
|
+
5. Reserve inventory for offers that manage inventory, and register promotion usage.
|
|
33
|
+
6. Set `completed_at` on the cart and emit `order.placed` and `order_group.created`.
|
|
34
|
+
7. Authorize the payment session, split a captured payment, and refresh commission lines per order.
|
|
21
35
|
|
|
22
36
|
```ts
|
|
23
37
|
import { completeCartWithSplitOrdersWorkflow } from "@mercurjs/core/workflows"
|
|
24
38
|
|
|
25
39
|
const { result } = await completeCartWithSplitOrdersWorkflow(container).run({
|
|
26
|
-
input: { cart_id: "
|
|
40
|
+
input: { cart_id: "cart_01JD8Q1234567" },
|
|
27
41
|
})
|
|
28
|
-
// result.order_group_id -> the parent group
|
|
29
42
|
```
|
|
30
43
|
|
|
31
|
-
|
|
32
|
-
Items are grouped by `item.offer.seller_id`. Sellers list against the shared
|
|
33
|
-
master catalog through **offers**, so the offer, not the product, is what
|
|
34
|
-
ties a line item to a seller.
|
|
35
|
-
</Note>
|
|
44
|
+
The run returns `{ order_group_id }`. Mercur’s cart completion route and its `processPaymentWorkflow` both run this workflow, so a webhook-driven checkout also produces a group.
|
|
36
45
|
|
|
37
|
-
##
|
|
46
|
+
## Payment stays on the cart until capture
|
|
38
47
|
|
|
39
|
-
|
|
48
|
+
The child orders share the cart’s payment collection until the payment is captured. Medusa allows a payment collection to belong to one order only, so the split doesn’t link the orders to it. Once the payment is captured, `splitCapturedCartPaymentWorkflow` gives each order a payment collection of its own for its share. It runs inside checkout for providers that capture on authorization, and from the `payment.captured` subscriber for the rest.
|
|
40
49
|
|
|
41
|
-
|
|
42
|
-
- links each order to the group (`order_group_order`) and to its seller (`order_order_seller_seller`)
|
|
43
|
-
- mirrors the line-item → offer links onto the new order lines
|
|
44
|
-
- splits payment captures proportionally across the child orders
|
|
45
|
-
- reserves offer-scoped inventory and refreshes commission lines per order
|
|
50
|
+
## One group per cart
|
|
46
51
|
|
|
47
|
-
|
|
48
|
-
orders and `order_group.created` for the group.
|
|
52
|
+
The split runs at most once per cart. The workflow takes a lock on the cart id, then looks for an existing group with the same `cart_id`. If it finds one, it skips the split and returns that group’s id, so a retried request or a duplicate webhook can’t create a second set of orders.
|
|
49
53
|
|
|
50
|
-
<
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
cart adjustments and belong to no single order.
|
|
54
|
-
</Tip>
|
|
54
|
+
<Warning>
|
|
55
|
+
Child orders don’t carry the cart’s credit lines yet. The split creates every order with an empty `credit_lines` list.
|
|
56
|
+
</Warning>
|
|
55
57
|
|
|
56
|
-
##
|
|
58
|
+
## Related
|
|
57
59
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
60
|
+
- [Run the split from your own code](/platform/order-group/guides/split-a-cart)
|
|
61
|
+
- [The workflow’s input, hooks, and events](/platform/order-group/reference/workflows#completecartwithsplitordersworkflow)
|
|
62
|
+
- [How offers tie items to sellers](/platform/offer/concepts/what-is-an-offer)
|
|
63
|
+
- [Commission lines per order](/platform/commission/concepts/order-commission-lines)
|
|
@@ -1,21 +1,16 @@
|
|
|
1
1
|
---
|
|
2
|
-
title: "
|
|
2
|
+
title: "What an order group is"
|
|
3
3
|
sidebarTitle: "The order group"
|
|
4
|
-
description: "The
|
|
4
|
+
description: "The record that ties a multi-seller purchase together: its display id, its customer, its cart, and its child orders."
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
orders created from a single cart.
|
|
7
|
+
An order group is the record that represents one checkout across several sellers. Sellers can’t share an order, because each one ships, refunds, and gets paid on their own, so Mercur gives every seller a child order and keeps the purchase together in a group. You use the group whenever you need the shopper’s view of a purchase: an order history page, a confirmation email, or an operator’s order list.
|
|
9
8
|
|
|
10
|
-
##
|
|
9
|
+
## The OrderGroup record
|
|
11
10
|
|
|
12
|
-
|
|
13
|
-
represented by the `OrderGroup` data model (table `order_group`, id prefix
|
|
14
|
-
`og`). When a cart containing offers from more than one seller is completed, the
|
|
15
|
-
cart is split into one child order per seller. All of those orders are attached
|
|
16
|
-
to a single group.
|
|
11
|
+
The group is the `OrderGroup` data model in the Seller module, stored in the `order_group` table with the id prefix `og`. It holds four stored fields and two computed ones:
|
|
17
12
|
|
|
18
|
-
```ts
|
|
13
|
+
```ts title="packages/core/src/modules/seller/models/order-group.ts"
|
|
19
14
|
const OrderGroup = model.define("order_group", {
|
|
20
15
|
id: model.id({ prefix: "og" }).primaryKey(),
|
|
21
16
|
display_id: model.autoincrement(),
|
|
@@ -26,39 +21,51 @@ const OrderGroup = model.define("order_group", {
|
|
|
26
21
|
})
|
|
27
22
|
```
|
|
28
23
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
24
|
+
Each field has one job:
|
|
25
|
+
|
|
26
|
+
- **`display_id`**: an auto-incrementing integer, so people can quote a purchase without the internal id
|
|
27
|
+
- **`customer_id`**: the customer who completed the cart, or `null` when the cart has no customer
|
|
28
|
+
- **`cart_id`**: the cart the group was created from
|
|
29
|
+
- **`seller_count` and `total`**: summed from the child orders when you read the group, covered in [Computed totals](/platform/order-group/concepts/computed-totals)
|
|
33
30
|
|
|
34
31
|
<Note>
|
|
35
|
-
|
|
36
|
-
marketplace order has exactly one parent group regardless of how many sellers
|
|
37
|
-
it spans.
|
|
32
|
+
Checkout creates a group for every completed cart, even when the cart has items from one seller. Every marketplace order therefore has exactly one parent group.
|
|
38
33
|
</Note>
|
|
39
34
|
|
|
40
35
|
## The cart link
|
|
41
36
|
|
|
42
|
-
|
|
43
|
-
is exposed as a **read-only** link to the Cart module. The cart is frozen
|
|
44
|
-
(`completed_at` is set) the moment the split runs, so the reference is a
|
|
45
|
-
historical record, not something you write through.
|
|
37
|
+
The `cart_id` field backs a read-only link to Medusa’s Cart module, so you can expand `cart.*` from a group. Checkout sets `completed_at` on the cart in the same run that creates the group, and the cart doesn’t change after that. Treat `cart_id` as a stable pointer to the basket the shopper paid for.
|
|
46
38
|
|
|
47
39
|
```ts
|
|
48
|
-
const { data:
|
|
40
|
+
const { data: [group] } = await query.graph({
|
|
49
41
|
entity: "order_group",
|
|
50
|
-
fields: ["id", "display_id", "cart.id", "
|
|
42
|
+
fields: ["id", "display_id", "cart.id", "cart.email"],
|
|
43
|
+
filters: { id: "og_01JD8Q1234567" },
|
|
51
44
|
})
|
|
52
45
|
```
|
|
53
46
|
|
|
54
|
-
|
|
55
|
-
Because the cart is immutable after checkout, the `cart_id` is safe to treat as
|
|
56
|
-
a stable audit pointer to the exact basket the shopper paid for.
|
|
57
|
-
</Tip>
|
|
47
|
+
The query loads one group and the cart it came from. Because the link is read-only, you can’t create or dismiss it with `link.create`: it always follows the `cart_id` column.
|
|
58
48
|
|
|
59
49
|
## Child orders
|
|
60
50
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
51
|
+
A group holds no line items. The items live on the child `Order` records, one per seller, connected through the `order_group_order` link table. Each child order is a regular Medusa order with its own items, shipping methods, fulfillments, returns, and refunds:
|
|
52
|
+
|
|
53
|
+
- **Seller**: each child order links to the seller who fulfills it
|
|
54
|
+
- **Line items**: each order line item links to the offer it was bought from
|
|
55
|
+
- **Cart**: each child order also links back to the completed cart
|
|
56
|
+
|
|
57
|
+
```ts
|
|
58
|
+
const { data: [group] } = await query.graph({
|
|
59
|
+
entity: "order_group",
|
|
60
|
+
fields: ["id", "orders.id", "orders.seller.name", "orders.status"],
|
|
61
|
+
filters: { id: "og_01JD8Q1234567" },
|
|
62
|
+
})
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
The query returns the group with each child order and the seller behind it. To go from an order to its group, query the `order_group_order` link entity by `order_id`.
|
|
66
|
+
|
|
67
|
+
## Related
|
|
68
|
+
|
|
69
|
+
- [How checkout splits a cart into child orders](/platform/order-group/concepts/order-splitting)
|
|
70
|
+
- [How `seller_count` and `total` are computed](/platform/order-group/concepts/computed-totals)
|
|
71
|
+
- [Every link around an order group](/platform/order-group/reference/links)
|
|
@@ -1,71 +1,111 @@
|
|
|
1
1
|
---
|
|
2
|
-
title: "List order groups"
|
|
2
|
+
title: "List and filter order groups"
|
|
3
3
|
sidebarTitle: "List order groups"
|
|
4
|
-
description: "Page through order groups and scope them to
|
|
4
|
+
description: "Page through order groups, filter them, and scope them to one seller with getOrderGroupsListWorkflow."
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
through the results, and optionally scope them to a single seller.
|
|
9
|
-
|
|
10
|
-
Mercur exposes a `getOrderGroupsListWorkflow` that returns groups with their
|
|
11
|
-
aggregated child orders and a total count for pagination.
|
|
12
|
-
|
|
13
|
-
## Run the workflow
|
|
14
|
-
|
|
15
|
-
```ts title="src/api/custom/order-groups/route.ts"
|
|
16
|
-
import type { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
|
|
17
|
-
import { getOrderGroupsListWorkflow } from "@mercurjs/core/workflows"
|
|
18
|
-
|
|
19
|
-
export async function GET(req: MedusaRequest, res: MedusaResponse) {
|
|
20
|
-
const { result } = await getOrderGroupsListWorkflow(req.scope).run({
|
|
21
|
-
input: {
|
|
22
|
-
fields: ["id", "display_id", "total", "seller_count"],
|
|
23
|
-
variables: {
|
|
24
|
-
skip: 0,
|
|
25
|
-
take: 20,
|
|
26
|
-
order: { created_at: "DESC" },
|
|
27
|
-
},
|
|
28
|
-
},
|
|
29
|
-
})
|
|
30
|
-
|
|
31
|
-
res.json({
|
|
32
|
-
order_groups: result.rows,
|
|
33
|
-
count: result.metadata?.count ?? 0,
|
|
34
|
-
})
|
|
35
|
-
}
|
|
36
|
-
```
|
|
7
|
+
List order groups from server code when you build an order history, a report, or a seller-facing view of purchases.
|
|
37
8
|
|
|
38
|
-
|
|
39
|
-
`skip`, and `take` you need to drive pagination.
|
|
9
|
+
## List groups in an API route
|
|
40
10
|
|
|
41
|
-
<
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
11
|
+
<Steps>
|
|
12
|
+
<Step title="Run the list workflow">
|
|
13
|
+
Pass the fields you want and put filters and pagination in `variables`.
|
|
14
|
+
|
|
15
|
+
```ts title="src/api/admin/purchases/route.ts"
|
|
16
|
+
import type {
|
|
17
|
+
MedusaRequest,
|
|
18
|
+
MedusaResponse,
|
|
19
|
+
} from "@medusajs/framework/http"
|
|
20
|
+
import { getOrderGroupsListWorkflow } from "@mercurjs/core/workflows"
|
|
21
|
+
|
|
22
|
+
export async function GET(req: MedusaRequest, res: MedusaResponse) {
|
|
23
|
+
const { result } = await getOrderGroupsListWorkflow(req.scope).run({
|
|
24
|
+
input: {
|
|
25
|
+
fields: ["id", "display_id", "total", "seller_count"],
|
|
26
|
+
variables: { skip: 0, take: 20, order: { display_id: "DESC" } },
|
|
27
|
+
},
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
res.json({
|
|
31
|
+
order_groups: result.rows,
|
|
32
|
+
count: result.metadata?.count ?? 0,
|
|
33
|
+
})
|
|
34
|
+
}
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
The workflow returns `{ rows, metadata }`. `metadata` carries `count`, `skip`, and `take` for pagination. Every row includes its child `orders` with `payment_status` and `fulfillment_status`.
|
|
38
|
+
</Step>
|
|
39
|
+
<Step title="Add filters">
|
|
40
|
+
Put filter keys next to the pagination keys in `variables`:
|
|
41
|
+
|
|
42
|
+
```ts
|
|
43
|
+
variables: {
|
|
44
|
+
customer_id: "cus_01JD8Q1234567",
|
|
45
|
+
payment_status: ["captured", "partially_captured"],
|
|
46
|
+
created_at: { $gte: "2026-10-01T00:00:00Z" },
|
|
47
|
+
take: 20,
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
This returns the customer’s groups from October 2026 that have at least one captured or partially captured child order.
|
|
52
|
+
</Step>
|
|
53
|
+
</Steps>
|
|
54
|
+
|
|
55
|
+
## Filters you can use
|
|
56
|
+
|
|
57
|
+
The Seller module reads groups through a custom repository that understands a fixed set of top-level keys:
|
|
58
|
+
|
|
59
|
+
- **Group filters**: `id`, `customer_id`, `cart_id`, as a string or an array
|
|
60
|
+
- **Order filters**: `seller_id`, `status`, `payment_status`, `sales_channel_id`, which match a group when at least one child order matches
|
|
61
|
+
- **Dates**: `created_at` and `updated_at`, with `$eq`, `$ne`, `$lt`, `$lte`, `$gt`, `$gte`, `$in`, `$nin`, `$like`, `$ilike`
|
|
62
|
+
- **Search**: `q`, which matches the group id or customer id, and a display id such as `#G98`, `G98`, or `98` on the group or a child order
|
|
63
|
+
|
|
64
|
+
You can sort by `display_id`, `created_at`, or `updated_at`. Without an `order`, groups come back newest first.
|
|
65
|
+
|
|
66
|
+
<Warning>
|
|
67
|
+
The repository ignores keys it doesn’t know, including `$and` and `$or`. Pass `id` as a plain string or array, not `{ $in: [...] }`. If you need two id constraints, intersect them yourself before you call the workflow.
|
|
68
|
+
</Warning>
|
|
50
69
|
|
|
51
|
-
## Scope to
|
|
70
|
+
## Scope groups to one seller
|
|
52
71
|
|
|
53
|
-
|
|
54
|
-
child orders down to that seller, so vendors only ever see their own orders
|
|
55
|
-
within a group.
|
|
72
|
+
To show a seller only their part of each purchase, filter the groups and trim their orders:
|
|
56
73
|
|
|
57
74
|
```ts
|
|
58
|
-
|
|
75
|
+
const sellerId = "sel_01JD8Q1234567"
|
|
76
|
+
|
|
77
|
+
const { result } = await getOrderGroupsListWorkflow(req.scope).run({
|
|
59
78
|
input: {
|
|
60
|
-
fields: ["id", "display_id"],
|
|
61
|
-
variables: { seller_id:
|
|
62
|
-
sellerId
|
|
79
|
+
fields: ["id", "display_id", "orders.total"],
|
|
80
|
+
variables: { seller_id: sellerId, take: 20 },
|
|
81
|
+
sellerId,
|
|
63
82
|
},
|
|
64
83
|
})
|
|
65
84
|
```
|
|
66
85
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
</
|
|
86
|
+
The two inputs do different jobs. `variables.seller_id` keeps only the groups where the seller has an order. `sellerId` removes the other sellers’ orders from each returned group. `sellerId` also accepts an array of seller ids.
|
|
87
|
+
|
|
88
|
+
<Note>
|
|
89
|
+
`seller_count` and `total` still describe the whole group when you scope it. Sum the trimmed `orders` to get the seller’s share.
|
|
90
|
+
</Note>
|
|
91
|
+
|
|
92
|
+
## List groups without child orders
|
|
93
|
+
|
|
94
|
+
When you only need group rows, call the Seller module service. It skips the child order expansion and the status work:
|
|
95
|
+
|
|
96
|
+
```ts
|
|
97
|
+
const sellerModuleService = req.scope.resolve(MercurModules.SELLER)
|
|
98
|
+
|
|
99
|
+
const [groups, count] = await sellerModuleService.listAndCountOrderGroups(
|
|
100
|
+
{ customer_id: "cus_01JD8Q1234567" },
|
|
101
|
+
{ take: 20, order: { created_at: "DESC" } }
|
|
102
|
+
)
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
`MercurModules` comes from `@mercurjs/types`. The rows still include the computed `seller_count` and `total`.
|
|
106
|
+
|
|
107
|
+
## Next steps
|
|
108
|
+
|
|
109
|
+
- [Retrieve one group with its child orders](/platform/order-group/guides/retrieve-an-order-group)
|
|
110
|
+
- [Why filters don’t change the totals](/platform/order-group/concepts/computed-totals#filters-pick-groups-not-orders)
|
|
111
|
+
- [`getOrderGroupsListWorkflow` reference](/platform/order-group/reference/workflows#getordergroupslistworkflow)
|
|
@@ -1,63 +1,114 @@
|
|
|
1
1
|
---
|
|
2
|
-
title: "Retrieve an order group"
|
|
2
|
+
title: "Retrieve an order group with its child orders"
|
|
3
3
|
sidebarTitle: "Retrieve an order group"
|
|
4
|
-
description: "Load
|
|
4
|
+
description: "Load one order group, its child orders, and their payment and fulfillment status with getOrderGroupDetailWorkflow."
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
child orders from your own server code.
|
|
7
|
+
Load one order group from server code when you build an order page, an email, or an export that needs the whole purchase.
|
|
9
8
|
|
|
10
|
-
|
|
11
|
-
its child orders, and derives each order's payment and fulfillment status. Run it
|
|
12
|
-
from any place that has access to the Medusa container.
|
|
9
|
+
## Before you begin
|
|
13
10
|
|
|
14
|
-
|
|
11
|
+
You need the following:
|
|
15
12
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
import { getOrderGroupDetailWorkflow } from "@mercurjs/core/workflows"
|
|
13
|
+
- **A completed multi-seller cart**: at least one order group in your database, for example from a storefront checkout
|
|
14
|
+
- **The group id**: an id with the `og` prefix, such as `og_01JD8Q1234567`
|
|
19
15
|
|
|
20
|
-
|
|
21
|
-
const { result: order_group } = await getOrderGroupDetailWorkflow(
|
|
22
|
-
req.scope
|
|
23
|
-
).run({
|
|
24
|
-
input: {
|
|
25
|
-
order_group_id: req.params.id,
|
|
26
|
-
fields: ["id", "display_id", "total", "seller_count", "cart_id"],
|
|
27
|
-
},
|
|
28
|
-
})
|
|
16
|
+
## Load the group in an API route
|
|
29
17
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
18
|
+
<Steps>
|
|
19
|
+
<Step title="Create the route">
|
|
20
|
+
Run `getOrderGroupDetailWorkflow` with the group id and the fields you want back.
|
|
21
|
+
|
|
22
|
+
```ts title="src/api/admin/purchases/[id]/route.ts"
|
|
23
|
+
import type {
|
|
24
|
+
MedusaRequest,
|
|
25
|
+
MedusaResponse,
|
|
26
|
+
} from "@medusajs/framework/http"
|
|
27
|
+
import { getOrderGroupDetailWorkflow } from "@mercurjs/core/workflows"
|
|
28
|
+
|
|
29
|
+
export async function GET(req: MedusaRequest, res: MedusaResponse) {
|
|
30
|
+
const { result: order_group } = await getOrderGroupDetailWorkflow(
|
|
31
|
+
req.scope
|
|
32
|
+
).run({
|
|
33
|
+
input: {
|
|
34
|
+
order_group_id: req.params.id,
|
|
35
|
+
fields: ["id", "display_id", "total", "seller_count", "cart_id"],
|
|
36
|
+
},
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
res.json({ order_group })
|
|
40
|
+
}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
The workflow adds the child order fields it needs on top of yours, so `order_group.orders` is always populated. Each order carries `payment_status` and `fulfillment_status`.
|
|
44
|
+
</Step>
|
|
45
|
+
<Step title="Add the child order fields you display">
|
|
46
|
+
Ask for extra order fields with the `orders.` prefix.
|
|
47
|
+
|
|
48
|
+
```ts
|
|
49
|
+
const fields = [
|
|
50
|
+
"id",
|
|
51
|
+
"display_id",
|
|
52
|
+
"total",
|
|
53
|
+
"orders.display_id",
|
|
54
|
+
"orders.total",
|
|
55
|
+
"orders.seller.name",
|
|
56
|
+
]
|
|
57
|
+
```
|
|
33
58
|
|
|
34
|
-
The workflow always
|
|
35
|
-
|
|
59
|
+
The workflow always returns `orders.id`, `orders.status`, `orders.currency_code`, and `orders.items.*`. Everything else on the order comes from your list.
|
|
60
|
+
</Step>
|
|
61
|
+
<Step title="Handle a missing group">
|
|
62
|
+
The workflow throws a `NOT_FOUND` error when no group has the id. Inside an API route, Medusa’s error handler turns it into a 404 response, so you don’t need to check the result.
|
|
63
|
+
</Step>
|
|
64
|
+
</Steps>
|
|
65
|
+
|
|
66
|
+
## Keep payment collections and fulfillments in the response
|
|
67
|
+
|
|
68
|
+
The workflow loads every child order’s payment collections and fulfillments to compute the statuses, then drops them from the result. To keep them, include a field that contains `payment_collections` or `fulfillments`:
|
|
69
|
+
|
|
70
|
+
```ts
|
|
71
|
+
fields: [
|
|
72
|
+
"id",
|
|
73
|
+
"orders.payment_collections.id",
|
|
74
|
+
"orders.fulfillments.id",
|
|
75
|
+
]
|
|
76
|
+
```
|
|
36
77
|
|
|
37
|
-
|
|
38
|
-
The workflow only fetches heavy relations when you ask for them: include a
|
|
39
|
-
`payment_collections` field to get per-order `payment_status`, and a
|
|
40
|
-
`fulfillments` field to get `fulfillment_status`. Otherwise those collections
|
|
41
|
-
are stripped from the response to keep it lean.
|
|
42
|
-
</Note>
|
|
78
|
+
Before the payment is captured, `payment_collections` holds the cart’s shared collection. After capture, it holds the order’s own collection. Fields that start with `orders.cart` keep the child orders’ `cart` in the result too.
|
|
43
79
|
|
|
44
80
|
## Follow the cart link
|
|
45
81
|
|
|
46
|
-
|
|
47
|
-
Expand it through the module link when you need the original basket:
|
|
82
|
+
Expand the read-only cart link to get the basket the group came from:
|
|
48
83
|
|
|
49
84
|
```ts
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
85
|
+
fields: ["id", "cart.id", "cart.email", "cart.completed_at"]
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
The cart is completed and frozen, so these values match what the shopper paid for.
|
|
89
|
+
|
|
90
|
+
## Find the group for an order
|
|
91
|
+
|
|
92
|
+
When you start from a child order id, look up the group through the `order_group_order` link entity, then run the workflow with its id:
|
|
93
|
+
|
|
94
|
+
```ts
|
|
95
|
+
const query = req.scope.resolve(ContainerRegistrationKeys.QUERY)
|
|
96
|
+
|
|
97
|
+
const { data: [link] } = await query.graph({
|
|
98
|
+
entity: "order_group_order",
|
|
99
|
+
fields: ["order_group_id"],
|
|
100
|
+
filters: { order_id: "order_01JD8Q1234568" },
|
|
55
101
|
})
|
|
56
102
|
```
|
|
57
103
|
|
|
104
|
+
`ContainerRegistrationKeys` comes from `@medusajs/framework/utils`. `link` is `undefined` when the order isn’t part of a group.
|
|
105
|
+
|
|
58
106
|
<Tip>
|
|
59
|
-
|
|
60
|
-
status aggregation for you. The raw
|
|
61
|
-
[service method](/platform/order-group/reference/service) returns only the
|
|
62
|
-
group row and its computed totals.
|
|
107
|
+
Use the workflow rather than `retrieveOrderGroup` when you need child orders. The service method returns the group row and its computed totals only, without orders or statuses.
|
|
63
108
|
</Tip>
|
|
109
|
+
|
|
110
|
+
## Next steps
|
|
111
|
+
|
|
112
|
+
- [List and filter order groups](/platform/order-group/guides/list-order-groups)
|
|
113
|
+
- [How the child order statuses are derived](/platform/order-group/concepts/computed-totals#child-order-statuses)
|
|
114
|
+
- [`getOrderGroupDetailWorkflow` reference](/platform/order-group/reference/workflows#getordergroupdetailworkflow)
|