diff --git a/integration-tests/api/__tests__/admin/draft-order.js b/integration-tests/api/__tests__/admin/draft-order.js index 4144a598ff..d72a6276d8 100644 --- a/integration-tests/api/__tests__/admin/draft-order.js +++ b/integration-tests/api/__tests__/admin/draft-order.js @@ -46,7 +46,9 @@ describe("/admin/draft-orders", () => { await manager.query(`DELETE FROM "product"`); await manager.query(`DELETE FROM "shipping_method"`); await manager.query(`DELETE FROM "shipping_option"`); + await manager.query(`UPDATE "discount" SET rule_id=NULL`); await manager.query(`DELETE FROM "discount"`); + await manager.query(`DELETE FROM "discount_rule"`); await manager.query(`DELETE FROM "payment_provider"`); await manager.query(`DELETE FROM "payment_session"`); await manager.query(`UPDATE "payment" SET order_id=NULL`); @@ -141,12 +143,13 @@ describe("/admin/draft-orders", () => { expect(response.status).toEqual(200); }); - it("creates a draft order with product variant with custom price", async () => { + it("creates a draft order with product variant with custom price and custom item price set to 0", async () => { const api = useApi(); const payload = { email: "oli@test.dk", shipping_address_id: "oli-shipping", + discounts: [{ code: "TEST" }], items: [ { variant_id: "test-variant", @@ -154,6 +157,11 @@ describe("/admin/draft-orders", () => { metadata: {}, unit_price: 10000000, }, + { + quantity: 2, + metadata: {}, + unit_price: -1000, + }, ], region_id: "test-region", customer_id: "oli-test", @@ -185,10 +193,21 @@ describe("/admin/draft-orders", () => { }); expect(response.status).toEqual(200); - expect(created.data.draft_order.cart.items[0]).toEqual( + expect(created.data.draft_order.cart.items).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + variant_id: "test-variant", + unit_price: 10000000, + }), + expect.objectContaining({ + unit_price: 0, + }), + ]) + ); + // Check that discount is applied + expect(created.data.draft_order.cart.discounts[0]).toEqual( expect.objectContaining({ - variant_id: "test-variant", - unit_price: 10000000, + code: "TEST", }) ); }); @@ -303,7 +322,9 @@ describe("/admin/draft-orders", () => { await manager.query(`DELETE FROM "product"`); await manager.query(`DELETE FROM "shipping_method"`); await manager.query(`DELETE FROM "shipping_option"`); + await manager.query(`UPDATE "discount" SET rule_id=NULL`); await manager.query(`DELETE FROM "discount"`); + await manager.query(`DELETE FROM "discount_rule"`); await manager.query(`DELETE FROM "payment_provider"`); await manager.query(`DELETE FROM "payment_session"`); await manager.query(`UPDATE "payment" SET order_id=NULL`); diff --git a/integration-tests/api/helpers/draft-order-seeder.js b/integration-tests/api/helpers/draft-order-seeder.js index ddbd597a4a..0decc69430 100644 --- a/integration-tests/api/helpers/draft-order-seeder.js +++ b/integration-tests/api/helpers/draft-order-seeder.js @@ -10,6 +10,8 @@ const { Cart, PaymentSession, DraftOrder, + Discount, + DiscountRule, } = require("@medusajs/medusa"); module.exports = async (connection, data = {}) => { @@ -107,6 +109,33 @@ module.exports = async (connection, data = {}) => { ], }); + await manager.insert(DiscountRule, { + id: "discount_rule_id", + description: "test description", + value: 10, + allocation: "total", + type: "percentage", + }); + + const d = manager.create(Discount, { + id: "test-discount", + code: "TEST", + is_dynamic: false, + is_disabled: false, + rule_id: "discount_rule_id", + }); + + d.regions = [ + { + id: "test-region", + name: "Test Region", + currency_code: "usd", + tax_rate: 0, + }, + ]; + + await manager.save(d); + await manager.query( `UPDATE "country" SET region_id='test-region' WHERE iso_2 = 'us'` ); diff --git a/packages/medusa/src/api/routes/admin/draft-orders/create-draft-order.js b/packages/medusa/src/api/routes/admin/draft-orders/create-draft-order.js index a9569e1ef6..da3690a085 100644 --- a/packages/medusa/src/api/routes/admin/draft-orders/create-draft-order.js +++ b/packages/medusa/src/api/routes/admin/draft-orders/create-draft-order.js @@ -27,9 +27,12 @@ export default async (req, res) => { }) .required(), region_id: Validator.string().required(), - discounts: Validator.array().optional(), + discounts: Validator.array() + .items({ + code: Validator.string().required(), + }) + .optional(), customer_id: Validator.string().optional(), - customer: Validator.string().optional(), shipping_methods: Validator.array() .items({ option_id: Validator.string().required(), diff --git a/packages/medusa/src/api/routes/admin/draft-orders/create-line-item.js b/packages/medusa/src/api/routes/admin/draft-orders/create-line-item.js index 1f232f939f..8d1ed67e16 100644 --- a/packages/medusa/src/api/routes/admin/draft-orders/create-line-item.js +++ b/packages/medusa/src/api/routes/admin/draft-orders/create-line-item.js @@ -43,8 +43,7 @@ export default async (req, res) => { value.variant_id, draftOrder.cart.region_id, value.quantity, - value.metadata, - value.unit_price + { metadata: value.metadata, unit_price: value.unit_price } ) await cartService diff --git a/packages/medusa/src/api/routes/store/carts/__tests__/create-line-item.js b/packages/medusa/src/api/routes/store/carts/__tests__/create-line-item.js index f603758fb1..04484ebb07 100644 --- a/packages/medusa/src/api/routes/store/carts/__tests__/create-line-item.js +++ b/packages/medusa/src/api/routes/store/carts/__tests__/create-line-item.js @@ -34,7 +34,7 @@ describe("POST /store/carts/:id", () => { IdMap.getId("testVariant"), IdMap.getId("testRegion"), 3, - undefined // no metadata + { metadata: undefined } ) }) @@ -73,7 +73,7 @@ describe("POST /store/carts/:id", () => { IdMap.getId("fail"), IdMap.getId("testRegion"), 3, - undefined // no metdata + { metadata: undefined } ) }) diff --git a/packages/medusa/src/api/routes/store/carts/create-line-item.js b/packages/medusa/src/api/routes/store/carts/create-line-item.js index da804f1eb5..4b034b53d3 100644 --- a/packages/medusa/src/api/routes/store/carts/create-line-item.js +++ b/packages/medusa/src/api/routes/store/carts/create-line-item.js @@ -50,7 +50,7 @@ export default async (req, res) => { value.variant_id, cart.region_id, value.quantity, - value.metadata + { metadata: value.metadata } ) await cartService.withTransaction(m).addLineItem(id, line) diff --git a/packages/medusa/src/models/line-item.ts b/packages/medusa/src/models/line-item.ts index 942c5d0505..3060050646 100644 --- a/packages/medusa/src/models/line-item.ts +++ b/packages/medusa/src/models/line-item.ts @@ -17,7 +17,6 @@ import { Cart } from "./cart" import { Order } from "./order" import { ClaimOrder } from "./claim-order" import { ProductVariant } from "./product-variant" -import { DraftOrder } from "./draft-order" @Check(`"fulfilled_quantity" <= "quantity"`) @Check(`"shipped_quantity" <= "fulfilled_quantity"`) diff --git a/packages/medusa/src/services/__tests__/draft-order.js b/packages/medusa/src/services/__tests__/draft-order.js index ae2aa2a5bf..81736e7347 100644 --- a/packages/medusa/src/services/__tests__/draft-order.js +++ b/packages/medusa/src/services/__tests__/draft-order.js @@ -179,8 +179,7 @@ describe("DraftOrderService", () => { "test-variant", "test-region", 2, - {}, - undefined + { metadata: {}, unit_price: undefined } ) expect(lineItemService.create).toHaveBeenCalledTimes(1) diff --git a/packages/medusa/src/services/cart.js b/packages/medusa/src/services/cart.js index 866c40e93d..198fc8f405 100644 --- a/packages/medusa/src/services/cart.js +++ b/packages/medusa/src/services/cart.js @@ -629,7 +629,7 @@ class CartService extends BaseService { if ("discounts" in update) { cart.discounts = [] for (const { code } of update.discounts) { - await this.applyDiscount_(cart, code) + await this.applyDiscount(cart, code) } } @@ -797,7 +797,7 @@ class CartService extends BaseService { * @param {string} discountCode - the discount code * @return {Promise} the result of the update operation */ - async applyDiscount_(cart, discountCode) { + async applyDiscount(cart, discountCode) { const discount = await this.discountService_.retrieveByCode(discountCode, [ "rule", "regions", diff --git a/packages/medusa/src/services/draft-order.js b/packages/medusa/src/services/draft-order.js index 1d349a15d6..8abc900f4d 100644 --- a/packages/medusa/src/services/draft-order.js +++ b/packages/medusa/src/services/draft-order.js @@ -249,7 +249,16 @@ class DraftOrderService extends BaseService { ) } - const { items, shipping_methods, ...rest } = data + const { items, shipping_methods, discounts, ...rest } = data + + if (discounts) { + for (const { code } of discounts) { + rest.discounts = [] + await this.cartService_ + .withTransaction(manager) + .applyDiscount(rest, code) + } + } const createdCart = await this.cartService_ .withTransaction(manager) @@ -284,13 +293,10 @@ class DraftOrderService extends BaseService { if (item.variant_id) { const line = await this.lineItemService_ .withTransaction(manager) - .generate( - item.variant_id, - data.region_id, - item.quantity, - item.metadata, - item.unit_price - ) + .generate(item.variant_id, data.region_id, item.quantity, { + metadata: item?.metadata || {}, + unit_price: item.unit_price, + }) const variant = await this.productVariantService_ .withTransaction(manager) @@ -305,13 +311,20 @@ class DraftOrderService extends BaseService { ...line, }) } else { + let price + if (typeof item.unit_price === `undefined` || item.unit_price < 0) { + price = 0 + } else { + price = item.unit_price + } + // 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, + unit_price: price, quantity: item.quantity, }) } diff --git a/packages/medusa/src/services/line-item.js b/packages/medusa/src/services/line-item.js index c1947b262e..ed9c14a32b 100644 --- a/packages/medusa/src/services/line-item.js +++ b/packages/medusa/src/services/line-item.js @@ -89,7 +89,7 @@ class LineItemService extends BaseService { return lineItem } - async generate(variantId, regionId, quantity, metadata = {}, unitPrice) { + async generate(variantId, regionId, quantity, config = {}) { const variant = await this.productVariantService_.retrieve(variantId, { relations: ["product"], }) @@ -97,8 +97,13 @@ class LineItemService extends BaseService { const region = await this.regionService_.retrieve(regionId) let price - if (unitPrice) { - price = unitPrice + + if (config.unit_price && typeof config.unit_price !== `undefined`) { + if (config.unit_price < 0) { + price = 0 + } else { + price = config.unit_price + } } else { price = await this.productVariantService_.getRegionPrice( variant.id, @@ -115,7 +120,7 @@ class LineItemService extends BaseService { quantity: quantity || 1, allow_discounts: !variant.product.is_giftcard, is_giftcard: variant.product.is_giftcard, - metadata: metadata || {}, + metadata: config?.metadata || {}, should_merge: true, }