docs: updates to use DML and other changes (#7834)
- Change existing data model guides and add new ones for DML - Change module's docs around service factory + remove guides that are now necessary - Hide/remove all mentions of module relationships, or label them as coming soon. - Change all data model creation snippets to use DML - use `property` instead of `field` when referring to a data model's properties. - Fix all snippets in commerce module guides to use new method suffix (no more main model methods) - Rework recipes, removing/hiding a lot of sections as a lot of recipes are incomplete with the current state of DML. ### Other changes - Highlight fixes in some guides - Remove feature flags guide - Fix code block styles when there are no line numbers. ### Upcoming changes in other PRs - Re-generate commerce module references (for the updates in the method names) - Ensure that the data model references are generated correctly for models using DML. - (probably at a very later point) revisit recipes
This commit is contained in:
@@ -8,6 +8,12 @@ export const metadata = {
|
||||
|
||||
This recipe provides the general steps to implement a B2B store with Medusa.
|
||||
|
||||
<Note type="soon" title="In Development">
|
||||
|
||||
This recipe is a work in progress, as some features are not ready yet in Medusa V2.
|
||||
|
||||
</Note>
|
||||
|
||||
## Overview
|
||||
|
||||
In a B2B store, you provide different types of customers with relevant pricing, products, shopping experience, and more.
|
||||
@@ -180,6 +186,12 @@ This is useful in B2B sales, as you often negotiate special prices with each cus
|
||||
|
||||
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.
|
||||
|
||||
<Note type="soon">
|
||||
|
||||
Module Relationships is coming soon.
|
||||
|
||||
</Note>
|
||||
|
||||
<CardList items={[
|
||||
{
|
||||
href: "!docs!/basics/modules-and-services",
|
||||
@@ -197,50 +209,37 @@ You can create a B2B module that adds necessary data models to represent a B2B c
|
||||
},
|
||||
]} />
|
||||
|
||||
<Card
|
||||
{/* <Card
|
||||
href="!docs!/advanced-development/modules/module-relationships"
|
||||
title="Create Module Relationships"
|
||||
text="Learn how to create a relationship between modules."
|
||||
startIcon={<AcademicCapSolid />}
|
||||
showLinkIcon={false}
|
||||
className="mt-1"
|
||||
/>
|
||||
/> */}
|
||||
|
||||
<Details summaryContent="Example">
|
||||
{/* <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={[["23", "", "Field will be used to create a relationship to customer groups."]]} collapsibleLines="1-7" expandButtonLabel="Show Imports"
|
||||
import { BaseEntity } from "@medusajs/utils"
|
||||
import {
|
||||
Entity,
|
||||
PrimaryKey,
|
||||
Property,
|
||||
} from "@mikro-orm/core"
|
||||
```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/utils"
|
||||
|
||||
@Entity()
|
||||
export class Company extends BaseEntity {
|
||||
@PrimaryKey({ columnType: "text" })
|
||||
id!: string
|
||||
const Company = model.define("company", {
|
||||
id: model.id(),
|
||||
name: model.text(),
|
||||
city: model.text(),
|
||||
country_code: model.text(),
|
||||
customer_group_id: model.text().nullable(),
|
||||
})
|
||||
|
||||
@Property({ columnType: "text" })
|
||||
name: string
|
||||
|
||||
@Property({ columnType: "text" })
|
||||
city: string
|
||||
|
||||
@Property({ columnType: "text" })
|
||||
country_code: string
|
||||
|
||||
@Property({ columnType: "text" })
|
||||
customer_group_id?: string
|
||||
}
|
||||
export default Company
|
||||
```
|
||||
|
||||
This creates a `Company` data model with some relevant fields. Most importantly, it has a `customer_group_id` field. It'll later be used when creating the relationship to the `CustomerGroup` data model in the Customer Module.
|
||||
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:
|
||||
|
||||
@@ -261,106 +260,22 @@ You can create a B2B module that adds necessary data models to represent a B2B c
|
||||
|
||||
You'll run the migration to reflect the data model in the database after finishing the module definition.
|
||||
|
||||
Before creating the module's main service, create the file `src/types/b2b/index.ts` with some helper types:
|
||||
|
||||
```ts title="src/types/b2b/index.ts"
|
||||
import { CustomerGroupDTO } from "@medusajs/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
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
You can now create the module's main service at `src/modules/b2b/service.ts` with the following content:
|
||||
|
||||
export const mainServiceHighlights = [
|
||||
["42", "relationships", "Implement the relationship to the `CustomerGroup` data model in the Customer Module."],
|
||||
["56", "create", "Implement a create method to create a company."]
|
||||
]
|
||||
Then, create the module's main service at `src/modules/b2b/service.ts` with the following content:
|
||||
|
||||
```ts title="src/modules/b2b/service.ts" highlights={mainServiceHighlights} collapsibleLines="1-6" expandButtonLabel="Show Imports"
|
||||
import { ModulesSdkUtils } from "@medusajs/utils"
|
||||
import { ModuleJoinerConfig, ModulesSdkTypes } from "@medusajs/types"
|
||||
import { Modules } from "@medusajs/modules-sdk"
|
||||
import { Company } from "./models/company"
|
||||
import { CompanyDTO, CreateCompanyDTO } from "../../types/b2b"
|
||||
|
||||
type InjectedDependencies = {
|
||||
companyService: ModulesSdkTypes.InternalModuleService<any>
|
||||
```ts title="src/modules/b2b/service.ts"
|
||||
import { MedusaService } from "@medusajs/utils"
|
||||
import Company from "./models/company"
|
||||
|
||||
class B2bModuleService extends MedusaService({
|
||||
Company,
|
||||
}){
|
||||
// TODO add custom methods
|
||||
}
|
||||
|
||||
type AllModelsDTO = {
|
||||
Company: {
|
||||
dto: CompanyDTO
|
||||
}
|
||||
}
|
||||
|
||||
class B2bModuleService extends ModulesSdkUtils
|
||||
.abstractModuleServiceFactory<
|
||||
InjectedDependencies,
|
||||
CompanyDTO,
|
||||
AllModelsDTO
|
||||
>(Company, []) {
|
||||
companyService_: ModulesSdkTypes.InternalModuleService<Company>
|
||||
|
||||
constructor({ companyService }: InjectedDependencies) {
|
||||
// @ts-ignore
|
||||
super(...arguments)
|
||||
this.companyService_ = companyService
|
||||
}
|
||||
|
||||
__joinerConfig(): ModuleJoinerConfig {
|
||||
return {
|
||||
serviceName: "b2bModuleService",
|
||||
alias: [
|
||||
{
|
||||
name: ["company"],
|
||||
args: {
|
||||
entity: Company.name,
|
||||
},
|
||||
},
|
||||
],
|
||||
relationships: [
|
||||
{
|
||||
serviceName: Modules.CUSTOMER,
|
||||
alias: "customer_group",
|
||||
primaryKey: "id",
|
||||
foreignKey: "customer_group_id",
|
||||
args: {
|
||||
methodSuffix: "CustomerGroups",
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
async create(data: CreateCompanyDTO): Promise<CompanyDTO> {
|
||||
const company = this.companyService_.create(data)
|
||||
|
||||
return company
|
||||
}
|
||||
}
|
||||
|
||||
export default B2bModuleService
|
||||
```
|
||||
|
||||
This creates a `B2bModuleService` that extends the service factory and implements:
|
||||
|
||||
- The module's relationship to the `CustomerGroup` data model in the Customer Module within the `__joinerConfig` method.
|
||||
- A `create` method to create a company.
|
||||
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:
|
||||
|
||||
@@ -398,7 +313,30 @@ export const mainServiceHighlights = [
|
||||
|
||||
To test out using the B2B Module, you'll add an API route to create a company.
|
||||
|
||||
Start by creating the file `src/workflows/create-company.ts` with the following content:
|
||||
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/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."],
|
||||
@@ -419,7 +357,7 @@ export const workflowHighlights = [
|
||||
import { CreateCustomerGroupDTO } from "@medusajs/types"
|
||||
import { CompanyDTO, CreateCompanyDTO } from "../types/b2b"
|
||||
import B2bModuleService from "../modules/b2b/service"
|
||||
|
||||
|
||||
export type CreateCompanyWorkflowInput = CreateCompanyDTO & {
|
||||
customer_group?: CreateCustomerGroupDTO
|
||||
}
|
||||
@@ -471,7 +409,7 @@ export const workflowHighlights = [
|
||||
"b2bModuleService"
|
||||
)
|
||||
|
||||
const company = await b2bModuleService.create(
|
||||
const company = await b2bModuleService.createCompany(
|
||||
companyData
|
||||
)
|
||||
|
||||
@@ -498,10 +436,10 @@ export const workflowHighlights = [
|
||||
|
||||
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` field.
|
||||
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.
|
||||
|
||||
Then, create the file `src/api/admin/b2b/company/route.ts` with the following content:
|
||||
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 {
|
||||
@@ -566,7 +504,7 @@ export const workflowHighlights = [
|
||||
|
||||
</Note>
|
||||
|
||||
</Details>
|
||||
</Details> */}
|
||||
|
||||
## Add B2B Customers
|
||||
|
||||
@@ -687,18 +625,21 @@ The API route can check if the customer has any group with an associated company
|
||||
showLinkIcon={false}
|
||||
/>
|
||||
|
||||
<Details summaryContent="Example">
|
||||
{/* <Details summaryContent="Example">
|
||||
|
||||
For example, create the API route `src/api/store/b2b/check-customer/route.ts` with the following content:
|
||||
|
||||
export const checkCustomerHighlights = [
|
||||
["16", "retrieve", "Retrieve the customer along with its groups."],
|
||||
["20", "list", "List the companies that have a customer group ID matching any of the customer's group IDs."],
|
||||
["25", "", "Return whether there are any companies associated with the customer's groups."]
|
||||
["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 type {
|
||||
AuthenticatedMedusaRequest,
|
||||
MedusaResponse,
|
||||
} from "@medusajs/medusa"
|
||||
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||
import { ICustomerModuleService } from "@medusajs/types"
|
||||
import B2bModuleService from "../../../../modules/b2b/service"
|
||||
@@ -713,11 +654,14 @@ export const checkCustomerHighlights = [
|
||||
"b2bModuleService"
|
||||
)
|
||||
|
||||
const customer = await customerModuleService.retrieve(req.auth.actor_id, {
|
||||
relations: ["groups"],
|
||||
})
|
||||
const customer = await customerModuleService.retrieveCustomer(
|
||||
req.auth_context.actor_id,
|
||||
{
|
||||
relations: ["groups"],
|
||||
}
|
||||
)
|
||||
|
||||
const companies = await b2bModuleService.list({
|
||||
const companies = await b2bModuleService.listCompanies({
|
||||
customer_group_id: customer.groups.map((group) => group.id),
|
||||
})
|
||||
|
||||
@@ -736,7 +680,10 @@ export const checkCustomerHighlights = [
|
||||
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"
|
||||
import {
|
||||
MiddlewaresConfig,
|
||||
authenticate,
|
||||
} from "@medusajs/medusa"
|
||||
|
||||
export const config: MiddlewaresConfig = {
|
||||
routes: [
|
||||
@@ -796,16 +743,12 @@ export const checkCustomerHighlights = [
|
||||
}
|
||||
```
|
||||
|
||||
</Details>
|
||||
</Details> */}
|
||||
|
||||
---
|
||||
|
||||
## Customize Admin
|
||||
|
||||
<Note type="soon">
|
||||
|
||||
Admin customizations are coming soon!
|
||||
|
||||
</Note>
|
||||
|
||||
Based on your use case, you may need to customize the Medusa Admin to add new widgets or pages.
|
||||
|
||||
The Medusa Admin plugin can be extended to add widgets, new pages, and setting pages.
|
||||
@@ -851,9 +794,9 @@ Use the publishable API key you associated with your B2B sales channel in the st
|
||||
showLinkIcon: false
|
||||
},
|
||||
{
|
||||
href: "!docs!/storefront-development/tips",
|
||||
title: "Storefront Tips",
|
||||
text: "Find tips on developing a custom storefront.",
|
||||
href: "/storefront-development",
|
||||
title: "Storefront Development",
|
||||
text: "Find guides for your storefront development.",
|
||||
startIcon: <AcademicCapSolid />,
|
||||
showLinkIcon: false
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user