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:
1 parent
638b47ff70
commit
8c6cc82c5d
9 files changed
+672
-112
No files matched your search
@@ -59,6 +59,51 @@ describe("Customer Module Service", () => {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it("should create address", async () => {
|
||||||
|
const customerData = {
|
||||||
|
company_name: "Acme Corp",
|
||||||
|
first_name: "John",
|
||||||
|
last_name: "Doe",
|
||||||
|
addresses: [
|
||||||
|
{
|
||||||
|
address_1: "Testvej 1",
|
||||||
|
address_2: "Testvej 2",
|
||||||
|
city: "Testby",
|
||||||
|
country_code: "DK",
|
||||||
|
province: "Test",
|
||||||
|
postal_code: "8000",
|
||||||
|
phone: "123456789",
|
||||||
|
metadata: { membership: "gold" },
|
||||||
|
is_default_shipping: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
const customer = await service.create(customerData)
|
||||||
|
|
||||||
|
expect(customer).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
id: expect.any(String),
|
||||||
|
company_name: "Acme Corp",
|
||||||
|
first_name: "John",
|
||||||
|
last_name: "Doe",
|
||||||
|
addresses: expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
id: expect.any(String),
|
||||||
|
address_1: "Testvej 1",
|
||||||
|
address_2: "Testvej 2",
|
||||||
|
city: "Testby",
|
||||||
|
country_code: "DK",
|
||||||
|
province: "Test",
|
||||||
|
postal_code: "8000",
|
||||||
|
phone: "123456789",
|
||||||
|
metadata: expect.objectContaining({ membership: "gold" }),
|
||||||
|
is_default_shipping: true,
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
it("should create multiple customers", async () => {
|
it("should create multiple customers", async () => {
|
||||||
const customersData = [
|
const customersData = [
|
||||||
{
|
{
|
||||||
@@ -419,6 +464,34 @@ describe("Customer Module Service", () => {
|
|||||||
expect(remainingCustomers.length).toBe(0)
|
expect(remainingCustomers.length).toBe(0)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it("should cascade address relationship when deleting customer", async () => {
|
||||||
|
// Creating a customer and an address
|
||||||
|
const customer = await service.create({
|
||||||
|
first_name: "John",
|
||||||
|
last_name: "Doe",
|
||||||
|
})
|
||||||
|
await service.addAddresses({
|
||||||
|
customer_id: customer.id,
|
||||||
|
first_name: "John",
|
||||||
|
last_name: "Doe",
|
||||||
|
postal_code: "10001",
|
||||||
|
country_code: "US",
|
||||||
|
})
|
||||||
|
|
||||||
|
// verify that the address was added
|
||||||
|
const customerWithAddress = await service.retrieve(customer.id, {
|
||||||
|
relations: ["addresses"],
|
||||||
|
})
|
||||||
|
expect(customerWithAddress.addresses?.length).toBe(1)
|
||||||
|
|
||||||
|
await service.delete(customer.id)
|
||||||
|
|
||||||
|
const res = await service.listAddresses({
|
||||||
|
customer_id: customer.id,
|
||||||
|
})
|
||||||
|
expect(res.length).toBe(0)
|
||||||
|
})
|
||||||
|
|
||||||
it("should cascade relationship when deleting customer", async () => {
|
it("should cascade relationship when deleting customer", async () => {
|
||||||
// Creating a customer and a group
|
// Creating a customer and a group
|
||||||
const customer = await service.create({
|
const customer = await service.create({
|
||||||
@@ -505,6 +578,288 @@ describe("Customer Module Service", () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe("addAddresses", () => {
|
||||||
|
it("should add a single address to a customer", async () => {
|
||||||
|
const customer = await service.create({
|
||||||
|
first_name: "John",
|
||||||
|
last_name: "Doe",
|
||||||
|
})
|
||||||
|
const address = await service.addAddresses({
|
||||||
|
customer_id: customer.id,
|
||||||
|
first_name: "John",
|
||||||
|
last_name: "Doe",
|
||||||
|
postal_code: "10001",
|
||||||
|
country_code: "US",
|
||||||
|
})
|
||||||
|
const [customerWithAddress] = await service.list(
|
||||||
|
{ id: customer.id },
|
||||||
|
{ relations: ["addresses"] }
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(customerWithAddress.addresses).toEqual([
|
||||||
|
expect.objectContaining({ id: address.id }),
|
||||||
|
])
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should add multiple addresses to a customer", async () => {
|
||||||
|
const customer = await service.create({
|
||||||
|
first_name: "John",
|
||||||
|
last_name: "Doe",
|
||||||
|
})
|
||||||
|
const addresses = await service.addAddresses([
|
||||||
|
{
|
||||||
|
customer_id: customer.id,
|
||||||
|
first_name: "John",
|
||||||
|
last_name: "Doe",
|
||||||
|
postal_code: "10001",
|
||||||
|
country_code: "US",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
customer_id: customer.id,
|
||||||
|
first_name: "John",
|
||||||
|
last_name: "Doe",
|
||||||
|
postal_code: "10002",
|
||||||
|
country_code: "US",
|
||||||
|
},
|
||||||
|
])
|
||||||
|
const [customerWithAddresses] = await service.list(
|
||||||
|
{ id: customer.id },
|
||||||
|
{ relations: ["addresses"] }
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(customerWithAddresses.addresses).toEqual(
|
||||||
|
expect.arrayContaining([
|
||||||
|
expect.objectContaining({ id: addresses[0].id }),
|
||||||
|
expect.objectContaining({ id: addresses[1].id }),
|
||||||
|
])
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should only be possible to add one default shipping address per customer", async () => {
|
||||||
|
const customer = await service.create({
|
||||||
|
first_name: "John",
|
||||||
|
last_name: "Doe",
|
||||||
|
})
|
||||||
|
await service.addAddresses({
|
||||||
|
customer_id: customer.id,
|
||||||
|
first_name: "John",
|
||||||
|
last_name: "Doe",
|
||||||
|
postal_code: "10001",
|
||||||
|
country_code: "US",
|
||||||
|
is_default_shipping: true,
|
||||||
|
})
|
||||||
|
await service.addAddresses({
|
||||||
|
customer_id: customer.id,
|
||||||
|
first_name: "John",
|
||||||
|
last_name: "Doe",
|
||||||
|
postal_code: "10001",
|
||||||
|
country_code: "US",
|
||||||
|
is_default_shipping: false,
|
||||||
|
})
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
service.addAddresses({
|
||||||
|
customer_id: customer.id,
|
||||||
|
first_name: "John",
|
||||||
|
last_name: "Doe",
|
||||||
|
postal_code: "10002",
|
||||||
|
country_code: "US",
|
||||||
|
is_default_shipping: true,
|
||||||
|
})
|
||||||
|
).rejects.toThrow()
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should only be possible to add one default billing address per customer", async () => {
|
||||||
|
const customer = await service.create({
|
||||||
|
first_name: "John",
|
||||||
|
last_name: "Doe",
|
||||||
|
})
|
||||||
|
await service.addAddresses({
|
||||||
|
customer_id: customer.id,
|
||||||
|
first_name: "John",
|
||||||
|
last_name: "Doe",
|
||||||
|
postal_code: "10001",
|
||||||
|
country_code: "US",
|
||||||
|
is_default_billing: true,
|
||||||
|
})
|
||||||
|
await service.addAddresses({
|
||||||
|
customer_id: customer.id,
|
||||||
|
first_name: "John",
|
||||||
|
last_name: "Doe",
|
||||||
|
postal_code: "10001",
|
||||||
|
country_code: "US",
|
||||||
|
is_default_billing: false,
|
||||||
|
})
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
service.addAddresses({
|
||||||
|
customer_id: customer.id,
|
||||||
|
first_name: "John",
|
||||||
|
last_name: "Doe",
|
||||||
|
postal_code: "10002",
|
||||||
|
country_code: "US",
|
||||||
|
is_default_billing: true,
|
||||||
|
})
|
||||||
|
).rejects.toThrow()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("updateAddresses", () => {
|
||||||
|
it("should update a single address", async () => {
|
||||||
|
const customer = await service.create({
|
||||||
|
first_name: "John",
|
||||||
|
last_name: "Doe",
|
||||||
|
})
|
||||||
|
const address = await service.addAddresses({
|
||||||
|
customer_id: customer.id,
|
||||||
|
address_name: "Home",
|
||||||
|
address_1: "123 Main St",
|
||||||
|
})
|
||||||
|
|
||||||
|
await service.updateAddress(address.id, {
|
||||||
|
address_name: "Work",
|
||||||
|
address_1: "456 Main St",
|
||||||
|
})
|
||||||
|
|
||||||
|
const updatedCustomer = await service.retrieve(customer.id, {
|
||||||
|
select: ["id"],
|
||||||
|
relations: ["addresses"],
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(updatedCustomer.addresses).toEqual([
|
||||||
|
expect.objectContaining({
|
||||||
|
id: address.id,
|
||||||
|
address_name: "Work",
|
||||||
|
address_1: "456 Main St",
|
||||||
|
}),
|
||||||
|
])
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should update multiple addresses", async () => {
|
||||||
|
const customer = await service.create({
|
||||||
|
first_name: "John",
|
||||||
|
last_name: "Doe",
|
||||||
|
})
|
||||||
|
const address1 = await service.addAddresses({
|
||||||
|
customer_id: customer.id,
|
||||||
|
address_name: "Home",
|
||||||
|
address_1: "123 Main St",
|
||||||
|
})
|
||||||
|
const address2 = await service.addAddresses({
|
||||||
|
customer_id: customer.id,
|
||||||
|
address_name: "Work",
|
||||||
|
address_1: "456 Main St",
|
||||||
|
})
|
||||||
|
|
||||||
|
await service.updateAddress(
|
||||||
|
{ customer_id: customer.id },
|
||||||
|
{
|
||||||
|
address_name: "Under Construction",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
const updatedCustomer = await service.retrieve(customer.id, {
|
||||||
|
select: ["id"],
|
||||||
|
relations: ["addresses"],
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(updatedCustomer.addresses).toEqual(
|
||||||
|
expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
id: address1.id,
|
||||||
|
address_name: "Under Construction",
|
||||||
|
}),
|
||||||
|
expect.objectContaining({
|
||||||
|
id: address2.id,
|
||||||
|
address_name: "Under Construction",
|
||||||
|
}),
|
||||||
|
])
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should update multiple addresses with ids", async () => {
|
||||||
|
const customer = await service.create({
|
||||||
|
first_name: "John",
|
||||||
|
last_name: "Doe",
|
||||||
|
})
|
||||||
|
const [address1, address2] = await service.addAddresses([
|
||||||
|
{
|
||||||
|
customer_id: customer.id,
|
||||||
|
address_name: "Home",
|
||||||
|
address_1: "123 Main St",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
customer_id: customer.id,
|
||||||
|
address_name: "Work",
|
||||||
|
address_1: "456 Main St",
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
await service.updateAddress([address1.id, address2.id], {
|
||||||
|
address_name: "Under Construction",
|
||||||
|
})
|
||||||
|
|
||||||
|
const updatedCustomer = await service.retrieve(customer.id, {
|
||||||
|
select: ["id"],
|
||||||
|
relations: ["addresses"],
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(updatedCustomer.addresses).toEqual(
|
||||||
|
expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
id: address1.id,
|
||||||
|
address_name: "Under Construction",
|
||||||
|
}),
|
||||||
|
expect.objectContaining({
|
||||||
|
id: address2.id,
|
||||||
|
address_name: "Under Construction",
|
||||||
|
}),
|
||||||
|
])
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("listAddresses", () => {
|
||||||
|
it("should list all addresses for a customer", async () => {
|
||||||
|
const customer = await service.create({
|
||||||
|
first_name: "John",
|
||||||
|
last_name: "Doe",
|
||||||
|
})
|
||||||
|
const [address1, address2] = await service.addAddresses([
|
||||||
|
{
|
||||||
|
customer_id: customer.id,
|
||||||
|
address_name: "Home",
|
||||||
|
address_1: "123 Main St",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
customer_id: customer.id,
|
||||||
|
address_name: "Work",
|
||||||
|
|
||||||
|
address_1: "456 Main St",
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
const addresses = await service.listAddresses({
|
||||||
|
customer_id: customer.id,
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(addresses).toEqual(
|
||||||
|
expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
id: address1.id,
|
||||||
|
address_name: "Home",
|
||||||
|
address_1: "123 Main St",
|
||||||
|
}),
|
||||||
|
expect.objectContaining({
|
||||||
|
id: address2.id,
|
||||||
|
address_name: "Work",
|
||||||
|
address_1: "456 Main St",
|
||||||
|
}),
|
||||||
|
])
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
describe("removeCustomerFromGroup", () => {
|
describe("removeCustomerFromGroup", () => {
|
||||||
it("should remove a single customer from a group", async () => {
|
it("should remove a single customer from a group", async () => {
|
||||||
// Creating a customer and a group
|
// Creating a customer and a group
|
||||||
|
|||||||
@@ -70,24 +70,6 @@
|
|||||||
"default": "false",
|
"default": "false",
|
||||||
"mappedType": "boolean"
|
"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": {
|
"metadata": {
|
||||||
"name": "metadata",
|
"name": "metadata",
|
||||||
"type": "jsonb",
|
"type": "jsonb",
|
||||||
@@ -142,24 +124,6 @@
|
|||||||
"name": "customer",
|
"name": "customer",
|
||||||
"schema": "public",
|
"schema": "public",
|
||||||
"indexes": [
|
"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",
|
"keyName": "customer_pkey",
|
||||||
"columnNames": [
|
"columnNames": [
|
||||||
@@ -171,34 +135,7 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"checks": [],
|
"checks": [],
|
||||||
"foreignKeys": {
|
"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"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"columns": {
|
"columns": {
|
||||||
@@ -211,6 +148,35 @@
|
|||||||
"nullable": false,
|
"nullable": false,
|
||||||
"mappedType": "text"
|
"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": {
|
"customer_id": {
|
||||||
"name": "customer_id",
|
"name": "customer_id",
|
||||||
"type": "text",
|
"type": "text",
|
||||||
@@ -354,6 +320,22 @@
|
|||||||
"primary": false,
|
"primary": false,
|
||||||
"unique": 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",
|
"keyName": "customer_address_pkey",
|
||||||
"columnNames": [
|
"columnNames": [
|
||||||
@@ -376,6 +358,7 @@
|
|||||||
"id"
|
"id"
|
||||||
],
|
],
|
||||||
"referencedTableName": "public.customer",
|
"referencedTableName": "public.customer",
|
||||||
|
"deleteRule": "cascade",
|
||||||
"updateRule": "cascade"
|
"updateRule": "cascade"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,35 +4,29 @@ export class Migration20240124154000 extends Migration {
|
|||||||
async up(): Promise<void> {
|
async up(): Promise<void> {
|
||||||
// Customer table modifications
|
// Customer table modifications
|
||||||
this.addSql(
|
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(
|
this.addSql(
|
||||||
'alter table "customer" add column if not exists "company_name" text null;'
|
'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(
|
this.addSql(
|
||||||
'alter table "customer" add column if not exists "created_by" text null;'
|
'alter table "customer" add column if not exists "created_by" text null;'
|
||||||
)
|
)
|
||||||
this.addSql('drop index if exists "IDX_8abe81b9aac151ae60bf507ad1";')
|
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
|
// Customer Address table
|
||||||
this.addSql(
|
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(
|
this.addSql(
|
||||||
'create index if not exists "IDX_customer_address_customer_id" on "customer_address" ("customer_id");'
|
'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
|
// Customer Group table modifications
|
||||||
this.addSql(
|
this.addSql(
|
||||||
@@ -62,13 +56,7 @@ export class Migration20240124154000 extends Migration {
|
|||||||
'alter table "customer" drop constraint if exists "FK_8abe81b9aac151ae60bf507ad15";'
|
'alter table "customer" drop constraint if exists "FK_8abe81b9aac151ae60bf507ad15";'
|
||||||
)
|
)
|
||||||
this.addSql(
|
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;'
|
'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" 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;'
|
|
||||||
)
|
)
|
||||||
this.addSql(
|
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;'
|
'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,
|
PrimaryKey,
|
||||||
Property,
|
Property,
|
||||||
ManyToOne,
|
ManyToOne,
|
||||||
|
Cascade,
|
||||||
|
Index,
|
||||||
} from "@mikro-orm/core"
|
} from "@mikro-orm/core"
|
||||||
import Customer from "./customer"
|
import Customer from "./customer"
|
||||||
|
|
||||||
type OptionalAddressProps = DAL.EntityDateColumns // TODO: To be revisited when more clear
|
type OptionalAddressProps = DAL.EntityDateColumns // TODO: To be revisited when more clear
|
||||||
|
|
||||||
@Entity({ tableName: "customer_address" })
|
@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 {
|
export default class Address {
|
||||||
[OptionalProps]: OptionalAddressProps
|
[OptionalProps]: OptionalAddressProps
|
||||||
|
|
||||||
@PrimaryKey({ columnType: "text" })
|
@PrimaryKey({ columnType: "text" })
|
||||||
id!: string
|
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" })
|
@Property({ columnType: "text" })
|
||||||
customer_id: string
|
customer_id: string
|
||||||
|
|
||||||
@ManyToOne(() => Customer, {
|
@ManyToOne(() => Customer, {
|
||||||
fieldName: "customer_id",
|
fieldName: "customer_id",
|
||||||
index: "IDX_customer_address_customer_id",
|
index: "IDX_customer_address_customer_id",
|
||||||
|
cascade: [Cascade.REMOVE, Cascade.PERSIST],
|
||||||
})
|
})
|
||||||
customer: Customer
|
customer: Customer
|
||||||
|
|
||||||
|
|||||||
@@ -22,8 +22,6 @@ import Address from "./address"
|
|||||||
type OptionalCustomerProps =
|
type OptionalCustomerProps =
|
||||||
| "groups"
|
| "groups"
|
||||||
| "addresses"
|
| "addresses"
|
||||||
| "default_shipping_address"
|
|
||||||
| "default_billing_address"
|
|
||||||
| DAL.SoftDeletableEntityDateColumns
|
| DAL.SoftDeletableEntityDateColumns
|
||||||
|
|
||||||
@Entity({ tableName: "customer" })
|
@Entity({ tableName: "customer" })
|
||||||
@@ -52,26 +50,6 @@ export default class Customer {
|
|||||||
@Property({ columnType: "boolean", default: false })
|
@Property({ columnType: "boolean", default: false })
|
||||||
has_account: boolean = 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 })
|
@Property({ columnType: "jsonb", nullable: true })
|
||||||
metadata: Record<string, unknown> | null = null
|
metadata: Record<string, unknown> | null = null
|
||||||
|
|
||||||
|
|||||||
@@ -415,6 +415,122 @@ export default class CustomerModuleService implements ICustomerModuleService {
|
|||||||
return { id: groupCustomers[0].id }
|
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(
|
async removeCustomerFromGroup(
|
||||||
groupCustomerPair: CustomerTypes.GroupCustomerPair,
|
groupCustomerPair: CustomerTypes.GroupCustomerPair,
|
||||||
sharedContext?: Context
|
sharedContext?: Context
|
||||||
|
|||||||
@@ -1,6 +1,50 @@
|
|||||||
|
import { AddressDTO } from "../address"
|
||||||
import { BaseFilterable } from "../dal"
|
import { BaseFilterable } from "../dal"
|
||||||
import { OperatorMap } from "../dal/utils"
|
import { OperatorMap } from "../dal/utils"
|
||||||
import { AddressDTO } from "../address"
|
|
||||||
|
export interface CustomerAddressDTO {
|
||||||
|
id: string
|
||||||
|
address_name?: string
|
||||||
|
is_default_shipping: boolean
|
||||||
|
is_default_billing: boolean
|
||||||
|
customer_id: string
|
||||||
|
company?: string
|
||||||
|
first_name?: string
|
||||||
|
last_name?: string
|
||||||
|
address_1?: string
|
||||||
|
address_2?: string
|
||||||
|
city?: string
|
||||||
|
country_code?: string
|
||||||
|
province?: string
|
||||||
|
postal_code?: string
|
||||||
|
phone?: string
|
||||||
|
metadata?: Record<string, unknown>
|
||||||
|
created_at: string
|
||||||
|
updated_at: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FilterableCustomerAddressProps
|
||||||
|
extends BaseFilterable<FilterableCustomerAddressProps> {
|
||||||
|
id?: string | string[]
|
||||||
|
address_name?: string | OperatorMap<string>
|
||||||
|
is_default_shipping?: boolean | OperatorMap<boolean>
|
||||||
|
is_default_billing?: boolean | OperatorMap<boolean>
|
||||||
|
customer_id?: string | string[]
|
||||||
|
customer?: FilterableCustomerProps | string | string[]
|
||||||
|
company?: string | OperatorMap<string>
|
||||||
|
first_name?: string | OperatorMap<string>
|
||||||
|
last_name?: string | OperatorMap<string>
|
||||||
|
address_1?: string | OperatorMap<string>
|
||||||
|
address_2?: string | OperatorMap<string>
|
||||||
|
city?: string | OperatorMap<string>
|
||||||
|
country_code?: string | OperatorMap<string>
|
||||||
|
province?: string | OperatorMap<string>
|
||||||
|
postal_code?: string | OperatorMap<string>
|
||||||
|
phone?: string | OperatorMap<string>
|
||||||
|
metadata?: Record<string, unknown> | OperatorMap<Record<string, unknown>>
|
||||||
|
created_at?: OperatorMap<string>
|
||||||
|
updated_at?: OperatorMap<string>
|
||||||
|
}
|
||||||
|
|
||||||
export interface FilterableCustomerGroupProps
|
export interface FilterableCustomerGroupProps
|
||||||
extends BaseFilterable<FilterableCustomerGroupProps> {
|
extends BaseFilterable<FilterableCustomerGroupProps> {
|
||||||
@@ -69,9 +113,7 @@ export interface CustomerDTO {
|
|||||||
company_name?: string | null
|
company_name?: string | null
|
||||||
first_name?: string | null
|
first_name?: string | null
|
||||||
last_name?: string | null
|
last_name?: string | null
|
||||||
default_billing_address?: AddressDTO
|
addresses?: CustomerAddressDTO[]
|
||||||
default_shipping_address?: AddressDTO
|
|
||||||
addresses?: AddressDTO[]
|
|
||||||
phone?: string | null
|
phone?: string | null
|
||||||
groups?: { id: string }[]
|
groups?: { id: string }[]
|
||||||
metadata?: Record<string, unknown>
|
metadata?: Record<string, unknown>
|
||||||
|
|||||||
@@ -1,3 +1,40 @@
|
|||||||
|
export interface CreateCustomerAddressDTO {
|
||||||
|
address_name?: string
|
||||||
|
is_default_shipping?: boolean
|
||||||
|
is_default_billing?: boolean
|
||||||
|
customer_id: string
|
||||||
|
company?: string
|
||||||
|
first_name?: string
|
||||||
|
last_name?: string
|
||||||
|
address_1?: string
|
||||||
|
address_2?: string
|
||||||
|
city?: string
|
||||||
|
country_code?: string
|
||||||
|
province?: string
|
||||||
|
postal_code?: string
|
||||||
|
phone?: string
|
||||||
|
metadata?: Record<string, unknown>
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UpdateCustomerAddressDTO {
|
||||||
|
id?: string
|
||||||
|
address_name?: string
|
||||||
|
is_default_shipping?: boolean
|
||||||
|
is_default_billing?: boolean
|
||||||
|
customer_id?: string
|
||||||
|
company?: string
|
||||||
|
first_name?: string
|
||||||
|
last_name?: string
|
||||||
|
address_1?: string
|
||||||
|
address_2?: string
|
||||||
|
city?: string
|
||||||
|
country_code?: string
|
||||||
|
province?: string
|
||||||
|
postal_code?: string
|
||||||
|
phone?: string
|
||||||
|
metadata?: Record<string, unknown>
|
||||||
|
}
|
||||||
|
|
||||||
export interface CreateCustomerDTO {
|
export interface CreateCustomerDTO {
|
||||||
company_name?: string
|
company_name?: string
|
||||||
first_name?: string
|
first_name?: string
|
||||||
@@ -5,6 +42,7 @@ export interface CreateCustomerDTO {
|
|||||||
email?: string
|
email?: string
|
||||||
phone?: string
|
phone?: string
|
||||||
created_by?: string
|
created_by?: string
|
||||||
|
addresses?: Omit<CreateCustomerAddressDTO, "customer_id">[]
|
||||||
metadata?: Record<string, unknown>
|
metadata?: Record<string, unknown>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,8 +10,15 @@ import {
|
|||||||
FilterableCustomerProps,
|
FilterableCustomerProps,
|
||||||
FilterableCustomerGroupProps,
|
FilterableCustomerGroupProps,
|
||||||
GroupCustomerPair,
|
GroupCustomerPair,
|
||||||
|
FilterableCustomerAddressProps,
|
||||||
|
CustomerAddressDTO,
|
||||||
} from "./common"
|
} from "./common"
|
||||||
import { CreateCustomerDTO, CreateCustomerGroupDTO } from "./mutations"
|
import {
|
||||||
|
CreateCustomerAddressDTO,
|
||||||
|
CreateCustomerDTO,
|
||||||
|
CreateCustomerGroupDTO,
|
||||||
|
UpdateCustomerAddressDTO,
|
||||||
|
} from "./mutations"
|
||||||
|
|
||||||
export interface ICustomerModuleService extends IModuleService {
|
export interface ICustomerModuleService extends IModuleService {
|
||||||
retrieve(
|
retrieve(
|
||||||
@@ -110,6 +117,37 @@ export interface ICustomerModuleService extends IModuleService {
|
|||||||
sharedContext?: Context
|
sharedContext?: Context
|
||||||
): Promise<void>
|
): Promise<void>
|
||||||
|
|
||||||
|
addAddresses(
|
||||||
|
addresses: CreateCustomerAddressDTO[],
|
||||||
|
sharedContext?: Context
|
||||||
|
): Promise<CustomerAddressDTO[]>
|
||||||
|
addAddresses(
|
||||||
|
address: CreateCustomerAddressDTO,
|
||||||
|
sharedContext?: Context
|
||||||
|
): Promise<CustomerAddressDTO>
|
||||||
|
|
||||||
|
updateAddress(
|
||||||
|
addressId: string,
|
||||||
|
data: UpdateCustomerAddressDTO,
|
||||||
|
sharedContext?: Context
|
||||||
|
): Promise<CustomerAddressDTO>
|
||||||
|
updateAddress(
|
||||||
|
addressIds: string[],
|
||||||
|
data: UpdateCustomerAddressDTO,
|
||||||
|
sharedContext?: Context
|
||||||
|
): Promise<CustomerAddressDTO[]>
|
||||||
|
updateAddress(
|
||||||
|
selector: FilterableCustomerAddressProps,
|
||||||
|
data: UpdateCustomerAddressDTO,
|
||||||
|
sharedContext?: Context
|
||||||
|
): Promise<CustomerAddressDTO[]>
|
||||||
|
|
||||||
|
listAddresses(
|
||||||
|
filters?: FilterableCustomerAddressProps,
|
||||||
|
config?: FindConfig<CustomerAddressDTO>,
|
||||||
|
sharedContext?: Context
|
||||||
|
): Promise<CustomerAddressDTO[]>
|
||||||
|
|
||||||
listCustomerGroupRelations(
|
listCustomerGroupRelations(
|
||||||
filters?: FilterableCustomerGroupCustomerProps,
|
filters?: FilterableCustomerGroupCustomerProps,
|
||||||
config?: FindConfig<CustomerGroupCustomerDTO>,
|
config?: FindConfig<CustomerGroupCustomerDTO>,
|
||||||
|
|||||||
Reference in new issue
Block a user