feat(customer): Introduce customer group (#6137)
**What** Methods for: - Bulk create customers - Bulk and single create of customer groups - Assigning customer <> group relationships - listing of customers and customer groups ++ Uses new repository loading mechanism
This commit is contained in:
1 parent
fd78f5e242
commit
a52586880c
18 files changed
+632
-119
No files matched your search
+275
-4
@@ -23,19 +23,290 @@ describe("Customer Module Service", () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
describe("create", () => {
|
describe("create", () => {
|
||||||
it("should create a customer", async () => {
|
it("should create a single customer", async () => {
|
||||||
const customerPromise = service.create({
|
const customerData = {
|
||||||
|
company_name: "Acme Corp",
|
||||||
first_name: "John",
|
first_name: "John",
|
||||||
last_name: "Doe",
|
last_name: "Doe",
|
||||||
})
|
email: "john.doe@acmecorp.com",
|
||||||
|
phone: "123456789",
|
||||||
|
created_by: "admin",
|
||||||
|
metadata: { membership: "gold" },
|
||||||
|
}
|
||||||
|
const customer = await service.create(customerData)
|
||||||
|
|
||||||
await expect(customerPromise).resolves.toEqual(
|
expect(customer).toEqual(
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
id: expect.any(String),
|
id: expect.any(String),
|
||||||
|
company_name: "Acme Corp",
|
||||||
first_name: "John",
|
first_name: "John",
|
||||||
last_name: "Doe",
|
last_name: "Doe",
|
||||||
|
email: "john.doe@acmecorp.com",
|
||||||
|
phone: "123456789",
|
||||||
|
created_by: "admin",
|
||||||
|
metadata: expect.objectContaining({ membership: "gold" }),
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it("should create multiple customers", async () => {
|
||||||
|
const customersData = [
|
||||||
|
{
|
||||||
|
company_name: "Acme Corp",
|
||||||
|
first_name: "John",
|
||||||
|
last_name: "Doe",
|
||||||
|
email: "john.doe@acmecorp.com",
|
||||||
|
phone: "123456789",
|
||||||
|
created_by: "admin",
|
||||||
|
metadata: { membership: "gold" },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
first_name: "Jane",
|
||||||
|
last_name: "Smith",
|
||||||
|
email: "jane.smith@example.com",
|
||||||
|
phone: "987654321",
|
||||||
|
metadata: { membership: "silver" },
|
||||||
|
},
|
||||||
|
]
|
||||||
|
const customer = await service.create(customersData)
|
||||||
|
|
||||||
|
expect(customer).toEqual(
|
||||||
|
expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
id: expect.any(String),
|
||||||
|
company_name: "Acme Corp",
|
||||||
|
first_name: "John",
|
||||||
|
last_name: "Doe",
|
||||||
|
email: "john.doe@acmecorp.com",
|
||||||
|
phone: "123456789",
|
||||||
|
created_by: "admin",
|
||||||
|
metadata: expect.objectContaining({ membership: "gold" }),
|
||||||
|
}),
|
||||||
|
expect.objectContaining({
|
||||||
|
id: expect.any(String),
|
||||||
|
first_name: "Jane",
|
||||||
|
last_name: "Smith",
|
||||||
|
email: "jane.smith@example.com",
|
||||||
|
phone: "987654321",
|
||||||
|
metadata: expect.objectContaining({ membership: "silver" }),
|
||||||
|
}),
|
||||||
|
])
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("createCustomerGroup", () => {
|
||||||
|
it("should create a single customer group", async () => {
|
||||||
|
const group = await service.createCustomerGroup({
|
||||||
|
name: "VIP Customers",
|
||||||
|
metadata: { priority: "high" },
|
||||||
|
created_by: "admin",
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(group).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
id: expect.any(String),
|
||||||
|
name: "VIP Customers",
|
||||||
|
metadata: expect.objectContaining({ priority: "high" }),
|
||||||
|
created_by: "admin",
|
||||||
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should create multiple customer groups", async () => {
|
||||||
|
const groups = await service.createCustomerGroup([
|
||||||
|
{
|
||||||
|
name: "VIP Customers",
|
||||||
|
metadata: { priority: "high" },
|
||||||
|
created_by: "admin",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Regular Customers",
|
||||||
|
metadata: { discount: "10%" },
|
||||||
|
created_by: "staff",
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
expect(groups).toEqual(
|
||||||
|
expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
id: expect.any(String),
|
||||||
|
name: "VIP Customers",
|
||||||
|
metadata: expect.objectContaining({ priority: "high" }),
|
||||||
|
created_by: "admin",
|
||||||
|
}),
|
||||||
|
expect.objectContaining({
|
||||||
|
id: expect.any(String),
|
||||||
|
name: "Regular Customers",
|
||||||
|
metadata: expect.objectContaining({ discount: "10%" }),
|
||||||
|
created_by: "staff",
|
||||||
|
}),
|
||||||
|
])
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("list", () => {
|
||||||
|
it("should list all customers when no filters are applied", async () => {
|
||||||
|
await service.create([
|
||||||
|
{ first_name: "John", last_name: "Doe", email: "john.doe@example.com" },
|
||||||
|
{
|
||||||
|
first_name: "Jane",
|
||||||
|
last_name: "Smith",
|
||||||
|
email: "jane.smith@example.com",
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
const customers = await service.list()
|
||||||
|
|
||||||
|
expect(customers.length).toBeGreaterThanOrEqual(2)
|
||||||
|
expect(customers).toEqual(
|
||||||
|
expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
first_name: "John",
|
||||||
|
last_name: "Doe",
|
||||||
|
email: "john.doe@example.com",
|
||||||
|
}),
|
||||||
|
expect.objectContaining({
|
||||||
|
first_name: "Jane",
|
||||||
|
last_name: "Smith",
|
||||||
|
email: "jane.smith@example.com",
|
||||||
|
}),
|
||||||
|
])
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should list customers filtered by a specific email", async () => {
|
||||||
|
await service.create([
|
||||||
|
{
|
||||||
|
first_name: "John",
|
||||||
|
last_name: "Doe",
|
||||||
|
email: "unique.email@example.com",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
first_name: "Jane",
|
||||||
|
last_name: "Smith",
|
||||||
|
email: "jane.smith@example.com",
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
const filter = { email: "unique.email@example.com" }
|
||||||
|
const customers = await service.list(filter)
|
||||||
|
|
||||||
|
expect(customers.length).toBe(1)
|
||||||
|
expect(customers[0]).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
first_name: "John",
|
||||||
|
last_name: "Doe",
|
||||||
|
email: "unique.email@example.com",
|
||||||
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should list customers by a specific customer group", async () => {
|
||||||
|
const vipGroup = await service.createCustomerGroup({ name: "VIP" })
|
||||||
|
|
||||||
|
const [john] = await service.create([
|
||||||
|
{ first_name: "John", last_name: "Doe", email: "john.doe@example.com" },
|
||||||
|
{
|
||||||
|
first_name: "Jane",
|
||||||
|
last_name: "Smith",
|
||||||
|
email: "jane.smith@example.com",
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
await service.addCustomerToGroup({
|
||||||
|
customer_id: john.id,
|
||||||
|
customer_group_id: vipGroup.id,
|
||||||
|
})
|
||||||
|
|
||||||
|
const filter = { groups: vipGroup.id }
|
||||||
|
const customers = await service.list(filter)
|
||||||
|
|
||||||
|
expect(customers).toEqual(
|
||||||
|
expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
first_name: "John",
|
||||||
|
last_name: "Doe",
|
||||||
|
email: "john.doe@example.com",
|
||||||
|
}),
|
||||||
|
])
|
||||||
|
)
|
||||||
|
expect(customers).not.toEqual(
|
||||||
|
expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
first_name: "Jane",
|
||||||
|
last_name: "Smith",
|
||||||
|
email: "jane.smith@example.com",
|
||||||
|
}),
|
||||||
|
])
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("addCustomerToGroup", () => {
|
||||||
|
it("should add a single customer to a customer group", async () => {
|
||||||
|
const [customer] = await service.create([
|
||||||
|
{ first_name: "John", last_name: "Doe", email: "john.doe@example.com" },
|
||||||
|
])
|
||||||
|
const [group] = await service.createCustomerGroup([{ name: "VIP" }])
|
||||||
|
|
||||||
|
const result = await service.addCustomerToGroup({
|
||||||
|
customer_id: customer.id,
|
||||||
|
customer_group_id: group.id,
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(result).toEqual(
|
||||||
|
expect.objectContaining({ id: expect.any(String) })
|
||||||
|
)
|
||||||
|
|
||||||
|
// Additional validation (optional): retrieve the customer and check if the group is assigned
|
||||||
|
const updatedCustomer = await service.retrieve(customer.id, {
|
||||||
|
relations: ["groups"],
|
||||||
|
})
|
||||||
|
expect(updatedCustomer.groups).toContainEqual(
|
||||||
|
expect.objectContaining({ id: group.id })
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should add multiple customers to customer groups", async () => {
|
||||||
|
const customers = await service.create([
|
||||||
|
{ first_name: "John", last_name: "Doe", email: "john.doe@example.com" },
|
||||||
|
{
|
||||||
|
first_name: "Jane",
|
||||||
|
last_name: "Smith",
|
||||||
|
email: "jane.smith@example.com",
|
||||||
|
},
|
||||||
|
])
|
||||||
|
const groups = await service.createCustomerGroup([
|
||||||
|
{ name: "VIP" },
|
||||||
|
{ name: "Regular" },
|
||||||
|
])
|
||||||
|
|
||||||
|
const pairs = customers.map((customer, index) => ({
|
||||||
|
customer_id: customer.id,
|
||||||
|
customer_group_id: groups[index % groups.length].id,
|
||||||
|
}))
|
||||||
|
|
||||||
|
const results = await service.addCustomerToGroup(pairs)
|
||||||
|
|
||||||
|
expect(results).toEqual(
|
||||||
|
expect.arrayContaining([
|
||||||
|
expect.objectContaining({ id: expect.any(String) }),
|
||||||
|
expect.objectContaining({ id: expect.any(String) }),
|
||||||
|
])
|
||||||
|
)
|
||||||
|
|
||||||
|
for (const customer of customers) {
|
||||||
|
const updatedCustomer = await service.retrieve(customer.id, {
|
||||||
|
relations: ["groups"],
|
||||||
|
})
|
||||||
|
expect(updatedCustomer.groups).toContainEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
id: groups[customers.indexOf(customer) % groups.length].id,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@@ -1,52 +1,9 @@
|
|||||||
import * as defaultRepositories from "@repositories"
|
import { MikroOrmBaseRepository, ModulesSdkUtils } from "@medusajs/utils"
|
||||||
|
import * as ModuleModels from "@models"
|
||||||
|
import * as ModuleServices from "@services"
|
||||||
|
|
||||||
import { LoaderOptions } from "@medusajs/modules-sdk"
|
export default ModulesSdkUtils.moduleContainerLoaderFactory({
|
||||||
import { ModulesSdkTypes } from "@medusajs/types"
|
moduleModels: ModuleModels,
|
||||||
import { loadCustomRepositories } from "@medusajs/utils"
|
moduleServices: ModuleServices,
|
||||||
import * as defaultServices from "@services"
|
moduleRepositories: { BaseRepository: MikroOrmBaseRepository },
|
||||||
import { asClass } from "awilix"
|
})
|
||||||
|
|
||||||
export default async ({
|
|
||||||
container,
|
|
||||||
options,
|
|
||||||
}: LoaderOptions<
|
|
||||||
| ModulesSdkTypes.ModuleServiceInitializeOptions
|
|
||||||
| ModulesSdkTypes.ModuleServiceInitializeCustomDataLayerOptions
|
|
||||||
>): Promise<void> => {
|
|
||||||
const customRepositories = (
|
|
||||||
options as ModulesSdkTypes.ModuleServiceInitializeCustomDataLayerOptions
|
|
||||||
)?.repositories
|
|
||||||
|
|
||||||
container.register({
|
|
||||||
customerService: asClass(defaultServices.CustomerService).singleton(),
|
|
||||||
addressService: asClass(defaultServices.AddressService).singleton(),
|
|
||||||
customerGroupService: asClass(
|
|
||||||
defaultServices.CustomerGroupService
|
|
||||||
).singleton(),
|
|
||||||
})
|
|
||||||
|
|
||||||
if (customRepositories) {
|
|
||||||
loadCustomRepositories({
|
|
||||||
defaultRepositories,
|
|
||||||
customRepositories,
|
|
||||||
container,
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
loadDefaultRepositories({ container })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function loadDefaultRepositories({ container }) {
|
|
||||||
container.register({
|
|
||||||
baseRepository: asClass(defaultRepositories.BaseRepository).singleton(),
|
|
||||||
customerRepository: asClass(
|
|
||||||
defaultRepositories.CustomerRepository
|
|
||||||
).singleton(),
|
|
||||||
addressRepository: asClass(
|
|
||||||
defaultRepositories.AddressRepository
|
|
||||||
).singleton(),
|
|
||||||
customerGroupRepository: asClass(
|
|
||||||
defaultRepositories.CustomerGroupRepository
|
|
||||||
).singleton(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
@@ -12,7 +12,7 @@ import {
|
|||||||
import Customer from "./customer"
|
import Customer from "./customer"
|
||||||
import CustomerGroup from "./customer-group"
|
import CustomerGroup from "./customer-group"
|
||||||
|
|
||||||
type OptionalGroupProps = DAL.EntityDateColumns // TODO: To be revisited when more clear
|
type OptionalGroupProps = "customer_group" | "customer" | DAL.EntityDateColumns // TODO: To be revisited when more clear
|
||||||
|
|
||||||
@Entity({ tableName: "customer_group_customer" })
|
@Entity({ tableName: "customer_group_customer" })
|
||||||
export default class CustomerGroupCustomer {
|
export default class CustomerGroupCustomer {
|
||||||
@@ -21,19 +21,27 @@ export default class CustomerGroupCustomer {
|
|||||||
@PrimaryKey({ columnType: "text" })
|
@PrimaryKey({ columnType: "text" })
|
||||||
id!: string
|
id!: string
|
||||||
|
|
||||||
|
@Property({ columnType: "text" })
|
||||||
|
customer_id: string
|
||||||
|
|
||||||
|
@Property({ columnType: "text" })
|
||||||
|
customer_group_id: string
|
||||||
|
|
||||||
@ManyToOne({
|
@ManyToOne({
|
||||||
entity: () => Customer,
|
entity: () => Customer,
|
||||||
fieldName: "customer__id",
|
fieldName: "customer_id",
|
||||||
index: "IDX_customer_group_customer_customer_id",
|
index: "IDX_customer_group_customer_customer_id",
|
||||||
|
nullable: true,
|
||||||
})
|
})
|
||||||
customer: Customer
|
customer: Customer | null
|
||||||
|
|
||||||
@ManyToOne({
|
@ManyToOne({
|
||||||
entity: () => CustomerGroup,
|
entity: () => CustomerGroup,
|
||||||
fieldName: "customer_group_id",
|
fieldName: "customer_group_id",
|
||||||
index: "IDX_customer_group_customer_group_id",
|
index: "IDX_customer_group_customer_group_id",
|
||||||
|
nullable: true,
|
||||||
})
|
})
|
||||||
customer_group: CustomerGroup
|
customer_group: CustomerGroup | null
|
||||||
|
|
||||||
@Property({ columnType: "jsonb", nullable: true })
|
@Property({ columnType: "jsonb", nullable: true })
|
||||||
metadata: Record<string, unknown> | null = null
|
metadata: Record<string, unknown> | null = null
|
||||||
@@ -53,6 +61,9 @@ export default class CustomerGroupCustomer {
|
|||||||
})
|
})
|
||||||
updated_at: Date
|
updated_at: Date
|
||||||
|
|
||||||
|
@Property({ columnType: "text", nullable: true })
|
||||||
|
created_by: string | null = null
|
||||||
|
|
||||||
@BeforeCreate()
|
@BeforeCreate()
|
||||||
onCreate() {
|
onCreate() {
|
||||||
this.id = generateEntityId(this.id, "cusgc")
|
this.id = generateEntityId(this.id, "cusgc")
|
||||||
|
|||||||
@@ -34,6 +34,9 @@ export default class CustomerGroup {
|
|||||||
@Property({ columnType: "jsonb", nullable: true })
|
@Property({ columnType: "jsonb", nullable: true })
|
||||||
metadata: Record<string, unknown> | null = null
|
metadata: Record<string, unknown> | null = null
|
||||||
|
|
||||||
|
@Property({ columnType: "text", nullable: true })
|
||||||
|
created_by: string | null = null
|
||||||
|
|
||||||
@Property({
|
@Property({
|
||||||
onCreate: () => new Date(),
|
onCreate: () => new Date(),
|
||||||
columnType: "timestamptz",
|
columnType: "timestamptz",
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ export default class Customer {
|
|||||||
metadata: Record<string, unknown> | null = null
|
metadata: Record<string, unknown> | null = null
|
||||||
|
|
||||||
@ManyToMany({
|
@ManyToMany({
|
||||||
inversedBy: (group) => group.customers,
|
mappedBy: "customers",
|
||||||
entity: () => CustomerGroup,
|
entity: () => CustomerGroup,
|
||||||
pivotEntity: () => CustomerGroupCustomer,
|
pivotEntity: () => CustomerGroupCustomer,
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,11 +0,0 @@
|
|||||||
import { DALUtils } from "@medusajs/utils"
|
|
||||||
import { Address } from "@models"
|
|
||||||
import { CreateAddressDTO, UpdateAddressDTO } from "@types"
|
|
||||||
|
|
||||||
export class AddressRepository extends DALUtils.mikroOrmBaseRepositoryFactory<
|
|
||||||
Address,
|
|
||||||
{
|
|
||||||
create: CreateAddressDTO
|
|
||||||
update: UpdateAddressDTO
|
|
||||||
}
|
|
||||||
>(Address) {}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
import { DALUtils } from "@medusajs/utils"
|
|
||||||
import { CustomerGroup } from "@models"
|
|
||||||
import { CreateCustomerGroupDTO, UpdateCustomerGroupDTO } from "@types"
|
|
||||||
|
|
||||||
export class CustomerGroupRepository extends DALUtils.mikroOrmBaseRepositoryFactory<
|
|
||||||
CustomerGroup,
|
|
||||||
{
|
|
||||||
create: CreateCustomerGroupDTO
|
|
||||||
update: UpdateCustomerGroupDTO
|
|
||||||
}
|
|
||||||
>(CustomerGroup) {}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
import { DALUtils } from "@medusajs/utils"
|
|
||||||
import { Customer } from "@models"
|
|
||||||
import { CreateCustomerDTO, UpdateCustomerDTO } from "@medusajs/types"
|
|
||||||
|
|
||||||
export class CustomerRepository extends DALUtils.mikroOrmBaseRepositoryFactory<
|
|
||||||
Customer,
|
|
||||||
{
|
|
||||||
create: CreateCustomerDTO
|
|
||||||
update: UpdateCustomerDTO
|
|
||||||
}
|
|
||||||
>(Customer) {}
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
export { MikroOrmBaseRepository as BaseRepository } from "@medusajs/utils"
|
|
||||||
export * from "./address"
|
|
||||||
export * from "./customer"
|
|
||||||
export * from "./customer-group"
|
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
import { DAL } from "@medusajs/types"
|
||||||
|
import { ModulesSdkUtils } from "@medusajs/utils"
|
||||||
|
import { CustomerGroupCustomer } from "@models"
|
||||||
|
|
||||||
|
type CreateCustomerGroupCustomerDTO = {
|
||||||
|
customer_id: string
|
||||||
|
customer_group_id: string
|
||||||
|
created_by?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
type InjectedDependencies = {
|
||||||
|
customerGroupRepository: DAL.RepositoryService
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class CustomerGroupCustomerService<
|
||||||
|
TEntity extends CustomerGroupCustomer = CustomerGroupCustomer
|
||||||
|
> extends ModulesSdkUtils.abstractServiceFactory<
|
||||||
|
InjectedDependencies,
|
||||||
|
{ create: CreateCustomerGroupCustomerDTO }
|
||||||
|
>(CustomerGroupCustomer)<TEntity> {
|
||||||
|
constructor(container: InjectedDependencies) {
|
||||||
|
// @ts-ignore
|
||||||
|
super(...arguments)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import { DAL } from "@medusajs/types"
|
import { DAL } from "@medusajs/types"
|
||||||
import { ModulesSdkUtils } from "@medusajs/utils"
|
import { ModulesSdkUtils } from "@medusajs/utils"
|
||||||
import { CustomerGroup } from "@models"
|
import { CustomerGroup } from "@models"
|
||||||
import { CreateCustomerGroupDTO, UpdateCustomerGroupDTO } from "@types"
|
import { CreateCustomerGroupDTO, UpdateCustomerGroupDTO } from "@medusajs/types"
|
||||||
|
|
||||||
type InjectedDependencies = {
|
type InjectedDependencies = {
|
||||||
customerGroupRepository: DAL.RepositoryService
|
customerGroupRepository: DAL.RepositoryService
|
||||||
|
|||||||
@@ -8,7 +8,11 @@ import {
|
|||||||
CustomerTypes,
|
CustomerTypes,
|
||||||
} from "@medusajs/types"
|
} from "@medusajs/types"
|
||||||
|
|
||||||
import { InjectManager, MedusaContext } from "@medusajs/utils"
|
import {
|
||||||
|
InjectManager,
|
||||||
|
InjectTransactionManager,
|
||||||
|
MedusaContext,
|
||||||
|
} from "@medusajs/utils"
|
||||||
import { joinerConfig } from "../joiner-config"
|
import { joinerConfig } from "../joiner-config"
|
||||||
import * as services from "../services"
|
import * as services from "../services"
|
||||||
|
|
||||||
@@ -17,6 +21,7 @@ type InjectedDependencies = {
|
|||||||
customerService: services.CustomerService
|
customerService: services.CustomerService
|
||||||
addressService: services.AddressService
|
addressService: services.AddressService
|
||||||
customerGroupService: services.CustomerGroupService
|
customerGroupService: services.CustomerGroupService
|
||||||
|
customerGroupCustomerService: services.CustomerGroupCustomerService
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class CustomerModuleService implements ICustomerModuleService {
|
export default class CustomerModuleService implements ICustomerModuleService {
|
||||||
@@ -24,6 +29,7 @@ export default class CustomerModuleService implements ICustomerModuleService {
|
|||||||
protected customerService_: services.CustomerService
|
protected customerService_: services.CustomerService
|
||||||
protected addressService_: services.AddressService
|
protected addressService_: services.AddressService
|
||||||
protected customerGroupService_: services.CustomerGroupService
|
protected customerGroupService_: services.CustomerGroupService
|
||||||
|
protected customerGroupCustomerService_: services.CustomerGroupCustomerService
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
{
|
{
|
||||||
@@ -31,6 +37,7 @@ export default class CustomerModuleService implements ICustomerModuleService {
|
|||||||
customerService,
|
customerService,
|
||||||
addressService,
|
addressService,
|
||||||
customerGroupService,
|
customerGroupService,
|
||||||
|
customerGroupCustomerService,
|
||||||
}: InjectedDependencies,
|
}: InjectedDependencies,
|
||||||
protected readonly moduleDeclaration: InternalModuleDeclaration
|
protected readonly moduleDeclaration: InternalModuleDeclaration
|
||||||
) {
|
) {
|
||||||
@@ -38,6 +45,7 @@ export default class CustomerModuleService implements ICustomerModuleService {
|
|||||||
this.customerService_ = customerService
|
this.customerService_ = customerService
|
||||||
this.addressService_ = addressService
|
this.addressService_ = addressService
|
||||||
this.customerGroupService_ = customerGroupService
|
this.customerGroupService_ = customerGroupService
|
||||||
|
this.customerGroupCustomerService_ = customerGroupCustomerService
|
||||||
}
|
}
|
||||||
|
|
||||||
__joinerConfig(): ModuleJoinerConfig {
|
__joinerConfig(): ModuleJoinerConfig {
|
||||||
@@ -64,18 +72,189 @@ export default class CustomerModuleService implements ICustomerModuleService {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@InjectManager("baseRepository_")
|
|
||||||
async create(
|
async create(
|
||||||
data: CustomerTypes.CreateCustomerDTO,
|
data: CustomerTypes.CreateCustomerDTO,
|
||||||
|
sharedContext?: Context
|
||||||
|
): Promise<CustomerTypes.CustomerDTO>
|
||||||
|
|
||||||
|
async create(
|
||||||
|
data: CustomerTypes.CreateCustomerDTO[],
|
||||||
|
sharedContext?: Context
|
||||||
|
): Promise<CustomerTypes.CustomerDTO[]>
|
||||||
|
|
||||||
|
@InjectTransactionManager("baseRepository_")
|
||||||
|
async create(
|
||||||
|
dataOrArray:
|
||||||
|
| CustomerTypes.CreateCustomerDTO
|
||||||
|
| CustomerTypes.CreateCustomerDTO[],
|
||||||
@MedusaContext() sharedContext: Context = {}
|
@MedusaContext() sharedContext: Context = {}
|
||||||
): Promise<CustomerTypes.CustomerDTO> {
|
) {
|
||||||
const [customer] = await this.customerService_.create([data], sharedContext)
|
const data = Array.isArray(dataOrArray) ? dataOrArray : [dataOrArray]
|
||||||
|
const customer = await this.customerService_.create(data, sharedContext)
|
||||||
|
|
||||||
|
if (Array.isArray(dataOrArray)) {
|
||||||
|
return await this.baseRepository_.serialize<CustomerTypes.CustomerDTO[]>(
|
||||||
|
customer,
|
||||||
|
{
|
||||||
|
populate: true,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
return await this.baseRepository_.serialize<CustomerTypes.CustomerDTO>(
|
return await this.baseRepository_.serialize<CustomerTypes.CustomerDTO>(
|
||||||
customer,
|
customer[0],
|
||||||
{
|
{
|
||||||
populate: true,
|
populate: true,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@InjectManager("baseRepository_")
|
||||||
|
async list(
|
||||||
|
filters: CustomerTypes.FilterableCustomerProps = {},
|
||||||
|
config: FindConfig<CustomerTypes.CustomerDTO> = {},
|
||||||
|
@MedusaContext() sharedContext: Context = {}
|
||||||
|
) {
|
||||||
|
const customers = await this.customerService_.list(
|
||||||
|
filters,
|
||||||
|
config,
|
||||||
|
sharedContext
|
||||||
|
)
|
||||||
|
|
||||||
|
return await this.baseRepository_.serialize<CustomerTypes.CustomerDTO[]>(
|
||||||
|
customers,
|
||||||
|
{
|
||||||
|
populate: true,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@InjectManager("baseRepository_")
|
||||||
|
async listAndCount(
|
||||||
|
filters: CustomerTypes.FilterableCustomerProps = {},
|
||||||
|
config: FindConfig<CustomerTypes.CustomerDTO> = {},
|
||||||
|
@MedusaContext() sharedContext: Context = {}
|
||||||
|
): Promise<[CustomerTypes.CustomerDTO[], number]> {
|
||||||
|
const [customers, count] = await this.customerService_.listAndCount(
|
||||||
|
filters,
|
||||||
|
config,
|
||||||
|
sharedContext
|
||||||
|
)
|
||||||
|
|
||||||
|
return [
|
||||||
|
await this.baseRepository_.serialize<CustomerTypes.CustomerDTO[]>(
|
||||||
|
customers,
|
||||||
|
{
|
||||||
|
populate: true,
|
||||||
|
}
|
||||||
|
),
|
||||||
|
count,
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
async createCustomerGroup(
|
||||||
|
dataOrArrayOfData: CustomerTypes.CreateCustomerGroupDTO,
|
||||||
|
sharedContext?: Context
|
||||||
|
): Promise<CustomerTypes.CustomerGroupDTO>
|
||||||
|
|
||||||
|
async createCustomerGroup(
|
||||||
|
dataOrArrayOfData: CustomerTypes.CreateCustomerGroupDTO[],
|
||||||
|
sharedContext?: Context
|
||||||
|
): Promise<CustomerTypes.CustomerGroupDTO[]>
|
||||||
|
|
||||||
|
@InjectTransactionManager("baseRepository_")
|
||||||
|
async createCustomerGroup(
|
||||||
|
dataOrArrayOfData:
|
||||||
|
| CustomerTypes.CreateCustomerGroupDTO
|
||||||
|
| CustomerTypes.CreateCustomerGroupDTO[],
|
||||||
|
@MedusaContext() sharedContext: Context = {}
|
||||||
|
) {
|
||||||
|
const data = Array.isArray(dataOrArrayOfData)
|
||||||
|
? dataOrArrayOfData
|
||||||
|
: [dataOrArrayOfData]
|
||||||
|
|
||||||
|
const groups = await this.customerGroupService_.create(data, sharedContext)
|
||||||
|
|
||||||
|
if (Array.isArray(dataOrArrayOfData)) {
|
||||||
|
return await this.baseRepository_.serialize<
|
||||||
|
CustomerTypes.CustomerGroupDTO[]
|
||||||
|
>(groups, {
|
||||||
|
populate: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return await this.baseRepository_.serialize<CustomerTypes.CustomerGroupDTO>(
|
||||||
|
groups[0],
|
||||||
|
{ populate: true }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
async addCustomerToGroup(
|
||||||
|
groupCustomerPair: CustomerTypes.GroupCustomerPair,
|
||||||
|
sharedContext?: Context
|
||||||
|
): Promise<{ id: string }>
|
||||||
|
|
||||||
|
async addCustomerToGroup(
|
||||||
|
groupCustomerPairs: CustomerTypes.GroupCustomerPair[],
|
||||||
|
sharedContext?: Context
|
||||||
|
): Promise<{ id: string }[]>
|
||||||
|
|
||||||
|
@InjectTransactionManager("baseRepository_")
|
||||||
|
async addCustomerToGroup(
|
||||||
|
data: CustomerTypes.GroupCustomerPair | CustomerTypes.GroupCustomerPair[],
|
||||||
|
@MedusaContext() sharedContext: Context = {}
|
||||||
|
): Promise<{ id: string } | { id: string }[]> {
|
||||||
|
const groupCustomers = await this.customerGroupCustomerService_.create(
|
||||||
|
Array.isArray(data) ? data : [data],
|
||||||
|
sharedContext
|
||||||
|
)
|
||||||
|
|
||||||
|
if (Array.isArray(data)) {
|
||||||
|
return groupCustomers.map((gc) => ({ id: gc.id }))
|
||||||
|
}
|
||||||
|
|
||||||
|
return { id: groupCustomers[0].id }
|
||||||
|
}
|
||||||
|
|
||||||
|
@InjectManager("baseRepository_")
|
||||||
|
async listCustomerGroups(
|
||||||
|
filters: CustomerTypes.FilterableCustomerGroupProps = {},
|
||||||
|
config: FindConfig<CustomerTypes.CustomerGroupDTO> = {},
|
||||||
|
@MedusaContext() sharedContext: Context = {}
|
||||||
|
) {
|
||||||
|
const groups = await this.customerGroupService_.list(
|
||||||
|
filters,
|
||||||
|
config,
|
||||||
|
sharedContext
|
||||||
|
)
|
||||||
|
|
||||||
|
return await this.baseRepository_.serialize<
|
||||||
|
CustomerTypes.CustomerGroupDTO[]
|
||||||
|
>(groups, {
|
||||||
|
populate: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
@InjectManager("baseRepository_")
|
||||||
|
async listAndCountCustomerGroups(
|
||||||
|
filters: CustomerTypes.FilterableCustomerGroupProps = {},
|
||||||
|
config: FindConfig<CustomerTypes.CustomerGroupDTO> = {},
|
||||||
|
@MedusaContext() sharedContext: Context = {}
|
||||||
|
): Promise<[CustomerTypes.CustomerGroupDTO[], number]> {
|
||||||
|
const [groups, count] = await this.customerGroupService_.listAndCount(
|
||||||
|
filters,
|
||||||
|
config,
|
||||||
|
sharedContext
|
||||||
|
)
|
||||||
|
|
||||||
|
return [
|
||||||
|
await this.baseRepository_.serialize<CustomerTypes.CustomerGroupDTO[]>(
|
||||||
|
groups,
|
||||||
|
{
|
||||||
|
populate: true,
|
||||||
|
}
|
||||||
|
),
|
||||||
|
count,
|
||||||
|
]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -2,3 +2,4 @@ export { default as AddressService } from "./address"
|
|||||||
export { default as CustomerGroupService } from "./customer-group"
|
export { default as CustomerGroupService } from "./customer-group"
|
||||||
export { default as CustomerService } from "./customer"
|
export { default as CustomerService } from "./customer"
|
||||||
export { default as CustomerModuleService } from "./customer-module"
|
export { default as CustomerModuleService } from "./customer-module"
|
||||||
|
export { default as CustomerGroupCustomerService } from "./customer-group-customer"
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
export type CreateCustomerGroupDTO = {
|
|
||||||
name: string
|
|
||||||
customer_ids?: string[]
|
|
||||||
metadata?: Record<string, unknown> | null
|
|
||||||
}
|
|
||||||
|
|
||||||
export type UpdateCustomerGroupDTO = {
|
|
||||||
name?: string
|
|
||||||
customer_ids?: string[]
|
|
||||||
metadata?: Record<string, unknown> | null
|
|
||||||
}
|
|
||||||
@@ -1,7 +1,5 @@
|
|||||||
import { Logger } from "@medusajs/types"
|
import { Logger } from "@medusajs/types"
|
||||||
export * from "./address"
|
export * from "./address"
|
||||||
export * from "./customer-group"
|
|
||||||
|
|
||||||
export type InitializeModuleInjectableDependencies = {
|
export type InitializeModuleInjectableDependencies = {
|
||||||
logger?: Logger
|
logger?: Logger
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,43 @@
|
|||||||
|
import { BaseFilterable } from "../dal"
|
||||||
|
import { OperatorMap } from "../dal/utils"
|
||||||
import { AddressDTO } from "../address"
|
import { AddressDTO } from "../address"
|
||||||
|
|
||||||
|
export interface CustomerGroupDTO {
|
||||||
|
id: string
|
||||||
|
name: string
|
||||||
|
customers?: Partial<CustomerDTO>[]
|
||||||
|
metadata?: Record<string, unknown>
|
||||||
|
created_by?: string | null
|
||||||
|
deleted_at?: Date | string | null
|
||||||
|
created_at?: Date | string
|
||||||
|
updated_at?: Date | string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FilterableCustomerGroupProps
|
||||||
|
extends BaseFilterable<FilterableCustomerGroupProps> {
|
||||||
|
id?: string | string[]
|
||||||
|
name?: OperatorMap<string>
|
||||||
|
customers?: FilterableCustomerProps | string | string[]
|
||||||
|
created_by?: string | string[] | null
|
||||||
|
created_at?: OperatorMap<string>
|
||||||
|
updated_at?: OperatorMap<string>
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FilterableCustomerProps
|
||||||
|
extends BaseFilterable<FilterableCustomerProps> {
|
||||||
|
id?: string | string[]
|
||||||
|
email?: string | string[] | OperatorMap<string>
|
||||||
|
groups?: FilterableCustomerGroupProps | string | string[]
|
||||||
|
default_billing_address_id?: string | string[] | null
|
||||||
|
default_shipping_address_id?: string | string[] | null
|
||||||
|
company_name?: string | string[] | OperatorMap<string> | null
|
||||||
|
first_name?: string | string[] | OperatorMap<string> | null
|
||||||
|
last_name?: string | string[] | OperatorMap<string> | null
|
||||||
|
created_by?: string | string[] | null
|
||||||
|
created_at?: OperatorMap<string>
|
||||||
|
updated_at?: OperatorMap<string>
|
||||||
|
}
|
||||||
|
|
||||||
export interface CustomerDTO {
|
export interface CustomerDTO {
|
||||||
id: string
|
id: string
|
||||||
email: string
|
email: string
|
||||||
@@ -14,11 +52,16 @@ export interface CustomerDTO {
|
|||||||
phone?: string | null
|
phone?: string | null
|
||||||
groups?: { id: string }[]
|
groups?: { id: string }[]
|
||||||
metadata?: Record<string, unknown>
|
metadata?: Record<string, unknown>
|
||||||
deleted_at?: Date | string
|
deleted_at?: Date | string | null
|
||||||
created_at?: Date | string
|
created_at?: Date | string
|
||||||
updated_at?: Date | string
|
updated_at?: Date | string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type GroupCustomerPair = {
|
||||||
|
customer_id: string
|
||||||
|
customer_group_id: string
|
||||||
|
}
|
||||||
|
|
||||||
export type legacy_CustomerDTO = {
|
export type legacy_CustomerDTO = {
|
||||||
id: string
|
id: string
|
||||||
email: string
|
email: string
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ export interface CreateCustomerDTO {
|
|||||||
last_name?: string
|
last_name?: string
|
||||||
email?: string
|
email?: string
|
||||||
phone?: string
|
phone?: string
|
||||||
|
created_by?: string
|
||||||
metadata?: Record<string, unknown>
|
metadata?: Record<string, unknown>
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -15,3 +16,20 @@ export interface UpdateCustomerDTO {
|
|||||||
phone?: string
|
phone?: string
|
||||||
metadata?: Record<string, unknown>
|
metadata?: Record<string, unknown>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface CreateCustomerGroupDTO {
|
||||||
|
name: string
|
||||||
|
metadata?: Record<string, unknown> | null
|
||||||
|
created_by?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CustomerGroupUpdatableFileds {
|
||||||
|
name?: string
|
||||||
|
metadata?: Record<string, unknown> | null
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UpdateCustomerGroupDTO {
|
||||||
|
id?: string
|
||||||
|
name?: string
|
||||||
|
metadata?: Record<string, unknown> | null
|
||||||
|
}
|
||||||
@@ -1,8 +1,14 @@
|
|||||||
import { FindConfig } from "../common"
|
import { FindConfig } from "../common"
|
||||||
import { IModuleService } from "../modules-sdk"
|
import { IModuleService } from "../modules-sdk"
|
||||||
import { Context } from "../shared-context"
|
import { Context } from "../shared-context"
|
||||||
import { CustomerDTO } from "./common"
|
import {
|
||||||
import { CreateCustomerDTO } from "./mutations"
|
CustomerDTO,
|
||||||
|
CustomerGroupDTO,
|
||||||
|
FilterableCustomerProps,
|
||||||
|
FilterableCustomerGroupProps,
|
||||||
|
GroupCustomerPair,
|
||||||
|
} from "./common"
|
||||||
|
import { CreateCustomerDTO, CreateCustomerGroupDTO } from "./mutations"
|
||||||
|
|
||||||
export interface ICustomerModuleService extends IModuleService {
|
export interface ICustomerModuleService extends IModuleService {
|
||||||
retrieve(
|
retrieve(
|
||||||
@@ -11,5 +17,54 @@ export interface ICustomerModuleService extends IModuleService {
|
|||||||
sharedContext?: Context
|
sharedContext?: Context
|
||||||
): Promise<CustomerDTO>
|
): Promise<CustomerDTO>
|
||||||
|
|
||||||
|
create(
|
||||||
|
data: CreateCustomerDTO[],
|
||||||
|
sharedContext?: Context
|
||||||
|
): Promise<CustomerDTO[]>
|
||||||
|
|
||||||
create(data: CreateCustomerDTO, sharedContext?: Context): Promise<CustomerDTO>
|
create(data: CreateCustomerDTO, sharedContext?: Context): Promise<CustomerDTO>
|
||||||
|
|
||||||
|
createCustomerGroup(
|
||||||
|
data: CreateCustomerGroupDTO[],
|
||||||
|
sharedContext?: Context
|
||||||
|
): Promise<CustomerGroupDTO[]>
|
||||||
|
|
||||||
|
createCustomerGroup(
|
||||||
|
data: CreateCustomerGroupDTO,
|
||||||
|
sharedContext?: Context
|
||||||
|
): Promise<CustomerGroupDTO>
|
||||||
|
|
||||||
|
addCustomerToGroup(
|
||||||
|
groupCustomerPair: GroupCustomerPair,
|
||||||
|
sharedContext?: Context
|
||||||
|
): Promise<{ id: string }>
|
||||||
|
|
||||||
|
addCustomerToGroup(
|
||||||
|
groupCustomerPairs: GroupCustomerPair[],
|
||||||
|
sharedContext?: Context
|
||||||
|
): Promise<{ id: string }[]>
|
||||||
|
|
||||||
|
list(
|
||||||
|
filters?: FilterableCustomerProps,
|
||||||
|
config?: FindConfig<CustomerDTO>,
|
||||||
|
sharedContext?: Context
|
||||||
|
): Promise<CustomerDTO[]>
|
||||||
|
|
||||||
|
listAndCount(
|
||||||
|
filters?: FilterableCustomerProps,
|
||||||
|
config?: FindConfig<CustomerDTO>,
|
||||||
|
sharedContext?: Context
|
||||||
|
): Promise<[CustomerDTO[], number]>
|
||||||
|
|
||||||
|
listCustomerGroups(
|
||||||
|
filters?: FilterableCustomerGroupProps,
|
||||||
|
config?: FindConfig<CustomerGroupDTO>,
|
||||||
|
sharedContext?: Context
|
||||||
|
): Promise<CustomerGroupDTO[]>
|
||||||
|
|
||||||
|
listAndCountCustomerGroups(
|
||||||
|
filters?: FilterableCustomerGroupProps,
|
||||||
|
config?: FindConfig<CustomerGroupDTO>,
|
||||||
|
sharedContext?: Context
|
||||||
|
): Promise<[CustomerGroupDTO[], number]>
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user