Replaces MongoDB support with PostgreSQL (#151)
- All schemas have been rewritten to a relational model - All services have been rewritten to accommodate the new data model - Adds idempotency keys to core endpoints allowing you to retry requests with no additional side effects - Adds staged jobs to avoid putting jobs in the queue when transactions abort - Adds atomic transactions to all methods with access to the data layer Co-authored-by: Oliver Windall Juhl <oliver@mrbltech.com>
This commit is contained in:
1 parent
5f819486fc
commit
f1baca3cbd
499 files changed
+25909
-16128
No files matched your search
@@ -7,6 +7,6 @@ if (process.env.NODE_ENV !== `test`) {
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
presets: [["babel-preset-medusa-package"]],
|
||||
presets: [["babel-preset-medusa-package"], ["@babel/preset-typescript"]],
|
||||
ignore,
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"cli": {
|
||||
"migrationsDir": "src/migrations"
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@medusajs/medusa",
|
||||
"version": "1.0.56",
|
||||
"version": "1.0.33-alpha.205+8c87f25",
|
||||
"description": "E-commerce for JAMstack",
|
||||
"main": "dist/app.js",
|
||||
"repository": {
|
||||
@@ -18,12 +18,14 @@
|
||||
"@babel/core": "^7.7.5",
|
||||
"@babel/node": "^7.7.4",
|
||||
"@babel/plugin-proposal-class-properties": "^7.7.4",
|
||||
"@babel/plugin-proposal-decorators": "^7.12.1",
|
||||
"@babel/plugin-transform-instanceof": "^7.8.3",
|
||||
"@babel/plugin-transform-runtime": "^7.7.6",
|
||||
"@babel/preset-env": "^7.7.5",
|
||||
"@babel/preset-typescript": "^7.12.7",
|
||||
"@babel/register": "^7.7.4",
|
||||
"@babel/runtime": "^7.7.6",
|
||||
"babel-preset-medusa-package": "^1.0.1",
|
||||
"babel-preset-medusa-package": "^1.0.2-alpha.787+0646bd3",
|
||||
"cross-env": "^5.2.1",
|
||||
"eslint": "^6.8.0",
|
||||
"jest": "^25.5.2",
|
||||
@@ -33,9 +35,9 @@
|
||||
},
|
||||
"scripts": {
|
||||
"start": "nodemon --watch plugins/ --watch src/ --exec babel-node src/app.js",
|
||||
"watch": "babel -w src --out-dir dist/ --ignore **/__tests__",
|
||||
"watch": "babel -w src --out-dir dist/ --ignore **/__tests__ --extensions \".ts,.js\"",
|
||||
"prepare": "cross-env NODE_ENV=production npm run build",
|
||||
"build": "babel src -d dist --ignore **/__tests__",
|
||||
"build": "babel src -d dist --ignore **/__tests__ --extensions \".ts,.js\"",
|
||||
"serve": "node dist/app.js",
|
||||
"test": "jest"
|
||||
},
|
||||
@@ -60,21 +62,28 @@
|
||||
"fs-exists-cached": "^1.0.0",
|
||||
"glob": "^7.1.6",
|
||||
"ioredis": "^4.17.3",
|
||||
"joi": "^17.3.0",
|
||||
"joi-objectid": "^3.0.1",
|
||||
"jsonwebtoken": "^8.5.1",
|
||||
"medusa-core-utils": "^1.0.11",
|
||||
"medusa-test-utils": "^1.0.13",
|
||||
"medusa-core-utils": "^1.0.12-alpha.787+0646bd3",
|
||||
"medusa-test-utils": "^1.0.12-alpha.176+0646bd3",
|
||||
"morgan": "^1.9.1",
|
||||
"multer": "^1.4.2",
|
||||
"passport": "^0.4.0",
|
||||
"passport-http-bearer": "^1.0.1",
|
||||
"passport-jwt": "^4.0.0",
|
||||
"passport-local": "^1.0.0",
|
||||
"pg": "^8.5.1",
|
||||
"randomatic": "^3.1.1",
|
||||
"redis": "^3.0.2",
|
||||
"reflect-metadata": "^0.1.13",
|
||||
"request-ip": "^2.1.3",
|
||||
"resolve-cwd": "^3.0.0",
|
||||
"scrypt-kdf": "^2.0.1",
|
||||
"typeorm": "^0.2.29",
|
||||
"ulid": "^2.3.0",
|
||||
"uuid": "^8.3.1",
|
||||
"winston": "^3.2.1"
|
||||
},
|
||||
"gitHead": "b54e28769a423c9285a1119535cfa1590d08a559"
|
||||
"gitHead": "8c87f25f766154368b0f15424028550966f7d3d6"
|
||||
}
|
||||
@@ -5,6 +5,8 @@ export default () => {
|
||||
const logger = req.scope.resolve("logger")
|
||||
logger.error(err.message)
|
||||
|
||||
console.log(err)
|
||||
|
||||
let statusCode = 500
|
||||
switch (err.name) {
|
||||
case MedusaError.Types.NOT_ALLOWED:
|
||||
|
||||
@@ -22,7 +22,7 @@ export default async (req, res) => {
|
||||
}
|
||||
|
||||
// Add JWT to cookie
|
||||
req.session.jwt = jwt.sign({ userId: result.user._id }, config.jwtSecret, {
|
||||
req.session.jwt = jwt.sign({ userId: result.user.id }, config.jwtSecret, {
|
||||
expiresIn: "24h",
|
||||
})
|
||||
|
||||
|
||||
@@ -18,8 +18,7 @@ export default async (req, res) => {
|
||||
try {
|
||||
const customerService = req.scope.resolve("customerService")
|
||||
const customer = await customerService.create(value)
|
||||
const data = await customerService.decorate(customer, ["_id", "email"])
|
||||
res.status(201).json({ customer: data })
|
||||
res.status(201).json({ customer })
|
||||
} catch (err) {
|
||||
throw err
|
||||
}
|
||||
|
||||
@@ -1,28 +1,10 @@
|
||||
export default async (req, res) => {
|
||||
const { id } = req.params
|
||||
try {
|
||||
const orderService = req.scope.resolve("orderService")
|
||||
const customerService = req.scope.resolve("customerService")
|
||||
let customer = await customerService.retrieve(id)
|
||||
customer = await customerService.decorate(
|
||||
customer,
|
||||
[
|
||||
"email",
|
||||
"payment_methods",
|
||||
"has_account",
|
||||
"shipping_addresses",
|
||||
"phone",
|
||||
],
|
||||
[]
|
||||
)
|
||||
|
||||
customer.orders = await orderService.list({ customer_id: customer._id })
|
||||
|
||||
customer.orders = await Promise.all(
|
||||
customer.orders.map(order => {
|
||||
return orderService.decorate(order, ["total", "payment_status"], [])
|
||||
})
|
||||
)
|
||||
const customer = await customerService.retrieve(id, {
|
||||
relations: ["orders"],
|
||||
})
|
||||
|
||||
res.json({ customer })
|
||||
} catch (err) {
|
||||
|
||||
@@ -1,22 +1,19 @@
|
||||
export default async (req, res) => {
|
||||
try {
|
||||
const customerService = req.scope.resolve("customerService")
|
||||
const queryBuilderService = req.scope.resolve("queryBuilderService")
|
||||
|
||||
const query = queryBuilderService.buildQuery(req.query, [
|
||||
"email",
|
||||
"first_name",
|
||||
"last_name",
|
||||
])
|
||||
|
||||
const limit = parseInt(req.query.limit) || 0
|
||||
const limit = parseInt(req.query.limit) || 10
|
||||
const offset = parseInt(req.query.offset) || 0
|
||||
|
||||
const customers = await customerService.list(query, offset, limit)
|
||||
const listConfig = {
|
||||
relations: [],
|
||||
skip: offset,
|
||||
take: limit,
|
||||
}
|
||||
|
||||
const numCustomers = await customerService.count()
|
||||
const customers = await customerService.list({}, listConfig)
|
||||
|
||||
res.json({ customers, total_count: numCustomers })
|
||||
res.json({ customers, count: customers.length, offset, limit })
|
||||
} catch (error) {
|
||||
throw error
|
||||
}
|
||||
|
||||
@@ -20,15 +20,10 @@ export default async (req, res) => {
|
||||
const customerService = req.scope.resolve("customerService")
|
||||
await customerService.update(id, value)
|
||||
|
||||
const customer = await customerService.retrieve(id)
|
||||
const data = await customerService.decorate(customer)
|
||||
data.orders = await Promise.all(
|
||||
customer.orders.map(async oId => {
|
||||
const order = await orderService.retrieve(oId)
|
||||
return orderService.decorate(order, [], [])
|
||||
})
|
||||
)
|
||||
res.status(200).json({ customer: data })
|
||||
const customer = await customerService.retrieve(id, {
|
||||
relations: ["orders"],
|
||||
})
|
||||
res.status(200).json({ customer })
|
||||
} catch (err) {
|
||||
throw err
|
||||
}
|
||||
|
||||
@@ -29,7 +29,23 @@ describe("POST /admin/discounts/:discount_id/regions/:region_id", () => {
|
||||
it("calls service retrieve", () => {
|
||||
expect(DiscountServiceMock.retrieve).toHaveBeenCalledTimes(1)
|
||||
expect(DiscountServiceMock.retrieve).toHaveBeenCalledWith(
|
||||
IdMap.getId("total10")
|
||||
IdMap.getId("total10"),
|
||||
{
|
||||
select: [
|
||||
"id",
|
||||
"code",
|
||||
"is_dynamic",
|
||||
"rule_id",
|
||||
"parent_discount_id",
|
||||
"starts_at",
|
||||
"ends_at",
|
||||
"created_at",
|
||||
"updated_at",
|
||||
"deleted_at",
|
||||
"metadata",
|
||||
],
|
||||
relations: ["rule", "parent_discount", "regions", "rule.valid_for"],
|
||||
}
|
||||
)
|
||||
|
||||
expect(DiscountServiceMock.addRegion).toHaveBeenCalledTimes(1)
|
||||
|
||||
+20
-4
@@ -9,7 +9,7 @@ describe("POST /admin/discounts/:discount_id/variants/:variant_id", () => {
|
||||
beforeAll(async () => {
|
||||
subject = await request(
|
||||
"POST",
|
||||
`/admin/discounts/${IdMap.getId("total10")}/variants/${IdMap.getId(
|
||||
`/admin/discounts/${IdMap.getId("total10")}/products/${IdMap.getId(
|
||||
"testVariant"
|
||||
)}`,
|
||||
{
|
||||
@@ -29,11 +29,27 @@ describe("POST /admin/discounts/:discount_id/variants/:variant_id", () => {
|
||||
it("calls service retrieve", () => {
|
||||
expect(DiscountServiceMock.retrieve).toHaveBeenCalledTimes(1)
|
||||
expect(DiscountServiceMock.retrieve).toHaveBeenCalledWith(
|
||||
IdMap.getId("total10")
|
||||
IdMap.getId("total10"),
|
||||
{
|
||||
select: [
|
||||
"id",
|
||||
"code",
|
||||
"is_dynamic",
|
||||
"rule_id",
|
||||
"parent_discount_id",
|
||||
"starts_at",
|
||||
"ends_at",
|
||||
"created_at",
|
||||
"updated_at",
|
||||
"deleted_at",
|
||||
"metadata",
|
||||
],
|
||||
relations: ["rule", "parent_discount", "regions", "rule.valid_for"],
|
||||
}
|
||||
)
|
||||
|
||||
expect(DiscountServiceMock.addValidVariant).toHaveBeenCalledTimes(1)
|
||||
expect(DiscountServiceMock.addValidVariant).toHaveBeenCalledWith(
|
||||
expect(DiscountServiceMock.addValidProduct).toHaveBeenCalledTimes(1)
|
||||
expect(DiscountServiceMock.addValidProduct).toHaveBeenCalledWith(
|
||||
IdMap.getId("total10"),
|
||||
IdMap.getId("testVariant")
|
||||
)
|
||||
@@ -10,7 +10,7 @@ describe("POST /admin/discounts", () => {
|
||||
subject = await request("POST", "/admin/discounts", {
|
||||
payload: {
|
||||
code: "TEST",
|
||||
discount_rule: {
|
||||
rule: {
|
||||
type: "fixed",
|
||||
value: 10,
|
||||
allocation: "total",
|
||||
@@ -32,12 +32,13 @@ describe("POST /admin/discounts", () => {
|
||||
expect(DiscountServiceMock.create).toHaveBeenCalledTimes(1)
|
||||
expect(DiscountServiceMock.create).toHaveBeenCalledWith({
|
||||
code: "TEST",
|
||||
discount_rule: {
|
||||
rule: {
|
||||
type: "fixed",
|
||||
value: 10,
|
||||
allocation: "total",
|
||||
},
|
||||
is_dynamic: false
|
||||
is_disabled: false,
|
||||
is_dynamic: false,
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -49,7 +50,7 @@ describe("POST /admin/discounts", () => {
|
||||
subject = await request("POST", "/admin/discounts", {
|
||||
payload: {
|
||||
code: "10%OFF",
|
||||
discount_rule: {
|
||||
rule: {
|
||||
value: 10,
|
||||
allocation: "total",
|
||||
},
|
||||
@@ -67,9 +68,7 @@ describe("POST /admin/discounts", () => {
|
||||
})
|
||||
|
||||
it("returns error", () => {
|
||||
expect(subject.body.message[0].message).toEqual(
|
||||
`"discount_rule.type" is required`
|
||||
)
|
||||
expect(subject.body.message[0].message).toEqual(`"rule.type" is required`)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -2,6 +2,27 @@ import { IdMap } from "medusa-test-utils"
|
||||
import { request } from "../../../../../helpers/test-request"
|
||||
import { DiscountServiceMock } from "../../../../../services/__mocks__/discount"
|
||||
|
||||
const defaultFields = [
|
||||
"id",
|
||||
"code",
|
||||
"is_dynamic",
|
||||
"rule_id",
|
||||
"parent_discount_id",
|
||||
"starts_at",
|
||||
"ends_at",
|
||||
"created_at",
|
||||
"updated_at",
|
||||
"deleted_at",
|
||||
"metadata",
|
||||
]
|
||||
|
||||
const defaultRelations = [
|
||||
"rule",
|
||||
"parent_discount",
|
||||
"regions",
|
||||
"rule.valid_for",
|
||||
]
|
||||
|
||||
describe("GET /admin/discounts/:discount_id", () => {
|
||||
describe("successful retrieval", () => {
|
||||
let subject
|
||||
@@ -27,7 +48,11 @@ describe("GET /admin/discounts/:discount_id", () => {
|
||||
it("calls service retrieve", () => {
|
||||
expect(DiscountServiceMock.retrieve).toHaveBeenCalledTimes(1)
|
||||
expect(DiscountServiceMock.retrieve).toHaveBeenCalledWith(
|
||||
IdMap.getId("total10")
|
||||
IdMap.getId("total10"),
|
||||
{
|
||||
select: defaultFields,
|
||||
relations: defaultRelations,
|
||||
}
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -23,33 +23,6 @@ describe("GET /admin/discounts", () => {
|
||||
|
||||
it("calls service retrieve", () => {
|
||||
expect(DiscountServiceMock.list).toHaveBeenCalledTimes(1)
|
||||
expect(DiscountServiceMock.list).toHaveBeenCalledWith({})
|
||||
})
|
||||
})
|
||||
|
||||
describe("is_giftcard filter", () => {
|
||||
let subject
|
||||
|
||||
beforeAll(async () => {
|
||||
jest.clearAllMocks()
|
||||
subject = await request("GET", `/admin/discounts?is_giftcard=true`, {
|
||||
adminSession: {
|
||||
jwt: {
|
||||
userId: IdMap.getId("admin_user"),
|
||||
},
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
it("returns 200", () => {
|
||||
expect(subject.status).toEqual(200)
|
||||
})
|
||||
|
||||
it("calls service retrieve", () => {
|
||||
expect(DiscountServiceMock.list).toHaveBeenCalledTimes(1)
|
||||
expect(DiscountServiceMock.list).toHaveBeenCalledWith({
|
||||
is_giftcard: true,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -72,12 +45,7 @@ describe("GET /admin/discounts", () => {
|
||||
})
|
||||
|
||||
it("calls service retrieve", () => {
|
||||
expect(DiscountServiceMock.decorate).toHaveBeenCalledTimes(1)
|
||||
expect(
|
||||
DiscountServiceMock.decorate
|
||||
).toHaveBeenCalledWith(expect.anything(), expect.anything(), ["regions"])
|
||||
expect(DiscountServiceMock.list).toHaveBeenCalledTimes(1)
|
||||
expect(DiscountServiceMock.list).toHaveBeenCalledWith({})
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -2,6 +2,27 @@ import { IdMap } from "medusa-test-utils"
|
||||
import { request } from "../../../../../helpers/test-request"
|
||||
import { DiscountServiceMock } from "../../../../../services/__mocks__/discount"
|
||||
|
||||
const defaultFields = [
|
||||
"id",
|
||||
"code",
|
||||
"is_dynamic",
|
||||
"rule_id",
|
||||
"parent_discount_id",
|
||||
"starts_at",
|
||||
"ends_at",
|
||||
"created_at",
|
||||
"updated_at",
|
||||
"deleted_at",
|
||||
"metadata",
|
||||
]
|
||||
|
||||
const defaultRelations = [
|
||||
"rule",
|
||||
"parent_discount",
|
||||
"regions",
|
||||
"rule.valid_for",
|
||||
]
|
||||
|
||||
describe("DELETE /admin/discounts/:discount_id/regions/region_id", () => {
|
||||
describe("successful removal", () => {
|
||||
let subject
|
||||
@@ -29,7 +50,11 @@ describe("DELETE /admin/discounts/:discount_id/regions/region_id", () => {
|
||||
it("calls service retrieve", () => {
|
||||
expect(DiscountServiceMock.retrieve).toHaveBeenCalledTimes(1)
|
||||
expect(DiscountServiceMock.retrieve).toHaveBeenCalledWith(
|
||||
IdMap.getId("total10")
|
||||
IdMap.getId("total10"),
|
||||
{
|
||||
select: defaultFields,
|
||||
relations: defaultRelations,
|
||||
}
|
||||
)
|
||||
|
||||
expect(DiscountServiceMock.removeRegion).toHaveBeenCalledTimes(1)
|
||||
|
||||
+30
-5
@@ -2,14 +2,35 @@ import { IdMap } from "medusa-test-utils"
|
||||
import { request } from "../../../../../helpers/test-request"
|
||||
import { DiscountServiceMock } from "../../../../../services/__mocks__/discount"
|
||||
|
||||
describe("DELETE /admin/discounts/:discount_id/variants/:variant_id", () => {
|
||||
const defaultFields = [
|
||||
"id",
|
||||
"code",
|
||||
"is_dynamic",
|
||||
"rule_id",
|
||||
"parent_discount_id",
|
||||
"starts_at",
|
||||
"ends_at",
|
||||
"created_at",
|
||||
"updated_at",
|
||||
"deleted_at",
|
||||
"metadata",
|
||||
]
|
||||
|
||||
const defaultRelations = [
|
||||
"rule",
|
||||
"parent_discount",
|
||||
"regions",
|
||||
"rule.valid_for",
|
||||
]
|
||||
|
||||
describe("DELETE /admin/discounts/:discount_id/products/:variant_id", () => {
|
||||
describe("successful addition", () => {
|
||||
let subject
|
||||
|
||||
beforeAll(async () => {
|
||||
subject = await request(
|
||||
"DELETE",
|
||||
`/admin/discounts/${IdMap.getId("total10")}/variants/${IdMap.getId(
|
||||
`/admin/discounts/${IdMap.getId("total10")}/products/${IdMap.getId(
|
||||
"testVariant"
|
||||
)}`,
|
||||
{
|
||||
@@ -29,11 +50,15 @@ describe("DELETE /admin/discounts/:discount_id/variants/:variant_id", () => {
|
||||
it("calls service retrieve", () => {
|
||||
expect(DiscountServiceMock.retrieve).toHaveBeenCalledTimes(1)
|
||||
expect(DiscountServiceMock.retrieve).toHaveBeenCalledWith(
|
||||
IdMap.getId("total10")
|
||||
IdMap.getId("total10"),
|
||||
{
|
||||
select: defaultFields,
|
||||
relations: defaultRelations,
|
||||
}
|
||||
)
|
||||
|
||||
expect(DiscountServiceMock.removeValidVariant).toHaveBeenCalledTimes(1)
|
||||
expect(DiscountServiceMock.removeValidVariant).toHaveBeenCalledWith(
|
||||
expect(DiscountServiceMock.removeValidProduct).toHaveBeenCalledTimes(1)
|
||||
expect(DiscountServiceMock.removeValidProduct).toHaveBeenCalledWith(
|
||||
IdMap.getId("total10"),
|
||||
IdMap.getId("testVariant")
|
||||
)
|
||||
@@ -13,7 +13,8 @@ describe("POST /admin/discounts", () => {
|
||||
{
|
||||
payload: {
|
||||
code: "10TOTALOFF",
|
||||
discount_rule: {
|
||||
rule: {
|
||||
id: "1234",
|
||||
type: "fixed",
|
||||
value: 10,
|
||||
allocation: "total",
|
||||
@@ -38,7 +39,8 @@ describe("POST /admin/discounts", () => {
|
||||
IdMap.getId("total10"),
|
||||
{
|
||||
code: "10TOTALOFF",
|
||||
discount_rule: {
|
||||
rule: {
|
||||
id: "1234",
|
||||
type: "fixed",
|
||||
value: 10,
|
||||
allocation: "total",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { MedusaError, Validator } from "medusa-core-utils"
|
||||
import { defaultFields, defaultRelations } from "./"
|
||||
|
||||
export default async (req, res) => {
|
||||
const { discount_id, region_id } = req.params
|
||||
@@ -7,8 +7,12 @@ export default async (req, res) => {
|
||||
|
||||
await discountService.addRegion(discount_id, region_id)
|
||||
|
||||
const data = discountService.retrieve(discount_id)
|
||||
res.status(200).json({ discount: data })
|
||||
const discount = await discountService.retrieve(discount_id, {
|
||||
select: defaultFields,
|
||||
relations: defaultRelations,
|
||||
})
|
||||
|
||||
res.status(200).json({ discount })
|
||||
} catch (err) {
|
||||
throw err
|
||||
}
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
import { defaultFields, defaultRelations } from "./"
|
||||
|
||||
export default async (req, res) => {
|
||||
const { discount_id, variant_id } = req.params
|
||||
|
||||
try {
|
||||
const discountService = req.scope.resolve("discountService")
|
||||
|
||||
await discountService.addValidProduct(discount_id, variant_id)
|
||||
|
||||
const discount = await discountService.retrieve(discount_id, {
|
||||
select: defaultFields,
|
||||
relations: defaultRelations,
|
||||
})
|
||||
|
||||
res.status(200).json({ discount })
|
||||
} catch (err) {
|
||||
throw err
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
import { MedusaError, Validator } from "medusa-core-utils"
|
||||
|
||||
export default async (req, res) => {
|
||||
const { discount_id, variant_id } = req.params
|
||||
try {
|
||||
const discountService = req.scope.resolve("discountService")
|
||||
|
||||
await discountService.addValidVariant(discount_id, variant_id)
|
||||
|
||||
const data = discountService.retrieve(discount_id)
|
||||
res.status(200).json({ discount: data })
|
||||
} catch (err) {
|
||||
throw err
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@ export default async (req, res) => {
|
||||
const schema = Validator.object().keys({
|
||||
code: Validator.string().required(),
|
||||
is_dynamic: Validator.boolean().default(false),
|
||||
discount_rule: Validator.object()
|
||||
rule: Validator.object()
|
||||
.keys({
|
||||
description: Validator.string().optional(),
|
||||
type: Validator.string().required(),
|
||||
@@ -14,11 +14,9 @@ export default async (req, res) => {
|
||||
allocation: Validator.string().required(),
|
||||
valid_for: Validator.array().items(Validator.string()),
|
||||
usage_limit: Validator.number().optional(),
|
||||
total_limit: Validator.number().optional(),
|
||||
})
|
||||
.required(),
|
||||
usage_count: Validator.number().optional(),
|
||||
disabled: Validator.boolean().optional(),
|
||||
is_disabled: Validator.boolean().default(false),
|
||||
starts_at: Validator.date().optional(),
|
||||
ends_at: Validator.date().optional(),
|
||||
regions: Validator.array()
|
||||
@@ -34,9 +32,15 @@ export default async (req, res) => {
|
||||
|
||||
try {
|
||||
const discountService = req.scope.resolve("discountService")
|
||||
const data = await discountService.create(value)
|
||||
|
||||
res.status(200).json({ discount: data })
|
||||
const created = await discountService.create(value)
|
||||
const discount = await discountService.retrieve(created.id, [
|
||||
"rule",
|
||||
"rule.valid_for",
|
||||
"regions",
|
||||
])
|
||||
|
||||
res.status(200).json({ discount })
|
||||
} catch (err) {
|
||||
throw err
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import { MedusaError, Validator } from "medusa-core-utils"
|
||||
|
||||
export default async (req, res) => {
|
||||
const { discount_id } = req.params
|
||||
|
||||
const schema = Validator.object().keys({
|
||||
code: Validator.string().required(),
|
||||
metadata: Validator.object().optional(),
|
||||
@@ -16,9 +17,11 @@ export default async (req, res) => {
|
||||
const discountService = req.scope.resolve("discountService")
|
||||
await discountService.createDynamicCode(discount_id, value)
|
||||
|
||||
const data = await discountService.retrieve(dicsount_id)
|
||||
const discount = await discountService.retrieve(discount_id, {
|
||||
relations: ["rule", "rule.valid_for", "regions"],
|
||||
})
|
||||
|
||||
res.status(200).json({ discount: data })
|
||||
res.status(200).json({ discount })
|
||||
} catch (err) {
|
||||
throw err
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
export default async (req, res) => {
|
||||
const { discount_id } = req.params
|
||||
|
||||
try {
|
||||
const discountService = req.scope.resolve("discountService")
|
||||
await discountService.delete(discount_id)
|
||||
|
||||
@@ -1,20 +1,15 @@
|
||||
import { MedusaError, Validator } from "medusa-core-utils"
|
||||
|
||||
export default async (req, res) => {
|
||||
const { discount_id, code } = req.params
|
||||
|
||||
const { value, error } = schema.validate(req.body)
|
||||
if (error) {
|
||||
throw new MedusaError(MedusaError.Types.INVALID_DATA, error.details)
|
||||
}
|
||||
|
||||
try {
|
||||
const discountService = req.scope.resolve("discountService")
|
||||
await discountService.deleteDynamicCode(discount_id, code)
|
||||
|
||||
const data = await discountService.retrieve(dicsount_id)
|
||||
const discount = await discountService.retrieve(discount_id, {
|
||||
relations: ["rule", "rule.valid_for", "regions"],
|
||||
})
|
||||
|
||||
res.status(200).json({ discount: data })
|
||||
res.status(200).json({ discount })
|
||||
} catch (err) {
|
||||
throw err
|
||||
}
|
||||
|
||||
@@ -1,25 +1,15 @@
|
||||
import { defaultFields, defaultRelations } from "./"
|
||||
|
||||
export default async (req, res) => {
|
||||
const { discount_id } = req.params
|
||||
try {
|
||||
const discountService = req.scope.resolve("discountService")
|
||||
const data = await discountService.retrieve(discount_id)
|
||||
const data = await discountService.retrieve(discount_id, {
|
||||
select: defaultFields,
|
||||
relations: defaultRelations,
|
||||
})
|
||||
|
||||
const discount = await discountService.decorate(
|
||||
data,
|
||||
[
|
||||
"code",
|
||||
"is_dynamic",
|
||||
"discount_rule",
|
||||
"usage_count",
|
||||
"disabled",
|
||||
"starts_at",
|
||||
"ends_at",
|
||||
"regions",
|
||||
],
|
||||
["valid_for"]
|
||||
)
|
||||
|
||||
res.status(200).json({ discount })
|
||||
res.status(200).json({ discount: data })
|
||||
} catch (err) {
|
||||
throw err
|
||||
}
|
||||
|
||||
@@ -34,12 +34,12 @@ export default app => {
|
||||
|
||||
// Discount valid variants management
|
||||
route.post(
|
||||
"/:discount_id/variants/:variant_id",
|
||||
middlewares.wrap(require("./add-valid-variant").default)
|
||||
"/:discount_id/products/:variant_id",
|
||||
middlewares.wrap(require("./add-valid-product").default)
|
||||
)
|
||||
route.delete(
|
||||
"/:discount_id/variants/:variant_id",
|
||||
middlewares.wrap(require("./remove-valid-variant").default)
|
||||
"/:discount_id/products/:variant_id",
|
||||
middlewares.wrap(require("./remove-valid-product").default)
|
||||
)
|
||||
|
||||
// Discount region management
|
||||
@@ -54,3 +54,24 @@ export default app => {
|
||||
|
||||
return app
|
||||
}
|
||||
|
||||
export const defaultFields = [
|
||||
"id",
|
||||
"code",
|
||||
"is_dynamic",
|
||||
"rule_id",
|
||||
"parent_discount_id",
|
||||
"starts_at",
|
||||
"ends_at",
|
||||
"created_at",
|
||||
"updated_at",
|
||||
"deleted_at",
|
||||
"metadata",
|
||||
]
|
||||
|
||||
export const defaultRelations = [
|
||||
"rule",
|
||||
"parent_discount",
|
||||
"regions",
|
||||
"rule.valid_for",
|
||||
]
|
||||
@@ -1,36 +1,24 @@
|
||||
import { defaultFields, defaultRelations } from "./"
|
||||
|
||||
export default async (req, res) => {
|
||||
try {
|
||||
const selector = {}
|
||||
|
||||
const limit = parseInt(req.query.limit) || 20
|
||||
const offset = parseInt(req.query.offset) || 0
|
||||
|
||||
const discountService = req.scope.resolve("discountService")
|
||||
|
||||
if ("is_giftcard" in req.query) {
|
||||
selector.is_giftcard = req.query.is_giftcard === "true"
|
||||
const listConfig = {
|
||||
select: defaultFields,
|
||||
relations: defaultRelations,
|
||||
skip: offset,
|
||||
take: limit,
|
||||
}
|
||||
|
||||
let expandFields = []
|
||||
if ("expand_fields" in req.query) {
|
||||
expandFields = req.query.expand_fields.split(",")
|
||||
}
|
||||
const discounts = await discountService.list(selector, listConfig)
|
||||
|
||||
let includeFields = [
|
||||
"usage_count",
|
||||
"starts_at",
|
||||
"ends_at",
|
||||
"original_amount",
|
||||
"created",
|
||||
]
|
||||
if ("fields" in req.query) {
|
||||
includeFields = req.query.fields.split(",")
|
||||
}
|
||||
|
||||
const raw = await discountService.list(selector)
|
||||
|
||||
const data = await Promise.all(
|
||||
raw.map(d => discountService.decorate(d, includeFields, expandFields))
|
||||
)
|
||||
|
||||
res.status(200).json({ discounts: data })
|
||||
res.status(200).json({ discounts })
|
||||
} catch (err) {
|
||||
throw err
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { defaultFields, defaultRelations } from "./"
|
||||
|
||||
export default async (req, res) => {
|
||||
const { discount_id, region_id } = req.params
|
||||
|
||||
@@ -5,9 +7,12 @@ export default async (req, res) => {
|
||||
const discountService = req.scope.resolve("discountService")
|
||||
|
||||
await discountService.removeRegion(discount_id, region_id)
|
||||
const discount = await discountService.retrieve(discount_id, {
|
||||
select: defaultFields,
|
||||
relations: defaultRelations,
|
||||
})
|
||||
|
||||
const data = discountService.retrieve(discount_id)
|
||||
res.status(200).json({ discounts: data })
|
||||
res.status(200).json({ discount })
|
||||
} catch (err) {
|
||||
throw err
|
||||
}
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
import { defaultFields, defaultRelations } from "./"
|
||||
|
||||
export default async (req, res) => {
|
||||
const { discount_id, variant_id } = req.params
|
||||
|
||||
try {
|
||||
const discountService = req.scope.resolve("discountService")
|
||||
|
||||
await discountService.removeValidProduct(discount_id, variant_id)
|
||||
|
||||
const discount = await discountService.retrieve(discount_id, {
|
||||
select: defaultFields,
|
||||
relations: defaultRelations,
|
||||
})
|
||||
|
||||
res.status(200).json({ discount })
|
||||
} catch (err) {
|
||||
throw err
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
export default async (req, res) => {
|
||||
const { discount_id, variant_id } = req.params
|
||||
|
||||
try {
|
||||
const discountService = req.scope.resolve("discountService")
|
||||
|
||||
await discountService.removeValidVariant(discount_id, variant_id)
|
||||
|
||||
const data = discountService.retrieve(discount_id)
|
||||
res.status(200).json({ discounts: data })
|
||||
} catch (err) {
|
||||
throw err
|
||||
}
|
||||
}
|
||||
@@ -1,23 +1,22 @@
|
||||
import { MedusaError, Validator } from "medusa-core-utils"
|
||||
import { defaultFields, defaultRelations } from "./"
|
||||
|
||||
export default async (req, res) => {
|
||||
const { discount_id } = req.params
|
||||
const schema = Validator.object().keys({
|
||||
code: Validator.string().optional(),
|
||||
is_dynamic: Validator.boolean().default(false),
|
||||
is_giftcard: Validator.boolean().optional(),
|
||||
discount_rule: Validator.object()
|
||||
rule: Validator.object()
|
||||
.keys({
|
||||
id: Validator.string().required(),
|
||||
description: Validator.string().optional(),
|
||||
type: Validator.string().required(),
|
||||
value: Validator.number().required(),
|
||||
allocation: Validator.string().required(),
|
||||
valid_for: Validator.array().items(Validator.string()),
|
||||
usage_limit: Validator.number().optional(),
|
||||
})
|
||||
.optional(),
|
||||
usage_count: Validator.number().optional(),
|
||||
disabled: Validator.boolean().optional(),
|
||||
is_disabled: Validator.boolean().optional(),
|
||||
starts_at: Validator.date().optional(),
|
||||
ends_at: Validator.date().optional(),
|
||||
regions: Validator.array()
|
||||
@@ -35,9 +34,12 @@ export default async (req, res) => {
|
||||
|
||||
await discountService.update(discount_id, value)
|
||||
|
||||
const data = await discountService.retrieve(discount_id)
|
||||
const discount = await discountService.retrieve(discount_id, {
|
||||
select: defaultFields,
|
||||
relations: defaultRelations,
|
||||
})
|
||||
|
||||
res.status(200).json({ discounts: data })
|
||||
res.status(200).json({ discount })
|
||||
} catch (err) {
|
||||
throw err
|
||||
}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
import { MedusaError, Validator } from "medusa-core-utils"
|
||||
import { defaultFields, defaultRelations } from "./"
|
||||
|
||||
export default async (req, res) => {
|
||||
const schema = Validator.object().keys({
|
||||
value: Validator.number()
|
||||
.integer()
|
||||
.optional(),
|
||||
ends_at: Validator.date().optional(),
|
||||
is_disabled: Validator.boolean().optional(),
|
||||
region_id: Validator.string().optional(),
|
||||
metadata: Validator.object().optional(),
|
||||
})
|
||||
|
||||
const { value, error } = schema.validate(req.body)
|
||||
if (error) {
|
||||
throw new MedusaError(MedusaError.Types.INVALID_DATA, error.details)
|
||||
}
|
||||
|
||||
try {
|
||||
const giftCardService = req.scope.resolve("giftCardService")
|
||||
|
||||
await giftCardService.create(value)
|
||||
|
||||
const giftCard = await giftCardService.retrieve(id, {
|
||||
select: defaultFields,
|
||||
relations: defaultRelations,
|
||||
})
|
||||
|
||||
res.status(200).json({ gift_card: giftCard })
|
||||
} catch (err) {
|
||||
throw err
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
export default async (req, res) => {
|
||||
const { id } = req.params
|
||||
|
||||
try {
|
||||
const giftCardService = req.scope.resolve("giftCardService")
|
||||
await giftCardService.delete(id)
|
||||
|
||||
res.json({
|
||||
id,
|
||||
object: "gift-card",
|
||||
deleted: true,
|
||||
})
|
||||
} catch (err) {
|
||||
throw err
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import { defaultFields, defaultRelations } from "./"
|
||||
|
||||
export default async (req, res) => {
|
||||
const { id } = req.params
|
||||
|
||||
try {
|
||||
const giftCardService = req.scope.resolve("giftCardService")
|
||||
const giftCard = await giftCardService.retrieve(id, {
|
||||
select: defaultFields,
|
||||
relations: defaultRelations,
|
||||
})
|
||||
|
||||
res.status(200).json({ gift_card: giftCard })
|
||||
} catch (err) {
|
||||
throw err
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
import { Router } from "express"
|
||||
import middlewares from "../../../middlewares"
|
||||
|
||||
const route = Router()
|
||||
|
||||
export default app => {
|
||||
app.use("/gift-cards", route)
|
||||
|
||||
route.get("/", middlewares.wrap(require("./list-gift-cards").default))
|
||||
|
||||
route.post("/", middlewares.wrap(require("./create-gift-card").default))
|
||||
|
||||
route.get("/:id", middlewares.wrap(require("./get-gift-card").default))
|
||||
|
||||
route.post("/:id", middlewares.wrap(require("./update-gift-card").default))
|
||||
|
||||
route.delete("/:id", middlewares.wrap(require("./delete-gift-card").default))
|
||||
|
||||
return app
|
||||
}
|
||||
|
||||
export const defaultFields = [
|
||||
"id",
|
||||
"code",
|
||||
"value",
|
||||
"balance",
|
||||
"region_id",
|
||||
"is_disabled",
|
||||
"ends_at",
|
||||
"created_at",
|
||||
"updated_at",
|
||||
"deleted_at",
|
||||
"metadata",
|
||||
]
|
||||
|
||||
export const defaultRelations = ["region"]
|
||||
|
||||
export const allowedFields = [
|
||||
"id",
|
||||
"code",
|
||||
"value",
|
||||
"balance",
|
||||
"region_id",
|
||||
"is_disabled",
|
||||
"ends_at",
|
||||
"created_at",
|
||||
"updated_at",
|
||||
"deleted_at",
|
||||
"metadata",
|
||||
]
|
||||
|
||||
export const allowedRelations = ["region"]
|
||||
@@ -0,0 +1,19 @@
|
||||
import { defaultFields, defaultRelations } from "./"
|
||||
|
||||
export default async (req, res) => {
|
||||
try {
|
||||
const selector = {}
|
||||
|
||||
const giftCardService = req.scope.resolve("giftCardService")
|
||||
|
||||
const giftCards = await giftCardService.list(selector, {
|
||||
select: defaultFields,
|
||||
relations: defaultRelations,
|
||||
order: { created_at: "DESC" },
|
||||
})
|
||||
|
||||
res.status(200).json({ gift_cards: giftCards })
|
||||
} catch (err) {
|
||||
throw err
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
import { MedusaError, Validator } from "medusa-core-utils"
|
||||
|
||||
export default async (req, res) => {
|
||||
const { id } = req.params
|
||||
|
||||
const schema = Validator.object().keys({
|
||||
balance: Validator.number()
|
||||
.precision(0)
|
||||
.optional(),
|
||||
ends_at: Validator.date().optional(),
|
||||
is_disabled: Validator.boolean().optional(),
|
||||
region_id: Validator.string().optional(),
|
||||
metadata: Validator.object().optional(),
|
||||
})
|
||||
|
||||
const { value, error } = schema.validate(req.body)
|
||||
if (error) {
|
||||
throw new MedusaError(MedusaError.Types.INVALID_DATA, error.details)
|
||||
}
|
||||
|
||||
try {
|
||||
const giftCardService = req.scope.resolve("giftCardService")
|
||||
|
||||
await giftCardService.update(id, value)
|
||||
|
||||
const giftCard = await giftCardService.retrieve(id, {
|
||||
select: defaultFields,
|
||||
relations: defaultRelations,
|
||||
})
|
||||
|
||||
res.status(200).json({ gift_card: giftCard })
|
||||
} catch (err) {
|
||||
throw err
|
||||
}
|
||||
}
|
||||
@@ -9,11 +9,15 @@ import regionRoutes from "./regions"
|
||||
import shippingOptionRoutes from "./shipping-options"
|
||||
import shippingProfileRoutes from "./shipping-profiles"
|
||||
import discountRoutes from "./discounts"
|
||||
import giftCardRoutes from "./gift-cards"
|
||||
import orderRoutes from "./orders"
|
||||
import storeRoutes from "./store"
|
||||
import uploadRoutes from "./uploads"
|
||||
import customerRoutes from "./customers"
|
||||
import appRoutes from "./apps"
|
||||
import swapRoutes from "./swaps"
|
||||
import returnRoutes from "./returns"
|
||||
import variantRoutes from "./variants"
|
||||
|
||||
const route = Router()
|
||||
|
||||
@@ -48,10 +52,14 @@ export default (app, container, config) => {
|
||||
shippingOptionRoutes(route)
|
||||
shippingProfileRoutes(route)
|
||||
discountRoutes(route)
|
||||
giftCardRoutes(route)
|
||||
orderRoutes(route)
|
||||
storeRoutes(route)
|
||||
uploadRoutes(route)
|
||||
customerRoutes(route)
|
||||
swapRoutes(route)
|
||||
returnRoutes(route)
|
||||
variantRoutes(route)
|
||||
|
||||
return app
|
||||
}
|
||||
@@ -33,7 +33,7 @@ describe("POST /admin/orders/:id/archive", () => {
|
||||
|
||||
it("returns order with status = archived", () => {
|
||||
expect(subject.status).toEqual(200)
|
||||
expect(subject.body.order._id).toEqual(IdMap.getId("processed-order"))
|
||||
expect(subject.body.order.id).toEqual(IdMap.getId("processed-order"))
|
||||
expect(subject.body.order.status).toEqual("archived")
|
||||
})
|
||||
})
|
||||
|
||||
@@ -33,7 +33,7 @@ describe("POST /admin/orders/:id/cancel", () => {
|
||||
|
||||
it("returns order with status = cancelled", () => {
|
||||
expect(subject.status).toEqual(200)
|
||||
expect(subject.body.order._id).toEqual(IdMap.getId("test-order"))
|
||||
expect(subject.body.order.id).toEqual(IdMap.getId("test-order"))
|
||||
expect(subject.body.order.status).toEqual("cancelled")
|
||||
})
|
||||
})
|
||||
|
||||
@@ -33,7 +33,7 @@ describe("POST /admin/orders/:id/capture", () => {
|
||||
|
||||
it("returns order with payment_status = captured", () => {
|
||||
expect(subject.status).toEqual(200)
|
||||
expect(subject.body.order._id).toEqual(IdMap.getId("test-order"))
|
||||
expect(subject.body.order.id).toEqual(IdMap.getId("test-order"))
|
||||
expect(subject.body.order.payment_status).toEqual("captured")
|
||||
})
|
||||
})
|
||||
|
||||
@@ -48,7 +48,7 @@ describe("POST /admin/orders/:id/fulfillment", () => {
|
||||
|
||||
it("returns order with fulfillment_status = fulfilled", () => {
|
||||
expect(subject.status).toEqual(200)
|
||||
expect(subject.body.order._id).toEqual(IdMap.getId("test-order"))
|
||||
expect(subject.body.order.id).toEqual(IdMap.getId("test-order"))
|
||||
expect(subject.body.order.fulfillment_status).toEqual("fulfilled")
|
||||
})
|
||||
})
|
||||
|
||||
@@ -2,6 +2,53 @@ import { IdMap } from "medusa-test-utils"
|
||||
import { request } from "../../../../../helpers/test-request"
|
||||
import { OrderServiceMock } from "../../../../../services/__mocks__/order"
|
||||
|
||||
const defaultRelations = [
|
||||
"customer",
|
||||
"billing_address",
|
||||
"shipping_address",
|
||||
"discounts",
|
||||
"shipping_methods",
|
||||
"payments",
|
||||
"fulfillments",
|
||||
"returns",
|
||||
"gift_cards",
|
||||
"gift_card_transactions",
|
||||
"swaps",
|
||||
"swaps.return_order",
|
||||
"swaps.payment",
|
||||
"swaps.shipping_methods",
|
||||
"swaps.shipping_address",
|
||||
"swaps.additional_items",
|
||||
"swaps.fulfillments",
|
||||
]
|
||||
|
||||
const defaultFields = [
|
||||
"id",
|
||||
"status",
|
||||
"fulfillment_status",
|
||||
"payment_status",
|
||||
"display_id",
|
||||
"cart_id",
|
||||
"customer_id",
|
||||
"email",
|
||||
"region_id",
|
||||
"currency_code",
|
||||
"tax_rate",
|
||||
"canceled_at",
|
||||
"created_at",
|
||||
"updated_at",
|
||||
"metadata",
|
||||
"items.refundable",
|
||||
"shipping_total",
|
||||
"discount_total",
|
||||
"tax_total",
|
||||
"refunded_total",
|
||||
"gift_card_total",
|
||||
"subtotal",
|
||||
"total",
|
||||
"refundable_amount",
|
||||
]
|
||||
|
||||
describe("GET /admin/orders", () => {
|
||||
describe("successfully gets an order", () => {
|
||||
let subject
|
||||
@@ -27,13 +74,17 @@ describe("GET /admin/orders", () => {
|
||||
it("calls orderService retrieve", () => {
|
||||
expect(OrderServiceMock.retrieve).toHaveBeenCalledTimes(1)
|
||||
expect(OrderServiceMock.retrieve).toHaveBeenCalledWith(
|
||||
IdMap.getId("test-order")
|
||||
IdMap.getId("test-order"),
|
||||
{
|
||||
select: defaultFields,
|
||||
relations: defaultRelations,
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
it("returns order", () => {
|
||||
expect(subject.status).toEqual(200)
|
||||
expect(subject.body.order._id).toEqual(IdMap.getId("test-order"))
|
||||
expect(subject.body.order.id).toEqual(IdMap.getId("test-order"))
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,6 +1,7 @@
|
||||
import { IdMap } from "medusa-test-utils"
|
||||
import { request } from "../../../../../helpers/test-request"
|
||||
import { OrderServiceMock } from "../../../../../services/__mocks__/order"
|
||||
import { orders } from "../../../../../services/__mocks__/order"
|
||||
import { ReturnService } from "../../../../../services/__mocks__/return"
|
||||
|
||||
describe("POST /admin/orders/:id/return", () => {
|
||||
describe("successfully returns full order", () => {
|
||||
@@ -19,6 +20,7 @@ describe("POST /admin/orders/:id/return", () => {
|
||||
quantity: 10,
|
||||
},
|
||||
],
|
||||
refund: 10,
|
||||
},
|
||||
adminSession: {
|
||||
jwt: {
|
||||
@@ -34,17 +36,21 @@ describe("POST /admin/orders/:id/return", () => {
|
||||
})
|
||||
|
||||
it("calls OrderService return", () => {
|
||||
expect(OrderServiceMock.requestReturn).toHaveBeenCalledTimes(1)
|
||||
expect(OrderServiceMock.requestReturn).toHaveBeenCalledWith(
|
||||
IdMap.getId("test-order"),
|
||||
[
|
||||
{
|
||||
item_id: IdMap.getId("existingLine"),
|
||||
quantity: 10,
|
||||
},
|
||||
],
|
||||
undefined, // no shipping method
|
||||
undefined // no refund amount
|
||||
expect(ReturnService.create).toHaveBeenCalledTimes(1)
|
||||
expect(ReturnService.create).toHaveBeenCalledWith(
|
||||
{
|
||||
order_id: IdMap.getId("test-order"),
|
||||
idempotency_key: "testkey",
|
||||
items: [
|
||||
{
|
||||
item_id: IdMap.getId("existingLine"),
|
||||
quantity: 10,
|
||||
},
|
||||
],
|
||||
refund_amount: 10,
|
||||
shipping_method: undefined,
|
||||
},
|
||||
orders.testOrder
|
||||
)
|
||||
})
|
||||
})
|
||||
@@ -81,18 +87,134 @@ describe("POST /admin/orders/:id/return", () => {
|
||||
})
|
||||
|
||||
it("calls OrderService return", () => {
|
||||
expect(OrderServiceMock.requestReturn).toHaveBeenCalledTimes(1)
|
||||
expect(OrderServiceMock.requestReturn).toHaveBeenCalledWith(
|
||||
IdMap.getId("test-order"),
|
||||
[
|
||||
{
|
||||
item_id: IdMap.getId("existingLine"),
|
||||
quantity: 10,
|
||||
},
|
||||
],
|
||||
undefined, // no shipping method
|
||||
0
|
||||
expect(ReturnService.create).toHaveBeenCalledTimes(1)
|
||||
expect(ReturnService.create).toHaveBeenCalledWith(
|
||||
{
|
||||
order_id: IdMap.getId("test-order"),
|
||||
idempotency_key: "testkey",
|
||||
items: [
|
||||
{
|
||||
item_id: IdMap.getId("existingLine"),
|
||||
quantity: 10,
|
||||
},
|
||||
],
|
||||
refund_amount: 0,
|
||||
shipping_method: undefined,
|
||||
},
|
||||
orders.testOrder
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe("defaults to 0 on negative refund amount", () => {
|
||||
let subject
|
||||
|
||||
beforeAll(async () => {
|
||||
jest.clearAllMocks()
|
||||
subject = await request(
|
||||
"POST",
|
||||
`/admin/orders/${IdMap.getId("test-order")}/return`,
|
||||
{
|
||||
payload: {
|
||||
items: [
|
||||
{
|
||||
item_id: IdMap.getId("existingLine"),
|
||||
quantity: 10,
|
||||
},
|
||||
],
|
||||
refund: -1,
|
||||
},
|
||||
adminSession: {
|
||||
jwt: {
|
||||
userId: IdMap.getId("admin_user"),
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
it("returns 200", () => {
|
||||
expect(subject.status).toEqual(200)
|
||||
})
|
||||
|
||||
it("calls OrderService return", () => {
|
||||
expect(ReturnService.create).toHaveBeenCalledTimes(1)
|
||||
expect(ReturnService.create).toHaveBeenCalledWith(
|
||||
{
|
||||
order_id: IdMap.getId("test-order"),
|
||||
idempotency_key: "testkey",
|
||||
items: [
|
||||
{
|
||||
item_id: IdMap.getId("existingLine"),
|
||||
quantity: 10,
|
||||
},
|
||||
],
|
||||
refund_amount: 0,
|
||||
shipping_method: undefined,
|
||||
},
|
||||
orders.testOrder
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe("fulfills", () => {
|
||||
let subject
|
||||
|
||||
beforeAll(async () => {
|
||||
jest.clearAllMocks()
|
||||
subject = await request(
|
||||
"POST",
|
||||
`/admin/orders/${IdMap.getId("test-order")}/return`,
|
||||
{
|
||||
payload: {
|
||||
items: [
|
||||
{
|
||||
item_id: IdMap.getId("existingLine"),
|
||||
quantity: 10,
|
||||
},
|
||||
],
|
||||
refund: 100,
|
||||
return_shipping: {
|
||||
option_id: "opt_1234",
|
||||
price: 12,
|
||||
},
|
||||
},
|
||||
adminSession: {
|
||||
jwt: {
|
||||
userId: IdMap.getId("admin_user"),
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
it("returns 200", () => {
|
||||
expect(subject.status).toEqual(200)
|
||||
})
|
||||
|
||||
it("calls OrderService return", () => {
|
||||
expect(ReturnService.create).toHaveBeenCalledTimes(1)
|
||||
expect(ReturnService.create).toHaveBeenCalledWith(
|
||||
{
|
||||
order_id: IdMap.getId("test-order"),
|
||||
idempotency_key: "testkey",
|
||||
items: [
|
||||
{
|
||||
item_id: IdMap.getId("existingLine"),
|
||||
quantity: 10,
|
||||
},
|
||||
],
|
||||
refund_amount: 100,
|
||||
shipping_method: {
|
||||
option_id: "opt_1234",
|
||||
price: 12,
|
||||
},
|
||||
},
|
||||
orders.testOrder
|
||||
)
|
||||
|
||||
expect(ReturnService.fulfill).toHaveBeenCalledTimes(1)
|
||||
expect(ReturnService.fulfill).toHaveBeenCalledWith("return")
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,40 +0,0 @@
|
||||
import { IdMap } from "medusa-test-utils"
|
||||
import { request } from "../../../../../helpers/test-request"
|
||||
import { OrderServiceMock } from "../../../../../services/__mocks__/order"
|
||||
|
||||
describe("POST /admin/orders/:id/metadata", () => {
|
||||
describe("successfully sets metadata on order", () => {
|
||||
let subject
|
||||
|
||||
beforeAll(async () => {
|
||||
subject = await request(
|
||||
"POST",
|
||||
`/admin/orders/${IdMap.getId("test-order")}/metadata`,
|
||||
{
|
||||
payload: {
|
||||
key: "Test key",
|
||||
value: "Test value",
|
||||
},
|
||||
adminSession: {
|
||||
jwt: {
|
||||
userId: IdMap.getId("admin_user"),
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
it("returns 200", () => {
|
||||
expect(subject.status).toEqual(200)
|
||||
})
|
||||
|
||||
it("calls OrderService setMetadata", () => {
|
||||
expect(OrderServiceMock.setMetadata).toHaveBeenCalledTimes(1)
|
||||
expect(OrderServiceMock.setMetadata).toHaveBeenCalledWith(
|
||||
IdMap.getId("test-order"),
|
||||
"Test key",
|
||||
"Test value"
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,41 @@
|
||||
import _ from "lodash"
|
||||
import { Validator, MedusaError } from "medusa-core-utils"
|
||||
import { defaultFields, defaultRelations } from "./"
|
||||
|
||||
export default async (req, res) => {
|
||||
const { id } = req.params
|
||||
|
||||
const schema = Validator.object().keys({
|
||||
price: Validator.number()
|
||||
.integer()
|
||||
.integer()
|
||||
.allow(0)
|
||||
.required(),
|
||||
option_id: Validator.string().required(),
|
||||
data: Validator.object()
|
||||
.optional()
|
||||
.default({}),
|
||||
})
|
||||
|
||||
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")
|
||||
|
||||
await orderService.addShippingMethod(id, value.option_id, value.data, {
|
||||
price: value.price,
|
||||
})
|
||||
|
||||
const order = await orderService.retrieve(id, {
|
||||
select: defaultFields,
|
||||
relations: defaultRelations,
|
||||
})
|
||||
|
||||
res.status(200).json({ order })
|
||||
} catch (err) {
|
||||
throw err
|
||||
}
|
||||
}
|
||||
@@ -3,12 +3,13 @@ export default async (req, res) => {
|
||||
|
||||
try {
|
||||
const orderService = req.scope.resolve("orderService")
|
||||
let order = await orderService.archive(id)
|
||||
order = await orderService.decorate(
|
||||
order,
|
||||
[],
|
||||
["region", "customer", "swaps"]
|
||||
)
|
||||
|
||||
await orderService.archive(id)
|
||||
|
||||
const order = await orderService.retrieve(id, {
|
||||
relations: ["region", "customer", "swaps"],
|
||||
})
|
||||
|
||||
res.json({ order })
|
||||
} catch (error) {
|
||||
throw error
|
||||
|
||||
@@ -3,12 +3,13 @@ export default async (req, res) => {
|
||||
|
||||
try {
|
||||
const orderService = req.scope.resolve("orderService")
|
||||
let order = await orderService.cancel(id)
|
||||
order = await orderService.decorate(
|
||||
order,
|
||||
[],
|
||||
["region", "customer", "swaps"]
|
||||
)
|
||||
|
||||
await orderService.cancel(id)
|
||||
|
||||
const order = await orderService.retrieve(id, {
|
||||
relations: ["region", "customer", "swaps"],
|
||||
})
|
||||
|
||||
res.json({ order })
|
||||
} catch (error) {
|
||||
throw error
|
||||
|
||||
@@ -1,14 +1,18 @@
|
||||
import { defaultRelations, defaultFields } from "./"
|
||||
|
||||
export default async (req, res) => {
|
||||
const { id } = req.params
|
||||
|
||||
try {
|
||||
const orderService = req.scope.resolve("orderService")
|
||||
let order = await orderService.capturePayment(id)
|
||||
order = await orderService.decorate(
|
||||
order,
|
||||
[],
|
||||
["region", "customer", "swaps"]
|
||||
)
|
||||
|
||||
await orderService.capturePayment(id)
|
||||
|
||||
const order = await orderService.retrieve(id, {
|
||||
select: defaultFields,
|
||||
relations: defaultRelations,
|
||||
})
|
||||
|
||||
res.json({ order })
|
||||
} catch (error) {
|
||||
throw error
|
||||
|
||||
@@ -3,12 +3,13 @@ export default async (req, res) => {
|
||||
|
||||
try {
|
||||
const orderService = req.scope.resolve("orderService")
|
||||
let order = await orderService.completeOrder(id)
|
||||
order = await orderService.decorate(
|
||||
order,
|
||||
[],
|
||||
["region", "customer", "swaps"]
|
||||
)
|
||||
|
||||
await orderService.completeOrder(id)
|
||||
|
||||
const order = await orderService.retrieve(id, {
|
||||
relations: ["region", "customer", "swaps"],
|
||||
})
|
||||
|
||||
res.json({ order })
|
||||
} catch (error) {
|
||||
throw error
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { MedusaError, Validator } from "medusa-core-utils"
|
||||
import { defaultRelations, defaultFields } from "./"
|
||||
|
||||
export default async (req, res) => {
|
||||
const { id } = req.params
|
||||
@@ -21,19 +22,14 @@ export default async (req, res) => {
|
||||
try {
|
||||
const orderService = req.scope.resolve("orderService")
|
||||
|
||||
let order = await orderService.createFulfillment(
|
||||
id,
|
||||
value.items,
|
||||
value.metadata
|
||||
)
|
||||
await orderService.createFulfillment(id, value.items, value.metadata)
|
||||
|
||||
const data = await orderService.decorate(
|
||||
order,
|
||||
[],
|
||||
["region", "customer", "swaps"]
|
||||
)
|
||||
const order = await orderService.retrieve(id, {
|
||||
select: defaultFields,
|
||||
relations: defaultRelations,
|
||||
})
|
||||
|
||||
res.json({ order: data })
|
||||
res.json({ order })
|
||||
} catch (error) {
|
||||
throw error
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { MedusaError, Validator } from "medusa-core-utils"
|
||||
import { defaultRelations, defaultFields } from "./"
|
||||
|
||||
export default async (req, res) => {
|
||||
const { id } = req.params
|
||||
@@ -17,16 +18,18 @@ export default async (req, res) => {
|
||||
|
||||
try {
|
||||
const orderService = req.scope.resolve("orderService")
|
||||
let order = await orderService.createShipment(
|
||||
|
||||
await orderService.createShipment(
|
||||
id,
|
||||
value.fulfillment_id,
|
||||
value.tracking_numbers
|
||||
)
|
||||
order = await orderService.decorate(
|
||||
order,
|
||||
[],
|
||||
["region", "customer", "swaps"]
|
||||
)
|
||||
|
||||
const order = await orderService.retrieve(id, {
|
||||
select: defaultFields,
|
||||
relations: defaultRelations,
|
||||
})
|
||||
|
||||
res.json({ order })
|
||||
} catch (error) {
|
||||
throw error
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { MedusaError, Validator } from "medusa-core-utils"
|
||||
import { defaultFields, defaultRelations } from "./"
|
||||
|
||||
export default async (req, res) => {
|
||||
const { id, swap_id } = req.params
|
||||
@@ -19,22 +20,18 @@ export default async (req, res) => {
|
||||
const orderService = req.scope.resolve("orderService")
|
||||
const swapService = req.scope.resolve("swapService")
|
||||
|
||||
const order = await orderService.retrieve(id)
|
||||
|
||||
await swapService.createShipment(
|
||||
swap_id,
|
||||
value.fulfillment_id,
|
||||
value.tracking_numbers
|
||||
)
|
||||
|
||||
// Decorate the order
|
||||
const data = await orderService.decorate(
|
||||
order,
|
||||
[],
|
||||
["region", "customer", "swaps"]
|
||||
)
|
||||
const order = await orderService.retrieve(id, {
|
||||
select: defaultFields,
|
||||
relations: defaultRelations,
|
||||
})
|
||||
|
||||
res.json({ order: data })
|
||||
res.json({ order })
|
||||
} catch (error) {
|
||||
throw error
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { MedusaError, Validator } from "medusa-core-utils"
|
||||
import { defaultFields, defaultRelations } from "./"
|
||||
|
||||
export default async (req, res) => {
|
||||
const { id } = req.params
|
||||
@@ -12,8 +13,10 @@ export default async (req, res) => {
|
||||
.required(),
|
||||
return_shipping: Validator.object()
|
||||
.keys({
|
||||
id: Validator.string().optional(),
|
||||
price: Validator.number().optional(),
|
||||
option_id: Validator.string().optional(),
|
||||
price: Validator.number()
|
||||
.integer()
|
||||
.optional(),
|
||||
})
|
||||
.optional(),
|
||||
additional_items: Validator.array().items({
|
||||
@@ -27,41 +30,141 @@ export default async (req, res) => {
|
||||
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 swapService = req.scope.resolve("swapService")
|
||||
const returnService = req.scope.resolve("returnService")
|
||||
|
||||
let order = await orderService.retrieve(id)
|
||||
let inProgress = true
|
||||
let err = false
|
||||
|
||||
// Phase 1: Create swap and add it to the order
|
||||
const swap = await swapService.create(
|
||||
order,
|
||||
value.return_items,
|
||||
value.additional_items,
|
||||
value.return_shipping
|
||||
)
|
||||
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(id, {
|
||||
select: ["refunded_total", "total"],
|
||||
relations: ["items", "swaps"],
|
||||
})
|
||||
|
||||
await orderService.registerSwapCreated(id, swap._id)
|
||||
const swap = await swapService
|
||||
.withTransaction(manager)
|
||||
.create(
|
||||
order,
|
||||
value.return_items,
|
||||
value.additional_items,
|
||||
value.return_shipping,
|
||||
{ idempotency_key: idempotencyKey.idempotency_key }
|
||||
)
|
||||
|
||||
// --> swap_created
|
||||
// Phase 2: Create a return request from the swap
|
||||
await swapService.requestReturn(order, swap._id)
|
||||
await swapService.withTransaction(manager).createCart(swap.id)
|
||||
const returnOrder = await returnService
|
||||
.withTransaction(manager)
|
||||
.retrieveBySwap(swap.id)
|
||||
|
||||
// --> return_request_created
|
||||
// Phase 3: Create a cart that can be used to pay for the swap difference
|
||||
await swapService.createCart(order, swap._id)
|
||||
await returnService
|
||||
.withTransaction(manager)
|
||||
.fulfill(returnOrder.id)
|
||||
|
||||
// --> finished
|
||||
return {
|
||||
recovery_point: "swap_created",
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
order = await orderService.retrieve(id)
|
||||
const data = await orderService.decorate(
|
||||
order,
|
||||
[],
|
||||
["region", "customer", "swaps"]
|
||||
)
|
||||
if (error) {
|
||||
inProgress = false
|
||||
err = error
|
||||
} else {
|
||||
idempotencyKey = key
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
res.status(200).json({ order: data })
|
||||
} catch (err) {
|
||||
throw err
|
||||
case "swap_created": {
|
||||
const { key, error } = await idempotencyKeyService.workStage(
|
||||
idempotencyKey.idempotency_key,
|
||||
async manager => {
|
||||
const swaps = await swapService.list({
|
||||
idempotency_key: idempotencyKey.idempotency_key,
|
||||
})
|
||||
|
||||
if (!swaps.length) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
"Swap not found"
|
||||
)
|
||||
}
|
||||
|
||||
const order = await orderService.retrieve(id, {
|
||||
select: defaultFields,
|
||||
relations: defaultRelations,
|
||||
})
|
||||
|
||||
return {
|
||||
response_code: 200,
|
||||
response_body: { order },
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
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 (error) {
|
||||
throw error
|
||||
}
|
||||
}
|
||||
@@ -3,8 +3,12 @@ export default async (req, res) => {
|
||||
|
||||
try {
|
||||
const orderService = req.scope.resolve("orderService")
|
||||
let order = await orderService.deleteMetadata(id, key)
|
||||
order = await orderService.decorate(order, [], ["region"])
|
||||
|
||||
await orderService.deleteMetadata(id, key)
|
||||
|
||||
const order = await orderService.retrieve(id, {
|
||||
relations: ["region", "customer", "swaps"],
|
||||
})
|
||||
|
||||
res.status(200).json({ order })
|
||||
} catch (err) {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { MedusaError, Validator } from "medusa-core-utils"
|
||||
import { defaultRelations, defaultFields } from "./"
|
||||
|
||||
export default async (req, res) => {
|
||||
const { id, swap_id } = req.params
|
||||
@@ -15,21 +16,20 @@ export default async (req, res) => {
|
||||
try {
|
||||
const orderService = req.scope.resolve("orderService")
|
||||
const swapService = req.scope.resolve("swapService")
|
||||
const entityManager = req.scope.resolve("manager")
|
||||
|
||||
// Fetch the order
|
||||
const order = await orderService.retrieve(id)
|
||||
await entityManager.transaction(async manager => {
|
||||
await swapService
|
||||
.withTransaction(manager)
|
||||
.createFulfillment(swap_id, value.metadata)
|
||||
|
||||
// Receive the return
|
||||
await swapService.createFulfillment(order, swap_id, value.metadata)
|
||||
const order = await orderService.withTransaction(manager).retrieve(id, {
|
||||
select: defaultFields,
|
||||
relations: defaultRelations,
|
||||
})
|
||||
|
||||
// Decorate the order
|
||||
const data = await orderService.decorate(
|
||||
order,
|
||||
[],
|
||||
["region", "customer", "swaps"]
|
||||
)
|
||||
|
||||
res.status(200).json({ order: data })
|
||||
res.status(200).json({ order })
|
||||
})
|
||||
} catch (err) {
|
||||
throw err
|
||||
}
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
import { defaultRelations, defaultFields } from "./"
|
||||
|
||||
export default async (req, res) => {
|
||||
const { id } = req.params
|
||||
|
||||
try {
|
||||
const orderService = req.scope.resolve("orderService")
|
||||
const customerService = req.scope.resolve("customerService")
|
||||
|
||||
let order = await orderService.retrieve(id)
|
||||
order = await orderService.decorate(
|
||||
order,
|
||||
[],
|
||||
["region", "customer", "swaps"]
|
||||
)
|
||||
const order = await orderService.retrieve(id, {
|
||||
select: defaultFields,
|
||||
relations: defaultRelations,
|
||||
})
|
||||
|
||||
res.json({ order })
|
||||
} catch (error) {
|
||||
|
||||
@@ -87,6 +87,14 @@ export default app => {
|
||||
*/
|
||||
route.post("/:id/cancel", middlewares.wrap(require("./cancel-order").default))
|
||||
|
||||
/**
|
||||
* Add a shipping method
|
||||
*/
|
||||
route.post(
|
||||
"/:id/shipping-methods",
|
||||
middlewares.wrap(require("./add-shipping-method").default)
|
||||
)
|
||||
|
||||
/**
|
||||
* Archive an order.
|
||||
*/
|
||||
@@ -132,14 +140,6 @@ export default app => {
|
||||
middlewares.wrap(require("./process-swap-payment").default)
|
||||
)
|
||||
|
||||
/**
|
||||
* Set metadata key / value pair.
|
||||
*/
|
||||
route.post(
|
||||
"/:id/metadata",
|
||||
middlewares.wrap(require("./set-metadata").default)
|
||||
)
|
||||
|
||||
/**
|
||||
* Delete metadata key / value pair.
|
||||
*/
|
||||
@@ -150,3 +150,91 @@ export default app => {
|
||||
|
||||
return app
|
||||
}
|
||||
|
||||
export const defaultRelations = [
|
||||
"customer",
|
||||
"billing_address",
|
||||
"shipping_address",
|
||||
"discounts",
|
||||
"shipping_methods",
|
||||
"payments",
|
||||
"fulfillments",
|
||||
"returns",
|
||||
"gift_cards",
|
||||
"gift_card_transactions",
|
||||
"swaps",
|
||||
"swaps.return_order",
|
||||
"swaps.payment",
|
||||
"swaps.shipping_methods",
|
||||
"swaps.shipping_address",
|
||||
"swaps.additional_items",
|
||||
"swaps.fulfillments",
|
||||
]
|
||||
|
||||
export const defaultFields = [
|
||||
"id",
|
||||
"status",
|
||||
"fulfillment_status",
|
||||
"payment_status",
|
||||
"display_id",
|
||||
"cart_id",
|
||||
"customer_id",
|
||||
"email",
|
||||
"region_id",
|
||||
"currency_code",
|
||||
"tax_rate",
|
||||
"canceled_at",
|
||||
"created_at",
|
||||
"updated_at",
|
||||
"metadata",
|
||||
"items.refundable",
|
||||
"shipping_total",
|
||||
"discount_total",
|
||||
"tax_total",
|
||||
"refunded_total",
|
||||
"gift_card_total",
|
||||
"subtotal",
|
||||
"total",
|
||||
"refundable_amount",
|
||||
]
|
||||
|
||||
export const allowedFields = [
|
||||
"id",
|
||||
"status",
|
||||
"fulfillment_status",
|
||||
"payment_status",
|
||||
"display_id",
|
||||
"cart_id",
|
||||
"customer_id",
|
||||
"email",
|
||||
"region_id",
|
||||
"currency_code",
|
||||
"tax_rate",
|
||||
"canceled_at",
|
||||
"created_at",
|
||||
"updated_at",
|
||||
"metadata",
|
||||
"shipping_total",
|
||||
"discount_total",
|
||||
"tax_total",
|
||||
"refunded_total",
|
||||
"subtotal",
|
||||
"gift_card_total",
|
||||
"total",
|
||||
"refundable_amount",
|
||||
]
|
||||
|
||||
export const allowedRelations = [
|
||||
"customer",
|
||||
"region",
|
||||
"billing_address",
|
||||
"shipping_address",
|
||||
"discounts",
|
||||
"shipping_methods",
|
||||
"payments",
|
||||
"fulfillments",
|
||||
"returns",
|
||||
"swaps",
|
||||
"swaps.return_order",
|
||||
"swaps.additional_items",
|
||||
]
|
||||
@@ -1,80 +1,61 @@
|
||||
import _ from "lodash"
|
||||
import { Not } from "typeorm"
|
||||
import { defaultRelations, defaultFields } from "./"
|
||||
|
||||
export default async (req, res) => {
|
||||
try {
|
||||
const orderService = req.scope.resolve("orderService")
|
||||
const queryBuilderService = req.scope.resolve("queryBuilderService")
|
||||
|
||||
let query = queryBuilderService.buildQuery(req.query, [
|
||||
"display_id",
|
||||
"email",
|
||||
"status",
|
||||
"fulfillment_status",
|
||||
"payment_status",
|
||||
])
|
||||
|
||||
const limit = parseInt(req.query.limit) || 50
|
||||
const offset = parseInt(req.query.offset) || 0
|
||||
|
||||
let orders
|
||||
let selector = {}
|
||||
|
||||
// Temporary solution for admin tabs filtering
|
||||
// Will be replaced in Mongo -> Postgres migration
|
||||
if (req.query["status"]) {
|
||||
if (req.query["status"] === "returns") {
|
||||
query = {
|
||||
"returns.status": "requested",
|
||||
}
|
||||
}
|
||||
|
||||
if (req.query["status"] === "new") {
|
||||
query = {
|
||||
$or: [
|
||||
{ fulfillment_status: { $ne: "shipped" } },
|
||||
{ payment_status: { $ne: "captured" } },
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
if (req.query["status"] === "requires_action") {
|
||||
query = {
|
||||
$or: [
|
||||
{ status: "requires_action" },
|
||||
{ payment_status: "requires_action" },
|
||||
{ fulfillment_status: "requires_action" },
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
if (req.query["status"] === "swaps") {
|
||||
const swapService = req.scope.resolve("swapService")
|
||||
|
||||
const swapsInProgress = await swapService.list({
|
||||
fulfillment_status: { $ne: "shipped" },
|
||||
})
|
||||
|
||||
if (swapsInProgress.length) {
|
||||
query = {
|
||||
swaps: { $in: swapsInProgress.map(s => s._id.toString()) },
|
||||
}
|
||||
}
|
||||
}
|
||||
if ("q" in req.query) {
|
||||
selector.q = req.query.q
|
||||
}
|
||||
|
||||
orders = await orderService.list(query, offset, limit)
|
||||
|
||||
let includeFields = []
|
||||
if ("fields" in req.query) {
|
||||
includeFields = req.query.fields.split(",")
|
||||
}
|
||||
|
||||
orders = await Promise.all(
|
||||
orders.map(order => orderService.decorate(order, includeFields))
|
||||
let expandFields = []
|
||||
if ("expand" in req.query) {
|
||||
expandFields = req.query.expand.split(",")
|
||||
}
|
||||
|
||||
if ("new" in req.query) {
|
||||
selector = {
|
||||
payment_status: Not("captured"),
|
||||
fulfillment_status: Not("shipped"),
|
||||
}
|
||||
}
|
||||
|
||||
if ("requires_more" in req.query) {
|
||||
selector = {
|
||||
payment_status: Not("captured"),
|
||||
fulfillment_status: Not("shipped"),
|
||||
}
|
||||
}
|
||||
|
||||
const listConfig = {
|
||||
select: includeFields.length ? includeFields : defaultFields,
|
||||
relations: expandFields.length ? expandFields : defaultRelations,
|
||||
skip: offset,
|
||||
take: limit,
|
||||
order: { created_at: "DESC" },
|
||||
}
|
||||
|
||||
const [orders, count] = await orderService.listAndCount(
|
||||
selector,
|
||||
listConfig
|
||||
)
|
||||
|
||||
let numOrders = await orderService.count()
|
||||
const fields = [...includeFields, ...expandFields]
|
||||
const data = orders.map(o => _.pick(o, fields))
|
||||
|
||||
res.json({ orders, total_count: numOrders })
|
||||
res.json({ orders: data, count, offset, limit })
|
||||
} catch (error) {
|
||||
throw error
|
||||
}
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
import { defaultFields, defaultRelations } from "./"
|
||||
export default async (req, res) => {
|
||||
const { id, swap_id } = req.params
|
||||
|
||||
try {
|
||||
const orderService = req.scope.resolve("orderService")
|
||||
const swapService = req.scope.resolve("swapService")
|
||||
const entityManager = req.scope.resolve("manager")
|
||||
|
||||
const order = await orderService.retrieve(id)
|
||||
await entityManager.transaction(async manager => {
|
||||
await swapService.withTransaction(manager).processDifference(swap_id)
|
||||
|
||||
await swapService.processDifference(swap_id)
|
||||
const order = await orderService.withTransaction(manager).retrieve(id, {
|
||||
select: defaultFields,
|
||||
relations: defaultRelations,
|
||||
})
|
||||
|
||||
// Decorate the order
|
||||
const data = await orderService.decorate(
|
||||
order,
|
||||
[],
|
||||
["region", "customer", "swaps"]
|
||||
)
|
||||
|
||||
res.json({ order: data })
|
||||
res.json({ order })
|
||||
})
|
||||
} catch (error) {
|
||||
throw error
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { MedusaError, Validator } from "medusa-core-utils"
|
||||
import { defaultRelations, defaultFields } from "./"
|
||||
|
||||
export default async (req, res) => {
|
||||
const { id, return_id } = req.params
|
||||
@@ -10,7 +11,9 @@ export default async (req, res) => {
|
||||
quantity: Validator.number().required(),
|
||||
})
|
||||
.required(),
|
||||
refund: Validator.number().optional(),
|
||||
refund: Validator.number()
|
||||
.integer()
|
||||
.optional(),
|
||||
})
|
||||
|
||||
const { value, error } = schema.validate(req.body)
|
||||
@@ -22,9 +25,11 @@ export default async (req, res) => {
|
||||
const orderService = req.scope.resolve("orderService")
|
||||
|
||||
let refundAmount = value.refund
|
||||
|
||||
if (typeof value.refund !== "undefined" && value.refund < 0) {
|
||||
refundAmount = 0
|
||||
}
|
||||
|
||||
let order = await orderService.receiveReturn(
|
||||
id,
|
||||
return_id,
|
||||
@@ -32,11 +37,11 @@ export default async (req, res) => {
|
||||
refundAmount,
|
||||
true
|
||||
)
|
||||
order = await orderService.decorate(
|
||||
order,
|
||||
[],
|
||||
["region", "customer", "swaps"]
|
||||
)
|
||||
|
||||
order = await orderService.retrieve(id, {
|
||||
select: defaultFields,
|
||||
relations: defaultRelations,
|
||||
})
|
||||
|
||||
res.status(200).json({ order })
|
||||
} catch (err) {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { MedusaError, Validator } from "medusa-core-utils"
|
||||
import { defaultFields, defaultRelations } from "./"
|
||||
|
||||
export default async (req, res) => {
|
||||
const { id, swap_id } = req.params
|
||||
@@ -20,24 +21,24 @@ export default async (req, res) => {
|
||||
try {
|
||||
const orderService = req.scope.resolve("orderService")
|
||||
const swapService = req.scope.resolve("swapService")
|
||||
const entityManager = req.scope.resolve("manager")
|
||||
|
||||
// Fetch the order
|
||||
let order = await orderService.retrieve(id)
|
||||
await entityManager.transaction(async manager => {
|
||||
await swapService
|
||||
.withTransaction(manager)
|
||||
.receiveReturn(swap_id, value.items)
|
||||
|
||||
// Receive the return
|
||||
await swapService.receiveReturn(order, swap_id, value.items)
|
||||
await orderService
|
||||
.withTransaction(manager)
|
||||
.registerSwapReceived(id, swap_id)
|
||||
})
|
||||
|
||||
// Register swap reception
|
||||
order = await orderService.registerSwapReceived(id, swap_id)
|
||||
const order = await orderService.retrieve(id, {
|
||||
select: defaultFields,
|
||||
relations: defaultRelations,
|
||||
})
|
||||
|
||||
// Decorate the order
|
||||
const data = await orderService.decorate(
|
||||
order,
|
||||
[],
|
||||
["region", "customer", "swaps"]
|
||||
)
|
||||
|
||||
res.status(200).json({ order: data })
|
||||
res.status(200).json({ order })
|
||||
} catch (err) {
|
||||
throw err
|
||||
}
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
import { MedusaError, Validator } from "medusa-core-utils"
|
||||
import { defaultRelations, defaultFields } from "./"
|
||||
|
||||
export default async (req, res) => {
|
||||
const { id } = req.params
|
||||
const schema = Validator.object().keys({
|
||||
amount: Validator.number().required(),
|
||||
amount: Validator.number()
|
||||
.integer()
|
||||
.required(),
|
||||
reason: Validator.string().required(),
|
||||
note: Validator.string()
|
||||
.allow("")
|
||||
@@ -17,17 +20,13 @@ export default async (req, res) => {
|
||||
|
||||
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", "customer", "swaps"]
|
||||
)
|
||||
|
||||
await orderService.createRefund(id, value.amount, value.reason, value.note)
|
||||
|
||||
const order = await orderService.retrieve(id, {
|
||||
select: defaultFields,
|
||||
relations: defaultRelations,
|
||||
})
|
||||
|
||||
res.status(200).json({ order })
|
||||
} catch (err) {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { MedusaError, Validator } from "medusa-core-utils"
|
||||
import { defaultRelations, defaultFields } from "./"
|
||||
|
||||
export default async (req, res) => {
|
||||
const { id } = req.params
|
||||
@@ -10,10 +11,18 @@ export default async (req, res) => {
|
||||
quantity: Validator.number().required(),
|
||||
})
|
||||
.required(),
|
||||
shipping_method: Validator.string().optional(),
|
||||
shipping_price: Validator.number().optional(),
|
||||
return_shipping: Validator.object()
|
||||
.keys({
|
||||
option_id: Validator.string().optional(),
|
||||
price: Validator.number()
|
||||
.integer()
|
||||
.optional(),
|
||||
})
|
||||
.optional(),
|
||||
receive_now: Validator.boolean().default(false),
|
||||
refund: Validator.number().optional(),
|
||||
refund: Validator.number()
|
||||
.integer()
|
||||
.optional(),
|
||||
})
|
||||
|
||||
const { value, error } = schema.validate(req.body)
|
||||
@@ -21,59 +30,166 @@ export default async (req, res) => {
|
||||
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")
|
||||
|
||||
let oldOrder
|
||||
let existingReturns = []
|
||||
if (value.receive_now) {
|
||||
oldOrder = await orderService.retrieve(id)
|
||||
existingReturns = oldOrder.returns.map(r => r._id)
|
||||
}
|
||||
let inProgress = true
|
||||
let err = false
|
||||
|
||||
let shippingMethod
|
||||
if (value.shipping_method) {
|
||||
shippingMethod = {
|
||||
id: value.shipping_method,
|
||||
price: value.shipping_price,
|
||||
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(id, {
|
||||
select: ["refunded_total", "total"],
|
||||
relations: ["items"],
|
||||
})
|
||||
|
||||
const returnObj = {
|
||||
order_id: id,
|
||||
idempotency_key: idempotencyKey.idempotency_key,
|
||||
items: value.items,
|
||||
}
|
||||
|
||||
if (value.return_shipping) {
|
||||
returnObj.shipping_method = value.return_shipping
|
||||
}
|
||||
|
||||
if (typeof value.refund !== "undefined" && value.refund < 0) {
|
||||
returnObj.refund_amount = 0
|
||||
} else {
|
||||
if (value.refund) {
|
||||
returnObj.refund_amount = value.refund
|
||||
}
|
||||
}
|
||||
|
||||
const createdReturn = await returnService
|
||||
.withTransaction(manager)
|
||||
.create(returnObj, order)
|
||||
|
||||
if (value.return_shipping) {
|
||||
await returnService
|
||||
.withTransaction(manager)
|
||||
.fulfill(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(id, { relations: ["returns"] })
|
||||
|
||||
/**
|
||||
* If we are ready to receive immediately, we find the newly created return
|
||||
* and register it as received.
|
||||
*/
|
||||
if (value.receive_now) {
|
||||
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]
|
||||
|
||||
order = await returnService
|
||||
.withTransaction(manager)
|
||||
.receiveReturn(order.id, ret.id, value.items, value.refund)
|
||||
}
|
||||
|
||||
order = await orderService.withTransaction(manager).retrieve(id, {
|
||||
select: defaultFields,
|
||||
relations: defaultRelations,
|
||||
})
|
||||
|
||||
return {
|
||||
response_code: 200,
|
||||
response_body: { order },
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
let refundAmount = value.refund
|
||||
if (typeof value.refund !== "undefined" && value.refund < 0) {
|
||||
refundAmount = 0
|
||||
}
|
||||
let order = await orderService.requestReturn(
|
||||
id,
|
||||
value.items,
|
||||
shippingMethod,
|
||||
refundAmount
|
||||
)
|
||||
|
||||
/**
|
||||
* If we are ready to receive immediately, we find the newly created return
|
||||
* and register it as received.
|
||||
*/
|
||||
if (value.receive_now) {
|
||||
const newReturn = order.returns.find(
|
||||
r => !existingReturns.includes(r._id)
|
||||
)
|
||||
order = await orderService.return(
|
||||
id,
|
||||
newReturn._id,
|
||||
value.items,
|
||||
value.refund
|
||||
)
|
||||
if (err) {
|
||||
throw err
|
||||
}
|
||||
|
||||
order = await orderService.decorate(
|
||||
order,
|
||||
[],
|
||||
["region", "customer", "swaps"]
|
||||
)
|
||||
|
||||
res.status(200).json({ order })
|
||||
res.status(idempotencyKey.response_code).json(idempotencyKey.response_body)
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
throw err
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,13 @@
|
||||
import { MedusaError, Validator } from "medusa-core-utils"
|
||||
import { defaultFields, defaultRelations } from "."
|
||||
|
||||
export default async (req, res) => {
|
||||
const { id } = req.params
|
||||
|
||||
const schema = Validator.object().keys({
|
||||
email: Validator.string().email(),
|
||||
billing_address: Validator.address(),
|
||||
shipping_address: Validator.address(),
|
||||
billing_address: Validator.object(),
|
||||
shipping_address: Validator.object(),
|
||||
items: Validator.array(),
|
||||
region: Validator.string(),
|
||||
discounts: Validator.array(),
|
||||
@@ -31,12 +32,13 @@ export default async (req, res) => {
|
||||
|
||||
try {
|
||||
const orderService = req.scope.resolve("orderService")
|
||||
let order = await orderService.update(id, value)
|
||||
order = await orderService.decorate(
|
||||
order,
|
||||
[],
|
||||
["region", "customer", "swaps"]
|
||||
)
|
||||
|
||||
await orderService.update(id, value)
|
||||
|
||||
const order = await orderService.retrieve(id, {
|
||||
select: defaultFields,
|
||||
relations: defaultRelations,
|
||||
})
|
||||
|
||||
res.status(200).json({ order })
|
||||
} catch (err) {
|
||||
|
||||
@@ -36,10 +36,7 @@ describe("POST /admin/products/:id/options", () => {
|
||||
})
|
||||
|
||||
it("returns the updated product decorated", () => {
|
||||
expect(subject.body.product._id).toEqual(
|
||||
IdMap.getId("productWithOptions")
|
||||
)
|
||||
expect(subject.body.product.decorated).toEqual(true)
|
||||
expect(subject.body.product.id).toEqual(IdMap.getId("productWithOptions"))
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -28,18 +28,18 @@ describe("POST /admin/products", () => {
|
||||
})
|
||||
|
||||
it("returns created product draft", () => {
|
||||
expect(subject.body.product._id).toEqual(IdMap.getId("product1"))
|
||||
expect(subject.body.product.decorated).toEqual(true)
|
||||
expect(subject.body.product.id).toEqual(IdMap.getId("product1"))
|
||||
})
|
||||
|
||||
it("calls service createDraft", () => {
|
||||
expect(ProductServiceMock.createDraft).toHaveBeenCalledTimes(1)
|
||||
expect(ProductServiceMock.createDraft).toHaveBeenCalledWith({
|
||||
expect(ProductServiceMock.create).toHaveBeenCalledTimes(1)
|
||||
expect(ProductServiceMock.create).toHaveBeenCalledWith({
|
||||
title: "Test Product",
|
||||
description: "Test Description",
|
||||
tags: "hi,med,dig",
|
||||
handle: "test-product",
|
||||
is_giftcard: false,
|
||||
profile_id: IdMap.getId("default_shipping_profile"),
|
||||
})
|
||||
})
|
||||
|
||||
@@ -89,13 +89,14 @@ describe("POST /admin/products", () => {
|
||||
})
|
||||
|
||||
it("calls service createDraft", () => {
|
||||
expect(ProductServiceMock.createDraft).toHaveBeenCalledTimes(1)
|
||||
expect(ProductServiceMock.createDraft).toHaveBeenCalledWith({
|
||||
expect(ProductServiceMock.create).toHaveBeenCalledTimes(1)
|
||||
expect(ProductServiceMock.create).toHaveBeenCalledWith({
|
||||
title: "Gift Card",
|
||||
description: "make someone happy",
|
||||
options: [{ title: "Denominations" }],
|
||||
handle: "test-gift-card",
|
||||
is_giftcard: true,
|
||||
profile_id: IdMap.getId("giftCardProfile"),
|
||||
})
|
||||
})
|
||||
|
||||
@@ -106,12 +107,6 @@ describe("POST /admin/products", () => {
|
||||
expect(
|
||||
ShippingProfileServiceMock.retrieveGiftCardDefault
|
||||
).toHaveBeenCalledWith()
|
||||
|
||||
expect(ShippingProfileServiceMock.addProduct).toHaveBeenCalledTimes(1)
|
||||
expect(ShippingProfileServiceMock.addProduct).toHaveBeenCalledWith(
|
||||
IdMap.getId("giftCardProfile"),
|
||||
undefined
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { IdMap } from "medusa-test-utils"
|
||||
import { request } from "../../../../../helpers/test-request"
|
||||
import { ProductServiceMock } from "../../../../../services/__mocks__/product"
|
||||
import { ProductVariantServiceMock } from "../../../../../services/__mocks__/product-variant"
|
||||
|
||||
describe("POST /admin/products/:id/variants", () => {
|
||||
describe("successful add variant", () => {
|
||||
@@ -34,10 +34,11 @@ describe("POST /admin/products/:id/variants", () => {
|
||||
})
|
||||
|
||||
it("calls service addVariant", () => {
|
||||
expect(ProductServiceMock.createVariant).toHaveBeenCalledTimes(1)
|
||||
expect(ProductServiceMock.createVariant).toHaveBeenCalledWith(
|
||||
expect(ProductVariantServiceMock.create).toHaveBeenCalledTimes(1)
|
||||
expect(ProductVariantServiceMock.create).toHaveBeenCalledWith(
|
||||
IdMap.getId("productWithOptions"),
|
||||
{
|
||||
inventory_quantity: 0,
|
||||
title: "Test Product Variant",
|
||||
options: [],
|
||||
prices: [
|
||||
@@ -51,10 +52,7 @@ describe("POST /admin/products/:id/variants", () => {
|
||||
})
|
||||
|
||||
it("returns the updated product decorated", () => {
|
||||
expect(subject.body.product._id).toEqual(
|
||||
IdMap.getId("productWithOptions")
|
||||
)
|
||||
expect(subject.body.product.decorated).toEqual(true)
|
||||
expect(subject.body.product.id).toEqual(IdMap.getId("productWithOptions"))
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,6 +1,6 @@
|
||||
import { IdMap } from "medusa-test-utils"
|
||||
import { request } from "../../../../../helpers/test-request"
|
||||
import { ProductServiceMock } from "../../../../../services/__mocks__/product"
|
||||
import { ProductVariantServiceMock } from "../../../../../services/__mocks__/product-variant"
|
||||
|
||||
describe("POST /admin/products/:id/variants/:variantId", () => {
|
||||
describe("successful removes variant", () => {
|
||||
@@ -27,9 +27,8 @@ describe("POST /admin/products/:id/variants/:variantId", () => {
|
||||
})
|
||||
|
||||
it("calls service removeVariant", () => {
|
||||
expect(ProductServiceMock.deleteVariant).toHaveBeenCalledTimes(1)
|
||||
expect(ProductServiceMock.deleteVariant).toHaveBeenCalledWith(
|
||||
IdMap.getId("productWithOptions"),
|
||||
expect(ProductVariantServiceMock.delete).toHaveBeenCalledTimes(1)
|
||||
expect(ProductVariantServiceMock.delete).toHaveBeenCalledWith(
|
||||
IdMap.getId("variant1")
|
||||
)
|
||||
})
|
||||
@@ -39,19 +38,7 @@ describe("POST /admin/products/:id/variants/:variantId", () => {
|
||||
variant_id: IdMap.getId("variant1"),
|
||||
object: "product-variant",
|
||||
deleted: true,
|
||||
product: {
|
||||
_id: IdMap.getId("productWithOptions"),
|
||||
decorated: true,
|
||||
options: [
|
||||
{
|
||||
_id: IdMap.getId("option1"),
|
||||
title: "Test",
|
||||
values: [IdMap.getId("optionValue1")],
|
||||
},
|
||||
],
|
||||
title: "Test",
|
||||
variants: [IdMap.getId("variant1")],
|
||||
},
|
||||
product: expect.any(Object),
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -27,13 +27,43 @@ describe("GET /admin/products/:id", () => {
|
||||
it("calls get product from productSerice", () => {
|
||||
expect(ProductServiceMock.retrieve).toHaveBeenCalledTimes(1)
|
||||
expect(ProductServiceMock.retrieve).toHaveBeenCalledWith(
|
||||
IdMap.getId("product1")
|
||||
IdMap.getId("product1"),
|
||||
{
|
||||
select: [
|
||||
"id",
|
||||
"title",
|
||||
"subtitle",
|
||||
"description",
|
||||
"tags",
|
||||
"handle",
|
||||
"is_giftcard",
|
||||
"thumbnail",
|
||||
"profile_id",
|
||||
"weight",
|
||||
"length",
|
||||
"height",
|
||||
"width",
|
||||
"hs_code",
|
||||
"origin_country",
|
||||
"mid_code",
|
||||
"material",
|
||||
"created_at",
|
||||
"updated_at",
|
||||
"metadata",
|
||||
],
|
||||
relations: [
|
||||
"variants",
|
||||
"variants.prices",
|
||||
"variants.options",
|
||||
"images",
|
||||
"options",
|
||||
],
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
it("returns product decorated", () => {
|
||||
expect(subject.body.product._id).toEqual(IdMap.getId("product1"))
|
||||
expect(subject.body.product.decorated).toEqual(true)
|
||||
expect(subject.body.product.id).toEqual(IdMap.getId("product1"))
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,39 +0,0 @@
|
||||
import { IdMap } from "medusa-test-utils"
|
||||
import { request } from "../../../../../helpers/test-request"
|
||||
import { ProductServiceMock } from "../../../../../services/__mocks__/product"
|
||||
|
||||
describe("GET /admin/products/:id/variants", () => {
|
||||
describe("successfully gets a product", () => {
|
||||
let subject
|
||||
|
||||
beforeAll(async () => {
|
||||
subject = await request(
|
||||
"GET",
|
||||
`/admin/products/${IdMap.getId("product1")}/variants`,
|
||||
{
|
||||
adminSession: {
|
||||
jwt: {
|
||||
userId: IdMap.getId("admin_user"),
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
afterAll(() => {
|
||||
jest.clearAllMocks()
|
||||
})
|
||||
|
||||
it("calls get product from productSerice", () => {
|
||||
expect(ProductServiceMock.retrieveVariants).toHaveBeenCalledTimes(1)
|
||||
expect(ProductServiceMock.retrieveVariants).toHaveBeenCalledWith(
|
||||
IdMap.getId("product1")
|
||||
)
|
||||
})
|
||||
|
||||
it("returns variants", () => {
|
||||
expect(subject.body.variants[0]._id).toEqual(IdMap.getId("1"))
|
||||
expect(subject.body.variants[1]._id).toEqual(IdMap.getId("2"))
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,38 +0,0 @@
|
||||
import { IdMap } from "medusa-test-utils"
|
||||
import { request } from "../../../../../helpers/test-request"
|
||||
import { ProductServiceMock } from "../../../../../services/__mocks__/product"
|
||||
|
||||
describe("POST /admin/products/:id/publish", () => {
|
||||
describe("successful publish", () => {
|
||||
let subject
|
||||
|
||||
beforeAll(async () => {
|
||||
subject = await request(
|
||||
"POST",
|
||||
`/admin/products/${IdMap.getId("publish")}/publish`,
|
||||
{
|
||||
adminSession: {
|
||||
jwt: {
|
||||
userId: IdMap.getId("admin_user"),
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
it("returns 200", () => {
|
||||
expect(subject.status).toEqual(200)
|
||||
})
|
||||
|
||||
it("returns product with published flag true", () => {
|
||||
expect(subject.body.product.published).toEqual(true)
|
||||
})
|
||||
|
||||
it("calls service publish", () => {
|
||||
expect(ProductServiceMock.publish).toHaveBeenCalledTimes(1)
|
||||
expect(ProductServiceMock.publish).toHaveBeenCalledWith(
|
||||
IdMap.getId("publish")
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,6 +1,5 @@
|
||||
import { IdMap } from "medusa-test-utils"
|
||||
import { request } from "../../../../../helpers/test-request"
|
||||
import { ProductServiceMock } from "../../../../../services/__mocks__/product"
|
||||
import { ProductVariantServiceMock } from "../../../../../services/__mocks__/product-variant"
|
||||
|
||||
describe("POST /admin/products/:id/variants/:variantId", () => {
|
||||
@@ -41,140 +40,28 @@ describe("POST /admin/products/:id/variants/:variantId", () => {
|
||||
expect(subject.status).toEqual(200)
|
||||
})
|
||||
|
||||
it("calls service removeVariant", () => {
|
||||
expect(ProductVariantServiceMock.setCurrencyPrice).toHaveBeenCalledTimes(
|
||||
1
|
||||
)
|
||||
expect(ProductVariantServiceMock.setCurrencyPrice).toHaveBeenCalledWith(
|
||||
IdMap.getId("variant1"),
|
||||
"DKK",
|
||||
100,
|
||||
undefined
|
||||
)
|
||||
|
||||
expect(ProductVariantServiceMock.setRegionPrice).toHaveBeenCalledTimes(1)
|
||||
expect(ProductVariantServiceMock.setRegionPrice).toHaveBeenCalledWith(
|
||||
IdMap.getId("variant1"),
|
||||
IdMap.getId("region-fr"),
|
||||
100,
|
||||
undefined
|
||||
)
|
||||
})
|
||||
|
||||
it("filters prices", () => {
|
||||
expect(ProductVariantServiceMock.update).toHaveBeenCalledTimes(1)
|
||||
expect(ProductVariantServiceMock.update).toHaveBeenCalledWith(
|
||||
IdMap.getId("variant1"),
|
||||
{
|
||||
title: "hi",
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
it("returns decorated product with variant removed", () => {
|
||||
expect(subject.body.product._id).toEqual(
|
||||
IdMap.getId("productWithOptions")
|
||||
)
|
||||
expect(subject.body.product.decorated).toEqual(true)
|
||||
})
|
||||
})
|
||||
|
||||
describe("successful updates options", () => {
|
||||
let subject
|
||||
|
||||
beforeAll(async () => {
|
||||
jest.clearAllMocks()
|
||||
subject = await request(
|
||||
"POST",
|
||||
`/admin/products/${IdMap.getId(
|
||||
"productWithOptions"
|
||||
)}/variants/${IdMap.getId("variant1")}`,
|
||||
{
|
||||
payload: {
|
||||
options: [
|
||||
{
|
||||
option_id: IdMap.getId("option_id"),
|
||||
value: 100,
|
||||
},
|
||||
],
|
||||
},
|
||||
adminSession: {
|
||||
jwt: {
|
||||
userId: IdMap.getId("admin_user"),
|
||||
prices: [
|
||||
{
|
||||
region_id: IdMap.getId("region-fr"),
|
||||
amount: 100,
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
it("returns 200", () => {
|
||||
expect(subject.status).toEqual(200)
|
||||
})
|
||||
|
||||
it("calls service removeVariant", () => {
|
||||
expect(ProductServiceMock.updateOptionValue).toHaveBeenCalledTimes(1)
|
||||
expect(ProductServiceMock.updateOptionValue).toHaveBeenCalledWith(
|
||||
IdMap.getId("productWithOptions"),
|
||||
IdMap.getId("variant1"),
|
||||
IdMap.getId("option_id"),
|
||||
100
|
||||
)
|
||||
})
|
||||
|
||||
it("returns decorated product with variant removed", () => {
|
||||
expect(subject.body.product._id).toEqual(
|
||||
IdMap.getId("productWithOptions")
|
||||
)
|
||||
expect(subject.body.product.decorated).toEqual(true)
|
||||
})
|
||||
})
|
||||
|
||||
describe("successful updates variant", () => {
|
||||
let subject
|
||||
|
||||
beforeAll(async () => {
|
||||
jest.clearAllMocks()
|
||||
subject = await request(
|
||||
"POST",
|
||||
`/admin/products/${IdMap.getId(
|
||||
"productWithOptions"
|
||||
)}/variants/${IdMap.getId("variant1")}`,
|
||||
{
|
||||
payload: {
|
||||
title: "hi",
|
||||
inventory_quantity: 123,
|
||||
allow_backorder: true,
|
||||
},
|
||||
adminSession: {
|
||||
jwt: {
|
||||
userId: IdMap.getId("admin_user"),
|
||||
{
|
||||
currency_code: "DKK",
|
||||
amount: 100,
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
it("returns 200", () => {
|
||||
expect(subject.status).toEqual(200)
|
||||
})
|
||||
|
||||
it("calls variant update", () => {
|
||||
expect(ProductVariantServiceMock.update).toHaveBeenCalledTimes(1)
|
||||
expect(ProductVariantServiceMock.update).toHaveBeenCalledWith(
|
||||
IdMap.getId("variant1"),
|
||||
{
|
||||
title: "hi",
|
||||
inventory_quantity: 123,
|
||||
allow_backorder: true,
|
||||
],
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
it("returns decorated product with variant removed", () => {
|
||||
expect(subject.body.product._id).toEqual(
|
||||
IdMap.getId("productWithOptions")
|
||||
)
|
||||
expect(subject.body.product.decorated).toEqual(true)
|
||||
expect(subject.body.product.id).toEqual(IdMap.getId("productWithOptions"))
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,4 +1,5 @@
|
||||
import { MedusaError, Validator } from "medusa-core-utils"
|
||||
import { defaultRelations, defaultFields } from "./"
|
||||
|
||||
export default async (req, res) => {
|
||||
const { id } = req.params
|
||||
@@ -13,23 +14,14 @@ export default async (req, res) => {
|
||||
|
||||
try {
|
||||
const productService = req.scope.resolve("productService")
|
||||
const newProduct = await productService.addOption(id, value.title)
|
||||
|
||||
const data = await productService.decorate(
|
||||
newProduct,
|
||||
[
|
||||
"title",
|
||||
"description",
|
||||
"tags",
|
||||
"handle",
|
||||
"images",
|
||||
"thumbnail",
|
||||
"options",
|
||||
"published",
|
||||
],
|
||||
["variants"]
|
||||
)
|
||||
res.json({ product: data })
|
||||
await productService.addOption(id, value.title)
|
||||
const product = await productService.retrieve(id, {
|
||||
select: defaultFields,
|
||||
relations: defaultRelations,
|
||||
})
|
||||
|
||||
res.json({ product })
|
||||
} catch (err) {
|
||||
throw err
|
||||
}
|
||||
|
||||
@@ -1,40 +1,85 @@
|
||||
import { MedusaError, Validator } from "medusa-core-utils"
|
||||
import { defaultRelations, defaultFields } from "."
|
||||
|
||||
export default async (req, res) => {
|
||||
const schema = Validator.object().keys({
|
||||
title: Validator.string().required(),
|
||||
subtitle: Validator.string().allow(""),
|
||||
description: Validator.string().allow(""),
|
||||
tags: Validator.string(),
|
||||
tags: Validator.string().optional(),
|
||||
is_giftcard: Validator.boolean().default(false),
|
||||
images: Validator.array()
|
||||
.items(Validator.string())
|
||||
.optional(),
|
||||
thumbnail: Validator.string().optional(),
|
||||
handle: Validator.string().optional(),
|
||||
options: Validator.array().items({
|
||||
title: Validator.string().required(),
|
||||
}),
|
||||
images: Validator.array().items(Validator.string()),
|
||||
thumbnail: Validator.string().optional(),
|
||||
variants: Validator.array().items({
|
||||
title: Validator.string().required(),
|
||||
sku: Validator.string(),
|
||||
ean: Validator.string(),
|
||||
barcode: Validator.string(),
|
||||
sku: Validator.string().allow(null),
|
||||
ean: Validator.string().allow(null),
|
||||
upc: Validator.string().allow(null),
|
||||
barcode: Validator.string().allow(null),
|
||||
hs_code: Validator.string().allow(null),
|
||||
inventory_quantity: Validator.number().default(0),
|
||||
allow_backorder: Validator.boolean().optional(),
|
||||
manage_inventory: Validator.boolean().optional(),
|
||||
weight: Validator.number().optional(),
|
||||
length: Validator.number().optional(),
|
||||
height: Validator.number().optional(),
|
||||
width: Validator.number().optional(),
|
||||
origin_country: Validator.string()
|
||||
.optional()
|
||||
.allow("")
|
||||
.allow(null),
|
||||
mid_code: Validator.string()
|
||||
.optional()
|
||||
.allow("")
|
||||
.allow(null),
|
||||
material: Validator.string()
|
||||
.optional()
|
||||
.allow("")
|
||||
.allow(null),
|
||||
metadata: Validator.object().optional(),
|
||||
prices: Validator.array()
|
||||
.items({
|
||||
currency_code: Validator.string().required(),
|
||||
amount: Validator.number().required(),
|
||||
sale_amount: Validator.number().optional(),
|
||||
})
|
||||
.items(
|
||||
Validator.object()
|
||||
.keys({
|
||||
region_id: Validator.string(),
|
||||
currency_code: Validator.string().required(),
|
||||
amount: Validator.number()
|
||||
.integer()
|
||||
.required(),
|
||||
sale_amount: Validator.number().optional(),
|
||||
})
|
||||
.xor("region_id", "currency_code")
|
||||
)
|
||||
.required(),
|
||||
options: Validator.array()
|
||||
.items({
|
||||
value: Validator.string().required(),
|
||||
})
|
||||
.default([]),
|
||||
inventory_quantity: Validator.number().optional(),
|
||||
allow_backorder: Validator.boolean().optional(),
|
||||
manage_inventory: Validator.boolean().optional(),
|
||||
metadata: Validator.object().optional(),
|
||||
}),
|
||||
metadata: Validator.object(),
|
||||
handle: Validator.string(),
|
||||
weight: Validator.number().optional(),
|
||||
length: Validator.number().optional(),
|
||||
height: Validator.number().optional(),
|
||||
width: Validator.number().optional(),
|
||||
hs_code: Validator.string()
|
||||
.optional()
|
||||
.allow(""),
|
||||
origin_country: Validator.string()
|
||||
.optional()
|
||||
.allow(""),
|
||||
mid_code: Validator.string()
|
||||
.optional()
|
||||
.allow(""),
|
||||
material: Validator.string()
|
||||
.optional()
|
||||
.allow(""),
|
||||
metadata: Validator.object().optional(),
|
||||
})
|
||||
|
||||
const { value, error } = schema.validate(req.body)
|
||||
@@ -43,62 +88,62 @@ export default async (req, res) => {
|
||||
}
|
||||
|
||||
try {
|
||||
const { variants } = value
|
||||
delete value.variants
|
||||
|
||||
const productService = req.scope.resolve("productService")
|
||||
const productVariantService = req.scope.resolve("productVariantService")
|
||||
const shippingProfileService = req.scope.resolve("shippingProfileService")
|
||||
|
||||
if (!value.thumbnail && value.images && value.images.length) {
|
||||
value.thumbnail = value.images[0]
|
||||
}
|
||||
let newProduct = await productService.createDraft(value)
|
||||
const entityManager = req.scope.resolve("manager")
|
||||
|
||||
if (variants) {
|
||||
const optionIds = value.options.map(
|
||||
o => newProduct.options.find(newO => newO.title === o.title)._id
|
||||
)
|
||||
let newProduct
|
||||
await entityManager.transaction(async manager => {
|
||||
const { variants } = value
|
||||
delete value.variants
|
||||
|
||||
await Promise.all(
|
||||
variants.map(v => {
|
||||
const variant = {
|
||||
...v,
|
||||
options: v.options.map((o, index) => ({
|
||||
...o,
|
||||
option_id: optionIds[index],
|
||||
})),
|
||||
}
|
||||
return productService.createVariant(newProduct._id, variant)
|
||||
})
|
||||
)
|
||||
}
|
||||
if (!value.thumbnail && value.images && value.images.length) {
|
||||
value.thumbnail = value.images[0]
|
||||
}
|
||||
|
||||
// Add to default shipping profile
|
||||
if (value.is_giftcard) {
|
||||
const { _id } = await shippingProfileService.retrieveGiftCardDefault()
|
||||
await shippingProfileService.addProduct(_id, newProduct._id)
|
||||
} else {
|
||||
const { _id } = await shippingProfileService.retrieveDefault()
|
||||
await shippingProfileService.addProduct(_id, newProduct._id)
|
||||
}
|
||||
let shippingProfile
|
||||
// Get default shipping profile
|
||||
if (value.is_giftcard) {
|
||||
shippingProfile = await shippingProfileService.retrieveGiftCardDefault()
|
||||
} else {
|
||||
shippingProfile = await shippingProfileService.retrieveDefault()
|
||||
}
|
||||
|
||||
newProduct = await productService.decorate(
|
||||
newProduct,
|
||||
[
|
||||
"title",
|
||||
"description",
|
||||
"tags",
|
||||
"handle",
|
||||
"images",
|
||||
"thumbnail",
|
||||
"options",
|
||||
"published",
|
||||
],
|
||||
["variants"]
|
||||
)
|
||||
res.json({ product: newProduct })
|
||||
newProduct = await productService
|
||||
.withTransaction(manager)
|
||||
.create({ ...value, profile_id: shippingProfile.id })
|
||||
|
||||
if (variants) {
|
||||
const optionIds = value.options.map(
|
||||
o => newProduct.options.find(newO => newO.title === o.title).id
|
||||
)
|
||||
|
||||
await Promise.all(
|
||||
variants.map(async v => {
|
||||
const variant = {
|
||||
...v,
|
||||
options: v.options.map((o, index) => ({
|
||||
...o,
|
||||
option_id: optionIds[index],
|
||||
})),
|
||||
}
|
||||
await productVariantService
|
||||
.withTransaction(manager)
|
||||
.create(newProduct.id, variant)
|
||||
})
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
const product = await productService.retrieve(newProduct.id, {
|
||||
select: defaultFields,
|
||||
relations: defaultRelations,
|
||||
})
|
||||
|
||||
res.json({ product })
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
throw err
|
||||
}
|
||||
}
|
||||
@@ -1,29 +1,47 @@
|
||||
import { MedusaError, Validator } from "medusa-core-utils"
|
||||
import { defaultFields, defaultRelations } from "./"
|
||||
|
||||
export default async (req, res) => {
|
||||
const { id } = req.params
|
||||
|
||||
const schema = Validator.object().keys({
|
||||
title: Validator.string().required(),
|
||||
sku: Validator.string().optional(),
|
||||
ean: Validator.string().optional(),
|
||||
sku: Validator.string().allow(""),
|
||||
ean: Validator.string().allow(""),
|
||||
upc: Validator.string().allow(""),
|
||||
barcode: Validator.string().allow(""),
|
||||
hs_code: Validator.string().allow(""),
|
||||
inventory_quantity: Validator.number().default(0),
|
||||
allow_backorder: Validator.boolean().optional(),
|
||||
manage_inventory: Validator.boolean().optional(),
|
||||
weight: Validator.number().optional(),
|
||||
length: Validator.number().optional(),
|
||||
height: Validator.number().optional(),
|
||||
width: Validator.number().optional(),
|
||||
origin_country: Validator.string().allow(""),
|
||||
mid_code: Validator.string().allow(""),
|
||||
material: Validator.string().allow(""),
|
||||
metadata: Validator.object().optional(),
|
||||
prices: Validator.array()
|
||||
.items({
|
||||
currency_code: Validator.string().required(),
|
||||
amount: Validator.number().required(),
|
||||
sale_amount: Validator.number().optional(),
|
||||
})
|
||||
.items(
|
||||
Validator.object()
|
||||
.keys({
|
||||
region_id: Validator.string(),
|
||||
currency_code: Validator.string().required(),
|
||||
amount: Validator.number()
|
||||
.integer()
|
||||
.required(),
|
||||
sale_amount: Validator.number().optional(),
|
||||
})
|
||||
.xor("region_id", "currency_code")
|
||||
)
|
||||
.required(),
|
||||
options: Validator.array()
|
||||
.items({
|
||||
option_id: Validator.objectId().required(),
|
||||
option_id: Validator.string().required(),
|
||||
value: Validator.string().required(),
|
||||
})
|
||||
.default([]),
|
||||
image: Validator.string().optional(),
|
||||
inventory_quantity: Validator.number().optional(),
|
||||
allow_backorder: Validator.boolean().optional(),
|
||||
manage_inventory: Validator.boolean().optional(),
|
||||
metadata: Validator.object().optional(),
|
||||
})
|
||||
|
||||
const { value, error } = schema.validate(req.body)
|
||||
@@ -32,24 +50,17 @@ export default async (req, res) => {
|
||||
}
|
||||
|
||||
try {
|
||||
const productVariantService = req.scope.resolve("productVariantService")
|
||||
const productService = req.scope.resolve("productService")
|
||||
const product = await productService.createVariant(id, value)
|
||||
const data = await productService.decorate(
|
||||
product,
|
||||
[
|
||||
"title",
|
||||
"description",
|
||||
"is_giftcard",
|
||||
"tags",
|
||||
"thumbnail",
|
||||
"handle",
|
||||
"images",
|
||||
"options",
|
||||
"published",
|
||||
],
|
||||
["variants"]
|
||||
)
|
||||
res.json({ product: data })
|
||||
|
||||
await productVariantService.create(id, value)
|
||||
|
||||
const product = await productService.retrieve(id, {
|
||||
select: defaultFields,
|
||||
relations: defaultRelations,
|
||||
})
|
||||
|
||||
res.json({ product })
|
||||
} catch (err) {
|
||||
throw err
|
||||
}
|
||||
|
||||
@@ -1,23 +1,15 @@
|
||||
import { defaultRelations, defaultFields } from "."
|
||||
|
||||
export default async (req, res) => {
|
||||
const { id, option_id } = req.params
|
||||
|
||||
try {
|
||||
const productService = req.scope.resolve("productService")
|
||||
const product = await productService.deleteOption(id, option_id)
|
||||
const data = await productService.decorate(
|
||||
product,
|
||||
[
|
||||
"title",
|
||||
"description",
|
||||
"tags",
|
||||
"handle",
|
||||
"thumbnail",
|
||||
"images",
|
||||
"options",
|
||||
"published",
|
||||
],
|
||||
["variants"]
|
||||
)
|
||||
await productService.deleteOption(id, option_id)
|
||||
const data = await productService.retrieve(id, {
|
||||
select: defaultFields,
|
||||
relations: defaultRelations,
|
||||
})
|
||||
|
||||
res.json({
|
||||
option_id,
|
||||
|
||||
@@ -1,23 +1,17 @@
|
||||
import { defaultRelations, defaultFields } from "."
|
||||
export default async (req, res) => {
|
||||
const { id, variant_id } = req.params
|
||||
|
||||
try {
|
||||
const productVariantService = req.scope.resolve("productVariantService")
|
||||
const productService = req.scope.resolve("productService")
|
||||
const product = await productService.deleteVariant(id, variant_id)
|
||||
const data = await productService.decorate(
|
||||
product,
|
||||
[
|
||||
"title",
|
||||
"description",
|
||||
"tags",
|
||||
"handle",
|
||||
"images",
|
||||
"thumbnail",
|
||||
"options",
|
||||
"published",
|
||||
],
|
||||
["variants"]
|
||||
)
|
||||
|
||||
await productVariantService.delete(variant_id)
|
||||
|
||||
const data = await productService.retrieve(id, {
|
||||
select: defaultFields,
|
||||
relations: defaultRelations,
|
||||
})
|
||||
|
||||
res.json({
|
||||
variant_id,
|
||||
|
||||
@@ -1,24 +1,14 @@
|
||||
import { defaultFields, defaultRelations } from "./"
|
||||
|
||||
export default async (req, res) => {
|
||||
const { id } = req.params
|
||||
|
||||
const productService = req.scope.resolve("productService")
|
||||
let product = await productService.retrieve(id)
|
||||
|
||||
product = await productService.decorate(
|
||||
product,
|
||||
[
|
||||
"title",
|
||||
"description",
|
||||
"is_giftcard",
|
||||
"tags",
|
||||
"thumbnail",
|
||||
"handle",
|
||||
"images",
|
||||
"options",
|
||||
"published",
|
||||
],
|
||||
["variants"]
|
||||
)
|
||||
const product = await productService.retrieve(id, {
|
||||
select: defaultFields,
|
||||
relations: defaultRelations,
|
||||
})
|
||||
|
||||
res.json({ product })
|
||||
}
|
||||
@@ -8,21 +8,12 @@ export default app => {
|
||||
|
||||
route.post("/", middlewares.wrap(require("./create-product").default))
|
||||
route.post("/:id", middlewares.wrap(require("./update-product").default))
|
||||
route.post(
|
||||
"/:id/publish",
|
||||
middlewares.wrap(require("./publish-product").default)
|
||||
)
|
||||
|
||||
route.post(
|
||||
"/:id/variants",
|
||||
middlewares.wrap(require("./create-variant").default)
|
||||
)
|
||||
|
||||
route.get(
|
||||
"/:id/variants",
|
||||
middlewares.wrap(require("./get-variants").default)
|
||||
)
|
||||
|
||||
route.post(
|
||||
"/:id/variants/:variant_id",
|
||||
middlewares.wrap(require("./update-variant").default)
|
||||
@@ -44,8 +35,74 @@ export default app => {
|
||||
middlewares.wrap(require("./delete-option").default)
|
||||
)
|
||||
|
||||
route.post(
|
||||
"/:id/metadata",
|
||||
middlewares.wrap(require("./set-metadata").default)
|
||||
)
|
||||
|
||||
route.get("/:id", middlewares.wrap(require("./get-product").default))
|
||||
route.get("/", middlewares.wrap(require("./list-products").default))
|
||||
|
||||
return app
|
||||
}
|
||||
|
||||
export const defaultRelations = [
|
||||
"variants",
|
||||
"variants.prices",
|
||||
"variants.options",
|
||||
"images",
|
||||
"options",
|
||||
]
|
||||
|
||||
export const defaultFields = [
|
||||
"id",
|
||||
"title",
|
||||
"subtitle",
|
||||
"description",
|
||||
"tags",
|
||||
"handle",
|
||||
"is_giftcard",
|
||||
"thumbnail",
|
||||
"profile_id",
|
||||
"weight",
|
||||
"length",
|
||||
"height",
|
||||
"width",
|
||||
"hs_code",
|
||||
"origin_country",
|
||||
"mid_code",
|
||||
"material",
|
||||
"created_at",
|
||||
"updated_at",
|
||||
"metadata",
|
||||
]
|
||||
|
||||
export const allowedFields = [
|
||||
"id",
|
||||
"title",
|
||||
"subtitle",
|
||||
"description",
|
||||
"tags",
|
||||
"handle",
|
||||
"is_giftcard",
|
||||
"thumbnail",
|
||||
"profile_id",
|
||||
"weight",
|
||||
"length",
|
||||
"height",
|
||||
"width",
|
||||
"hs_code",
|
||||
"origin_country",
|
||||
"mid_code",
|
||||
"material",
|
||||
"created_at",
|
||||
"updated_at",
|
||||
"metadata",
|
||||
]
|
||||
|
||||
export const allowedRelations = [
|
||||
"variants",
|
||||
"variants.prices",
|
||||
"images",
|
||||
"options",
|
||||
]
|
||||
@@ -1,65 +1,33 @@
|
||||
import _ from "lodash"
|
||||
import { defaultFields, defaultRelations } from "./"
|
||||
|
||||
export default async (req, res) => {
|
||||
try {
|
||||
const variantService = req.scope.resolve("productVariantService")
|
||||
const productService = req.scope.resolve("productService")
|
||||
const queryBuilderService = req.scope.resolve("queryBuilderService")
|
||||
|
||||
const query = queryBuilderService.buildQuery(req.query, [
|
||||
"title",
|
||||
"description",
|
||||
])
|
||||
|
||||
if ("is_giftcard" in req.query) {
|
||||
query.is_giftcard = req.query.is_giftcard === "true"
|
||||
}
|
||||
|
||||
let variantMatches = []
|
||||
if ("q" in req.query) {
|
||||
let textQ = req.query.q
|
||||
variantMatches = await variantService.list({
|
||||
$or: [
|
||||
{ sku: new RegExp(textQ, "i") },
|
||||
{ title: new RegExp(textQ, "i") },
|
||||
],
|
||||
})
|
||||
|
||||
query.$or = [
|
||||
...query.$or,
|
||||
{ variants: { $in: variantMatches.map(({ _id }) => _id.toString()) } },
|
||||
]
|
||||
}
|
||||
|
||||
const limit = parseInt(req.query.limit) || 0
|
||||
const limit = parseInt(req.query.limit) || 50
|
||||
const offset = parseInt(req.query.offset) || 0
|
||||
|
||||
let products = await productService.list(query, offset, limit)
|
||||
const selector = {}
|
||||
|
||||
products = await Promise.all(
|
||||
products.map(
|
||||
async product =>
|
||||
await productService.decorate(
|
||||
product,
|
||||
[
|
||||
"title",
|
||||
"description",
|
||||
"is_giftcard",
|
||||
"tags",
|
||||
"thumbnail",
|
||||
"handle",
|
||||
"images",
|
||||
"options",
|
||||
"published",
|
||||
],
|
||||
["variants"]
|
||||
)
|
||||
)
|
||||
)
|
||||
if ("q" in req.query) {
|
||||
selector.q = req.query.q
|
||||
}
|
||||
|
||||
const numProducts = await productService.count()
|
||||
if ("is_giftcard" in req.query) {
|
||||
selector.is_giftcard = req.query.is_giftcard === "true"
|
||||
}
|
||||
|
||||
res.json({ products, total_count: numProducts })
|
||||
const listConfig = {
|
||||
select: defaultFields,
|
||||
relations: defaultRelations,
|
||||
skip: offset,
|
||||
take: limit,
|
||||
}
|
||||
|
||||
let products = await productService.list(selector, listConfig)
|
||||
|
||||
res.json({ products, count: products.length, offset, limit })
|
||||
} catch (error) {
|
||||
throw error
|
||||
}
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
export default async (req, res) => {
|
||||
const { id } = req.params
|
||||
|
||||
try {
|
||||
const productService = req.scope.resolve("productService")
|
||||
const product = await productService.retrieve(id)
|
||||
await productService.publish(product._id)
|
||||
let publishedProduct = await productService.retrieve(product._id)
|
||||
publishedProduct = await productService.decorate(
|
||||
publishedProduct,
|
||||
[
|
||||
"title",
|
||||
"description",
|
||||
"tags",
|
||||
"handle",
|
||||
"thumbnail",
|
||||
"images",
|
||||
"options",
|
||||
"published",
|
||||
],
|
||||
["variants"]
|
||||
)
|
||||
res.json({ product: publishedProduct })
|
||||
} catch (error) {
|
||||
throw error
|
||||
}
|
||||
}
|
||||
+11
-8
@@ -1,4 +1,5 @@
|
||||
import { MedusaError, Validator } from "medusa-core-utils"
|
||||
import { defaultFields, defaultRelations } from "./"
|
||||
|
||||
export default async (req, res) => {
|
||||
const { id } = req.params
|
||||
@@ -14,15 +15,17 @@ export default async (req, res) => {
|
||||
}
|
||||
|
||||
try {
|
||||
const orderService = req.scope.resolve("orderService")
|
||||
let order = await orderService.setMetadata(id, value.key, value.value)
|
||||
order = await orderService.decorate(
|
||||
order,
|
||||
[],
|
||||
["region", "customer", "swaps"]
|
||||
)
|
||||
const productService = req.scope.resolve("productService")
|
||||
await productService.update(id, {
|
||||
metadata: { [value.key]: value.value },
|
||||
})
|
||||
|
||||
res.status(200).json({ order })
|
||||
const product = await productService.retrieve(id, {
|
||||
select: defaultFields,
|
||||
relations: defaultRelations,
|
||||
})
|
||||
|
||||
res.status(200).json({ product })
|
||||
} catch (err) {
|
||||
throw err
|
||||
}
|
||||
@@ -1,10 +1,11 @@
|
||||
import { MedusaError, Validator } from "medusa-core-utils"
|
||||
import { defaultFields, defaultRelations } from "./"
|
||||
|
||||
export default async (req, res) => {
|
||||
const { id, option_id } = req.params
|
||||
|
||||
const schema = Validator.object().keys({
|
||||
title: Validator.string(),
|
||||
title: Validator.string().required(),
|
||||
})
|
||||
|
||||
const { value, error } = schema.validate(req.body)
|
||||
@@ -15,24 +16,14 @@ export default async (req, res) => {
|
||||
try {
|
||||
const productService = req.scope.resolve("productService")
|
||||
|
||||
const product = await productService.updateOption(id, option_id, value)
|
||||
await productService.updateOption(id, option_id, value)
|
||||
|
||||
const data = await productService.decorate(
|
||||
product,
|
||||
[
|
||||
"title",
|
||||
"description",
|
||||
"tags",
|
||||
"handle",
|
||||
"images",
|
||||
"thumbnail",
|
||||
"options",
|
||||
"published",
|
||||
],
|
||||
["variants"]
|
||||
)
|
||||
const product = await productService.retrieve(id, {
|
||||
select: defaultFields,
|
||||
relations: defaultRelations,
|
||||
})
|
||||
|
||||
res.json({ product: data })
|
||||
res.json({ product })
|
||||
} catch (err) {
|
||||
throw err
|
||||
}
|
||||
|
||||
@@ -1,50 +1,64 @@
|
||||
import { MedusaError, Validator } from "medusa-core-utils"
|
||||
import { defaultRelations, defaultFields } from "."
|
||||
|
||||
export default async (req, res) => {
|
||||
const { id } = req.params
|
||||
|
||||
const schema = Validator.object().keys({
|
||||
title: Validator.string(),
|
||||
title: Validator.string().optional(),
|
||||
description: Validator.string().optional(),
|
||||
tags: Validator.string().optional(),
|
||||
handle: Validator.string(),
|
||||
handle: Validator.string().optional(),
|
||||
weight: Validator.number().optional(),
|
||||
length: Validator.number().optional(),
|
||||
height: Validator.number().optional(),
|
||||
width: Validator.number().optional(),
|
||||
origin_country: Validator.string().allow(null, ""),
|
||||
mid_code: Validator.string().allow(null, ""),
|
||||
material: Validator.string().allow(null, ""),
|
||||
images: Validator.array()
|
||||
.items(Validator.string())
|
||||
.optional()
|
||||
.optional(),
|
||||
thumbnail: Validator.string().optional(),
|
||||
variants: Validator.array()
|
||||
.items({
|
||||
_id: Validator.string(),
|
||||
title: Validator.string().optional(),
|
||||
sku: Validator.string().optional(),
|
||||
ean: Validator.string().optional(),
|
||||
published: Validator.boolean(),
|
||||
image: Validator.string()
|
||||
.allow("")
|
||||
.optional(),
|
||||
barcode: Validator.string()
|
||||
.allow("")
|
||||
.optional(),
|
||||
id: Validator.string().optional(),
|
||||
title: Validator.string().allow(null),
|
||||
sku: Validator.string().allow(null),
|
||||
ean: Validator.string().allow(null),
|
||||
barcode: Validator.string().allow(null),
|
||||
prices: Validator.array().items(
|
||||
Validator.object()
|
||||
.keys({
|
||||
region_id: Validator.string(),
|
||||
currency_code: Validator.string(),
|
||||
amount: Validator.number().required(),
|
||||
sale_amount: Validator.number().optional(),
|
||||
amount: Validator.number()
|
||||
.integer()
|
||||
.required(),
|
||||
})
|
||||
.xor("region_id", "currency_code")
|
||||
),
|
||||
options: Validator.array().items({
|
||||
option_id: Validator.objectId().required(),
|
||||
option_id: Validator.string().required(),
|
||||
value: Validator.alternatives(
|
||||
Validator.string(),
|
||||
Validator.number()
|
||||
).required(),
|
||||
}),
|
||||
inventory_quantity: Validator.number().optional(),
|
||||
allow_backorder: Validator.boolean().optional(),
|
||||
manage_inventory: Validator.boolean().optional(),
|
||||
inventory_quantity: Validator.number().allow(null),
|
||||
allow_backorder: Validator.boolean().allow(null),
|
||||
manage_inventory: Validator.boolean().allow(null),
|
||||
weight: Validator.number().optional(),
|
||||
length: Validator.number().optional(),
|
||||
height: Validator.number().optional(),
|
||||
width: Validator.number().optional(),
|
||||
hs_code: Validator.string()
|
||||
.optional()
|
||||
.allow(null, ""),
|
||||
origin_country: Validator.string().allow(null, ""),
|
||||
mid_code: Validator.string().allow(null, ""),
|
||||
material: Validator.string().allow(null, ""),
|
||||
metadata: Validator.object().optional(),
|
||||
})
|
||||
.optional(),
|
||||
@@ -58,33 +72,15 @@ export default async (req, res) => {
|
||||
|
||||
try {
|
||||
const productService = req.scope.resolve("productService")
|
||||
const oldProduct = await productService.retrieve(id)
|
||||
|
||||
if (
|
||||
!oldProduct.thumbnail &&
|
||||
!value.thumbnail &&
|
||||
value.images &&
|
||||
value.images.length
|
||||
) {
|
||||
value.thumbnail = value.images[0]
|
||||
}
|
||||
await productService.update(id, value)
|
||||
|
||||
const product = await productService.update(oldProduct._id, value)
|
||||
const data = await productService.decorate(
|
||||
product,
|
||||
[
|
||||
"title",
|
||||
"description",
|
||||
"tags",
|
||||
"handle",
|
||||
"images",
|
||||
"thumbnail",
|
||||
"options",
|
||||
"published",
|
||||
],
|
||||
["variants"]
|
||||
)
|
||||
res.json({ product: data })
|
||||
const product = await productService.retrieve(id, {
|
||||
select: defaultFields,
|
||||
relations: defaultRelations,
|
||||
})
|
||||
|
||||
res.json({ product })
|
||||
} catch (err) {
|
||||
throw err
|
||||
}
|
||||
|
||||
@@ -1,33 +1,52 @@
|
||||
import _ from "lodash"
|
||||
import { MedusaError, Validator } from "medusa-core-utils"
|
||||
import { defaultFields, defaultRelations } from "./"
|
||||
|
||||
export default async (req, res) => {
|
||||
const { id, variant_id } = req.params
|
||||
const schema = Validator.object().keys({
|
||||
title: Validator.string().optional(),
|
||||
sku: Validator.string(),
|
||||
ean: Validator.string(),
|
||||
sku: Validator.string().optional(),
|
||||
ean: Validator.string().optional(),
|
||||
barcode: Validator.string().optional(),
|
||||
prices: Validator.array().items(
|
||||
Validator.object()
|
||||
.keys({
|
||||
region_id: Validator.string(),
|
||||
currency_code: Validator.string(),
|
||||
amount: Validator.number().required(),
|
||||
amount: Validator.number()
|
||||
.integer()
|
||||
.required(),
|
||||
sale_amount: Validator.number().optional(),
|
||||
})
|
||||
.xor("region_id", "currency_code")
|
||||
),
|
||||
options: Validator.array().items({
|
||||
option_id: Validator.objectId().required(),
|
||||
option_id: Validator.string().required(),
|
||||
value: Validator.alternatives(
|
||||
Validator.string(),
|
||||
Validator.number()
|
||||
).required(),
|
||||
}),
|
||||
image: Validator.string().optional(),
|
||||
inventory_quantity: Validator.number().optional(),
|
||||
allow_backorder: Validator.boolean().optional(),
|
||||
manage_inventory: Validator.boolean().optional(),
|
||||
weight: Validator.number().optional(),
|
||||
length: Validator.number().optional(),
|
||||
height: Validator.number().optional(),
|
||||
width: Validator.number().optional(),
|
||||
hs_code: Validator.string()
|
||||
.optional()
|
||||
.allow(null, ""),
|
||||
origin_country: Validator.string()
|
||||
.optional()
|
||||
.allow(null, ""),
|
||||
mid_code: Validator.string()
|
||||
.optional()
|
||||
.allow(null, ""),
|
||||
material: Validator.string()
|
||||
.optional()
|
||||
.allow(null, ""),
|
||||
metadata: Validator.object().optional(),
|
||||
})
|
||||
|
||||
@@ -40,74 +59,13 @@ export default async (req, res) => {
|
||||
const productService = req.scope.resolve("productService")
|
||||
const productVariantService = req.scope.resolve("productVariantService")
|
||||
|
||||
if (value.prices && value.prices.length) {
|
||||
for (const price of value.prices) {
|
||||
if (price.region_id) {
|
||||
await productVariantService.setRegionPrice(
|
||||
variant_id,
|
||||
price.region_id,
|
||||
price.amount,
|
||||
price.sale_amount || undefined
|
||||
)
|
||||
} else {
|
||||
await productVariantService.setCurrencyPrice(
|
||||
variant_id,
|
||||
price.currency_code,
|
||||
price.amount,
|
||||
price.sale_amount || undefined
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
await productVariantService.update(variant_id, value)
|
||||
|
||||
if (value.options && value.options.length) {
|
||||
for (const option of value.options) {
|
||||
await productService.updateOptionValue(
|
||||
id,
|
||||
variant_id,
|
||||
option.option_id,
|
||||
option.value
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
delete value.prices
|
||||
delete value.options
|
||||
|
||||
if (!_.isEmpty(value.metadata)) {
|
||||
for (let key of Object.keys(value.metadata)) {
|
||||
await productVariantService.setMetadata(
|
||||
variant_id,
|
||||
key,
|
||||
value.metadata[key]
|
||||
)
|
||||
}
|
||||
|
||||
delete value.metadata
|
||||
}
|
||||
|
||||
if (!_.isEmpty(value)) {
|
||||
await productVariantService.update(variant_id, value)
|
||||
}
|
||||
|
||||
const product = await productService.retrieve(id)
|
||||
const data = await productService.decorate(
|
||||
product,
|
||||
[
|
||||
"title",
|
||||
"description",
|
||||
"tags",
|
||||
"handle",
|
||||
"images",
|
||||
"options",
|
||||
"thumbnail",
|
||||
"variants",
|
||||
"is_giftcard",
|
||||
"published",
|
||||
],
|
||||
["variants"]
|
||||
)
|
||||
res.json({ product: data })
|
||||
const product = await productService.retrieve(id, {
|
||||
select: defaultFields,
|
||||
relations: defaultRelations,
|
||||
})
|
||||
res.json({ product })
|
||||
} catch (err) {
|
||||
throw err
|
||||
}
|
||||
|
||||
@@ -2,6 +2,24 @@ import { IdMap } from "medusa-test-utils"
|
||||
import { request } from "../../../../../helpers/test-request"
|
||||
import { RegionServiceMock } from "../../../../../services/__mocks__/region"
|
||||
|
||||
const defaultFields = [
|
||||
"id",
|
||||
"name",
|
||||
"currency_code",
|
||||
"tax_rate",
|
||||
"tax_code",
|
||||
"created_at",
|
||||
"updated_at",
|
||||
"deleted_at",
|
||||
"metadata",
|
||||
]
|
||||
|
||||
const defaultRelations = [
|
||||
"countries",
|
||||
"payment_providers",
|
||||
"fulfillment_providers",
|
||||
]
|
||||
|
||||
describe("GET /admin/regions/:region_id", () => {
|
||||
describe("successful creation", () => {
|
||||
let subject
|
||||
@@ -24,7 +42,11 @@ describe("GET /admin/regions/:region_id", () => {
|
||||
it("calls service addCountry", () => {
|
||||
expect(RegionServiceMock.retrieve).toHaveBeenCalledTimes(1)
|
||||
expect(RegionServiceMock.retrieve).toHaveBeenCalledWith(
|
||||
IdMap.getId("testRegion")
|
||||
IdMap.getId("testRegion"),
|
||||
{
|
||||
select: defaultFields,
|
||||
relations: defaultRelations,
|
||||
}
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -2,6 +2,24 @@ import { IdMap } from "medusa-test-utils"
|
||||
import { request } from "../../../../../helpers/test-request"
|
||||
import { RegionServiceMock } from "../../../../../services/__mocks__/region"
|
||||
|
||||
const defaultFields = [
|
||||
"id",
|
||||
"name",
|
||||
"currency_code",
|
||||
"tax_rate",
|
||||
"tax_code",
|
||||
"created_at",
|
||||
"updated_at",
|
||||
"deleted_at",
|
||||
"metadata",
|
||||
]
|
||||
|
||||
const defaultRelations = [
|
||||
"countries",
|
||||
"payment_providers",
|
||||
"fulfillment_providers",
|
||||
]
|
||||
|
||||
describe("GET /admin/regions", () => {
|
||||
describe("successful creation", () => {
|
||||
let subject
|
||||
@@ -22,7 +40,15 @@ describe("GET /admin/regions", () => {
|
||||
|
||||
it("calls service addCountry", () => {
|
||||
expect(RegionServiceMock.list).toHaveBeenCalledTimes(1)
|
||||
expect(RegionServiceMock.list).toHaveBeenCalledWith({})
|
||||
expect(RegionServiceMock.list).toHaveBeenCalledWith(
|
||||
{},
|
||||
{
|
||||
select: defaultFields,
|
||||
relations: defaultRelations,
|
||||
take: 50,
|
||||
skip: 0,
|
||||
}
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,4 +1,5 @@
|
||||
import { MedusaError, Validator } from "medusa-core-utils"
|
||||
import { defaultRelations, defaultFields } from "./"
|
||||
|
||||
export default async (req, res) => {
|
||||
const { region_id } = req.params
|
||||
@@ -15,8 +16,12 @@ export default async (req, res) => {
|
||||
const regionService = req.scope.resolve("regionService")
|
||||
await regionService.addCountry(region_id, value.country_code)
|
||||
|
||||
const data = await regionService.retrieve(region_id)
|
||||
res.status(200).json({ region: data })
|
||||
const region = await regionService.retrieve(region_id, {
|
||||
select: defaultFields,
|
||||
relations: defaultRelations,
|
||||
})
|
||||
|
||||
res.status(200).json({ region })
|
||||
} catch (err) {
|
||||
throw err
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { MedusaError, Validator } from "medusa-core-utils"
|
||||
import { defaultRelations, defaultFields } from "./"
|
||||
|
||||
export default async (req, res) => {
|
||||
const { region_id } = req.params
|
||||
@@ -15,7 +16,10 @@ export default async (req, res) => {
|
||||
const regionService = req.scope.resolve("regionService")
|
||||
await regionService.addFulfillmentProvider(region_id, value.provider_id)
|
||||
|
||||
const data = await regionService.retrieve(region_id)
|
||||
const data = await regionService.retrieve(region_id, {
|
||||
select: defaultFields,
|
||||
relations: defaultRelations,
|
||||
})
|
||||
res.status(200).json({ region: data })
|
||||
} catch (err) {
|
||||
throw err
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { MedusaError, Validator } from "medusa-core-utils"
|
||||
import { defaultRelations, defaultFields } from "./"
|
||||
|
||||
export default async (req, res) => {
|
||||
const { region_id } = req.params
|
||||
@@ -15,7 +16,10 @@ export default async (req, res) => {
|
||||
const regionService = req.scope.resolve("regionService")
|
||||
await regionService.addPaymentProvider(region_id, value.provider_id)
|
||||
|
||||
const data = await regionService.retrieve(region_id)
|
||||
const data = await regionService.retrieve(region_id, {
|
||||
select: defaultFields,
|
||||
relations: defaultRelations,
|
||||
})
|
||||
res.status(200).json({ region: data })
|
||||
} catch (err) {
|
||||
throw err
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { MedusaError, Validator } from "medusa-core-utils"
|
||||
import { defaultRelations, defaultFields } from "./"
|
||||
|
||||
export default async (req, res) => {
|
||||
const schema = Validator.object().keys({
|
||||
@@ -18,8 +19,14 @@ export default async (req, res) => {
|
||||
|
||||
try {
|
||||
const regionService = req.scope.resolve("regionService")
|
||||
const data = await regionService.create(value)
|
||||
res.status(200).json({ region: data })
|
||||
const result = await regionService.create(value)
|
||||
|
||||
const region = await regionService.retrieve(result.id, {
|
||||
select: defaultFields,
|
||||
relations: defaultRelations,
|
||||
})
|
||||
|
||||
res.status(200).json({ region })
|
||||
} catch (err) {
|
||||
throw err
|
||||
}
|
||||
|
||||
@@ -1,9 +1,16 @@
|
||||
import { defaultRelations, defaultFields } from "./"
|
||||
|
||||
export default async (req, res) => {
|
||||
const { id, key } = req.params
|
||||
|
||||
try {
|
||||
const regionService = req.scope.resolve("regionService")
|
||||
const region = await regionService.deleteMetadata(id, key)
|
||||
await regionService.deleteMetadata(id, key)
|
||||
|
||||
const region = await regionService.retrieve(id, {
|
||||
select: defaultFields,
|
||||
relations: defaultRelations,
|
||||
})
|
||||
|
||||
res.status(200).json({ region })
|
||||
} catch (err) {
|
||||
|
||||
@@ -1,16 +1,19 @@
|
||||
import { MedusaError, Validator } from "medusa-core-utils"
|
||||
|
||||
export default async (req, res) => {
|
||||
const { region_id } = req.params
|
||||
|
||||
try {
|
||||
const fulfillmentProviderService = req.scope.resolve(
|
||||
"fulfillmentProviderService"
|
||||
)
|
||||
const regionService = req.scope.resolve("regionService")
|
||||
const region = await regionService.retrieve(region_id)
|
||||
const region = await regionService.retrieve(region_id, {
|
||||
relations: ["fulfillment_providers"],
|
||||
})
|
||||
|
||||
const fpsIds = region.fulfillment_providers.map(fp => fp.id) || []
|
||||
|
||||
const options = await fulfillmentProviderService.listFulfillmentOptions(
|
||||
region.fulfillment_providers || []
|
||||
fpsIds
|
||||
)
|
||||
|
||||
res.status(200).json({
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
import { MedusaError, Validator } from "medusa-core-utils"
|
||||
import { defaultRelations, defaultFields } from "./"
|
||||
|
||||
export default async (req, res) => {
|
||||
const { region_id } = req.params
|
||||
try {
|
||||
const regionService = req.scope.resolve("regionService")
|
||||
const data = await regionService.retrieve(region_id)
|
||||
const data = await regionService.retrieve(region_id, {
|
||||
select: defaultFields,
|
||||
relations: defaultRelations,
|
||||
})
|
||||
|
||||
res.status(200).json({ region: data })
|
||||
} catch (err) {
|
||||
|
||||
@@ -70,3 +70,21 @@ export default app => {
|
||||
|
||||
return app
|
||||
}
|
||||
|
||||
export const defaultFields = [
|
||||
"id",
|
||||
"name",
|
||||
"currency_code",
|
||||
"tax_rate",
|
||||
"tax_code",
|
||||
"created_at",
|
||||
"updated_at",
|
||||
"deleted_at",
|
||||
"metadata",
|
||||
]
|
||||
|
||||
export const defaultRelations = [
|
||||
"countries",
|
||||
"payment_providers",
|
||||
"fulfillment_providers",
|
||||
]
|
||||
@@ -1,11 +1,24 @@
|
||||
import { MedusaError, Validator } from "medusa-core-utils"
|
||||
import { defaultFields, defaultRelations } from "./"
|
||||
|
||||
export default async (req, res) => {
|
||||
try {
|
||||
const regionService = req.scope.resolve("regionService")
|
||||
const data = await regionService.list({})
|
||||
|
||||
res.status(200).json({ regions: data })
|
||||
const limit = parseInt(req.query.limit) || 50
|
||||
const offset = parseInt(req.query.offset) || 0
|
||||
|
||||
const selector = {}
|
||||
|
||||
const listConfig = {
|
||||
select: defaultFields,
|
||||
relations: defaultRelations,
|
||||
skip: offset,
|
||||
take: limit,
|
||||
}
|
||||
|
||||
let regions = await regionService.list(selector, listConfig)
|
||||
|
||||
res.json({ regions, count: regions.length, offset, limit })
|
||||
} catch (err) {
|
||||
throw err
|
||||
}
|
||||
|
||||
@@ -1,12 +1,18 @@
|
||||
import { MedusaError, Validator } from "medusa-core-utils"
|
||||
import { defaultRelations, defaultFields } from "./"
|
||||
|
||||
export default async (req, res) => {
|
||||
const { region_id, country_code } = req.params
|
||||
try {
|
||||
const regionService = req.scope.resolve("regionService")
|
||||
const data = await regionService.removeCountry(region_id, country_code)
|
||||
await regionService.removeCountry(region_id, country_code)
|
||||
|
||||
res.json({ region: data })
|
||||
const region = await regionService.retrieve(region_id, {
|
||||
select: defaultFields,
|
||||
relations: defaultRelations,
|
||||
})
|
||||
|
||||
res.json({ region })
|
||||
} catch (err) {
|
||||
throw err
|
||||
}
|
||||
|
||||
@@ -1,15 +1,19 @@
|
||||
import { MedusaError, Validator } from "medusa-core-utils"
|
||||
import { defaultRelations, defaultFields } from "./"
|
||||
|
||||
export default async (req, res) => {
|
||||
const { region_id, provider_id } = req.params
|
||||
try {
|
||||
const regionService = req.scope.resolve("regionService")
|
||||
const data = await regionService.removeFulfillmentProvider(
|
||||
region_id,
|
||||
provider_id
|
||||
)
|
||||
|
||||
res.json({ region: data })
|
||||
await regionService.removeFulfillmentProvider(region_id, provider_id)
|
||||
|
||||
const region = await regionService.retrieve(region_id, {
|
||||
select: defaultFields,
|
||||
relations: defaultRelations,
|
||||
})
|
||||
|
||||
res.json({ region })
|
||||
} catch (err) {
|
||||
throw err
|
||||
}
|
||||
|
||||
@@ -1,15 +1,18 @@
|
||||
import { MedusaError, Validator } from "medusa-core-utils"
|
||||
import { defaultRelations, defaultFields } from "./"
|
||||
|
||||
export default async (req, res) => {
|
||||
const { region_id, provider_id } = req.params
|
||||
try {
|
||||
const regionService = req.scope.resolve("regionService")
|
||||
const data = await regionService.removePaymentProvider(
|
||||
region_id,
|
||||
provider_id
|
||||
)
|
||||
await regionService.removePaymentProvider(region_id, provider_id)
|
||||
|
||||
res.json({ region: data })
|
||||
const region = await regionService.retrieve(region_id, {
|
||||
select: defaultFields,
|
||||
relations: defaultRelations,
|
||||
})
|
||||
|
||||
res.json({ region })
|
||||
} catch (err) {
|
||||
throw err
|
||||
}
|
||||
|
||||
Loaded 100 of 499 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user