Merge branch 'integration/dummy-project' of https://github.com/medusajs/medusa into integration/dummy-project

This commit is contained in:
olivermrbl
2020-08-12 20:55:35 +02:00
32 changed files with 851 additions and 147 deletions
+34 -4
View File
@@ -7,14 +7,44 @@ import errorHandler from "./middlewares/error-handler"
export default (container, config) => {
const app = Router()
app.post("/create-shipment/:order_id", async (req, res) => {
app.post("/create-shipment/:order_id/:fulfillment_id", async (req, res) => {
const orderService = req.scope.resolve("orderService")
const eventBus = req.scope.resolve("eventBusService")
const order = await orderService.retrieve(req.params.order_id)
await orderService.createShipment(order._id, {
item_ids: order.items.map(({ _id }) => `${_id}`),
tracking_number: "1234",
await orderService.createShipment(order._id, req.params.fulfillment_id, [
"1234",
])
res.sendStatus(200)
})
app.post("/run-hook/:order_id/refund", async (req, res) => {
const orderService = req.scope.resolve("orderService")
const eventBus = req.scope.resolve("eventBusService")
const order = await orderService.retrieve(req.params.order_id)
const returnToSend = order.refunds[order.refunds.length - 1]
eventBus.emit("order.refund_created", {
order,
refund: returnToSend,
})
res.sendStatus(200)
})
app.post("/run-hook/:order_id/return", async (req, res) => {
const orderService = req.scope.resolve("orderService")
const eventBus = req.scope.resolve("eventBusService")
const order = await orderService.retrieve(req.params.order_id)
const returnToSend = order.returns[0]
eventBus.emit("order.items_returned", {
order,
return: {
...returnToSend,
refund_amount: 1000,
},
})
res.sendStatus(200)
@@ -3,7 +3,8 @@ export default async (req, res) => {
try {
const orderService = req.scope.resolve("orderService")
const order = await orderService.capturePayment(id)
let order = await orderService.capturePayment(id)
order = await orderService.decorate(order, [], ["region"])
res.json({ order })
} catch (error) {
throw error
@@ -3,7 +3,8 @@ export default async (req, res) => {
try {
const orderService = req.scope.resolve("orderService")
const order = await orderService.completeOrder(id)
let order = await orderService.completeOrder(id)
order = await orderService.decorate(order, [], ["region"])
res.json({ order })
} catch (error) {
console.log(error)
@@ -3,7 +3,8 @@ export default async (req, res) => {
try {
const orderService = req.scope.resolve("orderService")
const order = await orderService.createFulfillment(id)
let order = await orderService.createFulfillment(id)
order = await orderService.decorate(order, [], ["region"])
res.json({ order })
} catch (error) {
throw error
@@ -37,7 +37,8 @@ export default async (req, res) => {
try {
const orderService = req.scope.resolve("orderService")
const order = await orderService.create(value)
let order = await orderService.create(value)
order = await orderService.decorate(order, [], ["region"])
res.status(200).json({ order })
} catch (err) {
@@ -16,6 +16,10 @@ export default app => {
middlewares.wrap(require("./complete-order").default)
)
route.post(
"/:id/refund",
middlewares.wrap(require("./refund-payment").default)
)
route.post(
"/:id/capture",
middlewares.wrap(require("./capture-payment").default)
@@ -0,0 +1,32 @@
import { MedusaError, Validator } from "medusa-core-utils"
export default async (req, res) => {
const { id } = req.params
const schema = Validator.object().keys({
amount: Validator.number().required(),
reason: Validator.string().required(),
note: Validator.string()
.allow("")
.optional(),
})
const { value, error } = schema.validate(req.body)
if (error) {
throw new MedusaError(MedusaError.Types.INVALID_DATA, error.details)
}
try {
const orderService = req.scope.resolve("orderService")
let order = await orderService.createRefund(
id,
value.amount,
value.reason,
value.note
)
order = await orderService.decorate(order, [], ["region"])
res.status(200).json({ order })
} catch (err) {
throw err
}
}
@@ -20,7 +20,8 @@ export default async (req, res) => {
try {
const orderService = req.scope.resolve("orderService")
const order = await orderService.return(id, value.items)
let order = await orderService.return(id, value.items)
order = await orderService.decorate(order, [], ["region"])
res.status(200).json({ order })
} catch (err) {
@@ -31,7 +31,8 @@ export default async (req, res) => {
try {
const orderService = req.scope.resolve("orderService")
const order = await orderService.update(id, value)
let order = await orderService.update(id, value)
order = await orderService.decorate(order, [], ["region"])
res.status(200).json({ order })
} catch (err) {
@@ -13,7 +13,7 @@ export default async (req, res) => {
res.status(200).json({ cart })
} catch (err) {
console.log(err.response.data)
console.log(err)
throw err
}
}
+2
View File
@@ -1,4 +1,5 @@
export default async ({ container }) => {
const counterService = container.resolve("counterService")
const storeService = container.resolve("storeService")
const profileService = container.resolve("shippingProfileService")
@@ -23,6 +24,7 @@ export default async ({ container }) => {
payment_providers: payIds,
})
await counterService.createDefaults()
await profileService.createDefault()
await profileService.createGiftCardDefault()
}
@@ -51,13 +51,9 @@ export const orders = {
price: 100,
provider_id: "default_provider",
profile_id: IdMap.getId("default"),
},
{
_id: IdMap.getId("freeShipping"),
name: "Free Shipping",
price: 10,
provider_id: "default_provider",
profile_id: IdMap.getId("profile1"),
data: {
extra: "hi",
},
},
],
fulfillment_status: "not_fulfilled",
+13
View File
@@ -0,0 +1,13 @@
import mongoose from "mongoose"
import { BaseModel } from "medusa-interfaces"
class CounterModel extends BaseModel {
static modelName = "Counter"
static schema = {
_id: String,
next: Number,
}
}
export default CounterModel
+7
View File
@@ -7,11 +7,15 @@ import ShippingMethodSchema from "./schemas/shipping-method"
import AddressSchema from "./schemas/address"
import DiscountSchema from "./schemas/discount"
import ShipmentSchema from "./schemas/shipment"
import ReturnSchema from "./schemas/return"
import RefundSchema from "./schemas/refund"
import FulfillmentSchema from "./schemas/fulfillment"
class OrderModel extends BaseModel {
static modelName = "Order"
static schema = {
display_id: { type: String, required: true, unique: true },
// pending, completed, archived, cancelled
status: { type: String, default: "pending" },
// not_fulfilled, partially_fulfilled (some line items have been returned), fulfilled, returned,
@@ -26,6 +30,9 @@ class OrderModel extends BaseModel {
currency_code: { type: String, required: true },
tax_rate: { type: Number, required: true },
shipments: { type: [ShipmentSchema], default: [] },
fulfillments: { type: [FulfillmentSchema], default: [] },
returns: { type: [ReturnSchema], default: [] },
refunds: { type: [RefundSchema], default: [] },
region_id: { type: String, required: true },
discounts: { type: [DiscountSchema], default: [] },
customer_id: { type: String },
@@ -0,0 +1,10 @@
import mongoose from "mongoose"
export default new mongoose.Schema({
created: { type: String, default: Date.now },
provider_id: { type: String, required: true },
items: { type: [mongoose.Schema.Types.Mixed], required: true },
data: { type: [mongoose.Schema.Types.Mixed], default: {} },
tracking_numbers: { type: [String], default: [] },
metadata: { type: mongoose.Schema.Types.Mixed, default: {} },
})
@@ -36,6 +36,8 @@ export default new mongoose.Schema({
content: { type: mongoose.Schema.Types.Mixed, required: true },
quantity: { type: Number, required: true },
returned: { type: Boolean, default: false },
fulfilled: { type: Boolean, default: false },
fulfilled_quantity: { type: Number, default: 0 },
returned_quantity: { type: Number, default: 0 },
metadata: { type: mongoose.Schema.Types.Mixed, default: {} },
})
@@ -0,0 +1,9 @@
import mongoose from "mongoose"
export default new mongoose.Schema({
created: { type: String, default: Date.now },
note: { type: String, default: "" },
reason: { type: String, default: "" },
amount: { type: Number, required: true },
metadata: { type: mongoose.Schema.Types.Mixed, default: {} },
})
@@ -0,0 +1,8 @@
import mongoose from "mongoose"
export default new mongoose.Schema({
created: { type: String, default: Date.now },
refund_amount: { type: Number, required: true },
items: { type: [mongoose.Schema.Types.Mixed], required: true },
metadata: { type: mongoose.Schema.Types.Mixed, default: {} },
})
@@ -17,7 +17,7 @@ export const DefaultProviderMock = {
return Promise.resolve()
}),
createOrder: jest.fn().mockImplementation(data => {
return Promise.resolve()
return Promise.resolve(data)
}),
}
@@ -128,7 +128,15 @@ export const OrderServiceMock = {
createFromCart: jest.fn().mockImplementation(data => {
return Promise.resolve(orders.testOrder)
}),
update: jest.fn().mockImplementation(data => Promise.resolve()),
update: jest.fn().mockImplementation(data => {
if (data === IdMap.getId("test-order")) {
return Promise.resolve(orders.testOrder)
}
if (data === IdMap.getId("processed-order")) {
return Promise.resolve(orders.processedOrder)
}
return Promise.resolve(undefined)
}),
retrieve: jest.fn().mockImplementation(orderId => {
if (orderId === IdMap.getId("test-order")) {
return Promise.resolve(orders.testOrder)
+134 -19
View File
@@ -7,7 +7,10 @@ import {
DefaultProviderMock,
} from "../__mocks__/payment-provider"
import { DiscountServiceMock } from "../__mocks__/discount"
import { FulfillmentProviderServiceMock } from "../__mocks__/fulfillment-provider"
import {
FulfillmentProviderServiceMock,
DefaultProviderMock as FulfillmentProviderMock,
} from "../__mocks__/fulfillment-provider"
import { ShippingProfileServiceMock } from "../__mocks__/shipping-profile"
import { TotalsServiceMock } from "../__mocks__/totals"
import { RegionServiceMock } from "../__mocks__/region"
@@ -402,32 +405,97 @@ describe("OrderService", () => {
})
it("calls order model functions", async () => {
await orderService.createFulfillment(IdMap.getId("test-order"))
await orderService.createFulfillment(IdMap.getId("test-order"), [
{
item_id: IdMap.getId("existingLine"),
quantity: 10,
},
])
expect(FulfillmentProviderMock.createOrder).toHaveBeenCalledTimes(1)
expect(FulfillmentProviderMock.createOrder).toHaveBeenCalledWith(
{
extra: "hi",
},
[
{
_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,
},
]
)
expect(OrderModelMock.updateOne).toHaveBeenCalledTimes(1)
expect(OrderModelMock.updateOne).toHaveBeenCalledWith(
{ _id: IdMap.getId("test-order") },
{
$push: {
fulfillments: {
$each: [
{
data: {
extra: "hi",
},
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,
},
quantity: 10,
},
],
provider_id: "default_provider",
},
],
},
},
$set: {
fulfillment_status: "fulfilled",
shipping_methods: [
items: [
{
_id: IdMap.getId("expensiveShipping"),
items: [],
name: "Expensive Shipping",
price: 100,
profile_id: IdMap.getId("default"),
provider_id: "default_provider",
},
{
_id: IdMap.getId("freeShipping"),
items: [],
name: "Free Shipping",
price: 10,
profile_id: IdMap.getId("profile1"),
provider_id: "default_provider",
_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,
fulfilled_quantity: 10,
fulfilled: true,
},
],
fulfillment_status: "fulfilled",
},
}
)
@@ -435,7 +503,12 @@ describe("OrderService", () => {
it("throws if payment is already processed", async () => {
await expect(
orderService.createFulfillment(IdMap.getId("fulfilled-order"))
orderService.createFulfillment(IdMap.getId("fulfilled-order"), [
{
item_id: IdMap.getId("existingLine"),
quantity: 10,
},
])
).rejects.toThrow("Order is already fulfilled")
})
})
@@ -464,6 +537,20 @@ describe("OrderService", () => {
expect(OrderModelMock.updateOne).toHaveBeenCalledWith(
{ _id: IdMap.getId("processed-order") },
{
$push: {
refunds: {
amount: 1230,
},
returns: {
items: [
{
item_id: IdMap.getId("existingLine"),
quantity: 10,
},
],
refund_amount: 1230,
},
},
$set: {
items: [
{
@@ -514,6 +601,20 @@ describe("OrderService", () => {
expect(OrderModelMock.updateOne).toHaveBeenCalledWith(
{ _id: IdMap.getId("processed-order") },
{
$push: {
refunds: {
amount: 102,
},
returns: {
items: [
{
item_id: IdMap.getId("existingLine"),
quantity: 10,
},
],
refund_amount: 102,
},
},
$set: {
items: [
{
@@ -560,6 +661,20 @@ describe("OrderService", () => {
expect(OrderModelMock.updateOne).toHaveBeenCalledWith(
{ _id: IdMap.getId("order-refund") },
{
$push: {
refunds: {
amount: 0,
},
returns: {
items: [
{
item_id: IdMap.getId("existingLine"),
quantity: 2,
},
],
refund_amount: 0,
},
},
$set: {
items: [
{
@@ -323,7 +323,7 @@ describe("TotalsService", () => {
it("calculates shipping", async () => {
res = await totalsService.getShippingTotal(orders.testOrder)
expect(res).toEqual(110)
expect(res).toEqual(100)
})
})
describe("getTaxTotal", () => {
@@ -341,7 +341,7 @@ describe("TotalsService", () => {
it("calculates tax", async () => {
res = await totalsService.getTaxTotal(orders.testOrder)
expect(res).toEqual(335)
expect(res).toEqual(332.5)
})
})
@@ -358,8 +358,7 @@ describe("TotalsService", () => {
it("calculates total", async () => {
res = await totalsService.getTotal(orders.testOrder)
expect(res).toEqual(1230 + 335 + 110)
expect(res).toEqual(1230 + 332.5 + 100)
})
})
})
+33
View File
@@ -0,0 +1,33 @@
import _ from "lodash"
import { Validator, MedusaError } from "medusa-core-utils"
import { BaseService } from "medusa-interfaces"
/**
* Provides layer to manipulate carts.
* @implements BaseService
*/
class CounterService extends BaseService {
constructor({ counterModel }) {
super()
/** @private @const {CartModel} */
this.counterModel_ = counterModel
}
async createDefaults() {
const orderCounter = await this.counterModel_.findOne({ _id: "orders" })
if (!orderCounter) {
await this.counterModel_.create({ _id: "orders", next: 1000 })
}
}
async getNext(id) {
const counter = await this.counterModel_.updateOne(
{ _id: id },
{ $inc: { next: 1 } }
)
return counter.next
}
}
export default CounterService
+189 -55
View File
@@ -8,6 +8,7 @@ class OrderService extends BaseService {
PAYMENT_CAPTURED: "order.payment_captured",
SHIPMENT_CREATED: "order.shipment_created",
ITEMS_RETURNED: "order.items_returned",
REFUND_CREATED: "order.refund_created",
PLACED: "order.placed",
UPDATED: "order.updated",
CANCELLED: "order.cancelled",
@@ -16,6 +17,7 @@ class OrderService extends BaseService {
constructor({
orderModel,
counterService,
paymentProviderService,
shippingProfileService,
discountService,
@@ -53,6 +55,8 @@ class OrderService extends BaseService {
/** @private @const {EventBus} */
this.eventBus_ = eventBusService
this.counterService_ = counterService
}
/**
@@ -364,6 +368,7 @@ class OrderService extends BaseService {
)
const o = {
display_id: await this.counterService_.getNext("orders"),
payment_method: {
provider_id: paymentSession.provider_id,
data: paymentData,
@@ -396,53 +401,39 @@ class OrderService extends BaseService {
/**
* Adds a shipment to the order to indicate that an order has left the warehouse
*/
async createShipment(orderId, shipment) {
async createShipment(orderId, fulfillmentId, trackingNumbers, metadata = {}) {
const order = await this.retrieve(orderId)
console.log(order)
if (order.fulfillment_status === "shipped") {
throw new MedusaError(
MedusaError.Types.NOT_ALLOWED,
"Order has already been shipped"
)
}
const shipmentSchema = Validator.object({
item_ids: Validator.array()
.items(Validator.string())
.required(),
tracking_number: Validator.string().required(),
let shipment
const updated = order.fulfillments.map(f => {
if (f._id.equals(fulfillmentId)) {
shipment = {
...f,
tracking_numbers: trackingNumbers,
metadata: {
...f.metadata,
...metadata,
},
}
}
return f
})
const { value, error } = shipmentSchema.validate(shipment)
if (error) {
throw new MedusaError(
MedusaError.Types.INVALID_DATA,
`Shipment not valid: ${error}`
)
}
const existing = order.shipments || []
const shipments = [...existing, value]
const allCovered = order.items.every(
i => !!shipments.find(s => s.item_ids.includes(`${i._id}`))
)
const update = {
$push: { shipments: value },
$set: {
fulfillment_status: allCovered ? "shipped" : "partially_shipped",
},
}
// Add the shipment to the order
return this.orderModel_.updateOne({ _id: orderId }, update).then(result => {
this.eventBus_.emit(OrderService.Events.SHIPMENT_CREATED, {
order_id: orderId,
shipment,
return this.orderModel_
.updateOne(
{ _id: orderId },
{
$set: { fulfillments: updated },
}
)
.then(result => {
this.eventBus_.emit(OrderService.Events.SHIPMENT_CREATED, {
order_id: orderId,
shipment,
})
return result
})
return result
})
}
/**
@@ -649,9 +640,33 @@ class OrderService extends BaseService {
* @param {string} orderId - id of order to cancel.
* @return {Promise} result of the update operation.
*/
async createFulfillment(orderId) {
async createFulfillment(orderId, itemsToFulfill, metadata = {}) {
const order = await this.retrieve(orderId)
const lineItems = itemsToFulfill
.map(({ item_id, quantity }) => {
const item = order.items.find(i => i._id.equals(item_id))
if (!item) {
// This will in most cases be called by a webhook so to ensure that
// things go through smoothly in instances where extra items outside
// of Medusa are added we allow unknown items
return null
}
if (quantity > item.quantity - item.fulfilled_quantity) {
throw new MedusaError(
MedusaError.Types.NOT_ALLOWED,
"Cannot fulfill more items than have been purchased"
)
}
return {
...item,
quantity,
}
})
.filter(i => !!i)
if (order.fulfillment_status !== "not_fulfilled") {
throw new MedusaError(
MedusaError.Types.INVALID_ARGUMENT,
@@ -659,7 +674,7 @@ class OrderService extends BaseService {
)
}
const { shipping_methods, items } = order
const { shipping_methods } = order
// prepare update object
const updateFields = { fulfillment_status: "fulfilled" }
@@ -669,26 +684,55 @@ class OrderService extends BaseService {
}
// partition order items to their dedicated shipping method
updateFields.shipping_methods = await this.partitionItems_(
shipping_methods,
items
)
const fulfillments = await this.partitionItems_(shipping_methods, lineItems)
await Promise.all(
updateFields.shipping_methods.map(method => {
let successfullyFulfilled = []
const results = await Promise.all(
fulfillments.map(async method => {
const provider = this.fulfillmentProviderService_.retrieveProvider(
method.provider_id
)
return provider.createOrder(method.data, method.items)
const data = await provider
.createOrder(method.data, method.items)
.then(res => {
successfullyFulfilled = [...successfullyFulfilled, ...method.items]
return res
})
return {
provider_id: method.provider_id,
items: method.items,
data,
metadata,
}
})
)
// Reflect the fulfillments in the items
updateFields.items = order.items.map(i => {
const ful = successfullyFulfilled.find(f => i._id.equals(f._id))
if (ful) {
if (i.quantity === ful.quantity) {
i.fulfilled = true
}
return {
...i,
fulfilled_quantity: ful.quantity,
}
}
return i
})
return this.orderModel_
.updateOne(
{
_id: orderId,
},
{
$push: { fulfillments: { $each: results } },
$set: updateFields,
}
)
@@ -711,6 +755,16 @@ class OrderService extends BaseService {
async return(orderId, lineItems, refundAmount) {
const order = await this.retrieve(orderId)
const total = await this.totalsService_.getTotal(order)
const refunded = await this.totalsService_.getRefundedTotal(order)
if (refundAmount > total - refunded) {
throw new MedusaError(
MedusaError.Types.INVALID_DATA,
"Cannot refund more than the original payment"
)
}
// Find the lines to return
const returnLines = lineItems.map(({ item_id, quantity }) => {
const item = order.items.find(i => i._id.equals(item_id))
@@ -721,6 +775,14 @@ class OrderService extends BaseService {
)
}
const returnable = item.quantity - item.returned_quantity
if (quantity > returnable) {
throw new MedusaError(
MedusaError.Types.NOT_ALLOWED,
"Cannot return more items than have been purchased"
)
}
return {
...item,
quantity,
@@ -757,17 +819,22 @@ class OrderService extends BaseService {
const newItems = order.items.map(i => {
const isReturn = returnLines.find(r => r._id.equals(i._id))
if (isReturn) {
const returnedQuantity = i.returned_quantity + isReturn.quantity
let returned = false
if (i.quantity === isReturn.quantity) {
if (i.quantity === returnedQuantity) {
returned = true
} else {
isFullReturn = false
}
return {
...i,
returned_quantity: isReturn.quantity,
returned_quantity: returnedQuantity,
returned,
}
} else {
isFullReturn = false
if (!i.returned) {
isFullReturn = false
}
return i
}
})
@@ -778,6 +845,15 @@ class OrderService extends BaseService {
_id: orderId,
},
{
$push: {
refunds: {
amount,
},
returns: {
items: lineItems,
refund_amount: amount,
},
},
$set: {
items: newItems,
fulfillment_status: isFullReturn
@@ -789,7 +865,10 @@ class OrderService extends BaseService {
.then(result => {
this.eventBus_.emit(OrderService.Events.ITEMS_RETURNED, {
order: result,
items: returnLines,
return: {
items: lineItems,
refund_amount: amount,
},
})
return result
})
@@ -821,6 +900,56 @@ class OrderService extends BaseService {
)
}
/**
* Refunds a given amount back to the customer.
*/
async createRefund(orderId, refundAmount, reason, note) {
const order = await this.retrieve(orderId)
const total = await this.totalsService_.getTotal(order)
const refunded = await this.totalsService_.getRefundedTotal(order)
if (refundAmount > total - refunded) {
throw new MedusaError(
MedusaError.Types.NOT_ALLOWED,
"Cannot refund more than original order amount"
)
}
const { provider_id, data } = order.payment_method
const paymentProvider = this.paymentProviderService_.retrieveProvider(
provider_id
)
await paymentProvider.refundPayment(data, refundAmount)
return this.orderModel_
.updateOne(
{
_id: orderId,
},
{
$push: {
refunds: {
amount: refundAmount,
reason,
note,
},
},
}
)
.then(result => {
this.eventBus_.emit(OrderService.Events.REFUND_CREATED, {
order: result,
refund: {
amount: refundAmount,
reason,
note,
},
})
return result
})
}
/**
* Decorates an order.
* @param {Order} order - the order to decorate.
@@ -835,6 +964,8 @@ class OrderService extends BaseService {
o.tax_total = await this.totalsService_.getTaxTotal(order)
o.subtotal = await this.totalsService_.getSubtotal(order)
o.total = await this.totalsService_.getTotal(order)
o.refunded_total = await this.totalsService_.getRefundedTotal(order)
o.refundable_amount = o.total - o.refunded_total
o.created = order._id.getTimestamp()
if (expandFields.includes("region")) {
o.region = await this.regionService_.retrieve(order.region_id)
@@ -843,7 +974,10 @@ class OrderService extends BaseService {
o.items = o.items.map(i => {
return {
...i,
refundable: this.totalsService_.getLineItemRefund(o, i),
refundable: this.totalsService_.getLineItemRefund(o, {
...i,
quantity: i.quantity - i.returned_quantity,
}),
}
})
+5
View File
@@ -80,6 +80,11 @@ class TotalsService extends BaseService {
return (subtotal - discountTotal + shippingTotal) * tax_rate
}
getRefundedTotal(object) {
const total = object.refunds.reduce((acc, next) => acc + next.amount, 0)
return total
}
getLineItemRefund(order, lineItem) {
const { tax_rate, discounts } = order
const taxRate = tax_rate || 0