From 6a3c5455371c33e47722c7ab433a48d1d9b5b511 Mon Sep 17 00:00:00 2001 From: Oliver Windall Juhl <59018053+olivermrbl@users.noreply.github.com> Date: Fri, 13 Nov 2020 12:43:02 +0100 Subject: [PATCH 1/2] feat(medusa): Adds shipped status to line items + Capture fails will give payment_status = requires_action --- packages/medusa/src/models/__mocks__/order.js | 94 ++++++++++++++++ .../medusa/src/models/schemas/line-item.js | 5 +- .../medusa/src/services/__tests__/order.js | 68 ++++++++++-- packages/medusa/src/services/order.js | 101 +++++++++++++----- 4 files changed, 231 insertions(+), 37 deletions(-) diff --git a/packages/medusa/src/models/__mocks__/order.js b/packages/medusa/src/models/__mocks__/order.js index 1c40847e7c..0ae717af16 100644 --- a/packages/medusa/src/models/__mocks__/order.js +++ b/packages/medusa/src/models/__mocks__/order.js @@ -368,6 +368,97 @@ export const orders = { ], discounts: [], }, + shippedOrder: { + _id: IdMap.getId("shippedOrder"), + email: "oliver@test.dk", + billing_address: { + first_name: "Oli", + last_name: "Medusa", + address_1: "testaddress", + city: "LA", + country_code: "US", + postal_code: "90002", + }, + shipping_address: { + first_name: "Oli", + last_name: "Medusa", + address_1: "testaddress", + city: "LA", + country_code: "US", + postal_code: "90002", + }, + items: [ + { + _id: IdMap.getId("existingLine"), + title: "merge line", + description: "This is a new line", + thumbnail: "test-img-yeah.com/thumb", + content: { + unit_price: 123, + variant: { + _id: IdMap.getId("can-cover"), + }, + product: { + _id: IdMap.getId("validId"), + }, + quantity: 1, + }, + fulfilled_quantity: 10, + shipped_quantity: 0, + quantity: 10, + }, + ], + region_id: IdMap.getId("region-france"), + customer_id: IdMap.getId("test-customer"), + payment_method: { + provider_id: "default_provider", + }, + shipping_methods: [ + { + _id: IdMap.getId("expensiveShipping"), + name: "Expensive Shipping", + price: 100, + provider_id: "default_provider", + profile_id: IdMap.getId("default"), + data: { + extra: "hi", + }, + }, + ], + fulfillments: [ + { + _id: IdMap.getId("fulfillment"), + provider_id: "default_provider", + data: {}, + items: [ + { + _id: IdMap.getId("existingLine"), + content: { + product: { + _id: IdMap.getId("validId"), + }, + quantity: 1, + unit_price: 123, + variant: { + _id: IdMap.getId("can-cover"), + }, + }, + description: "This is a new line", + fulfilled_quantity: 10, + quantity: 10, + thumbnail: "test-img-yeah.com/thumb", + title: "merge line", + }, + ], + }, + ], + fulfillment_status: "not_fulfilled", + payment_status: "awaiting", + status: "pending", + metadata: { + cart_id: IdMap.getId("test-cart"), + }, + }, } export const OrderModelMock = { @@ -420,6 +511,9 @@ export const OrderModelMock = { if (query.cart_id === IdMap.getId("test-cart")) { return Promise.resolve(orders.testOrder) } + if (query._id === IdMap.getId("shippedOrder")) { + return Promise.resolve(orders.shippedOrder) + } return Promise.resolve(undefined) }), } diff --git a/packages/medusa/src/models/schemas/line-item.js b/packages/medusa/src/models/schemas/line-item.js index 3964a56424..1f2e6a1f30 100644 --- a/packages/medusa/src/models/schemas/line-item.js +++ b/packages/medusa/src/models/schemas/line-item.js @@ -4,8 +4,8 @@ import mongoose from "mongoose" /** - * REMEMBER: When updating this line you must also update the LineItemService's - * validate method too. Otherwise we cannot copy lines directly. + * REMEMBER: When updating this line you must also update the LineItemService's + * validate method too. Otherwise we cannot copy lines directly. */ export default new mongoose.Schema( { @@ -45,6 +45,7 @@ export default new mongoose.Schema( fulfilled: { type: Boolean, default: false }, fulfilled_quantity: { type: Number, default: 0 }, returned_quantity: { type: Number, default: 0 }, + shipped_quantity: { type: Number, default: 0 }, metadata: { type: mongoose.Schema.Types.Mixed, default: {} }, }, { minimize: false } diff --git a/packages/medusa/src/services/__tests__/order.js b/packages/medusa/src/services/__tests__/order.js index d6eee86ca8..6c36b18282 100644 --- a/packages/medusa/src/services/__tests__/order.js +++ b/packages/medusa/src/services/__tests__/order.js @@ -509,7 +509,8 @@ describe("OrderService", () => { }, quantity: 1, }, - fulfilled_quantity: 0, + fulfilled_quantity: 10, + fulfilled: true, quantity: 10, }, ], @@ -1121,7 +1122,7 @@ describe("OrderService", () => { it("calls order model functions", async () => { await orderService.createShipment( - IdMap.getId("test-order"), + IdMap.getId("shippedOrder"), IdMap.getId("fulfillment"), ["1234", "2345"], {} @@ -1130,19 +1131,64 @@ describe("OrderService", () => { expect(OrderModelMock.updateOne).toHaveBeenCalledTimes(1) expect(OrderModelMock.updateOne).toHaveBeenCalledWith( { - _id: IdMap.getId("test-order"), + _id: IdMap.getId("shippedOrder"), "fulfillments._id": IdMap.getId("fulfillment"), }, { $set: { - "fulfillments.$": { - _id: IdMap.getId("fulfillment"), - provider_id: "default_provider", - tracking_numbers: ["1234", "2345"], - data: {}, - shipped_at: expect.anything(), - metadata: {}, - }, + "fulfillments.$": [ + { + _id: IdMap.getId("fulfillment"), + provider_id: "default_provider", + tracking_numbers: ["1234", "2345"], + data: {}, + items: [ + { + _id: IdMap.getId("existingLine"), + content: { + product: { + _id: IdMap.getId("validId"), + }, + quantity: 1, + unit_price: 123, + variant: { + _id: IdMap.getId("can-cover"), + }, + }, + description: "This is a new line", + fulfilled_quantity: 10, + shipped_quantity: 10, + quantity: 10, + thumbnail: "test-img-yeah.com/thumb", + title: "merge line", + }, + ], + shipped_at: expect.anything(), + metadata: {}, + }, + ], + items: [ + { + _id: IdMap.getId("existingLine"), + content: { + product: { + _id: IdMap.getId("validId"), + }, + quantity: 1, + unit_price: 123, + variant: { + _id: IdMap.getId("can-cover"), + }, + }, + description: "This is a new line", + fulfilled_quantity: 10, + shipped_quantity: 10, + quantity: 10, + thumbnail: "test-img-yeah.com/thumb", + title: "merge line", + }, + ], + fulfillment_status: "shipped", }, } ) diff --git a/packages/medusa/src/services/order.js b/packages/medusa/src/services/order.js index cbfa20b3e1..828797f774 100644 --- a/packages/medusa/src/services/order.js +++ b/packages/medusa/src/services/order.js @@ -6,6 +6,7 @@ class OrderService extends BaseService { static Events = { GIFT_CARD_CREATED: "order.gift_card_created", PAYMENT_CAPTURED: "order.payment_captured", + PAYMENT_CAPTURE_FAILED: "order.payment_capture_failed", SHIPMENT_CREATED: "order.shipment_created", FULFILLMENT_CREATED: "order.fulfillment_created", RETURN_REQUESTED: "order.return_requested", @@ -418,22 +419,39 @@ class OrderService extends BaseService { async createShipment(orderId, fulfillmentId, trackingNumbers, metadata = {}) { const order = await this.retrieve(orderId) - const shipment = order.fulfillments.find(f => f._id.equals(fulfillmentId)) - if (!shipment) { - throw new MedusaError( - MedusaError.Types.NOT_FOUND, - `Could not find a fulfillment with the provided id` - ) - } + let shipment + const updated = order.fulfillments.map(f => { + if (f._id.equals(fulfillmentId)) { + // For each item in the shipment, we set their status to shipped + f.items.map(item => { + const itemIdx = order.items.findIndex(el => el._id.equals(item._id)) + // Update item in order.items and in fullfillment.items to + // ensure consistency + if (item !== -1) { + item.shipped_quantity = item.quantity + order.items[itemIdx].shipped_quantity += item.quantity + } + }) + shipment = { + ...f, + tracking_numbers: trackingNumbers, + shipped_at: Date.now(), + metadata: { + ...f.metadata, + ...metadata, + }, + } + return shipment + } + return f + }) - const updated = { - ...shipment, - tracking_numbers: trackingNumbers, - shipped_at: Date.now(), - metadata: { - ...shipment.metadata, - ...metadata, - }, + let fulfillmentStatus = "shipped" + for (const item of order.items) { + if (item.quantity !== item.shipped_quantity) { + fulfillmentStatus = "partially_shipped" + break + } } // Add the shipment to the order @@ -441,7 +459,11 @@ class OrderService extends BaseService { .updateOne( { _id: orderId, "fulfillments._id": fulfillmentId }, { - $set: { "fulfillments.$": updated }, + $set: { + "fulfillments.$": updated, + items: order.items, + fulfillment_status: fulfillmentStatus, + }, } ) .then(result => { @@ -638,7 +660,26 @@ class OrderService extends BaseService { provider_id ) - await paymentProvider.capturePayment(data) + try { + await paymentProvider.capturePayment(data) + } catch (error) { + return this.orderModel_ + .updateOne( + { + _id: orderId, + }, + { + $set: { payment_status: "requires_action" }, + } + ) + .then(result => { + this.eventBus_.emit( + OrderService.Events.PAYMENT_CAPTURE_FAILED, + result + ) + return result + }) + } return this.orderModel_ .updateOne( @@ -705,12 +746,7 @@ class OrderService extends BaseService { const { shipping_methods } = order - // prepare update object - const updateFields = { fulfillment_status: "fulfilled" } - const completed = order.payment_status !== "awaiting" - if (completed) { - updateFields.status = "completed" - } + const updateFields = {} // partition order items to their dedicated shipping method const fulfillments = await this.partitionItems_(shipping_methods, lineItems) @@ -729,6 +765,14 @@ class OrderService extends BaseService { return res }) + method.items = method.items.map(el => { + return { + ...el, + fulfilled_quantity: el.quantity, + fulfilled: true, + } + }) + return { provider_id: method.provider_id, items: method.items, @@ -745,13 +789,22 @@ class OrderService extends BaseService { return { ...i, fulfilled: i.quantity === ful.quantity, - fulfilled_quantity: ful.quantity, + fulfilled_quantity: i.fulfilled_quantity + ful.quantity, } } return i }) + updateFields.fulfillment_status = "fulfilled" + + for (const el of updateFields.items) { + if (el.quantity !== el.fulfilled_quantity) { + updateFields.fulfillment_status = "partially_fulfilled" + break + } + } + return this.orderModel_ .updateOne( { From c87b585b5e5db495cce6c8ceae15098ac5d42131 Mon Sep 17 00:00:00 2001 From: olivermrbl Date: Fri, 13 Nov 2020 12:49:19 +0100 Subject: [PATCH 2/2] chore(release): Publish - @medusajs/medusa@1.0.40 --- packages/medusa/CHANGELOG.md | 11 +++++++++++ packages/medusa/package.json | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/packages/medusa/CHANGELOG.md b/packages/medusa/CHANGELOG.md index 032f3d8664..9e5dfa6aea 100644 --- a/packages/medusa/CHANGELOG.md +++ b/packages/medusa/CHANGELOG.md @@ -3,6 +3,17 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.0.40](https://github.com/medusajs/medusa/compare/@medusajs/medusa@1.0.39...@medusajs/medusa@1.0.40) (2020-11-13) + + +### Features + +* **medusa:** Adds shipped status to line items + Capture fails will give payment_status = requires_action ([6a3c545](https://github.com/medusajs/medusa/commit/6a3c5455371c33e47722c7ab433a48d1d9b5b511)) + + + + + ## [1.0.39](https://github.com/medusajs/medusa/compare/@medusajs/medusa@1.0.38...@medusajs/medusa@1.0.39) (2020-11-05) **Note:** Version bump only for package @medusajs/medusa diff --git a/packages/medusa/package.json b/packages/medusa/package.json index ec16efbb68..0191435a49 100644 --- a/packages/medusa/package.json +++ b/packages/medusa/package.json @@ -1,6 +1,6 @@ { "name": "@medusajs/medusa", - "version": "1.0.39", + "version": "1.0.40", "description": "E-commerce for JAMstack", "main": "dist/app.js", "repository": {