Feat(order): order changes (#6614)
This is a PR to keep them relatively small. Very likely changes, validations and other features will be added. What: Basic methods to cancel, confirm or decline order changes Apply order changes to modify and create a new version of an order Things related to calculation, Order and Item totals are not covered in this PR. Properties won't match with definition, etc. Co-authored-by: Adrien de Peretti <25098370+adrien2p@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
e4acde1aa2
commit
43399c8d0d
@@ -107,6 +107,14 @@ moduleIntegrationTestRunner({
|
||||
],
|
||||
},
|
||||
],
|
||||
transactions: [
|
||||
{
|
||||
amount: 58,
|
||||
currency_code: "USD",
|
||||
reference: "payment",
|
||||
reference_id: "pay_123",
|
||||
},
|
||||
],
|
||||
currency_code: "usd",
|
||||
customer_id: "joe",
|
||||
} as CreateOrderDTO
|
||||
@@ -114,6 +122,9 @@ moduleIntegrationTestRunner({
|
||||
const expectation = expect.objectContaining({
|
||||
id: expect.stringContaining("order_"),
|
||||
version: 1,
|
||||
summary: expect.objectContaining({
|
||||
total: expect.any(Number),
|
||||
}),
|
||||
shipping_address: expect.objectContaining({
|
||||
id: expect.stringContaining("ordaddr_"),
|
||||
}),
|
||||
@@ -193,6 +204,7 @@ moduleIntegrationTestRunner({
|
||||
"id",
|
||||
"version",
|
||||
"items.id",
|
||||
"summary",
|
||||
"items.quantity",
|
||||
"items.detail.id",
|
||||
"items.detail.version",
|
||||
@@ -223,20 +235,93 @@ moduleIntegrationTestRunner({
|
||||
expect(getOrder).toEqual(expectation)
|
||||
})
|
||||
|
||||
it.skip("should transform where clause to match the db schema and return the order", async function () {
|
||||
it("should return order transactions", async function () {
|
||||
const createdOrder = await service.create(input)
|
||||
const getOrder = await service.retrieve(createdOrder.id, {
|
||||
select: [
|
||||
"id",
|
||||
"version",
|
||||
"items.id",
|
||||
"items.detail.version",
|
||||
"items.quantity",
|
||||
"transactions.amount",
|
||||
"transactions.reference",
|
||||
"transactions.reference_id",
|
||||
],
|
||||
relations: ["items"],
|
||||
relations: ["transactions"],
|
||||
})
|
||||
|
||||
expect(getOrder).toEqual(expectation)
|
||||
expect(getOrder).toEqual(
|
||||
expect.objectContaining({
|
||||
id: createdOrder.id,
|
||||
transactions: [
|
||||
expect.objectContaining({
|
||||
amount: 58,
|
||||
reference: "payment",
|
||||
reference_id: "pay_123",
|
||||
}),
|
||||
],
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
it("should transform where clause to match the db schema and return the order", async function () {
|
||||
await service.create(input)
|
||||
const orders = await service.list(
|
||||
{
|
||||
items: {
|
||||
quantity: 2,
|
||||
},
|
||||
},
|
||||
{
|
||||
select: ["id"],
|
||||
relations: ["items"],
|
||||
take: null,
|
||||
}
|
||||
)
|
||||
expect(orders.length).toEqual(1)
|
||||
|
||||
const orders2 = await service.list(
|
||||
{
|
||||
items: {
|
||||
quantity: 5,
|
||||
},
|
||||
},
|
||||
{
|
||||
select: ["items.quantity"],
|
||||
relations: ["items"],
|
||||
take: null,
|
||||
}
|
||||
)
|
||||
expect(orders2.length).toEqual(0)
|
||||
|
||||
const orders3 = await service.list(
|
||||
{
|
||||
items: {
|
||||
detail: {
|
||||
shipped_quantity: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
select: ["id"],
|
||||
relations: ["items.detail"],
|
||||
take: null,
|
||||
}
|
||||
)
|
||||
expect(orders3.length).toEqual(1)
|
||||
|
||||
const orders4 = await service.list(
|
||||
{
|
||||
items: {
|
||||
detail: {
|
||||
shipped_quantity: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
select: ["id"],
|
||||
relations: ["items.detail"],
|
||||
take: null,
|
||||
}
|
||||
)
|
||||
expect(orders4.length).toEqual(0)
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
557
packages/order/integration-tests/__tests__/order-edit.ts
Normal file
557
packages/order/integration-tests/__tests__/order-edit.ts
Normal file
@@ -0,0 +1,557 @@
|
||||
import { Modules } from "@medusajs/modules-sdk"
|
||||
import { CreateOrderDTO, IOrderModuleService } from "@medusajs/types"
|
||||
import { SuiteOptions, moduleIntegrationTestRunner } from "medusa-test-utils"
|
||||
import { ChangeActionType } from "../../src/utils"
|
||||
|
||||
jest.setTimeout(100000)
|
||||
|
||||
moduleIntegrationTestRunner({
|
||||
debug: 0,
|
||||
moduleName: Modules.ORDER,
|
||||
testSuite: ({ service }: SuiteOptions<IOrderModuleService>) => {
|
||||
describe("Order Module Service - Order Edits", () => {
|
||||
const input = {
|
||||
email: "foo@bar.com",
|
||||
items: [
|
||||
{
|
||||
title: "Item 1",
|
||||
subtitle: "Subtitle 1",
|
||||
thumbnail: "thumbnail1.jpg",
|
||||
quantity: 1,
|
||||
product_id: "product1",
|
||||
product_title: "Product 1",
|
||||
product_description: "Description 1",
|
||||
product_subtitle: "Product Subtitle 1",
|
||||
product_type: "Type 1",
|
||||
product_collection: "Collection 1",
|
||||
product_handle: "handle1",
|
||||
variant_id: "variant1",
|
||||
variant_sku: "SKU1",
|
||||
variant_barcode: "Barcode1",
|
||||
variant_title: "Variant 1",
|
||||
variant_option_values: {
|
||||
color: "Red",
|
||||
size: "Large",
|
||||
},
|
||||
requires_shipping: true,
|
||||
is_discountable: true,
|
||||
is_tax_inclusive: true,
|
||||
compare_at_unit_price: 10,
|
||||
unit_price: 8,
|
||||
tax_lines: [
|
||||
{
|
||||
description: "Tax 1",
|
||||
tax_rate_id: "tax_usa",
|
||||
code: "code",
|
||||
rate: 0.1,
|
||||
provider_id: "taxify_master",
|
||||
},
|
||||
],
|
||||
adjustments: [
|
||||
{
|
||||
code: "VIP_10",
|
||||
amount: 10,
|
||||
description: "VIP discount",
|
||||
promotion_id: "prom_123",
|
||||
provider_id: "coupon_kings",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "Item 2",
|
||||
quantity: 2,
|
||||
unit_price: 5,
|
||||
},
|
||||
{
|
||||
title: "Item 3",
|
||||
quantity: 1,
|
||||
unit_price: 30,
|
||||
},
|
||||
],
|
||||
sales_channel_id: "test",
|
||||
shipping_address: {
|
||||
first_name: "Test",
|
||||
last_name: "Test",
|
||||
address_1: "Test",
|
||||
city: "Test",
|
||||
country_code: "US",
|
||||
postal_code: "12345",
|
||||
phone: "12345",
|
||||
},
|
||||
billing_address: {
|
||||
first_name: "Test",
|
||||
last_name: "Test",
|
||||
address_1: "Test",
|
||||
city: "Test",
|
||||
country_code: "US",
|
||||
postal_code: "12345",
|
||||
},
|
||||
shipping_methods: [
|
||||
{
|
||||
name: "Test shipping method",
|
||||
amount: 10,
|
||||
data: {},
|
||||
tax_lines: [
|
||||
{
|
||||
description: "shipping Tax 1",
|
||||
tax_rate_id: "tax_usa_shipping",
|
||||
code: "code",
|
||||
rate: 10,
|
||||
},
|
||||
],
|
||||
adjustments: [
|
||||
{
|
||||
code: "VIP_10",
|
||||
amount: 1,
|
||||
description: "VIP discount",
|
||||
promotion_id: "prom_123",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
transactions: [
|
||||
{
|
||||
amount: 58,
|
||||
currency_code: "USD",
|
||||
reference: "payment",
|
||||
reference_id: "pay_123",
|
||||
},
|
||||
],
|
||||
currency_code: "usd",
|
||||
customer_id: "joe",
|
||||
} as CreateOrderDTO
|
||||
|
||||
it("should change an order by adding actions to it", async function () {
|
||||
const createdOrder = await service.create(input)
|
||||
|
||||
await service.addOrderAction([
|
||||
{
|
||||
action: ChangeActionType.ITEM_ADD,
|
||||
order_id: createdOrder.id,
|
||||
version: createdOrder.version,
|
||||
internal_note: "adding an item",
|
||||
reference: "order_line_item",
|
||||
reference_id: createdOrder.items[0].id,
|
||||
amount:
|
||||
createdOrder.items[0].unit_price * createdOrder.items[0].quantity,
|
||||
details: {
|
||||
quantity: 1,
|
||||
},
|
||||
},
|
||||
{
|
||||
action: ChangeActionType.ITEM_ADD,
|
||||
order_id: createdOrder.id,
|
||||
version: createdOrder.version,
|
||||
reference: "order_line_item",
|
||||
reference_id: createdOrder.items[1].id,
|
||||
amount:
|
||||
createdOrder.items[1].unit_price * createdOrder.items[1].quantity,
|
||||
details: {
|
||||
quantity: 3,
|
||||
},
|
||||
},
|
||||
{
|
||||
action: ChangeActionType.FULFILL_ITEM,
|
||||
order_id: createdOrder.id,
|
||||
version: createdOrder.version,
|
||||
reference: "fullfilment",
|
||||
reference_id: "fulfill_123",
|
||||
details: {
|
||||
reference_id: createdOrder.items[2].id,
|
||||
quantity: 1,
|
||||
},
|
||||
},
|
||||
{
|
||||
action: ChangeActionType.SHIP_ITEM,
|
||||
order_id: createdOrder.id,
|
||||
version: createdOrder.version,
|
||||
reference: "fullfilment",
|
||||
reference_id: "shipping_123",
|
||||
details: {
|
||||
reference_id: createdOrder.items[2].id,
|
||||
quantity: 1,
|
||||
},
|
||||
},
|
||||
{
|
||||
action: ChangeActionType.RETURN_ITEM,
|
||||
order_id: createdOrder.id,
|
||||
version: createdOrder.version,
|
||||
internal_note: "client has called and wants to return an item",
|
||||
reference: "return",
|
||||
reference_id: "return_123",
|
||||
details: {
|
||||
reference_id: createdOrder.items[2].id,
|
||||
quantity: 1,
|
||||
},
|
||||
},
|
||||
{
|
||||
action: ChangeActionType.RECEIVE_DAMAGED_RETURN_ITEM,
|
||||
order_id: createdOrder.id,
|
||||
version: createdOrder.version,
|
||||
internal_note: "Item broken",
|
||||
reference: "return",
|
||||
reference_id: "return_123",
|
||||
details: {
|
||||
reference_id: createdOrder.items[2].id,
|
||||
quantity: 1,
|
||||
},
|
||||
},
|
||||
])
|
||||
|
||||
await service.applyPendingOrderActions(createdOrder.id)
|
||||
|
||||
const finalOrder = await service.retrieve(createdOrder.id, {
|
||||
select: [
|
||||
"id",
|
||||
"version",
|
||||
"items.detail",
|
||||
"summary",
|
||||
"shipping_methods",
|
||||
"transactions",
|
||||
],
|
||||
relations: ["items", "shipping_methods", "transactions"],
|
||||
})
|
||||
|
||||
expect(createdOrder.items).toEqual([
|
||||
expect.objectContaining({
|
||||
title: "Item 1",
|
||||
unit_price: 8,
|
||||
quantity: 1,
|
||||
detail: expect.objectContaining({
|
||||
version: 1,
|
||||
quantity: 1,
|
||||
fulfilled_quantity: 0,
|
||||
shipped_quantity: 0,
|
||||
return_requested_quantity: 0,
|
||||
return_received_quantity: 0,
|
||||
return_dismissed_quantity: 0,
|
||||
written_off_quantity: 0,
|
||||
}),
|
||||
}),
|
||||
expect.objectContaining({
|
||||
title: "Item 2",
|
||||
compare_at_unit_price: null,
|
||||
unit_price: 5,
|
||||
quantity: 2,
|
||||
}),
|
||||
expect.objectContaining({
|
||||
title: "Item 3",
|
||||
unit_price: 30,
|
||||
quantity: 1,
|
||||
detail: expect.objectContaining({
|
||||
version: 1,
|
||||
quantity: 1,
|
||||
fulfilled_quantity: 0,
|
||||
shipped_quantity: 0,
|
||||
return_requested_quantity: 0,
|
||||
return_received_quantity: 0,
|
||||
return_dismissed_quantity: 0,
|
||||
written_off_quantity: 0,
|
||||
}),
|
||||
}),
|
||||
])
|
||||
|
||||
expect(finalOrder).toEqual(
|
||||
expect.objectContaining({
|
||||
version: 1,
|
||||
})
|
||||
)
|
||||
expect(finalOrder.items).toEqual([
|
||||
expect.objectContaining({
|
||||
title: "Item 1",
|
||||
subtitle: "Subtitle 1",
|
||||
thumbnail: "thumbnail1.jpg",
|
||||
variant_id: "variant1",
|
||||
product_id: "product1",
|
||||
product_title: "Product 1",
|
||||
product_description: "Description 1",
|
||||
product_subtitle: "Product Subtitle 1",
|
||||
product_type: "Type 1",
|
||||
product_collection: "Collection 1",
|
||||
product_handle: "handle1",
|
||||
variant_sku: "SKU1",
|
||||
variant_barcode: "Barcode1",
|
||||
variant_title: "Variant 1",
|
||||
variant_option_values: { size: "Large", color: "Red" },
|
||||
requires_shipping: true,
|
||||
is_discountable: true,
|
||||
is_tax_inclusive: true,
|
||||
compare_at_unit_price: 10,
|
||||
unit_price: 8,
|
||||
quantity: 2,
|
||||
detail: expect.objectContaining({
|
||||
version: 1,
|
||||
quantity: 2,
|
||||
fulfilled_quantity: 0,
|
||||
shipped_quantity: 0,
|
||||
return_requested_quantity: 0,
|
||||
return_received_quantity: 0,
|
||||
return_dismissed_quantity: 0,
|
||||
written_off_quantity: 0,
|
||||
}),
|
||||
}),
|
||||
expect.objectContaining({
|
||||
title: "Item 2",
|
||||
compare_at_unit_price: null,
|
||||
unit_price: 5,
|
||||
quantity: 5,
|
||||
detail: expect.objectContaining({
|
||||
version: 1,
|
||||
quantity: 5,
|
||||
fulfilled_quantity: 0,
|
||||
shipped_quantity: 0,
|
||||
return_requested_quantity: 0,
|
||||
return_received_quantity: 0,
|
||||
return_dismissed_quantity: 0,
|
||||
written_off_quantity: 0,
|
||||
}),
|
||||
}),
|
||||
expect.objectContaining({
|
||||
title: "Item 3",
|
||||
unit_price: 30,
|
||||
quantity: 1,
|
||||
detail: expect.objectContaining({
|
||||
version: 1,
|
||||
quantity: 1,
|
||||
fulfilled_quantity: 1,
|
||||
shipped_quantity: 1,
|
||||
return_requested_quantity: 0,
|
||||
return_received_quantity: 0,
|
||||
return_dismissed_quantity: 1,
|
||||
written_off_quantity: 0,
|
||||
}),
|
||||
}),
|
||||
])
|
||||
})
|
||||
|
||||
it("should create an order change, add actions to it and confirm the changes.", async function () {
|
||||
const createdOrder = await service.create(input)
|
||||
|
||||
const orderChange = await service.createOrderChange({
|
||||
order_id: createdOrder.id,
|
||||
description: "changing the order",
|
||||
internal_note: "changing the order to version 2",
|
||||
created_by: "user_123",
|
||||
actions: [
|
||||
{
|
||||
action: ChangeActionType.ITEM_ADD,
|
||||
reference: "order_line_item",
|
||||
reference_id: createdOrder.items[0].id,
|
||||
amount:
|
||||
createdOrder.items[0].unit_price *
|
||||
createdOrder.items[0].quantity,
|
||||
details: {
|
||||
quantity: 1,
|
||||
},
|
||||
},
|
||||
{
|
||||
action: ChangeActionType.ITEM_ADD,
|
||||
reference: "order_line_item",
|
||||
reference_id: createdOrder.items[1].id,
|
||||
amount:
|
||||
createdOrder.items[1].unit_price *
|
||||
createdOrder.items[1].quantity,
|
||||
details: {
|
||||
quantity: 3,
|
||||
},
|
||||
},
|
||||
{
|
||||
action: ChangeActionType.FULFILL_ITEM,
|
||||
reference: "fullfilment",
|
||||
reference_id: "fulfill_123",
|
||||
details: {
|
||||
reference_id: createdOrder.items[2].id,
|
||||
quantity: 1,
|
||||
},
|
||||
},
|
||||
{
|
||||
action: ChangeActionType.SHIP_ITEM,
|
||||
reference: "fullfilment",
|
||||
reference_id: "shipping_123",
|
||||
details: {
|
||||
reference_id: createdOrder.items[2].id,
|
||||
quantity: 1,
|
||||
},
|
||||
},
|
||||
{
|
||||
action: ChangeActionType.RETURN_ITEM,
|
||||
reference: "return",
|
||||
reference_id: "return_123",
|
||||
details: {
|
||||
reference_id: createdOrder.items[2].id,
|
||||
quantity: 1,
|
||||
},
|
||||
},
|
||||
{
|
||||
action: ChangeActionType.RECEIVE_DAMAGED_RETURN_ITEM,
|
||||
internal_note: "Item broken",
|
||||
reference: "return",
|
||||
reference_id: "return_123",
|
||||
details: {
|
||||
reference_id: createdOrder.items[2].id,
|
||||
quantity: 1,
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
await service.confirmOrderChange(orderChange.id, {
|
||||
confirmed_by: "cx_agent_123",
|
||||
})
|
||||
|
||||
expect(service.confirmOrderChange(orderChange.id)).rejects.toThrowError(
|
||||
`Order Change cannot be modified: ${orderChange.id}`
|
||||
)
|
||||
|
||||
const modified = await service.retrieve(createdOrder.id, {
|
||||
select: [
|
||||
"id",
|
||||
"version",
|
||||
"items.detail",
|
||||
"summary",
|
||||
"shipping_methods",
|
||||
"transactions",
|
||||
],
|
||||
relations: ["items", "shipping_methods", "transactions"],
|
||||
})
|
||||
|
||||
expect(modified).toEqual(
|
||||
expect.objectContaining({
|
||||
version: 2,
|
||||
})
|
||||
)
|
||||
|
||||
expect(modified.items).toEqual([
|
||||
expect.objectContaining({
|
||||
quantity: 2,
|
||||
detail: expect.objectContaining({
|
||||
version: 2,
|
||||
quantity: 2,
|
||||
}),
|
||||
}),
|
||||
expect.objectContaining({
|
||||
title: "Item 2",
|
||||
unit_price: 5,
|
||||
quantity: 5,
|
||||
detail: expect.objectContaining({
|
||||
version: 2,
|
||||
quantity: 5,
|
||||
fulfilled_quantity: 0,
|
||||
shipped_quantity: 0,
|
||||
return_requested_quantity: 0,
|
||||
return_received_quantity: 0,
|
||||
return_dismissed_quantity: 0,
|
||||
written_off_quantity: 0,
|
||||
}),
|
||||
}),
|
||||
expect.objectContaining({
|
||||
title: "Item 3",
|
||||
unit_price: 30,
|
||||
quantity: 1,
|
||||
detail: expect.objectContaining({
|
||||
version: 2,
|
||||
quantity: 1,
|
||||
fulfilled_quantity: 1,
|
||||
shipped_quantity: 1,
|
||||
return_requested_quantity: 0,
|
||||
return_received_quantity: 0,
|
||||
return_dismissed_quantity: 1,
|
||||
written_off_quantity: 0,
|
||||
}),
|
||||
}),
|
||||
])
|
||||
})
|
||||
|
||||
it("should create order changes, cancel and reject them.", async function () {
|
||||
const createdOrder = await service.create(input)
|
||||
|
||||
const orderChange = await service.createOrderChange({
|
||||
order_id: createdOrder.id,
|
||||
description: "changing the order",
|
||||
internal_note: "changing the order to version 2",
|
||||
created_by: "user_123",
|
||||
})
|
||||
|
||||
const orderChange2 = await service.createOrderChange({
|
||||
order_id: createdOrder.id,
|
||||
description: "changing the order again",
|
||||
internal_note: "trying again...",
|
||||
created_by: "user_123",
|
||||
actions: [
|
||||
{
|
||||
action: ChangeActionType.ITEM_ADD,
|
||||
reference: "order_line_item",
|
||||
reference_id: createdOrder.items[0].id,
|
||||
amount:
|
||||
createdOrder.items[0].unit_price *
|
||||
createdOrder.items[0].quantity,
|
||||
details: {
|
||||
quantity: 1,
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
await service.cancelOrderChange({
|
||||
id: orderChange.id,
|
||||
canceled_by: "cx_agent_123",
|
||||
})
|
||||
|
||||
expect(service.cancelOrderChange(orderChange.id)).rejects.toThrowError(
|
||||
"Order Change cannot be modified"
|
||||
)
|
||||
|
||||
await service.declineOrderChange({
|
||||
id: orderChange2.id,
|
||||
declined_by: "user_123",
|
||||
declined_reason: "changed my mind",
|
||||
})
|
||||
|
||||
expect(
|
||||
service.declineOrderChange(orderChange2.id)
|
||||
).rejects.toThrowError("Order Change cannot be modified")
|
||||
|
||||
const [change1, change2] = await service.listOrderChanges(
|
||||
{
|
||||
id: [orderChange.id, orderChange2.id],
|
||||
},
|
||||
{
|
||||
select: [
|
||||
"id",
|
||||
"status",
|
||||
"canceled_by",
|
||||
"canceled_at",
|
||||
"declined_by",
|
||||
"declined_at",
|
||||
"declined_reason",
|
||||
],
|
||||
}
|
||||
)
|
||||
|
||||
expect(change1).toEqual(
|
||||
expect.objectContaining({
|
||||
id: expect.any(String),
|
||||
status: "canceled",
|
||||
declined_by: null,
|
||||
declined_reason: null,
|
||||
declined_at: null,
|
||||
canceled_by: "cx_agent_123",
|
||||
canceled_at: expect.any(Date),
|
||||
})
|
||||
)
|
||||
|
||||
expect(change2).toEqual(
|
||||
expect.objectContaining({
|
||||
id: expect.any(String),
|
||||
status: "declined",
|
||||
declined_by: "user_123",
|
||||
declined_reason: "changed my mind",
|
||||
declined_at: expect.any(Date),
|
||||
canceled_by: null,
|
||||
canceled_at: null,
|
||||
})
|
||||
)
|
||||
})
|
||||
})
|
||||
},
|
||||
})
|
||||
@@ -31,7 +31,6 @@ export class Migration20240219102530 extends Migration {
|
||||
"id" TEXT NOT NULL,
|
||||
"region_id" TEXT NULL,
|
||||
"customer_id" TEXT NULL,
|
||||
"original_order_id" TEXT NULL,
|
||||
"version" INTEGER NOT NULL DEFAULT 1,
|
||||
"sales_channel_id" TEXT NULL,
|
||||
"status" text check (
|
||||
@@ -49,7 +48,6 @@ export class Migration20240219102530 extends Migration {
|
||||
"shipping_address_id" text NULL,
|
||||
"billing_address_id" text NULL,
|
||||
"no_notification" boolean NULL,
|
||||
"summary" jsonb NOT NULL,
|
||||
"metadata" jsonb NULL,
|
||||
"created_at" timestamptz NOT NULL DEFAULT now(),
|
||||
"updated_at" timestamptz NOT NULL DEFAULT now(),
|
||||
@@ -61,9 +59,6 @@ export class Migration20240219102530 extends Migration {
|
||||
ALTER TABLE "order"
|
||||
ADD COLUMN if NOT exists "deleted_at" timestamptz NULL;
|
||||
|
||||
ALTER TABLE "order"
|
||||
ADD COLUMN if NOT exists "original_order_id" text NULL;
|
||||
|
||||
ALTER TABLE "order" DROP constraint if EXISTS "FK_6ff7e874f01b478c115fdd462eb" CASCADE;
|
||||
|
||||
ALTER TABLE "order" DROP constraint if EXISTS "FK_19b0c6293443d1b464f604c3316" CASCADE;
|
||||
@@ -112,11 +107,6 @@ export class Migration20240219102530 extends Migration {
|
||||
)
|
||||
WHERE deleted_at IS NOT NULL;
|
||||
|
||||
CREATE INDEX IF NOT EXISTS "IDX_order_original_order_id" ON "order" (
|
||||
original_order_id
|
||||
)
|
||||
WHERE deleted_at IS NOT NULL;
|
||||
|
||||
CREATE INDEX IF NOT EXISTS "IDX_order_customer_id" ON "order" (
|
||||
customer_id
|
||||
)
|
||||
@@ -142,6 +132,22 @@ export class Migration20240219102530 extends Migration {
|
||||
)
|
||||
WHERE deleted_at IS NOT NULL;
|
||||
|
||||
|
||||
CREATE TABLE IF NOT EXISTS "order_summary" (
|
||||
"id" TEXT NOT NULL,
|
||||
"order_id" TEXT NOT NULL,
|
||||
"version" INTEGER NOT NULL DEFAULT 1,
|
||||
"totals" JSONB NULL,
|
||||
"created_at" TIMESTAMPTZ NOT NULL DEFAULT Now(),
|
||||
"updated_at" TIMESTAMPTZ NOT NULL DEFAULT Now(),
|
||||
CONSTRAINT "order_summary_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS "IDX_order_summary_order_id_version" ON "order_summary" (
|
||||
order_id,
|
||||
version
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS "order_change" (
|
||||
"id" TEXT NOT NULL,
|
||||
"order_id" TEXT NOT NULL,
|
||||
@@ -186,12 +192,18 @@ export class Migration20240219102530 extends Migration {
|
||||
|
||||
CREATE TABLE IF NOT EXISTS "order_change_action" (
|
||||
"id" TEXT NOT NULL,
|
||||
"order_change_id" TEXT NOT NULL,
|
||||
"reference" TEXT NOT NULL,
|
||||
"reference_id" TEXT NOT NULL,
|
||||
"action" JSONB NOT NULL,
|
||||
"metadata" JSONB NULL,
|
||||
"order_id" TEXT NULL,
|
||||
"version" INTEGER NULL,
|
||||
"ordering" BIGSERIAL NOT NULL,
|
||||
"order_change_id" TEXT NULL,
|
||||
"reference" TEXT NULL,
|
||||
"reference_id" TEXT NULL,
|
||||
"action" TEXT NOT NULL,
|
||||
"details" JSONB NULL,
|
||||
"amount" NUMERIC NULL,
|
||||
"raw_amount" JSONB NULL,
|
||||
"internal_note" TEXT NULL,
|
||||
"applied" BOOLEAN NOT NULL DEFAULT false,
|
||||
"created_at" TIMESTAMPTZ NOT NULL DEFAULT Now(),
|
||||
"updated_at" TIMESTAMPTZ NOT NULL DEFAULT Now(),
|
||||
CONSTRAINT "order_change_action_pkey" PRIMARY KEY ("id")
|
||||
@@ -201,9 +213,12 @@ export class Migration20240219102530 extends Migration {
|
||||
order_change_id
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS "IDX_order_change_action_reference_reference_id" ON "order_change_action" (
|
||||
reference,
|
||||
reference_id
|
||||
CREATE INDEX IF NOT EXISTS "IDX_order_change_action_order_id" ON "order_change_action" (
|
||||
order_id
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS "IDX_order_change_action_ordering" ON "order_change_action" (
|
||||
ordering
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS "order_item" (
|
||||
|
||||
@@ -6,6 +6,7 @@ export { default as Order } from "./order"
|
||||
export { default as OrderChange } from "./order-change"
|
||||
export { default as OrderChangeAction } from "./order-change-action"
|
||||
export { default as OrderItem } from "./order-item"
|
||||
export { default as OrderSummary } from "./order-summary"
|
||||
export { default as ShippingMethod } from "./shipping-method"
|
||||
export { default as ShippingMethodAdjustment } from "./shipping-method-adjustment"
|
||||
export { default as ShippingMethodTaxLine } from "./shipping-method-tax-line"
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { DAL } from "@medusajs/types"
|
||||
import { BigNumberRawValue, DAL } from "@medusajs/types"
|
||||
import {
|
||||
BigNumber,
|
||||
MikroOrmBigNumberProperty,
|
||||
createPsqlIndexStatementHelper,
|
||||
generateEntityId,
|
||||
} from "@medusajs/utils"
|
||||
@@ -13,6 +15,7 @@ import {
|
||||
PrimaryKey,
|
||||
Property,
|
||||
} from "@mikro-orm/core"
|
||||
import Order from "./order"
|
||||
import OrderChange from "./order-change"
|
||||
|
||||
type OptionalLineItemProps = DAL.EntityDateColumns
|
||||
@@ -22,45 +25,87 @@ const OrderChangeIdIndex = createPsqlIndexStatementHelper({
|
||||
columns: "order_change_id",
|
||||
})
|
||||
|
||||
const ReferenceIndex = createPsqlIndexStatementHelper({
|
||||
const OrderIdIndex = createPsqlIndexStatementHelper({
|
||||
tableName: "order_change_action",
|
||||
columns: ["reference", "reference_id"],
|
||||
columns: "order_id",
|
||||
})
|
||||
|
||||
const ActionOrderingIndex = createPsqlIndexStatementHelper({
|
||||
tableName: "order_change_action",
|
||||
columns: "ordering",
|
||||
})
|
||||
|
||||
@Entity({ tableName: "order_change_action" })
|
||||
@ReferenceIndex.MikroORMIndex()
|
||||
export default class OrderChangeAction {
|
||||
[OptionalProps]?: OptionalLineItemProps
|
||||
|
||||
@PrimaryKey({ columnType: "text" })
|
||||
id: string
|
||||
|
||||
@Property({ columnType: "integer", autoincrement: true })
|
||||
@ActionOrderingIndex.MikroORMIndex()
|
||||
ordering: number
|
||||
|
||||
@ManyToOne({
|
||||
entity: () => Order,
|
||||
columnType: "text",
|
||||
fieldName: "order_id",
|
||||
cascade: [Cascade.REMOVE],
|
||||
mapToPk: true,
|
||||
nullable: true,
|
||||
})
|
||||
@OrderIdIndex.MikroORMIndex()
|
||||
order_id: string | null = null
|
||||
|
||||
@ManyToOne(() => Order, {
|
||||
persist: false,
|
||||
nullable: true,
|
||||
})
|
||||
order: Order | null = null
|
||||
|
||||
@Property({ columnType: "integer", nullable: true })
|
||||
version: number | null = null
|
||||
|
||||
@ManyToOne({
|
||||
entity: () => OrderChange,
|
||||
columnType: "text",
|
||||
fieldName: "order_change_id",
|
||||
cascade: [Cascade.REMOVE],
|
||||
mapToPk: true,
|
||||
nullable: true,
|
||||
})
|
||||
@OrderChangeIdIndex.MikroORMIndex()
|
||||
order_change_id: string
|
||||
order_change_id: string | null
|
||||
|
||||
@ManyToOne(() => OrderChange, {
|
||||
persist: false,
|
||||
nullable: true,
|
||||
})
|
||||
order_change: OrderChange
|
||||
order_change: OrderChange | null = null
|
||||
|
||||
@Property({
|
||||
columnType: "text",
|
||||
nullable: true,
|
||||
})
|
||||
reference: string | null = null
|
||||
|
||||
@Property({
|
||||
columnType: "text",
|
||||
nullable: true,
|
||||
})
|
||||
reference_id: string | null = null
|
||||
|
||||
@Property({ columnType: "text" })
|
||||
reference: string
|
||||
|
||||
@Property({ columnType: "text" })
|
||||
reference_id: string
|
||||
action: string
|
||||
|
||||
@Property({ columnType: "jsonb" })
|
||||
action: Record<string, unknown> = {}
|
||||
details: Record<string, unknown> = {}
|
||||
|
||||
@MikroOrmBigNumberProperty({ nullable: true })
|
||||
amount: BigNumber | number | null = null
|
||||
|
||||
@Property({ columnType: "jsonb", nullable: true })
|
||||
metadata: Record<string, unknown> | null = null
|
||||
raw_amount: BigNumberRawValue | null = null
|
||||
|
||||
@Property({
|
||||
columnType: "text",
|
||||
@@ -68,6 +113,12 @@ export default class OrderChangeAction {
|
||||
})
|
||||
internal_note: string | null = null
|
||||
|
||||
@Property({
|
||||
columnType: "boolean",
|
||||
defaultRaw: "false",
|
||||
})
|
||||
applied: boolean = false
|
||||
|
||||
@Property({
|
||||
onCreate: () => new Date(),
|
||||
columnType: "timestamptz",
|
||||
@@ -86,12 +137,14 @@ export default class OrderChangeAction {
|
||||
@BeforeCreate()
|
||||
onCreate() {
|
||||
this.id = generateEntityId(this.id, "ordchact")
|
||||
this.order_change_id ??= this.order_change?.id
|
||||
this.order_id ??= this.order?.id ?? null
|
||||
this.order_change_id ??= this.order_change?.id ?? null
|
||||
}
|
||||
|
||||
@OnInit()
|
||||
onInit() {
|
||||
this.id = generateEntityId(this.id, "ordchact")
|
||||
this.order_change_id ??= this.order_change?.id
|
||||
this.order_id ??= this.order?.id ?? null
|
||||
this.order_change_id ??= this.order_change?.id ?? null
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,11 +12,11 @@ import {
|
||||
Enum,
|
||||
ManyToOne,
|
||||
OnInit,
|
||||
OneToMany,
|
||||
OptionalProps,
|
||||
PrimaryKey,
|
||||
Property,
|
||||
} from "@mikro-orm/core"
|
||||
import { OneToMany } from "typeorm"
|
||||
import Order from "./order"
|
||||
import OrderChangeAction from "./order-change-action"
|
||||
|
||||
@@ -64,8 +64,8 @@ export default class OrderChange {
|
||||
@VersionIndex.MikroORMIndex()
|
||||
version: number
|
||||
|
||||
@OneToMany(() => OrderChangeAction, (action) => action.order_change_id, {
|
||||
cascade: [Cascade.REMOVE],
|
||||
@OneToMany(() => OrderChangeAction, (action) => action.order_change, {
|
||||
cascade: [Cascade.PERSIST],
|
||||
})
|
||||
actions = new Collection<OrderChangeAction>(this)
|
||||
|
||||
@@ -77,7 +77,7 @@ export default class OrderChange {
|
||||
|
||||
@Enum({ items: () => OrderChangeStatus, default: OrderChangeStatus.PENDING })
|
||||
@OrderChangeStatusIndex.MikroORMIndex()
|
||||
status: OrderChangeStatus
|
||||
status: OrderChangeStatus = OrderChangeStatus.PENDING
|
||||
|
||||
@Property({ columnType: "text", nullable: true })
|
||||
internal_note: string | null = null
|
||||
@@ -92,7 +92,7 @@ export default class OrderChange {
|
||||
columnType: "timestamptz",
|
||||
nullable: true,
|
||||
})
|
||||
requested_at?: Date
|
||||
requested_at: Date | null = null
|
||||
|
||||
@Property({ columnType: "text", nullable: true })
|
||||
confirmed_by: string | null = null // customer or user ID
|
||||
@@ -101,7 +101,7 @@ export default class OrderChange {
|
||||
columnType: "timestamptz",
|
||||
nullable: true,
|
||||
})
|
||||
confirmed_at?: Date
|
||||
confirmed_at: Date | null = null
|
||||
|
||||
@Property({ columnType: "text", nullable: true })
|
||||
declined_by: string | null = null // customer or user ID
|
||||
|
||||
105
packages/order/src/models/order-summary.ts
Normal file
105
packages/order/src/models/order-summary.ts
Normal file
@@ -0,0 +1,105 @@
|
||||
import {
|
||||
BigNumber,
|
||||
createPsqlIndexStatementHelper,
|
||||
generateEntityId,
|
||||
} from "@medusajs/utils"
|
||||
import {
|
||||
BeforeCreate,
|
||||
Cascade,
|
||||
Entity,
|
||||
ManyToOne,
|
||||
OnInit,
|
||||
PrimaryKey,
|
||||
Property,
|
||||
} from "@mikro-orm/core"
|
||||
import { Order } from "@models"
|
||||
|
||||
type OrderSummaryTotals = {
|
||||
total: BigNumber
|
||||
subtotal: BigNumber
|
||||
total_tax: BigNumber
|
||||
|
||||
ordered_total: BigNumber
|
||||
fulfilled_total: BigNumber
|
||||
returned_total: BigNumber
|
||||
return_request_total: BigNumber
|
||||
write_off_total: BigNumber
|
||||
projected_total: BigNumber
|
||||
|
||||
net_total: BigNumber
|
||||
net_subtotal: BigNumber
|
||||
net_total_tax: BigNumber
|
||||
|
||||
future_total: BigNumber
|
||||
future_subtotal: BigNumber
|
||||
future_total_tax: BigNumber
|
||||
future_projected_total: BigNumber
|
||||
|
||||
balance: BigNumber
|
||||
future_balance: BigNumber
|
||||
}
|
||||
|
||||
const OrderIdVersionIndex = createPsqlIndexStatementHelper({
|
||||
tableName: "order_summary",
|
||||
columns: ["order_id", "version"],
|
||||
})
|
||||
|
||||
@Entity({ tableName: "order_summary" })
|
||||
@OrderIdVersionIndex.MikroORMIndex()
|
||||
export default class OrderSummary {
|
||||
@PrimaryKey({ columnType: "text" })
|
||||
id: string
|
||||
|
||||
@ManyToOne({
|
||||
entity: () => Order,
|
||||
columnType: "text",
|
||||
fieldName: "order_id",
|
||||
mapToPk: true,
|
||||
cascade: [Cascade.REMOVE],
|
||||
})
|
||||
order_id: string
|
||||
|
||||
@ManyToOne({
|
||||
entity: () => Order,
|
||||
fieldName: "order_id",
|
||||
cascade: [Cascade.REMOVE],
|
||||
persist: false,
|
||||
})
|
||||
order: Order
|
||||
|
||||
@Property({
|
||||
columnType: "integer",
|
||||
defaultRaw: "1",
|
||||
})
|
||||
version: number = 1
|
||||
|
||||
@Property({ columnType: "jsonb" })
|
||||
totals: OrderSummaryTotals | null = {} as OrderSummaryTotals
|
||||
|
||||
@Property({
|
||||
onCreate: () => new Date(),
|
||||
columnType: "timestamptz",
|
||||
defaultRaw: "now()",
|
||||
})
|
||||
created_at: Date
|
||||
|
||||
@Property({
|
||||
onCreate: () => new Date(),
|
||||
onUpdate: () => new Date(),
|
||||
columnType: "timestamptz",
|
||||
defaultRaw: "now()",
|
||||
})
|
||||
updated_at: Date
|
||||
|
||||
@BeforeCreate()
|
||||
onCreate() {
|
||||
this.id = generateEntityId(this.id, "ordsum")
|
||||
this.order_id ??= this.order?.id
|
||||
}
|
||||
|
||||
@OnInit()
|
||||
onInit() {
|
||||
this.id = generateEntityId(this.id, "ordsum")
|
||||
this.order_id ??= this.order?.id
|
||||
}
|
||||
}
|
||||
@@ -17,9 +17,9 @@ import {
|
||||
PrimaryKey,
|
||||
Property,
|
||||
} from "@mikro-orm/core"
|
||||
import { OrderSummary } from "../types/common"
|
||||
import Address from "./address"
|
||||
import OrderItem from "./order-item"
|
||||
import OrderSummary from "./order-summary"
|
||||
import ShippingMethod from "./shipping-method"
|
||||
import Transaction from "./transaction"
|
||||
|
||||
@@ -52,12 +52,6 @@ const OrderDeletedAtIndex = createPsqlIndexStatementHelper({
|
||||
where: "deleted_at IS NOT NULL",
|
||||
})
|
||||
|
||||
const OriginalOrderIdIndex = createPsqlIndexStatementHelper({
|
||||
tableName: "order",
|
||||
columns: "original_order_id",
|
||||
where: "deleted_at IS NOT NULL",
|
||||
})
|
||||
|
||||
const CurrencyCodeIndex = createPsqlIndexStatementHelper({
|
||||
tableName: "order",
|
||||
columns: "currency_code",
|
||||
@@ -97,13 +91,6 @@ export default class Order {
|
||||
@CustomerIdIndex.MikroORMIndex()
|
||||
customer_id: string | null = null
|
||||
|
||||
@Property({
|
||||
columnType: "text",
|
||||
nullable: true,
|
||||
})
|
||||
@OriginalOrderIdIndex.MikroORMIndex()
|
||||
original_order_id: string | null = null
|
||||
|
||||
@Property({
|
||||
columnType: "integer",
|
||||
defaultRaw: "1",
|
||||
@@ -154,8 +141,10 @@ export default class Order {
|
||||
@Property({ columnType: "boolean", nullable: true })
|
||||
no_notification: boolean | null = null
|
||||
|
||||
@Property({ columnType: "jsonb" })
|
||||
summary: OrderSummary | null = {} as OrderSummary
|
||||
@OneToMany(() => OrderSummary, (summary) => summary.order, {
|
||||
cascade: [Cascade.PERSIST],
|
||||
})
|
||||
summary = new Collection<OrderSummary>(this)
|
||||
|
||||
@Property({ columnType: "jsonb", nullable: true })
|
||||
metadata: Record<string, unknown> | null = null
|
||||
|
||||
@@ -33,31 +33,35 @@ export class OrderRepository extends DALUtils.mikroOrmBaseRepositoryFactory<Orde
|
||||
|
||||
const config = mapRepositoryToOrderModel(findOptions_)
|
||||
|
||||
const expandDetails = config.options.populate?.filter((p) =>
|
||||
p.includes("items")
|
||||
)?.length
|
||||
let defaultVersion = knex.raw(`"o0"."version"`)
|
||||
const strategy = config.options.strategy ?? LoadStrategy.JOINED
|
||||
if (strategy === LoadStrategy.SELECT_IN) {
|
||||
const sql = manager
|
||||
.qb(Order, "_sub0")
|
||||
.select("version")
|
||||
.where({ id: knex.raw(`"o0"."order_id"`) })
|
||||
.getKnexQuery()
|
||||
.toString()
|
||||
|
||||
// If no version is specified, we default to the latest version
|
||||
if (expandDetails) {
|
||||
let defaultVersion = knex.raw(`"o0"."version"`)
|
||||
const strategy = config.options.strategy ?? LoadStrategy.JOINED
|
||||
if (strategy === LoadStrategy.SELECT_IN) {
|
||||
const sql = manager
|
||||
.qb(Order, "_sub0")
|
||||
.select("version")
|
||||
.where({ id: knex.raw(`"o0"."order_id"`) })
|
||||
.getKnexQuery()
|
||||
.toString()
|
||||
defaultVersion = knex.raw(`(${sql})`)
|
||||
}
|
||||
|
||||
defaultVersion = knex.raw(`(${sql})`)
|
||||
}
|
||||
const version = config.where.version ?? defaultVersion
|
||||
delete config.where?.version
|
||||
|
||||
const version = config.where.version ?? defaultVersion
|
||||
delete config.where?.version
|
||||
config.options.populateWhere ??= {}
|
||||
|
||||
config.options.populateWhere ??= {}
|
||||
config.options.populateWhere.items ??= {}
|
||||
config.options.populateWhere.items.version = version
|
||||
config.options.populateWhere.items ??= {}
|
||||
config.options.populateWhere.items.version = version
|
||||
|
||||
config.options.populateWhere.summary ??= {}
|
||||
config.options.populateWhere.summary.version = version
|
||||
|
||||
config.options.populateWhere.shipping_methods ??= {}
|
||||
config.options.populateWhere.shipping_methods.version = version
|
||||
|
||||
if (!config.options.orderBy) {
|
||||
config.options.orderBy = { id: "ASC" }
|
||||
}
|
||||
|
||||
return await manager.find(Order, config.where, config.options)
|
||||
@@ -82,31 +86,34 @@ export class OrderRepository extends DALUtils.mikroOrmBaseRepositoryFactory<Orde
|
||||
|
||||
const config = mapRepositoryToOrderModel(findOptions_)
|
||||
|
||||
const expandDetails = config.options.populate?.filter((p) =>
|
||||
p.includes("items")
|
||||
)?.length
|
||||
let defaultVersion = knex.raw(`"o0"."version"`)
|
||||
const strategy = config.options.strategy ?? LoadStrategy.JOINED
|
||||
if (strategy === LoadStrategy.SELECT_IN) {
|
||||
const sql = manager
|
||||
.qb(Order, "_sub0")
|
||||
.select("version")
|
||||
.where({ id: knex.raw(`"o0"."order_id"`) })
|
||||
.getKnexQuery()
|
||||
.toString()
|
||||
|
||||
// If no version is specified, we default to the latest version
|
||||
if (expandDetails) {
|
||||
let defaultVersion = knex.raw(`"o0"."version"`)
|
||||
const strategy = config.options.strategy ?? LoadStrategy.JOINED
|
||||
if (strategy === LoadStrategy.SELECT_IN) {
|
||||
const sql = manager
|
||||
.qb(Order, "_sub0")
|
||||
.select("version")
|
||||
.where({ id: knex.raw(`"o0"."order_id"`) })
|
||||
.getKnexQuery()
|
||||
.toString()
|
||||
defaultVersion = knex.raw(`(${sql})`)
|
||||
}
|
||||
|
||||
defaultVersion = knex.raw(`(${sql})`)
|
||||
}
|
||||
const version = config.where.version ?? defaultVersion
|
||||
delete config.where.version
|
||||
|
||||
const version = config.where.version ?? defaultVersion
|
||||
delete config.where.version
|
||||
config.options.populateWhere ??= {}
|
||||
config.options.populateWhere.items ??= {}
|
||||
config.options.populateWhere.items.version = version
|
||||
|
||||
config.options.populateWhere ??= {}
|
||||
config.options.populateWhere.items ??= {}
|
||||
config.options.populateWhere.items.version = version
|
||||
config.options.populateWhere.summary ??= {}
|
||||
config.options.populateWhere.summary.version = version
|
||||
|
||||
config.options.populateWhere.shipping_methods ??= {}
|
||||
config.options.populateWhere.shipping_methods.version = version
|
||||
|
||||
if (!config.options.orderBy) {
|
||||
config.options.orderBy = { id: "ASC" }
|
||||
}
|
||||
|
||||
return await manager.findAndCount(Order, config.where, config.options)
|
||||
|
||||
@@ -9,33 +9,45 @@ describe("Order Exchange - Actions", function () {
|
||||
quantity: 1,
|
||||
unit_price: 10,
|
||||
|
||||
fulfilled_quantity: 1,
|
||||
return_requested_quantity: 0,
|
||||
return_received_quantity: 0,
|
||||
return_dismissed_quantity: 0,
|
||||
written_off_quantity: 0,
|
||||
detail: {
|
||||
quantity: 1,
|
||||
shipped_quantity: 1,
|
||||
fulfilled_quantity: 1,
|
||||
return_requested_quantity: 0,
|
||||
return_received_quantity: 0,
|
||||
return_dismissed_quantity: 0,
|
||||
written_off_quantity: 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "2",
|
||||
quantity: 2,
|
||||
unit_price: 100,
|
||||
|
||||
fulfilled_quantity: 1,
|
||||
return_requested_quantity: 0,
|
||||
return_received_quantity: 0,
|
||||
return_dismissed_quantity: 0,
|
||||
written_off_quantity: 0,
|
||||
detail: {
|
||||
quantity: 2,
|
||||
shipped_quantity: 1,
|
||||
fulfilled_quantity: 1,
|
||||
return_requested_quantity: 0,
|
||||
return_received_quantity: 0,
|
||||
return_dismissed_quantity: 0,
|
||||
written_off_quantity: 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "3",
|
||||
quantity: 3,
|
||||
unit_price: 20,
|
||||
|
||||
fulfilled_quantity: 3,
|
||||
return_requested_quantity: 0,
|
||||
return_received_quantity: 0,
|
||||
return_dismissed_quantity: 0,
|
||||
written_off_quantity: 0,
|
||||
detail: {
|
||||
quantity: 3,
|
||||
shipped_quantity: 3,
|
||||
fulfilled_quantity: 3,
|
||||
return_requested_quantity: 0,
|
||||
return_received_quantity: 0,
|
||||
return_dismissed_quantity: 0,
|
||||
written_off_quantity: 0,
|
||||
},
|
||||
},
|
||||
],
|
||||
shipping_methods: [
|
||||
@@ -44,8 +56,9 @@ describe("Order Exchange - Actions", function () {
|
||||
price: 0,
|
||||
},
|
||||
],
|
||||
total: 270,
|
||||
shipping_total: 0,
|
||||
summary: {
|
||||
total: 270,
|
||||
},
|
||||
}
|
||||
|
||||
it("should perform an item exchage", function () {
|
||||
@@ -99,31 +112,43 @@ describe("Order Exchange - Actions", function () {
|
||||
id: "1",
|
||||
quantity: 1,
|
||||
unit_price: 10,
|
||||
fulfilled_quantity: 1,
|
||||
return_requested_quantity: 0,
|
||||
return_received_quantity: 0,
|
||||
return_dismissed_quantity: 0,
|
||||
written_off_quantity: 0,
|
||||
detail: {
|
||||
quantity: 1,
|
||||
shipped_quantity: 1,
|
||||
fulfilled_quantity: 1,
|
||||
return_requested_quantity: 0,
|
||||
return_received_quantity: 0,
|
||||
return_dismissed_quantity: 0,
|
||||
written_off_quantity: 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "2",
|
||||
quantity: 2,
|
||||
unit_price: 100,
|
||||
fulfilled_quantity: 1,
|
||||
return_requested_quantity: 0,
|
||||
return_received_quantity: 0,
|
||||
return_dismissed_quantity: 0,
|
||||
written_off_quantity: 0,
|
||||
detail: {
|
||||
quantity: 2,
|
||||
shipped_quantity: 1,
|
||||
fulfilled_quantity: 1,
|
||||
return_requested_quantity: 0,
|
||||
return_received_quantity: 0,
|
||||
return_dismissed_quantity: 0,
|
||||
written_off_quantity: 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "3",
|
||||
quantity: 3,
|
||||
unit_price: 20,
|
||||
fulfilled_quantity: 3,
|
||||
return_requested_quantity: 1,
|
||||
return_received_quantity: 0,
|
||||
return_dismissed_quantity: 0,
|
||||
written_off_quantity: 0,
|
||||
detail: {
|
||||
quantity: 3,
|
||||
shipped_quantity: 3,
|
||||
fulfilled_quantity: 3,
|
||||
return_requested_quantity: 1,
|
||||
return_received_quantity: 0,
|
||||
return_dismissed_quantity: 0,
|
||||
written_off_quantity: 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "item_555",
|
||||
|
||||
@@ -9,33 +9,45 @@ describe("Order Return - Actions", function () {
|
||||
quantity: 1,
|
||||
unit_price: 10,
|
||||
|
||||
fulfilled_quantity: 1,
|
||||
return_requested_quantity: 0,
|
||||
return_received_quantity: 0,
|
||||
return_dismissed_quantity: 0,
|
||||
written_off_quantity: 0,
|
||||
detail: {
|
||||
quantity: 1,
|
||||
shipped_quantity: 1,
|
||||
fulfilled_quantity: 1,
|
||||
return_requested_quantity: 0,
|
||||
return_received_quantity: 0,
|
||||
return_dismissed_quantity: 0,
|
||||
written_off_quantity: 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "2",
|
||||
quantity: 2,
|
||||
unit_price: 100,
|
||||
|
||||
fulfilled_quantity: 1,
|
||||
return_requested_quantity: 0,
|
||||
return_received_quantity: 0,
|
||||
return_dismissed_quantity: 0,
|
||||
written_off_quantity: 0,
|
||||
detail: {
|
||||
quantity: 2,
|
||||
shipped_quantity: 1,
|
||||
fulfilled_quantity: 1,
|
||||
return_requested_quantity: 0,
|
||||
return_received_quantity: 0,
|
||||
return_dismissed_quantity: 0,
|
||||
written_off_quantity: 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "3",
|
||||
quantity: 3,
|
||||
unit_price: 20,
|
||||
|
||||
fulfilled_quantity: 3,
|
||||
return_requested_quantity: 0,
|
||||
return_received_quantity: 0,
|
||||
return_dismissed_quantity: 0,
|
||||
written_off_quantity: 0,
|
||||
detail: {
|
||||
quantity: 3,
|
||||
shipped_quantity: 3,
|
||||
fulfilled_quantity: 3,
|
||||
return_requested_quantity: 0,
|
||||
return_received_quantity: 0,
|
||||
return_dismissed_quantity: 0,
|
||||
written_off_quantity: 0,
|
||||
},
|
||||
},
|
||||
],
|
||||
shipping_methods: [
|
||||
@@ -45,7 +57,6 @@ describe("Order Return - Actions", function () {
|
||||
},
|
||||
],
|
||||
total: 270,
|
||||
shipping_total: 0,
|
||||
}
|
||||
|
||||
it("should validate return requests", function () {
|
||||
@@ -66,7 +77,9 @@ describe("Order Return - Actions", function () {
|
||||
order: originalOrder,
|
||||
actions,
|
||||
})
|
||||
}).toThrow("Cannot request to return more items than what was fulfilled.")
|
||||
}).toThrow(
|
||||
"Cannot request to return more items than what was shipped for item 1."
|
||||
)
|
||||
|
||||
expect(() => {
|
||||
actions[0].details!.reference_id = undefined
|
||||
@@ -87,6 +100,7 @@ describe("Order Return - Actions", function () {
|
||||
|
||||
it("should validate return received", function () {
|
||||
const [] = []
|
||||
|
||||
const actions = [
|
||||
{
|
||||
action: ChangeActionType.RETURN_ITEM,
|
||||
@@ -116,31 +130,43 @@ describe("Order Return - Actions", function () {
|
||||
id: "1",
|
||||
quantity: 1,
|
||||
unit_price: 10,
|
||||
fulfilled_quantity: 1,
|
||||
return_requested_quantity: 0,
|
||||
return_received_quantity: 0,
|
||||
return_dismissed_quantity: 0,
|
||||
written_off_quantity: 0,
|
||||
detail: {
|
||||
quantity: 1,
|
||||
shipped_quantity: 1,
|
||||
fulfilled_quantity: 1,
|
||||
return_requested_quantity: 0,
|
||||
return_received_quantity: 0,
|
||||
return_dismissed_quantity: 0,
|
||||
written_off_quantity: 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "2",
|
||||
quantity: 2,
|
||||
unit_price: 100,
|
||||
fulfilled_quantity: 1,
|
||||
return_requested_quantity: 1,
|
||||
return_received_quantity: 0,
|
||||
return_dismissed_quantity: 0,
|
||||
written_off_quantity: 0,
|
||||
detail: {
|
||||
quantity: 2,
|
||||
shipped_quantity: 1,
|
||||
fulfilled_quantity: 1,
|
||||
return_requested_quantity: 1,
|
||||
return_received_quantity: 0,
|
||||
return_dismissed_quantity: 0,
|
||||
written_off_quantity: 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "3",
|
||||
quantity: 3,
|
||||
unit_price: 20,
|
||||
fulfilled_quantity: 3,
|
||||
return_requested_quantity: 2,
|
||||
return_received_quantity: 0,
|
||||
return_dismissed_quantity: 0,
|
||||
written_off_quantity: 0,
|
||||
detail: {
|
||||
quantity: 3,
|
||||
shipped_quantity: 3,
|
||||
fulfilled_quantity: 3,
|
||||
return_requested_quantity: 2,
|
||||
return_received_quantity: 0,
|
||||
return_dismissed_quantity: 0,
|
||||
written_off_quantity: 0,
|
||||
},
|
||||
},
|
||||
])
|
||||
|
||||
@@ -162,7 +188,9 @@ describe("Order Return - Actions", function () {
|
||||
},
|
||||
],
|
||||
})
|
||||
}).toThrow("Cannot request to return more items than what was fulfilled.")
|
||||
}).toThrow(
|
||||
"Cannot request to return more items than what was shipped for item 3."
|
||||
)
|
||||
|
||||
expect(() => {
|
||||
calculateOrderChange({
|
||||
@@ -178,7 +206,7 @@ describe("Order Return - Actions", function () {
|
||||
],
|
||||
})
|
||||
}).toThrow(
|
||||
"Cannot receive more items than what was requested to be returned."
|
||||
"Cannot receive more items than what was requested to be returned for item 3."
|
||||
)
|
||||
|
||||
expect(() => {
|
||||
@@ -202,7 +230,7 @@ describe("Order Return - Actions", function () {
|
||||
],
|
||||
})
|
||||
}).toThrow(
|
||||
"Cannot receive more items than what was requested to be returned."
|
||||
"Cannot receive more items than what was requested to be returned for item 3."
|
||||
)
|
||||
|
||||
const receivedChanges = calculateOrderChange({
|
||||
@@ -230,31 +258,43 @@ describe("Order Return - Actions", function () {
|
||||
id: "1",
|
||||
quantity: 1,
|
||||
unit_price: 10,
|
||||
fulfilled_quantity: 1,
|
||||
return_requested_quantity: 0,
|
||||
return_received_quantity: 0,
|
||||
return_dismissed_quantity: 0,
|
||||
written_off_quantity: 0,
|
||||
detail: {
|
||||
quantity: 1,
|
||||
shipped_quantity: 1,
|
||||
fulfilled_quantity: 1,
|
||||
return_requested_quantity: 0,
|
||||
return_received_quantity: 0,
|
||||
return_dismissed_quantity: 0,
|
||||
written_off_quantity: 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "2",
|
||||
quantity: 2,
|
||||
unit_price: 100,
|
||||
fulfilled_quantity: 1,
|
||||
return_requested_quantity: 1,
|
||||
return_received_quantity: 0,
|
||||
return_dismissed_quantity: 0,
|
||||
written_off_quantity: 0,
|
||||
detail: {
|
||||
quantity: 2,
|
||||
shipped_quantity: 1,
|
||||
fulfilled_quantity: 1,
|
||||
return_requested_quantity: 1,
|
||||
return_received_quantity: 0,
|
||||
return_dismissed_quantity: 0,
|
||||
written_off_quantity: 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "3",
|
||||
quantity: 3,
|
||||
unit_price: 20,
|
||||
fulfilled_quantity: 3,
|
||||
return_requested_quantity: 0,
|
||||
return_received_quantity: 1,
|
||||
return_dismissed_quantity: 1,
|
||||
written_off_quantity: 0,
|
||||
detail: {
|
||||
quantity: 3,
|
||||
shipped_quantity: 3,
|
||||
fulfilled_quantity: 3,
|
||||
return_requested_quantity: 0,
|
||||
return_received_quantity: 1,
|
||||
return_dismissed_quantity: 1,
|
||||
written_off_quantity: 0,
|
||||
},
|
||||
},
|
||||
])
|
||||
})
|
||||
|
||||
@@ -1 +1,3 @@
|
||||
export { default as OrderChangeService } from "./order-change-service"
|
||||
export { default as OrderModuleService } from "./order-module-service"
|
||||
export { default as OrderService } from "./order-service"
|
||||
|
||||
135
packages/order/src/services/order-change-service.ts
Normal file
135
packages/order/src/services/order-change-service.ts
Normal file
@@ -0,0 +1,135 @@
|
||||
import {
|
||||
Context,
|
||||
DAL,
|
||||
FindConfig,
|
||||
OrderTypes,
|
||||
RepositoryService,
|
||||
} from "@medusajs/types"
|
||||
import {
|
||||
InjectManager,
|
||||
InjectTransactionManager,
|
||||
MedusaContext,
|
||||
MedusaError,
|
||||
ModulesSdkUtils,
|
||||
OrderChangeStatus,
|
||||
deduplicate,
|
||||
} from "@medusajs/utils"
|
||||
import { OrderChange } from "@models"
|
||||
|
||||
type InjectedDependencies = {
|
||||
orderChangeRepository: DAL.RepositoryService
|
||||
}
|
||||
|
||||
export default class OrderChangeService<
|
||||
TEntity extends OrderChange = OrderChange
|
||||
> extends ModulesSdkUtils.internalModuleServiceFactory<InjectedDependencies>(
|
||||
OrderChange
|
||||
)<TEntity> {
|
||||
protected readonly orderChangeRepository_: RepositoryService<TEntity>
|
||||
|
||||
constructor(container: InjectedDependencies) {
|
||||
// @ts-ignore
|
||||
super(...arguments)
|
||||
this.orderChangeRepository_ = container.orderChangeRepository
|
||||
}
|
||||
|
||||
@InjectManager("orderChangeRepository_")
|
||||
async listCurrentOrderChange<TEntityMethod = OrderTypes.OrderDTO>(
|
||||
orderId: string | string[],
|
||||
config: FindConfig<TEntityMethod> = {},
|
||||
@MedusaContext() sharedContext: Context = {}
|
||||
): Promise<TEntity[]> {
|
||||
const allChanges = await super.list(
|
||||
{ order_id: orderId },
|
||||
config ?? {
|
||||
select: ["order_id", "status", "version"],
|
||||
order: {
|
||||
order_id: "ASC",
|
||||
version: "DESC",
|
||||
},
|
||||
}
|
||||
)
|
||||
if (!allChanges.length) {
|
||||
return []
|
||||
}
|
||||
|
||||
const lastChanges: string[] = []
|
||||
|
||||
const seen = new Set()
|
||||
for (let i = 0; i < allChanges.length; i++) {
|
||||
if (seen.has(allChanges[i].order_id)) {
|
||||
continue
|
||||
}
|
||||
seen.add(allChanges[i].order_id)
|
||||
|
||||
if (this.isActive(allChanges[i])) {
|
||||
lastChanges.push(allChanges[i].id)
|
||||
}
|
||||
}
|
||||
|
||||
let orderChange!: TEntity
|
||||
if (allChanges?.length > 0) {
|
||||
if (this.isActive(allChanges[0])) {
|
||||
orderChange = allChanges[0]
|
||||
}
|
||||
}
|
||||
|
||||
const relations = deduplicate([...(config.relations ?? []), "actions"])
|
||||
config.relations = relations
|
||||
|
||||
const queryConfig = ModulesSdkUtils.buildQuery<TEntity>(
|
||||
{
|
||||
id: lastChanges,
|
||||
order: {
|
||||
items: {
|
||||
version: orderChange.version,
|
||||
},
|
||||
},
|
||||
},
|
||||
config
|
||||
)
|
||||
|
||||
return await this.orderChangeRepository_.find(queryConfig, sharedContext)
|
||||
}
|
||||
|
||||
isActive(orderChange: OrderChange): boolean {
|
||||
return (
|
||||
orderChange.status === OrderChangeStatus.PENDING ||
|
||||
orderChange.status === OrderChangeStatus.REQUESTED
|
||||
)
|
||||
}
|
||||
|
||||
async create(
|
||||
data: Partial<TEntity>[],
|
||||
sharedContext?: Context
|
||||
): Promise<TEntity[]>
|
||||
|
||||
async create(
|
||||
data: Partial<TEntity>,
|
||||
sharedContext?: Context
|
||||
): Promise<TEntity>
|
||||
|
||||
@InjectTransactionManager("orderChangeRepository_")
|
||||
async create(
|
||||
data: Partial<TEntity>[] | Partial<TEntity>,
|
||||
@MedusaContext() sharedContext: Context = {}
|
||||
): Promise<TEntity[] | TEntity> {
|
||||
const dataArr = Array.isArray(data) ? data : [data]
|
||||
const activeOrderEdit = await this.listCurrentOrderChange(
|
||||
dataArr.map((d) => d.order_id!),
|
||||
{},
|
||||
sharedContext
|
||||
)
|
||||
|
||||
if (activeOrderEdit.length > 0) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
`An active order change already exists for the order(s) ${activeOrderEdit
|
||||
.map((a) => a.order_id)
|
||||
.join(",")}`
|
||||
)
|
||||
}
|
||||
|
||||
return await super.create(dataArr, sharedContext)
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
UpdateOrderItemWithSelectorDTO,
|
||||
} from "@medusajs/types"
|
||||
import {
|
||||
deduplicate,
|
||||
InjectManager,
|
||||
InjectTransactionManager,
|
||||
isObject,
|
||||
@@ -18,6 +19,8 @@ import {
|
||||
MedusaContext,
|
||||
MedusaError,
|
||||
ModulesSdkUtils,
|
||||
OrderChangeStatus,
|
||||
promiseAll,
|
||||
} from "@medusajs/utils"
|
||||
import {
|
||||
Address,
|
||||
@@ -28,6 +31,7 @@ import {
|
||||
OrderChange,
|
||||
OrderChangeAction,
|
||||
OrderItem,
|
||||
OrderSummary,
|
||||
ShippingMethod,
|
||||
ShippingMethodAdjustment,
|
||||
ShippingMethodTaxLine,
|
||||
@@ -45,11 +49,14 @@ import {
|
||||
UpdateOrderShippingMethodTaxLineDTO,
|
||||
} from "@types"
|
||||
import { entityNameToLinkableKeysMap, joinerConfig } from "../joiner-config"
|
||||
import { calculateOrderChange } from "../utils"
|
||||
import { formatOrder } from "../utils/transform-order"
|
||||
import OrderChangeService from "./order-change-service"
|
||||
import OrderService from "./order-service"
|
||||
|
||||
type InjectedDependencies = {
|
||||
baseRepository: DAL.RepositoryService
|
||||
orderService: ModulesSdkTypes.InternalModuleService<any>
|
||||
orderService: OrderService<any>
|
||||
addressService: ModulesSdkTypes.InternalModuleService<any>
|
||||
lineItemService: ModulesSdkTypes.InternalModuleService<any>
|
||||
shippingMethodAdjustmentService: ModulesSdkTypes.InternalModuleService<any>
|
||||
@@ -58,9 +65,10 @@ type InjectedDependencies = {
|
||||
lineItemTaxLineService: ModulesSdkTypes.InternalModuleService<any>
|
||||
shippingMethodTaxLineService: ModulesSdkTypes.InternalModuleService<any>
|
||||
transactionService: ModulesSdkTypes.InternalModuleService<any>
|
||||
orderChangeService: ModulesSdkTypes.InternalModuleService<any>
|
||||
orderChangeService: OrderChangeService<any>
|
||||
orderChangeActionService: ModulesSdkTypes.InternalModuleService<any>
|
||||
orderItemService: ModulesSdkTypes.InternalModuleService<any>
|
||||
orderSummaryService: ModulesSdkTypes.InternalModuleService<any>
|
||||
}
|
||||
|
||||
const generateMethodForModels = [
|
||||
@@ -75,6 +83,7 @@ const generateMethodForModels = [
|
||||
OrderChange,
|
||||
OrderChangeAction,
|
||||
OrderItem,
|
||||
OrderSummary,
|
||||
]
|
||||
|
||||
export default class OrderModuleService<
|
||||
@@ -89,7 +98,8 @@ export default class OrderModuleService<
|
||||
TTransaction extends Transaction = Transaction,
|
||||
TOrderChange extends OrderChange = OrderChange,
|
||||
TOrderChangeAction extends OrderChangeAction = OrderChangeAction,
|
||||
TOrderItem extends OrderItem = OrderItem
|
||||
TOrderItem extends OrderItem = OrderItem,
|
||||
TOrderSummary extends OrderSummary = OrderSummary
|
||||
>
|
||||
extends ModulesSdkUtils.abstractModuleServiceFactory<
|
||||
InjectedDependencies,
|
||||
@@ -105,15 +115,16 @@ export default class OrderModuleService<
|
||||
}
|
||||
ShippingMethodTaxLine: { dto: OrderTypes.OrderShippingMethodTaxLineDTO }
|
||||
Transaction: { dto: OrderTypes.OrderTransactionDTO }
|
||||
Change: { dto: OrderTypes.OrderChangeDTO }
|
||||
ChangeAction: { dto: OrderTypes.OrderChangeActionDTO }
|
||||
OrderChange: { dto: OrderTypes.OrderChangeDTO }
|
||||
OrderChangeAction: { dto: OrderTypes.OrderChangeActionDTO }
|
||||
OrderItem: { dto: OrderTypes.OrderItemDTO }
|
||||
OrderSummary: { dto: OrderTypes.OrderSummaryDTO }
|
||||
}
|
||||
>(Order, generateMethodForModels, entityNameToLinkableKeysMap)
|
||||
implements IOrderModuleService
|
||||
{
|
||||
protected baseRepository_: DAL.RepositoryService
|
||||
protected orderService_: ModulesSdkTypes.InternalModuleService<TOrder>
|
||||
protected orderService_: OrderService<TOrder>
|
||||
protected addressService_: ModulesSdkTypes.InternalModuleService<TAddress>
|
||||
protected lineItemService_: ModulesSdkTypes.InternalModuleService<TLineItem>
|
||||
protected shippingMethodAdjustmentService_: ModulesSdkTypes.InternalModuleService<TShippingMethodAdjustment>
|
||||
@@ -122,9 +133,10 @@ export default class OrderModuleService<
|
||||
protected lineItemTaxLineService_: ModulesSdkTypes.InternalModuleService<TLineItemTaxLine>
|
||||
protected shippingMethodTaxLineService_: ModulesSdkTypes.InternalModuleService<TShippingMethodTaxLine>
|
||||
protected transactionService_: ModulesSdkTypes.InternalModuleService<TTransaction>
|
||||
protected orderChangeService_: ModulesSdkTypes.InternalModuleService<TOrderChange>
|
||||
protected orderChangeService_: OrderChangeService<TOrderChange>
|
||||
protected orderChangeActionService_: ModulesSdkTypes.InternalModuleService<TOrderChangeAction>
|
||||
protected orderItemService_: ModulesSdkTypes.InternalModuleService<TOrderItem>
|
||||
protected orderSummaryService_: ModulesSdkTypes.InternalModuleService<TOrderSummary>
|
||||
|
||||
constructor(
|
||||
{
|
||||
@@ -141,6 +153,7 @@ export default class OrderModuleService<
|
||||
orderChangeService,
|
||||
orderChangeActionService,
|
||||
orderItemService,
|
||||
orderSummaryService,
|
||||
}: InjectedDependencies,
|
||||
protected readonly moduleDeclaration: InternalModuleDeclaration
|
||||
) {
|
||||
@@ -160,6 +173,7 @@ export default class OrderModuleService<
|
||||
this.orderChangeService_ = orderChangeService
|
||||
this.orderChangeActionService_ = orderChangeActionService
|
||||
this.orderItemService_ = orderItemService
|
||||
this.orderSummaryService_ = orderSummaryService
|
||||
}
|
||||
|
||||
__joinerConfig(): ModuleJoinerConfig {
|
||||
@@ -217,6 +231,20 @@ export default class OrderModuleService<
|
||||
): Promise<OrderTypes.OrderDTO[] | OrderTypes.OrderDTO> {
|
||||
const input = Array.isArray(data) ? data : [data]
|
||||
|
||||
// TODO: calculate order total
|
||||
for (const inp of input) {
|
||||
const ordTotals = inp as any
|
||||
ordTotals.summary = {
|
||||
totals: {
|
||||
total:
|
||||
inp.items?.reduce((acc, item) => {
|
||||
const it = item as any
|
||||
return acc + it.unit_price * it.quantity
|
||||
}, 0) ?? 0,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
const orders = await this.create_(input, sharedContext)
|
||||
|
||||
const result = await this.list(
|
||||
@@ -227,12 +255,14 @@ export default class OrderModuleService<
|
||||
relations: [
|
||||
"shipping_address",
|
||||
"billing_address",
|
||||
"summary",
|
||||
"items",
|
||||
"items.tax_lines",
|
||||
"items.adjustments",
|
||||
"shipping_methods",
|
||||
"shipping_methods.tax_lines",
|
||||
"shipping_methods.adjustments",
|
||||
"transactions",
|
||||
],
|
||||
},
|
||||
sharedContext
|
||||
@@ -428,15 +458,19 @@ export default class OrderModuleService<
|
||||
const item = lineItems[i]
|
||||
const toCreate = data[i]
|
||||
|
||||
orderItemToCreate.push({
|
||||
order_id: toCreate.order_id,
|
||||
version: toCreate.version ?? 1,
|
||||
item_id: item.id,
|
||||
quantity: toCreate.quantity,
|
||||
})
|
||||
if (toCreate.order_id) {
|
||||
orderItemToCreate.push({
|
||||
order_id: toCreate.order_id,
|
||||
version: toCreate.version ?? 1,
|
||||
item_id: item.id,
|
||||
quantity: toCreate.quantity,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
await this.orderItemService_.create(orderItemToCreate, sharedContext)
|
||||
if (orderItemToCreate.length) {
|
||||
await this.orderItemService_.create(orderItemToCreate, sharedContext)
|
||||
}
|
||||
|
||||
return lineItems
|
||||
}
|
||||
@@ -808,7 +842,7 @@ export default class OrderModuleService<
|
||||
@InjectTransactionManager("baseRepository_")
|
||||
protected async addShippingMethods_(
|
||||
orderId: string,
|
||||
data: OrderTypes.CreateOrderShippingMethodDTO[],
|
||||
data: CreateOrderShippingMethodDTO[],
|
||||
@MedusaContext() sharedContext: Context = {}
|
||||
): Promise<ShippingMethod[]> {
|
||||
const order = await this.retrieve(
|
||||
@@ -821,7 +855,7 @@ export default class OrderModuleService<
|
||||
return {
|
||||
...method,
|
||||
order_id: order.id,
|
||||
version: order.version ?? 1,
|
||||
version: method.version ?? order.version ?? 1,
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1501,4 +1535,496 @@ export default class OrderModuleService<
|
||||
|
||||
await this.shippingMethodTaxLineService_.delete(ids, sharedContext)
|
||||
}
|
||||
|
||||
async createOrderChange(
|
||||
data: OrderTypes.CreateOrderChangeDTO,
|
||||
sharedContext?: Context
|
||||
): Promise<OrderTypes.OrderChangeDTO>
|
||||
|
||||
async createOrderChange(
|
||||
data: OrderTypes.CreateOrderChangeDTO[],
|
||||
sharedContext?: Context
|
||||
): Promise<OrderTypes.OrderChangeDTO[]>
|
||||
|
||||
@InjectManager("baseRepository_")
|
||||
async createOrderChange(
|
||||
data: OrderTypes.CreateOrderChangeDTO | OrderTypes.CreateOrderChangeDTO[],
|
||||
sharedContext?: Context
|
||||
): Promise<OrderTypes.OrderChangeDTO | OrderTypes.OrderChangeDTO[]> {
|
||||
const changes = await this.createOrderChange_(data, sharedContext)
|
||||
|
||||
return await this.baseRepository_.serialize<OrderTypes.OrderChangeDTO>(
|
||||
Array.isArray(data) ? changes : changes[0],
|
||||
{
|
||||
populate: true,
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@InjectTransactionManager("baseRepository_")
|
||||
protected async createOrderChange_(
|
||||
data: OrderTypes.CreateOrderChangeDTO | OrderTypes.CreateOrderChangeDTO[],
|
||||
sharedContext?: Context
|
||||
): Promise<OrderChange[]> {
|
||||
const dataArr = Array.isArray(data) ? data : [data]
|
||||
|
||||
const orderIds: string[] = []
|
||||
const dataMap: Record<string, object> = {}
|
||||
for (const change of dataArr) {
|
||||
orderIds.push(change.order_id)
|
||||
dataMap[change.order_id] = change
|
||||
}
|
||||
|
||||
const orders = await this.list(
|
||||
{
|
||||
id: orderIds,
|
||||
},
|
||||
{
|
||||
select: ["id", "version"],
|
||||
},
|
||||
sharedContext
|
||||
)
|
||||
|
||||
if (orders.length !== orderIds.length) {
|
||||
const foundOrders = orders.map((o) => o.id)
|
||||
const missing = orderIds.filter((id) => !foundOrders.includes(id))
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
`Order could not be found: ${missing.join(", ")}`
|
||||
)
|
||||
}
|
||||
|
||||
const input = orders.map((order) => {
|
||||
return {
|
||||
...dataMap[order.id],
|
||||
version: order.version + 1,
|
||||
} as any
|
||||
})
|
||||
|
||||
return await this.orderChangeService_.create(input, sharedContext)
|
||||
}
|
||||
|
||||
async cancelOrderChange(
|
||||
orderId: string,
|
||||
sharedContext?: Context
|
||||
): Promise<void>
|
||||
|
||||
async cancelOrderChange(
|
||||
orderId: string[],
|
||||
sharedContext?: Context
|
||||
): Promise<void>
|
||||
|
||||
async cancelOrderChange(
|
||||
data: OrderTypes.CancelOrderChangeDTO,
|
||||
sharedContext?: Context
|
||||
): Promise<void>
|
||||
|
||||
async cancelOrderChange(
|
||||
data: OrderTypes.CancelOrderChangeDTO[],
|
||||
sharedContext?: Context
|
||||
): Promise<void>
|
||||
|
||||
@InjectTransactionManager("baseRepository_")
|
||||
async cancelOrderChange(
|
||||
orderChangeIdOrData:
|
||||
| string
|
||||
| string[]
|
||||
| OrderTypes.CancelOrderChangeDTO
|
||||
| OrderTypes.CancelOrderChangeDTO[],
|
||||
sharedContext?: Context
|
||||
): Promise<void> {
|
||||
const data = Array.isArray(orderChangeIdOrData)
|
||||
? orderChangeIdOrData
|
||||
: [orderChangeIdOrData]
|
||||
|
||||
const orderChangeIds = isString(data[0])
|
||||
? data
|
||||
: (data as any).map((dt) => dt.id)
|
||||
|
||||
await this.getAndValidateOrderChange_(orderChangeIds, false, sharedContext)
|
||||
|
||||
const updates = data.map((dt) => {
|
||||
return {
|
||||
...(isString(dt) ? { id: dt } : dt),
|
||||
canceled_at: new Date(),
|
||||
status: OrderChangeStatus.CANCELED,
|
||||
}
|
||||
})
|
||||
|
||||
await this.orderChangeService_.update(updates as any, sharedContext)
|
||||
}
|
||||
|
||||
async confirmOrderChange(
|
||||
orderId: string,
|
||||
sharedContext?: Context
|
||||
): Promise<void>
|
||||
|
||||
async confirmOrderChange(
|
||||
orderId: string[],
|
||||
sharedContext?: Context
|
||||
): Promise<void>
|
||||
|
||||
async confirmOrderChange(
|
||||
data: OrderTypes.ConfirmOrderChangeDTO,
|
||||
sharedContext?: Context
|
||||
): Promise<void>
|
||||
|
||||
async confirmOrderChange(
|
||||
data: OrderTypes.ConfirmOrderChangeDTO[],
|
||||
sharedContext?: Context
|
||||
): Promise<void>
|
||||
|
||||
@InjectTransactionManager("baseRepository_")
|
||||
async confirmOrderChange(
|
||||
orderChangeIdOrData:
|
||||
| string
|
||||
| string[]
|
||||
| OrderTypes.ConfirmOrderChangeDTO
|
||||
| OrderTypes.ConfirmOrderChangeDTO[],
|
||||
sharedContext?: Context
|
||||
): Promise<void> {
|
||||
const data = Array.isArray(orderChangeIdOrData)
|
||||
? orderChangeIdOrData
|
||||
: [orderChangeIdOrData]
|
||||
|
||||
const orderChangeIds = isString(data[0])
|
||||
? data
|
||||
: (data as any).map((dt) => dt.id)
|
||||
|
||||
const orderChange = await this.getAndValidateOrderChange_(
|
||||
orderChangeIds,
|
||||
true,
|
||||
sharedContext
|
||||
)
|
||||
|
||||
const updates = data.map((dt) => {
|
||||
return {
|
||||
...(isString(dt) ? { id: dt } : dt),
|
||||
confirmed_at: new Date(),
|
||||
status: OrderChangeStatus.CONFIRMED,
|
||||
}
|
||||
})
|
||||
|
||||
await this.orderChangeService_.update(updates as any, sharedContext)
|
||||
|
||||
const orderChanges = orderChange.map((change) => {
|
||||
change.actions = change.actions.map((action) => {
|
||||
return {
|
||||
...action,
|
||||
version: change.version,
|
||||
order_id: change.order_id,
|
||||
}
|
||||
})
|
||||
return change.actions
|
||||
})
|
||||
|
||||
await this.applyOrderChanges_(orderChanges.flat(), sharedContext)
|
||||
}
|
||||
|
||||
async declineOrderChange(
|
||||
orderId: string,
|
||||
sharedContext?: Context
|
||||
): Promise<void>
|
||||
|
||||
async declineOrderChange(
|
||||
orderId: string[],
|
||||
sharedContext?: Context
|
||||
): Promise<void>
|
||||
|
||||
async declineOrderChange(
|
||||
data: OrderTypes.DeclineOrderChangeDTO,
|
||||
sharedContext?: Context
|
||||
): Promise<void>
|
||||
|
||||
async declineOrderChange(
|
||||
data: OrderTypes.DeclineOrderChangeDTO[],
|
||||
sharedContext?: Context
|
||||
): Promise<void>
|
||||
|
||||
@InjectTransactionManager("baseRepository_")
|
||||
async declineOrderChange(
|
||||
orderChangeIdOrData:
|
||||
| string
|
||||
| string[]
|
||||
| OrderTypes.DeclineOrderChangeDTO
|
||||
| OrderTypes.DeclineOrderChangeDTO[],
|
||||
sharedContext?: Context
|
||||
): Promise<void> {
|
||||
const data = Array.isArray(orderChangeIdOrData)
|
||||
? orderChangeIdOrData
|
||||
: [orderChangeIdOrData]
|
||||
|
||||
const orderChangeIds = isString(data[0])
|
||||
? data
|
||||
: (data as any).map((dt) => dt.id)
|
||||
|
||||
await this.getAndValidateOrderChange_(orderChangeIds, false, sharedContext)
|
||||
|
||||
const updates = data.map((dt) => {
|
||||
return {
|
||||
...(isString(dt) ? { id: dt } : dt),
|
||||
declined_at: new Date(),
|
||||
status: OrderChangeStatus.DECLINED,
|
||||
}
|
||||
})
|
||||
|
||||
await this.orderChangeService_.update(updates as any, sharedContext)
|
||||
}
|
||||
|
||||
@InjectManager("baseRepository_")
|
||||
async applyPendingOrderActions(
|
||||
orderId: string | string[],
|
||||
sharedContext?: Context
|
||||
): Promise<void> {
|
||||
const orderIds = Array.isArray(orderId) ? orderId : [orderId]
|
||||
|
||||
const orders = await this.list(
|
||||
{ id: orderIds },
|
||||
{
|
||||
select: ["id", "version"],
|
||||
},
|
||||
sharedContext
|
||||
)
|
||||
|
||||
const changes = await this.orderChangeActionService_.list(
|
||||
{
|
||||
order_id: orders.map((order) => order.id),
|
||||
version: orders[0].version,
|
||||
applied: false,
|
||||
},
|
||||
{
|
||||
select: [
|
||||
"id",
|
||||
"order_id",
|
||||
"ordering",
|
||||
"version",
|
||||
"applied",
|
||||
"reference",
|
||||
"reference_id",
|
||||
"action",
|
||||
"details",
|
||||
"amount",
|
||||
"raw_amount",
|
||||
"internal_note",
|
||||
],
|
||||
order: {
|
||||
ordering: "ASC",
|
||||
},
|
||||
},
|
||||
sharedContext
|
||||
)
|
||||
|
||||
await this.applyOrderChanges_(changes, sharedContext)
|
||||
}
|
||||
|
||||
private async getAndValidateOrderChange_(
|
||||
orderChangeIds: string[],
|
||||
includeActions: boolean,
|
||||
sharedContext?: Context
|
||||
): Promise<any> {
|
||||
const options = {
|
||||
select: ["id", "order_id", "version", "status"],
|
||||
relations: [] as string[],
|
||||
order: {},
|
||||
}
|
||||
|
||||
if (includeActions) {
|
||||
options.select.push("actions")
|
||||
options.relations.push("actions")
|
||||
options.order = {
|
||||
actions: {
|
||||
ordering: "ASC",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
const orderChanges = await this.listOrderChanges(
|
||||
{
|
||||
id: orderChangeIds,
|
||||
},
|
||||
options,
|
||||
sharedContext
|
||||
)
|
||||
|
||||
if (orderChanges.length !== orderChangeIds.length) {
|
||||
const foundOrders = orderChanges.map((o) => o.id)
|
||||
const missing = orderChangeIds.filter((id) => !foundOrders.includes(id))
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
`Order Change could not be found: ${missing.join(", ")}`
|
||||
)
|
||||
}
|
||||
|
||||
for (const orderChange of orderChanges) {
|
||||
const notAllowed: string[] = []
|
||||
if (
|
||||
!(
|
||||
orderChange.status === OrderChangeStatus.PENDING ||
|
||||
orderChange.status === OrderChangeStatus.REQUESTED
|
||||
)
|
||||
) {
|
||||
notAllowed.push(orderChange.id)
|
||||
}
|
||||
|
||||
if (notAllowed.length) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
`Order Change cannot be modified: ${notAllowed.join(", ")}.`
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return orderChanges
|
||||
}
|
||||
|
||||
@InjectTransactionManager("baseRepository_")
|
||||
async addOrderAction(data: any, sharedContext?: Context): Promise<any> {
|
||||
let dataArr = Array.isArray(data) ? data : [data]
|
||||
|
||||
const orderChangeMap = {}
|
||||
const orderChangeIds = dataArr
|
||||
.map((data, idx) => {
|
||||
if (data.order_change_id) {
|
||||
orderChangeMap[data.order_change_id] ??= []
|
||||
orderChangeMap[data.order_change_id].push(dataArr[idx])
|
||||
}
|
||||
return data.order_change_id
|
||||
})
|
||||
.filter(Boolean)
|
||||
|
||||
if (orderChangeIds.length) {
|
||||
const ordChanges = await this.getAndValidateOrderChange_(
|
||||
orderChangeIds,
|
||||
false,
|
||||
sharedContext
|
||||
)
|
||||
for (const ordChange of ordChanges) {
|
||||
orderChangeMap[ordChange.id].forEach((data) => {
|
||||
if (data) {
|
||||
data.order_id = ordChange.order_id
|
||||
data.version = ordChange.version
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return await this.orderChangeActionService_.create(dataArr, sharedContext)
|
||||
}
|
||||
|
||||
private async applyOrderChanges_(
|
||||
changeActions: any[],
|
||||
sharedContext?: Context
|
||||
): Promise<void> {
|
||||
type ApplyOrderChangeDTO = {
|
||||
id: string
|
||||
order_id: string
|
||||
version: number
|
||||
actions: OrderChangeAction[]
|
||||
applied: boolean
|
||||
}
|
||||
|
||||
const actionsMap: Record<string, any[]> = {}
|
||||
const ordersIds: string[] = []
|
||||
const usedActions: any[] = []
|
||||
|
||||
for (const action of changeActions as ApplyOrderChangeDTO[]) {
|
||||
if (action.applied) {
|
||||
continue
|
||||
}
|
||||
|
||||
ordersIds.push(action.order_id)
|
||||
|
||||
actionsMap[action.order_id] ??= []
|
||||
actionsMap[action.order_id].push(action)
|
||||
|
||||
usedActions.push({
|
||||
selector: {
|
||||
id: action.id,
|
||||
},
|
||||
data: {
|
||||
applied: true,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
if (!ordersIds.length) {
|
||||
return
|
||||
}
|
||||
|
||||
const orders = await this.list(
|
||||
{ id: deduplicate(ordersIds) },
|
||||
{
|
||||
select: ["id", "version", "items.detail", "transactions", "summary"],
|
||||
relations: ["transactions", "items", "items.detail"],
|
||||
},
|
||||
sharedContext
|
||||
)
|
||||
|
||||
const itemsToUpsert: OrderItem[] = []
|
||||
const summariesToUpdate: any[] = []
|
||||
const orderToUpdate: any[] = []
|
||||
|
||||
for (const order of orders) {
|
||||
const calculated = calculateOrderChange({
|
||||
order: order as any,
|
||||
actions: actionsMap[order.id],
|
||||
transactions: order.transactions,
|
||||
})
|
||||
|
||||
const version = actionsMap[order.id][0].version!
|
||||
|
||||
for (const item of calculated.order.items) {
|
||||
itemsToUpsert.push({
|
||||
id: item.detail.id,
|
||||
item_id: item.id,
|
||||
order_id: order.id,
|
||||
version,
|
||||
quantity: item.detail.quantity,
|
||||
fulfilled_quantity: item.detail.fulfilled_quantity,
|
||||
shipped_quantity: item.detail.shipped_quantity,
|
||||
return_requested_quantity: item.detail.return_requested_quantity,
|
||||
return_received_quantity: item.detail.return_received_quantity,
|
||||
return_dismissed_quantity: item.detail.return_dismissed_quantity,
|
||||
written_off_quantity: item.detail.written_off_quantity,
|
||||
metadata: item.detail.metadata,
|
||||
} as any)
|
||||
}
|
||||
|
||||
if (version > order.version) {
|
||||
orderToUpdate.push({
|
||||
selector: {
|
||||
id: order.id,
|
||||
},
|
||||
data: {
|
||||
version,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
summariesToUpdate.push({
|
||||
selector: {
|
||||
order_id: order.id,
|
||||
},
|
||||
data: {
|
||||
version,
|
||||
totals: calculated.summary,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
await promiseAll([
|
||||
orderToUpdate.length
|
||||
? this.orderService_.update(orderToUpdate, sharedContext)
|
||||
: null,
|
||||
usedActions.length
|
||||
? this.orderChangeActionService_.update(usedActions, sharedContext)
|
||||
: null,
|
||||
itemsToUpsert.length
|
||||
? this.orderItemService_.upsert(itemsToUpsert, sharedContext)
|
||||
: null,
|
||||
summariesToUpdate.length
|
||||
? this.orderSummaryService_.update(summariesToUpdate, sharedContext)
|
||||
: null,
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
import { BigNumber } from "@medusajs/utils"
|
||||
|
||||
export type OrderSummary = {
|
||||
total: BigNumber
|
||||
subtotal: BigNumber
|
||||
total_tax: BigNumber
|
||||
|
||||
ordered_total: BigNumber
|
||||
fulfilled_total: BigNumber
|
||||
returned_total: BigNumber
|
||||
return_request_total: BigNumber
|
||||
write_off_total: BigNumber
|
||||
projected_total: BigNumber
|
||||
|
||||
net_total: BigNumber
|
||||
net_subtotal: BigNumber
|
||||
net_total_tax: BigNumber
|
||||
|
||||
future_total: BigNumber
|
||||
future_subtotal: BigNumber
|
||||
future_total_tax: BigNumber
|
||||
future_projected_total: BigNumber
|
||||
|
||||
balance: BigNumber
|
||||
future_balance: BigNumber
|
||||
}
|
||||
|
||||
export type ItemSummary = {
|
||||
returnable_quantity: BigNumber
|
||||
ordered_quantity: BigNumber
|
||||
fulfilled_quantity: BigNumber
|
||||
return_requested_quantity: BigNumber
|
||||
return_received_quantity: BigNumber
|
||||
return_dismissed_quantity: BigNumber
|
||||
written_off_quantity: BigNumber
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
import { BigNumberInput } from "@medusajs/types"
|
||||
|
||||
export interface CreateOrderShippingMethodDTO {
|
||||
version?: number
|
||||
name: string
|
||||
shipping_method_id: string
|
||||
order_id: string
|
||||
version?: number
|
||||
amount: BigNumberInput
|
||||
data?: Record<string, unknown>
|
||||
}
|
||||
|
||||
@@ -4,11 +4,17 @@ export type VirtualOrder = {
|
||||
unit_price: number
|
||||
quantity: number
|
||||
|
||||
fulfilled_quantity: number
|
||||
return_requested_quantity: number
|
||||
return_received_quantity: number
|
||||
return_dismissed_quantity: number
|
||||
written_off_quantity: number
|
||||
detail: {
|
||||
id?: string
|
||||
quantity: number
|
||||
shipped_quantity: number
|
||||
fulfilled_quantity: number
|
||||
return_requested_quantity: number
|
||||
return_received_quantity: number
|
||||
return_dismissed_quantity: number
|
||||
written_off_quantity: number
|
||||
metadata?: Record<string, unknown>
|
||||
}
|
||||
}[]
|
||||
|
||||
shipping_methods: {
|
||||
@@ -16,8 +22,11 @@ export type VirtualOrder = {
|
||||
price: number
|
||||
}[]
|
||||
|
||||
total: number
|
||||
shipping_total: number
|
||||
summary: {
|
||||
total: number
|
||||
}
|
||||
|
||||
metadata?: Record<string, unknown>
|
||||
}
|
||||
|
||||
export enum EVENT_STATUS {
|
||||
@@ -26,7 +35,7 @@ export enum EVENT_STATUS {
|
||||
DONE = "done",
|
||||
}
|
||||
|
||||
export interface OrderSummary {
|
||||
export interface OrderSummaryCalculated {
|
||||
currentOrderTotal: number
|
||||
originalOrderTotal: number
|
||||
transactionTotal: number
|
||||
@@ -70,7 +79,7 @@ export type OrderReferences = {
|
||||
action: InternalOrderChangeEvent
|
||||
previousEvents?: InternalOrderChangeEvent[]
|
||||
currentOrder: VirtualOrder
|
||||
summary: OrderSummary
|
||||
summary: OrderSummaryCalculated
|
||||
transactions: OrderTransaction[]
|
||||
type: ActionTypeDefinition
|
||||
actions: InternalOrderChangeEvent[]
|
||||
|
||||
@@ -8,5 +8,6 @@ export enum ChangeActionType {
|
||||
RECEIVE_RETURN_ITEM = "RECEIVE_RETURN_ITEM",
|
||||
RETURN_ITEM = "RETURN_ITEM",
|
||||
SHIPPING_ADD = "SHIPPING_ADD",
|
||||
SHIP_ITEM = "SHIP_ITEM",
|
||||
WRITE_OFF_ITEM = "WRITE_OFF_ITEM",
|
||||
}
|
||||
|
||||
@@ -8,7 +8,9 @@ OrderChangeProcessing.registerActionType(ChangeActionType.CANCEL_RETURN, {
|
||||
(item) => item.id === action.details.reference_id
|
||||
)!
|
||||
|
||||
existing.return_requested_quantity -= action.details.quantity
|
||||
existing.detail.return_requested_quantity ??= 0
|
||||
|
||||
existing.detail.return_requested_quantity -= action.details.quantity
|
||||
|
||||
return action.details.unit_price * action.details.quantity
|
||||
},
|
||||
@@ -17,7 +19,7 @@ OrderChangeProcessing.registerActionType(ChangeActionType.CANCEL_RETURN, {
|
||||
(item) => item.id === action.details.reference_id
|
||||
)!
|
||||
|
||||
existing.return_requested_quantity += action.details.quantity
|
||||
existing.detail.return_requested_quantity += action.details.quantity
|
||||
},
|
||||
validate({ action, currentOrder }) {
|
||||
const refId = action.details?.reference_id
|
||||
@@ -28,6 +30,13 @@ OrderChangeProcessing.registerActionType(ChangeActionType.CANCEL_RETURN, {
|
||||
)
|
||||
}
|
||||
|
||||
if (!isDefined(action.amount) && !isDefined(action.details?.unit_price)) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
`Unit price of item ${action.reference_id} is required if no action.amount is provided.`
|
||||
)
|
||||
}
|
||||
|
||||
const existing = currentOrder.items.find((item) => item.id === refId)
|
||||
|
||||
if (!existing) {
|
||||
@@ -37,13 +46,17 @@ OrderChangeProcessing.registerActionType(ChangeActionType.CANCEL_RETURN, {
|
||||
)
|
||||
}
|
||||
|
||||
const notFulfilled =
|
||||
(existing.quantity as number) - (existing.fulfilled_quantity as number)
|
||||
|
||||
if (action.details.quantity > notFulfilled) {
|
||||
if (!action.details?.quantity) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
"Cannot fulfill more items than what was ordered."
|
||||
`Quantity to cancel return of item ${refId} is required.`
|
||||
)
|
||||
}
|
||||
|
||||
if (action.details?.quantity > existing.detail?.return_requested_quantity) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
`Cannot cancel more items than what was requested to return for item ${refId}.`
|
||||
)
|
||||
}
|
||||
},
|
||||
|
||||
@@ -8,17 +8,19 @@ OrderChangeProcessing.registerActionType(ChangeActionType.FULFILL_ITEM, {
|
||||
(item) => item.id === action.details.reference_id
|
||||
)!
|
||||
|
||||
existing.fulfilled_quantity += action.details.quantity
|
||||
existing.detail.fulfilled_quantity ??= 0
|
||||
|
||||
existing.detail.fulfilled_quantity += action.details.quantity
|
||||
},
|
||||
revert({ action, currentOrder }) {
|
||||
const existing = currentOrder.items.find(
|
||||
(item) => item.id === action.reference_id
|
||||
)!
|
||||
|
||||
existing.fulfilled_quantity -= action.details.quantity
|
||||
existing.detail.fulfilled_quantity -= action.details.quantity
|
||||
},
|
||||
validate({ action, currentOrder }) {
|
||||
const refId = action.details.reference_id
|
||||
const refId = action.details?.reference_id
|
||||
if (!isDefined(refId)) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
@@ -34,10 +36,28 @@ OrderChangeProcessing.registerActionType(ChangeActionType.FULFILL_ITEM, {
|
||||
)
|
||||
}
|
||||
|
||||
if (action.details.quantity < 1) {
|
||||
if (!action.details?.quantity) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
"Quantity must be greater than 0."
|
||||
`Quantity to fulfill of item ${refId} is required.`
|
||||
)
|
||||
}
|
||||
|
||||
if (action.details?.quantity < 1) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
`Quantity of item ${refId} must be greater than 0.`
|
||||
)
|
||||
}
|
||||
|
||||
const notFulfilled =
|
||||
(existing.quantity as number) -
|
||||
(existing.detail?.fulfilled_quantity as number)
|
||||
|
||||
if (action.details?.quantity > notFulfilled) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
`Cannot fulfill more items than what was ordered for item ${refId}.`
|
||||
)
|
||||
}
|
||||
},
|
||||
|
||||
@@ -6,4 +6,5 @@ export * from "./item-remove"
|
||||
export * from "./receive-damaged-return-item"
|
||||
export * from "./receive-return-item"
|
||||
export * from "./return-item"
|
||||
export * from "./ship-item"
|
||||
export * from "./shipping-add"
|
||||
|
||||
@@ -10,12 +10,16 @@ OrderChangeProcessing.registerActionType(ChangeActionType.ITEM_ADD, {
|
||||
)
|
||||
|
||||
if (existing) {
|
||||
existing.detail.quantity ??= 0
|
||||
|
||||
existing.quantity += action.details.quantity
|
||||
existing.detail.quantity += action.details.quantity
|
||||
} else {
|
||||
currentOrder.items.push({
|
||||
id: action.reference_id!,
|
||||
unit_price: action.details.unit_price,
|
||||
quantity: action.details.quantity,
|
||||
// detail: {}
|
||||
} as VirtualOrder["items"][0])
|
||||
}
|
||||
|
||||
@@ -29,6 +33,7 @@ OrderChangeProcessing.registerActionType(ChangeActionType.ITEM_ADD, {
|
||||
if (existingIndex > -1) {
|
||||
const existing = currentOrder.items[existingIndex]
|
||||
existing.quantity -= action.details.quantity
|
||||
existing.detail.quantity -= action.details.quantity
|
||||
|
||||
if (existing.quantity <= 0) {
|
||||
currentOrder.items.splice(existingIndex, 1)
|
||||
@@ -36,6 +41,7 @@ OrderChangeProcessing.registerActionType(ChangeActionType.ITEM_ADD, {
|
||||
}
|
||||
},
|
||||
validate({ action }) {
|
||||
const refId = action.reference_id
|
||||
if (!isDefined(action.reference_id)) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
@@ -43,17 +49,24 @@ OrderChangeProcessing.registerActionType(ChangeActionType.ITEM_ADD, {
|
||||
)
|
||||
}
|
||||
|
||||
if (!isDefined(action.details.unit_price)) {
|
||||
if (!isDefined(action.amount) && !isDefined(action.details?.unit_price)) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
"Unit price is required."
|
||||
`Unit price of item ${refId} is required if no action.amount is provided.`
|
||||
)
|
||||
}
|
||||
|
||||
if (action.details.quantity < 1) {
|
||||
if (!action.details?.quantity) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
"Quantity must be greater than 0."
|
||||
`Quantity of item ${refId} is required.`
|
||||
)
|
||||
}
|
||||
|
||||
if (action.details?.quantity < 1) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
`Quantity of item ${refId} must be greater than 0.`
|
||||
)
|
||||
}
|
||||
},
|
||||
|
||||
@@ -11,7 +11,11 @@ OrderChangeProcessing.registerActionType(ChangeActionType.ITEM_REMOVE, {
|
||||
)
|
||||
|
||||
const existing = currentOrder.items[existingIndex]
|
||||
|
||||
existing.detail.quantity ??= 0
|
||||
|
||||
existing.quantity -= action.details.quantity
|
||||
existing.detail.quantity -= action.details.quantity
|
||||
|
||||
if (existing.quantity <= 0) {
|
||||
currentOrder.items.splice(existingIndex, 1)
|
||||
@@ -26,6 +30,7 @@ OrderChangeProcessing.registerActionType(ChangeActionType.ITEM_REMOVE, {
|
||||
|
||||
if (existing) {
|
||||
existing.quantity += action.details.quantity
|
||||
existing.detail.quantity += action.details.quantity
|
||||
} else {
|
||||
currentOrder.items.push({
|
||||
id: action.reference_id!,
|
||||
@@ -51,27 +56,35 @@ OrderChangeProcessing.registerActionType(ChangeActionType.ITEM_REMOVE, {
|
||||
)
|
||||
}
|
||||
|
||||
if (!isDefined(action.details.unit_price)) {
|
||||
if (!isDefined(action.amount) && !isDefined(action.details?.unit_price)) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
"Unit price is required."
|
||||
`Unit price of item ${refId} is required if no action.amount is provided.`
|
||||
)
|
||||
}
|
||||
|
||||
if (action.details.quantity < 1) {
|
||||
if (!action.details?.quantity) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
"Quantity must be greater than 0."
|
||||
`Quantity of item ${refId} is required.`
|
||||
)
|
||||
}
|
||||
|
||||
if (action.details?.quantity < 1) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
`Quantity of item ${refId} must be greater than 0.`
|
||||
)
|
||||
}
|
||||
|
||||
const notFulfilled =
|
||||
(existing.quantity as number) - (existing.fulfilled_quantity as number)
|
||||
(existing.quantity as number) -
|
||||
(existing.detail?.fulfilled_quantity as number)
|
||||
|
||||
if (action.details.quantity > notFulfilled) {
|
||||
if (action.details?.quantity > notFulfilled) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
"Cannot remove fulfilled items."
|
||||
`Cannot remove fulfilled item: Item ${refId}.`
|
||||
)
|
||||
}
|
||||
},
|
||||
|
||||
@@ -15,9 +15,11 @@ OrderChangeProcessing.registerActionType(
|
||||
|
||||
let toReturn = action.details.quantity
|
||||
|
||||
existing.return_dismissed_quantity ??= 0
|
||||
existing.return_dismissed_quantity += toReturn
|
||||
existing.return_requested_quantity -= toReturn
|
||||
existing.detail.return_dismissed_quantity ??= 0
|
||||
existing.detail.return_requested_quantity ??= 0
|
||||
|
||||
existing.detail.return_dismissed_quantity += toReturn
|
||||
existing.detail.return_requested_quantity -= toReturn
|
||||
|
||||
if (previousEvents) {
|
||||
for (const previousEvent of previousEvents) {
|
||||
@@ -40,8 +42,8 @@ OrderChangeProcessing.registerActionType(
|
||||
(item) => item.id === action.details.reference_id
|
||||
)!
|
||||
|
||||
existing.return_dismissed_quantity -= action.details.quantity
|
||||
existing.return_requested_quantity += action.details.quantity
|
||||
existing.detail.return_dismissed_quantity -= action.details.quantity
|
||||
existing.detail.return_requested_quantity += action.details.quantity
|
||||
|
||||
if (previousEvents) {
|
||||
for (const previousEvent of previousEvents) {
|
||||
@@ -76,11 +78,18 @@ OrderChangeProcessing.registerActionType(
|
||||
)
|
||||
}
|
||||
|
||||
const quantityRequested = existing?.return_requested_quantity || 0
|
||||
if (action.details.quantity > quantityRequested) {
|
||||
if (!action.details?.quantity) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
"Cannot receive more items than what was requested to be returned."
|
||||
`Quantity to return of item ${refId} is required.`
|
||||
)
|
||||
}
|
||||
|
||||
const quantityRequested = existing?.detail.return_requested_quantity || 0
|
||||
if (action.details?.quantity > quantityRequested) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
`Cannot receive more items than what was requested to be returned for item ${refId}.`
|
||||
)
|
||||
}
|
||||
},
|
||||
|
||||
@@ -13,9 +13,11 @@ OrderChangeProcessing.registerActionType(ChangeActionType.RECEIVE_RETURN_ITEM, {
|
||||
|
||||
let toReturn = action.details.quantity
|
||||
|
||||
existing.return_received_quantity ??= 0
|
||||
existing.return_received_quantity += toReturn
|
||||
existing.return_requested_quantity -= toReturn
|
||||
existing.detail.return_received_quantity ??= 0
|
||||
existing.detail.return_requested_quantity ??= 0
|
||||
|
||||
existing.detail.return_received_quantity += toReturn
|
||||
existing.detail.return_requested_quantity -= toReturn
|
||||
|
||||
if (previousEvents) {
|
||||
for (const previousEvent of previousEvents) {
|
||||
@@ -38,8 +40,8 @@ OrderChangeProcessing.registerActionType(ChangeActionType.RECEIVE_RETURN_ITEM, {
|
||||
(item) => item.id === action.details.reference_id
|
||||
)!
|
||||
|
||||
existing.return_received_quantity -= action.details.quantity
|
||||
existing.return_requested_quantity += action.details.quantity
|
||||
existing.detail.return_received_quantity -= action.details.quantity
|
||||
existing.detail.return_requested_quantity += action.details.quantity
|
||||
|
||||
if (previousEvents) {
|
||||
for (const previousEvent of previousEvents) {
|
||||
@@ -74,11 +76,18 @@ OrderChangeProcessing.registerActionType(ChangeActionType.RECEIVE_RETURN_ITEM, {
|
||||
)
|
||||
}
|
||||
|
||||
const quantityRequested = existing?.return_requested_quantity || 0
|
||||
if (action.details.quantity > quantityRequested) {
|
||||
if (!action.details?.quantity) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
"Cannot receive more items than what was requested to be returned."
|
||||
`Quantity to receive return of item ${refId} is required.`
|
||||
)
|
||||
}
|
||||
|
||||
const quantityRequested = existing?.detail?.return_requested_quantity || 0
|
||||
if (action.details?.quantity > quantityRequested) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
`Cannot receive more items than what was requested to be returned for item ${refId}.`
|
||||
)
|
||||
}
|
||||
},
|
||||
|
||||
@@ -10,8 +10,8 @@ OrderChangeProcessing.registerActionType(ChangeActionType.RETURN_ITEM, {
|
||||
(item) => item.id === action.details.reference_id
|
||||
)!
|
||||
|
||||
existing.return_requested_quantity ??= 0
|
||||
existing.return_requested_quantity += action.details.quantity
|
||||
existing.detail.return_requested_quantity ??= 0
|
||||
existing.detail.return_requested_quantity += action.details.quantity
|
||||
|
||||
return existing.unit_price * action.details.quantity
|
||||
},
|
||||
@@ -20,7 +20,7 @@ OrderChangeProcessing.registerActionType(ChangeActionType.RETURN_ITEM, {
|
||||
(item) => item.id === action.details.reference_id
|
||||
)!
|
||||
|
||||
existing.return_requested_quantity -= action.details.quantity
|
||||
existing.detail.return_requested_quantity -= action.details.quantity
|
||||
},
|
||||
validate({ action, currentOrder }) {
|
||||
const refId = action.details?.reference_id
|
||||
@@ -40,14 +40,21 @@ OrderChangeProcessing.registerActionType(ChangeActionType.RETURN_ITEM, {
|
||||
)
|
||||
}
|
||||
|
||||
const quantityAvailable =
|
||||
(existing!.fulfilled_quantity ?? 0) -
|
||||
(existing!.return_requested_quantity ?? 0)
|
||||
|
||||
if (action.details.quantity > quantityAvailable) {
|
||||
if (!action.details?.quantity) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
"Cannot request to return more items than what was fulfilled."
|
||||
`Quantity to return of item ${refId} is required.`
|
||||
)
|
||||
}
|
||||
|
||||
const quantityAvailable =
|
||||
(existing!.detail?.shipped_quantity ?? 0) -
|
||||
(existing!.detail?.return_requested_quantity ?? 0)
|
||||
|
||||
if (action.details?.quantity > quantityAvailable) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
`Cannot request to return more items than what was shipped for item ${refId}.`
|
||||
)
|
||||
}
|
||||
},
|
||||
|
||||
64
packages/order/src/utils/actions/ship-item.ts
Normal file
64
packages/order/src/utils/actions/ship-item.ts
Normal file
@@ -0,0 +1,64 @@
|
||||
import { MedusaError, isDefined } from "@medusajs/utils"
|
||||
import { ChangeActionType } from "../action-key"
|
||||
import { OrderChangeProcessing } from "../calculate-order-change"
|
||||
|
||||
OrderChangeProcessing.registerActionType(ChangeActionType.SHIP_ITEM, {
|
||||
operation({ action, currentOrder }) {
|
||||
const existing = currentOrder.items.find(
|
||||
(item) => item.id === action.details.reference_id
|
||||
)!
|
||||
|
||||
existing.detail.shipped_quantity ??= 0
|
||||
|
||||
existing.detail.shipped_quantity += action.details.quantity
|
||||
},
|
||||
revert({ action, currentOrder }) {
|
||||
const existing = currentOrder.items.find(
|
||||
(item) => item.id === action.reference_id
|
||||
)!
|
||||
|
||||
existing.detail.shipped_quantity -= action.details.quantity
|
||||
},
|
||||
validate({ action, currentOrder }) {
|
||||
const refId = action.details?.reference_id
|
||||
if (!isDefined(refId)) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
"Reference ID is required."
|
||||
)
|
||||
}
|
||||
|
||||
const existing = currentOrder.items.find((item) => item.id === refId)
|
||||
if (!existing) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
`Reference ID "${refId}" not found.`
|
||||
)
|
||||
}
|
||||
|
||||
if (!action.details?.quantity) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
`Quantity to ship of item ${refId} is required.`
|
||||
)
|
||||
}
|
||||
|
||||
if (action.details?.quantity < 1) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
`Quantity of item ${refId} must be greater than 0.`
|
||||
)
|
||||
}
|
||||
|
||||
const notShipped =
|
||||
(existing.detail?.fulfilled_quantity as number) -
|
||||
(existing.detail?.shipped_quantity as number)
|
||||
|
||||
if (action.details?.quantity > notShipped) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
`Cannot ship more items than what was fulfilled for item ${refId}.`
|
||||
)
|
||||
}
|
||||
},
|
||||
})
|
||||
@@ -14,7 +14,6 @@ OrderChangeProcessing.registerActionType(ChangeActionType.SHIPPING_ADD, {
|
||||
})
|
||||
|
||||
currentOrder.shipping_methods = shipping
|
||||
return action.amount
|
||||
},
|
||||
revert({ action, currentOrder }) {
|
||||
const shipping = Array.isArray(currentOrder.shipping_methods)
|
||||
|
||||
@@ -8,15 +8,15 @@ OrderChangeProcessing.registerActionType(ChangeActionType.WRITE_OFF_ITEM, {
|
||||
(item) => item.id === action.details.reference_id
|
||||
)!
|
||||
|
||||
existing.written_off_quantity ??= 0
|
||||
existing.written_off_quantity += action.details.quantity
|
||||
existing.detail.written_off_quantity ??= 0
|
||||
existing.detail.written_off_quantity += action.details.quantity
|
||||
},
|
||||
revert({ action, currentOrder }) {
|
||||
const existing = currentOrder.items.find(
|
||||
(item) => item.id === action.details.reference_id
|
||||
)!
|
||||
|
||||
existing.written_off_quantity -= action.details.quantity
|
||||
existing.detail.written_off_quantity -= action.details.quantity
|
||||
},
|
||||
validate({ action, currentOrder }) {
|
||||
const refId = action.details?.reference_id
|
||||
@@ -36,8 +36,15 @@ OrderChangeProcessing.registerActionType(ChangeActionType.WRITE_OFF_ITEM, {
|
||||
)
|
||||
}
|
||||
|
||||
if (!action.details?.quantity) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
`Quantity to write-off item ${refId} is required.`
|
||||
)
|
||||
}
|
||||
|
||||
const quantityAvailable = existing!.quantity ?? 0
|
||||
if (action.details.quantity > quantityAvailable) {
|
||||
if (action.details?.quantity > quantityAvailable) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
"Cannot claim more items than what was ordered."
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
import { OrderSummaryDTO } from "@medusajs/types"
|
||||
import { isDefined } from "@medusajs/utils"
|
||||
import {
|
||||
ActionTypeDefinition,
|
||||
EVENT_STATUS,
|
||||
InternalOrderChangeEvent,
|
||||
OrderChangeEvent,
|
||||
OrderSummary,
|
||||
OrderSummaryCalculated,
|
||||
OrderTransaction,
|
||||
VirtualOrder,
|
||||
} from "@types"
|
||||
|
||||
type InternalOrderSummary = OrderSummary & {
|
||||
type InternalOrderSummary = OrderSummaryCalculated & {
|
||||
futureTemporarySum: number
|
||||
}
|
||||
|
||||
@@ -55,8 +57,8 @@ export class OrderChangeProcessing {
|
||||
pendingDifference: 0,
|
||||
futureTemporarySum: 0,
|
||||
differenceSum: 0,
|
||||
currentOrderTotal: order.total as number,
|
||||
originalOrderTotal: order.total as number,
|
||||
currentOrderTotal: order?.summary?.total ?? 0,
|
||||
originalOrderTotal: order?.summary?.total ?? 0,
|
||||
transactionTotal,
|
||||
}
|
||||
}
|
||||
@@ -181,7 +183,10 @@ export class OrderChangeProcessing {
|
||||
if (typeof type.operation === "function") {
|
||||
calculatedAmount = type.operation(params) as number
|
||||
|
||||
action.amount = calculatedAmount ?? 0
|
||||
// the action.amount has priority over the calculated amount
|
||||
if (!isDefined(action.amount)) {
|
||||
action.amount = calculatedAmount ?? 0
|
||||
}
|
||||
}
|
||||
|
||||
// If an action commits previous ones, replay them with updated values
|
||||
@@ -313,7 +318,7 @@ export class OrderChangeProcessing {
|
||||
})
|
||||
}
|
||||
|
||||
public getSummary(): OrderSummary {
|
||||
public getSummary(): OrderSummaryDTO {
|
||||
const summary = this.summary
|
||||
const orderSummary = {
|
||||
transactionTotal: summary.transactionTotal,
|
||||
@@ -324,7 +329,35 @@ export class OrderChangeProcessing {
|
||||
futureTemporaryDifference: summary.futureTemporaryDifference,
|
||||
pendingDifference: summary.pendingDifference,
|
||||
differenceSum: summary.differenceSum,
|
||||
} as unknown as OrderSummaryDTO
|
||||
|
||||
/*
|
||||
{
|
||||
total: summary.currentOrderTotal
|
||||
|
||||
subtotal: number
|
||||
total_tax: number
|
||||
|
||||
ordered_total: summary.originalOrderTotal
|
||||
fulfilled_total: number
|
||||
returned_total: number
|
||||
return_request_total: number
|
||||
write_off_total: number
|
||||
projected_total: number
|
||||
|
||||
net_total: number
|
||||
net_subtotal: number
|
||||
net_total_tax: number
|
||||
|
||||
future_total: number
|
||||
future_subtotal: number
|
||||
future_total_tax: number
|
||||
future_projected_total: number
|
||||
|
||||
balance: summary.pendingDifference
|
||||
future_balance: number
|
||||
}
|
||||
*/
|
||||
|
||||
return orderSummary
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { OrderTypes } from "@medusajs/types"
|
||||
import { isDefined } from "@medusajs/utils"
|
||||
import { deduplicate, isDefined } from "@medusajs/utils"
|
||||
|
||||
export function formatOrder(
|
||||
order
|
||||
): OrderTypes.OrderDTO | OrderTypes.OrderDTO[] {
|
||||
const isArray = Array.isArray(order)
|
||||
const orders = isArray ? order : [order]
|
||||
const orders = [...(isArray ? order : [order])]
|
||||
|
||||
orders.map((order) => {
|
||||
order.items = order.items?.map((orderItem) => {
|
||||
@@ -21,6 +21,8 @@ export function formatOrder(
|
||||
}
|
||||
})
|
||||
|
||||
order.summary = order.summary?.[0]?.totals
|
||||
|
||||
return order
|
||||
})
|
||||
|
||||
@@ -35,25 +37,27 @@ export function mapRepositoryToOrderModel(config) {
|
||||
return
|
||||
}
|
||||
|
||||
return [
|
||||
...new Set<string>(
|
||||
obj[type].sort().map((rel) => {
|
||||
if (rel == "items.quantity") {
|
||||
if (type === "fields") {
|
||||
obj.populate.push("items.item")
|
||||
}
|
||||
return "items.item.quantity"
|
||||
} else if (rel.includes("items.detail")) {
|
||||
return rel.replace("items.detail", "items")
|
||||
} else if (rel == "items") {
|
||||
return "items.item"
|
||||
} else if (rel.includes("items.") && !rel.includes("items.item")) {
|
||||
return rel.replace("items.", "items.item.")
|
||||
return deduplicate(
|
||||
obj[type].sort().map((rel) => {
|
||||
if (rel == "items.quantity") {
|
||||
if (type === "fields") {
|
||||
obj.populate.push("items.item")
|
||||
}
|
||||
return rel
|
||||
})
|
||||
),
|
||||
]
|
||||
return "items.item.quantity"
|
||||
}
|
||||
if (rel == "summary" && type === "fields") {
|
||||
obj.populate.push("summary")
|
||||
return "summary.totals"
|
||||
} else if (rel.includes("items.detail")) {
|
||||
return rel.replace("items.detail", "items")
|
||||
} else if (rel == "items") {
|
||||
return "items.item"
|
||||
} else if (rel.includes("items.") && !rel.includes("items.item")) {
|
||||
return rel.replace("items.", "items.item.")
|
||||
}
|
||||
return rel
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
conf.options.fields = replace(config.options, "fields")
|
||||
|
||||
@@ -2,7 +2,7 @@ import { BaseFilterable } from "../dal"
|
||||
import { OperatorMap } from "../dal/utils"
|
||||
import { BigNumberRawValue } from "../totals"
|
||||
|
||||
type OrderSummary = {
|
||||
export type OrderSummaryDTO = {
|
||||
total: number
|
||||
subtotal: number
|
||||
total_tax: number
|
||||
@@ -600,10 +600,17 @@ export interface OrderDTO {
|
||||
* @expandable
|
||||
*/
|
||||
shipping_methods?: OrderShippingMethodDTO[]
|
||||
|
||||
/**
|
||||
* The tramsactions associated with the order
|
||||
*
|
||||
* @expandable
|
||||
*/
|
||||
transactions?: OrderTransactionDTO[]
|
||||
/**
|
||||
* The summary of the order totals.
|
||||
*/
|
||||
summary?: OrderSummary
|
||||
summary?: OrderSummaryDTO
|
||||
/**
|
||||
* Holds custom data in key-value pairs.
|
||||
*/
|
||||
@@ -633,6 +640,13 @@ export interface OrderChangeDTO {
|
||||
* @expandable
|
||||
*/
|
||||
order: OrderDTO
|
||||
|
||||
/**
|
||||
* The actions of the order change
|
||||
*
|
||||
* @expandable
|
||||
*/
|
||||
actions: OrderChangeActionDTO[]
|
||||
/**
|
||||
* The status of the order change
|
||||
*/
|
||||
@@ -695,13 +709,24 @@ export interface OrderChangeActionDTO {
|
||||
/**
|
||||
* The ID of the associated order change
|
||||
*/
|
||||
order_change_id: string
|
||||
order_change_id: string | null
|
||||
/**
|
||||
* The associated order change
|
||||
*
|
||||
* @expandable
|
||||
*/
|
||||
order_change: OrderChangeDTO
|
||||
order_change: OrderChangeDTO | null
|
||||
|
||||
/**
|
||||
* The ID of the associated order
|
||||
*/
|
||||
order_id: string | null
|
||||
/**
|
||||
* The associated order
|
||||
*
|
||||
* @expandable
|
||||
*/
|
||||
order: OrderDTO | null
|
||||
/**
|
||||
* The reference of the order change action
|
||||
*/
|
||||
|
||||
@@ -40,11 +40,13 @@ export interface CreateOrderDTO {
|
||||
no_notification?: boolean
|
||||
items?: CreateOrderLineItemDTO[]
|
||||
shipping_methods?: CreateOrderShippingMethodDTO[]
|
||||
transactions?: CreateOrderTransactionDTO[]
|
||||
metadata?: Record<string, unknown>
|
||||
}
|
||||
|
||||
export interface UpdateOrderDTO {
|
||||
id: string
|
||||
id?: string
|
||||
version?: number
|
||||
region_id?: string
|
||||
customer_id?: string
|
||||
sales_channel_id?: string
|
||||
@@ -234,17 +236,10 @@ export interface UpdateOrderShippingMethodAdjustmentDTO {
|
||||
|
||||
export interface CreateOrderChangeDTO {
|
||||
order_id: string
|
||||
status: string
|
||||
description?: string
|
||||
internal_note?: string
|
||||
requested_by?: string
|
||||
requested_at?: Date
|
||||
confirmed_by?: string
|
||||
confirmed_at?: Date
|
||||
declined_by?: string
|
||||
declined_reason?: string
|
||||
declined_at?: Date
|
||||
canceled_by?: string
|
||||
metadata?: Record<string, unknown>
|
||||
}
|
||||
|
||||
@@ -264,6 +259,24 @@ export interface UpdateOrderChangeDTO {
|
||||
metadata?: Record<string, unknown>
|
||||
}
|
||||
|
||||
export interface CancelOrderChangeDTO {
|
||||
id: string
|
||||
canceled_by?: string
|
||||
metadata?: Record<string, unknown>
|
||||
}
|
||||
|
||||
export interface DeclineOrderChangeDTO {
|
||||
id: string
|
||||
declined_by?: string
|
||||
metadata?: Record<string, unknown>
|
||||
}
|
||||
|
||||
export interface ConfirmOrderChangeDTO {
|
||||
id: string
|
||||
confirmed_by?: string
|
||||
metadata?: Record<string, unknown>
|
||||
}
|
||||
|
||||
/** ORDER CHANGE END */
|
||||
|
||||
/** ORDER CHANGE ACTION START */
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
FilterableOrderShippingMethodProps,
|
||||
FilterableOrderShippingMethodTaxLineProps,
|
||||
OrderAddressDTO,
|
||||
OrderChangeDTO,
|
||||
OrderDTO,
|
||||
OrderItemDTO,
|
||||
OrderLineItemAdjustmentDTO,
|
||||
@@ -21,8 +22,11 @@ import {
|
||||
OrderShippingMethodTaxLineDTO,
|
||||
} from "./common"
|
||||
import {
|
||||
CancelOrderChangeDTO,
|
||||
ConfirmOrderChangeDTO,
|
||||
CreateOrderAddressDTO,
|
||||
CreateOrderAdjustmentDTO,
|
||||
CreateOrderChangeDTO,
|
||||
CreateOrderDTO,
|
||||
CreateOrderLineItemDTO,
|
||||
CreateOrderLineItemForOrderDTO,
|
||||
@@ -30,6 +34,7 @@ import {
|
||||
CreateOrderShippingMethodAdjustmentDTO,
|
||||
CreateOrderShippingMethodDTO,
|
||||
CreateOrderShippingMethodTaxLineDTO,
|
||||
DeclineOrderChangeDTO,
|
||||
UpdateOrderAddressDTO,
|
||||
UpdateOrderDTO,
|
||||
UpdateOrderItemDTO,
|
||||
@@ -356,4 +361,55 @@ export interface IOrderModuleService extends IModuleService {
|
||||
selector: FilterableOrderShippingMethodTaxLineProps,
|
||||
sharedContext?: Context
|
||||
): Promise<void>
|
||||
|
||||
// Order Change
|
||||
createOrderChange(
|
||||
data: CreateOrderChangeDTO,
|
||||
sharedContext?: Context
|
||||
): Promise<OrderChangeDTO>
|
||||
createOrderChange(
|
||||
data: CreateOrderChangeDTO[],
|
||||
sharedContext?: Context
|
||||
): Promise<OrderChangeDTO[]>
|
||||
createOrderChange(
|
||||
data: CreateOrderChangeDTO | CreateOrderChangeDTO[],
|
||||
sharedContext?: Context
|
||||
): Promise<OrderChangeDTO | OrderChangeDTO[]>
|
||||
|
||||
cancelOrderChange(orderId: string, sharedContext?: Context): Promise<void>
|
||||
cancelOrderChange(orderId: string[], sharedContext?: Context): Promise<void>
|
||||
cancelOrderChange(
|
||||
data: CancelOrderChangeDTO,
|
||||
sharedContext?: Context
|
||||
): Promise<void>
|
||||
cancelOrderChange(
|
||||
data: CancelOrderChangeDTO[],
|
||||
sharedContext?: Context
|
||||
): Promise<void>
|
||||
|
||||
confirmOrderChange(orderId: string, sharedContext?: Context): Promise<void>
|
||||
confirmOrderChange(orderId: string[], sharedContext?: Context): Promise<void>
|
||||
confirmOrderChange(
|
||||
data: ConfirmOrderChangeDTO,
|
||||
sharedContext?: Context
|
||||
): Promise<void>
|
||||
confirmOrderChange(
|
||||
data: ConfirmOrderChangeDTO[],
|
||||
sharedContext?: Context
|
||||
): Promise<void>
|
||||
|
||||
declineOrderChange(orderId: string, sharedContext?: Context): Promise<void>
|
||||
declineOrderChange(orderId: string[], sharedContext?: Context): Promise<void>
|
||||
declineOrderChange(
|
||||
data: DeclineOrderChangeDTO,
|
||||
sharedContext?: Context
|
||||
): Promise<void>
|
||||
declineOrderChange(
|
||||
data: DeclineOrderChangeDTO[],
|
||||
sharedContext?: Context
|
||||
): Promise<void>
|
||||
|
||||
applyPendingOrderActions(orderId: string | string[], sharedContext?: Context)
|
||||
|
||||
addOrderAction(data: any, sharedContext?: Context)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user