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:
@@ -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
|
||||
|
||||
@@ -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(
|
||||
|
||||
46
packages/medusa/src/api/routes/store/orders/lookup-order.js
Normal file
46
packages/medusa/src/api/routes/store/orders/lookup-order.js
Normal 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
|
||||
}
|
||||
}
|
||||
170
packages/medusa/src/api/routes/store/returns/create-return.js
Normal file
170
packages/medusa/src/api/routes/store/returns/create-return.js
Normal 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
|
||||
}
|
||||
}
|
||||
12
packages/medusa/src/api/routes/store/returns/index.js
Normal file
12
packages/medusa/src/api/routes/store/returns/index.js
Normal 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
|
||||
}
|
||||
@@ -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
Reference in New Issue
Block a user