From a52586880c453c411a0740ffbe1e2457431f9cbc Mon Sep 17 00:00:00 2001 From: Sebastian Rindom Date: Mon, 22 Jan 2024 12:24:22 +0100 Subject: [PATCH] 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 --- .../services/customer-module/index.spec.ts | 279 +++++++++++++++++- packages/customer/src/loaders/container.ts | 59 +--- .../src/models/customer-group-customer.ts | 19 +- .../customer/src/models/customer-group.ts | 3 + packages/customer/src/models/customer.ts | 2 +- packages/customer/src/repositories/address.ts | 11 - .../src/repositories/customer-group.ts | 11 - .../customer/src/repositories/customer.ts | 11 - packages/customer/src/repositories/index.ts | 4 - .../src/services/customer-group-customer.ts | 25 ++ .../customer/src/services/customer-group.ts | 2 +- .../customer/src/services/customer-module.ts | 189 +++++++++++- packages/customer/src/services/index.ts | 1 + packages/customer/src/types/customer-group.ts | 11 - packages/customer/src/types/index.ts | 2 - packages/types/src/customer/common.ts | 45 ++- packages/types/src/customer/mutations.ts | 18 ++ packages/types/src/customer/service.ts | 59 +++- 18 files changed, 632 insertions(+), 119 deletions(-) delete mode 100644 packages/customer/src/repositories/address.ts delete mode 100644 packages/customer/src/repositories/customer-group.ts delete mode 100644 packages/customer/src/repositories/customer.ts delete mode 100644 packages/customer/src/repositories/index.ts create mode 100644 packages/customer/src/services/customer-group-customer.ts delete mode 100644 packages/customer/src/types/customer-group.ts diff --git a/packages/customer/integration-tests/__tests__/services/customer-module/index.spec.ts b/packages/customer/integration-tests/__tests__/services/customer-module/index.spec.ts index bb4719c245..dab64d59be 100644 --- a/packages/customer/integration-tests/__tests__/services/customer-module/index.spec.ts +++ b/packages/customer/integration-tests/__tests__/services/customer-module/index.spec.ts @@ -23,19 +23,290 @@ describe("Customer Module Service", () => { }) describe("create", () => { - it("should create a customer", async () => { - const customerPromise = service.create({ + it("should create a single customer", async () => { + const customerData = { + company_name: "Acme Corp", first_name: "John", 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({ 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" }), }) ) }) + + 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, + }) + ) + } + }) }) }) diff --git a/packages/customer/src/loaders/container.ts b/packages/customer/src/loaders/container.ts index facacdd7df..9a0c5553b4 100644 --- a/packages/customer/src/loaders/container.ts +++ b/packages/customer/src/loaders/container.ts @@ -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" -import { ModulesSdkTypes } from "@medusajs/types" -import { loadCustomRepositories } from "@medusajs/utils" -import * as defaultServices from "@services" -import { asClass } from "awilix" - -export default async ({ - container, - options, -}: LoaderOptions< - | ModulesSdkTypes.ModuleServiceInitializeOptions - | ModulesSdkTypes.ModuleServiceInitializeCustomDataLayerOptions ->): Promise => { - 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(), - }) -} +export default ModulesSdkUtils.moduleContainerLoaderFactory({ + moduleModels: ModuleModels, + moduleServices: ModuleServices, + moduleRepositories: { BaseRepository: MikroOrmBaseRepository }, +}) diff --git a/packages/customer/src/models/customer-group-customer.ts b/packages/customer/src/models/customer-group-customer.ts index ec6ed1c242..35e3fbfa02 100644 --- a/packages/customer/src/models/customer-group-customer.ts +++ b/packages/customer/src/models/customer-group-customer.ts @@ -12,7 +12,7 @@ import { import Customer from "./customer" 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" }) export default class CustomerGroupCustomer { @@ -21,19 +21,27 @@ export default class CustomerGroupCustomer { @PrimaryKey({ columnType: "text" }) id!: string + @Property({ columnType: "text" }) + customer_id: string + + @Property({ columnType: "text" }) + customer_group_id: string + @ManyToOne({ entity: () => Customer, - fieldName: "customer__id", + fieldName: "customer_id", index: "IDX_customer_group_customer_customer_id", + nullable: true, }) - customer: Customer + customer: Customer | null @ManyToOne({ entity: () => CustomerGroup, fieldName: "customer_group_id", index: "IDX_customer_group_customer_group_id", + nullable: true, }) - customer_group: CustomerGroup + customer_group: CustomerGroup | null @Property({ columnType: "jsonb", nullable: true }) metadata: Record | null = null @@ -53,6 +61,9 @@ export default class CustomerGroupCustomer { }) updated_at: Date + @Property({ columnType: "text", nullable: true }) + created_by: string | null = null + @BeforeCreate() onCreate() { this.id = generateEntityId(this.id, "cusgc") diff --git a/packages/customer/src/models/customer-group.ts b/packages/customer/src/models/customer-group.ts index 9a6e70695f..7d219b6df3 100644 --- a/packages/customer/src/models/customer-group.ts +++ b/packages/customer/src/models/customer-group.ts @@ -34,6 +34,9 @@ export default class CustomerGroup { @Property({ columnType: "jsonb", nullable: true }) metadata: Record | null = null + @Property({ columnType: "text", nullable: true }) + created_by: string | null = null + @Property({ onCreate: () => new Date(), columnType: "timestamptz", diff --git a/packages/customer/src/models/customer.ts b/packages/customer/src/models/customer.ts index 150d9fccc4..31bad011cb 100644 --- a/packages/customer/src/models/customer.ts +++ b/packages/customer/src/models/customer.ts @@ -71,7 +71,7 @@ export default class Customer { metadata: Record | null = null @ManyToMany({ - inversedBy: (group) => group.customers, + mappedBy: "customers", entity: () => CustomerGroup, pivotEntity: () => CustomerGroupCustomer, }) diff --git a/packages/customer/src/repositories/address.ts b/packages/customer/src/repositories/address.ts deleted file mode 100644 index 7d4871bd4a..0000000000 --- a/packages/customer/src/repositories/address.ts +++ /dev/null @@ -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) {} diff --git a/packages/customer/src/repositories/customer-group.ts b/packages/customer/src/repositories/customer-group.ts deleted file mode 100644 index b6d167c164..0000000000 --- a/packages/customer/src/repositories/customer-group.ts +++ /dev/null @@ -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) {} diff --git a/packages/customer/src/repositories/customer.ts b/packages/customer/src/repositories/customer.ts deleted file mode 100644 index e228d8549a..0000000000 --- a/packages/customer/src/repositories/customer.ts +++ /dev/null @@ -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) {} diff --git a/packages/customer/src/repositories/index.ts b/packages/customer/src/repositories/index.ts deleted file mode 100644 index 106e6d94ec..0000000000 --- a/packages/customer/src/repositories/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -export { MikroOrmBaseRepository as BaseRepository } from "@medusajs/utils" -export * from "./address" -export * from "./customer" -export * from "./customer-group" diff --git a/packages/customer/src/services/customer-group-customer.ts b/packages/customer/src/services/customer-group-customer.ts new file mode 100644 index 0000000000..cc61f576af --- /dev/null +++ b/packages/customer/src/services/customer-group-customer.ts @@ -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) { + constructor(container: InjectedDependencies) { + // @ts-ignore + super(...arguments) + } +} diff --git a/packages/customer/src/services/customer-group.ts b/packages/customer/src/services/customer-group.ts index 8b5e4851ec..830e949c58 100644 --- a/packages/customer/src/services/customer-group.ts +++ b/packages/customer/src/services/customer-group.ts @@ -1,7 +1,7 @@ import { DAL } from "@medusajs/types" import { ModulesSdkUtils } from "@medusajs/utils" import { CustomerGroup } from "@models" -import { CreateCustomerGroupDTO, UpdateCustomerGroupDTO } from "@types" +import { CreateCustomerGroupDTO, UpdateCustomerGroupDTO } from "@medusajs/types" type InjectedDependencies = { customerGroupRepository: DAL.RepositoryService diff --git a/packages/customer/src/services/customer-module.ts b/packages/customer/src/services/customer-module.ts index 96d0fde771..777b8c17c1 100644 --- a/packages/customer/src/services/customer-module.ts +++ b/packages/customer/src/services/customer-module.ts @@ -8,7 +8,11 @@ import { CustomerTypes, } from "@medusajs/types" -import { InjectManager, MedusaContext } from "@medusajs/utils" +import { + InjectManager, + InjectTransactionManager, + MedusaContext, +} from "@medusajs/utils" import { joinerConfig } from "../joiner-config" import * as services from "../services" @@ -17,6 +21,7 @@ type InjectedDependencies = { customerService: services.CustomerService addressService: services.AddressService customerGroupService: services.CustomerGroupService + customerGroupCustomerService: services.CustomerGroupCustomerService } export default class CustomerModuleService implements ICustomerModuleService { @@ -24,6 +29,7 @@ export default class CustomerModuleService implements ICustomerModuleService { protected customerService_: services.CustomerService protected addressService_: services.AddressService protected customerGroupService_: services.CustomerGroupService + protected customerGroupCustomerService_: services.CustomerGroupCustomerService constructor( { @@ -31,6 +37,7 @@ export default class CustomerModuleService implements ICustomerModuleService { customerService, addressService, customerGroupService, + customerGroupCustomerService, }: InjectedDependencies, protected readonly moduleDeclaration: InternalModuleDeclaration ) { @@ -38,6 +45,7 @@ export default class CustomerModuleService implements ICustomerModuleService { this.customerService_ = customerService this.addressService_ = addressService this.customerGroupService_ = customerGroupService + this.customerGroupCustomerService_ = customerGroupCustomerService } __joinerConfig(): ModuleJoinerConfig { @@ -64,18 +72,189 @@ export default class CustomerModuleService implements ICustomerModuleService { ) } - @InjectManager("baseRepository_") async create( data: CustomerTypes.CreateCustomerDTO, + sharedContext?: Context + ): Promise + + async create( + data: CustomerTypes.CreateCustomerDTO[], + sharedContext?: Context + ): Promise + + @InjectTransactionManager("baseRepository_") + async create( + dataOrArray: + | CustomerTypes.CreateCustomerDTO + | CustomerTypes.CreateCustomerDTO[], @MedusaContext() sharedContext: Context = {} - ): Promise { - 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( + customer, + { + populate: true, + } + ) + } return await this.baseRepository_.serialize( - customer, + customer[0], { populate: true, } ) } + + @InjectManager("baseRepository_") + async list( + filters: CustomerTypes.FilterableCustomerProps = {}, + config: FindConfig = {}, + @MedusaContext() sharedContext: Context = {} + ) { + const customers = await this.customerService_.list( + filters, + config, + sharedContext + ) + + return await this.baseRepository_.serialize( + customers, + { + populate: true, + } + ) + } + + @InjectManager("baseRepository_") + async listAndCount( + filters: CustomerTypes.FilterableCustomerProps = {}, + config: FindConfig = {}, + @MedusaContext() sharedContext: Context = {} + ): Promise<[CustomerTypes.CustomerDTO[], number]> { + const [customers, count] = await this.customerService_.listAndCount( + filters, + config, + sharedContext + ) + + return [ + await this.baseRepository_.serialize( + customers, + { + populate: true, + } + ), + count, + ] + } + + async createCustomerGroup( + dataOrArrayOfData: CustomerTypes.CreateCustomerGroupDTO, + sharedContext?: Context + ): Promise + + async createCustomerGroup( + dataOrArrayOfData: CustomerTypes.CreateCustomerGroupDTO[], + sharedContext?: Context + ): Promise + + @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( + 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 = {}, + @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 = {}, + @MedusaContext() sharedContext: Context = {} + ): Promise<[CustomerTypes.CustomerGroupDTO[], number]> { + const [groups, count] = await this.customerGroupService_.listAndCount( + filters, + config, + sharedContext + ) + + return [ + await this.baseRepository_.serialize( + groups, + { + populate: true, + } + ), + count, + ] + } } diff --git a/packages/customer/src/services/index.ts b/packages/customer/src/services/index.ts index f77c4c1554..07e39a5c1c 100644 --- a/packages/customer/src/services/index.ts +++ b/packages/customer/src/services/index.ts @@ -2,3 +2,4 @@ export { default as AddressService } from "./address" export { default as CustomerGroupService } from "./customer-group" export { default as CustomerService } from "./customer" export { default as CustomerModuleService } from "./customer-module" +export { default as CustomerGroupCustomerService } from "./customer-group-customer" diff --git a/packages/customer/src/types/customer-group.ts b/packages/customer/src/types/customer-group.ts deleted file mode 100644 index 0245273b03..0000000000 --- a/packages/customer/src/types/customer-group.ts +++ /dev/null @@ -1,11 +0,0 @@ -export type CreateCustomerGroupDTO = { - name: string - customer_ids?: string[] - metadata?: Record | null -} - -export type UpdateCustomerGroupDTO = { - name?: string - customer_ids?: string[] - metadata?: Record | null -} diff --git a/packages/customer/src/types/index.ts b/packages/customer/src/types/index.ts index 8a40d6c8e7..e70f89b103 100644 --- a/packages/customer/src/types/index.ts +++ b/packages/customer/src/types/index.ts @@ -1,7 +1,5 @@ import { Logger } from "@medusajs/types" export * from "./address" -export * from "./customer-group" - export type InitializeModuleInjectableDependencies = { logger?: Logger } diff --git a/packages/types/src/customer/common.ts b/packages/types/src/customer/common.ts index a59c00870b..5aea291312 100644 --- a/packages/types/src/customer/common.ts +++ b/packages/types/src/customer/common.ts @@ -1,5 +1,43 @@ +import { BaseFilterable } from "../dal" +import { OperatorMap } from "../dal/utils" import { AddressDTO } from "../address" +export interface CustomerGroupDTO { + id: string + name: string + customers?: Partial[] + metadata?: Record + created_by?: string | null + deleted_at?: Date | string | null + created_at?: Date | string + updated_at?: Date | string +} + +export interface FilterableCustomerGroupProps + extends BaseFilterable { + id?: string | string[] + name?: OperatorMap + customers?: FilterableCustomerProps | string | string[] + created_by?: string | string[] | null + created_at?: OperatorMap + updated_at?: OperatorMap +} + +export interface FilterableCustomerProps + extends BaseFilterable { + id?: string | string[] + email?: string | string[] | OperatorMap + groups?: FilterableCustomerGroupProps | string | string[] + default_billing_address_id?: string | string[] | null + default_shipping_address_id?: string | string[] | null + company_name?: string | string[] | OperatorMap | null + first_name?: string | string[] | OperatorMap | null + last_name?: string | string[] | OperatorMap | null + created_by?: string | string[] | null + created_at?: OperatorMap + updated_at?: OperatorMap +} + export interface CustomerDTO { id: string email: string @@ -14,11 +52,16 @@ export interface CustomerDTO { phone?: string | null groups?: { id: string }[] metadata?: Record - deleted_at?: Date | string + deleted_at?: Date | string | null created_at?: Date | string updated_at?: Date | string } +export type GroupCustomerPair = { + customer_id: string + customer_group_id: string +} + export type legacy_CustomerDTO = { id: string email: string diff --git a/packages/types/src/customer/mutations.ts b/packages/types/src/customer/mutations.ts index c7127bf270..b01a063a9d 100644 --- a/packages/types/src/customer/mutations.ts +++ b/packages/types/src/customer/mutations.ts @@ -4,6 +4,7 @@ export interface CreateCustomerDTO { last_name?: string email?: string phone?: string + created_by?: string metadata?: Record } @@ -15,3 +16,20 @@ export interface UpdateCustomerDTO { phone?: string metadata?: Record } + +export interface CreateCustomerGroupDTO { + name: string + metadata?: Record | null + created_by?: string +} + +export interface CustomerGroupUpdatableFileds { + name?: string + metadata?: Record | null +} + +export interface UpdateCustomerGroupDTO { + id?: string + name?: string + metadata?: Record | null +} diff --git a/packages/types/src/customer/service.ts b/packages/types/src/customer/service.ts index 82796e6d0f..f478024e46 100644 --- a/packages/types/src/customer/service.ts +++ b/packages/types/src/customer/service.ts @@ -1,8 +1,14 @@ import { FindConfig } from "../common" import { IModuleService } from "../modules-sdk" import { Context } from "../shared-context" -import { CustomerDTO } from "./common" -import { CreateCustomerDTO } from "./mutations" +import { + CustomerDTO, + CustomerGroupDTO, + FilterableCustomerProps, + FilterableCustomerGroupProps, + GroupCustomerPair, +} from "./common" +import { CreateCustomerDTO, CreateCustomerGroupDTO } from "./mutations" export interface ICustomerModuleService extends IModuleService { retrieve( @@ -11,5 +17,54 @@ export interface ICustomerModuleService extends IModuleService { sharedContext?: Context ): Promise + create( + data: CreateCustomerDTO[], + sharedContext?: Context + ): Promise + create(data: CreateCustomerDTO, sharedContext?: Context): Promise + + createCustomerGroup( + data: CreateCustomerGroupDTO[], + sharedContext?: Context + ): Promise + + createCustomerGroup( + data: CreateCustomerGroupDTO, + sharedContext?: Context + ): Promise + + addCustomerToGroup( + groupCustomerPair: GroupCustomerPair, + sharedContext?: Context + ): Promise<{ id: string }> + + addCustomerToGroup( + groupCustomerPairs: GroupCustomerPair[], + sharedContext?: Context + ): Promise<{ id: string }[]> + + list( + filters?: FilterableCustomerProps, + config?: FindConfig, + sharedContext?: Context + ): Promise + + listAndCount( + filters?: FilterableCustomerProps, + config?: FindConfig, + sharedContext?: Context + ): Promise<[CustomerDTO[], number]> + + listCustomerGroups( + filters?: FilterableCustomerGroupProps, + config?: FindConfig, + sharedContext?: Context + ): Promise + + listAndCountCustomerGroups( + filters?: FilterableCustomerGroupProps, + config?: FindConfig, + sharedContext?: Context + ): Promise<[CustomerGroupDTO[], number]> }