adds endpoints for draft orders
This commit is contained in:
@@ -0,0 +1,344 @@
|
|||||||
|
const { dropDatabase } = require("pg-god");
|
||||||
|
const path = require("path");
|
||||||
|
|
||||||
|
const setupServer = require("../../../helpers/setup-server");
|
||||||
|
const { useApi } = require("../../../helpers/use-api");
|
||||||
|
const { initDb } = require("../../../helpers/use-db");
|
||||||
|
|
||||||
|
const draftOrderSeeder = require("../../helpers/draft-order-seeder");
|
||||||
|
const adminSeeder = require("../../helpers/admin-seeder");
|
||||||
|
|
||||||
|
jest.setTimeout(30000);
|
||||||
|
|
||||||
|
describe("/admin/draft-orders", () => {
|
||||||
|
let medusaProcess;
|
||||||
|
let dbConnection;
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
const cwd = path.resolve(path.join(__dirname, "..", ".."));
|
||||||
|
dbConnection = await initDb({ cwd });
|
||||||
|
medusaProcess = await setupServer({ cwd });
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(async () => {
|
||||||
|
await dbConnection.close();
|
||||||
|
await dropDatabase({ databaseName: "medusa-integration" });
|
||||||
|
|
||||||
|
medusaProcess.kill();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("POST /admin/draft-orders", () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
try {
|
||||||
|
await adminSeeder(dbConnection);
|
||||||
|
await draftOrderSeeder(dbConnection);
|
||||||
|
} catch (err) {
|
||||||
|
console.log(err);
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(async () => {
|
||||||
|
const manager = dbConnection.manager;
|
||||||
|
await manager.query(`DELETE FROM "line_item"`);
|
||||||
|
await manager.query(`DELETE FROM "money_amount"`);
|
||||||
|
await manager.query(`DELETE FROM "product_variant"`);
|
||||||
|
await manager.query(`DELETE FROM "product"`);
|
||||||
|
await manager.query(`DELETE FROM "shipping_method"`);
|
||||||
|
await manager.query(`DELETE FROM "shipping_option"`);
|
||||||
|
await manager.query(`DELETE FROM "discount"`);
|
||||||
|
await manager.query(`DELETE FROM "payment_provider"`);
|
||||||
|
await manager.query(`DELETE FROM "payment_session"`);
|
||||||
|
await manager.query(`UPDATE "payment" SET order_id=NULL`);
|
||||||
|
await manager.query(`DELETE FROM "order"`);
|
||||||
|
await manager.query(`UPDATE "draft_order" SET order_id=NULL`);
|
||||||
|
await manager.query(`DELETE FROM "draft_order"`);
|
||||||
|
await manager.query(`DELETE FROM "cart"`);
|
||||||
|
await manager.query(`DELETE FROM "payment"`);
|
||||||
|
await manager.query(`DELETE FROM "customer"`);
|
||||||
|
await manager.query(`DELETE FROM "address"`);
|
||||||
|
|
||||||
|
await manager.query(
|
||||||
|
`UPDATE "country" SET region_id=NULL WHERE iso_2 = 'us'`
|
||||||
|
);
|
||||||
|
await manager.query(
|
||||||
|
`UPDATE "country" SET region_id=NULL WHERE iso_2 = 'de'`
|
||||||
|
);
|
||||||
|
await manager.query(`DELETE FROM "region"`);
|
||||||
|
await manager.query(`DELETE FROM "user"`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("creates a draft order cart", async () => {
|
||||||
|
const api = useApi();
|
||||||
|
|
||||||
|
const payload = {
|
||||||
|
email: "oli@test.dk",
|
||||||
|
shipping_address_id: "oli-shipping",
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
variant_id: "test-variant",
|
||||||
|
quantity: 2,
|
||||||
|
metadata: {},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
region_id: "test-region",
|
||||||
|
customer_id: "oli-test",
|
||||||
|
shipping_methods: [
|
||||||
|
{
|
||||||
|
option_id: "test-option",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
const response = await api
|
||||||
|
.post("/admin/draft-orders", payload, {
|
||||||
|
headers: {
|
||||||
|
Authorization: "Bearer test_token",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.log(err);
|
||||||
|
});
|
||||||
|
expect(response.status).toEqual(200);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("creates a draft order with custom item", async () => {
|
||||||
|
const api = useApi();
|
||||||
|
|
||||||
|
const payload = {
|
||||||
|
email: "oli@test.dk",
|
||||||
|
shipping_address_id: "oli-shipping",
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
variant_id: "test-variant",
|
||||||
|
quantity: 2,
|
||||||
|
metadata: {},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
quantity: 1,
|
||||||
|
metadata: {},
|
||||||
|
unit_price: 10000,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
region_id: "test-region",
|
||||||
|
customer_id: "oli-test",
|
||||||
|
shipping_methods: [
|
||||||
|
{
|
||||||
|
option_id: "test-option",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
const response = await api
|
||||||
|
.post("/admin/draft-orders", payload, {
|
||||||
|
headers: {
|
||||||
|
Authorization: "Bearer test_token",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.log(err);
|
||||||
|
});
|
||||||
|
expect(response.status).toEqual(200);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("creates a draft order with created shipping address", async () => {
|
||||||
|
const api = useApi();
|
||||||
|
|
||||||
|
const payload = {
|
||||||
|
email: "oli@test.dk",
|
||||||
|
shipping_address: {
|
||||||
|
first_name: "new",
|
||||||
|
last_name: "one",
|
||||||
|
address_1: "New place 1",
|
||||||
|
city: "Copenhagen",
|
||||||
|
country_code: "us",
|
||||||
|
postal_code: "2100",
|
||||||
|
},
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
variant_id: "test-variant",
|
||||||
|
quantity: 2,
|
||||||
|
metadata: {},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
quantity: 1,
|
||||||
|
metadata: {},
|
||||||
|
unit_price: 10000,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
region_id: "test-region",
|
||||||
|
customer_id: "oli-test",
|
||||||
|
shipping_methods: [
|
||||||
|
{
|
||||||
|
option_id: "test-option",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
const response = await api
|
||||||
|
.post("/admin/draft-orders", payload, {
|
||||||
|
headers: {
|
||||||
|
Authorization: "Bearer test_token",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.log(err);
|
||||||
|
});
|
||||||
|
expect(response.status).toEqual(200);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("creates a draft order and registers manual payment", async () => {
|
||||||
|
const api = useApi();
|
||||||
|
|
||||||
|
// register system payment for draft order
|
||||||
|
const orderResponse = await api.post(
|
||||||
|
`/admin/draft-orders/test-draft-order/register-payment`,
|
||||||
|
{},
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
Authorization: "Bearer test_token",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const createdOrder = await api.get(
|
||||||
|
`/admin/orders/${orderResponse.data.order.id}`,
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
Authorization: "Bearer test_token",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const updatedDraftOrder = await api.get(
|
||||||
|
`/admin/draft-orders/test-draft-order`,
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
Authorization: "Bearer test_token",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(orderResponse.status).toEqual(200);
|
||||||
|
// expect newly created order to have id of draft order and system payment
|
||||||
|
expect(createdOrder.data.order.draft_order_id).toEqual(
|
||||||
|
"test-draft-order"
|
||||||
|
);
|
||||||
|
expect(createdOrder.data.order.payments).toEqual(
|
||||||
|
expect.arrayContaining([
|
||||||
|
expect.objectContaining({ provider_id: "system" }),
|
||||||
|
])
|
||||||
|
);
|
||||||
|
// expect draft order to be complete
|
||||||
|
expect(updatedDraftOrder.data.draft_order.status).toEqual("completed");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe("GET /admin/draft-orders", () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
try {
|
||||||
|
await adminSeeder(dbConnection);
|
||||||
|
await draftOrderSeeder(dbConnection);
|
||||||
|
} catch (err) {
|
||||||
|
console.log(err);
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(async () => {
|
||||||
|
const manager = dbConnection.manager;
|
||||||
|
await manager.query(`DELETE FROM "line_item"`);
|
||||||
|
await manager.query(`DELETE FROM "money_amount"`);
|
||||||
|
await manager.query(`DELETE FROM "product_variant"`);
|
||||||
|
await manager.query(`DELETE FROM "product"`);
|
||||||
|
await manager.query(`DELETE FROM "shipping_method"`);
|
||||||
|
await manager.query(`DELETE FROM "shipping_option"`);
|
||||||
|
await manager.query(`DELETE FROM "discount"`);
|
||||||
|
await manager.query(`DELETE FROM "payment_provider"`);
|
||||||
|
await manager.query(`DELETE FROM "payment_session"`);
|
||||||
|
await manager.query(`UPDATE "payment" SET order_id=NULL`);
|
||||||
|
await manager.query(`DELETE FROM "order"`);
|
||||||
|
await manager.query(`UPDATE "draft_order" SET order_id=NULL`);
|
||||||
|
await manager.query(`DELETE FROM "draft_order"`);
|
||||||
|
await manager.query(`DELETE FROM "cart"`);
|
||||||
|
await manager.query(`DELETE FROM "payment"`);
|
||||||
|
await manager.query(`DELETE FROM "customer"`);
|
||||||
|
await manager.query(`DELETE FROM "address"`);
|
||||||
|
|
||||||
|
await manager.query(
|
||||||
|
`UPDATE "country" SET region_id=NULL WHERE iso_2 = 'us'`
|
||||||
|
);
|
||||||
|
await manager.query(
|
||||||
|
`UPDATE "country" SET region_id=NULL WHERE iso_2 = 'de'`
|
||||||
|
);
|
||||||
|
await manager.query(`DELETE FROM "region"`);
|
||||||
|
await manager.query(`DELETE FROM "user"`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("lists draft orders", async () => {
|
||||||
|
const api = useApi();
|
||||||
|
|
||||||
|
const response = await api
|
||||||
|
.get("/admin/draft-orders", {
|
||||||
|
headers: {
|
||||||
|
Authorization: "Bearer test_token",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.log(err);
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(response.status).toEqual(200);
|
||||||
|
|
||||||
|
expect(response.data.draft_orders).toEqual(
|
||||||
|
expect.arrayContaining([
|
||||||
|
expect.objectContaining({ id: "test-draft-order" }),
|
||||||
|
])
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("lists draft orders with query", async () => {
|
||||||
|
const api = useApi();
|
||||||
|
|
||||||
|
const response = await api
|
||||||
|
.get("/admin/draft-orders?q=oli@test", {
|
||||||
|
headers: {
|
||||||
|
Authorization: "Bearer test_token",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.log(err);
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(response.status).toEqual(200);
|
||||||
|
|
||||||
|
expect(response.data.draft_orders).toEqual(
|
||||||
|
expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
cart: expect.objectContaining({ email: "oli@test.dk" }),
|
||||||
|
}),
|
||||||
|
])
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("lists no draft orders on query for non-existing email", async () => {
|
||||||
|
const api = useApi();
|
||||||
|
|
||||||
|
const response = await api
|
||||||
|
.get("/admin/draft-orders?q=heyo@heyo.dk", {
|
||||||
|
headers: {
|
||||||
|
Authorization: "Bearer test_token",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.log(err);
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(response.status).toEqual(200);
|
||||||
|
|
||||||
|
console.log(response.data.draft_orders);
|
||||||
|
|
||||||
|
expect(response.data.draft_orders).toEqual([]);
|
||||||
|
expect(response.data.count).toEqual(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,88 @@
|
|||||||
|
const { dropDatabase } = require("pg-god");
|
||||||
|
const path = require("path");
|
||||||
|
|
||||||
|
const setupServer = require("../../../helpers/setup-server");
|
||||||
|
const { useApi } = require("../../../helpers/use-api");
|
||||||
|
const { initDb } = require("../../../helpers/use-db");
|
||||||
|
|
||||||
|
const draftOrderSeeder = require("../../helpers/draft-order-seeder");
|
||||||
|
const { create } = require("domain");
|
||||||
|
|
||||||
|
jest.setTimeout(30000);
|
||||||
|
|
||||||
|
describe("/store/carts (draft-orders)", () => {
|
||||||
|
let medusaProcess;
|
||||||
|
let dbConnection;
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
const cwd = path.resolve(path.join(__dirname, "..", ".."));
|
||||||
|
dbConnection = await initDb({ cwd });
|
||||||
|
medusaProcess = await setupServer({ cwd });
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(async () => {
|
||||||
|
await dbConnection.close();
|
||||||
|
await dropDatabase({ databaseName: "medusa-integration" });
|
||||||
|
|
||||||
|
medusaProcess.kill();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("POST /admin/draft-order", () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
try {
|
||||||
|
await draftOrderSeeder(dbConnection);
|
||||||
|
} catch (err) {
|
||||||
|
console.log(err);
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(async () => {
|
||||||
|
const manager = dbConnection.manager;
|
||||||
|
await manager.query(`DELETE FROM "line_item"`);
|
||||||
|
await manager.query(`DELETE FROM "money_amount"`);
|
||||||
|
await manager.query(`DELETE FROM "product_variant"`);
|
||||||
|
await manager.query(`DELETE FROM "product"`);
|
||||||
|
await manager.query(`DELETE FROM "shipping_method"`);
|
||||||
|
await manager.query(`DELETE FROM "shipping_option"`);
|
||||||
|
await manager.query(`DELETE FROM "discount"`);
|
||||||
|
await manager.query(`DELETE FROM "draft_order"`);
|
||||||
|
await manager.query(`DELETE FROM "payment_provider"`);
|
||||||
|
await manager.query(`DELETE FROM "payment_session"`);
|
||||||
|
await manager.query(`UPDATE "payment" SET order_id=NULL`);
|
||||||
|
await manager.query(`DELETE FROM "order"`);
|
||||||
|
await manager.query(`DELETE FROM "cart"`);
|
||||||
|
await manager.query(`DELETE FROM "payment"`);
|
||||||
|
await manager.query(`DELETE FROM "customer"`);
|
||||||
|
await manager.query(`DELETE FROM "address"`);
|
||||||
|
|
||||||
|
await manager.query(
|
||||||
|
`UPDATE "country" SET region_id=NULL WHERE iso_2 = 'us'`
|
||||||
|
);
|
||||||
|
await manager.query(
|
||||||
|
`UPDATE "country" SET region_id=NULL WHERE iso_2 = 'de'`
|
||||||
|
);
|
||||||
|
await manager.query(`DELETE FROM "region"`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("completes a cart for a draft order thereby creating an order for the draft order", async () => {
|
||||||
|
const api = useApi();
|
||||||
|
|
||||||
|
const response = await api
|
||||||
|
.post("/store/carts/test-cart/complete-cart", {})
|
||||||
|
.catch((err) => {
|
||||||
|
console.log(err);
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(response.status).toEqual(200);
|
||||||
|
|
||||||
|
const createdOrder = await api
|
||||||
|
.get(`/store/orders/${response.data.data.id}`, {})
|
||||||
|
.catch((err) => {
|
||||||
|
console.log(err);
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(createdOrder.data.order.cart_id).toEqual("test-cart");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,200 @@
|
|||||||
|
const {
|
||||||
|
ShippingProfile,
|
||||||
|
Customer,
|
||||||
|
MoneyAmount,
|
||||||
|
ShippingOption,
|
||||||
|
Product,
|
||||||
|
ProductVariant,
|
||||||
|
Region,
|
||||||
|
Address,
|
||||||
|
Cart,
|
||||||
|
PaymentSession,
|
||||||
|
DraftOrder,
|
||||||
|
} = require("@medusajs/medusa");
|
||||||
|
|
||||||
|
module.exports = async (connection, data = {}) => {
|
||||||
|
const manager = connection.manager;
|
||||||
|
|
||||||
|
const defaultProfile = await manager.findOne(ShippingProfile, {
|
||||||
|
type: "default",
|
||||||
|
});
|
||||||
|
|
||||||
|
await manager.insert(Product, {
|
||||||
|
id: "test-product",
|
||||||
|
title: "test product",
|
||||||
|
profile_id: defaultProfile.id,
|
||||||
|
options: [{ id: "test-option", title: "Size" }],
|
||||||
|
});
|
||||||
|
|
||||||
|
await manager.insert(Address, {
|
||||||
|
id: "oli-shipping",
|
||||||
|
first_name: "oli",
|
||||||
|
last_name: "test",
|
||||||
|
country_code: "us",
|
||||||
|
});
|
||||||
|
|
||||||
|
await manager.insert(Product, {
|
||||||
|
id: "test-product-2",
|
||||||
|
title: "test product 2",
|
||||||
|
profile_id: defaultProfile.id,
|
||||||
|
options: [{ id: "test-option-color", title: "Color" }],
|
||||||
|
});
|
||||||
|
|
||||||
|
await manager.insert(ProductVariant, {
|
||||||
|
id: "test-variant",
|
||||||
|
title: "test variant",
|
||||||
|
product_id: "test-product",
|
||||||
|
inventory_quantity: 1,
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
option_id: "test-option",
|
||||||
|
value: "Size",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
await manager.insert(ProductVariant, {
|
||||||
|
id: "test-variant-2",
|
||||||
|
title: "test variant-2",
|
||||||
|
product_id: "test-product-2",
|
||||||
|
inventory_quantity: 4,
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
option_id: "test-option-color",
|
||||||
|
value: "Color",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const ma = manager.create(MoneyAmount, {
|
||||||
|
variant_id: "test-variant",
|
||||||
|
currency_code: "usd",
|
||||||
|
amount: 8000,
|
||||||
|
});
|
||||||
|
await manager.save(ma);
|
||||||
|
|
||||||
|
const ma2 = manager.create(MoneyAmount, {
|
||||||
|
variant_id: "test-variant-2",
|
||||||
|
currency_code: "usd",
|
||||||
|
amount: 10000,
|
||||||
|
});
|
||||||
|
|
||||||
|
await manager.save(ma2);
|
||||||
|
|
||||||
|
await manager.insert(Region, {
|
||||||
|
id: "test-region",
|
||||||
|
name: "Test Region",
|
||||||
|
currency_code: "usd",
|
||||||
|
tax_rate: 0,
|
||||||
|
payment_providers: [
|
||||||
|
{
|
||||||
|
id: "test-pay",
|
||||||
|
is_installed: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
await manager.insert(Region, {
|
||||||
|
id: "test-region-2",
|
||||||
|
name: "Test Region 2",
|
||||||
|
currency_code: "eur",
|
||||||
|
tax_rate: 0,
|
||||||
|
payment_providers: [
|
||||||
|
{
|
||||||
|
id: "test-pay",
|
||||||
|
is_installed: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
await manager.query(
|
||||||
|
`UPDATE "country" SET region_id='test-region' WHERE iso_2 = 'us'`
|
||||||
|
);
|
||||||
|
|
||||||
|
await manager.query(
|
||||||
|
`UPDATE "country" SET region_id='test-region-2' WHERE iso_2 = 'de'`
|
||||||
|
);
|
||||||
|
|
||||||
|
await manager.insert(Customer, {
|
||||||
|
id: "oli-test",
|
||||||
|
email: "oli@test.dk",
|
||||||
|
});
|
||||||
|
|
||||||
|
await manager.insert(Customer, {
|
||||||
|
id: "lebron-james",
|
||||||
|
email: "lebron@james.com",
|
||||||
|
});
|
||||||
|
|
||||||
|
await manager.insert(ShippingOption, {
|
||||||
|
id: "test-option",
|
||||||
|
name: "test-option",
|
||||||
|
provider_id: "test-ful",
|
||||||
|
region_id: "test-region",
|
||||||
|
profile_id: defaultProfile.id,
|
||||||
|
price_type: "flat_rate",
|
||||||
|
amount: 1000,
|
||||||
|
data: {},
|
||||||
|
});
|
||||||
|
|
||||||
|
const c = manager.create(Cart, {
|
||||||
|
id: "test-cart",
|
||||||
|
customer_id: "oli-test",
|
||||||
|
email: "oli@test.dk",
|
||||||
|
shipping_address_id: "oli-shipping",
|
||||||
|
region_id: "test-region",
|
||||||
|
currency_code: "usd",
|
||||||
|
payment_sessions: [],
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
id: "test-item",
|
||||||
|
fulfilled_quantity: 1,
|
||||||
|
title: "Line Item",
|
||||||
|
description: "Line Item Desc",
|
||||||
|
thumbnail: "https://test.js/1234",
|
||||||
|
unit_price: 8000,
|
||||||
|
quantity: 1,
|
||||||
|
variant_id: "test-variant",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
type: "draft_order",
|
||||||
|
metadata: { draft_order_id: "test-draft-order" },
|
||||||
|
});
|
||||||
|
|
||||||
|
await manager.save(c);
|
||||||
|
|
||||||
|
await manager.insert(PaymentSession, {
|
||||||
|
id: "test-session",
|
||||||
|
cart_id: "test-cart",
|
||||||
|
provider_id: "test-pay",
|
||||||
|
is_selected: true,
|
||||||
|
data: {},
|
||||||
|
status: "pending",
|
||||||
|
});
|
||||||
|
|
||||||
|
// await manager.save(cart);
|
||||||
|
|
||||||
|
const draftOrder = manager.create(DraftOrder, {
|
||||||
|
id: "test-draft-order",
|
||||||
|
status: "awaiting",
|
||||||
|
display_id: 4,
|
||||||
|
cart_id: "test-cart",
|
||||||
|
customer_id: "oli-test",
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
id: "test-item",
|
||||||
|
fulfilled_quantity: 1,
|
||||||
|
title: "Line Item",
|
||||||
|
description: "Line Item Desc",
|
||||||
|
thumbnail: "https://test.js/1234",
|
||||||
|
unit_price: 8000,
|
||||||
|
quantity: 1,
|
||||||
|
variant_id: "test-variant",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
email: "oli@test.dk",
|
||||||
|
region_id: "test-region",
|
||||||
|
discounts: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
await manager.save(draftOrder);
|
||||||
|
};
|
||||||
@@ -28,7 +28,7 @@ class TestPayService extends PaymentService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async authorizePayment(sessionData, context = {}) {
|
async authorizePayment(sessionData, context = {}) {
|
||||||
return {};
|
return { data: {}, status: "authorized" };
|
||||||
}
|
}
|
||||||
|
|
||||||
async updatePaymentData(sessionData, update) {
|
async updatePaymentData(sessionData, update) {
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ export default async (req, res) => {
|
|||||||
try {
|
try {
|
||||||
const customerService = req.scope.resolve("customerService")
|
const customerService = req.scope.resolve("customerService")
|
||||||
const customer = await customerService.retrieve(id, {
|
const customer = await customerService.retrieve(id, {
|
||||||
relations: ["orders"],
|
relations: ["orders", "shipping_addresses"],
|
||||||
})
|
})
|
||||||
|
|
||||||
res.json({ customer })
|
res.json({ customer })
|
||||||
|
|||||||
@@ -5,13 +5,19 @@ export default async (req, res) => {
|
|||||||
const limit = parseInt(req.query.limit) || 10
|
const limit = parseInt(req.query.limit) || 10
|
||||||
const offset = parseInt(req.query.offset) || 0
|
const offset = parseInt(req.query.offset) || 0
|
||||||
|
|
||||||
|
const selector = {}
|
||||||
|
|
||||||
|
if ("q" in req.query) {
|
||||||
|
selector.q = req.query.q
|
||||||
|
}
|
||||||
|
|
||||||
const listConfig = {
|
const listConfig = {
|
||||||
relations: [],
|
relations: [],
|
||||||
skip: offset,
|
skip: offset,
|
||||||
take: limit,
|
take: limit,
|
||||||
}
|
}
|
||||||
|
|
||||||
const customers = await customerService.list({}, listConfig)
|
const customers = await customerService.list(selector, listConfig)
|
||||||
|
|
||||||
res.json({ customers, count: customers.length, offset, limit })
|
res.json({ customers, count: customers.length, offset, limit })
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -0,0 +1,73 @@
|
|||||||
|
import { MedusaError, Validator } from "medusa-core-utils"
|
||||||
|
import { defaultFields, defaultRelations } from "."
|
||||||
|
|
||||||
|
export default async (req, res) => {
|
||||||
|
const schema = Validator.object().keys({
|
||||||
|
status: Validator.string()
|
||||||
|
.valid("open", "awaiting", "completed")
|
||||||
|
.optional(),
|
||||||
|
email: Validator.string()
|
||||||
|
.email()
|
||||||
|
.required(),
|
||||||
|
billing_address: Validator.address().optional(),
|
||||||
|
shipping_address: Validator.address().optional(),
|
||||||
|
billing_address_id: Validator.string().optional(),
|
||||||
|
shipping_address_id: Validator.string().optional(),
|
||||||
|
items: Validator.array()
|
||||||
|
.items({
|
||||||
|
variant_id: Validator.string().optional(),
|
||||||
|
unit_price: Validator.number().optional(),
|
||||||
|
title: Validator.string().optional(),
|
||||||
|
quantity: Validator.number().required(),
|
||||||
|
metadata: Validator.object().default({}),
|
||||||
|
})
|
||||||
|
.required(),
|
||||||
|
region_id: Validator.string().required(),
|
||||||
|
discounts: Validator.array().optional(),
|
||||||
|
customer_id: Validator.string().optional(),
|
||||||
|
customer: Validator.string().optional(),
|
||||||
|
shipping_methods: Validator.array()
|
||||||
|
.items({
|
||||||
|
option_id: Validator.string().required(),
|
||||||
|
data: Validator.object().optional(),
|
||||||
|
price: Validator.number()
|
||||||
|
.integer()
|
||||||
|
.integer()
|
||||||
|
.allow(0)
|
||||||
|
.optional(),
|
||||||
|
})
|
||||||
|
.required(),
|
||||||
|
metadata: Validator.object().optional(),
|
||||||
|
requires_shipping: Validator.boolean().default(true),
|
||||||
|
})
|
||||||
|
|
||||||
|
const { value, error } = schema.validate(req.body)
|
||||||
|
if (error) {
|
||||||
|
throw new MedusaError(MedusaError.Types.INVALID_DATA, error.details)
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const draftOrderService = req.scope.resolve("draftOrderService")
|
||||||
|
const entityManager = req.scope.resolve("manager")
|
||||||
|
|
||||||
|
await entityManager.transaction(async manager => {
|
||||||
|
const requiresShipping = value.requires_shipping
|
||||||
|
delete value.requires_shipping
|
||||||
|
|
||||||
|
let draftOrder = await draftOrderService
|
||||||
|
.withTransaction(manager)
|
||||||
|
.create(value, requiresShipping)
|
||||||
|
|
||||||
|
draftOrder = await draftOrderService
|
||||||
|
.withTransaction(manager)
|
||||||
|
.retrieve(draftOrder.id, {
|
||||||
|
relations: defaultRelations,
|
||||||
|
select: defaultFields,
|
||||||
|
})
|
||||||
|
|
||||||
|
res.status(200).json({ draft_order: draftOrder })
|
||||||
|
})
|
||||||
|
} catch (err) {
|
||||||
|
throw err
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
import { MedusaError, Validator } from "medusa-core-utils"
|
||||||
|
import { defaultCartFields, defaultCartRelations, defaultFields } from "."
|
||||||
|
|
||||||
|
export default async (req, res) => {
|
||||||
|
const { id } = req.params
|
||||||
|
|
||||||
|
const schema = Validator.object().keys({
|
||||||
|
title: Validator.string().optional(),
|
||||||
|
unit_price: Validator.number().optional(),
|
||||||
|
variant_id: Validator.string().optional(),
|
||||||
|
quantity: Validator.number().required(),
|
||||||
|
metadata: Validator.object().optional(),
|
||||||
|
})
|
||||||
|
|
||||||
|
const { value, error } = schema.validate(req.body)
|
||||||
|
if (error) {
|
||||||
|
throw new MedusaError(MedusaError.Types.INVALID_DATA, error.details)
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const draftOrderService = req.scope.resolve("draftOrderService")
|
||||||
|
const cartService = req.scope.resolve("cartService")
|
||||||
|
const lineItemService = req.scope.resolve("lineItemService")
|
||||||
|
const entityManager = req.scope.resolve("manager")
|
||||||
|
|
||||||
|
await entityManager.transaction(async manager => {
|
||||||
|
const draftOrder = await draftOrderService
|
||||||
|
.withTransaction(manager)
|
||||||
|
.retrieve(id, { select: defaultFields, relations: ["cart"] })
|
||||||
|
|
||||||
|
if (
|
||||||
|
draftOrder.status === "completed" ||
|
||||||
|
draftOrder.status === "awaiting"
|
||||||
|
) {
|
||||||
|
throw new MedusaError(
|
||||||
|
MedusaError.Types.NOT_ALLOWED,
|
||||||
|
"You are only allowed to update open draft orders"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value.variant_id) {
|
||||||
|
const line = await lineItemService.generate(
|
||||||
|
value.variant_id,
|
||||||
|
draftOrder.cart.region_id,
|
||||||
|
value.quantity,
|
||||||
|
value.metadata
|
||||||
|
)
|
||||||
|
|
||||||
|
await cartService
|
||||||
|
.withTransaction(manager)
|
||||||
|
.addLineItem(draftOrder.cart_id, line)
|
||||||
|
} else {
|
||||||
|
// custom line items can be added to a draft order
|
||||||
|
await lineItemService.withTransaction(manager).create({
|
||||||
|
cart_id: draftOrder.cart_id,
|
||||||
|
has_shipping: true,
|
||||||
|
title: value.title || "Custom item",
|
||||||
|
allow_discounts: false,
|
||||||
|
unit_price: value.unit_price || 0,
|
||||||
|
quantity: value.quantity,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
draftOrder.cart = await cartService
|
||||||
|
.withTransaction(manager)
|
||||||
|
.retrieve(draftOrder.cart_id, {
|
||||||
|
relations: defaultCartRelations,
|
||||||
|
select: defaultCartFields,
|
||||||
|
})
|
||||||
|
|
||||||
|
res.status(200).json({ draft_order: draftOrder })
|
||||||
|
})
|
||||||
|
} catch (err) {
|
||||||
|
throw err
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
import { MedusaError, Validator } from "medusa-core-utils"
|
||||||
|
import { defaultCartFields, defaultCartRelations, defaultFields } from "."
|
||||||
|
|
||||||
|
export default async (req, res) => {
|
||||||
|
const { id, line_id } = req.params
|
||||||
|
|
||||||
|
try {
|
||||||
|
const draftOrderService = req.scope.resolve("draftOrderService")
|
||||||
|
const cartService = req.scope.resolve("cartService")
|
||||||
|
const entityManager = req.scope.resolve("manager")
|
||||||
|
|
||||||
|
await entityManager.transaction(async manager => {
|
||||||
|
const draftOrder = await draftOrderService
|
||||||
|
.withTransaction(manager)
|
||||||
|
.retrieve(id, { select: defaultFields })
|
||||||
|
|
||||||
|
if (
|
||||||
|
draftOrder.status === "completed" ||
|
||||||
|
draftOrder.status === "awaiting"
|
||||||
|
) {
|
||||||
|
throw new MedusaError(
|
||||||
|
MedusaError.Types.NOT_ALLOWED,
|
||||||
|
"You are only allowed to update open draft orders"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
await cartService
|
||||||
|
.withTransaction(manager)
|
||||||
|
.removeLineItem(draftOrder.cart_id, line_id)
|
||||||
|
|
||||||
|
draftOrder.cart = await cartService
|
||||||
|
.withTransaction(manager)
|
||||||
|
.retrieve(draftOrder.cart_id, {
|
||||||
|
relations: defaultCartRelations,
|
||||||
|
select: defaultCartFields,
|
||||||
|
})
|
||||||
|
|
||||||
|
res.status(200).json({ draft_order: draftOrder })
|
||||||
|
})
|
||||||
|
} catch (err) {
|
||||||
|
throw err
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
import { defaultRelations, defaultFields } from "."
|
||||||
|
|
||||||
|
export default async (req, res) => {
|
||||||
|
const { id } = req.params
|
||||||
|
|
||||||
|
try {
|
||||||
|
const draftOrderService = req.scope.resolve("draftOrderService")
|
||||||
|
const cartService = req.scope.resolve("cartService")
|
||||||
|
|
||||||
|
const draftOrder = await draftOrderService.retrieve(id, {
|
||||||
|
select: defaultFields,
|
||||||
|
relations: defaultRelations,
|
||||||
|
})
|
||||||
|
|
||||||
|
draftOrder.cart = await cartService.retrieve(draftOrder.cart_id, {
|
||||||
|
relations: [
|
||||||
|
"gift_cards",
|
||||||
|
"region",
|
||||||
|
"items",
|
||||||
|
"payment",
|
||||||
|
"shipping_address",
|
||||||
|
"billing_address",
|
||||||
|
"region.countries",
|
||||||
|
"region.payment_providers",
|
||||||
|
"shipping_methods",
|
||||||
|
"payment_sessions",
|
||||||
|
"shipping_methods.shipping_option",
|
||||||
|
"discounts",
|
||||||
|
],
|
||||||
|
select: [
|
||||||
|
"subtotal",
|
||||||
|
"tax_total",
|
||||||
|
"shipping_total",
|
||||||
|
"discount_total",
|
||||||
|
"gift_card_total",
|
||||||
|
"total",
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
res.json({ draft_order: draftOrder })
|
||||||
|
} catch (error) {
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
import { Validator, MedusaError } from "medusa-core-utils"
|
||||||
|
|
||||||
|
export default async (req, res) => {
|
||||||
|
const { id } = req.params
|
||||||
|
|
||||||
|
try {
|
||||||
|
const draftOrderService = req.scope.resolve("draftOrderService")
|
||||||
|
const shippingProfileService = req.scope.resolve("shippingProfileService")
|
||||||
|
|
||||||
|
const draftOrder = await cartService.retrieve(value.cart_id, {
|
||||||
|
select: ["subtotal"],
|
||||||
|
relations: ["region", "items", "items.variant", "items.variant.product"],
|
||||||
|
})
|
||||||
|
|
||||||
|
const options = await shippingProfileService.fetchCartOptions(cart)
|
||||||
|
|
||||||
|
res.status(200).json({ shipping_options: options })
|
||||||
|
} catch (err) {
|
||||||
|
throw err
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
import { Router } from "express"
|
||||||
|
import middlewares from "../../../middlewares"
|
||||||
|
|
||||||
|
const route = Router()
|
||||||
|
|
||||||
|
export default app => {
|
||||||
|
app.use("/draft-orders", route)
|
||||||
|
|
||||||
|
route.get("/", middlewares.wrap(require("./list-draft-orders").default))
|
||||||
|
|
||||||
|
route.get("/:id", middlewares.wrap(require("./get-draft-order").default))
|
||||||
|
|
||||||
|
route.post("/", middlewares.wrap(require("./create-draft-order").default))
|
||||||
|
|
||||||
|
route.delete(
|
||||||
|
"/:id/line-items/:line_id",
|
||||||
|
middlewares.wrap(require("./delete-line-item").default)
|
||||||
|
)
|
||||||
|
|
||||||
|
route.post(
|
||||||
|
"/:id/line-items",
|
||||||
|
middlewares.wrap(require("./create-line-item").default)
|
||||||
|
)
|
||||||
|
|
||||||
|
route.post("/", middlewares.wrap(require("./create-draft-order").default))
|
||||||
|
|
||||||
|
route.post(
|
||||||
|
"/:id/register-payment",
|
||||||
|
middlewares.wrap(require("./register-payment").default)
|
||||||
|
)
|
||||||
|
|
||||||
|
return app
|
||||||
|
}
|
||||||
|
|
||||||
|
export const defaultRelations = []
|
||||||
|
|
||||||
|
export const defaultCartRelations = [
|
||||||
|
"region",
|
||||||
|
"items",
|
||||||
|
"payment",
|
||||||
|
"shipping_address",
|
||||||
|
"billing_address",
|
||||||
|
"region.payment_providers",
|
||||||
|
"shipping_methods",
|
||||||
|
"payment_sessions",
|
||||||
|
"shipping_methods.shipping_option",
|
||||||
|
"discounts",
|
||||||
|
]
|
||||||
|
|
||||||
|
export const defaultCartFields = [
|
||||||
|
"region",
|
||||||
|
"items",
|
||||||
|
"payment",
|
||||||
|
"shipping_address",
|
||||||
|
"billing_address",
|
||||||
|
"region.payment_providers",
|
||||||
|
"shipping_methods",
|
||||||
|
"payment_sessions",
|
||||||
|
"shipping_methods.shipping_option",
|
||||||
|
"discounts",
|
||||||
|
]
|
||||||
|
|
||||||
|
export const defaultFields = [
|
||||||
|
"id",
|
||||||
|
"status",
|
||||||
|
"display_id",
|
||||||
|
"cart_id",
|
||||||
|
"canceled_at",
|
||||||
|
"created_at",
|
||||||
|
"updated_at",
|
||||||
|
"metadata",
|
||||||
|
]
|
||||||
|
|
||||||
|
export const allowedFields = [
|
||||||
|
"id",
|
||||||
|
"status",
|
||||||
|
"display_id",
|
||||||
|
"cart_id",
|
||||||
|
"canceled_at",
|
||||||
|
"created_at",
|
||||||
|
"updated_at",
|
||||||
|
"metadata",
|
||||||
|
]
|
||||||
|
|
||||||
|
export const allowedRelations = ["cart"]
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
import _ from "lodash"
|
||||||
|
import { defaultRelations, defaultFields } from "./"
|
||||||
|
|
||||||
|
export default async (req, res) => {
|
||||||
|
try {
|
||||||
|
const draftOrderService = req.scope.resolve("draftOrderService")
|
||||||
|
|
||||||
|
const limit = parseInt(req.query.limit) || 50
|
||||||
|
const offset = parseInt(req.query.offset) || 0
|
||||||
|
|
||||||
|
let selector = {}
|
||||||
|
|
||||||
|
if ("q" in req.query) {
|
||||||
|
selector.q = req.query.q
|
||||||
|
}
|
||||||
|
|
||||||
|
const listConfig = {
|
||||||
|
select: defaultFields,
|
||||||
|
relations: defaultRelations,
|
||||||
|
skip: offset,
|
||||||
|
take: limit,
|
||||||
|
order: { created_at: "DESC" },
|
||||||
|
}
|
||||||
|
|
||||||
|
const [draftOrders, count] = await draftOrderService.listAndCount(
|
||||||
|
selector,
|
||||||
|
listConfig
|
||||||
|
)
|
||||||
|
|
||||||
|
res.json({ draft_orders: draftOrders, count, offset, limit })
|
||||||
|
} catch (error) {
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
import {
|
||||||
|
defaultFields as defaultOrderFields,
|
||||||
|
defaultRelations as defaultOrderRelations,
|
||||||
|
} from "../orders/index"
|
||||||
|
|
||||||
|
export default async (req, res) => {
|
||||||
|
const { id } = req.params
|
||||||
|
|
||||||
|
try {
|
||||||
|
const draftOrderService = req.scope.resolve("draftOrderService")
|
||||||
|
const orderService = req.scope.resolve("orderService")
|
||||||
|
|
||||||
|
const createdOrder = await draftOrderService.registerSystemPayment(id)
|
||||||
|
|
||||||
|
const order = await orderService.retrieve(createdOrder.id, {
|
||||||
|
relations: defaultOrderRelations,
|
||||||
|
select: defaultOrderFields,
|
||||||
|
})
|
||||||
|
|
||||||
|
res.status(200).json({ order })
|
||||||
|
} catch (err) {
|
||||||
|
throw err
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
import { MedusaError, Validator } from "medusa-core-utils"
|
||||||
|
import { defaultCartFields, defaultCartRelations, defaultFields } from "."
|
||||||
|
|
||||||
|
export default async (req, res) => {
|
||||||
|
const { id } = req.params
|
||||||
|
|
||||||
|
const schema = Validator.object().keys({
|
||||||
|
region_id: Validator.string().optional(),
|
||||||
|
country_code: Validator.string().optional(),
|
||||||
|
email: Validator.string()
|
||||||
|
.email()
|
||||||
|
.optional(),
|
||||||
|
billing_address: Validator.object().optional(),
|
||||||
|
shipping_address: Validator.object().optional(),
|
||||||
|
|
||||||
|
discounts: Validator.array()
|
||||||
|
.items({
|
||||||
|
code: Validator.string(),
|
||||||
|
})
|
||||||
|
.optional(),
|
||||||
|
customer_id: Validator.string().optional(),
|
||||||
|
})
|
||||||
|
|
||||||
|
const { value, error } = schema.validate(req.body)
|
||||||
|
if (error) {
|
||||||
|
throw new MedusaError(MedusaError.Types.INVALID_DATA, error.details)
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const draftOrderService = req.scope.resolve("draftOrderService")
|
||||||
|
const cartService = req.scope.resolve("cartService")
|
||||||
|
const entityManager = req.scope.resolve("manager")
|
||||||
|
|
||||||
|
await entityManager.transaction(async manager => {
|
||||||
|
const draftOrder = await draftOrderService
|
||||||
|
.withTransaction(manager)
|
||||||
|
.retrieve(id, { select: defaultFields })
|
||||||
|
|
||||||
|
if (
|
||||||
|
draftOrder.status === "completed" ||
|
||||||
|
draftOrder.status === "awaiting"
|
||||||
|
) {
|
||||||
|
throw new MedusaError(
|
||||||
|
MedusaError.Types.NOT_ALLOWED,
|
||||||
|
"You are only allowed to update open draft orders"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
await cartService
|
||||||
|
.withTransaction(manager)
|
||||||
|
.update(draftOrder.cart_id, value)
|
||||||
|
|
||||||
|
draftOrder.cart = await cartService
|
||||||
|
.withTransaction(manager)
|
||||||
|
.retrieve(draftOrder.cart_id, {
|
||||||
|
relations: defaultCartRelations,
|
||||||
|
select: defaultCartFields,
|
||||||
|
})
|
||||||
|
|
||||||
|
res.status(200).json({ draft_order: draftOrder })
|
||||||
|
})
|
||||||
|
} catch (err) {
|
||||||
|
throw err
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -18,6 +18,7 @@ import appRoutes from "./apps"
|
|||||||
import swapRoutes from "./swaps"
|
import swapRoutes from "./swaps"
|
||||||
import returnRoutes from "./returns"
|
import returnRoutes from "./returns"
|
||||||
import variantRoutes from "./variants"
|
import variantRoutes from "./variants"
|
||||||
|
import draftOrderRoutes from "./draft-orders"
|
||||||
|
|
||||||
const route = Router()
|
const route = Router()
|
||||||
|
|
||||||
@@ -60,6 +61,7 @@ export default (app, container, config) => {
|
|||||||
swapRoutes(route)
|
swapRoutes(route)
|
||||||
returnRoutes(route)
|
returnRoutes(route)
|
||||||
variantRoutes(route)
|
variantRoutes(route)
|
||||||
|
draftOrderRoutes(route)
|
||||||
|
|
||||||
return app
|
return app
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ const defaultRelations = [
|
|||||||
"claims.fulfillments",
|
"claims.fulfillments",
|
||||||
"claims.claim_items",
|
"claims.claim_items",
|
||||||
"claims.claim_items.images",
|
"claims.claim_items.images",
|
||||||
"claims.claim_items.tags",
|
|
||||||
"swaps",
|
"swaps",
|
||||||
"swaps.return_order",
|
"swaps.return_order",
|
||||||
"swaps.payment",
|
"swaps.payment",
|
||||||
@@ -38,6 +37,7 @@ const defaultFields = [
|
|||||||
"payment_status",
|
"payment_status",
|
||||||
"display_id",
|
"display_id",
|
||||||
"cart_id",
|
"cart_id",
|
||||||
|
"draft_order_id",
|
||||||
"customer_id",
|
"customer_id",
|
||||||
"email",
|
"email",
|
||||||
"region_id",
|
"region_id",
|
||||||
|
|||||||
@@ -208,6 +208,7 @@ export const defaultFields = [
|
|||||||
"payment_status",
|
"payment_status",
|
||||||
"display_id",
|
"display_id",
|
||||||
"cart_id",
|
"cart_id",
|
||||||
|
"draft_order_id",
|
||||||
"customer_id",
|
"customer_id",
|
||||||
"email",
|
"email",
|
||||||
"region_id",
|
"region_id",
|
||||||
@@ -235,6 +236,7 @@ export const allowedFields = [
|
|||||||
"payment_status",
|
"payment_status",
|
||||||
"display_id",
|
"display_id",
|
||||||
"cart_id",
|
"cart_id",
|
||||||
|
"draft_order_id",
|
||||||
"customer_id",
|
"customer_id",
|
||||||
"email",
|
"email",
|
||||||
"region_id",
|
"region_id",
|
||||||
|
|||||||
@@ -37,3 +37,4 @@ export { StagedJob } from "./models/staged-job"
|
|||||||
export { Store } from "./models/store"
|
export { Store } from "./models/store"
|
||||||
export { Swap } from "./models/swap"
|
export { Swap } from "./models/swap"
|
||||||
export { User } from "./models/user"
|
export { User } from "./models/user"
|
||||||
|
export { DraftOrder } from "./models/draft-order"
|
||||||
|
|||||||
@@ -0,0 +1,46 @@
|
|||||||
|
import {MigrationInterface, QueryRunner} from "typeorm";
|
||||||
|
|
||||||
|
export class draftOrders1613384784316 implements MigrationInterface {
|
||||||
|
name = 'draftOrders1613384784316'
|
||||||
|
|
||||||
|
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
await queryRunner.query(`CREATE TYPE "draft_order_status_enum" AS ENUM('open', 'awaiting', 'completed')`);
|
||||||
|
await queryRunner.query(`CREATE TABLE "draft_order" ("id" character varying NOT NULL, "status" "draft_order_status_enum" NOT NULL DEFAULT 'open', "display_id" SERIAL NOT NULL, "cart_id" character varying, "order_id" character varying, "canceled_at" TIMESTAMP WITH TIME ZONE, "created_at" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "updated_at" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "metadata" jsonb, "idempotency_key" character varying, CONSTRAINT "REL_5bd11d0e2a9628128e2c26fd0a" UNIQUE ("cart_id"), CONSTRAINT "REL_8f6dd6c49202f1466ebf21e77d" UNIQUE ("order_id"), CONSTRAINT "PK_f478946c183d98f8d88a94cfcd7" PRIMARY KEY ("id"))`);
|
||||||
|
await queryRunner.query(`CREATE INDEX "IDX_e87cc617a22ef4edce5601edab" ON "draft_order" ("display_id") `);
|
||||||
|
await queryRunner.query(`CREATE INDEX "IDX_5bd11d0e2a9628128e2c26fd0a" ON "draft_order" ("cart_id") `);
|
||||||
|
await queryRunner.query(`CREATE INDEX "IDX_8f6dd6c49202f1466ebf21e77d" ON "draft_order" ("order_id") `);
|
||||||
|
await queryRunner.query(`ALTER TABLE "order" ADD "draft_order_id" character varying`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "order" ADD CONSTRAINT "UQ_727b872f86c7378474a8fa46147" UNIQUE ("draft_order_id")`);
|
||||||
|
await queryRunner.query(`ALTER TYPE "public"."cart_type_enum" RENAME TO "cart_type_enum_old"`);
|
||||||
|
await queryRunner.query(`CREATE TYPE "cart_type_enum" AS ENUM('default', 'swap', 'draft_order', 'payment_link')`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "cart" ALTER COLUMN "type" DROP DEFAULT`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "cart" ALTER COLUMN "type" TYPE "cart_type_enum" USING "type"::"text"::"cart_type_enum"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "cart" ALTER COLUMN "type" SET DEFAULT 'default'`);
|
||||||
|
await queryRunner.query(`DROP TYPE "cart_type_enum_old"`);
|
||||||
|
await queryRunner.query(`COMMENT ON COLUMN "cart"."type" IS NULL`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "draft_order" ADD CONSTRAINT "FK_5bd11d0e2a9628128e2c26fd0a6" FOREIGN KEY ("cart_id") REFERENCES "cart"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "draft_order" ADD CONSTRAINT "FK_8f6dd6c49202f1466ebf21e77da" FOREIGN KEY ("order_id") REFERENCES "order"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "order" ADD CONSTRAINT "FK_727b872f86c7378474a8fa46147" FOREIGN KEY ("draft_order_id") REFERENCES "draft_order"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
await queryRunner.query(`ALTER TABLE "order" DROP CONSTRAINT "FK_727b872f86c7378474a8fa46147"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "draft_order" DROP CONSTRAINT "FK_8f6dd6c49202f1466ebf21e77da"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "draft_order" DROP CONSTRAINT "FK_5bd11d0e2a9628128e2c26fd0a6"`);
|
||||||
|
await queryRunner.query(`COMMENT ON COLUMN "cart"."type" IS NULL`);
|
||||||
|
await queryRunner.query(`CREATE TYPE "cart_type_enum_old" AS ENUM('default', 'swap', 'payment_link')`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "cart" ALTER COLUMN "type" DROP DEFAULT`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "cart" ALTER COLUMN "type" TYPE "cart_type_enum_old" USING "type"::"text"::"cart_type_enum_old"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "cart" ALTER COLUMN "type" SET DEFAULT 'default'`);
|
||||||
|
await queryRunner.query(`DROP TYPE "cart_type_enum"`);
|
||||||
|
await queryRunner.query(`ALTER TYPE "cart_type_enum_old" RENAME TO "cart_type_enum"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "order" DROP CONSTRAINT "UQ_727b872f86c7378474a8fa46147"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "order" DROP COLUMN "draft_order_id"`);
|
||||||
|
await queryRunner.query(`DROP INDEX "IDX_8f6dd6c49202f1466ebf21e77d"`);
|
||||||
|
await queryRunner.query(`DROP INDEX "IDX_5bd11d0e2a9628128e2c26fd0a"`);
|
||||||
|
await queryRunner.query(`DROP INDEX "IDX_e87cc617a22ef4edce5601edab"`);
|
||||||
|
await queryRunner.query(`DROP TABLE "draft_order"`);
|
||||||
|
await queryRunner.query(`DROP TYPE "draft_order_status_enum"`);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -32,6 +32,7 @@ import { ShippingMethod } from "./shipping-method"
|
|||||||
export enum CartType {
|
export enum CartType {
|
||||||
DEFAULT = "default",
|
DEFAULT = "default",
|
||||||
SWAP = "swap",
|
SWAP = "swap",
|
||||||
|
DRAFT_ORDER = "draft_order",
|
||||||
PAYMENT_LINK = "payment_link",
|
PAYMENT_LINK = "payment_link",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -61,76 +61,6 @@ export class DraftOrder {
|
|||||||
@JoinColumn({ name: "order_id" })
|
@JoinColumn({ name: "order_id" })
|
||||||
order: Order
|
order: Order
|
||||||
|
|
||||||
@Index()
|
|
||||||
@Column()
|
|
||||||
customer_id: string
|
|
||||||
|
|
||||||
@ManyToOne(() => Customer, { cascade: ["insert"] })
|
|
||||||
@JoinColumn({ name: "customer_id" })
|
|
||||||
customer: Customer
|
|
||||||
|
|
||||||
@OneToMany(
|
|
||||||
() => LineItem,
|
|
||||||
lineItem => lineItem.draft_order,
|
|
||||||
{ cascade: ["insert", "remove"] }
|
|
||||||
)
|
|
||||||
items: LineItem[]
|
|
||||||
|
|
||||||
@Column()
|
|
||||||
email: string
|
|
||||||
|
|
||||||
@Index()
|
|
||||||
@Column({ nullable: true })
|
|
||||||
billing_address_id: string
|
|
||||||
|
|
||||||
@ManyToOne(() => Address, { cascade: ["insert"] })
|
|
||||||
@JoinColumn({ name: "billing_address_id" })
|
|
||||||
billing_address: Address
|
|
||||||
|
|
||||||
@Index()
|
|
||||||
@Column({ nullable: true })
|
|
||||||
shipping_address_id: string
|
|
||||||
|
|
||||||
@ManyToOne(() => Address, { cascade: ["insert"] })
|
|
||||||
@JoinColumn({ name: "shipping_address_id" })
|
|
||||||
shipping_address: Address
|
|
||||||
|
|
||||||
@Index()
|
|
||||||
@Column()
|
|
||||||
region_id: string
|
|
||||||
|
|
||||||
@ManyToOne(() => Region)
|
|
||||||
@JoinColumn({ name: "region_id" })
|
|
||||||
region: Region
|
|
||||||
|
|
||||||
@ManyToMany(() => Discount, { cascade: ["insert"] })
|
|
||||||
@JoinTable({
|
|
||||||
name: "draft_order_discounts",
|
|
||||||
joinColumn: {
|
|
||||||
name: "draft_order_id",
|
|
||||||
referencedColumnName: "id",
|
|
||||||
},
|
|
||||||
inverseJoinColumn: {
|
|
||||||
name: "discount_id",
|
|
||||||
referencedColumnName: "id",
|
|
||||||
},
|
|
||||||
})
|
|
||||||
discounts: Discount[]
|
|
||||||
|
|
||||||
@OneToMany(
|
|
||||||
() => ShippingMethod,
|
|
||||||
m => m.draft_order,
|
|
||||||
{ cascade: ["soft-remove", "remove"] }
|
|
||||||
)
|
|
||||||
shipping_methods: ShippingMethod[]
|
|
||||||
|
|
||||||
@OneToMany(
|
|
||||||
() => Payment,
|
|
||||||
p => p.draft_order,
|
|
||||||
{ cascade: ["insert"] }
|
|
||||||
)
|
|
||||||
payments: Payment[]
|
|
||||||
|
|
||||||
@Column({ nullable: true, type: "timestamptz" })
|
@Column({ nullable: true, type: "timestamptz" })
|
||||||
canceled_at: Date
|
canceled_at: Date
|
||||||
|
|
||||||
|
|||||||
@@ -49,17 +49,6 @@ export class LineItem {
|
|||||||
)
|
)
|
||||||
@JoinColumn({ name: "order_id" })
|
@JoinColumn({ name: "order_id" })
|
||||||
order: Order
|
order: Order
|
||||||
|
|
||||||
@Index()
|
|
||||||
@Column({ nullable: true })
|
|
||||||
draft_order_id: string
|
|
||||||
|
|
||||||
@ManyToOne(
|
|
||||||
() => DraftOrder,
|
|
||||||
dorder => dorder.items
|
|
||||||
)
|
|
||||||
@JoinColumn({ name: "draft_order_id" })
|
|
||||||
draft_order: DraftOrder
|
|
||||||
|
|
||||||
@Index()
|
@Index()
|
||||||
@Column({ nullable: true })
|
@Column({ nullable: true })
|
||||||
|
|||||||
@@ -50,14 +50,6 @@ export class Payment {
|
|||||||
@JoinColumn({ name: "order_id" })
|
@JoinColumn({ name: "order_id" })
|
||||||
order: Order
|
order: Order
|
||||||
|
|
||||||
@Index()
|
|
||||||
@Column({ nullable: true })
|
|
||||||
draft_order_id: string
|
|
||||||
|
|
||||||
@OneToOne(() => DraftOrder)
|
|
||||||
@JoinColumn({ name: "draft_order_id" })
|
|
||||||
draft_order: DraftOrder
|
|
||||||
|
|
||||||
@Column({ type: "int" })
|
@Column({ type: "int" })
|
||||||
amount: number
|
amount: number
|
||||||
|
|
||||||
|
|||||||
@@ -68,14 +68,6 @@ export class ShippingMethod {
|
|||||||
@Column({ nullable: true })
|
@Column({ nullable: true })
|
||||||
return_id: string
|
return_id: string
|
||||||
|
|
||||||
@Index()
|
|
||||||
@Column({ nullable: true })
|
|
||||||
draft_order_id: string
|
|
||||||
|
|
||||||
@ManyToOne(() => DraftOrder)
|
|
||||||
@JoinColumn({ name: "draft_order_id" })
|
|
||||||
draft_order: DraftOrder
|
|
||||||
|
|
||||||
@OneToOne(
|
@OneToOne(
|
||||||
() => Return,
|
() => Return,
|
||||||
ret => ret.shipping_method
|
ret => ret.shipping_method
|
||||||
|
|||||||
@@ -0,0 +1,335 @@
|
|||||||
|
import _ from "lodash"
|
||||||
|
import { IdMap, MockRepository, MockManager } from "medusa-test-utils"
|
||||||
|
import DraftOrderService from "../draft-order"
|
||||||
|
|
||||||
|
const eventBusService = {
|
||||||
|
emit: jest.fn(),
|
||||||
|
withTransaction: function() {
|
||||||
|
return this
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("DraftOrderService", () => {
|
||||||
|
const totalsService = {
|
||||||
|
getTotal: o => {
|
||||||
|
return o.total || 0
|
||||||
|
},
|
||||||
|
getSubtotal: o => {
|
||||||
|
return o.subtotal || 0
|
||||||
|
},
|
||||||
|
getTaxTotal: o => {
|
||||||
|
return o.tax_total || 0
|
||||||
|
},
|
||||||
|
getDiscountTotal: o => {
|
||||||
|
return o.discount_total || 0
|
||||||
|
},
|
||||||
|
getShippingTotal: o => {
|
||||||
|
return o.shipping_total || 0
|
||||||
|
},
|
||||||
|
getGiftCardTotal: o => {
|
||||||
|
return o.gift_card_total || 0
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("create", () => {
|
||||||
|
let result
|
||||||
|
|
||||||
|
const regionService = {
|
||||||
|
retrieve: () =>
|
||||||
|
Promise.resolve({ id: "test-region", countries: [{ iso_2: "dk" }] }),
|
||||||
|
withTransaction: function() {
|
||||||
|
return this
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const shippingOptionService = {
|
||||||
|
createShippingMethod: jest.fn().mockImplementation(() =>
|
||||||
|
Promise.resolve({
|
||||||
|
shipping_option: {
|
||||||
|
profile_id: "test-profile",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
),
|
||||||
|
withTransaction: function() {
|
||||||
|
return this
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const lineItemService = {
|
||||||
|
generate: jest.fn().mockImplementation(() =>
|
||||||
|
Promise.resolve({
|
||||||
|
title: "test-item",
|
||||||
|
variant_id: "test-variant",
|
||||||
|
})
|
||||||
|
),
|
||||||
|
create: jest.fn(),
|
||||||
|
withTransaction: function() {
|
||||||
|
return this
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const productVariantService = {
|
||||||
|
retrieve: () =>
|
||||||
|
Promise.resolve({
|
||||||
|
id: "test-variant",
|
||||||
|
product: {
|
||||||
|
profile_id: "test-profile",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
withTransaction: function() {
|
||||||
|
return this
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const cartService = {
|
||||||
|
create: jest.fn().mockImplementation(data =>
|
||||||
|
Promise.resolve({
|
||||||
|
id: "test-cart",
|
||||||
|
...data,
|
||||||
|
})
|
||||||
|
),
|
||||||
|
withTransaction: function() {
|
||||||
|
return this
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const testOrder = {
|
||||||
|
region_id: "test-region",
|
||||||
|
shipping_address_id: "test-shipping",
|
||||||
|
billing_address_id: "test-billing",
|
||||||
|
customer_id: "test-customer",
|
||||||
|
items: [{ variant_id: "test-variant", quantity: 2, metadata: {} }],
|
||||||
|
shipping_methods: [
|
||||||
|
{
|
||||||
|
option_id: "test-option",
|
||||||
|
data: {},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
const addressRepository = MockRepository({
|
||||||
|
create: addr => ({
|
||||||
|
...addr,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
|
||||||
|
const draftOrderRepository = MockRepository({
|
||||||
|
create: d => ({
|
||||||
|
...d,
|
||||||
|
}),
|
||||||
|
save: d => ({
|
||||||
|
id: "test-draft-order",
|
||||||
|
...d,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
|
||||||
|
const draftOrderService = new DraftOrderService({
|
||||||
|
manager: MockManager,
|
||||||
|
regionService,
|
||||||
|
cartService,
|
||||||
|
shippingOptionService,
|
||||||
|
lineItemService,
|
||||||
|
productVariantService,
|
||||||
|
draftOrderRepository,
|
||||||
|
addressRepository,
|
||||||
|
eventBusService,
|
||||||
|
})
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
jest.clearAllMocks()
|
||||||
|
})
|
||||||
|
|
||||||
|
it("creates a draft order", async () => {
|
||||||
|
await draftOrderService.create(testOrder)
|
||||||
|
|
||||||
|
expect(draftOrderRepository.create).toHaveBeenCalledTimes(1)
|
||||||
|
expect(draftOrderRepository.create).toHaveBeenCalledWith({
|
||||||
|
cart_id: "test-cart",
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(cartService.create).toHaveBeenCalledTimes(1)
|
||||||
|
expect(cartService.create).toHaveBeenCalledWith({
|
||||||
|
region_id: "test-region",
|
||||||
|
shipping_address_id: "test-shipping",
|
||||||
|
billing_address_id: "test-billing",
|
||||||
|
customer_id: "test-customer",
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(shippingOptionService.createShippingMethod).toHaveBeenCalledTimes(
|
||||||
|
1
|
||||||
|
)
|
||||||
|
expect(shippingOptionService.createShippingMethod).toHaveBeenCalledWith(
|
||||||
|
"test-option",
|
||||||
|
{},
|
||||||
|
{
|
||||||
|
cart: {
|
||||||
|
id: "test-cart",
|
||||||
|
region_id: "test-region",
|
||||||
|
shipping_address_id: "test-shipping",
|
||||||
|
billing_address_id: "test-billing",
|
||||||
|
customer_id: "test-customer",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(lineItemService.generate).toHaveBeenCalledTimes(1)
|
||||||
|
expect(lineItemService.generate).toHaveBeenCalledWith(
|
||||||
|
"test-variant",
|
||||||
|
"test-region",
|
||||||
|
2,
|
||||||
|
{}
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(lineItemService.create).toHaveBeenCalledTimes(1)
|
||||||
|
expect(lineItemService.create).toHaveBeenCalledWith({
|
||||||
|
cart_id: "test-cart",
|
||||||
|
has_shipping: true,
|
||||||
|
title: "test-item",
|
||||||
|
variant_id: "test-variant",
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it("fails on missing region", async () => {
|
||||||
|
try {
|
||||||
|
await draftOrderService.create({
|
||||||
|
items: [],
|
||||||
|
})
|
||||||
|
} catch (error) {
|
||||||
|
expect(error.message).toEqual(
|
||||||
|
`region_id is required to create a draft order`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
it("fails on missing items", async () => {
|
||||||
|
try {
|
||||||
|
await draftOrderService.create({
|
||||||
|
region_id: "test-region",
|
||||||
|
items: [],
|
||||||
|
})
|
||||||
|
} catch (error) {
|
||||||
|
expect(error.message).toEqual(
|
||||||
|
`Items are required to create a draft order`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("registerSystemPayment", () => {
|
||||||
|
let result
|
||||||
|
|
||||||
|
const shippingOptionService = {
|
||||||
|
update: jest.fn(),
|
||||||
|
withTransaction: function() {
|
||||||
|
return this
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const lineItemService = {
|
||||||
|
update: jest.fn(),
|
||||||
|
withTransaction: function() {
|
||||||
|
return this
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const cartService = {
|
||||||
|
retrieve: jest.fn().mockImplementation(data =>
|
||||||
|
Promise.resolve({
|
||||||
|
id: "test-cart",
|
||||||
|
total: 1000,
|
||||||
|
region_id: "test-region",
|
||||||
|
region: {
|
||||||
|
id: "test-region",
|
||||||
|
currency_code: "usd",
|
||||||
|
tax_rate: 0,
|
||||||
|
},
|
||||||
|
items: [{ id: "test-item" }],
|
||||||
|
discounts: [],
|
||||||
|
email: "oli@test.dk",
|
||||||
|
customer_id: "test-customer",
|
||||||
|
draft_order_id: "test-draft-order",
|
||||||
|
metadata: {},
|
||||||
|
})
|
||||||
|
),
|
||||||
|
withTransaction: function() {
|
||||||
|
return this
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const draftOrderRepository = MockRepository({
|
||||||
|
findOne: () =>
|
||||||
|
Promise.resolve({ id: "test-draft-order", cart_id: "test-cart" }),
|
||||||
|
})
|
||||||
|
const orderRepository = MockRepository({
|
||||||
|
create: d => ({ id: "test-order", ...d }),
|
||||||
|
})
|
||||||
|
const paymentRepository = MockRepository({
|
||||||
|
create: d => ({
|
||||||
|
...d,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
|
||||||
|
const draftOrderService = new DraftOrderService({
|
||||||
|
manager: MockManager,
|
||||||
|
cartService,
|
||||||
|
shippingOptionService,
|
||||||
|
lineItemService,
|
||||||
|
paymentRepository,
|
||||||
|
draftOrderRepository,
|
||||||
|
orderRepository,
|
||||||
|
eventBusService,
|
||||||
|
})
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
jest.clearAllMocks()
|
||||||
|
})
|
||||||
|
|
||||||
|
it("registers system payment", async () => {
|
||||||
|
await draftOrderService.registerSystemPayment("test-draft-order")
|
||||||
|
|
||||||
|
expect(cartService.retrieve).toHaveBeenCalledTimes(1)
|
||||||
|
expect(cartService.retrieve).toHaveBeenCalledWith("test-cart", {
|
||||||
|
relations: ["discounts", "shipping_methods", "region", "items"],
|
||||||
|
select: ["total"],
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(paymentRepository.create).toHaveBeenCalledTimes(1)
|
||||||
|
expect(paymentRepository.create).toHaveBeenCalledWith({
|
||||||
|
provider_id: "system",
|
||||||
|
amount: 1000,
|
||||||
|
currency_code: "usd",
|
||||||
|
data: {},
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(orderRepository.create).toHaveBeenCalledTimes(1)
|
||||||
|
expect(orderRepository.create).toHaveBeenCalledWith({
|
||||||
|
cart_id: "test-cart",
|
||||||
|
discounts: [],
|
||||||
|
region_id: "test-region",
|
||||||
|
discounts: [],
|
||||||
|
email: "oli@test.dk",
|
||||||
|
customer_id: "test-customer",
|
||||||
|
draft_order_id: "test-draft-order",
|
||||||
|
tax_rate: 0,
|
||||||
|
payment_status: "awaiting",
|
||||||
|
currency_code: "usd",
|
||||||
|
metadata: {},
|
||||||
|
payments: [
|
||||||
|
{
|
||||||
|
provider_id: "system",
|
||||||
|
amount: 1000,
|
||||||
|
currency_code: "usd",
|
||||||
|
data: {},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(draftOrderRepository.save).toHaveBeenCalledTimes(1)
|
||||||
|
expect(draftOrderRepository.save).toHaveBeenCalledWith({
|
||||||
|
id: "test-draft-order",
|
||||||
|
cart_id: "test-cart",
|
||||||
|
status: "completed",
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -958,6 +958,8 @@ class CartService extends BaseService {
|
|||||||
relations: ["payment_sessions"],
|
relations: ["payment_sessions"],
|
||||||
})
|
})
|
||||||
|
|
||||||
|
console.log("YELLO: ", session.status)
|
||||||
|
|
||||||
if (session.status === "authorized") {
|
if (session.status === "authorized") {
|
||||||
const payment = await this.paymentProviderService_
|
const payment = await this.paymentProviderService_
|
||||||
.withTransaction(manager)
|
.withTransaction(manager)
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import Scrypt from "scrypt-kdf"
|
|||||||
import _ from "lodash"
|
import _ from "lodash"
|
||||||
import { Validator, MedusaError } from "medusa-core-utils"
|
import { Validator, MedusaError } from "medusa-core-utils"
|
||||||
import { BaseService } from "medusa-interfaces"
|
import { BaseService } from "medusa-interfaces"
|
||||||
|
import { Brackets } from "typeorm"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides layer to manipulate customers.
|
* Provides layer to manipulate customers.
|
||||||
@@ -127,7 +128,34 @@ class CustomerService extends BaseService {
|
|||||||
this.customerRepository_
|
this.customerRepository_
|
||||||
)
|
)
|
||||||
|
|
||||||
|
let q
|
||||||
|
if ("q" in selector) {
|
||||||
|
q = selector.q
|
||||||
|
delete selector.q
|
||||||
|
}
|
||||||
|
|
||||||
const query = this.buildQuery_(selector, config)
|
const query = this.buildQuery_(selector, config)
|
||||||
|
|
||||||
|
if (q) {
|
||||||
|
const where = query.where
|
||||||
|
|
||||||
|
delete where.email
|
||||||
|
delete where.first_name
|
||||||
|
delete where.last_name
|
||||||
|
|
||||||
|
query.where = qb => {
|
||||||
|
qb.where(where)
|
||||||
|
|
||||||
|
qb.andWhere(
|
||||||
|
new Brackets(qb => {
|
||||||
|
qb.where(`email ILIKE :q`, { q: `%${q}%` })
|
||||||
|
.orWhere(`first_name ILIKE :q`, { q: `%${q}%` })
|
||||||
|
.orWhere(`last_name ILIKE :q`, { q: `%${q}%` })
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return customerRepo.find(query)
|
return customerRepo.find(query)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import _ from "lodash"
|
import _ from "lodash"
|
||||||
import { BaseService } from "medusa-interfaces"
|
import { BaseService } from "medusa-interfaces"
|
||||||
import { MedusaError } from "medusa-core-utils"
|
import { MedusaError, Validator } from "medusa-core-utils"
|
||||||
|
import { Brackets } from "typeorm"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles swaps
|
* Handles swaps
|
||||||
@@ -14,11 +15,15 @@ class DraftOrderService extends BaseService {
|
|||||||
constructor({
|
constructor({
|
||||||
manager,
|
manager,
|
||||||
draftOrderRepository,
|
draftOrderRepository,
|
||||||
|
paymentRepository,
|
||||||
|
orderRepository,
|
||||||
eventBusService,
|
eventBusService,
|
||||||
addressRepository,
|
addressRepository,
|
||||||
cartService,
|
cartService,
|
||||||
|
customerService,
|
||||||
totalsService,
|
totalsService,
|
||||||
lineItemService,
|
lineItemService,
|
||||||
|
paymentProviderService,
|
||||||
productVariantService,
|
productVariantService,
|
||||||
shippingOptionService,
|
shippingOptionService,
|
||||||
regionService,
|
regionService,
|
||||||
@@ -28,9 +33,15 @@ class DraftOrderService extends BaseService {
|
|||||||
/** @private @const {EntityManager} */
|
/** @private @const {EntityManager} */
|
||||||
this.manager_ = manager
|
this.manager_ = manager
|
||||||
|
|
||||||
/** @private @const {SwapModel} */
|
/** @private @const {DraftOrderRepository} */
|
||||||
this.draftOrderRepository_ = draftOrderRepository
|
this.draftOrderRepository_ = draftOrderRepository
|
||||||
|
|
||||||
|
/** @private @const {PaymentRepository} */
|
||||||
|
this.paymentRepository_ = paymentRepository
|
||||||
|
|
||||||
|
/** @private @const {OrderRepository} */
|
||||||
|
this.orderRepository_ = orderRepository
|
||||||
|
|
||||||
/** @private @const {TotalsService} */
|
/** @private @const {TotalsService} */
|
||||||
this.totalsService_ = totalsService
|
this.totalsService_ = totalsService
|
||||||
|
|
||||||
@@ -40,15 +51,18 @@ class DraftOrderService extends BaseService {
|
|||||||
/** @private @const {LineItemService} */
|
/** @private @const {LineItemService} */
|
||||||
this.lineItemService_ = lineItemService
|
this.lineItemService_ = lineItemService
|
||||||
|
|
||||||
/** @private @const {ReturnService} */
|
|
||||||
this.returnService_ = returnService
|
|
||||||
|
|
||||||
/** @private @const {CartService} */
|
/** @private @const {CartService} */
|
||||||
this.cartService_ = cartService
|
this.cartService_ = cartService
|
||||||
|
|
||||||
|
/** @private @const {CustomerService} */
|
||||||
|
this.customerService_ = customerService
|
||||||
|
|
||||||
/** @private @const {RegionService} */
|
/** @private @const {RegionService} */
|
||||||
this.regionService_ = regionService
|
this.regionService_ = regionService
|
||||||
|
|
||||||
|
/** @private @const {PaymentProviderService} */
|
||||||
|
this.paymentProviderService_ = paymentProviderService
|
||||||
|
|
||||||
/** @private @const {ProductVariantService} */
|
/** @private @const {ProductVariantService} */
|
||||||
this.productVariantService_ = productVariantService
|
this.productVariantService_ = productVariantService
|
||||||
|
|
||||||
@@ -67,10 +81,16 @@ class DraftOrderService extends BaseService {
|
|||||||
const cloned = new DraftOrderService({
|
const cloned = new DraftOrderService({
|
||||||
manager: transactionManager,
|
manager: transactionManager,
|
||||||
draftOrderRepository: this.draftOrderRepository_,
|
draftOrderRepository: this.draftOrderRepository_,
|
||||||
|
orderRepository: this.orderRepository_,
|
||||||
|
paymentRepository: this.paymentRepository_,
|
||||||
|
paymentProviderService: this.paymentProviderService_,
|
||||||
|
regionService: this.regionService_,
|
||||||
eventBusService: this.eventBus_,
|
eventBusService: this.eventBus_,
|
||||||
cartService: this.cartService_,
|
cartService: this.cartService_,
|
||||||
totalsService: this.totalsService_,
|
totalsService: this.totalsService_,
|
||||||
productVariantService: this.productVariantService_,
|
productVariantService: this.productVariantService_,
|
||||||
|
addressRepository: this.addressRepository_,
|
||||||
|
shippingOptionService: this.shippingOptionService_,
|
||||||
lineItemService: this.lineItemService_,
|
lineItemService: this.lineItemService_,
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -79,6 +99,68 @@ class DraftOrderService extends BaseService {
|
|||||||
return cloned
|
return cloned
|
||||||
}
|
}
|
||||||
|
|
||||||
|
transformQueryForTotals_(config) {
|
||||||
|
let { select, relations } = config
|
||||||
|
|
||||||
|
if (!select) {
|
||||||
|
return {
|
||||||
|
select,
|
||||||
|
relations,
|
||||||
|
totalsToSelect: [],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const totalFields = [
|
||||||
|
"subtotal",
|
||||||
|
"tax_total",
|
||||||
|
"shipping_total",
|
||||||
|
"discount_total",
|
||||||
|
"total",
|
||||||
|
]
|
||||||
|
|
||||||
|
const totalsToSelect = select.filter(v => totalFields.includes(v))
|
||||||
|
if (totalsToSelect.length > 0) {
|
||||||
|
const relationSet = new Set(relations)
|
||||||
|
relationSet.add("items")
|
||||||
|
relationSet.add("discounts")
|
||||||
|
relationSet.add("shipping_methods")
|
||||||
|
relationSet.add("region")
|
||||||
|
relations = [...relationSet]
|
||||||
|
|
||||||
|
select = select.filter(v => !totalFields.includes(v))
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
relations,
|
||||||
|
select,
|
||||||
|
totalsToSelect,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
decorateTotals_(draftOrder, totalsFields = []) {
|
||||||
|
if (totalsFields.includes("shipping_total")) {
|
||||||
|
draftOrder.shipping_total = this.totalsService_.getShippingTotal(
|
||||||
|
draftOrder
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (totalsFields.includes("discount_total")) {
|
||||||
|
draftOrder.discount_total = this.totalsService_.getDiscountTotal(
|
||||||
|
draftOrder
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (totalsFields.includes("tax_total")) {
|
||||||
|
draftOrder.tax_total = this.totalsService_.getTaxTotal(draftOrder)
|
||||||
|
}
|
||||||
|
if (totalsFields.includes("subtotal")) {
|
||||||
|
draftOrder.subtotal = this.totalsService_.getSubtotal(draftOrder)
|
||||||
|
}
|
||||||
|
if (totalsFields.includes("total")) {
|
||||||
|
draftOrder.total = this.totalsService_.getTotal(draftOrder)
|
||||||
|
}
|
||||||
|
|
||||||
|
return draftOrder
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves a draft order with the given id.
|
* Retrieves a draft order with the given id.
|
||||||
* @param {string} id - id of the draft order to retrieve
|
* @param {string} id - id of the draft order to retrieve
|
||||||
@@ -91,17 +173,32 @@ class DraftOrderService extends BaseService {
|
|||||||
|
|
||||||
const validatedId = this.validateId_(id)
|
const validatedId = this.validateId_(id)
|
||||||
|
|
||||||
const query = this.buildQuery_({ id: validatedId }, config)
|
const { select, relations, totalsToSelect } = this.transformQueryForTotals_(
|
||||||
|
config
|
||||||
|
)
|
||||||
|
|
||||||
const draftOrder = await draftOrderRepo.findOne(query)
|
const query = {
|
||||||
|
where: { id: validatedId },
|
||||||
|
}
|
||||||
|
|
||||||
if (!draftOrder) {
|
if (relations && relations.length > 0) {
|
||||||
|
query.relations = relations
|
||||||
|
}
|
||||||
|
|
||||||
|
if (select && select.length > 0) {
|
||||||
|
query.select = select
|
||||||
|
}
|
||||||
|
|
||||||
|
const raw = await draftOrderRepo.findOne(query)
|
||||||
|
|
||||||
|
if (!raw) {
|
||||||
throw new MedusaError(
|
throw new MedusaError(
|
||||||
MedusaError.Types.NOT_FOUND,
|
MedusaError.Types.NOT_FOUND,
|
||||||
`Draft order with id: ${id} was not found`
|
`Draft order with ${id} was not found`
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const draftOrder = this.decorateTotals_(raw, totalsToSelect)
|
||||||
return draftOrder
|
return draftOrder
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -132,6 +229,65 @@ class DraftOrderService extends BaseService {
|
|||||||
return draftOrder
|
return draftOrder
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async listAndCount(
|
||||||
|
selector,
|
||||||
|
config = { skip: 0, take: 50, order: { created_at: "DESC" } }
|
||||||
|
) {
|
||||||
|
const draftOrderRepository = this.manager_.getCustomRepository(
|
||||||
|
this.draftOrderRepository_
|
||||||
|
)
|
||||||
|
|
||||||
|
let q
|
||||||
|
if ("q" in selector) {
|
||||||
|
q = selector.q
|
||||||
|
delete selector.q
|
||||||
|
}
|
||||||
|
|
||||||
|
const query = this.buildQuery_(selector, config)
|
||||||
|
|
||||||
|
if (q) {
|
||||||
|
const where = query.where
|
||||||
|
|
||||||
|
delete where.display_id
|
||||||
|
|
||||||
|
query.join = {
|
||||||
|
alias: "draftOrder",
|
||||||
|
innerJoin: {
|
||||||
|
cart: "draftOrder.cart",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
query.where = qb => {
|
||||||
|
qb.where(where)
|
||||||
|
|
||||||
|
qb.andWhere(
|
||||||
|
new Brackets(qb => {
|
||||||
|
qb.where(`cart.email ILIKE :q`, {
|
||||||
|
q: `%${q}%`,
|
||||||
|
}).orWhere(`display_id::varchar(255) ILIKE :dId`, { dId: `${q}` })
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const { select, relations, totalsToSelect } = this.transformQueryForTotals_(
|
||||||
|
config
|
||||||
|
)
|
||||||
|
|
||||||
|
if (select && select.length) {
|
||||||
|
query.select = select
|
||||||
|
}
|
||||||
|
|
||||||
|
if (relations && relations.length) {
|
||||||
|
query.relations = relations
|
||||||
|
}
|
||||||
|
|
||||||
|
const [raw, count] = await draftOrderRepository.findAndCount(query)
|
||||||
|
const draftOrders = raw.map(r => this.decorateTotals_(r, totalsToSelect))
|
||||||
|
|
||||||
|
return [draftOrders, count]
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Lists draft orders
|
* Lists draft orders
|
||||||
* @param {Object} selector - query object for find
|
* @param {Object} selector - query object for find
|
||||||
@@ -151,31 +307,6 @@ class DraftOrderService extends BaseService {
|
|||||||
return draftOrderRepo.find(query)
|
return draftOrderRepo.find(query)
|
||||||
}
|
}
|
||||||
|
|
||||||
async setAddress_(region, address) {
|
|
||||||
const addressRepo = this.manager_.getCustomRepository(
|
|
||||||
this.addressRepository_
|
|
||||||
)
|
|
||||||
|
|
||||||
const regCountries = region.countries.map(({ iso_2 }) => iso_2)
|
|
||||||
|
|
||||||
if (!address.country_code) {
|
|
||||||
throw new MedusaError(
|
|
||||||
MedusaError.Types.INVALID_DATA,
|
|
||||||
`Address is missing country code`
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!regCountries.includes(address.country_code)) {
|
|
||||||
throw new MedusaError(
|
|
||||||
MedusaError.Types.INVALID_DATA,
|
|
||||||
`Country ${address.country_code} is not in region ${region.name}`
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const created = addressRepo.create(adress)
|
|
||||||
return created
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Confirms if the contents of a line item is covered by the inventory.
|
* Confirms if the contents of a line item is covered by the inventory.
|
||||||
* To be covered a variant must either not have its inventory managed or it
|
* To be covered a variant must either not have its inventory managed or it
|
||||||
@@ -197,86 +328,13 @@ class DraftOrderService extends BaseService {
|
|||||||
return this.productVariantService_.canCoverQuantity(variantId, quantity)
|
return this.productVariantService_.canCoverQuantity(variantId, quantity)
|
||||||
}
|
}
|
||||||
|
|
||||||
async addLineItem(doId, lineItem) {
|
|
||||||
return this.atomicPhase_(async manager => {
|
|
||||||
const draftOrder = await this.retrieve(doId, {
|
|
||||||
relations: [
|
|
||||||
"shipping_methods",
|
|
||||||
"items",
|
|
||||||
"payment_sessions",
|
|
||||||
"items.variant",
|
|
||||||
"items.variant.product",
|
|
||||||
],
|
|
||||||
})
|
|
||||||
|
|
||||||
if (draftOrder.status !== "open") {
|
|
||||||
throw new MedusaError(
|
|
||||||
MedusaError.Types.NOT_ALLOWED,
|
|
||||||
"You are not allowed to add items to a draft order with status awaiting or completed"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
let currentItem
|
|
||||||
if (lineItem.should_merge) {
|
|
||||||
currentItem = draftOrder.items.find(line => {
|
|
||||||
if (line.should_merge && line.variant_id === lineItem.variant_id) {
|
|
||||||
return _.isEqual(line.metadata, lineItem.metadata)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// If content matches one of the line items currently in the cart we can
|
|
||||||
// simply update the quantity of the existing line item
|
|
||||||
if (currentItem) {
|
|
||||||
const newQuantity = currentItem.quantity + lineItem.quantity
|
|
||||||
|
|
||||||
// Confirm inventory
|
|
||||||
const hasInventory = await this.confirmInventory_(
|
|
||||||
lineItem.variant_id,
|
|
||||||
newQuantity
|
|
||||||
)
|
|
||||||
|
|
||||||
if (!hasInventory) {
|
|
||||||
throw new MedusaError(
|
|
||||||
MedusaError.Types.NOT_ALLOWED,
|
|
||||||
"Inventory doesn't cover the desired quantity"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
await this.lineItemService_
|
|
||||||
.withTransaction(manager)
|
|
||||||
.update(currentItem.id, {
|
|
||||||
quantity: newQuantity,
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
// Confirm inventory
|
|
||||||
const hasInventory = await this.confirmInventory_(
|
|
||||||
lineItem.variant_id,
|
|
||||||
lineItem.quantity
|
|
||||||
)
|
|
||||||
|
|
||||||
if (!hasInventory) {
|
|
||||||
throw new MedusaError(
|
|
||||||
MedusaError.Types.NOT_ALLOWED,
|
|
||||||
"Inventory doesn't cover the desired quantity"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
await this.lineItemService_.withTransaction(manager).create({
|
|
||||||
...lineItem,
|
|
||||||
has_shipping: false,
|
|
||||||
cart_id: cartId,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a draft order.
|
* Creates a draft order.
|
||||||
* @param {Object} data - data to create draft order from
|
* @param {object} data - data to create draft order from
|
||||||
|
* @param {boolean} shippingRequired - needs shipping flag
|
||||||
* @return {Promise<DraftOrder>} the created draft order
|
* @return {Promise<DraftOrder>} the created draft order
|
||||||
*/
|
*/
|
||||||
async create(data) {
|
async create(data, shippingRequired = true) {
|
||||||
return this.atomicPhase_(async manager => {
|
return this.atomicPhase_(async manager => {
|
||||||
const draftOrderRepo = manager.getCustomRepository(
|
const draftOrderRepo = manager.getCustomRepository(
|
||||||
this.draftOrderRepository_
|
this.draftOrderRepository_
|
||||||
@@ -289,36 +347,20 @@ class DraftOrderService extends BaseService {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const region = await this.regionService_
|
if (!data.items || !data.items.length) {
|
||||||
.withTransaction(manager)
|
|
||||||
.retrieve(data.region_id, {
|
|
||||||
relations: ["countries"],
|
|
||||||
})
|
|
||||||
|
|
||||||
if (!data.shipping_address && !data.shipping_address_id) {
|
|
||||||
throw new MedusaError(
|
throw new MedusaError(
|
||||||
MedusaError.Types.INVALID_DATA,
|
MedusaError.Types.INVALID_DATA,
|
||||||
`Shipping addresss is required to create a draft order`
|
`Items are required to create a draft order`
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data.shipping_address && !data.shipping_address_id) {
|
|
||||||
data.shipping_address = await this.setAddress_(
|
|
||||||
region,
|
|
||||||
data.shipping_address
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data.billing_address && !data.billing_address_id) {
|
|
||||||
data.billing_address = await this.setAddress_(
|
|
||||||
region,
|
|
||||||
data.billing_address
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const { items, shipping_methods, ...rest } = data
|
const { items, shipping_methods, ...rest } = data
|
||||||
|
|
||||||
const draftOrder = draftOrderRepo.create(rest)
|
const createdCart = await this.cartService_
|
||||||
|
.withTransaction(manager)
|
||||||
|
.create({ type: "draft_order", ...rest })
|
||||||
|
|
||||||
|
const draftOrder = draftOrderRepo.create({ cart_id: createdCart.id })
|
||||||
const result = await draftOrderRepo.save(draftOrder)
|
const result = await draftOrderRepo.save(draftOrder)
|
||||||
|
|
||||||
await this.eventBus_
|
await this.eventBus_
|
||||||
@@ -328,31 +370,140 @@ class DraftOrderService extends BaseService {
|
|||||||
})
|
})
|
||||||
|
|
||||||
let shippingMethods = []
|
let shippingMethods = []
|
||||||
for (const method of shipping_methods) {
|
let profiles = []
|
||||||
const m = await this.shippingOptionService_
|
if (shippingRequired) {
|
||||||
.withTransaction(manager)
|
for (const method of shipping_methods) {
|
||||||
.createShippingMethod(method.option_id, method.data, {
|
const m = await this.shippingOptionService_
|
||||||
draft_order_id: draftOrder.id,
|
.withTransaction(manager)
|
||||||
})
|
.createShippingMethod(method.option_id, method.data, {
|
||||||
|
cart: createdCart,
|
||||||
|
})
|
||||||
|
|
||||||
shippingMethods.push(m)
|
shippingMethods.push(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
profiles = shippingMethods.map(
|
||||||
|
({ shipping_option }) => shipping_option.profile_id
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const item of items) {
|
for (const item of items) {
|
||||||
const line = await this.lineItemService_
|
if (item.variant_id) {
|
||||||
.withTransaction(manager)
|
const line = await this.lineItemService_
|
||||||
.generate(
|
.withTransaction(manager)
|
||||||
item.variant_id,
|
.generate(
|
||||||
cart.region_id,
|
item.variant_id,
|
||||||
item.quantity,
|
data.region_id,
|
||||||
item.metadata
|
item.quantity,
|
||||||
)
|
item.metadata
|
||||||
|
)
|
||||||
|
|
||||||
|
const variant = await this.productVariantService_
|
||||||
|
.withTransaction(manager)
|
||||||
|
.retrieve(item.variant_id)
|
||||||
|
const itemProfile = variant.product.profile_id
|
||||||
|
|
||||||
|
let hasShipping = true
|
||||||
|
|
||||||
|
// if shipping is required, ensure items can be shipped
|
||||||
|
if (shippingRequired) {
|
||||||
|
hasShipping = profiles.includes(itemProfile)
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.lineItemService_.withTransaction(manager).create({
|
||||||
|
cart_id: createdCart.id,
|
||||||
|
has_shipping: hasShipping,
|
||||||
|
...line,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
// custom line items can be added to a draft order
|
||||||
|
await this.lineItemService_.withTransaction(manager).create({
|
||||||
|
cart_id: createdCart.id,
|
||||||
|
has_shipping: true,
|
||||||
|
title: item.title || "Custom item",
|
||||||
|
allow_discounts: false,
|
||||||
|
unit_price: item.unit_price || 0,
|
||||||
|
quantity: item.quantity,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async registerSystemPayment(doId) {
|
||||||
|
return this.atomicPhase_(async manager => {
|
||||||
|
const draftOrder = await this.retrieve(doId)
|
||||||
|
|
||||||
|
const draftOrderCart = await this.cartService_
|
||||||
|
.withTransaction(manager)
|
||||||
|
.retrieve(draftOrder.cart_id, {
|
||||||
|
select: ["total"],
|
||||||
|
relations: ["discounts", "shipping_methods", "region", "items"],
|
||||||
|
})
|
||||||
|
|
||||||
|
const orderRepo = manager.getCustomRepository(this.orderRepository_)
|
||||||
|
const draftOrderRepo = manager.getCustomRepository(
|
||||||
|
this.draftOrderRepository_
|
||||||
|
)
|
||||||
|
|
||||||
|
const paymentRepo = manager.getCustomRepository(this.paymentRepository_)
|
||||||
|
|
||||||
|
const created = paymentRepo.create({
|
||||||
|
provider_id: "system",
|
||||||
|
amount: draftOrderCart.total,
|
||||||
|
currency_code: draftOrderCart.region.currency_code,
|
||||||
|
data: {},
|
||||||
|
})
|
||||||
|
|
||||||
|
const toCreate = {
|
||||||
|
payment_status: "awaiting",
|
||||||
|
discounts: draftOrderCart.discounts,
|
||||||
|
region_id: draftOrderCart.region_id,
|
||||||
|
email: draftOrderCart.email,
|
||||||
|
customer_id: draftOrderCart.customer_id,
|
||||||
|
draft_order_id: draftOrder.id,
|
||||||
|
cart_id: draftOrderCart.id,
|
||||||
|
tax_rate: draftOrderCart.region.tax_rate,
|
||||||
|
currency_code: draftOrderCart.region.currency_code,
|
||||||
|
metadata: draftOrderCart.metadata || {},
|
||||||
|
payments: [created],
|
||||||
|
}
|
||||||
|
|
||||||
|
if (draftOrderCart.shipping_address_id) {
|
||||||
|
toCreate.shipping_address_id = draftOrderCart.shipping_address_id
|
||||||
|
}
|
||||||
|
|
||||||
|
if (draftOrderCart.billing_address_id) {
|
||||||
|
toCreate.billing_address_id = draftOrderCart.billing_address_id
|
||||||
|
}
|
||||||
|
|
||||||
|
if (draftOrderCart.shipping_methods) {
|
||||||
|
toCreate.shipping_methods = draftOrderCart.shipping_methods
|
||||||
|
}
|
||||||
|
|
||||||
|
const o = orderRepo.create(toCreate)
|
||||||
|
|
||||||
|
const result = await orderRepo.save(o)
|
||||||
|
|
||||||
|
if (draftOrderCart.shipping_methods) {
|
||||||
|
for (const method of draftOrderCart.shipping_methods) {
|
||||||
|
await this.shippingOptionService_
|
||||||
|
.withTransaction(manager)
|
||||||
|
.updateShippingMethod(method.id, { order_id: result.id })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const item of draftOrderCart.items) {
|
||||||
await this.lineItemService_
|
await this.lineItemService_
|
||||||
.withTransaction(manager)
|
.withTransaction(manager)
|
||||||
.create({ draft_order_id: draftOrder.id, ...line })
|
.update(item.id, { order_id: result.id })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
draftOrder.status = "completed"
|
||||||
|
await draftOrderRepo.save(draftOrder)
|
||||||
|
|
||||||
return result
|
return result
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -484,7 +484,6 @@ class OrderService extends BaseService {
|
|||||||
payment_status: "awaiting",
|
payment_status: "awaiting",
|
||||||
discounts: cart.discounts,
|
discounts: cart.discounts,
|
||||||
gift_cards: cart.gift_cards,
|
gift_cards: cart.gift_cards,
|
||||||
payment_status: "awaiting",
|
|
||||||
shipping_methods: cart.shipping_methods,
|
shipping_methods: cart.shipping_methods,
|
||||||
shipping_address_id: cart.shipping_address_id,
|
shipping_address_id: cart.shipping_address_id,
|
||||||
billing_address_id: cart.billing_address_id,
|
billing_address_id: cart.billing_address_id,
|
||||||
|
|||||||
@@ -0,0 +1,47 @@
|
|||||||
|
import { BaseService } from "medusa-interfaces"
|
||||||
|
|
||||||
|
class SystemProviderService extends BaseService {
|
||||||
|
static identifier = "system"
|
||||||
|
|
||||||
|
constructor({}) {
|
||||||
|
super()
|
||||||
|
}
|
||||||
|
|
||||||
|
async createPayment(_) {
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
|
||||||
|
async getPaymentData(_) {
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
|
||||||
|
async authorizePayment(_) {
|
||||||
|
return { data: {}, status: "authorized" }
|
||||||
|
}
|
||||||
|
|
||||||
|
async updatePaymentData(_) {
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
|
||||||
|
async updatePayment(_) {
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
|
||||||
|
async deletePayment(_) {
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
|
||||||
|
async capturePayment(_) {
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
|
||||||
|
async refundPayment(_) {
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
|
||||||
|
async cancelPayment(_) {
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default SystemProviderService
|
||||||
Reference in New Issue
Block a user