Tests passing

This commit is contained in:
Sebastian Rindom committed 2020-08-05 15:26:00 +02:00
1 parent 21bc096b2e
commit ee93256e69
17 files changed
+295 -229

No files matched your search

@@ -1,14 +0,0 @@
class OrderSubscriber {
constructor({ klarnaProviderService, eventBusService }) {
this.klarnaProviderService_ = klarnaProviderService
this.eventBus_ = eventBusService
this.eventBus_.subscribe("order.completed", async (order) => {
const klarnaOrderId = order.payment_method.data.id
await this.klarnaProviderService_.acknowledgeOrder(klarnaOrderId)
})
}
}
export default OrderSubscriber
@@ -13,11 +13,6 @@ class CartSubscriber {
this.eventBus_.subscribe("cart.customer_updated", async (cart) => { this.eventBus_.subscribe("cart.customer_updated", async (cart) => {
await this.onCustomerUpdated(cart) await this.onCustomerUpdated(cart)
}) })
this.eventBus_.subscribe("order.completed", async (order) => {
const paymentData = order.payment_method.data
await this.stripeProviderService_.capturePayment(paymentData)
})
} }
async onCustomerUpdated(cart) { async onCustomerUpdated(cart) {
@@ -5,13 +5,57 @@ Object.defineProperty(exports, "__esModule", {
}); });
exports["default"] = void 0; exports["default"] = void 0;
var inventorySync = function inventorySync(container) { function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } }
var brightpearlService = container.resolve("brightpearlService");
var eventBus = container.resolve("eventBusService"); function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; }
var pattern = "43 4,10,14,20 * * *"; // nice for tests "*/10 * * * * *"
var inventorySync = /*#__PURE__*/function () {
var _ref = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee(container) {
var brightpearlService, eventBus, client, pattern;
return regeneratorRuntime.wrap(function _callee$(_context) {
while (1) {
switch (_context.prev = _context.next) {
case 0:
brightpearlService = container.resolve("brightpearlService");
eventBus = container.resolve("eventBusService");
_context.prev = 2;
_context.next = 5;
return brightpearlService.getClient();
case 5:
client = _context.sent;
pattern = "43 4,10,14,20 * * *"; // nice for tests "*/10 * * * * *"
eventBus.createCronJob("inventory-sync", {}, pattern, brightpearlService.syncInventory()); eventBus.createCronJob("inventory-sync", {}, pattern, brightpearlService.syncInventory());
_context.next = 15;
break;
case 10:
_context.prev = 10;
_context.t0 = _context["catch"](2);
if (!(_context.t0.name === "not_allowed")) {
_context.next = 14;
break;
}
return _context.abrupt("return");
case 14:
throw _context.t0;
case 15:
case "end":
return _context.stop();
}
}
}, _callee, null, [[2, 10]]);
}));
return function inventorySync(_x) {
return _ref.apply(this, arguments);
}; };
}();
var _default = inventorySync; var _default = inventorySync;
exports["default"] = _default; exports["default"] = _default;
@@ -11,21 +11,45 @@ function _asyncToGenerator(fn) { return function () { var self = this, args = ar
var webhookLoader = /*#__PURE__*/function () { var webhookLoader = /*#__PURE__*/function () {
var _ref = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee(container) { var _ref = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee(container) {
var brightpearlService; var brightpearlService, client;
return regeneratorRuntime.wrap(function _callee$(_context) { return regeneratorRuntime.wrap(function _callee$(_context) {
while (1) { while (1) {
switch (_context.prev = _context.next) { switch (_context.prev = _context.next) {
case 0: case 0:
brightpearlService = container.resolve("brightpearlService"); brightpearlService = container.resolve("brightpearlService");
_context.next = 3; _context.prev = 1;
_context.next = 4;
return brightpearlService.getClient();
case 4:
client = _context.sent;
_context.next = 7;
return brightpearlService.verifyWebhooks(); return brightpearlService.verifyWebhooks();
case 3: case 7:
_context.next = 14;
break;
case 9:
_context.prev = 9;
_context.t0 = _context["catch"](1);
if (!(_context.t0.name === "not_allowed")) {
_context.next = 13;
break;
}
return _context.abrupt("return");
case 13:
throw _context.t0;
case 14:
case "end": case "end":
return _context.stop(); return _context.stop();
} }
} }
}, _callee); }, _callee, null, [[1, 9]]);
})); }));
return function webhookLoader(_x) { return function webhookLoader(_x) {
@@ -1,8 +1,22 @@
const inventorySync = container => { const inventorySync = async (container) => {
const brightpearlService = container.resolve("brightpearlService") const brightpearlService = container.resolve("brightpearlService")
const eventBus = container.resolve("eventBusService") const eventBus = container.resolve("eventBusService")
try {
const client = await brightpearlService.getClient()
const pattern = "43 4,10,14,20 * * *" // nice for tests "*/10 * * * * *" const pattern = "43 4,10,14,20 * * *" // nice for tests "*/10 * * * * *"
eventBus.createCronJob("inventory-sync", {}, pattern, brightpearlService.syncInventory()) eventBus.createCronJob(
"inventory-sync",
{},
pattern,
brightpearlService.syncInventory()
)
} catch (err) {
if (err.name === "not_allowed") {
return
}
throw err
}
} }
export default inventorySync export default inventorySync
@@ -1,6 +1,14 @@
const webhookLoader = async (container) => { const webhookLoader = async (container) => {
const brightpearlService = container.resolve("brightpearlService") const brightpearlService = container.resolve("brightpearlService")
try {
const client = await brightpearlService.getClient()
await brightpearlService.verifyWebhooks() await brightpearlService.verifyWebhooks()
} catch (err) {
if (err.name === "not_allowed") {
return
}
throw err
}
} }
export default webhookLoader export default webhookLoader
@@ -1,3 +1,4 @@
import { MedusaError } from "medusa-core-utils"
import { BaseService } from "medusa-interfaces" import { BaseService } from "medusa-interfaces"
import Brightpearl from "../utils/brightpearl" import Brightpearl from "../utils/brightpearl"
@@ -14,20 +14,7 @@ describe("POST /admin/orders/:id/return", () => {
payload: { payload: {
items: [ items: [
{ {
_id: IdMap.getId("existingLine"), item_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,
},
quantity: 10, quantity: 10,
}, },
], ],
@@ -51,20 +38,7 @@ describe("POST /admin/orders/:id/return", () => {
IdMap.getId("test-order"), IdMap.getId("test-order"),
[ [
{ {
_id: IdMap.getId("existingLine"), item_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,
},
quantity: 10, quantity: 10,
}, },
] ]
@@ -4,7 +4,13 @@ export default async (req, res) => {
const { id } = req.params const { id } = req.params
const schema = Validator.object().keys({ const schema = Validator.object().keys({
items: Validator.array().required(), items: Validator.array()
.items({
item_id: Validator.string().required(),
quantity: Validator.number().required(),
})
.required(),
refund: Validator.number().optional(),
}) })
const { value, error } = schema.validate(req.body) const { value, error } = schema.validate(req.body)
@@ -109,6 +109,9 @@ export const orders = {
customer_id: IdMap.getId("test-customer"), customer_id: IdMap.getId("test-customer"),
payment_method: { payment_method: {
provider_id: "default_provider", provider_id: "default_provider",
data: {
hi: "hi",
},
}, },
shipping_methods: [ shipping_methods: [
{ {
@@ -133,6 +136,7 @@ export const orders = {
orderToRefund: { orderToRefund: {
_id: IdMap.getId("refund-order"), _id: IdMap.getId("refund-order"),
email: "oliver@test.dk", email: "oliver@test.dk",
tax_rate: 0.25,
billing_address: { billing_address: {
first_name: "Oli", first_name: "Oli",
last_name: "Medusa", last_name: "Medusa",
@@ -36,5 +36,6 @@ export default new mongoose.Schema({
content: { type: mongoose.Schema.Types.Mixed, required: true }, content: { type: mongoose.Schema.Types.Mixed, required: true },
quantity: { type: Number, required: true }, quantity: { type: Number, required: true },
returned: { type: Boolean, default: false }, returned: { type: Boolean, default: false },
returned_quantity: { type: Number, default: 0 },
metadata: { type: mongoose.Schema.Types.Mixed, default: {} }, metadata: { type: mongoose.Schema.Types.Mixed, default: {} },
}) })
@@ -22,7 +22,7 @@ describe("EventBusService", () => {
}) })
it("creates bull queue", () => { it("creates bull queue", () => {
expect(Bull).toHaveBeenCalledTimes(1) expect(Bull).toHaveBeenCalledTimes(2)
expect(Bull).toHaveBeenCalledWith("EventBusService:queue", "testhost") expect(Bull).toHaveBeenCalledWith("EventBusService:queue", "testhost")
}) })
}) })
+67 -28
View File
@@ -2,7 +2,10 @@ import { IdMap } from "medusa-test-utils"
import { OrderModelMock, orders } from "../../models/__mocks__/order" import { OrderModelMock, orders } from "../../models/__mocks__/order"
import { carts } from "../../models/__mocks__/cart" import { carts } from "../../models/__mocks__/cart"
import OrderService from "../order" import OrderService from "../order"
import { PaymentProviderServiceMock } from "../__mocks__/payment-provider" import {
PaymentProviderServiceMock,
DefaultProviderMock,
} from "../__mocks__/payment-provider"
import { DiscountServiceMock } from "../__mocks__/discount" import { DiscountServiceMock } from "../__mocks__/discount"
import { FulfillmentProviderServiceMock } from "../__mocks__/fulfillment-provider" import { FulfillmentProviderServiceMock } from "../__mocks__/fulfillment-provider"
import { ShippingProfileServiceMock } from "../__mocks__/shipping-profile" import { ShippingProfileServiceMock } from "../__mocks__/shipping-profile"
@@ -53,6 +56,7 @@ describe("OrderService", () => {
...carts.completeCart, ...carts.completeCart,
currency_code: "eur", currency_code: "eur",
cart_id: carts.completeCart._id, cart_id: carts.completeCart._id,
tax_rate: 0.25,
} }
delete order._id delete order._id
delete order.payment_sessions delete order.payment_sessions
@@ -112,6 +116,7 @@ describe("OrderService", () => {
], ],
currency_code: "eur", currency_code: "eur",
cart_id: carts.withGiftCard._id, cart_id: carts.withGiftCard._id,
tax_rate: 0.25,
} }
delete order._id delete order._id
@@ -359,6 +364,7 @@ describe("OrderService", () => {
const orderService = new OrderService({ const orderService = new OrderService({
orderModel: OrderModelMock, orderModel: OrderModelMock,
paymentProviderService: PaymentProviderServiceMock, paymentProviderService: PaymentProviderServiceMock,
eventBusService: EventBusServiceMock,
}) })
beforeEach(async () => { beforeEach(async () => {
@@ -439,6 +445,7 @@ describe("OrderService", () => {
orderModel: OrderModelMock, orderModel: OrderModelMock,
paymentProviderService: PaymentProviderServiceMock, paymentProviderService: PaymentProviderServiceMock,
totalsService: TotalsServiceMock, totalsService: TotalsServiceMock,
eventBusService: EventBusServiceMock,
}) })
beforeEach(async () => { beforeEach(async () => {
@@ -448,20 +455,7 @@ describe("OrderService", () => {
it("calls order model functions", async () => { it("calls order model functions", async () => {
await orderService.return(IdMap.getId("processed-order"), [ await orderService.return(IdMap.getId("processed-order"), [
{ {
_id: IdMap.getId("existingLine"), item_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,
},
quantity: 10, quantity: 10,
}, },
]) ])
@@ -489,31 +483,75 @@ describe("OrderService", () => {
returned_quantity: 10, returned_quantity: 10,
thumbnail: "test-img-yeah.com/thumb", thumbnail: "test-img-yeah.com/thumb",
title: "merge line", title: "merge line",
returned: true,
}, },
], ],
fulfillment_status: "returned", fulfillment_status: "returned",
}, },
} }
) )
expect(DefaultProviderMock.refundPayment).toHaveBeenCalledTimes(1)
expect(DefaultProviderMock.refundPayment).toHaveBeenCalledWith(
{ hi: "hi" },
1230
)
}) })
it("calls order model functions and sets partially_fulfilled", async () => { it("return with custom refund", async () => {
await orderService.return(IdMap.getId("order-refund"), [ await orderService.return(
IdMap.getId("processed-order"),
[
{
item_id: IdMap.getId("existingLine"),
quantity: 10,
},
],
102
)
expect(OrderModelMock.updateOne).toHaveBeenCalledTimes(1)
expect(OrderModelMock.updateOne).toHaveBeenCalledWith(
{ _id: IdMap.getId("processed-order") },
{
$set: {
items: [
{ {
_id: IdMap.getId("existingLine"), _id: IdMap.getId("existingLine"),
title: "merge line",
description: "This is a new line",
thumbnail: "test-img-yeah.com/thumb",
content: { content: {
unit_price: 100,
variant: {
_id: IdMap.getId("eur-8-us-10"),
},
product: { product: {
_id: IdMap.getId("product"), _id: IdMap.getId("validId"),
}, },
quantity: 1, quantity: 1,
unit_price: 123,
variant: {
_id: IdMap.getId("can-cover"),
}, },
},
description: "This is a new line",
quantity: 10,
returned_quantity: 10,
thumbnail: "test-img-yeah.com/thumb",
title: "merge line",
returned: true,
},
],
fulfillment_status: "returned",
},
}
)
expect(DefaultProviderMock.refundPayment).toHaveBeenCalledTimes(1)
expect(DefaultProviderMock.refundPayment).toHaveBeenCalledWith(
{ hi: "hi" },
102
)
})
it("calls order model functions and sets partially_returned", async () => {
await orderService.return(IdMap.getId("order-refund"), [
{
item_id: IdMap.getId("existingLine"),
quantity: 2, quantity: 2,
}, },
]) ])
@@ -538,6 +576,7 @@ describe("OrderService", () => {
}, },
description: "This is a new line", description: "This is a new line",
quantity: 10, quantity: 10,
returned: false,
returned_quantity: 2, returned_quantity: 2,
thumbnail: "test-img-yeah.com/thumb", thumbnail: "test-img-yeah.com/thumb",
title: "merge line", title: "merge line",
@@ -560,7 +599,7 @@ describe("OrderService", () => {
quantity: 10, quantity: 10,
}, },
], ],
fulfillment_status: "partially_fulfilled", fulfillment_status: "partially_returned",
}, },
} }
) )
@@ -568,7 +607,7 @@ describe("OrderService", () => {
it("throws if payment is already processed", async () => { it("throws if payment is already processed", async () => {
try { try {
await orderService.return(IdMap.getId("fulfilled-order")) await orderService.return(IdMap.getId("fulfilled-order"), [])
} catch (error) { } catch (error) {
expect(error.message).toEqual( expect(error.message).toEqual(
"Can't return an order with payment unprocessed" "Can't return an order with payment unprocessed"
@@ -578,7 +617,7 @@ describe("OrderService", () => {
it("throws if return is attempted on unfulfilled order", async () => { it("throws if return is attempted on unfulfilled order", async () => {
try { try {
await orderService.return(IdMap.getId("not-fulfilled-order")) await orderService.return(IdMap.getId("not-fulfilled-order"), [])
} catch (error) { } catch (error) {
expect(error.message).toEqual( expect(error.message).toEqual(
"Can't return an unfulfilled or already returned order" "Can't return an unfulfilled or already returned order"
@@ -193,7 +193,7 @@ describe("TotalsService", () => {
description: "This is a new line", description: "This is a new line",
thumbnail: "test-img-yeah.com/thumb", thumbnail: "test-img-yeah.com/thumb",
content: { content: {
unit_price: 123, unit_price: 100,
variant: { variant: {
_id: IdMap.getId("can-cover"), _id: IdMap.getId("can-cover"),
}, },
@@ -206,7 +206,7 @@ describe("TotalsService", () => {
}, },
]) ])
expect(res).toEqual(1107) expect(res).toEqual(1125)
}) })
it("calculates refund with total fixed discount", async () => { it("calculates refund with total fixed discount", async () => {
@@ -218,7 +218,7 @@ describe("TotalsService", () => {
description: "This is a new line", description: "This is a new line",
thumbnail: "test-img-yeah.com/thumb", thumbnail: "test-img-yeah.com/thumb",
content: { content: {
unit_price: 123, unit_price: 100,
variant: { variant: {
_id: IdMap.getId("can-cover"), _id: IdMap.getId("can-cover"),
}, },
@@ -231,7 +231,7 @@ describe("TotalsService", () => {
}, },
]) ])
expect(res).toEqual(359) expect(res).toEqual(373.125)
}) })
it("calculates refund with item fixed discount", async () => { it("calculates refund with item fixed discount", async () => {
@@ -243,7 +243,7 @@ describe("TotalsService", () => {
description: "This is a new line", description: "This is a new line",
thumbnail: "test-img-yeah.com/thumb", thumbnail: "test-img-yeah.com/thumb",
content: { content: {
unit_price: 123, unit_price: 100,
variant: { variant: {
_id: IdMap.getId("eur-8-us-10"), _id: IdMap.getId("eur-8-us-10"),
}, },
@@ -256,7 +256,7 @@ describe("TotalsService", () => {
}, },
]) ])
expect(res).toEqual(363) expect(res).toEqual(367.5)
}) })
it("calculates refund with item percentage discount", async () => { it("calculates refund with item percentage discount", async () => {
@@ -268,7 +268,7 @@ describe("TotalsService", () => {
description: "This is a new line", description: "This is a new line",
thumbnail: "test-img-yeah.com/thumb", thumbnail: "test-img-yeah.com/thumb",
content: { content: {
unit_price: 123, unit_price: 100,
variant: { variant: {
_id: IdMap.getId("eur-8-us-10"), _id: IdMap.getId("eur-8-us-10"),
}, },
@@ -281,7 +281,7 @@ describe("TotalsService", () => {
}, },
]) ])
expect(res).toEqual(332.1) expect(res).toEqual(337.5)
}) })
it("throws if line items to return is not in order", async () => { it("throws if line items to return is not in order", async () => {
+73 -29
View File
@@ -7,6 +7,7 @@ class OrderService extends BaseService {
GIFT_CARD_CREATED: "order.gift_card_created", GIFT_CARD_CREATED: "order.gift_card_created",
PAYMENT_CAPTURED: "order.payment_captured", PAYMENT_CAPTURED: "order.payment_captured",
SHIPMENT_CREATED: "order.shipment_created", SHIPMENT_CREATED: "order.shipment_created",
ITEMS_RETURNED: "order.items_returned",
PLACED: "order.placed", PLACED: "order.placed",
UPDATED: "order.updated", UPDATED: "order.updated",
CANCELLED: "order.cancelled", CANCELLED: "order.cancelled",
@@ -218,28 +219,28 @@ class OrderService extends BaseService {
*/ */
async completeOrder(orderId) { async completeOrder(orderId) {
const order = await this.retrieve(orderId) const order = await this.retrieve(orderId)
this.orderModel_
// Capture the payment
await this.capturePayment(orderId)
// Run all other registered events
const completeOrderJob = await this.eventBus_.emit(
OrderService.Events.COMPLETED,
result
)
await completeOrderJob.finished().catch(error => {
throw error
})
return this.orderModel_
.updateOne( .updateOne(
{ _id: order._id }, { _id: order._id },
{ {
$set: { status: "completed" }, $set: { status: "completed" },
} }
) )
.then(async result => { .then(async result => {})
const completeOrderJob = await this.eventBus_.emit(
OrderService.Events.COMPLETED,
result
)
return completeOrderJob
.finished()
.then(async () => {
return this.retrieve(order._id)
})
.catch(error => {
throw error
})
})
} }
/** /**
@@ -672,9 +673,25 @@ class OrderService extends BaseService {
* @param {string[]} lineItems - the line items to return * @param {string[]} lineItems - the line items to return
* @return {Promise} the result of the update operation * @return {Promise} the result of the update operation
*/ */
async return(orderId, lineItems) { async return(orderId, lineItems, refundAmount) {
const order = await this.retrieve(orderId) const order = await this.retrieve(orderId)
// Find the lines to return
const returnLines = lineItems.map(({ item_id, quantity }) => {
const item = order.items.find(i => i._id.equals(item_id))
if (!item) {
throw new MedusaError(
MedusaError.Types.INVALID_DATA,
"Return contains invalid line item"
)
}
return {
...item,
quantity,
}
})
if ( if (
order.fulfillment_status === "not_fulfilled" || order.fulfillment_status === "not_fulfilled" ||
order.fulfillment_status === "returned" order.fulfillment_status === "returned"
@@ -697,31 +714,50 @@ class OrderService extends BaseService {
provider_id provider_id
) )
const amount = this.totalsService_.getRefundTotal(order, lineItems) const amount =
refundAmount || this.totalsService_.getRefundTotal(order, returnLines)
await paymentProvider.refundPayment(data, amount) await paymentProvider.refundPayment(data, amount)
lineItems.map(item => { let isFullReturn = true
const returnedItem = order.items.find(({ _id }) => _id === item._id) const newItems = order.items.map(i => {
if (returnedItem) { const isReturn = returnLines.find(r => r._id.equals(i._id))
returnedItem.returned_quantity = item.quantity if (isReturn) {
let returned = false
if (i.quantity === isReturn.quantity) {
returned = true
}
return {
...i,
returned_quantity: isReturn.quantity,
returned,
}
} else {
isFullReturn = false
return i
} }
}) })
const fullReturn = order.items.every( return this.orderModel_
item => item.quantity === item.returned_quantity .updateOne(
)
return this.orderModel_.updateOne(
{ {
_id: orderId, _id: orderId,
}, },
{ {
$set: { $set: {
items: order.items, items: newItems,
fulfillment_status: fullReturn ? "returned" : "partially_fulfilled", fulfillment_status: isFullReturn
? "returned"
: "partially_returned",
}, },
} }
) )
.then(result => {
this.eventBus_.emit(OrderService.Events.ITEMS_RETURNED, {
order: result,
items: returnLines,
})
return result
})
} }
/** /**
@@ -768,6 +804,14 @@ class OrderService extends BaseService {
if (expandFields.includes("region")) { if (expandFields.includes("region")) {
o.region = await this.regionService_.retrieve(order.region_id) o.region = await this.regionService_.retrieve(order.region_id)
} }
o.items = o.items.map(i => {
return {
...i,
refundable: this.totalsService_.getLineItemRefund(o, i),
}
})
return o return o
} }
+31 -97
View File
@@ -80,6 +80,32 @@ class TotalsService extends BaseService {
return (subtotal - discountTotal + shippingTotal) * tax_rate return (subtotal - discountTotal + shippingTotal) * tax_rate
} }
getLineItemRefund(order, lineItem) {
const { tax_rate, discounts } = order
const taxRate = tax_rate || 0
const discount = discounts.find(
({ discount_rule }) => discount_rule.type !== "free_shipping"
)
if (!discount) {
return lineItem.content.unit_price * lineItem.quantity * (1 + taxRate)
}
const lineDiscounts = this.getLineDiscounts(order, discount)
const discountedLine = lineDiscounts.find(line =>
line.item._id.equals(lineItem._id)
)
const discountAmount =
(discountedLine.amount / discountedLine.item.quantity) * lineItem.quantity
return (
(lineItem.content.unit_price * lineItem.quantity - discountAmount) *
(1 + taxRate)
)
}
/** /**
* Calculates refund total of line items. * Calculates refund total of line items.
* If any of the items to return have been discounted, we need to * If any of the items to return have been discounted, we need to
@@ -88,101 +114,9 @@ class TotalsService extends BaseService {
* @param {[LineItem]} lineItems - * @param {[LineItem]} lineItems -
* @return {int} the calculated subtotal * @return {int} the calculated subtotal
*/ */
async getRefundTotal(order, lineItems) { getRefundTotal(order, lineItems) {
const discount = order.discounts.find( const refunds = lineItems.map(i => this.getLineItemRefund(order, i))
({ discount_rule }) => discount_rule.type !== "free_shipping" return refunds.reduce((acc, next) => acc + next, 0)
)
if (_.differenceBy(lineItems, order.items, "_id").length !== 0) {
throw new MedusaError(
MedusaError.Types.INVALID_ARGUMENT,
"Line items does not exist on order"
)
}
const subtotal = this.getSubtotal({ items: lineItems })
const region = await this.regionService_.retrieve(order.region_id)
// if nothing is discounted, return the subtotal of line items
if (!discount) {
return subtotal * (1 + region.tax_rate)
}
const { value, type, allocation } = discount.discount_rule
if (type === "percentage" && allocation === "total") {
const discountTotal = (subtotal / 100) * value
return subtotal - discountTotal
}
if (type === "fixed" && allocation === "total") {
return subtotal - value
}
if (type === "percentage" && allocation === "item") {
// Find discounted items
const itemPercentageDiscounts = await this.getAllocationItemDiscounts(
discount,
{ items: lineItems },
"percentage"
)
// Find discount total by taking each discounted item, reducing it by
// its discount value. Then summing all those items together.
const discountRefundTotal = _.sumBy(
itemPercentageDiscounts,
d => d.lineItem.content.unit_price * d.lineItem.quantity - d.amount
)
// Find the items that weren't discounted
const notDiscountedItems = _.differenceBy(
lineItems,
Array.from(itemPercentageDiscounts, el => el.lineItem),
"_id"
)
// If all items were discounted, we return the total of the discounted
// items
if (!notDiscountedItems) {
return discountRefundTotal
}
// Otherwise, we find the total those not discounted
const notDiscRefundTotal = this.getSubtotal({ items: notDiscountedItems })
// Finally, return the sum of discounted and not discounted items
return notDiscRefundTotal + discountRefundTotal
}
// See immediate `if`-statement above for a elaboration on the following
// calculations. This time with fixed discount type.
if (type === "fixed" && allocation === "item") {
const itemPercentageDiscounts = await this.getAllocationItemDiscounts(
discount,
{ items: lineItems },
"fixed"
)
const discountRefundTotal = _.sumBy(
itemPercentageDiscounts,
d => d.lineItem.content.unit_price * d.lineItem.quantity - d.amount
)
const notDiscountedItems = _.differenceBy(
lineItems,
Array.from(itemPercentageDiscounts, el => el.lineItem),
"_id"
)
if (!notDiscountedItems) {
return notDiscRefundTotal
}
const notDiscRefundTotal = this.getSubtotal({ items: notDiscountedItems })
return notDiscRefundTotal + discountRefundTotal
}
} }
/** /**
@@ -225,7 +159,7 @@ class TotalsService extends BaseService {
* @return {[{ string, string, int }]} array of triples of lineitem, variant * @return {[{ string, string, int }]} array of triples of lineitem, variant
* and applied discount * and applied discount
*/ */
async getAllocationItemDiscounts(discount, cart) { getAllocationItemDiscounts(discount, cart) {
const discounts = [] const discounts = []
for (const item of cart.items) { for (const item of cart.items) {
if (discount.discount_rule.valid_for.length > 0) { if (discount.discount_rule.valid_for.length > 0) {
@@ -252,7 +186,7 @@ class TotalsService extends BaseService {
return discounts return discounts
} }
async getLineDiscounts(cart, discount) { getLineDiscounts(cart, discount) {
const subtotal = this.getSubtotal(cart) const subtotal = this.getSubtotal(cart)
const { type, allocation, value } = discount.discount_rule const { type, allocation, value } = discount.discount_rule
if (allocation === "total") { if (allocation === "total") {
-8
View File
@@ -19,14 +19,6 @@ class OrderSubscriber {
this.eventBus_ = eventBusService this.eventBus_ = eventBusService
this.eventBus_.subscribe("order.completed", async order => {
const paymentProvider = this.paymentProviderService_.retrieveProvider(
order.payment_method.provider_id
)
await paymentProvider.capturePayment(order._id)
})
this.eventBus_.subscribe("order.placed", async order => { this.eventBus_.subscribe("order.placed", async order => {
await this.customerService_.addOrder(order.customer_id, order._id) await this.customerService_.addOrder(order.customer_id, order._id)