feat(customer): add customer addresses (#6224)
**What** - adds methods to create update list customer addresses - removes default_shipping_id and billing id from customer record and moves them to address (better normalization)
This commit is contained in:
@@ -70,24 +70,6 @@
|
||||
"default": "false",
|
||||
"mappedType": "boolean"
|
||||
},
|
||||
"default_shipping_address_id": {
|
||||
"name": "default_shipping_address_id",
|
||||
"type": "text",
|
||||
"unsigned": false,
|
||||
"autoincrement": false,
|
||||
"primary": false,
|
||||
"nullable": true,
|
||||
"mappedType": "text"
|
||||
},
|
||||
"default_billing_address_id": {
|
||||
"name": "default_billing_address_id",
|
||||
"type": "text",
|
||||
"unsigned": false,
|
||||
"autoincrement": false,
|
||||
"primary": false,
|
||||
"nullable": true,
|
||||
"mappedType": "text"
|
||||
},
|
||||
"metadata": {
|
||||
"name": "metadata",
|
||||
"type": "jsonb",
|
||||
@@ -142,24 +124,6 @@
|
||||
"name": "customer",
|
||||
"schema": "public",
|
||||
"indexes": [
|
||||
{
|
||||
"columnNames": [
|
||||
"default_shipping_address_id"
|
||||
],
|
||||
"composite": false,
|
||||
"keyName": "IDX_customer_default_shipping_address_id",
|
||||
"primary": false,
|
||||
"unique": false
|
||||
},
|
||||
{
|
||||
"columnNames": [
|
||||
"default_billing_address_id"
|
||||
],
|
||||
"composite": false,
|
||||
"keyName": "IDX_customer_default_billing_address_id",
|
||||
"primary": false,
|
||||
"unique": false
|
||||
},
|
||||
{
|
||||
"keyName": "customer_pkey",
|
||||
"columnNames": [
|
||||
@@ -171,34 +135,7 @@
|
||||
}
|
||||
],
|
||||
"checks": [],
|
||||
"foreignKeys": {
|
||||
"customer_default_shipping_address_id_foreign": {
|
||||
"constraintName": "customer_default_shipping_address_id_foreign",
|
||||
"columnNames": [
|
||||
"default_shipping_address_id"
|
||||
],
|
||||
"localTableName": "public.customer",
|
||||
"referencedColumnNames": [
|
||||
"id"
|
||||
],
|
||||
"referencedTableName": "public.customer_address",
|
||||
"deleteRule": "set null",
|
||||
"updateRule": "cascade"
|
||||
},
|
||||
"customer_default_billing_address_id_foreign": {
|
||||
"constraintName": "customer_default_billing_address_id_foreign",
|
||||
"columnNames": [
|
||||
"default_billing_address_id"
|
||||
],
|
||||
"localTableName": "public.customer",
|
||||
"referencedColumnNames": [
|
||||
"id"
|
||||
],
|
||||
"referencedTableName": "public.customer_address",
|
||||
"deleteRule": "set null",
|
||||
"updateRule": "cascade"
|
||||
}
|
||||
}
|
||||
"foreignKeys": {}
|
||||
},
|
||||
{
|
||||
"columns": {
|
||||
@@ -211,6 +148,35 @@
|
||||
"nullable": false,
|
||||
"mappedType": "text"
|
||||
},
|
||||
"address_name": {
|
||||
"name": "address_name",
|
||||
"type": "text",
|
||||
"unsigned": false,
|
||||
"autoincrement": false,
|
||||
"primary": false,
|
||||
"nullable": true,
|
||||
"mappedType": "text"
|
||||
},
|
||||
"is_default_shipping": {
|
||||
"name": "is_default_shipping",
|
||||
"type": "boolean",
|
||||
"unsigned": false,
|
||||
"autoincrement": false,
|
||||
"primary": false,
|
||||
"nullable": false,
|
||||
"default": "false",
|
||||
"mappedType": "boolean"
|
||||
},
|
||||
"is_default_billing": {
|
||||
"name": "is_default_billing",
|
||||
"type": "boolean",
|
||||
"unsigned": false,
|
||||
"autoincrement": false,
|
||||
"primary": false,
|
||||
"nullable": false,
|
||||
"default": "false",
|
||||
"mappedType": "boolean"
|
||||
},
|
||||
"customer_id": {
|
||||
"name": "customer_id",
|
||||
"type": "text",
|
||||
@@ -354,6 +320,22 @@
|
||||
"primary": false,
|
||||
"unique": false
|
||||
},
|
||||
{
|
||||
"keyName": "IDX_customer_address_unqiue_customer_billing",
|
||||
"columnNames": [],
|
||||
"composite": false,
|
||||
"primary": false,
|
||||
"unique": false,
|
||||
"expression": "create unique index \"IDX_customer_address_unqiue_customer_billing\" on \"customer_address\" (\"customer_id\") where \"is_default_billing\" = true"
|
||||
},
|
||||
{
|
||||
"keyName": "IDX_customer_address_unqiue_customer_shipping",
|
||||
"columnNames": [],
|
||||
"composite": false,
|
||||
"primary": false,
|
||||
"unique": false,
|
||||
"expression": "create unique index \"IDX_customer_address_unique_customer_shipping\" on \"customer_address\" (\"customer_id\") where \"is_default_shipping\" = true"
|
||||
},
|
||||
{
|
||||
"keyName": "customer_address_pkey",
|
||||
"columnNames": [
|
||||
@@ -376,6 +358,7 @@
|
||||
"id"
|
||||
],
|
||||
"referencedTableName": "public.customer",
|
||||
"deleteRule": "cascade",
|
||||
"updateRule": "cascade"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,35 +4,29 @@ export class Migration20240124154000 extends Migration {
|
||||
async up(): Promise<void> {
|
||||
// Customer table modifications
|
||||
this.addSql(
|
||||
'create table if not exists "customer" ("id" text not null, "company_name" text null, "first_name" text null, "last_name" text null, "email" text null, "phone" text null, "has_account" boolean not null default false, "default_shipping_address_id" text null, "default_billing_address_id" text null, "metadata" jsonb null, "created_at" timestamptz not null default now(), "updated_at" timestamptz not null default now(), "deleted_at" timestamptz null, "created_by" text null, constraint "customer_pkey" primary key ("id"));'
|
||||
'create table if not exists "customer" ("id" text not null, "company_name" text null, "first_name" text null, "last_name" text null, "email" text null, "phone" text null, "has_account" boolean not null default false, "metadata" jsonb null, "created_at" timestamptz not null default now(), "updated_at" timestamptz not null default now(), "deleted_at" timestamptz null, "created_by" text null, constraint "customer_pkey" primary key ("id"));'
|
||||
)
|
||||
this.addSql(
|
||||
'alter table "customer" add column if not exists "company_name" text null;'
|
||||
)
|
||||
this.addSql(
|
||||
'alter table "customer" add column if not exists "default_shipping_address_id" text null;'
|
||||
)
|
||||
this.addSql(
|
||||
'alter table "customer" add column if not exists "default_billing_address_id" text null;'
|
||||
)
|
||||
this.addSql(
|
||||
'alter table "customer" add column if not exists "created_by" text null;'
|
||||
)
|
||||
this.addSql('drop index if exists "IDX_8abe81b9aac151ae60bf507ad1";')
|
||||
this.addSql(
|
||||
'create index if not exists "IDX_customer_default_shipping_address_id" on "customer" ("default_shipping_address_id");'
|
||||
)
|
||||
this.addSql(
|
||||
'create index if not exists "IDX_customer_default_billing_address_id" on "customer" ("default_billing_address_id");'
|
||||
)
|
||||
|
||||
// Customer Address table
|
||||
this.addSql(
|
||||
'create table if not exists "customer_address" ("id" text not null, "customer_id" text not null, "company" text null, "first_name" text null, "last_name" text null, "address_1" text null, "address_2" text null, "city" text null, "country_code" text null, "province" text null, "postal_code" text null, "phone" text null, "metadata" jsonb null, "created_at" timestamptz not null default now(), "updated_at" timestamptz not null default now(), constraint "customer_address_pkey" primary key ("id"));'
|
||||
'create table if not exists "customer_address" ("id" text not null, "customer_id" text not null, "address_name" text null, "is_default_shipping" boolean not null default false, "is_default_billing" boolean not null default false, "company" text null, "first_name" text null, "last_name" text null, "address_1" text null, "address_2" text null, "city" text null, "country_code" text null, "province" text null, "postal_code" text null, "phone" text null, "metadata" jsonb null, "created_at" timestamptz not null default now(), "updated_at" timestamptz not null default now(), constraint "customer_address_pkey" primary key ("id"));'
|
||||
)
|
||||
this.addSql(
|
||||
'create index if not exists "IDX_customer_address_customer_id" on "customer_address" ("customer_id");'
|
||||
)
|
||||
this.addSql(
|
||||
'create unique index "IDX_customer_address_unqiue_customer_billing" on "customer_address" ("customer_id") where "is_default_billing" = true;'
|
||||
)
|
||||
this.addSql(
|
||||
'create unique index "IDX_customer_address_unique_customer_shipping" on "customer_address" ("customer_id") where "is_default_shipping" = true;'
|
||||
)
|
||||
|
||||
// Customer Group table modifications
|
||||
this.addSql(
|
||||
@@ -62,13 +56,7 @@ export class Migration20240124154000 extends Migration {
|
||||
'alter table "customer" drop constraint if exists "FK_8abe81b9aac151ae60bf507ad15";'
|
||||
)
|
||||
this.addSql(
|
||||
'alter table "customer" add constraint "customer_default_shipping_address_id_foreign" foreign key ("default_shipping_address_id") references "customer_address" ("id") on update cascade on delete set null;'
|
||||
)
|
||||
this.addSql(
|
||||
'alter table "customer" add constraint "customer_default_billing_address_id_foreign" foreign key ("default_billing_address_id") references "customer_address" ("id") on update cascade on delete set null;'
|
||||
)
|
||||
this.addSql(
|
||||
'alter table "customer_address" add constraint "customer_address_customer_id_foreign" foreign key ("customer_id") references "customer" ("id") on update cascade;'
|
||||
'alter table "customer_address" add constraint "customer_address_customer_id_foreign" foreign key ("customer_id") references "customer" ("id") on update cascade on delete cascade;'
|
||||
)
|
||||
this.addSql(
|
||||
'alter table "customer_group_customer" add constraint "customer_group_customer_customer_group_id_foreign" foreign key ("customer_group_id") references "customer_group" ("id") on delete cascade;'
|
||||
|
||||
@@ -8,24 +8,46 @@ import {
|
||||
PrimaryKey,
|
||||
Property,
|
||||
ManyToOne,
|
||||
Cascade,
|
||||
Index,
|
||||
} from "@mikro-orm/core"
|
||||
import Customer from "./customer"
|
||||
|
||||
type OptionalAddressProps = DAL.EntityDateColumns // TODO: To be revisited when more clear
|
||||
|
||||
@Entity({ tableName: "customer_address" })
|
||||
@Index({
|
||||
name: "IDX_customer_address_unique_customer_shipping",
|
||||
expression:
|
||||
'create unique index "IDX_customer_address_unique_customer_shipping" on "customer_address" ("customer_id") where "is_default_shipping" = true',
|
||||
})
|
||||
@Index({
|
||||
name: "IDX_customer_address_unique_customer_billing",
|
||||
expression:
|
||||
'create unique index "IDX_customer_address_unique_customer_billing" on "customer_address" ("customer_id") where "is_default_billing" = true',
|
||||
})
|
||||
export default class Address {
|
||||
[OptionalProps]: OptionalAddressProps
|
||||
|
||||
@PrimaryKey({ columnType: "text" })
|
||||
id!: string
|
||||
|
||||
@Property({ columnType: "text", nullable: true })
|
||||
address_name: string | null = null
|
||||
|
||||
@Property({ columnType: "boolean", default: false })
|
||||
is_default_shipping: boolean = false
|
||||
|
||||
@Property({ columnType: "boolean", default: false })
|
||||
is_default_billing: boolean = false
|
||||
|
||||
@Property({ columnType: "text" })
|
||||
customer_id: string
|
||||
|
||||
@ManyToOne(() => Customer, {
|
||||
fieldName: "customer_id",
|
||||
index: "IDX_customer_address_customer_id",
|
||||
cascade: [Cascade.REMOVE, Cascade.PERSIST],
|
||||
})
|
||||
customer: Customer
|
||||
|
||||
|
||||
@@ -22,8 +22,6 @@ import Address from "./address"
|
||||
type OptionalCustomerProps =
|
||||
| "groups"
|
||||
| "addresses"
|
||||
| "default_shipping_address"
|
||||
| "default_billing_address"
|
||||
| DAL.SoftDeletableEntityDateColumns
|
||||
|
||||
@Entity({ tableName: "customer" })
|
||||
@@ -52,26 +50,6 @@ export default class Customer {
|
||||
@Property({ columnType: "boolean", default: false })
|
||||
has_account: boolean = false
|
||||
|
||||
@Index({ name: "IDX_customer_default_shipping_address_id" })
|
||||
@Property({ columnType: "text", nullable: true })
|
||||
default_shipping_address_id: string | null = null
|
||||
|
||||
@ManyToOne(() => Address, {
|
||||
fieldName: "default_shipping_address_id",
|
||||
nullable: true,
|
||||
})
|
||||
default_shipping_address: Address | null
|
||||
|
||||
@Index({ name: "IDX_customer_default_billing_address_id" })
|
||||
@Property({ columnType: "text", nullable: true })
|
||||
default_billing_address_id: string | null = null
|
||||
|
||||
@ManyToOne(() => Address, {
|
||||
fieldName: "default_billing_address_id",
|
||||
nullable: true,
|
||||
})
|
||||
default_billing_address: Address | null
|
||||
|
||||
@Property({ columnType: "jsonb", nullable: true })
|
||||
metadata: Record<string, unknown> | null = null
|
||||
|
||||
|
||||
@@ -415,6 +415,122 @@ export default class CustomerModuleService implements ICustomerModuleService {
|
||||
return { id: groupCustomers[0].id }
|
||||
}
|
||||
|
||||
async addAddresses(
|
||||
addresses: CustomerTypes.CreateCustomerAddressDTO[],
|
||||
sharedContext?: Context
|
||||
): Promise<CustomerTypes.CustomerAddressDTO[]>
|
||||
async addAddresses(
|
||||
address: CustomerTypes.CreateCustomerAddressDTO,
|
||||
sharedContext?: Context
|
||||
): Promise<CustomerTypes.CustomerAddressDTO>
|
||||
|
||||
@InjectTransactionManager("baseRepository_")
|
||||
async addAddresses(
|
||||
data:
|
||||
| CustomerTypes.CreateCustomerAddressDTO
|
||||
| CustomerTypes.CreateCustomerAddressDTO[],
|
||||
@MedusaContext() sharedContext: Context = {}
|
||||
): Promise<
|
||||
CustomerTypes.CustomerAddressDTO | CustomerTypes.CustomerAddressDTO[]
|
||||
> {
|
||||
const addresses = await this.addressService_.create(
|
||||
Array.isArray(data) ? data : [data],
|
||||
sharedContext
|
||||
)
|
||||
|
||||
const serialized = await this.baseRepository_.serialize<
|
||||
CustomerTypes.CustomerAddressDTO[]
|
||||
>(addresses, { populate: true })
|
||||
|
||||
if (Array.isArray(data)) {
|
||||
return serialized
|
||||
}
|
||||
|
||||
return serialized[0]
|
||||
}
|
||||
|
||||
async updateAddress(
|
||||
addressId: string,
|
||||
data: CustomerTypes.UpdateCustomerAddressDTO,
|
||||
sharedContext?: Context
|
||||
): Promise<CustomerTypes.CustomerAddressDTO>
|
||||
async updateAddress(
|
||||
addressIds: string[],
|
||||
data: CustomerTypes.UpdateCustomerAddressDTO,
|
||||
sharedContext?: Context
|
||||
): Promise<CustomerTypes.CustomerAddressDTO[]>
|
||||
async updateAddress(
|
||||
selector: CustomerTypes.FilterableCustomerAddressProps,
|
||||
data: CustomerTypes.UpdateCustomerAddressDTO,
|
||||
sharedContext?: Context
|
||||
): Promise<CustomerTypes.CustomerAddressDTO[]>
|
||||
|
||||
@InjectTransactionManager("baseRepository_")
|
||||
async updateAddress(
|
||||
addressIdOrSelector:
|
||||
| string
|
||||
| string[]
|
||||
| CustomerTypes.FilterableCustomerAddressProps,
|
||||
data: CustomerTypes.UpdateCustomerAddressDTO,
|
||||
@MedusaContext() sharedContext: Context = {}
|
||||
) {
|
||||
let updateData: CustomerTypes.UpdateCustomerAddressDTO[] = []
|
||||
if (isString(addressIdOrSelector)) {
|
||||
updateData = [
|
||||
{
|
||||
id: addressIdOrSelector,
|
||||
...data,
|
||||
},
|
||||
]
|
||||
} else if (Array.isArray(addressIdOrSelector)) {
|
||||
updateData = addressIdOrSelector.map((id) => ({
|
||||
id,
|
||||
...data,
|
||||
}))
|
||||
} else {
|
||||
const ids = await this.addressService_.list(
|
||||
addressIdOrSelector,
|
||||
{ select: ["id"] },
|
||||
sharedContext
|
||||
)
|
||||
updateData = ids.map(({ id }) => ({
|
||||
id,
|
||||
...data,
|
||||
}))
|
||||
}
|
||||
|
||||
const addresses = await this.addressService_.update(
|
||||
updateData,
|
||||
sharedContext
|
||||
)
|
||||
const serialized = await this.baseRepository_.serialize<
|
||||
CustomerTypes.CustomerAddressDTO[]
|
||||
>(addresses, { populate: true })
|
||||
|
||||
if (isString(addressIdOrSelector)) {
|
||||
return serialized[0]
|
||||
}
|
||||
|
||||
return serialized
|
||||
}
|
||||
|
||||
@InjectManager("baseRepository_")
|
||||
async listAddresses(
|
||||
filters?: CustomerTypes.FilterableCustomerAddressProps,
|
||||
config?: FindConfig<CustomerTypes.CustomerAddressDTO>,
|
||||
@MedusaContext() sharedContext: Context = {}
|
||||
): Promise<CustomerTypes.CustomerAddressDTO[]> {
|
||||
const addresses = await this.addressService_.list(
|
||||
filters,
|
||||
config,
|
||||
sharedContext
|
||||
)
|
||||
|
||||
return await this.baseRepository_.serialize<
|
||||
CustomerTypes.CustomerAddressDTO[]
|
||||
>(addresses, { populate: true })
|
||||
}
|
||||
|
||||
async removeCustomerFromGroup(
|
||||
groupCustomerPair: CustomerTypes.GroupCustomerPair,
|
||||
sharedContext?: Context
|
||||
|
||||
Reference in New Issue
Block a user