docs: update b2b recipe (#11213)
This commit is contained in:
1 parent
a16eaad6c9
commit
88202761a5
2 files changed
+72
-586
No files matched your search
@@ -1,14 +1,19 @@
|
|||||||
import { AcademicCapSolid, UsersSolid } from "@medusajs/icons"
|
import { AcademicCapSolid, UsersSolid } from "@medusajs/icons"
|
||||||
import { BetaBadge } from "docs-ui"
|
|
||||||
|
|
||||||
export const metadata = {
|
export const metadata = {
|
||||||
title: `B2B Recipe`,
|
title: `B2B Recipe`,
|
||||||
}
|
}
|
||||||
|
|
||||||
# {metadata.title} <BetaBadge text="Soon" tooltipText="This recipe is a work in progress." />
|
# {metadata.title}
|
||||||
|
|
||||||
This recipe provides the general steps to implement a B2B store with Medusa.
|
This recipe provides the general steps to implement a B2B store with Medusa.
|
||||||
|
|
||||||
|
<Note title="Tip">
|
||||||
|
|
||||||
|
Medusa has a ready-to-use B2B starter that you install and use in [this GitHub repository](https://github.com/medusajs/b2b-starter-medusa).
|
||||||
|
|
||||||
|
</Note>
|
||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
In a B2B store, you provide different types of customers with relevant pricing, products, shopping experience, and more.
|
In a B2B store, you provide different types of customers with relevant pricing, products, shopping experience, and more.
|
||||||
@@ -29,26 +34,12 @@ Use sales channels to set product availability per channel. In this case, create
|
|||||||
|
|
||||||
You can create a sales channel through the Medusa Admin or Admin REST APIs.
|
You can create a sales channel through the Medusa Admin or Admin REST APIs.
|
||||||
|
|
||||||
{/* TODO add links */}
|
<Card
|
||||||
|
href="!api!/admin#sales-channels_postsaleschannels"
|
||||||
<CardList items={[
|
title="Create Sales Channel"
|
||||||
{
|
text="Create the sales channel using the REST APIs."
|
||||||
href: "#",
|
icon={AcademicCapSolid}
|
||||||
title: "Option 1: Use Medusa Admin",
|
/>
|
||||||
text: "Create the sales channel using the Medusa Admin.",
|
|
||||||
icon: UsersSolid,
|
|
||||||
badge: {
|
|
||||||
variant: "blue",
|
|
||||||
children: "Guide Soon"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
href: "!api!/admin#sales-channels_postsaleschannels",
|
|
||||||
title: "Option 2: Using the REST APIs",
|
|
||||||
text: "Create the sales channel using the REST APIs.",
|
|
||||||
icon: AcademicCapSolid,
|
|
||||||
},
|
|
||||||
]} />
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -63,51 +54,23 @@ Then, all products retrieved belong to the associated sales channel(s).
|
|||||||
|
|
||||||
You can create a publishable API key through the Medusa Admin or the Admin REST APIs, then associate it with the B2B sales channel. Later, you'll use this key when developing your B2B storefront.
|
You can create a publishable API key through the Medusa Admin or the Admin REST APIs, then associate it with the B2B sales channel. Later, you'll use this key when developing your B2B storefront.
|
||||||
|
|
||||||
{/* TODO add links */}
|
|
||||||
|
|
||||||
### Create Key
|
### Create Key
|
||||||
|
|
||||||
<CardList items={[
|
<Card
|
||||||
{
|
href="!api!/admin#api-keys_postapikeys"
|
||||||
href: "#",
|
title="Create Publishable API Key"
|
||||||
title: "Option 1: Use Medusa Admin",
|
text="Create the publishable API key using the REST APIs."
|
||||||
text: "Create the publishable API key using the Medusa Admin.",
|
icon={AcademicCapSolid}
|
||||||
icon: UsersSolid,
|
/>
|
||||||
badge: {
|
|
||||||
variant: "blue",
|
|
||||||
children: "Guide Soon"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
href: "!api!/admin#api-keys_postapikeys",
|
|
||||||
title: "Option 2: Using the REST APIs",
|
|
||||||
text: "Create the publishable API key using the REST APIs.",
|
|
||||||
icon: AcademicCapSolid,
|
|
||||||
},
|
|
||||||
]} />
|
|
||||||
|
|
||||||
### Associate Key with Sales Channel
|
### Associate Key with Sales Channel
|
||||||
|
|
||||||
{/* TODO add links */}
|
<Card
|
||||||
|
href="!api!/admin#api-keys_postapikeysidsaleschannels"
|
||||||
<CardList items={[
|
title="Create Publishable API Key"
|
||||||
{
|
text="Create the publishable API key using the REST APIs."
|
||||||
href: "#",
|
icon={AcademicCapSolid}
|
||||||
title: "Option 1: Use Medusa Admin",
|
/>
|
||||||
text: "Associate the publishable API key with the B2B sales channel using the Medusa Admin.",
|
|
||||||
icon: UsersSolid,
|
|
||||||
badge: {
|
|
||||||
variant: "blue",
|
|
||||||
children: "Guide Soon"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
href: "#",
|
|
||||||
title: "Option 2: Using the REST APIs",
|
|
||||||
text: "Associate the publishable API key with the B2B sales channel using the REST APIs.",
|
|
||||||
icon: AcademicCapSolid,
|
|
||||||
},
|
|
||||||
]} />
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -117,49 +80,21 @@ You can create new products or add existing ones to the B2B sales channel using
|
|||||||
|
|
||||||
### Create Products
|
### Create Products
|
||||||
|
|
||||||
{/* TODO add links */}
|
<Card
|
||||||
|
href="!api!/admin#products_postproducts"
|
||||||
<CardList items={[
|
title="Create Products"
|
||||||
{
|
text="Create products using the REST APIs."
|
||||||
href: "#",
|
icon={AcademicCapSolid}
|
||||||
title: "Using Medusa Admin",
|
/>
|
||||||
text: "Create products using the Medusa Admin.",
|
|
||||||
icon: UsersSolid,
|
|
||||||
badge: {
|
|
||||||
variant: "blue",
|
|
||||||
children: "Guide Soon"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
href: "!api!/admin#products_postproducts",
|
|
||||||
title: "Using REST APIs",
|
|
||||||
text: "Create products using the REST APIs.",
|
|
||||||
icon: AcademicCapSolid,
|
|
||||||
},
|
|
||||||
]} />
|
|
||||||
|
|
||||||
### Add Products to Sales Channel
|
### Add Products to Sales Channel
|
||||||
|
|
||||||
{/* TODO add links */}
|
<Card
|
||||||
|
href="!api!/admin#sales-channels_postsaleschannelsidproductsbatchadd"
|
||||||
<CardList items={[
|
title="Add to Sales Channel"
|
||||||
{
|
text="Add the products to the B2B sales channel using the REST APIs."
|
||||||
href: "#",
|
icon={AcademicCapSolid}
|
||||||
title: "Using Medusa Admin",
|
/>
|
||||||
text: "Add the products to the B2B sales channel using the Medusa Admin.",
|
|
||||||
icon: UsersSolid,
|
|
||||||
badge: {
|
|
||||||
variant: "blue",
|
|
||||||
children: "Guide Soon"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
href: "!api!/admin#sales-channels_postsaleschannelsidproductsbatchadd",
|
|
||||||
title: "Using REST APIs",
|
|
||||||
text: "Add the products to the B2B sales channel using the REST APIs.",
|
|
||||||
icon: AcademicCapSolid,
|
|
||||||
},
|
|
||||||
]} />
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -169,7 +104,7 @@ Use customer groups to organize your customers into different groups. Then, you
|
|||||||
|
|
||||||
This is useful in B2B sales, as you often negotiate special prices with each customer or company.
|
This is useful in B2B sales, as you often negotiate special prices with each customer or company.
|
||||||
|
|
||||||
You can create a B2B module that adds necessary data models to represent a B2B company. Then, you link that company to a customer group. Any customer belonging to that group also belongs to the company, meaning they're a B2B customer.
|
You can create a B2B module that adds necessary data models to represent a B2B company. Then, you link that company to a customer group, which is defined in the Customer Module. Any customer belonging to that group also belongs to the company, meaning they're a B2B customer.
|
||||||
|
|
||||||
<CardList items={[
|
<CardList items={[
|
||||||
{
|
{
|
||||||
@@ -186,302 +121,13 @@ You can create a B2B module that adds necessary data models to represent a B2B c
|
|||||||
},
|
},
|
||||||
]} />
|
]} />
|
||||||
|
|
||||||
{/* <Card
|
<Card
|
||||||
href="!docs!/learn/fundamentals/modules/module-relationships"
|
href="!docs!/learn/fundamentals/module-links"
|
||||||
title="Create Module Relationships"
|
title="Create Module Links"
|
||||||
text="Learn how to create a relationship between modules."
|
text="Learn how to create links between modules."
|
||||||
icon={AcademicCapSolid}
|
icon={AcademicCapSolid}
|
||||||
className="mt-1"
|
className="mt-1"
|
||||||
/> */}
|
/>
|
||||||
|
|
||||||
{/* <Details summaryContent="Example">
|
|
||||||
In this section, you'll create a B2B module that has a `Company` data model. The `Company` data model has a relationship to the `CustomerGroup` data model of the Customer Module.
|
|
||||||
|
|
||||||
Start by creating the `src/modules/b2b` directory.
|
|
||||||
|
|
||||||
Then, create the file `src/modules/b2b/models/company.ts` with the following content:
|
|
||||||
|
|
||||||
```ts title="src/modules/b2b/models/company.ts" highlights={[["8", "", "The property will be used to create a relationship to customer groups."]]}
|
|
||||||
import { model } from "@medusajs/framework/utils"
|
|
||||||
|
|
||||||
const Company = model.define("company", {
|
|
||||||
id: model.id().primaryKey(),
|
|
||||||
name: model.text(),
|
|
||||||
city: model.text(),
|
|
||||||
country_code: model.text(),
|
|
||||||
customer_group_id: model.text().nullable(),
|
|
||||||
})
|
|
||||||
|
|
||||||
export default Company
|
|
||||||
```
|
|
||||||
|
|
||||||
This creates a `Company` data model with some relevant properties. Most importantly, it has a `customer_group_id` property. It'll later be used when creating the relationship to the `CustomerGroup` data model in the Customer Module.
|
|
||||||
|
|
||||||
Next, create the migration in the file `src/modules/b2b/migrations/Migration20240516081502.ts` with the following content:
|
|
||||||
|
|
||||||
```ts title="src/modules/b2b/migrations/Migration20240516081502.ts"
|
|
||||||
import { Migration } from "@mikro-orm/migrations"
|
|
||||||
|
|
||||||
export class Migration20240516081502 extends Migration {
|
|
||||||
|
|
||||||
async up(): Promise<void> {
|
|
||||||
this.addSql("create table if not exists \"company\" (\"id\" text not null, \"name\" text not null, \"city\" text not null, \"country_code\" text not null, \"customer_group_id\" text not null, constraint \"company_pkey\" primary key (\"id\"));")
|
|
||||||
}
|
|
||||||
|
|
||||||
async down(): Promise<void> {
|
|
||||||
this.addSql("drop table if exists \"company\" cascade;")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
You'll run the migration to reflect the data model in the database after finishing the module definition.
|
|
||||||
|
|
||||||
Then, create the module's main service at `src/modules/b2b/service.ts` with the following content:
|
|
||||||
|
|
||||||
```ts title="src/modules/b2b/service.ts"
|
|
||||||
import { MedusaService } from "@medusajs/framework/utils"
|
|
||||||
import Company from "./models/company"
|
|
||||||
|
|
||||||
class B2bModuleService extends MedusaService({
|
|
||||||
Company,
|
|
||||||
}){
|
|
||||||
// TODO add custom methods
|
|
||||||
}
|
|
||||||
|
|
||||||
export default B2bModuleService
|
|
||||||
```
|
|
||||||
|
|
||||||
This creates a `B2bModuleService` that extends the service factory, which generates data-management functionalities for the `Company` data model.
|
|
||||||
|
|
||||||
Next, create the module definition at `src/modules/b2b/index.ts` with the following content:
|
|
||||||
|
|
||||||
```ts title="src/modules/b2b/index.ts"
|
|
||||||
import B2bModuleService from "./service"
|
|
||||||
import { Module } from "@medusajs/framework/utils"
|
|
||||||
|
|
||||||
export default Module("b2b", {
|
|
||||||
service: B2bModuleService,
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
Finally, add the module to the `modules` object in `medusa-config.js`:
|
|
||||||
|
|
||||||
```js title="medusa-config.js"
|
|
||||||
module.exports = defineConfig({
|
|
||||||
// ...
|
|
||||||
modules: {
|
|
||||||
b2bModuleService: {
|
|
||||||
resolve: "./modules/b2b",
|
|
||||||
definition: {
|
|
||||||
isQueryable: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
You can now run migrations with the following commands:
|
|
||||||
|
|
||||||
```bash npm2yarn
|
|
||||||
npx medusa db:migrate
|
|
||||||
```
|
|
||||||
|
|
||||||
### Add Create Company API Route
|
|
||||||
|
|
||||||
To test out using the B2B Module, you'll add an API route to create a company.
|
|
||||||
|
|
||||||
Start by creating the file `src/types/b2b/index.ts` with some helper types:
|
|
||||||
|
|
||||||
```ts title="src/types/b2b/index.ts"
|
|
||||||
import { CustomerGroupDTO } from "@medusajs/framework/types"
|
|
||||||
|
|
||||||
export type CompanyDTO = {
|
|
||||||
id: string
|
|
||||||
name: string
|
|
||||||
city: string
|
|
||||||
country_code: string
|
|
||||||
customer_group_id?: string
|
|
||||||
customer_group?: CustomerGroupDTO
|
|
||||||
}
|
|
||||||
|
|
||||||
export type CreateCompanyDTO = {
|
|
||||||
name: string
|
|
||||||
city: string
|
|
||||||
country_code: string
|
|
||||||
customer_group_id?: string
|
|
||||||
}
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
Then, create the file `src/workflows/create-company.ts` with the following content:
|
|
||||||
|
|
||||||
export const workflowHighlights = [
|
|
||||||
["23", "tryToCreateCustomerGroupStep", "This step creates the customer group if its data is passed in the `customer_group` property."],
|
|
||||||
["36", "createCustomerGroupsWorkflow", "Use the `createCustomerGroupsWorkflow` defined by Medusa to create the customer group."],
|
|
||||||
["44", "", "Set the ID of the new customer group in the `customer_group_id` property so that it's added to the created company."],
|
|
||||||
["54", "createCompanyStep", "This step creates the company."],
|
|
||||||
]
|
|
||||||
|
|
||||||
```ts title="src/workflows/create-company.ts" highlights={workflowHighlights} collapsibleLines="1-12" expandButtonLabel="Show Imports"
|
|
||||||
import {
|
|
||||||
StepResponse,
|
|
||||||
createStep,
|
|
||||||
createWorkflow,
|
|
||||||
} from "@medusajs/framework/workflows-sdk"
|
|
||||||
import {
|
|
||||||
createCustomerGroupsWorkflow,
|
|
||||||
} from "@medusajs/medusa/core-flows"
|
|
||||||
import { CreateCustomerGroupDTO } from "@medusajs/framework/types"
|
|
||||||
import { CompanyDTO, CreateCompanyDTO } from "../types/b2b"
|
|
||||||
import B2bModuleService from "../modules/b2b/service"
|
|
||||||
|
|
||||||
export type CreateCompanyWorkflowInput = CreateCompanyDTO & {
|
|
||||||
customer_group?: CreateCustomerGroupDTO
|
|
||||||
}
|
|
||||||
|
|
||||||
type CreateCompanyWorkflowOutput = {
|
|
||||||
company: CompanyDTO
|
|
||||||
}
|
|
||||||
|
|
||||||
type CreateCustomerGroupStepInput = CreateCompanyWorkflowInput
|
|
||||||
|
|
||||||
const tryToCreateCustomerGroupStep = createStep(
|
|
||||||
"try-to-create-customer-group-step",
|
|
||||||
async (
|
|
||||||
{
|
|
||||||
customer_group,
|
|
||||||
...company
|
|
||||||
}: CreateCustomerGroupStepInput,
|
|
||||||
{ container }) => {
|
|
||||||
if (!customer_group) {
|
|
||||||
return new StepResponse({ company })
|
|
||||||
}
|
|
||||||
|
|
||||||
// create customer group
|
|
||||||
const { result } = await createCustomerGroupsWorkflow(
|
|
||||||
container
|
|
||||||
).run({
|
|
||||||
input: {
|
|
||||||
customersData: [customer_group],
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
company.customer_group_id = result[0].id
|
|
||||||
|
|
||||||
return new StepResponse({ company })
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
export type CreateCompanyStep = {
|
|
||||||
companyData: CreateCompanyDTO
|
|
||||||
}
|
|
||||||
|
|
||||||
const createCompanyStep = createStep(
|
|
||||||
"create-company-step",
|
|
||||||
async (
|
|
||||||
{ companyData }: CreateCompanyStep,
|
|
||||||
{ container }) => {
|
|
||||||
const b2bModuleService: B2bModuleService = container
|
|
||||||
.resolve(
|
|
||||||
"b2bModuleService"
|
|
||||||
)
|
|
||||||
|
|
||||||
const company = await b2bModuleService.createCompany(
|
|
||||||
companyData
|
|
||||||
)
|
|
||||||
|
|
||||||
return new StepResponse({ company })
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
export const createCompanyWorkflow = createWorkflow<
|
|
||||||
CreateCompanyWorkflowInput,
|
|
||||||
CreateCompanyWorkflowOutput
|
|
||||||
>(
|
|
||||||
"create-company",
|
|
||||||
function (input) {
|
|
||||||
const {
|
|
||||||
company: companyData,
|
|
||||||
} = tryToCreateCustomerGroupStep(input)
|
|
||||||
|
|
||||||
const company = createCompanyStep({ companyData })
|
|
||||||
|
|
||||||
return company
|
|
||||||
}
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
You create a workflow with two steps:
|
|
||||||
|
|
||||||
1. The first one tries to create a customer group if its data is provided in the `customer_group` property and sets its value in the `customer_group_id` property.
|
|
||||||
2. The second one creates the company.
|
|
||||||
|
|
||||||
Finally, create the file `src/api/admin/b2b/company/route.ts` with the following content:
|
|
||||||
|
|
||||||
```ts title="src/api/admin/b2b/company/route.ts" collapsibleLines="1-9" expandButtonLabel="Show Imports"
|
|
||||||
import type {
|
|
||||||
MedusaRequest,
|
|
||||||
MedusaResponse,
|
|
||||||
} from "@medusajs/medusa"
|
|
||||||
import {
|
|
||||||
CreateCompanyWorkflowInput,
|
|
||||||
createCompanyWorkflow,
|
|
||||||
} from "../../../../workflows/create-company"
|
|
||||||
|
|
||||||
type CreateCompanyReq = CreateCompanyWorkflowInput
|
|
||||||
|
|
||||||
export async function POST(
|
|
||||||
req: MedusaRequest<CreateCompanyReq>,
|
|
||||||
res: MedusaResponse
|
|
||||||
) {
|
|
||||||
const { result } = await createCompanyWorkflow(req.scope)
|
|
||||||
.run({
|
|
||||||
input: req.body,
|
|
||||||
})
|
|
||||||
|
|
||||||
res.json({
|
|
||||||
company: result.company,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
The API route uses the workflow to create the company. It passes the request body as the workflow's input.
|
|
||||||
|
|
||||||
### Test API Route
|
|
||||||
|
|
||||||
To test the API route, start the Medusa application:
|
|
||||||
|
|
||||||
```bash npm2yarn
|
|
||||||
npm run dev
|
|
||||||
```
|
|
||||||
|
|
||||||
Next, make sure you authenticate as an admin user as explained in [this Authentication guide](!api!/admin#authentication).
|
|
||||||
|
|
||||||
Then, send a `POST` request to the `/admin/b2b/company` API route:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
curl -X POST 'localhost:9000/admin/b2b/company' \
|
|
||||||
--header 'Content-Type: application/json' \
|
|
||||||
--header 'Authorization: Bearer {jwt_token}' \
|
|
||||||
--data '{
|
|
||||||
"name": "Acme",
|
|
||||||
"city": "London",
|
|
||||||
"country_code": "gb",
|
|
||||||
"customer_group": {
|
|
||||||
"name": "B2B"
|
|
||||||
}
|
|
||||||
}'
|
|
||||||
```
|
|
||||||
|
|
||||||
This creates a company and its associated customer group.
|
|
||||||
|
|
||||||
<Note title="Tip">
|
|
||||||
|
|
||||||
You can alternatively pass a `customer_group_id` to use an existing customer group.
|
|
||||||
|
|
||||||
</Note>
|
|
||||||
|
|
||||||
</Details> */}
|
|
||||||
|
|
||||||
## Add B2B Customers
|
## Add B2B Customers
|
||||||
|
|
||||||
@@ -491,49 +137,21 @@ You can do that through the Medusa Admin or Admin REST APIs.
|
|||||||
|
|
||||||
### Create Customers
|
### Create Customers
|
||||||
|
|
||||||
{/* TODO add links */}
|
<Card
|
||||||
|
href="!api!/admin#customers_postcustomers"
|
||||||
<CardList items={[
|
title="Create Customers"
|
||||||
{
|
text="Create the customers using the REST APIs."
|
||||||
href: "#",
|
icon={AcademicCapSolid}
|
||||||
title: "Option 1: Use Medusa Admin",
|
/>
|
||||||
text: "Create the customers using the Medusa Admin.",
|
|
||||||
icon: UsersSolid,
|
|
||||||
badge: {
|
|
||||||
variant: "blue",
|
|
||||||
children: "Guide Soon"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
href: "!api!/admin#customers_postcustomers",
|
|
||||||
title: "Option 2: Using the REST APIs",
|
|
||||||
text: "Create the customers using the REST APIs.",
|
|
||||||
icon: AcademicCapSolid,
|
|
||||||
},
|
|
||||||
]} />
|
|
||||||
|
|
||||||
### Assign Customers to Groups
|
### Assign Customers to Groups
|
||||||
|
|
||||||
{/* TODO add links */}
|
<Card
|
||||||
|
href="!api!/admin#customer-groups_postcustomergroupsidcustomersbatch"
|
||||||
<CardList items={[
|
title="Assign Customers to Groups"
|
||||||
{
|
text="Assign the customers to the B2B customer group using the REST APIs."
|
||||||
href: "#",
|
icon={AcademicCapSolid}
|
||||||
title: "Option 1: Use Medusa Admin",
|
/>
|
||||||
text: "Assign the customers to the B2B customer group using the Medusa Admin.",
|
|
||||||
icon: UsersSolid,
|
|
||||||
badge: {
|
|
||||||
variant: "blue",
|
|
||||||
children: "Guide Soon"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
href: "!api!/admin#customer-groups_postcustomergroupsidcustomersbatch",
|
|
||||||
title: "Option 2: Using the REST APIs",
|
|
||||||
text: "Assign the customers to the B2B customer group using the REST APIs.",
|
|
||||||
icon: AcademicCapSolid,
|
|
||||||
},
|
|
||||||
]} />
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -543,26 +161,12 @@ Use price lists to set different prices for each B2B customer group, among other
|
|||||||
|
|
||||||
You can create a price list using the Medusa Admin or the Admin REST APIs. Make sure to set the B2B customer group as a condition.
|
You can create a price list using the Medusa Admin or the Admin REST APIs. Make sure to set the B2B customer group as a condition.
|
||||||
|
|
||||||
{/* TODO add links */}
|
<Card
|
||||||
|
href="!api!/admin#price-lists_postpricelists"
|
||||||
<CardList items={[
|
title="Create Price List"
|
||||||
{
|
text="Create the price list using the REST APIs."
|
||||||
href: "#",
|
icon={AcademicCapSolid}
|
||||||
title: "Using Medusa Admin",
|
/>
|
||||||
text: "Create the price list using the Medusa Admin.",
|
|
||||||
icon: UsersSolid,
|
|
||||||
badge: {
|
|
||||||
variant: "blue",
|
|
||||||
children: "Guide Soon"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
href: "!api!/admin#price-lists_postpricelists",
|
|
||||||
title: "Using REST APIs",
|
|
||||||
text: "Create the price list using the REST APIs.",
|
|
||||||
icon: AcademicCapSolid,
|
|
||||||
},
|
|
||||||
]} />
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -594,126 +198,6 @@ The API route can check if the customer has any group with an associated company
|
|||||||
icon={AcademicCapSolid}
|
icon={AcademicCapSolid}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* <Details summaryContent="Example">
|
|
||||||
|
|
||||||
For example, create the API route `src/api/store/b2b/check-customer/route.ts` with the following content:
|
|
||||||
|
|
||||||
export const checkCustomerHighlights = [
|
|
||||||
["19", "retrieveCustomer", "Retrieve the customer along with its groups."],
|
|
||||||
["26", "listCompanies", "List the companies that have a customer group ID matching any of the customer's group IDs."],
|
|
||||||
["31", "", "Return whether there are any companies associated with the customer's groups."]
|
|
||||||
]
|
|
||||||
|
|
||||||
```ts title="src/api/store/b2b/check-customer/route.ts" highlights={checkCustomerHighlights} collapsibleLines="1-5" expandButtonLabel="Show Imports"
|
|
||||||
import type {
|
|
||||||
AuthenticatedMedusaRequest,
|
|
||||||
MedusaResponse,
|
|
||||||
} from "@medusajs/medusa"
|
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
|
||||||
import { ICustomerModuleService } from "@medusajs/framework/types"
|
|
||||||
import B2bModuleService from "../../../../modules/b2b/service"
|
|
||||||
|
|
||||||
export async function GET(
|
|
||||||
req: AuthenticatedMedusaRequest,
|
|
||||||
res: MedusaResponse
|
|
||||||
) {
|
|
||||||
const customerModuleService: ICustomerModuleService = req
|
|
||||||
.scope.resolve(Modules.CUSTOMER)
|
|
||||||
const b2bModuleService: B2bModuleService = req.scope.resolve(
|
|
||||||
"b2bModuleService"
|
|
||||||
)
|
|
||||||
|
|
||||||
const customer = await customerModuleService.retrieveCustomer(
|
|
||||||
req.auth_context.actor_id,
|
|
||||||
{
|
|
||||||
relations: ["groups"],
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
const companies = await b2bModuleService.listCompanies({
|
|
||||||
customer_group_id: customer.groups.map((group) => group.id),
|
|
||||||
})
|
|
||||||
|
|
||||||
res.json({
|
|
||||||
is_b2b: companies.length > 0,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
This creates a `GET` API Route at `/store/b2b/check-customer` that:
|
|
||||||
|
|
||||||
1. Retrieves the customer along with its groups using the Customer Module's main service.
|
|
||||||
2. Lists the companies that have a customer group ID matching any of the customer's group IDs.
|
|
||||||
3. Return an `is_b2b` field whose value is `true` if there are any companies associated with the customer's groups.
|
|
||||||
|
|
||||||
Before using the API route, create the file `src/api/middlewares.ts` with the following content:
|
|
||||||
|
|
||||||
```ts title="src/api/middlewares.ts"
|
|
||||||
import {
|
|
||||||
MiddlewaresConfig,
|
|
||||||
authenticate,
|
|
||||||
} from "@medusajs/medusa"
|
|
||||||
|
|
||||||
export const config: MiddlewaresConfig = {
|
|
||||||
routes: [
|
|
||||||
{
|
|
||||||
matcher: "/store/b2b*",
|
|
||||||
middlewares: [
|
|
||||||
authenticate("store", ["bearer", "session"]),
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
This ensures that only logged-in customers can use the API route.
|
|
||||||
|
|
||||||
### Test API Route
|
|
||||||
|
|
||||||
To test out the API route:
|
|
||||||
|
|
||||||
1. Start the Medusa application.
|
|
||||||
2. Obtain an authentication JWT token for a new customer. Do that by sending a `POST` request to the `/auth/store/emailpass` API Route:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
curl -X POST 'http://localhost:9000/auth/store/emailpass' \
|
|
||||||
-H 'Content-Type: application/json' \
|
|
||||||
--data-raw '{
|
|
||||||
"email": "test@medusajs.com",
|
|
||||||
"password": "supersecret"
|
|
||||||
}'
|
|
||||||
```
|
|
||||||
|
|
||||||
3. Send a `POST` request to the `/store/customers` API route that registers the customer. Make sure to pass the authentication JWT token from the previous token in the header:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
curl -X POST 'http://localhost:9000/store/customers' \
|
|
||||||
-H 'Content-Type: application/json' \
|
|
||||||
-H 'Authorization: Bearer {jwt_token}' \
|
|
||||||
--data-raw '{
|
|
||||||
"email": "test@medusajs.com",
|
|
||||||
"password": "supersecret"
|
|
||||||
}'
|
|
||||||
```
|
|
||||||
|
|
||||||
4. Add the customer to the B2B group as explained in a [previous section](#add-b2b-customers).
|
|
||||||
5. Send a `GET` request to the `/store/b2b/check-customer` API route you created in this section:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
curl 'http://localhost:9000/store/b2b/check-customer' \
|
|
||||||
--header 'Authorization: Bearer {jwt_token}'
|
|
||||||
```
|
|
||||||
|
|
||||||
You'll receive a JSON response as the following:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"is_b2b": true
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</Details> */}
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Customize Admin
|
## Customize Admin
|
||||||
@@ -726,23 +210,25 @@ The Medusa Admin plugin can be extended to add widgets, new pages, and setting p
|
|||||||
{
|
{
|
||||||
href: "!docs!/learn/fundamentals/admin/widgets",
|
href: "!docs!/learn/fundamentals/admin/widgets",
|
||||||
title: "Create Admin Widget",
|
title: "Create Admin Widget",
|
||||||
text: "Learn how to add widgets into existing admin pages.",
|
text: "Add widgets into existing admin pages.",
|
||||||
icon: AcademicCapSolid,
|
icon: AcademicCapSolid,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
href: "!docs!/learn/fundamentals/admin/ui-routes",
|
href: "!docs!/learn/fundamentals/admin/ui-routes",
|
||||||
title: "Create Admin UI Routes",
|
title: "Create Admin UI Routes",
|
||||||
text: "Learn how to add new pages to your Medusa Admin.",
|
text: "Add new pages to your Medusa Admin.",
|
||||||
icon: AcademicCapSolid,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
href: "!docs!/learn/fundamentals/admin/ui-routes#create-settings-page",
|
|
||||||
title: "Create Admin Setting Page",
|
|
||||||
text: "Learn how to add new page to the Medusa Admin settings.",
|
|
||||||
icon: AcademicCapSolid,
|
icon: AcademicCapSolid,
|
||||||
},
|
},
|
||||||
]} />
|
]} />
|
||||||
|
|
||||||
|
<Card
|
||||||
|
href="!docs!/learn/fundamentals/admin/ui-routes#create-settings-page"
|
||||||
|
title="Create Admin Setting Page"
|
||||||
|
text="Add new page to the Medusa Admin settings."
|
||||||
|
icon={AcademicCapSolid}
|
||||||
|
className="mt-1"
|
||||||
|
/>
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Customize Storefront
|
## Customize Storefront
|
||||||
|
|||||||
@@ -110,7 +110,7 @@ export const generatedEditDates = {
|
|||||||
"app/medusa-container-resources/page.mdx": "2025-01-06T11:19:35.623Z",
|
"app/medusa-container-resources/page.mdx": "2025-01-06T11:19:35.623Z",
|
||||||
"app/medusa-workflows-reference/page.mdx": "2025-01-20T08:21:29.962Z",
|
"app/medusa-workflows-reference/page.mdx": "2025-01-20T08:21:29.962Z",
|
||||||
"app/nextjs-starter/page.mdx": "2025-01-06T12:19:31.143Z",
|
"app/nextjs-starter/page.mdx": "2025-01-06T12:19:31.143Z",
|
||||||
"app/recipes/b2b/page.mdx": "2024-10-03T13:07:44.153Z",
|
"app/recipes/b2b/page.mdx": "2025-01-29T11:35:23.247Z",
|
||||||
"app/recipes/commerce-automation/page.mdx": "2024-10-16T08:52:01.585Z",
|
"app/recipes/commerce-automation/page.mdx": "2024-10-16T08:52:01.585Z",
|
||||||
"app/recipes/digital-products/examples/standard/page.mdx": "2025-01-13T11:31:35.362Z",
|
"app/recipes/digital-products/examples/standard/page.mdx": "2025-01-13T11:31:35.362Z",
|
||||||
"app/recipes/digital-products/page.mdx": "2025-01-06T11:19:35.623Z",
|
"app/recipes/digital-products/page.mdx": "2025-01-06T11:19:35.623Z",
|
||||||
|
|||||||
Reference in new issue
Block a user