diff --git a/packages/medusa/src/api/routes/admin/orders/__tests__/create-fulfillment.js b/packages/medusa/src/api/routes/admin/orders/__tests__/create-fulfillment.js index 971eb227e6..4a3f04c0ed 100644 --- a/packages/medusa/src/api/routes/admin/orders/__tests__/create-fulfillment.js +++ b/packages/medusa/src/api/routes/admin/orders/__tests__/create-fulfillment.js @@ -1,6 +1,7 @@ import { IdMap } from "medusa-test-utils" import { request } from "../../../../../helpers/test-request" import { OrderServiceMock } from "../../../../../services/__mocks__/order" +import { EventBusServiceMock } from "../../../../../services/__mocks__/event-bus" describe("POST /admin/orders/:id/fulfillment", () => { describe("successfully fulfills an order", () => { @@ -42,6 +43,7 @@ describe("POST /admin/orders/:id/fulfillment", () => { quantity: 1, }, ], + undefined, undefined ) }) @@ -51,5 +53,6 @@ describe("POST /admin/orders/:id/fulfillment", () => { expect(subject.body.order.id).toEqual(IdMap.getId("test-order")) expect(subject.body.order.fulfillment_status).toEqual("fulfilled") }) + }) }) diff --git a/packages/medusa/src/api/routes/admin/orders/create-fulfillment.js b/packages/medusa/src/api/routes/admin/orders/create-fulfillment.js index f8ccab4ac1..4d3351a745 100644 --- a/packages/medusa/src/api/routes/admin/orders/create-fulfillment.js +++ b/packages/medusa/src/api/routes/admin/orders/create-fulfillment.js @@ -24,6 +24,9 @@ import { defaultRelations, defaultFields } from "./" * quantity: * description: The quantity of the Line Item to fulfill. * type: integer + * no_notification: + * description: If set to true no notification will be send related to this Swap. + * type: boolean * metadata: * description: An optional set of key-value pairs to hold additional information. * type: object @@ -49,6 +52,7 @@ export default async (req, res) => { quantity: Validator.number().required(), }) .required(), + no_notification: Validator.boolean().optional(), metadata: Validator.object().optional(), }) @@ -60,7 +64,7 @@ export default async (req, res) => { try { const orderService = req.scope.resolve("orderService") - await orderService.createFulfillment(id, value.items, value.metadata) + await orderService.createFulfillment(id, value.items, value.no_notification, value.metadata) const order = await orderService.retrieve(id, { select: defaultFields, diff --git a/packages/medusa/src/api/routes/admin/orders/create-shipment.js b/packages/medusa/src/api/routes/admin/orders/create-shipment.js index a371ef244f..9c6efdd2dc 100644 --- a/packages/medusa/src/api/routes/admin/orders/create-shipment.js +++ b/packages/medusa/src/api/routes/admin/orders/create-shipment.js @@ -21,6 +21,9 @@ import { defaultRelations, defaultFields } from "./" * type: array * items: * type: string + * no_notification: + * description: If set to true no notification will be send related to this Claim. + * type: boolean * tags: * - Order * responses: @@ -41,6 +44,7 @@ export default async (req, res) => { tracking_numbers: Validator.array() .items(Validator.string()) .optional(), + no_notification: Validator.boolean().optional(), }) const { value, error } = schema.validate(req.body) @@ -54,7 +58,8 @@ export default async (req, res) => { await orderService.createShipment( id, value.fulfillment_id, - value.tracking_numbers.map(n => ({ tracking_number: n })) + value.tracking_numbers.map(n => ({ tracking_number: n })), + value.no_notification, ) const order = await orderService.retrieve(id, { diff --git a/packages/medusa/src/api/routes/admin/orders/refund-payment.js b/packages/medusa/src/api/routes/admin/orders/refund-payment.js index 0bb0ccc616..2c253dea46 100644 --- a/packages/medusa/src/api/routes/admin/orders/refund-payment.js +++ b/packages/medusa/src/api/routes/admin/orders/refund-payment.js @@ -54,6 +54,7 @@ export default async (req, res) => { }) const { value, error } = schema.validate(req.body) + if (error) { throw new MedusaError(MedusaError.Types.INVALID_DATA, error.details) } diff --git a/packages/medusa/src/api/routes/admin/returns/receive-return.js b/packages/medusa/src/api/routes/admin/returns/receive-return.js index dce7cf18f4..404ee7d69d 100644 --- a/packages/medusa/src/api/routes/admin/returns/receive-return.js +++ b/packages/medusa/src/api/routes/admin/returns/receive-return.js @@ -54,10 +54,13 @@ export default async (req, res) => { }) const { value, error } = schema.validate(req.body) + if (error) { + console.log(error) throw new MedusaError(MedusaError.Types.INVALID_DATA, error.details) } + try { const returnService = req.scope.resolve("returnService") const orderService = req.scope.resolve("orderService") diff --git a/packages/medusa/src/services/__tests__/draft-order.js b/packages/medusa/src/services/__tests__/draft-order.js index f151650d73..53c170d310 100644 --- a/packages/medusa/src/services/__tests__/draft-order.js +++ b/packages/medusa/src/services/__tests__/draft-order.js @@ -1,7 +1,9 @@ import _ from "lodash" import { IdMap, MockRepository, MockManager } from "medusa-test-utils" +import { EventBusServiceMock } from "../__mocks__/event-bus" import DraftOrderService from "../draft-order" + const eventBusService = { emit: jest.fn(), withTransaction: function() { @@ -207,8 +209,91 @@ describe("DraftOrderService", () => { }) describe("update", () => { + + 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 completedOrder = { + status: "completed", + ...testOrder + } + + const draftOrderRepository = MockRepository({ + create: d => ({ + ...d, + }), + save: d => ({ + id: "test-draft-order", + ...d, + }), + findOne: (q) => { + switch (q.where.id) { + case "completed": + return Promise.resolve(completedOrder) + default: + return Promise.resolve(testOrder) + } + } + }) + + + const draftOrderService = new DraftOrderService({ + manager: MockManager, + regionService: undefined, + cartService: undefined, + shippingOptionService: undefined, + lineItemService: undefined, + productVariantService: undefined, + draftOrderRepository, + addressRepository: undefined, + eventBusService: EventBusServiceMock, + }) + + beforeEach(async () => { + jest.clearAllMocks() + }) - fail('implement tests for update'); + it("calls draftOrder model functions", async () => { + await draftOrderService.update("test-draft-order", { + no_notification_order: true, + }) + + expect(draftOrderRepository.save).toHaveBeenCalledTimes(1) + expect(draftOrderRepository.save).toHaveBeenCalledWith({ + no_notification_order: true, + billing_address_id: "test-billing", + customer_id: "test-customer", + items: [ { + metadata: {}, + quantity: 2, + variant_id: "test-variant" + }], + region_id: "test-region", + shipping_address_id: "test-shipping", + shipping_methods: [{ + data: {}, + option_id: "test-option" + }] + }) + }) + + it("fails to update draftOrder when already complete", async () => { + await expect( + draftOrderService.update("completed", {}) + ).rejects.toThrow("Can't update a draft order which is complete") + + }) }) }) diff --git a/packages/medusa/src/services/__tests__/order.js b/packages/medusa/src/services/__tests__/order.js index a64b18a688..e2783d38e1 100644 --- a/packages/medusa/src/services/__tests__/order.js +++ b/packages/medusa/src/services/__tests__/order.js @@ -1,5 +1,6 @@ import { IdMap, MockManager, MockRepository } from "medusa-test-utils" import OrderService from "../order" +import { EventBusServiceMock } from "../__mocks__/event-bus" describe("OrderService", () => { const totalsService = { @@ -711,6 +712,7 @@ describe("OrderService", () => { const order = { fulfillments: [], shipping_methods: [{ id: "ship" }], + no_notification: true, items: [ { id: "item_1", @@ -859,8 +861,32 @@ describe("OrderService", () => { fulfillment_status: "partially_fulfilled", }) }) + + it.each([ + [true, true], + [false, false], + [undefined, true], + ])("emits correct no_notification option with '%s'", async (input, expected) => { + await orderService.createFulfillment( + "test-order", + [ + { + item_id: "item_1", + quantity: 1, + }, + ], + input + ) + + expect(eventBusService.emit).toHaveBeenCalledWith(expect.any(String),{ + id: expect.any(String), + no_notification: expected, + }) + }) }) + + describe("registerReturnReceived", () => { const order = { items: [ @@ -975,6 +1001,7 @@ describe("OrderService", () => { fulfilled_quantity: 0, }, ], + no_notification: true, } const orderRepo = MockRepository({ @@ -1048,6 +1075,25 @@ describe("OrderService", () => { fulfillment_status: "shipped", }) }) + + it.each([ + [true, true], + [false, false], + [undefined, true], + ])("2emits correct no_notification option with '%s'", async (input, expected) => { + await orderService.createShipment( + IdMap.getId("test"), + IdMap.getId("fulfillment"), + [{ tracking_number: "1234" }, { tracking_number: "2345" }], + input, + {} + ) + + expect(eventBusService.emit).toHaveBeenCalledWith(expect.any(String),{ + id: expect.any(String), + no_notification: expected, + }) + }) }) describe("createRefund", () => { diff --git a/packages/medusa/src/services/draft-order.js b/packages/medusa/src/services/draft-order.js index dd1ac31a6f..2954d6ef39 100644 --- a/packages/medusa/src/services/draft-order.js +++ b/packages/medusa/src/services/draft-order.js @@ -348,6 +348,13 @@ class DraftOrderService extends BaseService { const doRepo = manager.getCustomRepository(this.draftOrderRepository_) const draftOrder = await this.retrieve(doId) let touched = false + + if(draftOrder.status === "completed"){ + throw new MedusaError( + MedusaError.Types.NOT_ALLOWED, + "Can't update a draft order which is complete" + ) + } if(data.no_notification_order !== undefined){ touched = true diff --git a/packages/medusa/src/services/order.js b/packages/medusa/src/services/order.js index a0fe677397..57637ad4ac 100644 --- a/packages/medusa/src/services/order.js +++ b/packages/medusa/src/services/order.js @@ -574,7 +574,7 @@ class OrderService extends BaseService { * the fulfillment * @return {order} the resulting order following the update. */ - async createShipment(orderId, fulfillmentId, trackingLinks, metadata = {}) { + async createShipment(orderId, fulfillmentId, trackingLinks, noNotification = undefined, metadata = {}) { return this.atomicPhase_(async manager => { const order = await this.retrieve(orderId, { relations: ["items"] }) const shipment = await this.fulfillmentService_.retrieve(fulfillmentId) @@ -612,12 +612,14 @@ class OrderService extends BaseService { const orderRepo = manager.getCustomRepository(this.orderRepository_) const result = await orderRepo.save(order) + const evaluatedNoNotification = noNotification !== undefined ? noNotification : order.no_notification + await this.eventBus_ .withTransaction(manager) .emit(OrderService.Events.SHIPMENT_CREATED, { id: orderId, fulfillment_id: shipmentRes.id, - no_notification: order.no_notification + no_notification: evaluatedNoNotification }) return result @@ -909,7 +911,8 @@ class OrderService extends BaseService { .emit(OrderService.Events.PAYMENT_CAPTURE_FAILED, { id: orderId, payment_id: p.id, - error: err + error: err, + no_notification: order.no_notification }) }) @@ -982,7 +985,7 @@ class OrderService extends BaseService { * @param {string} orderId - id of order to cancel. * @return {Promise} result of the update operation. */ - async createFulfillment(orderId, itemsToFulfill, metadata = {}) { + async createFulfillment(orderId, itemsToFulfill, noNotification = undefined, metadata = {}) { return this.atomicPhase_(async manager => { const order = await this.retrieve(orderId, { select: [ @@ -991,6 +994,8 @@ class OrderService extends BaseService { "discount_total", "tax_total", "gift_card_total", + "no_notification", + "id", "total", ], relations: [ @@ -1058,13 +1063,15 @@ class OrderService extends BaseService { order.fulfillments = [...order.fulfillments, ...fulfillments] const result = await orderRepo.save(order) + const evaluatedNoNotification = noNotification !== undefined ? noNotification : order.no_notification + for (const fulfillment of fulfillments) { await this.eventBus_ .withTransaction(manager) .emit(OrderService.Events.FULFILLMENT_CREATED, { id: orderId, fulfillment_id: fulfillment.id, - no_notification: order.no_notification + no_notification: evaluatedNoNotification }) } @@ -1120,10 +1127,10 @@ class OrderService extends BaseService { /** * Refunds a given amount back to the customer. */ - async createRefund(orderId, refundAmount, reason, note, noNotification) { + async createRefund(orderId, refundAmount, reason, note, noNotification = undefined) { return this.atomicPhase_(async manager => { const order = await this.retrieve(orderId, { - select: ["refundable_amount", "total", "refunded_total", "no_notification"], + select: ["refundable_amount", "total", "refunded_total"], relations: ["payments"], }) @@ -1226,7 +1233,7 @@ class OrderService extends BaseService { async registerReturnReceived(orderId, receivedReturn, customRefundAmount) { return this.atomicPhase_(async manager => { const order = await this.retrieve(orderId, { - select: ["total", "refunded_total", "refundable_amount", "no_notification"], + select: ["total", "refunded_total", "refundable_amount"], relations: ["items", "returns", "payments"], }) @@ -1249,7 +1256,7 @@ class OrderService extends BaseService { .emit(OrderService.Events.RETURN_ACTION_REQUIRED, { id: result.id, return_id: receivedReturn.id, - no_notification: order.no_notification + no_notification: receivedReturn.no_notification }) return result } @@ -1281,7 +1288,7 @@ class OrderService extends BaseService { .emit(OrderService.Events.ITEMS_RETURNED, { id: order.id, return_id: receivedReturn.id, - no_notification: order.no_notification + no_notification: receivedReturn.no_notification }) return result })