feat(medusa): storefront return (#194)

* feat: adds endpoint to lookup an order by its display_id and email

* feat: adds storefront returns

* fix: pr comments
This commit is contained in:
Sebastian Rindom
2021-03-10 17:42:39 +01:00
committed by GitHub
parent d9c17b5122
commit 252db5ef7e
12 changed files with 613 additions and 892 deletions

View File

@@ -10,6 +10,7 @@ import orderRoutes from "./orders"
import customerRoutes from "./customers"
import shippingOptionRoutes from "./shipping-options"
import regionRoutes from "./regions"
import returnRoutes from "./returns"
import swapRoutes from "./swaps"
import variantRoutes from "./variants"
import giftCardRoutes from "./gift-cards"
@@ -38,6 +39,7 @@ export default (app, container, config) => {
regionRoutes(route)
swapRoutes(route)
variantRoutes(route)
returnRoutes(route)
giftCardRoutes(route)
return app

View File

@@ -6,6 +6,7 @@ const route = Router()
export default app => {
app.use("/orders", route)
route.get("/", middlewares.wrap(require("./lookup-order").default))
route.get("/:id", middlewares.wrap(require("./get-order").default))
route.get(

View File

@@ -0,0 +1,46 @@
import { Validator, MedusaError } from "medusa-core-utils"
import { defaultRelations, defaultFields } from "./index"
export default async (req, res) => {
const schema = Validator.object().keys({
display_id: Validator.number().required(),
email: Validator.string().required(),
shipping_address: Validator.object()
.keys({
postal_code: Validator.string(),
})
.optional(),
})
const { value, error } = schema.validate(req.query)
if (error) {
throw new MedusaError(MedusaError.Types.INVALID_DATA, error.details)
}
try {
const orderService = req.scope.resolve("orderService")
const orders = await orderService.list(
{
display_id: value.display_id,
email: value.email,
},
{
select: defaultFields,
relations: defaultRelations,
}
)
if (orders.length !== 1) {
res.sendStatus(404)
return
}
const order = orders[0]
res.json({ order })
} catch (error) {
console.log(error)
throw error
}
}

View File

@@ -0,0 +1,170 @@
import { MedusaError, Validator } from "medusa-core-utils"
import { defaultRelations, defaultFields } from "./"
export default async (req, res) => {
const schema = Validator.object().keys({
order_id: Validator.string().required(),
items: Validator.array()
.items({
item_id: Validator.string().required(),
quantity: Validator.number().required(),
})
.required(),
return_shipping: Validator.object()
.keys({
option_id: Validator.string().optional(),
})
.optional(),
})
const { value, error } = schema.validate(req.body)
if (error) {
throw new MedusaError(MedusaError.Types.INVALID_DATA, error.details)
}
const idempotencyKeyService = req.scope.resolve("idempotencyKeyService")
const headerKey = req.get("Idempotency-Key") || ""
let idempotencyKey
try {
idempotencyKey = await idempotencyKeyService.initializeRequest(
headerKey,
req.method,
req.params,
req.path
)
} catch (error) {
res.status(409).send("Failed to create idempotency key")
return
}
res.setHeader("Access-Control-Expose-Headers", "Idempotency-Key")
res.setHeader("Idempotency-Key", idempotencyKey.idempotency_key)
try {
const orderService = req.scope.resolve("orderService")
const returnService = req.scope.resolve("returnService")
const eventBus = req.scope.resolve("eventBusService")
let inProgress = true
let err = false
while (inProgress) {
switch (idempotencyKey.recovery_point) {
case "started": {
const { key, error } = await idempotencyKeyService.workStage(
idempotencyKey.idempotency_key,
async manager => {
const order = await orderService
.withTransaction(manager)
.retrieve(value.order_id, {
select: ["refunded_total", "total"],
relations: ["items"],
})
const returnObj = {
order_id: value.order_id,
idempotency_key: idempotencyKey.idempotency_key,
items: value.items,
}
if (value.return_shipping) {
returnObj.shipping_method = value.return_shipping
}
const createdReturn = await returnService
.withTransaction(manager)
.create(returnObj, order)
if (value.return_shipping) {
await returnService
.withTransaction(manager)
.fulfill(createdReturn.id)
}
await eventBus
.withTransaction(manager)
.emit("order.return_requested", {
id: value.order_id,
return_id: createdReturn.id,
})
return {
recovery_point: "return_requested",
}
}
)
if (error) {
inProgress = false
err = error
} else {
idempotencyKey = key
}
break
}
case "return_requested": {
const { key, error } = await idempotencyKeyService.workStage(
idempotencyKey.idempotency_key,
async manager => {
let order = await orderService
.withTransaction(manager)
.retrieve(value.order_id, { relations: ["returns"] })
let ret = await returnService.withTransaction(manager).list({
idempotency_key: idempotencyKey.idempotency_key,
})
if (!ret.length) {
throw new MedusaError(
MedusaError.Types.INVALID_DATA,
`Return not found`
)
}
ret = ret[0]
return {
response_code: 200,
response_body: { return: ret },
}
}
)
if (error) {
inProgress = false
err = error
} else {
idempotencyKey = key
}
break
}
case "finished": {
inProgress = false
break
}
default:
idempotencyKey = await idempotencyKeyService.update(
idempotencyKey.idempotency_key,
{
recovery_point: "finished",
response_code: 500,
response_body: { message: "Unknown recovery point" },
}
)
break
}
}
if (err) {
throw err
}
res.status(idempotencyKey.response_code).json(idempotencyKey.response_body)
} catch (err) {
console.log(err)
throw err
}
}

View File

@@ -0,0 +1,12 @@
import { Router } from "express"
import middlewares from "../../../middlewares"
const route = Router()
export default app => {
app.use("/returns", route)
route.post("/", middlewares.wrap(require("./create-return").default))
return app
}

View File

@@ -240,6 +240,15 @@ class ReturnService extends BaseService {
this.validateReturnLineItem_
)
if (data.shipping_method) {
if (typeof data.shipping_method.price === "undefined") {
const opt = await this.shippingOptionService_.retrieve(
data.shipping_method.option_id
)
data.shipping_method.price = opt.amount
}
}
let toRefund = data.refund_amount
if (typeof toRefund !== "undefined") {
const refundable = orderLike.total - orderLike.refunded_total

File diff suppressed because it is too large Load Diff