fix: allow changing regions safely
This commit is contained in:
commit
06f5fe2670
278 files changed
+14657
-9233
No files matched your search
@@ -1,9 +0,0 @@
|
||||
{
|
||||
"plugins": ["prettier"],
|
||||
"extends": ["prettier"],
|
||||
"rules": {
|
||||
"prettier/prettier": "error",
|
||||
"semi": "error",
|
||||
"no-unused-expressions": "true"
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"endOfLine": "lf",
|
||||
"semi": false,
|
||||
"singleQuote": false,
|
||||
"tabWidth": 2,
|
||||
"trailingComma": "es5"
|
||||
}
|
||||
+337
-804
File diff suppressed because it is too large.
Load diff
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@medusajs/medusa",
|
||||
"version": "1.1.41",
|
||||
"version": "1.1.45",
|
||||
"description": "E-commerce for JAMstack",
|
||||
"main": "dist/index.js",
|
||||
"bin": {
|
||||
@@ -20,11 +20,11 @@
|
||||
"@babel/cli": "^7.14.3",
|
||||
"@babel/core": "^7.14.3",
|
||||
"@babel/preset-typescript": "^7.13.0",
|
||||
"babel-preset-medusa-package": "^1.1.15",
|
||||
"babel-preset-medusa-package": "^1.1.17",
|
||||
"cross-env": "^5.2.1",
|
||||
"eslint": "^6.8.0",
|
||||
"jest": "^25.5.2",
|
||||
"medusa-interfaces": "^1.1.23",
|
||||
"medusa-interfaces": "^1.1.27",
|
||||
"nodemon": "^2.0.1",
|
||||
"prettier": "^1.19.1",
|
||||
"sqlite3": "^5.0.2",
|
||||
@@ -45,7 +45,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@hapi/joi": "^16.1.8",
|
||||
"@medusajs/medusa-cli": "^1.1.18",
|
||||
"@medusajs/medusa-cli": "^1.1.22",
|
||||
"@types/lodash": "^4.14.168",
|
||||
"awilix": "^4.2.3",
|
||||
"body-parser": "^1.19.0",
|
||||
@@ -67,8 +67,8 @@
|
||||
"joi": "^17.3.0",
|
||||
"joi-objectid": "^3.0.1",
|
||||
"jsonwebtoken": "^8.5.1",
|
||||
"medusa-core-utils": "^1.1.22",
|
||||
"medusa-test-utils": "^1.1.25",
|
||||
"medusa-core-utils": "^1.1.26",
|
||||
"medusa-test-utils": "^1.1.29",
|
||||
"morgan": "^1.9.1",
|
||||
"multer": "^1.4.2",
|
||||
"passport": "^0.4.0",
|
||||
@@ -87,5 +87,5 @@
|
||||
"uuid": "^8.3.1",
|
||||
"winston": "^3.2.1"
|
||||
},
|
||||
"gitHead": "41a5425405aea5045a26def95c0dc00cf4a5a44d"
|
||||
"gitHead": "5afd0dfbabaebfb6b09f0e2f055386ac76059372"
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import _ from "lodash"
|
||||
|
||||
/**
|
||||
* @oas [get] /auth
|
||||
* operationId: "DeleteAuth"
|
||||
* summary: "Delete Session"
|
||||
* description: "Deletes the current session for the logged in user."
|
||||
* tags:
|
||||
* - Auth
|
||||
* responses:
|
||||
* "200":
|
||||
* description: OK
|
||||
*/
|
||||
export default async (req, res) => {
|
||||
req.session.destroy()
|
||||
res.status(200).end()
|
||||
}
|
||||
@@ -13,5 +13,11 @@ export default app => {
|
||||
)
|
||||
route.post("/", middlewares.wrap(require("./create-session").default))
|
||||
|
||||
route.delete(
|
||||
"/",
|
||||
middlewares.authenticate(),
|
||||
middlewares.wrap(require("./delete-session").default)
|
||||
)
|
||||
|
||||
return app
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
import { MedusaError, Validator } from "medusa-core-utils"
|
||||
import { defaultRelations } from "."
|
||||
|
||||
/**
|
||||
* @oas [post] /discounts
|
||||
@@ -96,11 +97,10 @@ export default async (req, res) => {
|
||||
const discountService = req.scope.resolve("discountService")
|
||||
|
||||
const created = await discountService.create(value)
|
||||
const discount = await discountService.retrieve(created.id, [
|
||||
"rule",
|
||||
"rule.valid_for",
|
||||
"regions",
|
||||
])
|
||||
const discount = await discountService.retrieve(
|
||||
created.id,
|
||||
defaultRelations
|
||||
)
|
||||
|
||||
res.status(200).json({ discount })
|
||||
} catch (err) {
|
||||
|
||||
@@ -57,6 +57,7 @@ export const defaultCartRelations = [
|
||||
"payment_sessions",
|
||||
"shipping_methods.shipping_option",
|
||||
"discounts",
|
||||
"discounts.rule",
|
||||
]
|
||||
|
||||
export const defaultCartFields = [
|
||||
|
||||
@@ -43,7 +43,14 @@ export default async (req, res) => {
|
||||
.withTransaction(manager)
|
||||
.retrieve(draftOrder.cart_id, {
|
||||
select: ["total"],
|
||||
relations: ["discounts", "shipping_methods", "region", "items"],
|
||||
relations: [
|
||||
"discounts",
|
||||
"discounts.rule",
|
||||
"discounts.rule.valid_for",
|
||||
"shipping_methods",
|
||||
"region",
|
||||
"items",
|
||||
],
|
||||
})
|
||||
|
||||
await paymentProviderService
|
||||
|
||||
@@ -7,6 +7,8 @@ const defaultRelations = [
|
||||
"billing_address",
|
||||
"shipping_address",
|
||||
"discounts",
|
||||
"discounts.rule",
|
||||
"discounts.rule.valid_for",
|
||||
"shipping_methods",
|
||||
"payments",
|
||||
"fulfillments",
|
||||
@@ -25,6 +27,7 @@ const defaultRelations = [
|
||||
"claims.additional_items",
|
||||
"claims.fulfillments",
|
||||
"claims.claim_items",
|
||||
"claims.claim_items.item",
|
||||
"claims.claim_items.images",
|
||||
"swaps",
|
||||
"swaps.return_order",
|
||||
@@ -54,6 +57,7 @@ const defaultFields = [
|
||||
"metadata",
|
||||
"items.refundable",
|
||||
"swaps.additional_items.refundable",
|
||||
"claims.additional_items.refundable",
|
||||
"shipping_total",
|
||||
"discount_total",
|
||||
"tax_total",
|
||||
|
||||
@@ -202,7 +202,12 @@ export default async (req, res) => {
|
||||
const order = await orderService
|
||||
.withTransaction(manager)
|
||||
.retrieve(id, {
|
||||
relations: ["items", "discounts"],
|
||||
relations: [
|
||||
"items",
|
||||
"cart",
|
||||
"cart.discounts",
|
||||
"cart.discounts.rule",
|
||||
],
|
||||
})
|
||||
|
||||
await claimService.withTransaction(manager).create({
|
||||
|
||||
@@ -45,6 +45,17 @@ import { defaultFields, defaultRelations } from "./"
|
||||
* quantity:
|
||||
* description: The quantity of the Product Variant to ship.
|
||||
* type: integer
|
||||
* custom_shipping_options:
|
||||
* description: The custom shipping options to potentially create a Shipping Method from.
|
||||
* type: array
|
||||
* items:
|
||||
* properties:
|
||||
* option_id:
|
||||
* description: The id of the Shipping Option to override with a custom price.
|
||||
* type: string
|
||||
* price:
|
||||
* description: The custom price of the Shipping Option.
|
||||
* type: integer
|
||||
* no_notification:
|
||||
* description: If set to true no notification will be send related to this Swap.
|
||||
* type: boolean
|
||||
@@ -85,6 +96,12 @@ export default async (req, res) => {
|
||||
variant_id: Validator.string().required(),
|
||||
quantity: Validator.number().required(),
|
||||
}),
|
||||
custom_shipping_options: Validator.array()
|
||||
.items({
|
||||
option_id: Validator.string().required(),
|
||||
price: Validator.number().required(),
|
||||
})
|
||||
.default([]),
|
||||
no_notification: Validator.boolean().optional(),
|
||||
allow_backorder: Validator.boolean().default(true),
|
||||
})
|
||||
@@ -149,7 +166,9 @@ export default async (req, res) => {
|
||||
}
|
||||
)
|
||||
|
||||
await swapService.withTransaction(manager).createCart(swap.id)
|
||||
await swapService
|
||||
.withTransaction(manager)
|
||||
.createCart(swap.id, value.custom_shipping_options)
|
||||
const returnOrder = await returnService
|
||||
.withTransaction(manager)
|
||||
.retrieveBySwap(swap.id)
|
||||
|
||||
@@ -221,6 +221,8 @@ export const defaultRelations = [
|
||||
"billing_address",
|
||||
"shipping_address",
|
||||
"discounts",
|
||||
"discounts.rule",
|
||||
"discounts.rule.valid_for",
|
||||
"shipping_methods",
|
||||
"payments",
|
||||
"fulfillments",
|
||||
@@ -239,6 +241,7 @@ export const defaultRelations = [
|
||||
"claims.additional_items",
|
||||
"claims.fulfillments",
|
||||
"claims.claim_items",
|
||||
"claims.claim_items.item",
|
||||
"claims.claim_items.images",
|
||||
// "claims.claim_items.tags",
|
||||
"swaps",
|
||||
@@ -269,6 +272,7 @@ export const defaultFields = [
|
||||
"metadata",
|
||||
"items.refundable",
|
||||
"swaps.additional_items.refundable",
|
||||
"claims.additional_items.refundable",
|
||||
"shipping_total",
|
||||
"discount_total",
|
||||
"tax_total",
|
||||
@@ -316,6 +320,8 @@ export const allowedRelations = [
|
||||
"billing_address",
|
||||
"shipping_address",
|
||||
"discounts",
|
||||
"discounts.rule",
|
||||
"discounts.rule.valid_for",
|
||||
"shipping_methods",
|
||||
"payments",
|
||||
"fulfillments",
|
||||
|
||||
@@ -46,7 +46,11 @@ export default app => {
|
||||
)
|
||||
|
||||
route.get("/:id", middlewares.wrap(require("./get-product").default))
|
||||
route.get("/", middlewares.wrap(require("./list-products").default))
|
||||
route.get(
|
||||
"/",
|
||||
middlewares.normalizeQuery(),
|
||||
middlewares.wrap(require("./list-products").default)
|
||||
)
|
||||
|
||||
return app
|
||||
}
|
||||
@@ -121,3 +125,18 @@ export const allowedRelations = [
|
||||
"type",
|
||||
"collection",
|
||||
]
|
||||
|
||||
export const filterableFields = [
|
||||
"id",
|
||||
"status",
|
||||
"collection_id",
|
||||
"tags",
|
||||
"title",
|
||||
"description",
|
||||
"handle",
|
||||
"is_giftcard",
|
||||
"type",
|
||||
"created_at",
|
||||
"updated_at",
|
||||
"deleted_at",
|
||||
]
|
||||
@@ -1,6 +1,6 @@
|
||||
import _ from "lodash"
|
||||
import { MedusaError, Validator } from "medusa-core-utils"
|
||||
import { defaultFields, defaultRelations } from "./"
|
||||
import { defaultFields, defaultRelations, filterableFields } from "./"
|
||||
|
||||
/**
|
||||
* @oas [get] /products
|
||||
@@ -31,6 +31,17 @@ import { defaultFields, defaultRelations } from "./"
|
||||
* $ref: "#/components/schemas/product"
|
||||
*/
|
||||
export default async (req, res) => {
|
||||
const schema = Validator.productFilter()
|
||||
|
||||
const { value, error } = schema.validate(req.query)
|
||||
|
||||
if (error) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
JSON.stringify(error.details)
|
||||
)
|
||||
}
|
||||
|
||||
try {
|
||||
const productService = req.scope.resolve("productService")
|
||||
|
||||
@@ -53,21 +64,16 @@ export default async (req, res) => {
|
||||
expandFields = req.query.expand.split(",")
|
||||
}
|
||||
|
||||
if ("is_giftcard" in req.query) {
|
||||
selector.is_giftcard = req.query.is_giftcard === "true"
|
||||
for (const k of filterableFields) {
|
||||
if (k in value) {
|
||||
selector[k] = value[k]
|
||||
}
|
||||
}
|
||||
|
||||
if ("status" in req.query) {
|
||||
const schema = Validator.array()
|
||||
.items(
|
||||
Validator.string().valid("proposed", "draft", "published", "rejected")
|
||||
)
|
||||
.single()
|
||||
|
||||
const { value, error } = schema.validate(req.query.status)
|
||||
|
||||
if (value && !error) {
|
||||
selector.status = value
|
||||
if (selector.status?.indexOf("null") > -1) {
|
||||
selector.status.splice(selector.status.indexOf("null"), 1)
|
||||
if (selector.status.length === 0) {
|
||||
delete selector.status
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ describe("POST /store/carts/:id/shipping-methods", () => {
|
||||
jest.clearAllMocks()
|
||||
})
|
||||
|
||||
it("calls CartService addShipping", () => {
|
||||
it("calls CartService addShippingMethod", () => {
|
||||
expect(CartServiceMock.addShippingMethod).toHaveBeenCalledTimes(1)
|
||||
expect(CartServiceMock.addShippingMethod).toHaveBeenCalledWith(
|
||||
IdMap.getId("fr-cart"),
|
||||
@@ -45,6 +45,50 @@ describe("POST /store/carts/:id/shipping-methods", () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe("successfully adds a shipping method", () => {
|
||||
let subject
|
||||
|
||||
beforeAll(async () => {
|
||||
const cartId = IdMap.getId("swap-cart")
|
||||
subject = await request(
|
||||
"POST",
|
||||
`/store/carts/${cartId}/shipping-methods`,
|
||||
{
|
||||
payload: {
|
||||
option_id: IdMap.getId("freeShipping"),
|
||||
},
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
afterAll(() => {
|
||||
jest.clearAllMocks()
|
||||
})
|
||||
|
||||
it("calls CartService addShippingMethod", () => {
|
||||
expect(CartServiceMock.addShippingMethod).toHaveBeenCalledTimes(1)
|
||||
expect(CartServiceMock.addShippingMethod).toHaveBeenCalledWith(
|
||||
IdMap.getId("swap-cart"),
|
||||
IdMap.getId("freeShipping"),
|
||||
{}
|
||||
)
|
||||
})
|
||||
|
||||
it("calls CartService retrieve", () => {
|
||||
expect(CartServiceMock.retrieve).toHaveBeenCalledTimes(2)
|
||||
})
|
||||
|
||||
it("returns 200", () => {
|
||||
expect(subject.status).toEqual(200)
|
||||
})
|
||||
|
||||
it("returns the cart", () => {
|
||||
expect(subject.body.cart).toEqual(
|
||||
expect.objectContaining({ type: "swap", id: IdMap.getId("test-swap") })
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe("successfully adds a shipping method with additional data", () => {
|
||||
let subject
|
||||
|
||||
@@ -68,7 +112,7 @@ describe("POST /store/carts/:id/shipping-methods", () => {
|
||||
jest.clearAllMocks()
|
||||
})
|
||||
|
||||
it("calls CartService addShipping", () => {
|
||||
it("calls CartService addShippingMethod", () => {
|
||||
expect(CartServiceMock.addShippingMethod).toHaveBeenCalledTimes(1)
|
||||
expect(CartServiceMock.addShippingMethod).toHaveBeenCalledWith(
|
||||
IdMap.getId("fr-cart"),
|
||||
|
||||
@@ -44,7 +44,9 @@ export default async (req, res) => {
|
||||
|
||||
await manager.transaction(async m => {
|
||||
const txCartService = cartService.withTransaction(m)
|
||||
|
||||
await txCartService.addShippingMethod(id, value.option_id, value.data)
|
||||
|
||||
const updated = await txCartService.retrieve(id, {
|
||||
relations: ["payment_sessions"],
|
||||
})
|
||||
@@ -54,12 +56,12 @@ export default async (req, res) => {
|
||||
}
|
||||
})
|
||||
|
||||
const cart = await cartService.retrieve(id, {
|
||||
const updatedCart = await cartService.retrieve(id, {
|
||||
select: defaultFields,
|
||||
relations: defaultRelations,
|
||||
})
|
||||
|
||||
res.status(200).json({ cart })
|
||||
res.status(200).json({ cart: updatedCart })
|
||||
} catch (err) {
|
||||
throw err
|
||||
}
|
||||
|
||||
@@ -82,6 +82,14 @@ export default async (req, res) => {
|
||||
if (!value.region_id) {
|
||||
const regionService = req.scope.resolve("regionService")
|
||||
const regions = await regionService.withTransaction(manager).list({})
|
||||
|
||||
if (!regions?.length) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
`A region is required to create a cart`
|
||||
)
|
||||
}
|
||||
|
||||
regionId = regions[0].id
|
||||
}
|
||||
|
||||
|
||||
@@ -116,4 +116,6 @@ export const defaultRelations = [
|
||||
"payment_sessions",
|
||||
"shipping_methods.shipping_option",
|
||||
"discounts",
|
||||
"discounts.rule",
|
||||
"discounts.rule.valid_for",
|
||||
]
|
||||
@@ -72,11 +72,9 @@ export default async (req, res) => {
|
||||
const schema = Validator.object().keys({
|
||||
region_id: Validator.string().optional(),
|
||||
country_code: Validator.string().optional(),
|
||||
email: Validator.string()
|
||||
.email()
|
||||
.optional(),
|
||||
billing_address: Validator.address().optional(),
|
||||
shipping_address: Validator.address().optional(),
|
||||
email: Validator.string().email().optional(),
|
||||
billing_address: Validator.address().optional().allow(null),
|
||||
shipping_address: Validator.address().optional().allow(null),
|
||||
gift_cards: Validator.array()
|
||||
.items({
|
||||
code: Validator.string(),
|
||||
|
||||
@@ -50,7 +50,7 @@ export default async (req, res) => {
|
||||
const id = req.user.customer_id
|
||||
|
||||
const schema = Validator.object().keys({
|
||||
billing_address: Validator.address().optional(),
|
||||
billing_address: Validator.address().optional().allow(null),
|
||||
first_name: Validator.string().optional(),
|
||||
last_name: Validator.string().optional(),
|
||||
password: Validator.string().optional(),
|
||||
@@ -66,9 +66,9 @@ export default async (req, res) => {
|
||||
|
||||
try {
|
||||
const customerService = req.scope.resolve("customerService")
|
||||
let customer = await customerService.update(id, value)
|
||||
await customerService.update(id, value)
|
||||
|
||||
customer = await customerService.retrieve(customer.id, {
|
||||
const customer = await customerService.retrieve(id, {
|
||||
relations: defaultRelations,
|
||||
select: defaultFields,
|
||||
})
|
||||
|
||||
@@ -36,6 +36,8 @@ export const defaultRelations = [
|
||||
"items.variant.product",
|
||||
"shipping_methods",
|
||||
"discounts",
|
||||
"discounts.rule",
|
||||
"discounts.rule.valid_for",
|
||||
"customer",
|
||||
"payments",
|
||||
"region",
|
||||
@@ -74,6 +76,8 @@ export const allowedRelations = [
|
||||
"items.variant.product",
|
||||
"shipping_methods",
|
||||
"discounts",
|
||||
"discounts.rule",
|
||||
"discounts.rule.valid_for",
|
||||
"customer",
|
||||
"payments",
|
||||
"region",
|
||||
|
||||
@@ -7,6 +7,7 @@ export default app => {
|
||||
app.use("/products", route)
|
||||
|
||||
route.get("/", middlewares.wrap(require("./list-products").default))
|
||||
route.post("/search", middlewares.wrap(require("./search").default))
|
||||
route.get("/:id", middlewares.wrap(require("./get-product").default))
|
||||
|
||||
return app
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
import { Validator, MedusaError } from "medusa-core-utils"
|
||||
import ProductService from "../../../../services/product"
|
||||
|
||||
export default async (req, res) => {
|
||||
const schema = Validator.object()
|
||||
.keys({
|
||||
q: Validator.string().required(),
|
||||
offset: Validator.number().optional(),
|
||||
limit: Validator.number().optional(),
|
||||
filter: Validator.any(),
|
||||
})
|
||||
.options({ allowUnknown: true })
|
||||
|
||||
const { value, error } = schema.validate(req.body)
|
||||
if (error) {
|
||||
throw new MedusaError(MedusaError.Types.INVALID_DATA, error.details)
|
||||
}
|
||||
|
||||
try {
|
||||
const { q, offset, limit, filter, ...options } = value
|
||||
const paginationOptions = { offset, limit }
|
||||
|
||||
const searchService = req.scope.resolve("searchService")
|
||||
|
||||
const results = await searchService.search(ProductService.IndexName, q, {
|
||||
paginationOptions,
|
||||
filter,
|
||||
additionalOptions: options,
|
||||
})
|
||||
|
||||
res.status(200).send(results)
|
||||
} catch (error) {
|
||||
throw error
|
||||
}
|
||||
}
|
||||
@@ -108,7 +108,7 @@ export default async (req, res) => {
|
||||
case "started": {
|
||||
const { key, error } = await idempotencyKeyService.workStage(
|
||||
idempotencyKey.idempotency_key,
|
||||
async (manager) => {
|
||||
async manager => {
|
||||
const order = await orderService
|
||||
.withTransaction(manager)
|
||||
.retrieve(value.order_id, {
|
||||
@@ -163,7 +163,7 @@ export default async (req, res) => {
|
||||
case "swap_created": {
|
||||
const { key, error } = await idempotencyKeyService.workStage(
|
||||
idempotencyKey.idempotency_key,
|
||||
async (manager) => {
|
||||
async manager => {
|
||||
const swaps = await swapService.list({
|
||||
idempotency_key: idempotencyKey.idempotency_key,
|
||||
})
|
||||
|
||||
@@ -16,7 +16,7 @@ describe("Get variant by id", () => {
|
||||
it("calls get variant from variantSerice", () => {
|
||||
expect(ProductVariantServiceMock.retrieve).toHaveBeenCalledTimes(1)
|
||||
expect(ProductVariantServiceMock.retrieve).toHaveBeenCalledWith("1", {
|
||||
relations: ["prices"],
|
||||
relations: ["prices", "options"],
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@@ -12,4 +12,4 @@ export default app => {
|
||||
return app
|
||||
}
|
||||
|
||||
export const defaultRelations = ["prices"]
|
||||
export const defaultRelations = ["prices", "options"]
|
||||
@@ -46,3 +46,4 @@ export { User } from "./models/user"
|
||||
export { DraftOrder } from "./models/draft-order"
|
||||
export { ReturnReason } from "./models/return-reason"
|
||||
export { Note } from "./models/note"
|
||||
export { CustomShippingOption } from "./models/custom-shipping-option"
|
||||
@@ -16,6 +16,7 @@ import passportLoader from "./passport"
|
||||
import pluginsLoader, { registerPluginModels } from "./plugins"
|
||||
import defaultsLoader from "./defaults"
|
||||
import Logger from "./logger"
|
||||
import searchIndexLoader from "./search-index"
|
||||
|
||||
export default async ({ directory: rootDirectory, expressApp }) => {
|
||||
const { configModule } = getConfigFile(rootDirectory, `medusa-config`)
|
||||
@@ -101,12 +102,6 @@ export default async ({ directory: rootDirectory, expressApp }) => {
|
||||
const servAct = Logger.success(servicesActivity, "Services initialized") || {}
|
||||
track("SERVICES_INIT_COMPLETED", { duration: servAct.duration })
|
||||
|
||||
const subActivity = Logger.activity("Initializing subscribers")
|
||||
track("SUBSCRIBERS_INIT_STARTED")
|
||||
subscribersLoader({ container, activityId: subActivity })
|
||||
const subAct = Logger.success(subActivity, "Subscribers initialized") || {}
|
||||
track("SUBSCRIBERS_INIT_COMPLETED", { duration: subAct.duration })
|
||||
|
||||
const expActivity = Logger.activity("Initializing express")
|
||||
track("EXPRESS_INIT_STARTED")
|
||||
await expressLoader({
|
||||
@@ -138,6 +133,12 @@ export default async ({ directory: rootDirectory, expressApp }) => {
|
||||
const pAct = Logger.success(pluginsActivity, "Plugins intialized") || {}
|
||||
track("PLUGINS_INIT_COMPLETED", { duration: pAct.duration })
|
||||
|
||||
const subActivity = Logger.activity("Initializing subscribers")
|
||||
track("SUBSCRIBERS_INIT_STARTED")
|
||||
subscribersLoader({ container, activityId: subActivity })
|
||||
const subAct = Logger.success(subActivity, "Subscribers initialized") || {}
|
||||
track("SUBSCRIBERS_INIT_COMPLETED", { duration: subAct.duration })
|
||||
|
||||
const apiActivity = Logger.activity("Initializing API")
|
||||
track("API_INIT_STARTED")
|
||||
await apiLoader({
|
||||
@@ -155,6 +156,12 @@ export default async ({ directory: rootDirectory, expressApp }) => {
|
||||
const dAct = Logger.success(defaultsActivity, "Defaults initialized") || {}
|
||||
track("DEFAULTS_INIT_COMPLETED", { duration: dAct.duration })
|
||||
|
||||
const searchActivity = Logger.activity("Initializing search engine indexing")
|
||||
track("SEARCH_ENGINE_INDEXING_STARTED")
|
||||
searchIndexLoader({ container, activityId: searchActivity })
|
||||
const searchAct = Logger.success(searchActivity, "Indexing completed") || {}
|
||||
track("SEARCH_ENGINE_INDEXING_COMPLETED", { duration: searchAct.duration })
|
||||
|
||||
return { container, dbConnection, app: expressApp }
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
NotificationService,
|
||||
FileService,
|
||||
OauthService,
|
||||
SearchService,
|
||||
} from "medusa-interfaces"
|
||||
import { getConfigFile, createRequireFromPath } from "medusa-core-utils"
|
||||
import _ from "lodash"
|
||||
@@ -249,7 +250,7 @@ async function registerServices(pluginDetails, container) {
|
||||
)
|
||||
|
||||
// Add the service directly to the container in order to make simple
|
||||
// resolution if we already know which payment provider we need to use
|
||||
// resolution if we already know which fulfillment provider we need to use
|
||||
container.register({
|
||||
[name]: asFunction(
|
||||
cradle => new loaded(cradle, pluginDetails.options)
|
||||
@@ -263,7 +264,7 @@ async function registerServices(pluginDetails, container) {
|
||||
)
|
||||
|
||||
// Add the service directly to the container in order to make simple
|
||||
// resolution if we already know which payment provider we need to use
|
||||
// resolution if we already know which notification provider we need to use
|
||||
container.register({
|
||||
[name]: asFunction(
|
||||
cradle => new loaded(cradle, pluginDetails.options)
|
||||
@@ -272,13 +273,22 @@ async function registerServices(pluginDetails, container) {
|
||||
})
|
||||
} else if (loaded.prototype instanceof FileService) {
|
||||
// Add the service directly to the container in order to make simple
|
||||
// resolution if we already know which payment provider we need to use
|
||||
// resolution if we already know which file storage provider we need to use
|
||||
container.register({
|
||||
[name]: asFunction(
|
||||
cradle => new loaded(cradle, pluginDetails.options)
|
||||
),
|
||||
[`fileService`]: aliasTo(name),
|
||||
})
|
||||
} else if (loaded.prototype instanceof SearchService) {
|
||||
// Add the service directly to the container in order to make simple
|
||||
// resolution if we already know which search provider we need to use
|
||||
container.register({
|
||||
[name]: asFunction(
|
||||
cradle => new loaded(cradle, pluginDetails.options)
|
||||
),
|
||||
[`searchService`]: aliasTo(name),
|
||||
})
|
||||
} else {
|
||||
container.register({
|
||||
[name]: asFunction(
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
import ProductService from "../services/product"
|
||||
import { indexTypes } from "medusa-core-utils"
|
||||
|
||||
async function loadProductsIntoSearchEngine(container) {
|
||||
const searchService = container.resolve("searchService")
|
||||
const productService = container.resolve("productService")
|
||||
|
||||
const TAKE = 20
|
||||
let hasMore = true
|
||||
|
||||
let lastSeenId = ""
|
||||
|
||||
while (hasMore) {
|
||||
const products = await productService.list(
|
||||
{ id: { gt: lastSeenId } },
|
||||
{
|
||||
select: [
|
||||
"id",
|
||||
"title",
|
||||
"subtitle",
|
||||
"description",
|
||||
"handle",
|
||||
"is_giftcard",
|
||||
"discountable",
|
||||
"thumbnail",
|
||||
"profile_id",
|
||||
"collection_id",
|
||||
"type_id",
|
||||
"origin_country",
|
||||
"created_at",
|
||||
"updated_at",
|
||||
],
|
||||
relations: [
|
||||
"variants",
|
||||
"tags",
|
||||
"type",
|
||||
"collection",
|
||||
"variants.prices",
|
||||
"variants.options",
|
||||
"options",
|
||||
],
|
||||
take: TAKE,
|
||||
order: { id: "ASC" },
|
||||
}
|
||||
)
|
||||
|
||||
if (products.length > 0) {
|
||||
await searchService.addDocuments(
|
||||
ProductService.IndexName,
|
||||
products,
|
||||
indexTypes.products
|
||||
)
|
||||
lastSeenId = products[products.length - 1].id
|
||||
} else {
|
||||
hasMore = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default async ({ container }) => {
|
||||
const searchService = container.resolve("searchService")
|
||||
const logger = container.resolve("logger")
|
||||
if (searchService.isDefault) {
|
||||
logger.warn(
|
||||
"No search engine provider was found: make sure to include a search plugin to enable searching"
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
await loadProductsIntoSearchEngine(container)
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
import {MigrationInterface, QueryRunner} from "typeorm";
|
||||
|
||||
export class addCustomShippingOptions1633614437919 implements MigrationInterface {
|
||||
name = 'addCustomShippingOptions1633614437919'
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`CREATE TABLE "custom_shipping_option" ("id" character varying NOT NULL, "price" integer NOT NULL, "shipping_option_id" character varying NOT NULL, "cart_id" character varying, "created_at" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "updated_at" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "deleted_at" TIMESTAMP WITH TIME ZONE, "metadata" jsonb, CONSTRAINT "UQ_0f838b122a9a01d921aa1cdb669" UNIQUE ("shipping_option_id", "cart_id"), CONSTRAINT "PK_8dfcb5c1172c29eec4a728420cc" PRIMARY KEY ("id"))`);
|
||||
await queryRunner.query(`CREATE INDEX "IDX_44090cb11b06174cbcc667e91c" ON "custom_shipping_option" ("shipping_option_id") `);
|
||||
await queryRunner.query(`CREATE INDEX "IDX_93caeb1bb70d37c1d36d6701a7" ON "custom_shipping_option" ("cart_id") `);
|
||||
await queryRunner.query(`ALTER TYPE "cart_type_enum" RENAME TO "cart_type_enum_old"`);
|
||||
await queryRunner.query(`CREATE TYPE "cart_type_enum" AS ENUM('default', 'swap', 'draft_order', 'payment_link', 'claim')`);
|
||||
await queryRunner.query(`ALTER TABLE "cart" ALTER COLUMN "type" DROP DEFAULT`);
|
||||
await queryRunner.query(`ALTER TABLE "cart" ALTER COLUMN "type" TYPE "cart_type_enum" USING "type"::"text"::"cart_type_enum"`);
|
||||
await queryRunner.query(`ALTER TABLE "cart" ALTER COLUMN "type" SET DEFAULT 'default'`);
|
||||
await queryRunner.query(`DROP TYPE "cart_type_enum_old"`);
|
||||
await queryRunner.query(`ALTER TABLE "custom_shipping_option" ADD CONSTRAINT "FK_44090cb11b06174cbcc667e91ca" FOREIGN KEY ("shipping_option_id") REFERENCES "shipping_option"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`);
|
||||
await queryRunner.query(`ALTER TABLE "custom_shipping_option" ADD CONSTRAINT "FK_93caeb1bb70d37c1d36d6701a7a" FOREIGN KEY ("cart_id") REFERENCES "cart"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`ALTER TABLE "custom_shipping_option" DROP CONSTRAINT "FK_93caeb1bb70d37c1d36d6701a7a"`);
|
||||
await queryRunner.query(`ALTER TABLE "custom_shipping_option" DROP CONSTRAINT "FK_44090cb11b06174cbcc667e91ca"`);
|
||||
await queryRunner.query(`CREATE TYPE "cart_type_enum_old" AS ENUM('default', 'swap', 'draft_order', 'payment_link')`);
|
||||
await queryRunner.query(`ALTER TABLE "cart" ALTER COLUMN "type" DROP DEFAULT`);
|
||||
await queryRunner.query(`ALTER TABLE "cart" ALTER COLUMN "type" TYPE "cart_type_enum_old" USING "type"::"text"::"cart_type_enum_old"`);
|
||||
await queryRunner.query(`ALTER TABLE "cart" ALTER COLUMN "type" SET DEFAULT 'default'`);
|
||||
await queryRunner.query(`DROP TYPE "cart_type_enum"`);
|
||||
await queryRunner.query(`ALTER TYPE "cart_type_enum_old" RENAME TO "cart_type_enum"`);
|
||||
await queryRunner.query(`DROP INDEX "IDX_93caeb1bb70d37c1d36d6701a7"`);
|
||||
await queryRunner.query(`DROP INDEX "IDX_44090cb11b06174cbcc667e91c"`);
|
||||
await queryRunner.query(`DROP TABLE "custom_shipping_option"`);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -113,12 +113,14 @@ import { PaymentSession } from "./payment-session"
|
||||
import { Payment } from "./payment"
|
||||
import { GiftCard } from "./gift-card"
|
||||
import { ShippingMethod } from "./shipping-method"
|
||||
import { CustomShippingOption } from "./custom-shipping-option"
|
||||
|
||||
export enum CartType {
|
||||
DEFAULT = "default",
|
||||
SWAP = "swap",
|
||||
DRAFT_ORDER = "draft_order",
|
||||
PAYMENT_LINK = "payment_link",
|
||||
CLAIM = "claim",
|
||||
}
|
||||
|
||||
@Entity()
|
||||
@@ -149,11 +151,9 @@ export class Cart {
|
||||
@JoinColumn({ name: "shipping_address_id" })
|
||||
shipping_address: Address
|
||||
|
||||
@OneToMany(
|
||||
() => LineItem,
|
||||
lineItem => lineItem.cart,
|
||||
{ cascade: ["insert", "remove"] }
|
||||
)
|
||||
@OneToMany(() => LineItem, (lineItem) => lineItem.cart, {
|
||||
cascade: ["insert", "remove"],
|
||||
})
|
||||
items: LineItem[]
|
||||
|
||||
@Index()
|
||||
@@ -202,11 +202,9 @@ export class Cart {
|
||||
|
||||
payment_session: PaymentSession
|
||||
|
||||
@OneToMany(
|
||||
() => PaymentSession,
|
||||
paymentSession => paymentSession.cart,
|
||||
{ cascade: true }
|
||||
)
|
||||
@OneToMany(() => PaymentSession, (paymentSession) => paymentSession.cart, {
|
||||
cascade: true,
|
||||
})
|
||||
payment_sessions: PaymentSession[]
|
||||
|
||||
@Index()
|
||||
@@ -217,11 +215,9 @@ export class Cart {
|
||||
@JoinColumn({ name: "payment_id" })
|
||||
payment: Payment
|
||||
|
||||
@OneToMany(
|
||||
() => ShippingMethod,
|
||||
method => method.cart,
|
||||
{ cascade: ["soft-remove", "remove"] }
|
||||
)
|
||||
@OneToMany(() => ShippingMethod, (method) => method.cart, {
|
||||
cascade: ["soft-remove", "remove"],
|
||||
})
|
||||
shipping_methods: ShippingMethod[]
|
||||
|
||||
@DbAwareColumn({ type: "enum", enum: CartType, default: "default" })
|
||||
@@ -271,7 +267,7 @@ export class Cart {
|
||||
@AfterLoad()
|
||||
private afterLoad() {
|
||||
if (this.payment_sessions) {
|
||||
this.payment_session = this.payment_sessions.find(p => p.is_selected)
|
||||
this.payment_session = this.payment_sessions.find((p) => p.is_selected)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
import {
|
||||
BeforeInsert, Column,
|
||||
CreateDateColumn,
|
||||
DeleteDateColumn,
|
||||
Entity,
|
||||
Index,
|
||||
JoinColumn,
|
||||
ManyToOne,
|
||||
PrimaryColumn,
|
||||
Unique,
|
||||
UpdateDateColumn
|
||||
} from "typeorm";
|
||||
import { ulid } from "ulid";
|
||||
import { DbAwareColumn, resolveDbType } from "../utils/db-aware-column";
|
||||
import { Cart } from './cart';
|
||||
import { ShippingOption } from "./shipping-option";
|
||||
|
||||
|
||||
@Entity()
|
||||
@Unique(['shipping_option_id', 'cart_id'])
|
||||
export class CustomShippingOption {
|
||||
@PrimaryColumn()
|
||||
id: string
|
||||
|
||||
@Column({ type: "int" })
|
||||
price: number
|
||||
|
||||
@Index()
|
||||
@Column()
|
||||
shipping_option_id: string;
|
||||
|
||||
@ManyToOne(() => ShippingOption)
|
||||
@JoinColumn({ name: "shipping_option_id" })
|
||||
shipping_option: ShippingOption
|
||||
|
||||
@Index()
|
||||
@Column({ nullable: true })
|
||||
cart_id: string
|
||||
|
||||
@ManyToOne(() => Cart)
|
||||
@JoinColumn({ name: "cart_id" })
|
||||
cart: Cart
|
||||
|
||||
@CreateDateColumn({ type: resolveDbType("timestamptz") })
|
||||
created_at: Date
|
||||
|
||||
@UpdateDateColumn({ type: resolveDbType("timestamptz") })
|
||||
updated_at: Date
|
||||
|
||||
@DeleteDateColumn({ type: resolveDbType("timestamptz") })
|
||||
deleted_at: Date
|
||||
|
||||
@DbAwareColumn({ type: "jsonb", nullable: true })
|
||||
metadata: any
|
||||
|
||||
@BeforeInsert()
|
||||
private beforeInsert() {
|
||||
if (this.id) return
|
||||
const id = ulid()
|
||||
this.id = `cso_${id}`
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @schema Custom shipping_option
|
||||
* title: "Custom Shipping Option"
|
||||
* description: "Custom Shipping Options are 'overriden' Shipping Options. Store managers can attach a Custom Shipping Option to a cart in order to set a custom price for a particular Shipping Option"
|
||||
* x-resourceId: custom_shipping_option
|
||||
* properties:
|
||||
* id:
|
||||
* description: "The id of the Custom Shipping Option. This value will be prefixed with `cso_`."
|
||||
* type: string
|
||||
* price:
|
||||
* description: "The custom price set that will override the shipping option's original price"
|
||||
* type: integer
|
||||
* shipping_option_id:
|
||||
* description: "The id of the Shipping Option that the custom shipping option overrides"
|
||||
* anyOf:
|
||||
* - $ref: "#/components/schemas/shipping_option"
|
||||
* cart_id:
|
||||
* description: "The id of the Cart that the custom shipping option is attached to"
|
||||
* anyOf:
|
||||
* - $ref: "#/components/schemas/cart"
|
||||
* created_at:
|
||||
* description: "The date with timezone at which the resource was created."
|
||||
* type: string
|
||||
* format: date-time
|
||||
* updated_at:
|
||||
* description: "The date with timezone at which the resource was last updated."
|
||||
* type: string
|
||||
* format: date-time
|
||||
* deleted_at:
|
||||
* description: "The date with timezone at which the resource was deleted."
|
||||
* type: string
|
||||
* format: date-time
|
||||
* metadata:
|
||||
* description: "An optional key-value map with additional information."
|
||||
* type: object
|
||||
*/
|
||||
@@ -35,7 +35,7 @@ export class Discount {
|
||||
@Column({ nullable: true })
|
||||
rule_id: string
|
||||
|
||||
@ManyToOne(() => DiscountRule, { cascade: true, eager: true })
|
||||
@ManyToOne(() => DiscountRule, { cascade: true })
|
||||
@JoinColumn({ name: "rule_id" })
|
||||
rule: DiscountRule
|
||||
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
import { EntityRepository, Repository } from "typeorm"
|
||||
import { CustomShippingOption } from './../models/custom-shipping-option';
|
||||
|
||||
@EntityRepository(CustomShippingOption)
|
||||
export class CustomShippingOptionRepository extends Repository<CustomShippingOption> {}
|
||||
@@ -34,7 +34,7 @@ export class OrderRepository extends Repository<Order> {
|
||||
|
||||
const entitiesAndRelationsById = groupBy(entitiesAndRelations, "id")
|
||||
|
||||
return map(entities, e => merge({}, ...entitiesAndRelationsById[e.id]))
|
||||
return map(entities, (e) => merge({}, ...entitiesAndRelationsById[e.id]))
|
||||
}
|
||||
|
||||
public async findOneWithRelations(
|
||||
|
||||
@@ -1,21 +1,52 @@
|
||||
import { flatten, groupBy, map, merge } from "lodash"
|
||||
import { EntityRepository, FindManyOptions, Repository } from "typeorm"
|
||||
import {
|
||||
OrderByCondition,
|
||||
EntityRepository,
|
||||
FindManyOptions,
|
||||
Repository,
|
||||
} from "typeorm"
|
||||
import { Product } from "../models/product"
|
||||
|
||||
type DefaultWithoutRelations = Omit<FindManyOptions<Product>, "relations">
|
||||
|
||||
type CustomOptions = {
|
||||
where?: DefaultWithoutRelations["where"] & { tags?: string[] }
|
||||
order?: OrderByCondition
|
||||
skip?: number
|
||||
take?: number
|
||||
}
|
||||
|
||||
type FindWithRelationsOptions = CustomOptions
|
||||
|
||||
@EntityRepository(Product)
|
||||
export class ProductRepository extends Repository<Product> {
|
||||
public async findWithRelations(
|
||||
relations: Array<keyof Product> = [],
|
||||
idsOrOptionsWithoutRelations: Omit<
|
||||
FindManyOptions<Product>,
|
||||
"relations"
|
||||
> = {}
|
||||
idsOrOptionsWithoutRelations: FindWithRelationsOptions = {}
|
||||
): Promise<Product[]> {
|
||||
let entities
|
||||
let entities: Product[]
|
||||
if (Array.isArray(idsOrOptionsWithoutRelations)) {
|
||||
entities = await this.findByIds(idsOrOptionsWithoutRelations)
|
||||
} else {
|
||||
entities = await this.find(idsOrOptionsWithoutRelations)
|
||||
// Since tags are in a one-to-many realtion they cant be included in a
|
||||
// regular query, to solve this add the join on tags seperately if
|
||||
// the query exists
|
||||
const tags = idsOrOptionsWithoutRelations.where.tags
|
||||
delete idsOrOptionsWithoutRelations.where.tags
|
||||
let qb = this.createQueryBuilder("product")
|
||||
.select(["product.id"])
|
||||
.where(idsOrOptionsWithoutRelations.where)
|
||||
.skip(idsOrOptionsWithoutRelations.skip)
|
||||
.take(idsOrOptionsWithoutRelations.take)
|
||||
.orderBy(idsOrOptionsWithoutRelations.order)
|
||||
|
||||
if (tags) {
|
||||
qb = qb
|
||||
.leftJoinAndSelect("product.tags", "tags")
|
||||
.andWhere(`tags.id IN (:...ids)`, { ids: tags._value })
|
||||
}
|
||||
|
||||
entities = await qb.getMany()
|
||||
}
|
||||
const entitiesIds = entities.map(({ id }) => id)
|
||||
|
||||
|
||||
@@ -6,29 +6,29 @@ import { MedusaError } from "medusa-core-utils"
|
||||
|
||||
const eventBusService = {
|
||||
emit: jest.fn(),
|
||||
withTransaction: function() {
|
||||
withTransaction: function () {
|
||||
return this
|
||||
},
|
||||
}
|
||||
|
||||
describe("CartService", () => {
|
||||
const totalsService = {
|
||||
getTotal: o => {
|
||||
getTotal: (o) => {
|
||||
return o.total || 0
|
||||
},
|
||||
getSubtotal: o => {
|
||||
getSubtotal: (o) => {
|
||||
return o.subtotal || 0
|
||||
},
|
||||
getTaxTotal: o => {
|
||||
getTaxTotal: (o) => {
|
||||
return o.tax_total || 0
|
||||
},
|
||||
getDiscountTotal: o => {
|
||||
getDiscountTotal: (o) => {
|
||||
return o.discount_total || 0
|
||||
},
|
||||
getShippingTotal: o => {
|
||||
getShippingTotal: (o) => {
|
||||
return o.shipping_total || 0
|
||||
},
|
||||
getGiftCardTotal: o => {
|
||||
getGiftCardTotal: (o) => {
|
||||
return o.gift_card_total || 0
|
||||
},
|
||||
}
|
||||
@@ -117,7 +117,7 @@ describe("CartService", () => {
|
||||
|
||||
describe("deleteMetadata", () => {
|
||||
const cartRepository = MockRepository({
|
||||
findOne: id => {
|
||||
findOne: (id) => {
|
||||
if (id === "empty") {
|
||||
return Promise.resolve({
|
||||
metadata: {},
|
||||
@@ -200,7 +200,7 @@ describe("CartService", () => {
|
||||
},
|
||||
}
|
||||
|
||||
const addressRepository = MockRepository({ create: c => c })
|
||||
const addressRepository = MockRepository({ create: (c) => c })
|
||||
const cartRepository = MockRepository()
|
||||
const customerService = {
|
||||
retrieveByEmail: jest.fn().mockReturnValue(
|
||||
@@ -209,7 +209,7 @@ describe("CartService", () => {
|
||||
email: "email@test.com",
|
||||
})
|
||||
),
|
||||
withTransaction: function() {
|
||||
withTransaction: function () {
|
||||
return this
|
||||
},
|
||||
}
|
||||
@@ -317,14 +317,14 @@ describe("CartService", () => {
|
||||
const lineItemService = {
|
||||
update: jest.fn(),
|
||||
create: jest.fn(),
|
||||
withTransaction: function() {
|
||||
withTransaction: function () {
|
||||
return this
|
||||
},
|
||||
}
|
||||
|
||||
const shippingOptionService = {
|
||||
deleteShippingMethod: jest.fn(),
|
||||
withTransaction: function() {
|
||||
withTransaction: function () {
|
||||
return this
|
||||
},
|
||||
}
|
||||
@@ -505,7 +505,7 @@ describe("CartService", () => {
|
||||
const lineItemService = {
|
||||
delete: jest.fn(),
|
||||
update: jest.fn(),
|
||||
withTransaction: function() {
|
||||
withTransaction: function () {
|
||||
return this
|
||||
},
|
||||
}
|
||||
@@ -547,7 +547,7 @@ describe("CartService", () => {
|
||||
|
||||
const shippingOptionService = {
|
||||
deleteShippingMethod: jest.fn(),
|
||||
withTransaction: function() {
|
||||
withTransaction: function () {
|
||||
return this
|
||||
},
|
||||
}
|
||||
@@ -647,12 +647,13 @@ describe("CartService", () => {
|
||||
"shipping_address",
|
||||
"billing_address",
|
||||
"gift_cards",
|
||||
"discounts",
|
||||
"customer",
|
||||
"region",
|
||||
"payment_sessions",
|
||||
"region.countries",
|
||||
"discounts",
|
||||
"discounts.rule",
|
||||
"discounts.rule.valid_for",
|
||||
"discounts.regions",
|
||||
],
|
||||
{
|
||||
@@ -665,7 +666,7 @@ describe("CartService", () => {
|
||||
describe("updateLineItem", () => {
|
||||
const lineItemService = {
|
||||
update: jest.fn(),
|
||||
withTransaction: function() {
|
||||
withTransaction: function () {
|
||||
return this
|
||||
},
|
||||
}
|
||||
@@ -673,7 +674,7 @@ describe("CartService", () => {
|
||||
...InventoryServiceMock,
|
||||
confirmInventory: jest
|
||||
.fn()
|
||||
.mockImplementation(id => id !== IdMap.getId("cannot-cover")),
|
||||
.mockImplementation((id) => id !== IdMap.getId("cannot-cover")),
|
||||
}
|
||||
|
||||
const cartRepository = MockRepository({
|
||||
@@ -746,7 +747,7 @@ describe("CartService", () => {
|
||||
|
||||
describe("updateEmail", () => {
|
||||
const customerService = {
|
||||
retrieveByEmail: jest.fn().mockImplementation(email => {
|
||||
retrieveByEmail: jest.fn().mockImplementation((email) => {
|
||||
if (email === "no@mail.com") {
|
||||
return Promise.reject()
|
||||
}
|
||||
@@ -755,13 +756,13 @@ describe("CartService", () => {
|
||||
email,
|
||||
})
|
||||
}),
|
||||
create: jest.fn().mockImplementation(data =>
|
||||
create: jest.fn().mockImplementation((data) =>
|
||||
Promise.resolve({
|
||||
id: IdMap.getId("newCus"),
|
||||
email: data.email,
|
||||
})
|
||||
),
|
||||
withTransaction: function() {
|
||||
withTransaction: function () {
|
||||
return this
|
||||
},
|
||||
}
|
||||
@@ -846,7 +847,7 @@ describe("CartService", () => {
|
||||
}),
|
||||
})
|
||||
|
||||
const addressRepository = MockRepository({ create: c => c })
|
||||
const addressRepository = MockRepository({ create: (c) => c })
|
||||
|
||||
const cartService = new CartService({
|
||||
manager: MockManager,
|
||||
@@ -907,7 +908,7 @@ describe("CartService", () => {
|
||||
region: { countries: [{ iso_2: "us" }] },
|
||||
}),
|
||||
})
|
||||
const addressRepository = MockRepository({ create: c => c })
|
||||
const addressRepository = MockRepository({ create: (c) => c })
|
||||
|
||||
const cartService = new CartService({
|
||||
manager: MockManager,
|
||||
@@ -981,15 +982,15 @@ describe("CartService", () => {
|
||||
|
||||
describe("setRegion", () => {
|
||||
const lineItemService = {
|
||||
update: jest.fn(r => r),
|
||||
update: jest.fn((r) => r),
|
||||
delete: jest.fn(),
|
||||
withTransaction: function() {
|
||||
withTransaction: function () {
|
||||
return this
|
||||
},
|
||||
}
|
||||
const addressRepository = MockRepository({ create: c => c })
|
||||
const addressRepository = MockRepository({ create: (c) => c })
|
||||
const productVariantService = {
|
||||
getRegionPrice: jest.fn().mockImplementation(id => {
|
||||
getRegionPrice: jest.fn().mockImplementation((id) => {
|
||||
if (id === IdMap.getId("fail")) {
|
||||
return Promise.reject()
|
||||
}
|
||||
@@ -1033,7 +1034,7 @@ describe("CartService", () => {
|
||||
deleteSession: jest.fn(),
|
||||
updateSession: jest.fn(),
|
||||
createSession: jest.fn(),
|
||||
withTransaction: function() {
|
||||
withTransaction: function () {
|
||||
return this
|
||||
},
|
||||
}
|
||||
@@ -1237,7 +1238,7 @@ describe("CartService", () => {
|
||||
deleteSession: jest.fn(),
|
||||
updateSession: jest.fn(),
|
||||
createSession: jest.fn(),
|
||||
withTransaction: function() {
|
||||
withTransaction: function () {
|
||||
return this
|
||||
},
|
||||
}
|
||||
@@ -1308,11 +1309,50 @@ describe("CartService", () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe("findCustomShippingOption", () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks()
|
||||
})
|
||||
|
||||
let cartService = new CartService({})
|
||||
|
||||
it("given a cart with custom shipping options and a shipping option id corresponding to a custom shipping option, then it should return a custom shipping option", async () => {
|
||||
const cartCSO = [
|
||||
{ id: "cso-test", shipping_option_id: "test-so", price: 20 },
|
||||
]
|
||||
const result = cartService.findCustomShippingOption(cartCSO, "test-so")
|
||||
|
||||
expect(result).toEqual({
|
||||
id: "cso-test",
|
||||
shipping_option_id: "test-so",
|
||||
price: 20,
|
||||
})
|
||||
})
|
||||
|
||||
it("given a cart with empty custom shipping options and shipping option id, then it should return undefined", async () => {
|
||||
const cartCSO = []
|
||||
|
||||
const result = cartService.findCustomShippingOption(cartCSO, "test-so")
|
||||
|
||||
expect(result).toBeUndefined()
|
||||
})
|
||||
|
||||
it("given a cart with custom shipping options and a shipping option id that does not belong to the cart, then it should throw an invalid error", async () => {
|
||||
const cartCSO = [
|
||||
{ id: "cso-test", shipping_option_id: "test-so", price: 500 },
|
||||
]
|
||||
|
||||
expect(() => {
|
||||
cartService.findCustomShippingOption(cartCSO, "some-other-so")
|
||||
}).toThrow(MedusaError)
|
||||
})
|
||||
})
|
||||
|
||||
describe("addShippingMethod", () => {
|
||||
const buildCart = (id, config = {}) => {
|
||||
return {
|
||||
id: IdMap.getId(id),
|
||||
items: (config.items || []).map(i => ({
|
||||
items: (config.items || []).map((i) => ({
|
||||
id: IdMap.getId(i.id),
|
||||
variant: {
|
||||
product: {
|
||||
@@ -1320,7 +1360,7 @@ describe("CartService", () => {
|
||||
},
|
||||
},
|
||||
})),
|
||||
shipping_methods: (config.shipping_methods || []).map(m => ({
|
||||
shipping_methods: (config.shipping_methods || []).map((m) => ({
|
||||
id: IdMap.getId(m.id),
|
||||
shipping_option: {
|
||||
profile_id: IdMap.getId(m.profile),
|
||||
@@ -1337,6 +1377,7 @@ describe("CartService", () => {
|
||||
const cart3 = buildCart("lines", {
|
||||
items: [{ id: "line", profile: "profile1" }],
|
||||
})
|
||||
const cartWithCustomSO = buildCart("cart-with-custom-so")
|
||||
|
||||
const cartRepository = MockRepository({
|
||||
findOneWithRelations: (rels, q) => {
|
||||
@@ -1345,6 +1386,8 @@ describe("CartService", () => {
|
||||
return Promise.resolve(cart3)
|
||||
case IdMap.getId("existing"):
|
||||
return Promise.resolve(cart2)
|
||||
case IdMap.getId("cart-with-custom-so"):
|
||||
return Promise.resolve(cartWithCustomSO)
|
||||
default:
|
||||
return Promise.resolve(cart1)
|
||||
}
|
||||
@@ -1353,12 +1396,12 @@ describe("CartService", () => {
|
||||
|
||||
const lineItemService = {
|
||||
update: jest.fn(),
|
||||
withTransaction: function() {
|
||||
withTransaction: function () {
|
||||
return this
|
||||
},
|
||||
}
|
||||
const shippingOptionService = {
|
||||
createShippingMethod: jest.fn().mockImplementation(id => {
|
||||
createShippingMethod: jest.fn().mockImplementation((id) => {
|
||||
return Promise.resolve({
|
||||
shipping_option: {
|
||||
profile_id: id,
|
||||
@@ -1366,11 +1409,25 @@ describe("CartService", () => {
|
||||
})
|
||||
}),
|
||||
deleteShippingMethod: jest.fn(),
|
||||
withTransaction: function() {
|
||||
withTransaction: function () {
|
||||
return this
|
||||
},
|
||||
}
|
||||
|
||||
const customShippingOptionService = {
|
||||
list: jest.fn().mockImplementation(({ cart_id }) => {
|
||||
if (cart_id === IdMap.getId("cart-with-custom-so")) {
|
||||
return [
|
||||
{
|
||||
id: "cso-test",
|
||||
shipping_profile_id: "test-so",
|
||||
cart_id: IdMap.getId("cart-with-custom-so"),
|
||||
},
|
||||
]
|
||||
}
|
||||
}),
|
||||
}
|
||||
|
||||
const cartService = new CartService({
|
||||
manager: MockManager,
|
||||
totalsService,
|
||||
@@ -1378,6 +1435,7 @@ describe("CartService", () => {
|
||||
shippingOptionService,
|
||||
lineItemService,
|
||||
eventBusService,
|
||||
customShippingOptionService,
|
||||
})
|
||||
|
||||
beforeEach(() => {
|
||||
@@ -1395,9 +1453,11 @@ describe("CartService", () => {
|
||||
IdMap.getId("option"),
|
||||
data
|
||||
)
|
||||
expect(
|
||||
shippingOptionService.createShippingMethod
|
||||
).toHaveBeenCalledWith(IdMap.getId("option"), data, { cart: cart1 })
|
||||
expect(shippingOptionService.createShippingMethod).toHaveBeenCalledWith(
|
||||
IdMap.getId("option"),
|
||||
data,
|
||||
{ cart: cart1 }
|
||||
)
|
||||
})
|
||||
|
||||
it("successfully overrides existing profile shipping method", async () => {
|
||||
@@ -1409,9 +1469,11 @@ describe("CartService", () => {
|
||||
IdMap.getId("profile1"),
|
||||
data
|
||||
)
|
||||
expect(
|
||||
shippingOptionService.createShippingMethod
|
||||
).toHaveBeenCalledWith(IdMap.getId("profile1"), data, { cart: cart2 })
|
||||
expect(shippingOptionService.createShippingMethod).toHaveBeenCalledWith(
|
||||
IdMap.getId("profile1"),
|
||||
data,
|
||||
{ cart: cart2 }
|
||||
)
|
||||
expect(shippingOptionService.deleteShippingMethod).toHaveBeenCalledWith({
|
||||
id: IdMap.getId("ship1"),
|
||||
shipping_option: {
|
||||
@@ -1437,9 +1499,11 @@ describe("CartService", () => {
|
||||
expect(shippingOptionService.createShippingMethod).toHaveBeenCalledTimes(
|
||||
1
|
||||
)
|
||||
expect(
|
||||
shippingOptionService.createShippingMethod
|
||||
).toHaveBeenCalledWith(IdMap.getId("additional"), data, { cart: cart2 })
|
||||
expect(shippingOptionService.createShippingMethod).toHaveBeenCalledWith(
|
||||
IdMap.getId("additional"),
|
||||
data,
|
||||
{ cart: cart2 }
|
||||
)
|
||||
})
|
||||
|
||||
it("updates item shipping", async () => {
|
||||
@@ -1459,19 +1523,50 @@ describe("CartService", () => {
|
||||
expect(shippingOptionService.createShippingMethod).toHaveBeenCalledTimes(
|
||||
1
|
||||
)
|
||||
expect(
|
||||
shippingOptionService.createShippingMethod
|
||||
).toHaveBeenCalledWith(IdMap.getId("profile1"), data, { cart: cart3 })
|
||||
expect(shippingOptionService.createShippingMethod).toHaveBeenCalledWith(
|
||||
IdMap.getId("profile1"),
|
||||
data,
|
||||
{ cart: cart3 }
|
||||
)
|
||||
|
||||
expect(lineItemService.update).toHaveBeenCalledTimes(1)
|
||||
expect(lineItemService.update).toHaveBeenCalledWith(IdMap.getId("line"), {
|
||||
has_shipping: true,
|
||||
})
|
||||
})
|
||||
|
||||
it("successfully adds a shipping method from a custom shipping option and custom price", async () => {
|
||||
const data = {
|
||||
id: "test",
|
||||
extra: "yes",
|
||||
}
|
||||
|
||||
cartService.findCustomShippingOption = jest
|
||||
.fn()
|
||||
.mockImplementation((cartCustomShippingOptions) => {
|
||||
return {
|
||||
price: 0,
|
||||
}
|
||||
})
|
||||
|
||||
await cartService.addShippingMethod(
|
||||
IdMap.getId("cart-with-custom-so"),
|
||||
IdMap.getId("test-so"),
|
||||
data
|
||||
)
|
||||
expect(shippingOptionService.createShippingMethod).toHaveBeenCalledWith(
|
||||
IdMap.getId("test-so"),
|
||||
data,
|
||||
{
|
||||
cart_id: IdMap.getId("cart-with-custom-so"),
|
||||
price: 0,
|
||||
}
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe("applyDiscount", () => {
|
||||
const getOffsetDate = offset => {
|
||||
const getOffsetDate = (offset) => {
|
||||
const date = new Date()
|
||||
date.setDate(date.getDate() + offset)
|
||||
return date
|
||||
@@ -1508,7 +1603,7 @@ describe("CartService", () => {
|
||||
})
|
||||
|
||||
const discountService = {
|
||||
retrieveByCode: jest.fn().mockImplementation(code => {
|
||||
retrieveByCode: jest.fn().mockImplementation((code) => {
|
||||
if (code === "US10") {
|
||||
return Promise.resolve({
|
||||
regions: [{ id: IdMap.getId("bad") }],
|
||||
|
||||
@@ -0,0 +1,130 @@
|
||||
import CustomShippingOptionService from "../custom-shipping-option"
|
||||
import { MockManager, MockRepository, IdMap } from "medusa-test-utils"
|
||||
|
||||
describe("CustomShippingOptionService", () => {
|
||||
describe("list", () => {
|
||||
const customShippingOptionRepository = MockRepository({
|
||||
find: q => {
|
||||
return Promise.resolve([
|
||||
{
|
||||
id: "cso-test",
|
||||
shipping_option_id: "test-so",
|
||||
price: 0,
|
||||
cart_id: "test-cso-cart",
|
||||
},
|
||||
])
|
||||
},
|
||||
})
|
||||
|
||||
const customShippingOptionService = new CustomShippingOptionService({
|
||||
manager: MockManager,
|
||||
customShippingOptionRepository,
|
||||
})
|
||||
|
||||
beforeAll(async () => {
|
||||
jest.clearAllMocks()
|
||||
})
|
||||
|
||||
it("calls customShippingOptionRepository find method", async () => {
|
||||
await customShippingOptionService.list(
|
||||
{ cart_id: "test-cso-cart" },
|
||||
{
|
||||
relations: ["shipping_option"],
|
||||
}
|
||||
)
|
||||
expect(customShippingOptionRepository.find).toHaveBeenCalledTimes(1)
|
||||
expect(customShippingOptionRepository.find).toHaveBeenCalledWith({
|
||||
where: {
|
||||
cart_id: "test-cso-cart",
|
||||
},
|
||||
relations: ["shipping_option"],
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe("retrieve", () => {
|
||||
const customShippingOptionRepository = MockRepository({
|
||||
findOne: q => {
|
||||
if (q.where.id === "cso-test") {
|
||||
return Promise.resolve({
|
||||
id: "cso-test",
|
||||
shipping_option_id: "test-so",
|
||||
price: 0,
|
||||
cart_id: "test-cso-cart",
|
||||
})
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
const customShippingOptionService = new CustomShippingOptionService({
|
||||
manager: MockManager,
|
||||
customShippingOptionRepository,
|
||||
})
|
||||
|
||||
beforeAll(async () => {
|
||||
jest.clearAllMocks()
|
||||
})
|
||||
|
||||
it("calls customShippingOptionRepository findOne method", async () => {
|
||||
await customShippingOptionService.retrieve("cso-test", {
|
||||
relations: ["shipping_option", "cart"],
|
||||
})
|
||||
|
||||
expect(customShippingOptionRepository.findOne).toHaveBeenCalledTimes(1)
|
||||
expect(customShippingOptionRepository.findOne).toHaveBeenCalledWith({
|
||||
where: { id: "cso-test" },
|
||||
relations: ["shipping_option", "cart"],
|
||||
})
|
||||
})
|
||||
|
||||
it("fails when custom shipping option is not found", async () => {
|
||||
expect(customShippingOptionService.retrieve("bad-cso")).rejects.toThrow(
|
||||
`Custom shipping option with id: bad-cso was not found.`
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe("create", () => {
|
||||
const customShippingOptionRepository = MockRepository({
|
||||
create: jest
|
||||
.fn()
|
||||
.mockImplementation(f => Promise.resolve({ id: "test-cso", ...f })),
|
||||
save: jest.fn().mockImplementation(f => Promise.resolve(f)),
|
||||
})
|
||||
|
||||
const customShippingOptionService = new CustomShippingOptionService({
|
||||
manager: MockManager,
|
||||
customShippingOptionRepository,
|
||||
})
|
||||
|
||||
beforeAll(async () => {
|
||||
jest.clearAllMocks()
|
||||
})
|
||||
|
||||
it("calls customShippingOptionRepository create method", async () => {
|
||||
const customShippingOption = {
|
||||
cart_id: "test-cso-cart",
|
||||
shipping_option_id: "test-so",
|
||||
price: 30,
|
||||
}
|
||||
await customShippingOptionService.create(customShippingOption)
|
||||
|
||||
expect(customShippingOptionRepository.create).toHaveBeenCalledTimes(1)
|
||||
expect(customShippingOptionRepository.create).toHaveBeenCalledWith({
|
||||
cart_id: "test-cso-cart",
|
||||
shipping_option_id: "test-so",
|
||||
price: 30,
|
||||
metadata: {},
|
||||
})
|
||||
|
||||
expect(customShippingOptionRepository.save).toHaveBeenCalledTimes(1)
|
||||
expect(customShippingOptionRepository.save).toHaveBeenCalledWith({
|
||||
id: "test-cso",
|
||||
cart_id: "test-cso-cart",
|
||||
shipping_option_id: "test-so",
|
||||
price: 30,
|
||||
metadata: {},
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -13,6 +13,9 @@ describe("DiscountService", () => {
|
||||
id: IdMap.getId("france"),
|
||||
}
|
||||
},
|
||||
withTransaction: function() {
|
||||
return this
|
||||
},
|
||||
}
|
||||
|
||||
const discountService = new DiscountService({
|
||||
|
||||
@@ -231,6 +231,8 @@ describe("OrderService", () => {
|
||||
"payment",
|
||||
"items",
|
||||
"discounts",
|
||||
"discounts.rule",
|
||||
"discounts.rule.valid_for",
|
||||
"gift_cards",
|
||||
"shipping_methods",
|
||||
],
|
||||
|
||||
@@ -802,6 +802,7 @@ describe("ProductVariantService", () => {
|
||||
}
|
||||
return Promise.resolve({
|
||||
id: IdMap.getId("ironman"),
|
||||
product_id: IdMap.getId("product-test"),
|
||||
})
|
||||
},
|
||||
})
|
||||
@@ -820,9 +821,20 @@ describe("ProductVariantService", () => {
|
||||
await productVariantService.delete(IdMap.getId("ironman"))
|
||||
|
||||
expect(productVariantRepository.softRemove).toBeCalledTimes(1)
|
||||
expect(productVariantRepository.softRemove).toBeCalledWith({
|
||||
id: IdMap.getId("ironman"),
|
||||
})
|
||||
expect(productVariantRepository.softRemove).toBeCalledWith(
|
||||
expect.objectContaining({
|
||||
id: IdMap.getId("ironman"),
|
||||
})
|
||||
)
|
||||
|
||||
expect(eventBusService.emit).toHaveBeenCalledTimes(1)
|
||||
expect(eventBusService.emit).toHaveBeenCalledWith(
|
||||
"product-variant.deleted",
|
||||
{
|
||||
id: IdMap.getId("ironman"),
|
||||
product_id: IdMap.getId("product-test"),
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
it("successfully resolves if variant does not exist", async () => {
|
||||
|
||||
@@ -415,7 +415,9 @@ describe("ProductService", () => {
|
||||
|
||||
const productService = new ProductService({
|
||||
manager: MockManager,
|
||||
eventBusService,
|
||||
productRepository,
|
||||
eventBusService,
|
||||
})
|
||||
|
||||
beforeEach(() => {
|
||||
@@ -428,6 +430,11 @@ describe("ProductService", () => {
|
||||
expect(productRepository.softRemove).toBeCalledWith({
|
||||
id: IdMap.getId("ironman"),
|
||||
})
|
||||
|
||||
expect(eventBusService.emit).toBeCalledTimes(1)
|
||||
expect(eventBusService.emit).toBeCalledWith("product.deleted", {
|
||||
id: IdMap.getId("ironman"),
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@@ -176,8 +176,17 @@ describe("ShippingProfileService", () => {
|
||||
})
|
||||
|
||||
const shippingOptionService = {
|
||||
list: jest.fn().mockImplementation(() =>
|
||||
Promise.resolve([
|
||||
list: jest.fn().mockImplementation(({ id }) => {
|
||||
if (id && id.includes("test-option")) {
|
||||
return Promise.resolve([
|
||||
{
|
||||
id: "test-option",
|
||||
amount: 1000,
|
||||
name: "Test option",
|
||||
},
|
||||
])
|
||||
}
|
||||
return Promise.resolve([
|
||||
{
|
||||
id: "ship_1",
|
||||
},
|
||||
@@ -185,24 +194,74 @@ describe("ShippingProfileService", () => {
|
||||
id: "ship_2",
|
||||
},
|
||||
])
|
||||
),
|
||||
}),
|
||||
validateCartOption: jest.fn().mockImplementation(s => s),
|
||||
withTransaction: function() {
|
||||
return this
|
||||
},
|
||||
}
|
||||
|
||||
const customShippingOptionService = {
|
||||
list: jest.fn().mockImplementation(({ cart_id }, config) => {
|
||||
if (cart_id === "cso-cart") {
|
||||
return Promise.resolve([
|
||||
{
|
||||
id: "cso_1",
|
||||
cart_id: "cso-cart",
|
||||
shipping_option_id: "test-option",
|
||||
price: 0,
|
||||
},
|
||||
])
|
||||
}
|
||||
return Promise.resolve([])
|
||||
}),
|
||||
}
|
||||
|
||||
const profileService = new ShippingProfileService({
|
||||
manager: MockManager,
|
||||
shippingProfileRepository: profRepo,
|
||||
shippingOptionService,
|
||||
customShippingOptionService,
|
||||
})
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks()
|
||||
})
|
||||
|
||||
it("fetches correct options", async () => {
|
||||
it("given a cart with custom shipping options, should return correct custom shipping options ", async () => {
|
||||
const cart = {
|
||||
id: "cso-cart",
|
||||
items: [
|
||||
{
|
||||
variant: {
|
||||
product: {
|
||||
_id: IdMap.getId("product_1"),
|
||||
profile_id: IdMap.getId("profile"),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
variant: {
|
||||
product: {
|
||||
_id: IdMap.getId("product_2"),
|
||||
profile_id: IdMap.getId("profile"),
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
type: "swap",
|
||||
}
|
||||
|
||||
await expect(profileService.fetchCartOptions(cart)).resolves.toEqual([
|
||||
expect.objectContaining({
|
||||
id: "test-option",
|
||||
amount: 0,
|
||||
name: "Test option",
|
||||
}),
|
||||
])
|
||||
})
|
||||
|
||||
it("given a cart with no custom shipping options, should return normal shipping options", async () => {
|
||||
const cart = {
|
||||
items: [
|
||||
{
|
||||
|
||||
@@ -5,7 +5,7 @@ import { InventoryServiceMock } from "../__mocks__/inventory"
|
||||
|
||||
const eventBusService = {
|
||||
emit: jest.fn(),
|
||||
withTransaction: function() {
|
||||
withTransaction: function () {
|
||||
return this
|
||||
},
|
||||
}
|
||||
@@ -161,7 +161,7 @@ describe("SwapService", () => {
|
||||
const cartService = {
|
||||
create: jest.fn().mockReturnValue(Promise.resolve({ id: "cart" })),
|
||||
update: jest.fn().mockReturnValue(Promise.resolve()),
|
||||
withTransaction: function() {
|
||||
withTransaction: function () {
|
||||
return this
|
||||
},
|
||||
}
|
||||
@@ -170,25 +170,36 @@ describe("SwapService", () => {
|
||||
findOneWithRelations: () => Promise.resolve(existing),
|
||||
})
|
||||
|
||||
const lineItemService = {
|
||||
create: jest.fn().mockImplementation(d => Promise.resolve(d)),
|
||||
update: jest.fn().mockImplementation(d => Promise.resolve(d)),
|
||||
retrieve: () => Promise.resolve({}),
|
||||
const customShippingOptionService = {
|
||||
create: jest.fn().mockReturnValue(Promise.resolve({ id: "cso-test" })),
|
||||
update: jest.fn().mockReturnValue(Promise.resolve()),
|
||||
withTransaction: function() {
|
||||
return this
|
||||
},
|
||||
}
|
||||
|
||||
const lineItemService = {
|
||||
create: jest.fn().mockImplementation((d) => Promise.resolve(d)),
|
||||
update: jest.fn().mockImplementation((d) => Promise.resolve(d)),
|
||||
retrieve: () => Promise.resolve({}),
|
||||
withTransaction: function () {
|
||||
return this
|
||||
},
|
||||
}
|
||||
|
||||
const swapService = new SwapService({
|
||||
manager: MockManager,
|
||||
eventBusService,
|
||||
swapRepository: swapRepo,
|
||||
cartService,
|
||||
lineItemService,
|
||||
customShippingOptionService,
|
||||
})
|
||||
|
||||
it("finds swap and calls return create cart", async () => {
|
||||
await swapService.createCart(IdMap.getId("swap-1"))
|
||||
await swapService.createCart(IdMap.getId("swap-1"), [
|
||||
{ option_id: "test-option", price: 10 },
|
||||
])
|
||||
|
||||
expect(swapRepo.findOneWithRelations).toHaveBeenCalledTimes(1)
|
||||
expect(swapRepo.findOneWithRelations).toHaveBeenCalledWith(
|
||||
@@ -198,6 +209,9 @@ describe("SwapService", () => {
|
||||
"order.swaps",
|
||||
"order.swaps.additional_items",
|
||||
"order.discounts",
|
||||
"order.discounts.rule",
|
||||
"order.claims",
|
||||
"order.claims.additional_items",
|
||||
"additional_items",
|
||||
"return_order",
|
||||
"return_order.items",
|
||||
@@ -303,7 +317,7 @@ describe("SwapService", () => {
|
||||
const swapRepo = MockRepository()
|
||||
const returnService = {
|
||||
create: jest.fn().mockReturnValue(Promise.resolve({ id: "ret" })),
|
||||
withTransaction: function() {
|
||||
withTransaction: function () {
|
||||
return this
|
||||
},
|
||||
}
|
||||
@@ -400,7 +414,7 @@ describe("SwapService", () => {
|
||||
receiveReturn: jest
|
||||
.fn()
|
||||
.mockReturnValue(Promise.resolve({ test: "received" })),
|
||||
withTransaction: function() {
|
||||
withTransaction: function () {
|
||||
return this
|
||||
},
|
||||
}
|
||||
@@ -446,7 +460,7 @@ describe("SwapService", () => {
|
||||
receiveReturn: jest
|
||||
.fn()
|
||||
.mockReturnValue(Promise.resolve({ status: "requires_action" })),
|
||||
withTransaction: function() {
|
||||
withTransaction: function () {
|
||||
return this
|
||||
},
|
||||
}
|
||||
@@ -524,7 +538,7 @@ describe("SwapService", () => {
|
||||
{ items: [{ item_id: "1234", quantity: 2 }], data: "new" },
|
||||
])
|
||||
),
|
||||
withTransaction: function() {
|
||||
withTransaction: function () {
|
||||
return this
|
||||
},
|
||||
}
|
||||
@@ -545,7 +559,7 @@ describe("SwapService", () => {
|
||||
const lineItemService = {
|
||||
update: jest.fn(),
|
||||
retrieve: () => Promise.resolve({}),
|
||||
withTransaction: function() {
|
||||
withTransaction: function () {
|
||||
return this
|
||||
},
|
||||
}
|
||||
@@ -620,11 +634,11 @@ describe("SwapService", () => {
|
||||
describe("cancelFulfillment", () => {
|
||||
const swapRepo = MockRepository({
|
||||
findOneWithRelations: () => Promise.resolve({}),
|
||||
save: f => Promise.resolve(f),
|
||||
save: (f) => Promise.resolve(f),
|
||||
})
|
||||
|
||||
const fulfillmentService = {
|
||||
cancelFulfillment: jest.fn().mockImplementation(f => {
|
||||
cancelFulfillment: jest.fn().mockImplementation((f) => {
|
||||
switch (f) {
|
||||
case IdMap.getId("no-swap"):
|
||||
return Promise.resolve({})
|
||||
@@ -634,7 +648,7 @@ describe("SwapService", () => {
|
||||
})
|
||||
}
|
||||
}),
|
||||
withTransaction: function() {
|
||||
withTransaction: function () {
|
||||
return this
|
||||
},
|
||||
}
|
||||
@@ -688,14 +702,14 @@ describe("SwapService", () => {
|
||||
data: "new",
|
||||
})
|
||||
}),
|
||||
withTransaction: function() {
|
||||
withTransaction: function () {
|
||||
return this
|
||||
},
|
||||
}
|
||||
|
||||
const eventBusService = {
|
||||
emit: jest.fn().mockReturnValue(Promise.resolve()),
|
||||
withTransaction: function() {
|
||||
withTransaction: function () {
|
||||
return this
|
||||
},
|
||||
}
|
||||
@@ -736,14 +750,14 @@ describe("SwapService", () => {
|
||||
const lineItemService = {
|
||||
update: jest.fn(),
|
||||
retrieve: () => Promise.resolve({}),
|
||||
withTransaction: function() {
|
||||
withTransaction: function () {
|
||||
return this
|
||||
},
|
||||
}
|
||||
|
||||
const cartService = {
|
||||
update: jest.fn(),
|
||||
withTransaction: function() {
|
||||
withTransaction: function () {
|
||||
return this
|
||||
},
|
||||
}
|
||||
@@ -819,7 +833,7 @@ describe("SwapService", () => {
|
||||
|
||||
const eventBusService = {
|
||||
emit: jest.fn().mockReturnValue(Promise.resolve()),
|
||||
withTransaction: function() {
|
||||
withTransaction: function () {
|
||||
return this
|
||||
},
|
||||
}
|
||||
@@ -834,7 +848,7 @@ describe("SwapService", () => {
|
||||
updateShippingMethod: () => {
|
||||
return Promise.resolve()
|
||||
},
|
||||
withTransaction: function() {
|
||||
withTransaction: function () {
|
||||
return this
|
||||
},
|
||||
}
|
||||
@@ -843,7 +857,7 @@ describe("SwapService", () => {
|
||||
update: () => {
|
||||
return Promise.resolve()
|
||||
},
|
||||
withTransaction: function() {
|
||||
withTransaction: function () {
|
||||
return this
|
||||
},
|
||||
}
|
||||
@@ -858,14 +872,14 @@ describe("SwapService", () => {
|
||||
cancelPayment: jest.fn(() => {
|
||||
return Promise.resolve()
|
||||
}),
|
||||
withTransaction: function() {
|
||||
withTransaction: function () {
|
||||
return this
|
||||
},
|
||||
}
|
||||
|
||||
const inventoryService = {
|
||||
...InventoryServiceMock,
|
||||
withTransaction: function() {
|
||||
withTransaction: function () {
|
||||
return this
|
||||
},
|
||||
}
|
||||
@@ -986,19 +1000,19 @@ describe("SwapService", () => {
|
||||
describe("success", () => {
|
||||
const eventBusService = {
|
||||
emit: jest.fn().mockReturnValue(Promise.resolve()),
|
||||
withTransaction: function() {
|
||||
withTransaction: function () {
|
||||
return this
|
||||
},
|
||||
}
|
||||
|
||||
const paymentProviderService = {
|
||||
capturePayment: jest.fn(g =>
|
||||
capturePayment: jest.fn((g) =>
|
||||
g.id === "good" ? Promise.resolve() : Promise.reject()
|
||||
),
|
||||
refundPayment: jest.fn(g =>
|
||||
refundPayment: jest.fn((g) =>
|
||||
g[0].id === "good" ? Promise.resolve() : Promise.reject()
|
||||
),
|
||||
withTransaction: function() {
|
||||
withTransaction: function () {
|
||||
return this
|
||||
},
|
||||
}
|
||||
@@ -1126,7 +1140,7 @@ describe("SwapService", () => {
|
||||
|
||||
const eventBusService = {
|
||||
emit: jest.fn().mockReturnValue(Promise.resolve()),
|
||||
withTransaction: function() {
|
||||
withTransaction: function () {
|
||||
return this
|
||||
},
|
||||
}
|
||||
@@ -1189,7 +1203,7 @@ describe("SwapService", () => {
|
||||
|
||||
const paymentProviderService = {
|
||||
cancelPayment: jest.fn(() => Promise.resolve({})),
|
||||
withTransaction: function() {
|
||||
withTransaction: function () {
|
||||
return this
|
||||
},
|
||||
}
|
||||
@@ -1221,7 +1235,7 @@ describe("SwapService", () => {
|
||||
return Promise.resolve(swap)
|
||||
}
|
||||
},
|
||||
save: f => f,
|
||||
save: (f) => f,
|
||||
})
|
||||
|
||||
const swapService = new SwapService({
|
||||
@@ -1270,7 +1284,7 @@ describe("SwapService", () => {
|
||||
|
||||
it.each([["fail-refund-1"], ["fail-refund-2"], ["fail-refund-3"]])(
|
||||
"fails to cancel swap when contains refund",
|
||||
async input => {
|
||||
async (input) => {
|
||||
await expect(swapService.cancel(IdMap.getId(input))).rejects.toThrow(
|
||||
"Swap with a refund cannot be canceled"
|
||||
)
|
||||
|
||||
@@ -3,7 +3,7 @@ import { BaseService } from "medusa-interfaces"
|
||||
|
||||
/**
|
||||
* Can authenticate a user based on email password combination
|
||||
* @implements BaseService
|
||||
* @extends BaseService
|
||||
*/
|
||||
class AuthService extends BaseService {
|
||||
constructor({ userService, customerService }) {
|
||||
@@ -47,7 +47,9 @@ class AuthService extends BaseService {
|
||||
success: true,
|
||||
user,
|
||||
}
|
||||
} catch (error) {}
|
||||
} catch (error) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
@@ -24,7 +24,6 @@ class CartService extends BaseService {
|
||||
regionService,
|
||||
lineItemService,
|
||||
shippingOptionService,
|
||||
shippingProfileService,
|
||||
customerService,
|
||||
discountService,
|
||||
giftCardService,
|
||||
@@ -32,6 +31,7 @@ class CartService extends BaseService {
|
||||
addressRepository,
|
||||
paymentSessionRepository,
|
||||
inventoryService,
|
||||
customShippingOptionService,
|
||||
}) {
|
||||
super()
|
||||
|
||||
@@ -62,9 +62,6 @@ class CartService extends BaseService {
|
||||
/** @private @const {PaymentProviderService} */
|
||||
this.paymentProviderService_ = paymentProviderService
|
||||
|
||||
/** @private @const {ShippingProfileService} */
|
||||
this.shippingProfileService_ = shippingProfileService
|
||||
|
||||
/** @private @const {CustomerService} */
|
||||
this.customerService_ = customerService
|
||||
|
||||
@@ -88,6 +85,9 @@ class CartService extends BaseService {
|
||||
|
||||
/** @private @const {InventoryService} */
|
||||
this.inventoryService_ = inventoryService
|
||||
|
||||
/** @private @const {CustomShippingOptionService} */
|
||||
this.customShippingOptionService_ = customShippingOptionService
|
||||
}
|
||||
|
||||
withTransaction(transactionManager) {
|
||||
@@ -107,13 +107,13 @@ class CartService extends BaseService {
|
||||
regionService: this.regionService_,
|
||||
lineItemService: this.lineItemService_,
|
||||
shippingOptionService: this.shippingOptionService_,
|
||||
shippingProfileService: this.shippingProfileService_,
|
||||
customerService: this.customerService_,
|
||||
discountService: this.discountService_,
|
||||
totalsService: this.totalsService_,
|
||||
addressRepository: this.addressRepository_,
|
||||
giftCardService: this.giftCardService_,
|
||||
inventoryService: this.inventoryService_,
|
||||
customShippingOptionService: this.customShippingOptionService_,
|
||||
})
|
||||
|
||||
cloned.transactionManager_ = transactionManager
|
||||
@@ -166,9 +166,11 @@ class CartService extends BaseService {
|
||||
relationSet.add("items")
|
||||
relationSet.add("gift_cards")
|
||||
relationSet.add("discounts")
|
||||
//relationSet.add("discounts.parent_discount")
|
||||
//relationSet.add("discounts.parent_discount.rule")
|
||||
//relationSet.add("discounts.parent_discount.regions")
|
||||
relationSet.add("discounts.rule")
|
||||
relationSet.add("discounts.rule.valid_for")
|
||||
// relationSet.add("discounts.parent_discount")
|
||||
// relationSet.add("discounts.parent_discount.rule")
|
||||
// relationSet.add("discounts.parent_discount.regions")
|
||||
relationSet.add("shipping_methods")
|
||||
relationSet.add("region")
|
||||
relations = [...relationSet]
|
||||
@@ -599,12 +601,13 @@ class CartService extends BaseService {
|
||||
"shipping_address",
|
||||
"billing_address",
|
||||
"gift_cards",
|
||||
"discounts",
|
||||
"customer",
|
||||
"region",
|
||||
"payment_sessions",
|
||||
"region.countries",
|
||||
"discounts",
|
||||
"discounts.rule",
|
||||
"discounts.rule.valid_for",
|
||||
"discounts.regions",
|
||||
],
|
||||
})
|
||||
@@ -794,13 +797,18 @@ class CartService extends BaseService {
|
||||
* @return {Promise} the result of the update operation
|
||||
*/
|
||||
async updateShippingAddress_(cart, addressOrId, addrRepo) {
|
||||
if (addressOrId === null) {
|
||||
cart.shipping_address = null
|
||||
return
|
||||
}
|
||||
|
||||
if (typeof addressOrId === `string`) {
|
||||
addressOrId = await addrRepo.findOne({
|
||||
where: { id: addressOrId },
|
||||
})
|
||||
}
|
||||
|
||||
addressOrId.country_code = addressOrId.country_code?.toLowerCase()
|
||||
addressOrId.country_code = addressOrId.country_code?.toLowerCase() ?? null
|
||||
|
||||
if (
|
||||
addressOrId.country_code &&
|
||||
@@ -815,7 +823,7 @@ class CartService extends BaseService {
|
||||
}
|
||||
|
||||
if (addressOrId.id) {
|
||||
cart.shipping_address = addressOrId
|
||||
await addrRepo.save(addressOrId)
|
||||
cart.shipping_address_id = addressOrId.id
|
||||
} else {
|
||||
if (cart.shipping_address_id) {
|
||||
@@ -871,6 +879,7 @@ class CartService extends BaseService {
|
||||
async applyDiscount(cart, discountCode) {
|
||||
const discount = await this.discountService_.retrieveByCode(discountCode, [
|
||||
"rule",
|
||||
"rule.valid_for",
|
||||
"regions",
|
||||
])
|
||||
|
||||
@@ -880,11 +889,12 @@ class CartService extends BaseService {
|
||||
if (discount.usage_limit) {
|
||||
discount.usage_count = discount.usage_count || 0
|
||||
|
||||
if (discount.usage_limit === discount.usage_count)
|
||||
if (discount.usage_limit === discount.usage_count) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.NOT_ALLOWED,
|
||||
"Discount has been used maximum allowed times"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const today = new Date()
|
||||
@@ -968,7 +978,13 @@ class CartService extends BaseService {
|
||||
async removeDiscount(cartId, discountCode) {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
const cart = await this.retrieve(cartId, {
|
||||
relations: ["discounts", "payment_sessions", "shipping_methods"],
|
||||
relations: [
|
||||
"discounts",
|
||||
"discounts.rule",
|
||||
"discounts.rule.valid_for",
|
||||
"payment_sessions",
|
||||
"shipping_methods",
|
||||
],
|
||||
})
|
||||
|
||||
if (cart.discounts.some(({ rule }) => rule.type === "free_shipping")) {
|
||||
@@ -1084,7 +1100,7 @@ class CartService extends BaseService {
|
||||
* Sets a payment method for a cart.
|
||||
* @param {string} cartId - the id of the cart to add payment method to
|
||||
* @param {PaymentMethod} paymentMethod - the method to be set to the cart
|
||||
* @returns {Promise} result of update operation
|
||||
* @return {Promise} result of update operation
|
||||
*/
|
||||
async setPaymentSession(cartId, providerId) {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
@@ -1145,13 +1161,13 @@ class CartService extends BaseService {
|
||||
* amounts, currencies, etc. as well as make sure to filter payment sessions
|
||||
* that are not available for the cart's region.
|
||||
* @param {string} cartId - the id of the cart to set payment session for
|
||||
* @returns {Promise} the result of the update operation.
|
||||
* @return {Promise} the result of the update operation.
|
||||
*/
|
||||
async setPaymentSessions(cartOrCartId) {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
const psRepo = manager.getCustomRepository(this.paymentSessionRepository_)
|
||||
|
||||
let cartId =
|
||||
const cartId =
|
||||
typeof cartOrCartId === `string` ? cartOrCartId : cartOrCartId.id
|
||||
const cart = await this.retrieve(cartId, {
|
||||
select: [
|
||||
@@ -1165,6 +1181,8 @@ class CartService extends BaseService {
|
||||
relations: [
|
||||
"items",
|
||||
"discounts",
|
||||
"discounts.rule",
|
||||
"discounts.rule.valid_for",
|
||||
"gift_cards",
|
||||
"billing_address",
|
||||
"shipping_address",
|
||||
@@ -1178,7 +1196,7 @@ class CartService extends BaseService {
|
||||
const region = cart.region
|
||||
|
||||
// If there are existing payment sessions ensure that these are up to date
|
||||
let seen = []
|
||||
const seen = []
|
||||
if (cart.payment_sessions && cart.payment_sessions.length) {
|
||||
for (const session of cart.payment_sessions) {
|
||||
if (
|
||||
@@ -1228,7 +1246,7 @@ class CartService extends BaseService {
|
||||
* @param {string} cartId - the id of the cart to remove from
|
||||
* @param {string} providerId - the id of the provider whoose payment session
|
||||
* should be removed.
|
||||
* @returns {Promise<Cart>} the resulting cart.
|
||||
* @return {Promise<Cart>} the resulting cart.
|
||||
*/
|
||||
async deletePaymentSession(cartId, providerId) {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
@@ -1269,7 +1287,7 @@ class CartService extends BaseService {
|
||||
* @param {string} cartId - the id of the cart to remove from
|
||||
* @param {string} providerId - the id of the provider whoose payment session
|
||||
* should be removed.
|
||||
* @returns {Promise<Cart>} the resulting cart.
|
||||
* @return {Promise<Cart>} the resulting cart.
|
||||
*/
|
||||
async refreshPaymentSession(cartId, providerId) {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
@@ -1317,6 +1335,8 @@ class CartService extends BaseService {
|
||||
relations: [
|
||||
"shipping_methods",
|
||||
"discounts",
|
||||
"discounts.rule",
|
||||
"discounts.rule.valid_for",
|
||||
"shipping_methods.shipping_option",
|
||||
"items",
|
||||
"items.variant",
|
||||
@@ -1324,11 +1344,32 @@ class CartService extends BaseService {
|
||||
"items.variant.product",
|
||||
],
|
||||
})
|
||||
|
||||
const cartCustomShippingOptions =
|
||||
await this.customShippingOptionService_.list({ cart_id: cart.id })
|
||||
|
||||
const customShippingOption = this.findCustomShippingOption(
|
||||
cartCustomShippingOptions,
|
||||
optionId
|
||||
)
|
||||
|
||||
const { shipping_methods } = cart
|
||||
|
||||
/**
|
||||
* If we have a custom shipping option configured we want the price
|
||||
* override to take effect and do not want `validateCartOption` to check
|
||||
* if requirements are met, hence we are not passing the entire cart, but
|
||||
* just the id.
|
||||
*/
|
||||
const shippingMethodConfig = customShippingOption
|
||||
? { cart_id: cart.id, price: customShippingOption.price }
|
||||
: {
|
||||
cart,
|
||||
}
|
||||
|
||||
const newMethod = await this.shippingOptionService_
|
||||
.withTransaction(manager)
|
||||
.createShippingMethod(optionId, data, { cart })
|
||||
.createShippingMethod(optionId, data, shippingMethodConfig)
|
||||
|
||||
const methods = [newMethod]
|
||||
if (shipping_methods.length) {
|
||||
@@ -1353,7 +1394,12 @@ class CartService extends BaseService {
|
||||
}
|
||||
|
||||
const result = await this.retrieve(cartId, {
|
||||
relations: ["discounts", "shipping_methods"],
|
||||
relations: [
|
||||
"discounts",
|
||||
"discounts.rule",
|
||||
"discounts.rule.valid_for",
|
||||
"shipping_methods",
|
||||
],
|
||||
})
|
||||
|
||||
// if cart has freeshipping, adjust price
|
||||
@@ -1368,6 +1414,29 @@ class CartService extends BaseService {
|
||||
}, "SERIALIZABLE")
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the cart's custom shipping options based on the passed option id.
|
||||
* throws if custom options is not empty and no shipping option corresponds to optionId
|
||||
* @param {Object} cartCustomShippingOptions - the cart's custom shipping options
|
||||
* @param {string} option - id of the normal or custom shipping option to find in the cartCustomShippingOptions
|
||||
* @return {CustomShippingOption | undefined}
|
||||
*/
|
||||
findCustomShippingOption(cartCustomShippingOptions, optionId) {
|
||||
const customOption = cartCustomShippingOptions?.find(
|
||||
(cso) => cso.shipping_option_id === optionId
|
||||
)
|
||||
const hasCustomOptions = cartCustomShippingOptions?.length
|
||||
|
||||
if (hasCustomOptions && !customOption) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
"Wrong shipping option"
|
||||
)
|
||||
}
|
||||
|
||||
return customOption
|
||||
}
|
||||
|
||||
/**
|
||||
* Set's the region of a cart.
|
||||
* @param {string} cartId - the id of the cart to set region on
|
||||
@@ -1418,6 +1487,14 @@ class CartService extends BaseService {
|
||||
)
|
||||
}
|
||||
|
||||
/*
|
||||
* When changing the region you are changing the set of countries that your
|
||||
* cart can be shipped to so we need to make sure that the current shipping
|
||||
* address adheres to the new country set.
|
||||
*
|
||||
* First check if there is an existing shipping address on the cart if so
|
||||
* fetch the entire thing so we can modify the shipping country
|
||||
*/
|
||||
let shippingAddress = {}
|
||||
if (cart.shipping_address_id) {
|
||||
shippingAddress = await addrRepo.findOne({
|
||||
@@ -1425,6 +1502,10 @@ class CartService extends BaseService {
|
||||
})
|
||||
}
|
||||
|
||||
/*
|
||||
* If the client has specified which country code we are updating to check
|
||||
* that that country is in fact in the country and perform the update.
|
||||
*/
|
||||
if (countryCode !== undefined) {
|
||||
if (
|
||||
!region.countries.find(
|
||||
@@ -1444,7 +1525,17 @@ class CartService extends BaseService {
|
||||
|
||||
await addrRepo.save(updated)
|
||||
} else {
|
||||
/*
|
||||
* In the case where the country code is not specified we need to check
|
||||
*
|
||||
* 1. if the region we are switching to has only one country preselect
|
||||
* that
|
||||
* 2. if the region has multiple countries we need to unset the country
|
||||
* and wait for client to decide which country to use
|
||||
*/
|
||||
|
||||
let updated = { ...shippingAddress }
|
||||
|
||||
// If the country code of a shipping address is set we need to clear it
|
||||
if (!_.isEmpty(shippingAddress) && shippingAddress.country_code) {
|
||||
updated = {
|
||||
@@ -1501,13 +1592,19 @@ class CartService extends BaseService {
|
||||
/**
|
||||
* Deletes a cart from the database. Completed carts cannot be deleted.
|
||||
* @param {string} cartId - the id of the cart to delete
|
||||
* @returns {Promise<Cart?>} the deleted cart or undefined if the cart was
|
||||
* @return {Promise<Cart?>} the deleted cart or undefined if the cart was
|
||||
* not found.
|
||||
*/
|
||||
async delete(cartId) {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
const cart = await this.retrieve(cartId, {
|
||||
relations: ["items", "discounts", "payment_sessions"],
|
||||
relations: [
|
||||
"items",
|
||||
"discounts",
|
||||
"discounts.rule",
|
||||
"discounts.rule.valid_for",
|
||||
"payment_sessions",
|
||||
],
|
||||
})
|
||||
|
||||
if (cart.completed_at) {
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import _ from "lodash"
|
||||
import { Validator, MedusaError } from "medusa-core-utils"
|
||||
import { MedusaError } from "medusa-core-utils"
|
||||
import { BaseService } from "medusa-interfaces"
|
||||
import { Brackets } from "typeorm"
|
||||
|
||||
class ClaimService extends BaseService {
|
||||
static Events = {
|
||||
@@ -102,7 +100,7 @@ class ClaimService extends BaseService {
|
||||
}
|
||||
|
||||
update(id, data) {
|
||||
return this.atomicPhase_(async manager => {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
const claimRepo = manager.getCustomRepository(this.claimRepository_)
|
||||
const claim = await this.retrieve(id, { relations: ["shipping_methods"] })
|
||||
|
||||
@@ -113,13 +111,7 @@ class ClaimService extends BaseService {
|
||||
)
|
||||
}
|
||||
|
||||
const {
|
||||
claim_items,
|
||||
shipping_methods,
|
||||
metadata,
|
||||
fulfillment_status,
|
||||
no_notification,
|
||||
} = data
|
||||
const { claim_items, shipping_methods, metadata, no_notification } = data
|
||||
|
||||
if (metadata) {
|
||||
claim.metadata = this.setMetadata_(claim, metadata)
|
||||
@@ -183,9 +175,11 @@ class ClaimService extends BaseService {
|
||||
* Creates a Claim on an Order. Claims consists of items that are claimed and
|
||||
* optionally items to be sent as replacement for the claimed items. The
|
||||
* shipping address that the new items will be shipped to
|
||||
* @param {Object} data - the object containing all data required to create a claim
|
||||
* @return {Object} created claim
|
||||
*/
|
||||
create(data) {
|
||||
return this.atomicPhase_(async manager => {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
const claimRepo = manager.getCustomRepository(this.claimRepository_)
|
||||
|
||||
const {
|
||||
@@ -257,8 +251,8 @@ class ClaimService extends BaseService {
|
||||
|
||||
let toRefund = refund_amount
|
||||
if (type === "refund" && typeof refund_amount === "undefined") {
|
||||
const lines = claim_items.map(ci => {
|
||||
const orderItem = order.items.find(oi => oi.id === ci.item_id)
|
||||
const lines = claim_items.map((ci) => {
|
||||
const orderItem = order.items.find((oi) => oi.id === ci.item_id)
|
||||
return {
|
||||
...orderItem,
|
||||
quantity: ci.quantity,
|
||||
@@ -274,7 +268,7 @@ class ClaimService extends BaseService {
|
||||
}
|
||||
|
||||
const newItems = await Promise.all(
|
||||
additional_items.map(i =>
|
||||
additional_items.map((i) =>
|
||||
this.lineItemService_
|
||||
.withTransaction(manager)
|
||||
.generate(i.variant_id, order.region_id, i.quantity)
|
||||
@@ -332,7 +326,7 @@ class ClaimService extends BaseService {
|
||||
await this.returnService_.withTransaction(manager).create({
|
||||
order_id: order.id,
|
||||
claim_order_id: result.id,
|
||||
items: claim_items.map(ci => ({
|
||||
items: claim_items.map((ci) => ({
|
||||
item_id: ci.item_id,
|
||||
quantity: ci.quantity,
|
||||
metadata: ci.metadata,
|
||||
@@ -362,7 +356,7 @@ class ClaimService extends BaseService {
|
||||
) {
|
||||
const { metadata, no_notification } = config
|
||||
|
||||
return this.atomicPhase_(async manager => {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
const claim = await this.retrieve(id, {
|
||||
relations: [
|
||||
"additional_items",
|
||||
@@ -429,7 +423,7 @@ class ClaimService extends BaseService {
|
||||
is_claim: true,
|
||||
no_notification: evaluatedNoNotification,
|
||||
},
|
||||
claim.additional_items.map(i => ({
|
||||
claim.additional_items.map((i) => ({
|
||||
item_id: i.id,
|
||||
quantity: i.quantity,
|
||||
})),
|
||||
@@ -445,7 +439,7 @@ class ClaimService extends BaseService {
|
||||
|
||||
for (const item of claim.additional_items) {
|
||||
const fulfillmentItem = successfullyFulfilled.find(
|
||||
f => item.id === f.item_id
|
||||
(f) => item.id === f.item_id
|
||||
)
|
||||
|
||||
if (fulfillmentItem) {
|
||||
@@ -485,7 +479,7 @@ class ClaimService extends BaseService {
|
||||
}
|
||||
|
||||
async cancelFulfillment(fulfillmentId) {
|
||||
return this.atomicPhase_(async manager => {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
const canceled = await this.fulfillmentService_
|
||||
.withTransaction(manager)
|
||||
.cancelFulfillment(fulfillmentId)
|
||||
@@ -508,7 +502,7 @@ class ClaimService extends BaseService {
|
||||
}
|
||||
|
||||
async processRefund(id) {
|
||||
return this.atomicPhase_(async manager => {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
const claim = await this.retrieve(id, {
|
||||
relations: ["order", "order.payments"],
|
||||
})
|
||||
@@ -560,7 +554,7 @@ class ClaimService extends BaseService {
|
||||
) {
|
||||
const { metadata, no_notification } = config
|
||||
|
||||
return this.atomicPhase_(async manager => {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
const claim = await this.retrieve(id, {
|
||||
relations: ["additional_items"],
|
||||
})
|
||||
@@ -584,7 +578,7 @@ class ClaimService extends BaseService {
|
||||
claim.fulfillment_status = "shipped"
|
||||
|
||||
for (const i of claim.additional_items) {
|
||||
const shipped = shipment.items.find(si => si.item_id === i.id)
|
||||
const shipped = shipment.items.find((si) => si.item_id === i.id)
|
||||
if (shipped) {
|
||||
const shippedQty = (i.shipped_quantity || 0) + shipped.quantity
|
||||
await this.lineItemService_.withTransaction(manager).update(i.id, {
|
||||
@@ -617,7 +611,7 @@ class ClaimService extends BaseService {
|
||||
}
|
||||
|
||||
async cancel(id) {
|
||||
return this.atomicPhase_(async manager => {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
const claim = await this.retrieve(id, {
|
||||
relations: ["return_order", "fulfillments", "order", "order.refunds"],
|
||||
})
|
||||
@@ -665,6 +659,7 @@ class ClaimService extends BaseService {
|
||||
|
||||
/**
|
||||
* @param {Object} selector - the query object for find
|
||||
* @param {Object} config - the config object containing query settings
|
||||
* @return {Promise} the result of the find operation
|
||||
*/
|
||||
async list(
|
||||
@@ -678,7 +673,8 @@ class ClaimService extends BaseService {
|
||||
|
||||
/**
|
||||
* Gets an order by id.
|
||||
* @param {string} orderId - id of order to retrieve
|
||||
* @param {string} claimId - id of order to retrieve
|
||||
* @param {Object} config - the config object containing query settings
|
||||
* @return {Promise<Order>} the order document
|
||||
*/
|
||||
async retrieve(claimId, config = {}) {
|
||||
@@ -717,7 +713,7 @@ class ClaimService extends BaseService {
|
||||
const keyPath = `metadata.${key}`
|
||||
return this.orderModel_
|
||||
.updateOne({ _id: validatedId }, { $unset: { [keyPath]: "" } })
|
||||
.catch(err => {
|
||||
.catch((err) => {
|
||||
throw new MedusaError(MedusaError.Types.DB_ERROR, err.message)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
import { MedusaError } from "medusa-core-utils"
|
||||
import { BaseService } from "medusa-interfaces"
|
||||
|
||||
class CustomShippingOptionService extends BaseService {
|
||||
constructor({ manager, customShippingOptionRepository }) {
|
||||
super()
|
||||
|
||||
/** @private @const {EntityManager} */
|
||||
this.manager_ = manager
|
||||
|
||||
/** @private @const {CustomShippingOptionRepository} */
|
||||
this.customShippingOptionRepository_ = customShippingOptionRepository
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the service's manager to a given transaction manager
|
||||
* @param {EntityManager} manager - the manager to use
|
||||
* @return {CustomShippingOptionService} a cloned CustomShippingOption service
|
||||
*/
|
||||
withTransaction(manager) {
|
||||
if (!manager) {
|
||||
return this
|
||||
}
|
||||
|
||||
const cloned = new CustomShippingOptionService({
|
||||
manager,
|
||||
customShippingOptionRepository: this.customShippingOptionRepository_,
|
||||
})
|
||||
|
||||
cloned.transactionManager_ = manager
|
||||
return cloned
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a specific shipping option.
|
||||
* @param {string} id - the id of the custom shipping option to retrieve.
|
||||
* @param {*} config - any options needed to query for the result.
|
||||
* @return {Promise<CustomShippingOption>} which resolves to the requested custom shipping option.
|
||||
*/
|
||||
async retrieve(id, config = {}) {
|
||||
const customShippingOptionRepo = this.manager_.getCustomRepository(
|
||||
this.customShippingOptionRepository_
|
||||
)
|
||||
|
||||
const validatedId = this.validateId_(id)
|
||||
const query = this.buildQuery_({ id: validatedId }, config)
|
||||
|
||||
const customShippingOption = await customShippingOptionRepo.findOne(query)
|
||||
|
||||
if (!customShippingOption) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.NOT_FOUND,
|
||||
`Custom shipping option with id: ${id} was not found.`
|
||||
)
|
||||
}
|
||||
|
||||
return customShippingOption
|
||||
}
|
||||
|
||||
/** Fetches all custom shipping options related to the given selector
|
||||
* @param {Object} selector - the query object for find
|
||||
* @param {Object} config - the configuration used to find the objects. contains relations, skip, and take.
|
||||
* @return {Promise<CustomShippingOption[]>} custom shipping options matching the query
|
||||
*/
|
||||
async list(
|
||||
selector,
|
||||
config = {
|
||||
skip: 0,
|
||||
take: 50,
|
||||
relations: [],
|
||||
}
|
||||
) {
|
||||
const customShippingOptionRepo = this.manager_.getCustomRepository(
|
||||
this.customShippingOptionRepository_
|
||||
)
|
||||
|
||||
const query = this.buildQuery_(selector, config)
|
||||
|
||||
return customShippingOptionRepo.find(query)
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a custom shipping option associated with a given author
|
||||
* @param {object} data - the custom shipping option to create
|
||||
* @param {*} config - any configurations if needed, including meta data
|
||||
* @return {Promise<CustomShippingOption>} resolves to the creation result
|
||||
*/
|
||||
async create(data, config = { metadata: {} }) {
|
||||
const { metadata } = config
|
||||
|
||||
const { cart_id, shipping_option_id, price } = data
|
||||
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
const customShippingOptionRepo = manager.getCustomRepository(
|
||||
this.customShippingOptionRepository_
|
||||
)
|
||||
|
||||
const customShippingOption = await customShippingOptionRepo.create({
|
||||
cart_id,
|
||||
shipping_option_id,
|
||||
price,
|
||||
metadata,
|
||||
})
|
||||
const result = await customShippingOptionRepo.save(customShippingOption)
|
||||
|
||||
return result
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export default CustomShippingOptionService
|
||||
@@ -7,7 +7,7 @@ import { Brackets, ILike } from "typeorm"
|
||||
|
||||
/**
|
||||
* Provides layer to manipulate customers.
|
||||
* @implements BaseService
|
||||
* @implements {BaseService}
|
||||
*/
|
||||
class CustomerService extends BaseService {
|
||||
static Events = {
|
||||
@@ -60,9 +60,7 @@ class CustomerService extends BaseService {
|
||||
* @return {string} the validated email
|
||||
*/
|
||||
validateEmail_(email) {
|
||||
const schema = Validator.string()
|
||||
.email()
|
||||
.required()
|
||||
const schema = Validator.string().email().required()
|
||||
const { value, error } = schema.validate(email)
|
||||
if (error) {
|
||||
throw new MedusaError(
|
||||
@@ -93,7 +91,7 @@ class CustomerService extends BaseService {
|
||||
* secret a long side a payload with userId and the expiry time for the token,
|
||||
* which is always 15 minutes.
|
||||
* @param {string} customerId - the customer to reset the password for
|
||||
* @returns {string} the generated JSON web token
|
||||
* @return {string} the generated JSON web token
|
||||
*/
|
||||
async generateResetPasswordToken(customerId) {
|
||||
const customer = await this.retrieve(customerId)
|
||||
@@ -122,6 +120,7 @@ class CustomerService extends BaseService {
|
||||
|
||||
/**
|
||||
* @param {Object} selector - the query object for find
|
||||
* @param {Object} config - the config object containing query settings
|
||||
* @return {Promise} the result of the find operation
|
||||
*/
|
||||
async list(selector = {}, config = { relations: [], skip: 0, take: 50 }) {
|
||||
@@ -144,11 +143,11 @@ class CustomerService extends BaseService {
|
||||
delete where.first_name
|
||||
delete where.last_name
|
||||
|
||||
query.where = qb => {
|
||||
query.where = (qb) => {
|
||||
qb.where(where)
|
||||
|
||||
qb.andWhere(
|
||||
new Brackets(qb => {
|
||||
new Brackets((qb) => {
|
||||
qb.where({ email: ILike(`%${q}%`) })
|
||||
.orWhere({ first_name: ILike(`%${q}%`) })
|
||||
.orWhere({ last_name: ILike(`%${q}%`) })
|
||||
@@ -183,11 +182,11 @@ class CustomerService extends BaseService {
|
||||
delete where.first_name
|
||||
delete where.last_name
|
||||
|
||||
query.where = qb => {
|
||||
query.where = (qb) => {
|
||||
qb.where(where)
|
||||
|
||||
qb.andWhere(
|
||||
new Brackets(qb => {
|
||||
new Brackets((qb) => {
|
||||
qb.where({ email: ILike(`%${q}%`) })
|
||||
.orWhere({ first_name: ILike(`%${q}%`) })
|
||||
.orWhere({ last_name: ILike(`%${q}%`) })
|
||||
@@ -214,6 +213,7 @@ class CustomerService extends BaseService {
|
||||
/**
|
||||
* Gets a customer by id.
|
||||
* @param {string} customerId - the id of the customer to get.
|
||||
* @param {Object} config - the config object containing query settings
|
||||
* @return {Promise<Customer>} the customer document.
|
||||
*/
|
||||
async retrieve(customerId, config = {}) {
|
||||
@@ -224,7 +224,6 @@ class CustomerService extends BaseService {
|
||||
const query = this.buildQuery_({ id: validatedId }, config)
|
||||
|
||||
const customer = await customerRepo.findOne(query)
|
||||
|
||||
if (!customer) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.NOT_FOUND,
|
||||
@@ -238,6 +237,7 @@ class CustomerService extends BaseService {
|
||||
/**
|
||||
* Gets a customer by email.
|
||||
* @param {string} email - the email of the customer to get.
|
||||
* @param {Object} config - the config object containing query settings
|
||||
* @return {Promise<Customer>} the customer document.
|
||||
*/
|
||||
async retrieveByEmail(email, config = {}) {
|
||||
@@ -261,6 +261,7 @@ class CustomerService extends BaseService {
|
||||
/**
|
||||
* Gets a customer by phone.
|
||||
* @param {string} phone - the phone of the customer to get.
|
||||
* @param {Object} config - the config object containing query settings
|
||||
* @return {Promise<Customer>} the customer document.
|
||||
*/
|
||||
async retrieveByPhone(phone, config = {}) {
|
||||
@@ -284,7 +285,7 @@ class CustomerService extends BaseService {
|
||||
/**
|
||||
* Hashes a password
|
||||
* @param {string} password - the value to hash
|
||||
* @return hashed password
|
||||
* @return {Promise<string>} hashed password
|
||||
*/
|
||||
async hashPassword_(password) {
|
||||
const buf = await Scrypt.kdf(password, { logN: 1, r: 1, p: 1 })
|
||||
@@ -300,7 +301,7 @@ class CustomerService extends BaseService {
|
||||
* @return {Promise} the result of create
|
||||
*/
|
||||
async create(customer) {
|
||||
return this.atomicPhase_(async manager => {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
const customerRepository = manager.getCustomRepository(
|
||||
this.customerRepository_
|
||||
)
|
||||
@@ -312,7 +313,9 @@ class CustomerService extends BaseService {
|
||||
customer.billing_address = this.validateBillingAddress_(billing_address)
|
||||
}
|
||||
|
||||
const existing = await this.retrieveByEmail(email).catch(err => undefined)
|
||||
const existing = await this.retrieveByEmail(email).catch(
|
||||
(err) => undefined
|
||||
)
|
||||
|
||||
if (existing && existing.has_account) {
|
||||
throw new MedusaError(
|
||||
@@ -353,13 +356,13 @@ class CustomerService extends BaseService {
|
||||
|
||||
/**
|
||||
* Updates a customer.
|
||||
* @param {string} variantId - the id of the variant. Must be a string that
|
||||
* @param {string} customerId - the id of the variant. Must be a string that
|
||||
* can be casted to an ObjectId
|
||||
* @param {object} update - an object with the update values.
|
||||
* @return {Promise} resolves to the update result.
|
||||
*/
|
||||
async update(customerId, update) {
|
||||
return this.atomicPhase_(async manager => {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
const customerRepository = manager.getCustomRepository(
|
||||
this.customerRepository_
|
||||
)
|
||||
@@ -370,10 +373,9 @@ class CustomerService extends BaseService {
|
||||
const {
|
||||
email,
|
||||
password,
|
||||
password_hash,
|
||||
metadata,
|
||||
billing_address,
|
||||
billing_address_id,
|
||||
metadata,
|
||||
...rest
|
||||
} = update
|
||||
|
||||
@@ -386,7 +388,7 @@ class CustomerService extends BaseService {
|
||||
}
|
||||
|
||||
if ("billing_address_id" in update || "billing_address" in update) {
|
||||
const address = update.billing_address_id || update.billing_address
|
||||
const address = billing_address_id || billing_address
|
||||
await this.updateBillingAddress_(customer, address, addrRepo)
|
||||
}
|
||||
|
||||
@@ -408,12 +410,18 @@ class CustomerService extends BaseService {
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the customers's billing address.
|
||||
* Updates the customers' billing address.
|
||||
* @param {Customer} customer - the Customer to update
|
||||
* @param {object} address - the value to set the billing address to
|
||||
* @param {Object|string} addressOrId - the value to set the billing address to
|
||||
* @param {Object} addrRepo - address repository
|
||||
* @return {Promise} the result of the update operation
|
||||
*/
|
||||
async updateBillingAddress_(customer, addressOrId, addrRepo) {
|
||||
if (addressOrId === null) {
|
||||
customer.billing_address_id = null
|
||||
return
|
||||
}
|
||||
|
||||
if (typeof addressOrId === `string`) {
|
||||
addressOrId = await addrRepo.findOne({
|
||||
where: { id: addressOrId },
|
||||
@@ -424,7 +432,6 @@ class CustomerService extends BaseService {
|
||||
|
||||
if (addressOrId.id) {
|
||||
customer.billing_address_id = addressOrId.id
|
||||
customer.billing_address = addressOrId
|
||||
} else {
|
||||
if (customer.billing_address_id) {
|
||||
const addr = await addrRepo.findOne({
|
||||
@@ -443,7 +450,7 @@ class CustomerService extends BaseService {
|
||||
}
|
||||
|
||||
async updateAddress(customerId, addressId, address) {
|
||||
return this.atomicPhase_(async manager => {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
const addressRepo = manager.getCustomRepository(this.addressRepository_)
|
||||
|
||||
address.country_code = address.country_code.toLowerCase()
|
||||
@@ -464,7 +471,7 @@ class CustomerService extends BaseService {
|
||||
}
|
||||
|
||||
async removeAddress(customerId, addressId) {
|
||||
return this.atomicPhase_(async manager => {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
const addressRepo = manager.getCustomRepository(this.addressRepository_)
|
||||
|
||||
// Should not fail, if user does not exist, since delete is idempotent
|
||||
@@ -472,7 +479,9 @@ class CustomerService extends BaseService {
|
||||
where: { id: addressId, customer_id: customerId },
|
||||
})
|
||||
|
||||
if (!address) return Promise.resolve()
|
||||
if (!address) {
|
||||
return Promise.resolve()
|
||||
}
|
||||
|
||||
await addressRepo.softRemove(address)
|
||||
|
||||
@@ -481,7 +490,7 @@ class CustomerService extends BaseService {
|
||||
}
|
||||
|
||||
async addAddress(customerId, address) {
|
||||
return this.atomicPhase_(async manager => {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
const addressRepository = manager.getCustomRepository(
|
||||
this.addressRepository_
|
||||
)
|
||||
@@ -493,8 +502,8 @@ class CustomerService extends BaseService {
|
||||
})
|
||||
this.validateBillingAddress_(address)
|
||||
|
||||
let shouldAdd = !customer.shipping_addresses.find(
|
||||
a =>
|
||||
const shouldAdd = !customer.shipping_addresses.find(
|
||||
(a) =>
|
||||
a.country_code.toLowerCase() === address.country_code.toLowerCase() &&
|
||||
a.address_1 === address.address_1 &&
|
||||
a.address_2 === address.address_2 &&
|
||||
@@ -526,13 +535,15 @@ class CustomerService extends BaseService {
|
||||
* @return {Promise} the result of the delete operation.
|
||||
*/
|
||||
async delete(customerId) {
|
||||
return this.atomicPhase_(async manager => {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
const customerRepo = manager.getCustomRepository(this.customerRepository_)
|
||||
|
||||
// Should not fail, if user does not exist, since delete is idempotent
|
||||
const customer = await customerRepo.findOne({ where: { id: customerId } })
|
||||
|
||||
if (!customer) return Promise.resolve()
|
||||
if (!customer) {
|
||||
return Promise.resolve()
|
||||
}
|
||||
|
||||
await customerRepo.softRemove(customer)
|
||||
|
||||
|
||||
@@ -1,14 +1,11 @@
|
||||
import _ from "lodash"
|
||||
import randomize from "randomatic"
|
||||
import { BaseService } from "medusa-interfaces"
|
||||
import { Validator, MedusaError } from "medusa-core-utils"
|
||||
import { MedusaErrorCodes } from "medusa-core-utils/dist/errors"
|
||||
import { parse, toSeconds } from "iso8601-duration"
|
||||
import { Brackets, ILike } from "typeorm"
|
||||
|
||||
/**
|
||||
* Provides layer to manipulate discounts.
|
||||
* @implements BaseService
|
||||
* @implements {BaseService}
|
||||
*/
|
||||
class DiscountService extends BaseService {
|
||||
constructor({
|
||||
@@ -17,7 +14,6 @@ class DiscountService extends BaseService {
|
||||
discountRuleRepository,
|
||||
giftCardRepository,
|
||||
totalsService,
|
||||
productVariantService,
|
||||
productService,
|
||||
regionService,
|
||||
eventBusService,
|
||||
@@ -80,21 +76,13 @@ class DiscountService extends BaseService {
|
||||
id: Validator.string().optional(),
|
||||
description: Validator.string().optional(),
|
||||
type: Validator.string().required(),
|
||||
value: Validator.number()
|
||||
.min(0)
|
||||
.required(),
|
||||
value: Validator.number().min(0).required(),
|
||||
allocation: Validator.string().required(),
|
||||
valid_for: Validator.array().optional(),
|
||||
created_at: Validator.date().optional(),
|
||||
updated_at: Validator.date()
|
||||
.allow(null)
|
||||
.optional(),
|
||||
deleted_at: Validator.date()
|
||||
.allow(null)
|
||||
.optional(),
|
||||
metadata: Validator.object()
|
||||
.allow(null)
|
||||
.optional(),
|
||||
updated_at: Validator.date().allow(null).optional(),
|
||||
deleted_at: Validator.date().allow(null).optional(),
|
||||
metadata: Validator.object().allow(null).optional(),
|
||||
})
|
||||
|
||||
const { value, error } = schema.validate(discountRule)
|
||||
@@ -117,6 +105,7 @@ class DiscountService extends BaseService {
|
||||
|
||||
/**
|
||||
* @param {Object} selector - the query object for find
|
||||
* @param {Object} config - the config object containing query settings
|
||||
* @return {Promise} the result of the find operation
|
||||
*/
|
||||
async list(selector = {}, config = { relations: [], skip: 0, take: 10 }) {
|
||||
@@ -130,6 +119,7 @@ class DiscountService extends BaseService {
|
||||
|
||||
/**
|
||||
* @param {Object} selector - the query object for find
|
||||
* @param {Object} config - the config object containing query settings
|
||||
* @return {Promise} the result of the find operation
|
||||
*/
|
||||
async listAndCount(
|
||||
@@ -153,11 +143,11 @@ class DiscountService extends BaseService {
|
||||
|
||||
delete where.code
|
||||
|
||||
query.where = qb => {
|
||||
query.where = (qb) => {
|
||||
qb.where(where)
|
||||
|
||||
qb.andWhere(
|
||||
new Brackets(qb => {
|
||||
new Brackets((qb) => {
|
||||
qb.where({ code: ILike(`%${q}%`) })
|
||||
})
|
||||
)
|
||||
@@ -176,16 +166,20 @@ class DiscountService extends BaseService {
|
||||
* @return {Promise} the result of the create operation
|
||||
*/
|
||||
async create(discount) {
|
||||
return this.atomicPhase_(async manager => {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
const discountRepo = manager.getCustomRepository(this.discountRepository_)
|
||||
const ruleRepo = manager.getCustomRepository(this.discountRuleRepository_)
|
||||
|
||||
if (discount.rule?.valid_for) {
|
||||
discount.rule.valid_for = discount.rule.valid_for.map((id) => ({ id }))
|
||||
}
|
||||
|
||||
const validatedRule = this.validateDiscountRule_(discount.rule)
|
||||
|
||||
if (discount.regions) {
|
||||
discount.regions = await Promise.all(
|
||||
discount.regions.map(regionId =>
|
||||
this.regionService_.retrieve(regionId)
|
||||
discount.regions.map((regionId) =>
|
||||
this.regionService_.withTransaction(manager).retrieve(regionId)
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -205,6 +199,7 @@ class DiscountService extends BaseService {
|
||||
/**
|
||||
* Gets a discount by id.
|
||||
* @param {string} discountId - id of discount to retrieve
|
||||
* @param {Object} config - the config object containing query settings
|
||||
* @return {Promise<Discount>} the discount
|
||||
*/
|
||||
async retrieve(discountId, config = {}) {
|
||||
@@ -229,6 +224,7 @@ class DiscountService extends BaseService {
|
||||
/**
|
||||
* Gets a discount by discount code.
|
||||
* @param {string} discountCode - discount code of discount to retrieve
|
||||
* @param {array} relations - list of relations
|
||||
* @return {Promise<Discount>} the discount document
|
||||
*/
|
||||
async retrieveByCode(discountCode, relations = []) {
|
||||
@@ -265,7 +261,7 @@ class DiscountService extends BaseService {
|
||||
* @return {Promise} the result of the update operation
|
||||
*/
|
||||
async update(discountId, update) {
|
||||
return this.atomicPhase_(async manager => {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
const discountRepo = manager.getCustomRepository(this.discountRepository_)
|
||||
|
||||
const discount = await this.retrieve(discountId)
|
||||
@@ -283,7 +279,7 @@ class DiscountService extends BaseService {
|
||||
|
||||
if (regions) {
|
||||
discount.regions = await Promise.all(
|
||||
regions.map(regionId => this.regionService_.retrieve(regionId))
|
||||
regions.map((regionId) => this.regionService_.retrieve(regionId))
|
||||
)
|
||||
}
|
||||
|
||||
@@ -293,6 +289,11 @@ class DiscountService extends BaseService {
|
||||
|
||||
if (rule) {
|
||||
discount.rule = this.validateDiscountRule_(rule)
|
||||
if (rule.valid_for) {
|
||||
discount.rule.valid_for = discount.rule.valid_for.map((id) => ({
|
||||
id,
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
for (const [key, value] of Object.entries(rest)) {
|
||||
@@ -307,11 +308,11 @@ class DiscountService extends BaseService {
|
||||
/**
|
||||
* Creates a dynamic code for a discount id.
|
||||
* @param {string} discountId - the id of the discount to create a code for
|
||||
* @param {string} code - the code to identify the discount by
|
||||
* @param {Object} data - the object containing a code to identify the discount by
|
||||
* @return {Promise} the newly created dynamic code
|
||||
*/
|
||||
async createDynamicCode(discountId, data) {
|
||||
return this.atomicPhase_(async manager => {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
const discountRepo = manager.getCustomRepository(this.discountRepository_)
|
||||
|
||||
const discount = await this.retrieve(discountId)
|
||||
@@ -360,13 +361,15 @@ class DiscountService extends BaseService {
|
||||
* @return {Promise} the newly created dynamic code
|
||||
*/
|
||||
async deleteDynamicCode(discountId, code) {
|
||||
return this.atomicPhase_(async manager => {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
const discountRepo = manager.getCustomRepository(this.discountRepository_)
|
||||
const discount = await discountRepo.findOne({
|
||||
where: { parent_discount_id: discountId, code },
|
||||
})
|
||||
|
||||
if (!discount) return Promise.resolve()
|
||||
if (!discount) {
|
||||
return Promise.resolve()
|
||||
}
|
||||
|
||||
await discountRepo.softRemove(discount)
|
||||
|
||||
@@ -381,7 +384,7 @@ class DiscountService extends BaseService {
|
||||
* @return {Promise} the result of the update operation
|
||||
*/
|
||||
async addValidProduct(discountId, productId) {
|
||||
return this.atomicPhase_(async manager => {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
const discountRuleRepo = manager.getCustomRepository(
|
||||
this.discountRuleRepository_
|
||||
)
|
||||
@@ -392,7 +395,7 @@ class DiscountService extends BaseService {
|
||||
|
||||
const { rule } = discount
|
||||
|
||||
const exists = rule.valid_for.find(p => p.id === productId)
|
||||
const exists = rule.valid_for.find((p) => p.id === productId)
|
||||
// If product is already present, we return early
|
||||
if (exists) {
|
||||
return rule
|
||||
@@ -414,7 +417,7 @@ class DiscountService extends BaseService {
|
||||
* @return {Promise} the result of the update operation
|
||||
*/
|
||||
async removeValidProduct(discountId, productId) {
|
||||
return this.atomicPhase_(async manager => {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
const discountRuleRepo = manager.getCustomRepository(
|
||||
this.discountRuleRepository_
|
||||
)
|
||||
@@ -425,13 +428,13 @@ class DiscountService extends BaseService {
|
||||
|
||||
const { rule } = discount
|
||||
|
||||
const exists = rule.valid_for.find(p => p.id === productId)
|
||||
const exists = rule.valid_for.find((p) => p.id === productId)
|
||||
// If product is not present, we return early
|
||||
if (!exists) {
|
||||
return rule
|
||||
}
|
||||
|
||||
rule.valid_for = rule.valid_for.filter(p => p.id !== productId)
|
||||
rule.valid_for = rule.valid_for.filter((p) => p.id !== productId)
|
||||
|
||||
const updated = await discountRuleRepo.save(rule)
|
||||
return updated
|
||||
@@ -445,14 +448,14 @@ class DiscountService extends BaseService {
|
||||
* @return {Promise} the result of the update operation
|
||||
*/
|
||||
async addRegion(discountId, regionId) {
|
||||
return this.atomicPhase_(async manager => {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
const discountRepo = manager.getCustomRepository(this.discountRepository_)
|
||||
|
||||
const discount = await this.retrieve(discountId, {
|
||||
relations: ["regions"],
|
||||
})
|
||||
|
||||
const exists = discount.regions.find(r => r.id === regionId)
|
||||
const exists = discount.regions.find((r) => r.id === regionId)
|
||||
// If region is already present, we return early
|
||||
if (exists) {
|
||||
return discount
|
||||
@@ -474,20 +477,20 @@ class DiscountService extends BaseService {
|
||||
* @return {Promise} the result of the update operation
|
||||
*/
|
||||
async removeRegion(discountId, regionId) {
|
||||
return this.atomicPhase_(async manager => {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
const discountRepo = manager.getCustomRepository(this.discountRepository_)
|
||||
|
||||
const discount = await this.retrieve(discountId, {
|
||||
relations: ["regions"],
|
||||
})
|
||||
|
||||
const exists = discount.regions.find(r => r.id === regionId)
|
||||
const exists = discount.regions.find((r) => r.id === regionId)
|
||||
// If region is not present, we return early
|
||||
if (!exists) {
|
||||
return discount
|
||||
}
|
||||
|
||||
discount.regions = discount.regions.filter(r => r.id !== regionId)
|
||||
discount.regions = discount.regions.filter((r) => r.id !== regionId)
|
||||
|
||||
const updated = await discountRepo.save(discount)
|
||||
return updated
|
||||
@@ -500,12 +503,14 @@ class DiscountService extends BaseService {
|
||||
* @return {Promise} the result of the delete operation
|
||||
*/
|
||||
async delete(discountId) {
|
||||
return this.atomicPhase_(async manager => {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
const discountRepo = manager.getCustomRepository(this.discountRepository_)
|
||||
|
||||
const discount = await discountRepo.findOne({ where: { id: discountId } })
|
||||
|
||||
if (!discount) return Promise.resolve()
|
||||
if (!discount) {
|
||||
return Promise.resolve()
|
||||
}
|
||||
|
||||
await discountRepo.softRemove(discount)
|
||||
|
||||
@@ -515,7 +520,7 @@ class DiscountService extends BaseService {
|
||||
|
||||
/**
|
||||
* Decorates a discount.
|
||||
* @param {Discount} discount - the discount to decorate.
|
||||
* @param {string} discountId - id of discount to decorate
|
||||
* @param {string[]} fields - the fields to include.
|
||||
* @param {string[]} expandFields - fields to expand.
|
||||
* @return {Discount} return the decorated discount.
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
import _ from "lodash"
|
||||
import { BaseService } from "medusa-interfaces"
|
||||
import { MedusaError } from "medusa-core-utils"
|
||||
import { Brackets, ILike } from "typeorm"
|
||||
import { Brackets } from "typeorm"
|
||||
|
||||
/**
|
||||
* Handles draft orders
|
||||
* @implements BaseService
|
||||
* @implements {BaseService}
|
||||
*/
|
||||
class DraftOrderService extends BaseService {
|
||||
static Events = {
|
||||
@@ -106,7 +105,7 @@ class DraftOrderService extends BaseService {
|
||||
/**
|
||||
* Retrieves a draft order based on its associated cart id
|
||||
* @param {string} cartId - cart id that the draft orders's cart has
|
||||
* * @param {object} config - query object for findOne
|
||||
* @param {object} config - query object for findOne
|
||||
* @return {Promise<DraftOrder>} the draft order
|
||||
*/
|
||||
async retrieveByCartId(cartId, config = {}) {
|
||||
@@ -134,7 +133,7 @@ class DraftOrderService extends BaseService {
|
||||
* @return {Promise} empty promise
|
||||
*/
|
||||
async delete(draftOrderId) {
|
||||
return this.atomicPhase_(async manager => {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
const draftOrderRepo = manager.getCustomRepository(
|
||||
this.draftOrderRepository_
|
||||
)
|
||||
@@ -143,7 +142,9 @@ class DraftOrderService extends BaseService {
|
||||
where: { id: draftOrderId },
|
||||
})
|
||||
|
||||
if (!draftOrder) return Promise.resolve()
|
||||
if (!draftOrder) {
|
||||
return Promise.resolve()
|
||||
}
|
||||
|
||||
await draftOrderRepo.remove(draftOrder)
|
||||
|
||||
@@ -185,11 +186,11 @@ class DraftOrderService extends BaseService {
|
||||
},
|
||||
}
|
||||
|
||||
query.where = qb => {
|
||||
query.where = (qb) => {
|
||||
qb.where(where)
|
||||
|
||||
qb.andWhere(
|
||||
new Brackets(qb => {
|
||||
new Brackets((qb) => {
|
||||
qb.where(`cart.email ILIKE :q`, {
|
||||
q: `%${q}%`,
|
||||
}).orWhere(`draft_order.display_id::varchar(255) ILIKE :dId`, {
|
||||
@@ -230,7 +231,7 @@ class DraftOrderService extends BaseService {
|
||||
* @return {Promise<DraftOrder>} the created draft order
|
||||
*/
|
||||
async create(data) {
|
||||
return this.atomicPhase_(async manager => {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
const draftOrderRepo = manager.getCustomRepository(
|
||||
this.draftOrderRepository_
|
||||
)
|
||||
@@ -250,10 +251,10 @@ class DraftOrderService extends BaseService {
|
||||
}
|
||||
|
||||
const {
|
||||
items,
|
||||
shipping_methods,
|
||||
discounts,
|
||||
no_notification_order,
|
||||
items,
|
||||
...rest
|
||||
} = data
|
||||
|
||||
@@ -332,7 +333,7 @@ class DraftOrderService extends BaseService {
|
||||
* @return {Promise} the created order
|
||||
*/
|
||||
async registerCartCompletion(doId, orderId) {
|
||||
return this.atomicPhase_(async manager => {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
const draftOrderRepo = manager.getCustomRepository(
|
||||
this.draftOrderRepository_
|
||||
)
|
||||
@@ -350,10 +351,10 @@ class DraftOrderService extends BaseService {
|
||||
* Updates a draft order with the given data
|
||||
* @param {String} doId - id of the draft order
|
||||
* @param {DraftOrder} data - values to update the order with
|
||||
* @returns {Promise<DraftOrder>} the updated draft order
|
||||
* @return {Promise<DraftOrder>} the updated draft order
|
||||
*/
|
||||
async update(doId, data) {
|
||||
return this.atomicPhase_(async manager => {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
const doRepo = manager.getCustomRepository(this.draftOrderRepository_)
|
||||
const draftOrder = await this.retrieve(doId)
|
||||
let touched = false
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import _ from "lodash"
|
||||
import { BaseService } from "medusa-interfaces"
|
||||
import { MedusaError } from "medusa-core-utils"
|
||||
|
||||
/**
|
||||
* Handles Fulfillments
|
||||
* @implements BaseService
|
||||
* @extends BaseService
|
||||
*/
|
||||
class FulfillmentService extends BaseService {
|
||||
constructor({
|
||||
@@ -61,7 +60,7 @@ class FulfillmentService extends BaseService {
|
||||
}
|
||||
|
||||
partitionItems_(shippingMethods, items) {
|
||||
let partitioned = []
|
||||
const partitioned = []
|
||||
// partition order items to their dedicated shipping method
|
||||
for (const method of shippingMethods) {
|
||||
const temp = { shipping_method: method }
|
||||
@@ -95,12 +94,12 @@ class FulfillmentService extends BaseService {
|
||||
async getFulfillmentItems_(order, items, transformer) {
|
||||
const toReturn = await Promise.all(
|
||||
items.map(async ({ item_id, quantity }) => {
|
||||
const item = order.items.find(i => i.id === item_id)
|
||||
const item = order.items.find((i) => i.id === item_id)
|
||||
return transformer(item, quantity)
|
||||
})
|
||||
)
|
||||
|
||||
return toReturn.filter(i => !!i)
|
||||
return toReturn.filter((i) => !!i)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -137,6 +136,7 @@ class FulfillmentService extends BaseService {
|
||||
/**
|
||||
* Retrieves a fulfillment by its id.
|
||||
* @param {string} id - the id of the fulfillment to retrieve
|
||||
* @param {object} config - optional values to include with fulfillmentRepository query
|
||||
* @return {Fulfillment} the fulfillment
|
||||
*/
|
||||
async retrieve(id, config = {}) {
|
||||
@@ -165,11 +165,11 @@ class FulfillmentService extends BaseService {
|
||||
* those partitions.
|
||||
* @param {Order} order - order to create fulfillment for
|
||||
* @param {{ item_id: string, quantity: number}[]} itemsToFulfill - the items in the order to fulfill
|
||||
* @param {object} metadata - potential metadata to add
|
||||
* @param {object} custom - potential custom values to add
|
||||
* @return {Fulfillment[]} the created fulfillments
|
||||
*/
|
||||
async createFulfillment(order, itemsToFulfill, custom = {}) {
|
||||
return this.atomicPhase_(async manager => {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
const fulfillmentRepository = manager.getCustomRepository(
|
||||
this.fulfillmentRepository_
|
||||
)
|
||||
@@ -190,18 +190,19 @@ class FulfillmentService extends BaseService {
|
||||
const ful = fulfillmentRepository.create({
|
||||
...custom,
|
||||
provider_id: shipping_method.shipping_option.provider_id,
|
||||
items: items.map(i => ({ item_id: i.id, quantity: i.quantity })),
|
||||
items: items.map((i) => ({ item_id: i.id, quantity: i.quantity })),
|
||||
data: {},
|
||||
})
|
||||
|
||||
let result = await fulfillmentRepository.save(ful)
|
||||
const result = await fulfillmentRepository.save(ful)
|
||||
|
||||
result.data = await this.fulfillmentProviderService_.createFulfillment(
|
||||
shipping_method,
|
||||
items,
|
||||
{ ...order },
|
||||
{ ...result }
|
||||
)
|
||||
result.data =
|
||||
await this.fulfillmentProviderService_.createFulfillment(
|
||||
shipping_method,
|
||||
items,
|
||||
{ ...order },
|
||||
{ ...result }
|
||||
)
|
||||
|
||||
return fulfillmentRepository.save(result)
|
||||
})
|
||||
@@ -220,7 +221,7 @@ class FulfillmentService extends BaseService {
|
||||
*
|
||||
*/
|
||||
cancelFulfillment(fulfillmentOrId) {
|
||||
return this.atomicPhase_(async manager => {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
let id = fulfillmentOrId
|
||||
if (typeof fulfillmentOrId === "object") {
|
||||
id = fulfillmentOrId.id
|
||||
@@ -260,9 +261,9 @@ class FulfillmentService extends BaseService {
|
||||
|
||||
/**
|
||||
* Creates a shipment by marking a fulfillment as shipped. Adds
|
||||
* tracking numbers and potentially more metadata.
|
||||
* tracking links and potentially more metadata.
|
||||
* @param {Order} fulfillmentId - the fulfillment to ship
|
||||
* @param {TrackingLink[]} trackingNumbers - tracking numbers for the shipment
|
||||
* @param {TrackingLink[]} trackingLinks - tracking links for the shipment
|
||||
* @param {object} config - potential configuration settings, such as no_notification and metadata
|
||||
* @return {Fulfillment} the shipped fulfillment
|
||||
*/
|
||||
@@ -276,7 +277,7 @@ class FulfillmentService extends BaseService {
|
||||
) {
|
||||
const { metadata, no_notification } = config
|
||||
|
||||
return this.atomicPhase_(async manager => {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
const fulfillmentRepository = manager.getCustomRepository(
|
||||
this.fulfillmentRepository_
|
||||
)
|
||||
@@ -298,7 +299,7 @@ class FulfillmentService extends BaseService {
|
||||
const now = new Date()
|
||||
fulfillment.shipped_at = now
|
||||
|
||||
fulfillment.tracking_links = trackingLinks.map(tl =>
|
||||
fulfillment.tracking_links = trackingLinks.map((tl) =>
|
||||
trackingLinkRepo.create(tl)
|
||||
)
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import { Brackets } from "typeorm"
|
||||
|
||||
/**
|
||||
* Provides layer to manipulate gift cards.
|
||||
* @implements BaseService
|
||||
* @extends BaseService
|
||||
*/
|
||||
class GiftCardService extends BaseService {
|
||||
static Events = {
|
||||
@@ -101,7 +101,7 @@ class GiftCardService extends BaseService {
|
||||
.select(["gift_card.id"])
|
||||
.where(where)
|
||||
.andWhere(
|
||||
new Brackets(qb => {
|
||||
new Brackets((qb) => {
|
||||
return qb
|
||||
.where(`gift_card.code ILIKE :q`, { q: `%${q}%` })
|
||||
.orWhere(`display_id::varchar(255) ILIKE :dId`, { dId: `${q}` })
|
||||
@@ -111,14 +111,14 @@ class GiftCardService extends BaseService {
|
||||
|
||||
return giftCardRepo.findWithRelations(
|
||||
rels,
|
||||
raw.map(i => i.id)
|
||||
raw.map((i) => i.id)
|
||||
)
|
||||
}
|
||||
return giftCardRepo.findWithRelations(rels, query)
|
||||
}
|
||||
|
||||
async createTransaction(data) {
|
||||
return this.atomicPhase_(async manager => {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
const gctRepo = manager.getCustomRepository(this.giftCardTransactionRepo_)
|
||||
const created = gctRepo.create(data)
|
||||
const saved = await gctRepo.save(created)
|
||||
@@ -132,7 +132,7 @@ class GiftCardService extends BaseService {
|
||||
* @return {Promise<GiftCard>} the result of the create operation
|
||||
*/
|
||||
async create(giftCard) {
|
||||
return this.atomicPhase_(async manager => {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
const giftCardRepo = manager.getCustomRepository(this.giftCardRepository_)
|
||||
|
||||
if (!giftCard.region_id) {
|
||||
@@ -169,6 +169,7 @@ class GiftCardService extends BaseService {
|
||||
/**
|
||||
* Gets a gift card by id.
|
||||
* @param {string} giftCardId - id of gift card to retrieve
|
||||
* @param {object} config - optional values to include with gift card query
|
||||
* @return {Promise<GiftCard>} the gift card
|
||||
*/
|
||||
async retrieve(giftCardId, config = {}) {
|
||||
@@ -244,7 +245,7 @@ class GiftCardService extends BaseService {
|
||||
* @return {Promise} the result of the update operation
|
||||
*/
|
||||
async update(giftCardId, update) {
|
||||
return this.atomicPhase_(async manager => {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
const giftCardRepo = manager.getCustomRepository(this.giftCardRepository_)
|
||||
|
||||
const giftCard = await this.retrieve(giftCardId)
|
||||
@@ -285,12 +286,14 @@ class GiftCardService extends BaseService {
|
||||
* @return {Promise} the result of the delete operation
|
||||
*/
|
||||
async delete(giftCardId) {
|
||||
return this.atomicPhase_(async manager => {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
const giftCardRepo = manager.getCustomRepository(this.giftCardRepository_)
|
||||
|
||||
const giftCard = await giftCardRepo.findOne({ where: { id: giftCardId } })
|
||||
|
||||
if (!giftCard) return Promise.resolve()
|
||||
if (!giftCard) {
|
||||
return Promise.resolve()
|
||||
}
|
||||
|
||||
await giftCardRepo.softRemove(giftCard)
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { BaseService } from "medusa-interfaces"
|
||||
import { MedusaError } from "medusa-core-utils"
|
||||
import { v4 } from "uuid"
|
||||
import IdempotencyKeyModel from "../models/idempotency-key"
|
||||
|
||||
const KEY_LOCKED_TIMEOUT = 1000
|
||||
|
||||
@@ -28,7 +27,7 @@ class IdempotencyKeyService extends BaseService {
|
||||
* @return {Promise<IdempotencyKeyModel>} the existing or created idempotency key
|
||||
*/
|
||||
async initializeRequest(headerKey, reqMethod, reqParams, reqPath) {
|
||||
return this.atomicPhase_(async _ => {
|
||||
return this.atomicPhase_(async (_) => {
|
||||
// If idempotency key exists, return it
|
||||
let key = await this.retrieve(headerKey)
|
||||
|
||||
@@ -54,7 +53,7 @@ class IdempotencyKeyService extends BaseService {
|
||||
* @return {Promise<IdempotencyKeyModel>} the created idempotency key
|
||||
*/
|
||||
async create(payload) {
|
||||
return this.atomicPhase_(async manager => {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
const idempotencyKeyRepo = manager.getCustomRepository(
|
||||
this.idempotencyKeyRepository_
|
||||
)
|
||||
@@ -93,7 +92,7 @@ class IdempotencyKeyService extends BaseService {
|
||||
* @return {Promise} result of the update operation
|
||||
*/
|
||||
async lock(idempotencyKey) {
|
||||
return this.atomicPhase_(async manager => {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
const idempotencyKeyRepo = manager.getCustomRepository(
|
||||
this.idempotencyKeyRepository_
|
||||
)
|
||||
@@ -120,7 +119,7 @@ class IdempotencyKeyService extends BaseService {
|
||||
* @return {Promise} result of the update operation
|
||||
*/
|
||||
async update(idempotencyKey, update) {
|
||||
return this.atomicPhase_(async manager => {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
const idempotencyKeyRepo = manager.getCustomRepository(
|
||||
this.idempotencyKeyRepository_
|
||||
)
|
||||
@@ -148,7 +147,7 @@ class IdempotencyKeyService extends BaseService {
|
||||
*/
|
||||
async workStage(idempotencyKey, func) {
|
||||
try {
|
||||
return await this.atomicPhase_(async manager => {
|
||||
return await this.atomicPhase_(async (manager) => {
|
||||
let key
|
||||
|
||||
const { recovery_point, response_code, response_body } = await func(
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import { Validator, MedusaError } from "medusa-core-utils"
|
||||
import { MedusaError } from "medusa-core-utils"
|
||||
import { BaseService } from "medusa-interfaces"
|
||||
import _ from "lodash"
|
||||
|
||||
/**
|
||||
* Provides layer to manipulate line items.
|
||||
* @implements BaseService
|
||||
* @extends BaseService
|
||||
*/
|
||||
class LineItemService extends BaseService {
|
||||
constructor({
|
||||
@@ -67,6 +66,7 @@ class LineItemService extends BaseService {
|
||||
/**
|
||||
* Retrieves a line item by its id.
|
||||
* @param {string} id - the id of the line item to retrieve
|
||||
* @param {object} config - the config to be used at query building
|
||||
* @return {LineItem} the line item
|
||||
*/
|
||||
async retrieve(id, config = {}) {
|
||||
@@ -90,7 +90,7 @@ class LineItemService extends BaseService {
|
||||
}
|
||||
|
||||
async generate(variantId, regionId, quantity, config = {}) {
|
||||
return this.atomicPhase_(async manager => {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
const variant = await this.productVariantService_
|
||||
.withTransaction(manager)
|
||||
.retrieve(variantId, {
|
||||
@@ -142,7 +142,7 @@ class LineItemService extends BaseService {
|
||||
* @return {LineItem} the created line item
|
||||
*/
|
||||
async create(lineItem) {
|
||||
return this.atomicPhase_(async manager => {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
const lineItemRepository = manager.getCustomRepository(
|
||||
this.lineItemRepository_
|
||||
)
|
||||
@@ -160,7 +160,7 @@ class LineItemService extends BaseService {
|
||||
* @return {LineItem} the update line item
|
||||
*/
|
||||
async update(id, update) {
|
||||
return this.atomicPhase_(async manager => {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
const lineItemRepository = manager.getCustomRepository(
|
||||
this.lineItemRepository_
|
||||
)
|
||||
@@ -188,14 +188,16 @@ class LineItemService extends BaseService {
|
||||
* @return {Promise} the result of the delete operation
|
||||
*/
|
||||
async delete(id) {
|
||||
return this.atomicPhase_(async manager => {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
const lineItemRepository = manager.getCustomRepository(
|
||||
this.lineItemRepository_
|
||||
)
|
||||
|
||||
const lineItem = await lineItemRepository.findOne({ where: { id } })
|
||||
|
||||
if (!lineItem) return Promise.resolve()
|
||||
if (!lineItem) {
|
||||
return Promise.resolve()
|
||||
}
|
||||
|
||||
await lineItemRepository.remove(lineItem)
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { MedusaError } from "medusa-core-utils"
|
||||
import { BaseService } from "medusa-interfaces"
|
||||
import _ from "lodash"
|
||||
import { TransactionManager } from "typeorm"
|
||||
|
||||
class NoteService extends BaseService {
|
||||
@@ -47,7 +46,7 @@ class NoteService extends BaseService {
|
||||
* Retrieves a specific note.
|
||||
* @param {*} id - the id of the note to retrieve.
|
||||
* @param {*} config - any options needed to query for the result.
|
||||
* @returns {Promise} which resolves to the requested note.
|
||||
* @return {Promise} which resolves to the requested note.
|
||||
*/
|
||||
async retrieve(id, config = {}) {
|
||||
const noteRepo = this.manager_.getCustomRepository(this.noteRepository_)
|
||||
@@ -91,14 +90,14 @@ class NoteService extends BaseService {
|
||||
* Creates a note associated with a given author
|
||||
* @param {object} data - the note to create
|
||||
* @param {*} config - any configurations if needed, including meta data
|
||||
* @returns {Promise} resolves to the creation result
|
||||
* @return {Promise} resolves to the creation result
|
||||
*/
|
||||
async create(data, config = { metadata: {} }) {
|
||||
const { metadata } = config
|
||||
|
||||
const { resource_id, resource_type, value, author_id } = data
|
||||
|
||||
return this.atomicPhase_(async manager => {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
const noteRepo = manager.getCustomRepository(this.noteRepository_)
|
||||
|
||||
const toCreate = {
|
||||
@@ -124,10 +123,10 @@ class NoteService extends BaseService {
|
||||
* Updates a given note with a new value
|
||||
* @param {*} noteId - the id of the note to update
|
||||
* @param {*} value - the new value
|
||||
* @returns {Promise} resolves to the updated element
|
||||
* @return {Promise} resolves to the updated element
|
||||
*/
|
||||
async update(noteId, value) {
|
||||
return this.atomicPhase_(async manager => {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
const noteRepo = manager.getCustomRepository(this.noteRepository_)
|
||||
|
||||
const note = await this.retrieve(noteId, { relations: ["author"] })
|
||||
@@ -147,10 +146,10 @@ class NoteService extends BaseService {
|
||||
/**
|
||||
* Deletes a given note
|
||||
* @param {*} noteId - id of the note to delete
|
||||
* @returns {Promise}
|
||||
* @return {Promise}
|
||||
*/
|
||||
async delete(noteId) {
|
||||
return this.atomicPhase_(async manager => {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
const noteRepo = manager.getCustomRepository(this.noteRepository_)
|
||||
|
||||
const note = await this.retrieve(noteId)
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import { MedusaError } from "medusa-core-utils"
|
||||
import { BaseService } from "medusa-interfaces"
|
||||
import _ from "lodash"
|
||||
|
||||
/**
|
||||
* Provides layer to manipulate orchestrate notifications.
|
||||
* @implements BaseService
|
||||
* @extends BaseService
|
||||
*/
|
||||
class NotificationService extends BaseService {
|
||||
constructor(container) {
|
||||
@@ -34,6 +33,7 @@ class NotificationService extends BaseService {
|
||||
/**
|
||||
* Registers an attachment generator to the service. The generator can be
|
||||
* used to generate on demand invoices or other documents.
|
||||
* @param {object} service
|
||||
*/
|
||||
registerAttachmentGenerator(service) {
|
||||
this.attachmentGenerator_ = service
|
||||
@@ -42,16 +42,18 @@ class NotificationService extends BaseService {
|
||||
/**
|
||||
* Sets the service's manager to a given transaction manager.
|
||||
* @param {EntityManager} transactionManager - the manager to use
|
||||
* return {NotificationService} a cloned notification service
|
||||
* @return {NotificationService} a cloned notification service
|
||||
*/
|
||||
withTransaction(transactionManager) {
|
||||
if (!transactionManager) {
|
||||
return this
|
||||
}
|
||||
|
||||
const cloned = new LineItemService({
|
||||
const cloned = new NotificationService({
|
||||
manager: transactionManager,
|
||||
notificationProviderRepository: this.notificationProviderRepository_,
|
||||
notificationRepository: this.notificationRepository_,
|
||||
logger: this.logger_,
|
||||
})
|
||||
|
||||
cloned.transactionManager_ = transactionManager
|
||||
@@ -93,6 +95,7 @@ class NotificationService extends BaseService {
|
||||
/**
|
||||
* Retrieves a notification with a given id
|
||||
* @param {string} id - the id of the notification
|
||||
* @param {object} config - the configuration to apply to the query
|
||||
* @return {Notification} the notification
|
||||
*/
|
||||
async retrieve(id, config = {}) {
|
||||
@@ -158,6 +161,7 @@ class NotificationService extends BaseService {
|
||||
* order to allow for resends. Will log any errors that are encountered.
|
||||
* @param {string} eventName - the event to handle
|
||||
* @param {object} data - the data the event was sent with
|
||||
* @return {Promise} - the result of notification subscribed
|
||||
*/
|
||||
handleEvent(eventName, data) {
|
||||
const subs = this.subscribers_[eventName]
|
||||
@@ -169,8 +173,8 @@ class NotificationService extends BaseService {
|
||||
}
|
||||
|
||||
return Promise.all(
|
||||
subs.map(async providerId => {
|
||||
return this.send(eventName, data, providerId).catch(err => {
|
||||
subs.map(async (providerId) => {
|
||||
return this.send(eventName, data, providerId).catch((err) => {
|
||||
console.log(err)
|
||||
this.logger_.warn(
|
||||
`An error occured while ${providerId} was processing a notification for ${eventName}: ${err.message}`
|
||||
|
||||
@@ -178,6 +178,7 @@ class OrderService extends BaseService {
|
||||
|
||||
/**
|
||||
* @param {Object} selector - the query object for find
|
||||
* @param {Object} config - the config to be used for find
|
||||
* @return {Promise} the result of the find operation
|
||||
*/
|
||||
async list(
|
||||
@@ -187,9 +188,8 @@ class OrderService extends BaseService {
|
||||
const orderRepo = this.manager_.getCustomRepository(this.orderRepository_)
|
||||
const query = this.buildQuery_(selector, config)
|
||||
|
||||
const { select, relations, totalsToSelect } = this.transformQueryForTotals_(
|
||||
config
|
||||
)
|
||||
const { select, relations, totalsToSelect } =
|
||||
this.transformQueryForTotals_(config)
|
||||
|
||||
if (select && select.length) {
|
||||
query.select = select
|
||||
@@ -201,7 +201,7 @@ class OrderService extends BaseService {
|
||||
|
||||
const raw = await orderRepo.find(query)
|
||||
|
||||
return raw.map(r => this.decorateTotals_(r, totalsToSelect))
|
||||
return raw.map((r) => this.decorateTotals_(r, totalsToSelect))
|
||||
}
|
||||
|
||||
async listAndCount(
|
||||
@@ -231,11 +231,11 @@ class OrderService extends BaseService {
|
||||
},
|
||||
}
|
||||
|
||||
query.where = qb => {
|
||||
query.where = (qb) => {
|
||||
qb.where(where)
|
||||
|
||||
qb.andWhere(
|
||||
new Brackets(qb => {
|
||||
new Brackets((qb) => {
|
||||
qb.where(`shipping_address.first_name ILIKE :qfn`, {
|
||||
qfn: `%${q}%`,
|
||||
})
|
||||
@@ -246,20 +246,19 @@ class OrderService extends BaseService {
|
||||
}
|
||||
}
|
||||
|
||||
const { select, relations, totalsToSelect } = this.transformQueryForTotals_(
|
||||
config
|
||||
)
|
||||
const { select, relations, totalsToSelect } =
|
||||
this.transformQueryForTotals_(config)
|
||||
|
||||
if (select && select.length) {
|
||||
query.select = select
|
||||
}
|
||||
|
||||
let rels = relations
|
||||
const rels = relations
|
||||
delete query.relations
|
||||
|
||||
const raw = await orderRepo.findWithRelations(rels, query)
|
||||
const count = await orderRepo.count(query)
|
||||
const orders = raw.map(r => this.decorateTotals_(r, totalsToSelect))
|
||||
const orders = raw.map((r) => this.decorateTotals_(r, totalsToSelect))
|
||||
|
||||
return [orders, count]
|
||||
}
|
||||
@@ -287,15 +286,20 @@ class OrderService extends BaseService {
|
||||
"refundable_amount",
|
||||
"items.refundable",
|
||||
"swaps.additional_items.refundable",
|
||||
"claims.additional_items.refundable",
|
||||
]
|
||||
|
||||
const totalsToSelect = select.filter(v => totalFields.includes(v))
|
||||
const totalsToSelect = select.filter((v) => totalFields.includes(v))
|
||||
if (totalsToSelect.length > 0) {
|
||||
const relationSet = new Set(relations)
|
||||
relationSet.add("items")
|
||||
relationSet.add("swaps")
|
||||
relationSet.add("swaps.additional_items")
|
||||
relationSet.add("claims")
|
||||
relationSet.add("claims.additional_items")
|
||||
relationSet.add("discounts")
|
||||
relationSet.add("discounts.rule")
|
||||
relationSet.add("discounts.rule.valid_for")
|
||||
relationSet.add("gift_cards")
|
||||
relationSet.add("gift_card_transactions")
|
||||
relationSet.add("refunds")
|
||||
@@ -303,7 +307,7 @@ class OrderService extends BaseService {
|
||||
relationSet.add("region")
|
||||
relations = [...relationSet]
|
||||
|
||||
select = select.filter(v => !totalFields.includes(v))
|
||||
select = select.filter((v) => !totalFields.includes(v))
|
||||
}
|
||||
|
||||
return {
|
||||
@@ -316,15 +320,15 @@ class OrderService extends BaseService {
|
||||
/**
|
||||
* Gets an order by id.
|
||||
* @param {string} orderId - id of order to retrieve
|
||||
* @param {Object} config - config of order to retrieve
|
||||
* @return {Promise<Order>} the order document
|
||||
*/
|
||||
async retrieve(orderId, config = {}) {
|
||||
const orderRepo = this.manager_.getCustomRepository(this.orderRepository_)
|
||||
const validatedId = this.validateId_(orderId)
|
||||
|
||||
const { select, relations, totalsToSelect } = this.transformQueryForTotals_(
|
||||
config
|
||||
)
|
||||
const { select, relations, totalsToSelect } =
|
||||
this.transformQueryForTotals_(config)
|
||||
|
||||
const query = {
|
||||
where: { id: validatedId },
|
||||
@@ -355,14 +359,14 @@ class OrderService extends BaseService {
|
||||
/**
|
||||
* Gets an order by cart id.
|
||||
* @param {string} cartId - cart id to find order
|
||||
* @param {Object} config - the config to be used to find order
|
||||
* @return {Promise<Order>} the order document
|
||||
*/
|
||||
async retrieveByCartId(cartId, config = {}) {
|
||||
const orderRepo = this.manager_.getCustomRepository(this.orderRepository_)
|
||||
|
||||
const { select, relations, totalsToSelect } = this.transformQueryForTotals_(
|
||||
config
|
||||
)
|
||||
const { select, relations, totalsToSelect } =
|
||||
this.transformQueryForTotals_(config)
|
||||
|
||||
const query = {
|
||||
where: { cart_id: cartId },
|
||||
@@ -395,7 +399,7 @@ class OrderService extends BaseService {
|
||||
* @return {Promise<Order>} the order document
|
||||
*/
|
||||
async existsByCartId(cartId) {
|
||||
const order = await this.retrieveByCartId(cartId).catch(_ => undefined)
|
||||
const order = await this.retrieveByCartId(cartId).catch((_) => undefined)
|
||||
if (!order) {
|
||||
return false
|
||||
}
|
||||
@@ -407,7 +411,7 @@ class OrderService extends BaseService {
|
||||
* @return {Promise} the result of the find operation
|
||||
*/
|
||||
async completeOrder(orderId) {
|
||||
return this.atomicPhase_(async manager => {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
const order = await this.retrieve(orderId)
|
||||
|
||||
if (order.status === "canceled") {
|
||||
@@ -426,7 +430,7 @@ class OrderService extends BaseService {
|
||||
}
|
||||
)
|
||||
|
||||
await completeOrderJob.finished().catch(error => {
|
||||
await completeOrderJob.finished().catch((error) => {
|
||||
throw error
|
||||
})
|
||||
|
||||
@@ -443,7 +447,7 @@ class OrderService extends BaseService {
|
||||
* @return {Promise} resolves to the creation result.
|
||||
*/
|
||||
async createFromCart(cartId) {
|
||||
return this.atomicPhase_(async manager => {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
const cart = await this.cartService_
|
||||
.withTransaction(manager)
|
||||
.retrieve(cartId, {
|
||||
@@ -453,6 +457,8 @@ class OrderService extends BaseService {
|
||||
"payment",
|
||||
"items",
|
||||
"discounts",
|
||||
"discounts.rule",
|
||||
"discounts.rule.valid_for",
|
||||
"gift_cards",
|
||||
"shipping_methods",
|
||||
],
|
||||
@@ -615,6 +621,7 @@ class OrderService extends BaseService {
|
||||
* @param {string} fulfillmentId - the fulfillment that has now been shipped
|
||||
* @param {TrackingLink[]} trackingLinks - array of tracking numebers
|
||||
* associated with the shipment
|
||||
* @param {Object} config - the config of the order that has been shipped
|
||||
* @param {Dictionary<String, String>} metadata - optional metadata to add to
|
||||
* the fulfillment
|
||||
* @return {order} the resulting order following the update.
|
||||
@@ -630,7 +637,7 @@ class OrderService extends BaseService {
|
||||
) {
|
||||
const { metadata, no_notification } = config
|
||||
|
||||
return this.atomicPhase_(async manager => {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
const order = await this.retrieve(orderId, { relations: ["items"] })
|
||||
const shipment = await this.fulfillmentService_.retrieve(fulfillmentId)
|
||||
|
||||
@@ -662,7 +669,7 @@ class OrderService extends BaseService {
|
||||
|
||||
order.fulfillment_status = "shipped"
|
||||
for (const item of order.items) {
|
||||
const shipped = shipmentRes.items.find(si => si.item_id === item.id)
|
||||
const shipped = shipmentRes.items.find((si) => si.item_id === item.id)
|
||||
if (shipped) {
|
||||
const shippedQty = (item.shipped_quantity || 0) + shipped.quantity
|
||||
if (shippedQty !== item.quantity) {
|
||||
@@ -696,11 +703,11 @@ class OrderService extends BaseService {
|
||||
|
||||
/**
|
||||
* Creates an order
|
||||
* @param {object} order - the order to create
|
||||
* @param {object} data - the data to create an order
|
||||
* @return {Promise} resolves to the creation result.
|
||||
*/
|
||||
async create(data) {
|
||||
return this.atomicPhase_(async manager => {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
const orderRepo = manager.getCustomRepository(this.orderRepository_)
|
||||
const order = orderRepo.create(data)
|
||||
const result = await orderRepo.save(order)
|
||||
@@ -716,7 +723,7 @@ class OrderService extends BaseService {
|
||||
|
||||
/**
|
||||
* Updates the order's billing address.
|
||||
* @param {string} orderId - the id of the order to update
|
||||
* @param {object} order - the order to update
|
||||
* @param {object} address - the value to set the billing address to
|
||||
* @return {Promise} the result of the update operation
|
||||
*/
|
||||
@@ -751,7 +758,7 @@ class OrderService extends BaseService {
|
||||
|
||||
/**
|
||||
* Updates the order's shipping address.
|
||||
* @param {string} orderId - the id of the order to update
|
||||
* @param {object} order - the order to update
|
||||
* @param {object} address - the value to set the shipping address to
|
||||
* @return {Promise} the result of the update operation
|
||||
*/
|
||||
@@ -783,7 +790,7 @@ class OrderService extends BaseService {
|
||||
}
|
||||
|
||||
async addShippingMethod(orderId, optionId, data, config = {}) {
|
||||
return this.atomicPhase_(async manager => {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
const order = await this.retrieve(orderId, {
|
||||
select: ["subtotal"],
|
||||
relations: [
|
||||
@@ -841,7 +848,7 @@ class OrderService extends BaseService {
|
||||
* @return {Promise} resolves to the update result.
|
||||
*/
|
||||
async update(orderId, update) {
|
||||
return this.atomicPhase_(async manager => {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
const order = await this.retrieve(orderId)
|
||||
|
||||
if (order.status === "canceled") {
|
||||
@@ -870,13 +877,7 @@ class OrderService extends BaseService {
|
||||
)
|
||||
}
|
||||
|
||||
const {
|
||||
metadata,
|
||||
items,
|
||||
billing_address,
|
||||
shipping_address,
|
||||
...rest
|
||||
} = update
|
||||
const { ...rest } = update
|
||||
|
||||
if ("metadata" in update) {
|
||||
order.metadata = this.setMetadata_(order, update.metadata)
|
||||
@@ -928,7 +929,7 @@ class OrderService extends BaseService {
|
||||
* @return {Promise} result of the update operation.
|
||||
*/
|
||||
async cancel(orderId) {
|
||||
return this.atomicPhase_(async manager => {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
const order = await this.retrieve(orderId, {
|
||||
relations: [
|
||||
"fulfillments",
|
||||
@@ -948,16 +949,16 @@ class OrderService extends BaseService {
|
||||
}
|
||||
|
||||
const throwErrorIf = (arr, pred, type) =>
|
||||
arr?.filter(pred).find(_ => {
|
||||
arr?.filter(pred).find((_) => {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.NOT_ALLOWED,
|
||||
`All ${type} must be canceled before canceling an order`
|
||||
)
|
||||
})
|
||||
const notCanceled = o => !o.canceled_at
|
||||
const notCanceled = (o) => !o.canceled_at
|
||||
|
||||
throwErrorIf(order.fulfillments, notCanceled, "fulfillments")
|
||||
throwErrorIf(order.returns, r => r.status !== "canceled", "returns")
|
||||
throwErrorIf(order.returns, (r) => r.status !== "canceled", "returns")
|
||||
throwErrorIf(order.swaps, notCanceled, "swaps")
|
||||
throwErrorIf(order.claims, notCanceled, "claims")
|
||||
|
||||
@@ -996,7 +997,7 @@ class OrderService extends BaseService {
|
||||
* @return {Promise} result of the update operation.
|
||||
*/
|
||||
async capturePayment(orderId) {
|
||||
return this.atomicPhase_(async manager => {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
const orderRepo = manager.getCustomRepository(this.orderRepository_)
|
||||
const order = await this.retrieve(orderId, { relations: ["payments"] })
|
||||
|
||||
@@ -1013,7 +1014,7 @@ class OrderService extends BaseService {
|
||||
const result = await this.paymentProviderService_
|
||||
.withTransaction(manager)
|
||||
.capturePayment(p)
|
||||
.catch(err => {
|
||||
.catch((err) => {
|
||||
this.eventBus_
|
||||
.withTransaction(manager)
|
||||
.emit(OrderService.Events.PAYMENT_CAPTURE_FAILED, {
|
||||
@@ -1035,7 +1036,7 @@ class OrderService extends BaseService {
|
||||
}
|
||||
|
||||
order.payments = payments
|
||||
order.payment_status = payments.every(p => p.captured_at !== null)
|
||||
order.payment_status = payments.every((p) => p.captured_at !== null)
|
||||
? "captured"
|
||||
: "requires_action"
|
||||
|
||||
@@ -1091,6 +1092,8 @@ class OrderService extends BaseService {
|
||||
* we need to partition the order items, such that they can be sent
|
||||
* to their respective fulfillment provider.
|
||||
* @param {string} orderId - id of order to cancel.
|
||||
* @param {Object} itemsToFulfill - items to fulfil.
|
||||
* @param {Object} config - the config to cancel.
|
||||
* @return {Promise} result of the update operation.
|
||||
*/
|
||||
async createFulfillment(
|
||||
@@ -1103,7 +1106,7 @@ class OrderService extends BaseService {
|
||||
) {
|
||||
const { metadata, no_notification } = config
|
||||
|
||||
return this.atomicPhase_(async manager => {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
// NOTE: we are telling the service to calculate all totals for us which
|
||||
// will add to what is fetched from the database. We want this to happen
|
||||
// so that we get all order details. These will thereafter be forwarded
|
||||
@@ -1119,6 +1122,8 @@ class OrderService extends BaseService {
|
||||
],
|
||||
relations: [
|
||||
"discounts",
|
||||
"discounts.rule",
|
||||
"discounts.rule.valid_for",
|
||||
"region",
|
||||
"fulfillments",
|
||||
"shipping_address",
|
||||
@@ -1163,7 +1168,7 @@ class OrderService extends BaseService {
|
||||
// Update all line items to reflect fulfillment
|
||||
for (const item of order.items) {
|
||||
const fulfillmentItem = successfullyFulfilled.find(
|
||||
f => item.id === f.item_id
|
||||
(f) => item.id === f.item_id
|
||||
)
|
||||
|
||||
if (fulfillmentItem) {
|
||||
@@ -1211,10 +1216,10 @@ class OrderService extends BaseService {
|
||||
/**
|
||||
* Cancels a fulfillment (if related to an order)
|
||||
* @param {string} fulfillmentId - the ID of the fulfillment to cancel
|
||||
* @returns updated order
|
||||
* @return {Promise} updated order
|
||||
*/
|
||||
async cancelFulfillment(fulfillmentId) {
|
||||
return this.atomicPhase_(async manager => {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
const canceled = await this.fulfillmentService_
|
||||
.withTransaction(manager)
|
||||
.cancelFulfillment(fulfillmentId)
|
||||
@@ -1258,12 +1263,12 @@ class OrderService extends BaseService {
|
||||
async getFulfillmentItems_(order, items, transformer) {
|
||||
const toReturn = await Promise.all(
|
||||
items.map(async ({ item_id, quantity }) => {
|
||||
const item = order.items.find(i => i.id.equals(item_id))
|
||||
const item = order.items.find((i) => i.id.equals(item_id))
|
||||
return transformer(item, quantity)
|
||||
})
|
||||
)
|
||||
|
||||
return toReturn.filter(i => !!i)
|
||||
return toReturn.filter((i) => !!i)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1273,7 +1278,7 @@ class OrderService extends BaseService {
|
||||
* @return {Promise} the result of the update operation
|
||||
*/
|
||||
async archive(orderId) {
|
||||
return this.atomicPhase_(async manager => {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
const order = await this.retrieve(orderId)
|
||||
|
||||
if (order.status !== ("completed" || "refunded")) {
|
||||
@@ -1292,6 +1297,12 @@ class OrderService extends BaseService {
|
||||
|
||||
/**
|
||||
* Refunds a given amount back to the customer.
|
||||
* @param {string} orderId - id of the order to refund.
|
||||
* @param {float} refundAmount - the amount to refund.
|
||||
* @param {string} reason - the reason to refund.
|
||||
* @param {string} note - note for refund.
|
||||
* @param {Object} config - the config for refund.
|
||||
* @return {Promise} the result of the refund operation.
|
||||
*/
|
||||
async createRefund(
|
||||
orderId,
|
||||
@@ -1304,7 +1315,7 @@ class OrderService extends BaseService {
|
||||
) {
|
||||
const { no_notification } = config
|
||||
|
||||
return this.atomicPhase_(async manager => {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
const order = await this.retrieve(orderId, {
|
||||
select: ["refundable_amount", "total", "refunded_total"],
|
||||
relations: ["payments"],
|
||||
@@ -1374,7 +1385,7 @@ class OrderService extends BaseService {
|
||||
}
|
||||
|
||||
if (totalsFields.includes("items.refundable")) {
|
||||
order.items = order.items.map(i => ({
|
||||
order.items = order.items.map((i) => ({
|
||||
...i,
|
||||
refundable: this.totalsService_.getLineItemRefund(order, {
|
||||
...i,
|
||||
@@ -1389,7 +1400,23 @@ class OrderService extends BaseService {
|
||||
order.swaps.length
|
||||
) {
|
||||
for (const s of order.swaps) {
|
||||
s.additional_items = s.additional_items.map(i => ({
|
||||
s.additional_items = s.additional_items.map((i) => ({
|
||||
...i,
|
||||
refundable: this.totalsService_.getLineItemRefund(order, {
|
||||
...i,
|
||||
quantity: i.quantity - (i.returned_quantity || 0),
|
||||
}),
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
totalsFields.includes("claims.additional_items.refundable") &&
|
||||
order.claims &&
|
||||
order.claims.length
|
||||
) {
|
||||
for (const c of order.claims) {
|
||||
c.additional_items = c.additional_items.map((i) => ({
|
||||
...i,
|
||||
refundable: this.totalsService_.getLineItemRefund(order, {
|
||||
...i,
|
||||
@@ -1412,10 +1439,11 @@ class OrderService extends BaseService {
|
||||
* mismatches.
|
||||
* @param {string} orderId - the order to return.
|
||||
* @param {object} receivedReturn - the received return
|
||||
* @param {float} customRefundAmount - the custom refund amount return
|
||||
* @return {Promise} the result of the update operation
|
||||
*/
|
||||
async registerReturnReceived(orderId, receivedReturn, customRefundAmount) {
|
||||
return this.atomicPhase_(async manager => {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
const order = await this.retrieve(orderId, {
|
||||
select: ["total", "refunded_total", "refundable_amount"],
|
||||
relations: ["items", "returns", "payments"],
|
||||
@@ -1435,7 +1463,7 @@ class OrderService extends BaseService {
|
||||
)
|
||||
}
|
||||
|
||||
let refundAmount = customRefundAmount || receivedReturn.refund_amount
|
||||
const refundAmount = customRefundAmount || receivedReturn.refund_amount
|
||||
|
||||
const orderRepo = manager.getCustomRepository(this.orderRepository_)
|
||||
|
||||
@@ -1504,7 +1532,7 @@ class OrderService extends BaseService {
|
||||
const keyPath = `metadata.${key}`
|
||||
return this.orderModel_
|
||||
.updateOne({ _id: validatedId }, { $unset: { [keyPath]: "" } })
|
||||
.catch(err => {
|
||||
.catch((err) => {
|
||||
throw new MedusaError(MedusaError.Types.DB_ERROR, err.message)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ class ProductVariantService extends BaseService {
|
||||
static Events = {
|
||||
UPDATED: "product-variant.updated",
|
||||
CREATED: "product-variant.created",
|
||||
DELETED: "product-variant.deleted",
|
||||
}
|
||||
|
||||
/** @param { productVariantModel: (ProductVariantModel) } */
|
||||
@@ -118,7 +119,7 @@ class ProductVariantService extends BaseService {
|
||||
* @return {Promise} resolves to the creation result.
|
||||
*/
|
||||
async create(productOrProductId, variant) {
|
||||
return this.atomicPhase_(async manager => {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
const productRepo = manager.getCustomRepository(this.productRepository_)
|
||||
const variantRepo = manager.getCustomRepository(
|
||||
this.productVariantRepository_
|
||||
@@ -147,8 +148,8 @@ class ProductVariantService extends BaseService {
|
||||
)
|
||||
}
|
||||
|
||||
product.options.forEach(option => {
|
||||
if (!variant.options.find(vo => option.id === vo.option_id)) {
|
||||
product.options.forEach((option) => {
|
||||
if (!variant.options.find((vo) => option.id === vo.option_id)) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
`Variant options do not contain value for ${option.title}`
|
||||
@@ -157,10 +158,10 @@ class ProductVariantService extends BaseService {
|
||||
})
|
||||
|
||||
let variantExists = undefined
|
||||
variantExists = product.variants.find(v => {
|
||||
return v.options.every(option => {
|
||||
variantExists = product.variants.find((v) => {
|
||||
return v.options.every((option) => {
|
||||
const variantOption = variant.options.find(
|
||||
o => option.option_id === o.option_id
|
||||
(o) => option.option_id === o.option_id
|
||||
)
|
||||
|
||||
return option.value === variantOption.value
|
||||
@@ -206,6 +207,7 @@ class ProductVariantService extends BaseService {
|
||||
.withTransaction(manager)
|
||||
.emit(ProductVariantService.Events.CREATED, {
|
||||
id: result.id,
|
||||
product_id: result.product_id,
|
||||
})
|
||||
|
||||
return result
|
||||
@@ -218,7 +220,7 @@ class ProductVariantService extends BaseService {
|
||||
* @return {Promise}
|
||||
*/
|
||||
async publish(variantId) {
|
||||
return this.atomicPhase_(async manager => {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
const variantRepo = manager.getCustomRepository(
|
||||
this.productVariantRepository_
|
||||
)
|
||||
@@ -233,6 +235,7 @@ class ProductVariantService extends BaseService {
|
||||
.withTransaction(manager)
|
||||
.emit(ProductVariantService.Events.UPDATED, {
|
||||
id: result.id,
|
||||
product_id: result.product_id,
|
||||
})
|
||||
|
||||
return result
|
||||
@@ -249,7 +252,7 @@ class ProductVariantService extends BaseService {
|
||||
* @return {Promise} resolves to the update result.
|
||||
*/
|
||||
async update(variantOrVariantId, update) {
|
||||
return this.atomicPhase_(async manager => {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
const variantRepo = manager.getCustomRepository(
|
||||
this.productVariantRepository_
|
||||
)
|
||||
@@ -309,6 +312,7 @@ class ProductVariantService extends BaseService {
|
||||
.withTransaction(manager)
|
||||
.emit(ProductVariantService.Events.UPDATED, {
|
||||
id: result.id,
|
||||
product_id: result.product_id,
|
||||
fields: Object.keys(update),
|
||||
})
|
||||
return result
|
||||
@@ -324,7 +328,7 @@ class ProductVariantService extends BaseService {
|
||||
* @return {Promise} the result of the update operation
|
||||
*/
|
||||
async setCurrencyPrice(variantId, price) {
|
||||
return this.atomicPhase_(async manager => {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
const moneyAmountRepo = manager.getCustomRepository(
|
||||
this.moneyAmountRepository_
|
||||
)
|
||||
@@ -363,7 +367,7 @@ class ProductVariantService extends BaseService {
|
||||
* @return {number} the price specific to the region
|
||||
*/
|
||||
async getRegionPrice(variantId, regionId) {
|
||||
return this.atomicPhase_(async manager => {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
const moneyAmountRepo = manager.getCustomRepository(
|
||||
this.moneyAmountRepository_
|
||||
)
|
||||
@@ -411,7 +415,7 @@ class ProductVariantService extends BaseService {
|
||||
* @return {Promise} the result of the update operation
|
||||
*/
|
||||
async setRegionPrice(variantId, price) {
|
||||
return this.atomicPhase_(async manager => {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
const moneyAmountRepo = manager.getCustomRepository(
|
||||
this.moneyAmountRepository_
|
||||
)
|
||||
@@ -448,7 +452,7 @@ class ProductVariantService extends BaseService {
|
||||
* @return {Promise} the result of the update operation.
|
||||
*/
|
||||
async updateOptionValue(variantId, optionId, optionValue) {
|
||||
return this.atomicPhase_(async manager => {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
const productOptionValueRepo = manager.getCustomRepository(
|
||||
this.productOptionValueRepository_
|
||||
)
|
||||
@@ -483,7 +487,7 @@ class ProductVariantService extends BaseService {
|
||||
* @return {Promise} the result of the update operation.
|
||||
*/
|
||||
async addOptionValue(variantId, optionId, optionValue) {
|
||||
return this.atomicPhase_(async manager => {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
const productOptionValueRepo = manager.getCustomRepository(
|
||||
this.productOptionValueRepository_
|
||||
)
|
||||
@@ -507,7 +511,7 @@ class ProductVariantService extends BaseService {
|
||||
* @return {Promise} empty promise
|
||||
*/
|
||||
async deleteOptionValue(variantId, optionId) {
|
||||
return this.atomicPhase_(async manager => {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
const productOptionValueRepo = manager.getCustomRepository(
|
||||
this.productOptionValueRepository_
|
||||
)
|
||||
@@ -557,7 +561,7 @@ class ProductVariantService extends BaseService {
|
||||
},
|
||||
}
|
||||
|
||||
query.where = qb => {
|
||||
query.where = (qb) => {
|
||||
qb.where(where).andWhere([
|
||||
{ sku: ILike(`%${q}%`) },
|
||||
{ title: ILike(`%${q}%`) },
|
||||
@@ -577,7 +581,7 @@ class ProductVariantService extends BaseService {
|
||||
* @return {Promise} empty promise
|
||||
*/
|
||||
async delete(variantId) {
|
||||
return this.atomicPhase_(async manager => {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
const variantRepo = manager.getCustomRepository(
|
||||
this.productVariantRepository_
|
||||
)
|
||||
@@ -588,6 +592,13 @@ class ProductVariantService extends BaseService {
|
||||
|
||||
await variantRepo.softRemove(variant)
|
||||
|
||||
await this.eventBus_
|
||||
.withTransaction(manager)
|
||||
.emit(ProductVariantService.Events.DELETED, {
|
||||
id: variant.id,
|
||||
product_id: variant.product_id,
|
||||
})
|
||||
|
||||
return Promise.resolve()
|
||||
})
|
||||
}
|
||||
|
||||
@@ -8,9 +8,11 @@ import { Brackets } from "typeorm"
|
||||
* @implements BaseService
|
||||
*/
|
||||
class ProductService extends BaseService {
|
||||
static IndexName = `products`
|
||||
static Events = {
|
||||
UPDATED: "product.updated",
|
||||
CREATED: "product.created",
|
||||
DELETED: "product.deleted",
|
||||
}
|
||||
|
||||
constructor({
|
||||
@@ -24,6 +26,7 @@ class ProductService extends BaseService {
|
||||
productTypeRepository,
|
||||
productTagRepository,
|
||||
imageRepository,
|
||||
searchService,
|
||||
}) {
|
||||
super()
|
||||
|
||||
@@ -56,6 +59,9 @@ class ProductService extends BaseService {
|
||||
|
||||
/** @private @const {ImageRepository} */
|
||||
this.imageRepository_ = imageRepository
|
||||
|
||||
/** @private @const {SearchService} */
|
||||
this.searchService_ = searchService
|
||||
}
|
||||
|
||||
withTransaction(transactionManager) {
|
||||
@@ -122,7 +128,7 @@ class ProductService extends BaseService {
|
||||
.select(["product.id"])
|
||||
.where(where)
|
||||
.andWhere(
|
||||
new Brackets(qb => {
|
||||
new Brackets((qb) => {
|
||||
qb.where(`product.description ILIKE :q`, { q: `%${q}%` })
|
||||
.orWhere(`product.title ILIKE :q`, { q: `%${q}%` })
|
||||
.orWhere(`variant.title ILIKE :q`, { q: `%${q}%` })
|
||||
@@ -134,7 +140,7 @@ class ProductService extends BaseService {
|
||||
|
||||
return productRepo.findWithRelations(
|
||||
rels,
|
||||
raw.map(i => i.id)
|
||||
raw.map((i) => i.id)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -276,7 +282,7 @@ class ProductService extends BaseService {
|
||||
* @return {Promise} resolves to the creation result.
|
||||
*/
|
||||
async create(productObject) {
|
||||
return this.atomicPhase_(async manager => {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
const productRepo = manager.getCustomRepository(this.productRepository_)
|
||||
const optionRepo = manager.getCustomRepository(
|
||||
this.productOptionRepository_
|
||||
@@ -295,7 +301,7 @@ class ProductService extends BaseService {
|
||||
|
||||
let product = productRepo.create(rest)
|
||||
|
||||
if (images && images.length) {
|
||||
if (images) {
|
||||
product.images = await this.upsertImages_(images)
|
||||
}
|
||||
|
||||
@@ -310,7 +316,7 @@ class ProductService extends BaseService {
|
||||
product = await productRepo.save(product)
|
||||
|
||||
product.options = await Promise.all(
|
||||
options.map(async o => {
|
||||
options.map(async (o) => {
|
||||
const res = optionRepo.create({ ...o, product_id: product.id })
|
||||
await optionRepo.save(res)
|
||||
return res
|
||||
@@ -360,7 +366,7 @@ class ProductService extends BaseService {
|
||||
* @return {Promise} resolves to the update result.
|
||||
*/
|
||||
async update(productId, update) {
|
||||
return this.atomicPhase_(async manager => {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
const productRepo = manager.getCustomRepository(this.productRepository_)
|
||||
const productVariantRepo = manager.getCustomRepository(
|
||||
this.productVariantRepository_
|
||||
@@ -370,21 +376,14 @@ class ProductService extends BaseService {
|
||||
relations: ["variants", "tags", "images"],
|
||||
})
|
||||
|
||||
const {
|
||||
variants,
|
||||
metadata,
|
||||
options,
|
||||
images,
|
||||
tags,
|
||||
type,
|
||||
...rest
|
||||
} = update
|
||||
const { variants, metadata, options, images, tags, type, ...rest } =
|
||||
update
|
||||
|
||||
if (!product.thumbnail && !update.thumbnail && images && images.length) {
|
||||
if (!product.thumbnail && !update.thumbnail && images?.length) {
|
||||
product.thumbnail = images[0]
|
||||
}
|
||||
|
||||
if (images && images.length) {
|
||||
if (images) {
|
||||
product.images = await this.upsertImages_(images)
|
||||
}
|
||||
|
||||
@@ -403,7 +402,7 @@ class ProductService extends BaseService {
|
||||
if (variants) {
|
||||
// Iterate product variants and update their properties accordingly
|
||||
for (const variant of product.variants) {
|
||||
const exists = variants.find(v => v.id && variant.id === v.id)
|
||||
const exists = variants.find((v) => v.id && variant.id === v.id)
|
||||
if (!exists) {
|
||||
await productVariantRepo.remove(variant)
|
||||
}
|
||||
@@ -414,7 +413,7 @@ class ProductService extends BaseService {
|
||||
newVariant.variant_rank = i
|
||||
|
||||
if (newVariant.id) {
|
||||
const variant = product.variants.find(v => v.id === newVariant.id)
|
||||
const variant = product.variants.find((v) => v.id === newVariant.id)
|
||||
|
||||
if (!variant) {
|
||||
throw new MedusaError(
|
||||
@@ -465,7 +464,7 @@ class ProductService extends BaseService {
|
||||
* @return {Promise} empty promise
|
||||
*/
|
||||
async delete(productId) {
|
||||
return this.atomicPhase_(async manager => {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
const productRepo = manager.getCustomRepository(this.productRepository_)
|
||||
|
||||
// Should not fail, if product does not exist, since delete is idempotent
|
||||
@@ -475,6 +474,12 @@ class ProductService extends BaseService {
|
||||
|
||||
await productRepo.softRemove(product)
|
||||
|
||||
await this.eventBus_
|
||||
.withTransaction(manager)
|
||||
.emit(ProductService.Events.DELETED, {
|
||||
id: productId,
|
||||
})
|
||||
|
||||
return Promise.resolve()
|
||||
})
|
||||
}
|
||||
@@ -488,7 +493,7 @@ class ProductService extends BaseService {
|
||||
* @return {Promise} the result of the model update operation
|
||||
*/
|
||||
async addOption(productId, optionTitle) {
|
||||
return this.atomicPhase_(async manager => {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
const productOptionRepo = manager.getCustomRepository(
|
||||
this.productOptionRepository_
|
||||
)
|
||||
@@ -497,7 +502,7 @@ class ProductService extends BaseService {
|
||||
relations: ["options", "variants"],
|
||||
})
|
||||
|
||||
if (product.options.find(o => o.title === optionTitle)) {
|
||||
if (product.options.find((o) => o.title === optionTitle)) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.DUPLICATE_ERROR,
|
||||
`An option with the title: ${optionTitle} already exists`
|
||||
@@ -527,7 +532,7 @@ class ProductService extends BaseService {
|
||||
}
|
||||
|
||||
async reorderVariants(productId, variantOrder) {
|
||||
return this.atomicPhase_(async manager => {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
const productRepo = manager.getCustomRepository(this.productRepository_)
|
||||
|
||||
const product = await this.retrieve(productId, {
|
||||
@@ -541,8 +546,8 @@ class ProductService extends BaseService {
|
||||
)
|
||||
}
|
||||
|
||||
product.variants = variantOrder.map(vId => {
|
||||
const variant = product.variants.find(v => v.id === vId)
|
||||
product.variants = variantOrder.map((vId) => {
|
||||
const variant = product.variants.find((v) => v.id === vId)
|
||||
if (!variant) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
@@ -571,7 +576,7 @@ class ProductService extends BaseService {
|
||||
* @return {Promise} the result of the update operation
|
||||
*/
|
||||
async reorderOptions(productId, optionOrder) {
|
||||
return this.atomicPhase_(async manager => {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
const productRepo = manager.getCustomRepository(this.productRepository_)
|
||||
|
||||
const product = await this.retrieve(productId, { relations: ["options"] })
|
||||
@@ -583,8 +588,8 @@ class ProductService extends BaseService {
|
||||
)
|
||||
}
|
||||
|
||||
product.options = optionOrder.map(oId => {
|
||||
const option = product.options.find(o => o.id === oId)
|
||||
product.options = optionOrder.map((oId) => {
|
||||
const option = product.options.find((o) => o.id === oId)
|
||||
if (!option) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
@@ -612,7 +617,7 @@ class ProductService extends BaseService {
|
||||
* @return {Promise} the updated product
|
||||
*/
|
||||
async updateOption(productId, optionId, data) {
|
||||
return this.atomicPhase_(async manager => {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
const productOptionRepo = manager.getCustomRepository(
|
||||
this.productOptionRepository_
|
||||
)
|
||||
@@ -622,7 +627,8 @@ class ProductService extends BaseService {
|
||||
const { title, values } = data
|
||||
|
||||
const optionExists = product.options.some(
|
||||
o => o.title.toUpperCase() === title.toUpperCase() && o.id !== optionId
|
||||
(o) =>
|
||||
o.title.toUpperCase() === title.toUpperCase() && o.id !== optionId
|
||||
)
|
||||
if (optionExists) {
|
||||
throw new MedusaError(
|
||||
@@ -661,7 +667,7 @@ class ProductService extends BaseService {
|
||||
* @return {Promise} the updated product
|
||||
*/
|
||||
async deleteOption(productId, optionId) {
|
||||
return this.atomicPhase_(async manager => {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
const productOptionRepo = manager.getCustomRepository(
|
||||
this.productOptionRepository_
|
||||
)
|
||||
@@ -689,17 +695,17 @@ class ProductService extends BaseService {
|
||||
const firstVariant = product.variants[0]
|
||||
|
||||
const valueToMatch = firstVariant.options.find(
|
||||
o => o.option_id === optionId
|
||||
(o) => o.option_id === optionId
|
||||
).value
|
||||
|
||||
const equalsFirst = await Promise.all(
|
||||
product.variants.map(async v => {
|
||||
const option = v.options.find(o => o.option_id === optionId)
|
||||
product.variants.map(async (v) => {
|
||||
const option = v.options.find((o) => o.option_id === optionId)
|
||||
return option.value === valueToMatch
|
||||
})
|
||||
)
|
||||
|
||||
if (!equalsFirst.every(v => v)) {
|
||||
if (!equalsFirst.every((v) => v)) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
`To delete an option, first delete all variants, such that when option is deleted, no duplicate variants will exist.`
|
||||
|
||||
@@ -23,11 +23,13 @@ class QueryBuilderService extends BaseService {
|
||||
}
|
||||
|
||||
buildFilterQuery(filters) {
|
||||
if (_.isEmpty(filters)) return
|
||||
if (_.isEmpty(filters)) {
|
||||
return
|
||||
}
|
||||
|
||||
const filterQuery = {}
|
||||
|
||||
Object.keys(filters).map(filter => {
|
||||
Object.keys(filters).map((filter) => {
|
||||
filterQuery[filter] = filters[filter]
|
||||
})
|
||||
|
||||
@@ -35,9 +37,11 @@ class QueryBuilderService extends BaseService {
|
||||
}
|
||||
|
||||
buildTextSearchQuery(search, searchProperties) {
|
||||
if (_.isEmpty(search)) return
|
||||
if (_.isEmpty(search)) {
|
||||
return
|
||||
}
|
||||
|
||||
const searchQuery = searchProperties.map(s => ({
|
||||
const searchQuery = searchProperties.map((s) => ({
|
||||
[s]: new RegExp(search.q, "i"),
|
||||
}))
|
||||
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
import _ from "lodash"
|
||||
import { Validator, MedusaError } from "medusa-core-utils"
|
||||
import { MedusaError } from "medusa-core-utils"
|
||||
import { BaseService } from "medusa-interfaces"
|
||||
import { countries } from "../utils/countries"
|
||||
|
||||
/**
|
||||
* Provides layer to manipulate regions.
|
||||
* @implements BaseService
|
||||
* @extends BaseService
|
||||
*/
|
||||
class RegionService extends BaseService {
|
||||
static Events = {
|
||||
UPDATED: "region.updated",
|
||||
CREATED: "region.created",
|
||||
DELETED: "region.deleted",
|
||||
}
|
||||
|
||||
constructor({
|
||||
@@ -83,11 +83,11 @@ class RegionService extends BaseService {
|
||||
|
||||
/**
|
||||
* Creates a region.
|
||||
* @param {Region} rawRegion - the unvalidated region
|
||||
* @param {Region} regionObject - the unvalidated region
|
||||
* @return {Region} the newly created region
|
||||
*/
|
||||
async create(regionObject) {
|
||||
return this.atomicPhase_(async manager => {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
const regionRepository = manager.getCustomRepository(
|
||||
this.regionRepository_
|
||||
)
|
||||
@@ -118,7 +118,7 @@ class RegionService extends BaseService {
|
||||
}
|
||||
|
||||
if (metadata) {
|
||||
regionObject.metadata = this.setMetadata_(region, metadata)
|
||||
regionObject.metadata = this.setMetadata_(regionObject, metadata)
|
||||
}
|
||||
|
||||
for (const [key, value] of Object.entries(validated)) {
|
||||
@@ -145,7 +145,7 @@ class RegionService extends BaseService {
|
||||
* @return {Promise} the result of the update operation
|
||||
*/
|
||||
async update(regionId, update) {
|
||||
return this.atomicPhase_(async manager => {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
const regionRepository = manager.getCustomRepository(
|
||||
this.regionRepository_
|
||||
)
|
||||
@@ -218,17 +218,17 @@ class RegionService extends BaseService {
|
||||
|
||||
if (region.countries) {
|
||||
region.countries = await Promise.all(
|
||||
region.countries.map(countryCode =>
|
||||
region.countries.map((countryCode) =>
|
||||
this.validateCountry_(countryCode, id)
|
||||
)
|
||||
).catch(err => {
|
||||
).catch((err) => {
|
||||
throw err
|
||||
})
|
||||
}
|
||||
|
||||
if (region.payment_providers) {
|
||||
region.payment_providers = await Promise.all(
|
||||
region.payment_providers.map(async pId => {
|
||||
region.payment_providers.map(async (pId) => {
|
||||
const pp = await ppRepository.findOne({ where: { id: pId } })
|
||||
if (!pp) {
|
||||
throw new MedusaError(
|
||||
@@ -244,7 +244,7 @@ class RegionService extends BaseService {
|
||||
|
||||
if (region.fulfillment_providers) {
|
||||
region.fulfillment_providers = await Promise.all(
|
||||
region.fulfillment_providers.map(async fId => {
|
||||
region.fulfillment_providers.map(async (fId) => {
|
||||
const fp = await fpRepository.findOne({ where: { id: fId } })
|
||||
if (!fp) {
|
||||
throw new MedusaError(
|
||||
@@ -283,7 +283,7 @@ class RegionService extends BaseService {
|
||||
.withTransaction(this.transactionManager_)
|
||||
.retrieve(["currencies"])
|
||||
|
||||
const storeCurrencies = store.currencies.map(curr => curr.code)
|
||||
const storeCurrencies = store.currencies.map((curr) => curr.code)
|
||||
|
||||
if (!storeCurrencies.includes(currencyCode.toLowerCase())) {
|
||||
throw new MedusaError(
|
||||
@@ -305,7 +305,7 @@ class RegionService extends BaseService {
|
||||
)
|
||||
|
||||
const countryCode = code.toUpperCase()
|
||||
const validCountry = countries.find(c => c.alpha2 === countryCode)
|
||||
const validCountry = countries.find((c) => c.alpha2 === countryCode)
|
||||
if (!validCountry) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
@@ -339,6 +339,7 @@ class RegionService extends BaseService {
|
||||
/**
|
||||
* Retrieves a region by its id.
|
||||
* @param {string} regionId - the id of the region to retrieve
|
||||
* @param {object} config - configuration settings
|
||||
* @return {Region} the region
|
||||
*/
|
||||
async retrieve(regionId, config = {}) {
|
||||
@@ -361,7 +362,8 @@ class RegionService extends BaseService {
|
||||
|
||||
/**
|
||||
* Lists all regions based on a query
|
||||
* @param {object} listOptions - query object for find
|
||||
* @param {object} selector - query object for find
|
||||
* @param {object} config - configuration settings
|
||||
* @return {Promise} result of the find operation
|
||||
*/
|
||||
async list(selector = {}, config = { relations: [], skip: 0, take: 10 }) {
|
||||
@@ -377,15 +379,23 @@ class RegionService extends BaseService {
|
||||
* @return {Promise} the result of the delete operation
|
||||
*/
|
||||
async delete(regionId) {
|
||||
return this.atomicPhase_(async manager => {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
const regionRepo = manager.getCustomRepository(this.regionRepository_)
|
||||
|
||||
const region = await regionRepo.findOne({ where: { id: regionId } })
|
||||
|
||||
if (!region) return Promise.resolve()
|
||||
if (!region) {
|
||||
return Promise.resolve()
|
||||
}
|
||||
|
||||
await regionRepo.softRemove(region)
|
||||
|
||||
await this.eventBus_
|
||||
.withTransaction(manager)
|
||||
.emit(RegionService.Events.DELETED, {
|
||||
id: regionId,
|
||||
})
|
||||
|
||||
return Promise.resolve()
|
||||
})
|
||||
}
|
||||
@@ -397,7 +407,7 @@ class RegionService extends BaseService {
|
||||
* @return {Promise} the result of the update operation
|
||||
*/
|
||||
async addCountry(regionId, code) {
|
||||
return this.atomicPhase_(async manager => {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
const regionRepo = manager.getCustomRepository(this.regionRepository_)
|
||||
|
||||
const country = await this.validateCountry_(code, regionId)
|
||||
@@ -407,7 +417,7 @@ class RegionService extends BaseService {
|
||||
// Check if region already has country
|
||||
if (
|
||||
region.countries &&
|
||||
region.countries.map(c => c.iso_2).includes(country.iso_2)
|
||||
region.countries.map((c) => c.iso_2).includes(country.iso_2)
|
||||
) {
|
||||
return region
|
||||
}
|
||||
@@ -434,7 +444,7 @@ class RegionService extends BaseService {
|
||||
* @return {Promise} the result of the update operation
|
||||
*/
|
||||
async removeCountry(regionId, code) {
|
||||
return this.atomicPhase_(async manager => {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
const regionRepo = manager.getCustomRepository(this.regionRepository_)
|
||||
|
||||
const region = await this.retrieve(regionId, { relations: ["countries"] })
|
||||
@@ -442,13 +452,13 @@ class RegionService extends BaseService {
|
||||
// Check if region contains country. If not, we simpy resolve
|
||||
if (
|
||||
region.countries &&
|
||||
!region.countries.map(c => c.iso_2).includes(code)
|
||||
!region.countries.map((c) => c.iso_2).includes(code)
|
||||
) {
|
||||
return region
|
||||
}
|
||||
|
||||
region.countries = region.countries.filter(
|
||||
country => country.iso_2 !== code
|
||||
(country) => country.iso_2 !== code
|
||||
)
|
||||
|
||||
const updated = await regionRepo.save(region)
|
||||
@@ -470,7 +480,7 @@ class RegionService extends BaseService {
|
||||
* @return {Promise} the result of the update operation
|
||||
*/
|
||||
async addPaymentProvider(regionId, providerId) {
|
||||
return this.atomicPhase_(async manager => {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
const regionRepo = manager.getCustomRepository(this.regionRepository_)
|
||||
const ppRepo = manager.getCustomRepository(
|
||||
this.paymentProviderRepository_
|
||||
@@ -517,7 +527,7 @@ class RegionService extends BaseService {
|
||||
* @return {Promise} the result of the update operation
|
||||
*/
|
||||
async addFulfillmentProvider(regionId, providerId) {
|
||||
return this.atomicPhase_(async manager => {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
const regionRepo = manager.getCustomRepository(this.regionRepository_)
|
||||
const fpRepo = manager.getCustomRepository(
|
||||
this.fulfillmentProviderRepository_
|
||||
@@ -561,7 +571,7 @@ class RegionService extends BaseService {
|
||||
* @return {Promise} the result of the update operation
|
||||
*/
|
||||
async removePaymentProvider(regionId, providerId) {
|
||||
return this.atomicPhase_(async manager => {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
const regionRepo = manager.getCustomRepository(this.regionRepository_)
|
||||
|
||||
const region = await this.retrieve(regionId, {
|
||||
@@ -595,7 +605,7 @@ class RegionService extends BaseService {
|
||||
* @return {Promise} the result of the update operation
|
||||
*/
|
||||
async removeFulfillmentProvider(regionId, providerId) {
|
||||
return this.atomicPhase_(async manager => {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
const regionRepo = manager.getCustomRepository(this.regionRepository_)
|
||||
|
||||
const region = await this.retrieve(regionId, {
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { Validator, MedusaError } from "medusa-core-utils"
|
||||
import { MedusaError } from "medusa-core-utils"
|
||||
import { BaseService } from "medusa-interfaces"
|
||||
import { In } from "typeorm"
|
||||
|
||||
class ReturnReasonService extends BaseService {
|
||||
constructor({ manager, returnReasonRepository }) {
|
||||
@@ -29,7 +28,7 @@ class ReturnReasonService extends BaseService {
|
||||
}
|
||||
|
||||
create(data) {
|
||||
return this.atomicPhase_(async manager => {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
const rrRepo = manager.getCustomRepository(this.retReasonRepo_)
|
||||
|
||||
if (data.parent_return_reason_id && data.parent_return_reason_id !== "") {
|
||||
@@ -51,7 +50,7 @@ class ReturnReasonService extends BaseService {
|
||||
}
|
||||
|
||||
update(id, data) {
|
||||
return this.atomicPhase_(async manager => {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
const rrRepo = manager.getCustomRepository(this.retReasonRepo_)
|
||||
const reason = await this.retrieve(id)
|
||||
|
||||
@@ -77,6 +76,7 @@ class ReturnReasonService extends BaseService {
|
||||
|
||||
/**
|
||||
* @param {Object} selector - the query object for find
|
||||
* @param {Object} config - config object
|
||||
* @return {Promise} the result of the find operation
|
||||
*/
|
||||
async list(
|
||||
@@ -90,7 +90,8 @@ class ReturnReasonService extends BaseService {
|
||||
|
||||
/**
|
||||
* Gets an order by id.
|
||||
* @param {string} orderId - id of order to retrieve
|
||||
* @param {string} id - id of order to retrieve
|
||||
* @param {Object} config - config object
|
||||
* @return {Promise<Order>} the order document
|
||||
*/
|
||||
async retrieve(id, config = {}) {
|
||||
@@ -111,7 +112,7 @@ class ReturnReasonService extends BaseService {
|
||||
}
|
||||
|
||||
async delete(returnReasonId) {
|
||||
return this.atomicPhase_(async manager => {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
const rrRepo = manager.getCustomRepository(this.retReasonRepo_)
|
||||
|
||||
// We include the relation 'return_reason_children' to enable cascading deletes of return reasons if a parent is removed
|
||||
|
||||
@@ -93,6 +93,12 @@ class ReturnService extends BaseService {
|
||||
}
|
||||
}
|
||||
|
||||
if (order.claims && order.claims.length) {
|
||||
for (const c of order.claims) {
|
||||
merged = [...merged, ...c.additional_items]
|
||||
}
|
||||
}
|
||||
|
||||
const toReturn = await Promise.all(
|
||||
items.map(async data => {
|
||||
const item = merged.find(i => i.id === data.item_id)
|
||||
@@ -325,7 +331,13 @@ class ReturnService extends BaseService {
|
||||
.withTransaction(manager)
|
||||
.retrieve(orderId, {
|
||||
select: ["refunded_total", "total", "refundable_amount"],
|
||||
relations: ["swaps", "swaps.additional_items", "items"],
|
||||
relations: [
|
||||
"swaps",
|
||||
"swaps.additional_items",
|
||||
"claims",
|
||||
"claims.additional_items",
|
||||
"items",
|
||||
],
|
||||
})
|
||||
|
||||
const returnLines = await this.getFulfillmentItems_(
|
||||
@@ -487,13 +499,18 @@ class ReturnService extends BaseService {
|
||||
* @param {string[]} lineItems - the line items to return
|
||||
* @return {Promise} the result of the update operation
|
||||
*/
|
||||
async receive(returnId, receivedItems, refundAmount, allowMismatch = false) {
|
||||
async receive(
|
||||
return_id,
|
||||
received_items,
|
||||
refund_amount,
|
||||
allow_mismatch = false
|
||||
) {
|
||||
return this.atomicPhase_(async manager => {
|
||||
const returnRepository = manager.getCustomRepository(
|
||||
this.returnRepository_
|
||||
)
|
||||
|
||||
const returnObj = await this.retrieve(returnId, {
|
||||
const returnObj = await this.retrieve(return_id, {
|
||||
relations: ["items", "swap", "swap.additional_items"],
|
||||
})
|
||||
|
||||
@@ -518,6 +535,8 @@ class ReturnService extends BaseService {
|
||||
"returns",
|
||||
"payments",
|
||||
"discounts",
|
||||
"discounts.rule",
|
||||
"discounts.rule.valid_for",
|
||||
"refunds",
|
||||
"shipping_methods",
|
||||
"region",
|
||||
@@ -535,7 +554,7 @@ class ReturnService extends BaseService {
|
||||
|
||||
const returnLines = await this.getFulfillmentItems_(
|
||||
order,
|
||||
receivedItems,
|
||||
received_items,
|
||||
this.validateReturnLineItem_
|
||||
)
|
||||
|
||||
@@ -564,12 +583,12 @@ class ReturnService extends BaseService {
|
||||
let returnStatus = "received"
|
||||
|
||||
const isMatching = newLines.every(l => l.is_requested)
|
||||
if (!isMatching && !allowMismatch) {
|
||||
if (!isMatching && !allow_mismatch) {
|
||||
// Should update status
|
||||
returnStatus = "requires_action"
|
||||
}
|
||||
|
||||
const totalRefundableAmount = refundAmount || returnObj.refund_amount
|
||||
const totalRefundableAmount = refund_amount || returnObj.refund_amount
|
||||
|
||||
const now = new Date()
|
||||
const updateObj = {
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
import { SearchService } from "medusa-interfaces"
|
||||
|
||||
/**
|
||||
* Default class that implements SearchService but provides stuv implementation for all methods
|
||||
* @extends SearchService
|
||||
*/
|
||||
class DefaultSearchService extends SearchService {
|
||||
constructor(container) {
|
||||
super()
|
||||
|
||||
this.isDefault = true
|
||||
|
||||
this.logger_ = container.logger
|
||||
}
|
||||
|
||||
createIndex(indexName, options) {
|
||||
this.logger_.warn(
|
||||
"This is an empty method: createIndex must be overridden by a child class"
|
||||
)
|
||||
}
|
||||
|
||||
getIndex(indexName) {
|
||||
this.logger_.warn(
|
||||
"This is an empty method: getIndex must be overridden by a child class"
|
||||
)
|
||||
}
|
||||
|
||||
addDocuments(indexName, documents, type) {
|
||||
this.logger_.warn(
|
||||
"This is an empty method: addDocuments must be overridden by a child class"
|
||||
)
|
||||
}
|
||||
|
||||
replaceDocuments(indexName, documents, type) {
|
||||
this.logger_.warn(
|
||||
"This is an empty method: replaceDocuments must be overridden by a child class"
|
||||
)
|
||||
}
|
||||
|
||||
deleteDocument(indexName, document_id) {
|
||||
this.logger_.warn(
|
||||
"This is an empty method: deleteDocument must be overridden by a child class"
|
||||
)
|
||||
}
|
||||
|
||||
deleteAllDocuments(indexName) {
|
||||
this.logger_.warn(
|
||||
"This is an empty method: deleteAllDocuments must be overridden by a child class"
|
||||
)
|
||||
}
|
||||
|
||||
search(indexName, query, options) {
|
||||
this.logger_.warn(
|
||||
"This is an empty method: search must be overridden a the child class"
|
||||
)
|
||||
return { hits: [] }
|
||||
}
|
||||
|
||||
updateSettings(indexName, settings) {
|
||||
this.logger_.warn(
|
||||
"This is an empty method: updateSettings must be overridden by a child class"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default DefaultSearchService
|
||||
@@ -1,11 +1,9 @@
|
||||
import _ from "lodash"
|
||||
import { MedusaError } from "medusa-core-utils"
|
||||
import { BaseService } from "medusa-interfaces"
|
||||
import { In } from "typeorm"
|
||||
|
||||
/**
|
||||
* Provides layer to manipulate profiles.
|
||||
* @implements BaseService
|
||||
* @extends BaseService
|
||||
*/
|
||||
class ShippingOptionService extends BaseService {
|
||||
constructor({
|
||||
@@ -64,6 +62,7 @@ class ShippingOptionService extends BaseService {
|
||||
/**
|
||||
* Validates a requirement
|
||||
* @param {ShippingRequirement} requirement - the requirement to validate
|
||||
* @param {string} optionId - the id to validate the requirement
|
||||
* @return {ShippingRequirement} a validated shipping requirement
|
||||
*/
|
||||
async validateRequirement_(requirement, optionId) {
|
||||
@@ -123,6 +122,7 @@ class ShippingOptionService extends BaseService {
|
||||
|
||||
/**
|
||||
* @param {Object} selector - the query object for find
|
||||
* @param {object} config - config object
|
||||
* @return {Promise} the result of the find operation
|
||||
*/
|
||||
async list(selector, config = { skip: 0, take: 50 }) {
|
||||
@@ -136,6 +136,7 @@ class ShippingOptionService extends BaseService {
|
||||
* Gets a profile by id.
|
||||
* Throws in case of DB Error and if profile was not found.
|
||||
* @param {string} optionId - the id of the profile to get.
|
||||
* @param {object} options - the options to get a profile
|
||||
* @return {Promise<Product>} the profile document.
|
||||
*/
|
||||
async retrieve(optionId, options = {}) {
|
||||
@@ -171,10 +172,10 @@ class ShippingOptionService extends BaseService {
|
||||
* and its methods should be copied to an order/swap entity.
|
||||
* @param {string} id - the id of the shipping method to update
|
||||
* @param {object} update - the values to update the method with
|
||||
* @returns {Promise<ShippingMethod>} the resulting shipping method
|
||||
* @return {Promise<ShippingMethod>} the resulting shipping method
|
||||
*/
|
||||
async updateShippingMethod(id, update) {
|
||||
return this.atomicPhase_(async manager => {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
const methodRepo = manager.getCustomRepository(this.methodRepository_)
|
||||
const method = await methodRepo.findOne({ where: { id } })
|
||||
|
||||
@@ -200,10 +201,10 @@ class ShippingOptionService extends BaseService {
|
||||
|
||||
/**
|
||||
* Removes a given shipping method
|
||||
* @param {string} id - the id of the option to use for the method.
|
||||
* @param {string} sm - the shipping method to remove
|
||||
*/
|
||||
async deleteShippingMethod(sm) {
|
||||
return this.atomicPhase_(async manager => {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
const methodRepo = manager.getCustomRepository(this.methodRepository_)
|
||||
return methodRepo.remove(sm)
|
||||
})
|
||||
@@ -214,10 +215,10 @@ class ShippingOptionService extends BaseService {
|
||||
* @param {string} optionId - the id of the option to use for the method.
|
||||
* @param {object} data - the optional provider data to use.
|
||||
* @param {object} config - the cart to create the shipping method for.
|
||||
* @returns {ShippingMethod} the resulting shipping method.
|
||||
* @return {ShippingMethod} the resulting shipping method.
|
||||
*/
|
||||
async createShippingMethod(optionId, data, config) {
|
||||
return this.atomicPhase_(async manager => {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
const option = await this.retrieve(optionId, {
|
||||
relations: ["requirements"],
|
||||
})
|
||||
@@ -255,6 +256,10 @@ class ShippingOptionService extends BaseService {
|
||||
toCreate.cart_id = config.cart.id
|
||||
}
|
||||
|
||||
if (config.cart_id) {
|
||||
toCreate.cart_id = config.cart_id
|
||||
}
|
||||
|
||||
if (config.return_id) {
|
||||
toCreate.return_id = config.return_id
|
||||
}
|
||||
@@ -286,7 +291,7 @@ class ShippingOptionService extends BaseService {
|
||||
* Checks if a given option id is a valid option for a cart. If it is the
|
||||
* option is returned with the correct price. Throws when region_ids do not
|
||||
* match, or when the shipping option requirements are not satisfied.
|
||||
* @param {string} optionId - the id of the option to check
|
||||
* @param {object} option - the option object to check
|
||||
* @param {Cart} cart - the cart object to check against
|
||||
* @return {ShippingOption} the validated shipping option
|
||||
*/
|
||||
@@ -303,7 +308,7 @@ class ShippingOptionService extends BaseService {
|
||||
}
|
||||
|
||||
const subtotal = cart.subtotal
|
||||
const requirementResults = option.requirements.map(requirement => {
|
||||
const requirementResults = option.requirements.map((requirement) => {
|
||||
switch (requirement.type) {
|
||||
case "max_subtotal":
|
||||
return requirement.amount > subtotal
|
||||
@@ -328,11 +333,11 @@ class ShippingOptionService extends BaseService {
|
||||
* Creates a new shipping option. Used both for outbound and inbound shipping
|
||||
* options. The difference is registered by the `is_return` field which
|
||||
* defaults to false.
|
||||
* @param {ShippingOption} option - the shipping option to create
|
||||
* @param {ShippingOption} data - the data to create shipping options
|
||||
* @return {Promise<ShippingOption>} the result of the create operation
|
||||
*/
|
||||
async create(data) {
|
||||
return this.atomicPhase_(async manager => {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
const optionRepo = manager.getCustomRepository(this.optionRepository_)
|
||||
const option = await optionRepo.create(data)
|
||||
|
||||
@@ -370,7 +375,7 @@ class ShippingOptionService extends BaseService {
|
||||
for (const r of data.requirements) {
|
||||
const validated = await this.validateRequirement_(r)
|
||||
|
||||
if (acc.find(raw => raw.type === validated.type)) {
|
||||
if (acc.find((raw) => raw.type === validated.type)) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
"Only one requirement of each type is allowed"
|
||||
@@ -379,7 +384,7 @@ class ShippingOptionService extends BaseService {
|
||||
|
||||
if (
|
||||
acc.find(
|
||||
raw =>
|
||||
(raw) =>
|
||||
(raw.type === "max_subtotal" &&
|
||||
validated.amount > raw.amount) ||
|
||||
(raw.type === "min_subtotal" && validated.amount < raw.amount)
|
||||
@@ -408,7 +413,7 @@ class ShippingOptionService extends BaseService {
|
||||
|
||||
/**
|
||||
* Validates a shipping option price
|
||||
* @param {ShippingOptionPrice} price - the price to validate
|
||||
* @param {ShippingOptionPrice} priceType - the price to validate
|
||||
* @param {ShippingOption} option - the option to validate against
|
||||
* @return {Promise<ShippingOptionPrice>} the validated price
|
||||
*/
|
||||
@@ -449,7 +454,7 @@ class ShippingOptionService extends BaseService {
|
||||
* @return {Promise} resolves to the update result.
|
||||
*/
|
||||
async update(optionId, update) {
|
||||
return this.atomicPhase_(async manager => {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
const option = await this.retrieve(optionId, {
|
||||
relations: ["requirements"],
|
||||
})
|
||||
@@ -477,7 +482,7 @@ class ShippingOptionService extends BaseService {
|
||||
for (const r of update.requirements) {
|
||||
const validated = await this.validateRequirement_(r, optionId)
|
||||
|
||||
if (acc.find(raw => raw.type === validated.type)) {
|
||||
if (acc.find((raw) => raw.type === validated.type)) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
"Only one requirement of each type is allowed"
|
||||
@@ -486,7 +491,7 @@ class ShippingOptionService extends BaseService {
|
||||
|
||||
if (
|
||||
acc.find(
|
||||
raw =>
|
||||
(raw) =>
|
||||
(raw.type === "max_subtotal" &&
|
||||
validated.amount > raw.amount) ||
|
||||
(raw.type === "min_subtotal" && validated.amount < raw.amount)
|
||||
@@ -502,12 +507,12 @@ class ShippingOptionService extends BaseService {
|
||||
}
|
||||
|
||||
if (option.requirements) {
|
||||
const accReqs = acc.map(a => a.id)
|
||||
const accReqs = acc.map((a) => a.id)
|
||||
const toRemove = option.requirements.filter(
|
||||
r => !accReqs.includes(r.id)
|
||||
(r) => !accReqs.includes(r.id)
|
||||
)
|
||||
await Promise.all(
|
||||
toRemove.map(async req => {
|
||||
toRemove.map(async (req) => {
|
||||
await this.removeRequirement(req.id)
|
||||
})
|
||||
)
|
||||
@@ -552,7 +557,7 @@ class ShippingOptionService extends BaseService {
|
||||
*/
|
||||
async delete(optionId) {
|
||||
try {
|
||||
let option = await this.retrieve(optionId)
|
||||
const option = await this.retrieve(optionId)
|
||||
|
||||
const optionRepo = this.manager_.getCustomRepository(
|
||||
this.optionRepository_
|
||||
@@ -579,13 +584,13 @@ class ShippingOptionService extends BaseService {
|
||||
* @return {Promise} the result of update
|
||||
*/
|
||||
async addRequirement(optionId, requirement) {
|
||||
return this.atomicPhase_(async manager => {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
const option = await this.retrieve(optionId, {
|
||||
relations: ["requirements"],
|
||||
})
|
||||
const validatedReq = await this.validateRequirement_(requirement)
|
||||
|
||||
if (option.requirements.find(r => r.type === validatedReq.type)) {
|
||||
if (option.requirements.find((r) => r.type === validatedReq.type)) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.DUPLICATE_ERROR,
|
||||
`A requirement with type: ${validatedReq.type} already exists`
|
||||
@@ -605,7 +610,7 @@ class ShippingOptionService extends BaseService {
|
||||
* @return {Promise} the result of update
|
||||
*/
|
||||
async removeRequirement(requirementId) {
|
||||
return this.atomicPhase_(async manager => {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
try {
|
||||
const reqRepo = manager.getCustomRepository(this.requirementRepository_)
|
||||
const requirement = await reqRepo.findOne({
|
||||
@@ -624,7 +629,7 @@ class ShippingOptionService extends BaseService {
|
||||
|
||||
/**
|
||||
* Decorates a shipping option.
|
||||
* @param {ShippingOption} shippingOption - the shipping option to decorate.
|
||||
* @param {ShippingOption} optionId - the shipping option to decorate using optionId.
|
||||
* @param {string[]} fields - the fields to include.
|
||||
* @param {string[]} expandFields - fields to expand.
|
||||
* @return {ShippingOption} the decorated ShippingOption.
|
||||
@@ -644,9 +649,8 @@ class ShippingOptionService extends BaseService {
|
||||
|
||||
/**
|
||||
* Dedicated method to set metadata for a shipping option.
|
||||
* @param {string} optionId - the option to set metadata for.
|
||||
* @param {string} key - key for metadata field
|
||||
* @param {string} value - value for metadata field.
|
||||
* @param {object} option - the option to set metadata for.
|
||||
* @param {object} metadata - object for metadata field
|
||||
* @return {Promise} resolves to the updated result.
|
||||
*/
|
||||
async setMetadata_(option, metadata) {
|
||||
@@ -677,9 +681,10 @@ class ShippingOptionService extends BaseService {
|
||||
* price type "calculated".
|
||||
* @param {ShippingOption} option - the shipping option to retrieve the price
|
||||
* for.
|
||||
* @param {Cart || Order} cart - the context in which the price should be
|
||||
* @param {ShippingData} data - the shipping data to retrieve the price.
|
||||
* @param {Cart | Order} cart - the context in which the price should be
|
||||
* retrieved.
|
||||
* @returns {Promise<Number>} the price of the shipping option.
|
||||
* @return {Promise<Number>} the price of the shipping option.
|
||||
*/
|
||||
async getPrice_(option, data, cart) {
|
||||
if (option.price_type === "calculated") {
|
||||
|
||||
@@ -14,6 +14,7 @@ class ShippingProfileService extends BaseService {
|
||||
productService,
|
||||
productRepository,
|
||||
shippingOptionService,
|
||||
customShippingOptionService,
|
||||
}) {
|
||||
super()
|
||||
|
||||
@@ -31,6 +32,9 @@ class ShippingProfileService extends BaseService {
|
||||
|
||||
/** @private @const {ShippingOptionService} */
|
||||
this.shippingOptionService_ = shippingOptionService
|
||||
|
||||
/** @private @const {CustomShippingOptionService} */
|
||||
this.customShippingOptionService_ = customShippingOptionService
|
||||
}
|
||||
|
||||
withTransaction(transactionManager) {
|
||||
@@ -43,6 +47,7 @@ class ShippingProfileService extends BaseService {
|
||||
shippingProfileRepository: this.shippingProfileRepository_,
|
||||
productService: this.productService_,
|
||||
shippingOptionService: this.shippingOptionService_,
|
||||
customShippingOptionService: this.customShippingOptionService_,
|
||||
})
|
||||
|
||||
cloned.transactionManager_ = transactionManager
|
||||
@@ -410,19 +415,47 @@ class ShippingProfileService extends BaseService {
|
||||
* Finds all the shipping profiles that cover the products in a cart, and
|
||||
* validates all options that are available for the cart.
|
||||
* @param {Cart} cart - the cart object to find shipping options for
|
||||
* @return {[ShippingOptions]} a list of the available shipping options
|
||||
* @return {[ShippingOption]} a list of the available shipping options
|
||||
*/
|
||||
async fetchCartOptions(cart) {
|
||||
const profileIds = this.getProfilesInCart_(cart)
|
||||
|
||||
const rawOpts = await this.shippingOptionService_.list(
|
||||
const selector = {
|
||||
profile_id: profileIds,
|
||||
admin_only: false,
|
||||
}
|
||||
|
||||
const customShippingOptions = await this.customShippingOptionService_.list(
|
||||
{
|
||||
profile_id: profileIds,
|
||||
admin_only: false,
|
||||
cart_id: cart.id,
|
||||
},
|
||||
{ relations: ["requirements", "profile"] }
|
||||
{ select: ["id", "shipping_option_id", "price"] }
|
||||
)
|
||||
|
||||
const hasCustomShippingOptions = customShippingOptions?.length
|
||||
// if there are custom shipping options associated with the cart, use those
|
||||
if (hasCustomShippingOptions) {
|
||||
selector.id = customShippingOptions.map(cso => cso.shipping_option_id)
|
||||
}
|
||||
|
||||
const rawOpts = await this.shippingOptionService_.list(selector, {
|
||||
relations: ["requirements", "profile"],
|
||||
})
|
||||
|
||||
// if there are custom shipping options associated with the cart, return cart shipping options with custom price
|
||||
if (hasCustomShippingOptions) {
|
||||
return rawOpts.map(so => {
|
||||
const customOption = customShippingOptions.find(
|
||||
cso => cso.shipping_option_id === so.id
|
||||
)
|
||||
|
||||
return {
|
||||
...so,
|
||||
amount: customOption?.price,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const options = []
|
||||
|
||||
for (const o of rawOpts) {
|
||||
|
||||
@@ -32,6 +32,7 @@ class SwapService extends BaseService {
|
||||
fulfillmentService,
|
||||
orderService,
|
||||
inventoryService,
|
||||
customShippingOptionService,
|
||||
}) {
|
||||
super()
|
||||
|
||||
@@ -70,6 +71,9 @@ class SwapService extends BaseService {
|
||||
|
||||
/** @private @const {EventBusService} */
|
||||
this.eventBus_ = eventBusService
|
||||
|
||||
/** @private @const {CustomShippingOptionService} */
|
||||
this.customShippingOptionService_ = customShippingOptionService
|
||||
}
|
||||
|
||||
withTransaction(transactionManager) {
|
||||
@@ -90,6 +94,7 @@ class SwapService extends BaseService {
|
||||
orderService: this.orderService_,
|
||||
inventoryService: this.inventoryService_,
|
||||
fulfillmentService: this.fulfillmentService_,
|
||||
customShippingOptionService: this.customShippingOptionService_,
|
||||
})
|
||||
|
||||
cloned.transactionManager_ = transactionManager
|
||||
@@ -116,7 +121,7 @@ class SwapService extends BaseService {
|
||||
"cart.total",
|
||||
]
|
||||
|
||||
const totalsToSelect = select.filter(v => totalFields.includes(v))
|
||||
const totalsToSelect = select.filter((v) => totalFields.includes(v))
|
||||
if (totalsToSelect.length > 0) {
|
||||
const relationSet = new Set(relations)
|
||||
relationSet.add("cart")
|
||||
@@ -127,7 +132,7 @@ class SwapService extends BaseService {
|
||||
relationSet.add("cart.region")
|
||||
relations = [...relationSet]
|
||||
|
||||
select = select.filter(v => !totalFields.includes(v))
|
||||
select = select.filter((v) => !totalFields.includes(v))
|
||||
}
|
||||
|
||||
return {
|
||||
@@ -170,9 +175,8 @@ class SwapService extends BaseService {
|
||||
|
||||
const validatedId = this.validateId_(id)
|
||||
|
||||
const { totalsToSelect, ...newConfig } = this.transformQueryForTotals_(
|
||||
config
|
||||
)
|
||||
const { totalsToSelect, ...newConfig } =
|
||||
this.transformQueryForTotals_(config)
|
||||
|
||||
const query = this.buildQuery_({ id: validatedId }, newConfig)
|
||||
|
||||
@@ -251,7 +255,7 @@ class SwapService extends BaseService {
|
||||
*/
|
||||
validateReturnItems_(order, returnItems) {
|
||||
return returnItems.map(({ item_id, quantity }) => {
|
||||
const item = order.items.find(i => i.id === item_id)
|
||||
const item = order.items.find((i) => i.id === item_id)
|
||||
|
||||
// The item must exist in the order
|
||||
if (!item) {
|
||||
@@ -303,7 +307,7 @@ class SwapService extends BaseService {
|
||||
}
|
||||
) {
|
||||
const { no_notification, ...rest } = custom
|
||||
return this.atomicPhase_(async manager => {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
if (
|
||||
order.fulfillment_status === "not_fulfilled" ||
|
||||
order.payment_status !== "captured"
|
||||
@@ -318,7 +322,6 @@ class SwapService extends BaseService {
|
||||
const line = await this.lineItemService_.retrieve(item.item_id, {
|
||||
relations: ["order", "swap", "claim_order"],
|
||||
})
|
||||
console.log(line)
|
||||
|
||||
if (
|
||||
line.order?.canceled_at ||
|
||||
@@ -377,7 +380,7 @@ class SwapService extends BaseService {
|
||||
}
|
||||
|
||||
async processDifference(swapId) {
|
||||
return this.atomicPhase_(async manager => {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
const swap = await this.retrieve(swapId, {
|
||||
relations: ["payment", "order", "order.payments"],
|
||||
})
|
||||
@@ -493,7 +496,7 @@ class SwapService extends BaseService {
|
||||
}
|
||||
|
||||
async update(swapId, update) {
|
||||
return this.atomicPhase_(async manager => {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
const swap = await this.retrieve(swapId)
|
||||
|
||||
if ("metadata" in update) {
|
||||
@@ -524,8 +527,8 @@ class SwapService extends BaseService {
|
||||
* @returns {Promise<Swap>} the swap with its cart_id prop set to the id of
|
||||
* the new cart.
|
||||
*/
|
||||
async createCart(swapId) {
|
||||
return this.atomicPhase_(async manager => {
|
||||
async createCart(swapId, customShippingOptions = []) {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
const swap = await this.retrieve(swapId, {
|
||||
relations: [
|
||||
"order",
|
||||
@@ -533,6 +536,9 @@ class SwapService extends BaseService {
|
||||
"order.swaps",
|
||||
"order.swaps.additional_items",
|
||||
"order.discounts",
|
||||
"order.discounts.rule",
|
||||
"order.claims",
|
||||
"order.claims.additional_items",
|
||||
"additional_items",
|
||||
"return_order",
|
||||
"return_order.items",
|
||||
@@ -575,6 +581,16 @@ class SwapService extends BaseService {
|
||||
},
|
||||
})
|
||||
|
||||
for (const customShippingOption of customShippingOptions) {
|
||||
await this.customShippingOptionService_
|
||||
.withTransaction(manager)
|
||||
.create({
|
||||
cart_id: cart.id,
|
||||
shipping_option_id: customShippingOption.option_id,
|
||||
price: customShippingOption.price,
|
||||
})
|
||||
}
|
||||
|
||||
for (const item of swap.additional_items) {
|
||||
await this.lineItemService_.withTransaction(manager).update(item.id, {
|
||||
cart_id: cart.id,
|
||||
@@ -606,7 +622,13 @@ class SwapService extends BaseService {
|
||||
}
|
||||
}
|
||||
|
||||
const lineItem = allItems.find(i => i.id === r.item_id)
|
||||
if (order.claims && order.claims.length) {
|
||||
for (const c of order.claims) {
|
||||
allItems = [...allItems, ...c.additional_items]
|
||||
}
|
||||
}
|
||||
|
||||
const lineItem = allItems.find((i) => i.id === r.item_id)
|
||||
|
||||
const toCreate = {
|
||||
cart_id: cart.id,
|
||||
@@ -637,7 +659,7 @@ class SwapService extends BaseService {
|
||||
*
|
||||
*/
|
||||
async registerCartCompletion(swapId) {
|
||||
return this.atomicPhase_(async manager => {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
const swap = await this.retrieve(swapId, {
|
||||
relations: [
|
||||
"cart",
|
||||
@@ -769,7 +791,7 @@ class SwapService extends BaseService {
|
||||
* status.
|
||||
*/
|
||||
async receiveReturn(swapId, returnItems) {
|
||||
return this.atomicPhase_(async manager => {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
const swap = await this.retrieve(swapId, { relations: ["return_order"] })
|
||||
|
||||
if (swap.canceled_at) {
|
||||
@@ -810,7 +832,7 @@ class SwapService extends BaseService {
|
||||
* @returns {Promise<Swap>} the canceled swap.
|
||||
*/
|
||||
async cancel(swapId) {
|
||||
return this.atomicPhase_(async manager => {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
const swap = await this.retrieve(swapId, {
|
||||
relations: ["payment", "fulfillments", "return_order"],
|
||||
})
|
||||
@@ -876,7 +898,7 @@ class SwapService extends BaseService {
|
||||
) {
|
||||
const { metadata, no_notification } = config
|
||||
|
||||
return this.atomicPhase_(async manager => {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
const swap = await this.retrieve(swapId, {
|
||||
relations: [
|
||||
"payment",
|
||||
@@ -936,7 +958,7 @@ class SwapService extends BaseService {
|
||||
is_swap: true,
|
||||
no_notification: evaluatedNoNotification,
|
||||
},
|
||||
swap.additional_items.map(i => ({
|
||||
swap.additional_items.map((i) => ({
|
||||
item_id: i.id,
|
||||
quantity: i.quantity,
|
||||
})),
|
||||
@@ -953,7 +975,7 @@ class SwapService extends BaseService {
|
||||
// Update all line items to reflect fulfillment
|
||||
for (const item of swap.additional_items) {
|
||||
const fulfillmentItem = successfullyFulfilled.find(
|
||||
f => item.id === f.item_id
|
||||
(f) => item.id === f.item_id
|
||||
)
|
||||
|
||||
if (fulfillmentItem) {
|
||||
@@ -998,7 +1020,7 @@ class SwapService extends BaseService {
|
||||
* @returns updated swap
|
||||
*/
|
||||
async cancelFulfillment(fulfillmentId) {
|
||||
return this.atomicPhase_(async manager => {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
const canceled = await this.fulfillmentService_
|
||||
.withTransaction(manager)
|
||||
.cancelFulfillment(fulfillmentId)
|
||||
@@ -1041,7 +1063,7 @@ class SwapService extends BaseService {
|
||||
) {
|
||||
const { metadata, no_notification } = config
|
||||
|
||||
return this.atomicPhase_(async manager => {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
const swap = await this.retrieve(swapId, {
|
||||
relations: ["additional_items"],
|
||||
})
|
||||
@@ -1067,7 +1089,7 @@ class SwapService extends BaseService {
|
||||
|
||||
// Go through all the additional items in the swap
|
||||
for (const i of swap.additional_items) {
|
||||
const shipped = shipment.items.find(si => si.item_id === i.id)
|
||||
const shipped = shipment.items.find((si) => si.item_id === i.id)
|
||||
if (shipped) {
|
||||
const shippedQty = (i.shipped_quantity || 0) + shipped.quantity
|
||||
await this.lineItemService_.withTransaction(manager).update(i.id, {
|
||||
@@ -1116,7 +1138,7 @@ class SwapService extends BaseService {
|
||||
const keyPath = `metadata.${key}`
|
||||
return this.swapModel_
|
||||
.updateOne({ _id: validatedId }, { $unset: { [keyPath]: "" } })
|
||||
.catch(err => {
|
||||
.catch((err) => {
|
||||
throw new MedusaError(MedusaError.Types.DB_ERROR, err.message)
|
||||
})
|
||||
}
|
||||
@@ -1129,7 +1151,7 @@ class SwapService extends BaseService {
|
||||
* @returns {Promise<Order>} the resulting order
|
||||
*/
|
||||
async registerReceived(id) {
|
||||
return this.atomicPhase_(async manager => {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
const swap = await this.retrieve(id, {
|
||||
relations: ["return_order", "return_order.items"],
|
||||
})
|
||||
|
||||
@@ -3,7 +3,7 @@ import { BaseService } from "medusa-interfaces"
|
||||
class SystemProviderService extends BaseService {
|
||||
static identifier = "system"
|
||||
|
||||
constructor({}) {
|
||||
constructor(_) {
|
||||
super()
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import _ from "lodash"
|
||||
import { BaseService } from "medusa-interfaces"
|
||||
import { MedusaError } from "medusa-core-utils"
|
||||
import carts from "../api/routes/store/carts"
|
||||
|
||||
/**
|
||||
* A service that calculates total and subtotals for orders, carts etc..
|
||||
@@ -158,6 +159,13 @@ class TotalsService extends BaseService {
|
||||
}
|
||||
}
|
||||
|
||||
if (order.claims && order.claims.length) {
|
||||
for (const c of order.claims) {
|
||||
const claimItemIds = c.additional_items.map(el => el.id)
|
||||
itemIds = [...itemIds, ...claimItemIds]
|
||||
}
|
||||
}
|
||||
|
||||
const refunds = lineItems.map(i => {
|
||||
if (!itemIds.includes(i.id)) {
|
||||
throw new MedusaError(
|
||||
@@ -253,6 +261,12 @@ class TotalsService extends BaseService {
|
||||
}
|
||||
}
|
||||
|
||||
if (cart.claims && cart.claims.length) {
|
||||
for (const c of cart.claims) {
|
||||
merged = [...merged, ...c.additional_items]
|
||||
}
|
||||
}
|
||||
|
||||
const { type, allocation, value } = discount.rule
|
||||
if (allocation === "total") {
|
||||
let percentage = 0
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import { BaseService } from "medusa-interfaces"
|
||||
import mongoose from "mongoose"
|
||||
import _ from "lodash"
|
||||
|
||||
class TransactionService extends BaseService {
|
||||
constructor({}) {
|
||||
constructor() {
|
||||
super()
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ import { BaseService } from "medusa-interfaces"
|
||||
|
||||
/**
|
||||
* Provides layer to manipulate users.
|
||||
* @implements BaseService
|
||||
* @extends BaseService
|
||||
*/
|
||||
class UserService extends BaseService {
|
||||
static Events = {
|
||||
@@ -48,9 +48,7 @@ class UserService extends BaseService {
|
||||
* @return {string} the validated email
|
||||
*/
|
||||
validateEmail_(email) {
|
||||
const schema = Validator.string()
|
||||
.email()
|
||||
.required()
|
||||
const schema = Validator.string().email().required()
|
||||
const { value, error } = schema.validate(email)
|
||||
if (error) {
|
||||
throw new MedusaError(
|
||||
@@ -75,6 +73,7 @@ class UserService extends BaseService {
|
||||
* Gets a user by id.
|
||||
* Throws in case of DB Error and if user was not found.
|
||||
* @param {string} userId - the id of the user to get.
|
||||
* @param {Object} config - query configs
|
||||
* @return {Promise<User>} the user document.
|
||||
*/
|
||||
async retrieve(userId, config = {}) {
|
||||
@@ -99,6 +98,7 @@ class UserService extends BaseService {
|
||||
* Gets a user by api token.
|
||||
* Throws in case of DB Error and if user was not found.
|
||||
* @param {string} apiToken - the token of the user to get.
|
||||
* @param {string[]} relations - relations to include with the user
|
||||
* @return {Promise<User>} the user document.
|
||||
*/
|
||||
async retrieveByApiToken(apiToken, relations = []) {
|
||||
@@ -123,6 +123,7 @@ class UserService extends BaseService {
|
||||
* Gets a user by email.
|
||||
* Throws in case of DB Error and if user was not found.
|
||||
* @param {string} email - the email of the user to get.
|
||||
* @param {Object} config - query config
|
||||
* @return {Promise<User>} the user document.
|
||||
*/
|
||||
async retrieveByEmail(email, config = {}) {
|
||||
@@ -144,7 +145,7 @@ class UserService extends BaseService {
|
||||
/**
|
||||
* Hashes a password
|
||||
* @param {string} password - the value to hash
|
||||
* @return hashed password
|
||||
* @return {string} hashed password
|
||||
*/
|
||||
async hashPassword_(password) {
|
||||
const buf = await Scrypt.kdf(password, { logN: 1, r: 1, p: 1 })
|
||||
@@ -155,10 +156,11 @@ class UserService extends BaseService {
|
||||
* Creates a user with username being validated.
|
||||
* Fails if email is not a valid format.
|
||||
* @param {object} user - the user to create
|
||||
* @param {string} password - user's password to hash
|
||||
* @return {Promise} the result of create
|
||||
*/
|
||||
async create(user, password) {
|
||||
return this.atomicPhase_(async manager => {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
const userRepo = manager.getCustomRepository(this.userRepository_)
|
||||
|
||||
const validatedEmail = this.validateEmail_(user.email)
|
||||
@@ -177,11 +179,12 @@ class UserService extends BaseService {
|
||||
|
||||
/**
|
||||
* Updates a user.
|
||||
* @param {object} user - the user to create
|
||||
* @param {object} userId - id of the user to update
|
||||
* @param {object} update - the values to be updated on the user
|
||||
* @return {Promise} the result of create
|
||||
*/
|
||||
async update(userId, update) {
|
||||
return this.atomicPhase_(async manager => {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
const userRepo = manager.getCustomRepository(this.userRepository_)
|
||||
const validatedId = this.validateId_(userId)
|
||||
|
||||
@@ -222,13 +225,15 @@ class UserService extends BaseService {
|
||||
* @return {Promise} the result of the delete operation.
|
||||
*/
|
||||
async delete(userId) {
|
||||
return this.atomicPhase_(async manager => {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
const userRepo = manager.getCustomRepository(this.userRepository_)
|
||||
|
||||
// Should not fail, if user does not exist, since delete is idempotent
|
||||
const user = await userRepo.findOne({ where: { id: userId } })
|
||||
|
||||
if (!user) return Promise.resolve()
|
||||
if (!user) {
|
||||
return Promise.resolve()
|
||||
}
|
||||
|
||||
await userRepo.softRemove(user)
|
||||
|
||||
@@ -242,10 +247,10 @@ class UserService extends BaseService {
|
||||
* password does not work.
|
||||
* @param {string} userId - the userId to set password for
|
||||
* @param {string} password - the old password to set
|
||||
* @returns {Promise} the result of the update operation
|
||||
* @return {Promise} the result of the update operation
|
||||
*/
|
||||
async setPassword_(userId, password) {
|
||||
return this.atomicPhase_(async manager => {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
const userRepo = manager.getCustomRepository(this.userRepository_)
|
||||
|
||||
const user = await this.retrieve(userId)
|
||||
@@ -270,8 +275,8 @@ class UserService extends BaseService {
|
||||
* The token will be signed with the users current password hash as a secret
|
||||
* a long side a payload with userId and the expiry time for the token, which
|
||||
* is always 15 minutes.
|
||||
* @param {User} user - the user to reset password for
|
||||
* @returns {string} the generated JSON web token
|
||||
* @param {string} userId - the id of the user to reset password for
|
||||
* @return {string} the generated JSON web token
|
||||
*/
|
||||
async generateResetPasswordToken(userId) {
|
||||
const user = await this.retrieve(userId)
|
||||
|
||||
@@ -32,7 +32,13 @@ class OrderSubscriber {
|
||||
handleOrderPlaced = async data => {
|
||||
const order = await this.orderService_.retrieve(data.id, {
|
||||
select: ["subtotal"],
|
||||
relations: ["discounts", "items", "gift_cards"],
|
||||
relations: [
|
||||
"discounts",
|
||||
"discounts.rule",
|
||||
"discounts.rule.valid_for",
|
||||
"items",
|
||||
"gift_cards",
|
||||
],
|
||||
})
|
||||
|
||||
await Promise.all(
|
||||
|
||||
@@ -0,0 +1,110 @@
|
||||
import ProductVariantService from "../services/product-variant"
|
||||
import ProductService from "../services/product"
|
||||
import { indexTypes } from "medusa-core-utils"
|
||||
|
||||
const searchFields = [
|
||||
"id",
|
||||
"title",
|
||||
"subtitle",
|
||||
"description",
|
||||
"handle",
|
||||
"is_giftcard",
|
||||
"discountable",
|
||||
"thumbnail",
|
||||
"profile_id",
|
||||
"collection_id",
|
||||
"type_id",
|
||||
"origin_country",
|
||||
"created_at",
|
||||
"updated_at",
|
||||
]
|
||||
|
||||
const searchRelations = [
|
||||
"variants",
|
||||
"tags",
|
||||
"type",
|
||||
"collection",
|
||||
"variants.prices",
|
||||
"variants.options",
|
||||
"options",
|
||||
]
|
||||
|
||||
class ProductSearchSubscriber {
|
||||
constructor({ eventBusService, searchService, productService }) {
|
||||
this.eventBus_ = eventBusService
|
||||
|
||||
this.searchService_ = searchService
|
||||
|
||||
this.productService_ = productService
|
||||
|
||||
this.eventBus_.subscribe(
|
||||
ProductService.Events.CREATED,
|
||||
this.handleProductCreation
|
||||
)
|
||||
|
||||
this.eventBus_.subscribe(
|
||||
ProductService.Events.UPDATED,
|
||||
this.handleProductUpdate
|
||||
)
|
||||
|
||||
this.eventBus_.subscribe(
|
||||
ProductService.Events.DELETED,
|
||||
this.handleProductDeletion
|
||||
)
|
||||
|
||||
this.eventBus_.subscribe(
|
||||
ProductVariantService.Events.CREATED,
|
||||
this.handleProductVariantChange
|
||||
)
|
||||
|
||||
this.eventBus_.subscribe(
|
||||
ProductVariantService.Events.UPDATED,
|
||||
this.handleProductVariantChange
|
||||
)
|
||||
|
||||
this.eventBus_.subscribe(
|
||||
ProductVariantService.Events.DELETED,
|
||||
this.handleProductVariantChange
|
||||
)
|
||||
}
|
||||
|
||||
handleProductCreation = async (data) => {
|
||||
const product = await this.retrieveProduct_(data.id)
|
||||
await this.searchService_.addDocuments(
|
||||
ProductService.IndexName,
|
||||
[product],
|
||||
indexTypes.products
|
||||
)
|
||||
}
|
||||
|
||||
retrieveProduct_ = async (product_id) => {
|
||||
return await this.productService_.retrieve(product_id, {
|
||||
select: searchFields,
|
||||
relations: searchRelations,
|
||||
})
|
||||
}
|
||||
|
||||
handleProductUpdate = async (data) => {
|
||||
const product = await this.retrieveProduct_(data.id)
|
||||
await this.searchService_.addDocuments(
|
||||
ProductService.IndexName,
|
||||
[product],
|
||||
indexTypes.products
|
||||
)
|
||||
}
|
||||
|
||||
handleProductDeletion = async (data) => {
|
||||
await this.searchService_.deleteDocument(ProductService.IndexName, data.id)
|
||||
}
|
||||
|
||||
handleProductVariantChange = async (data) => {
|
||||
const product = await this.retrieveProduct_(data.product_id)
|
||||
await this.searchService_.addDocuments(
|
||||
ProductService.IndexName,
|
||||
[product],
|
||||
indexTypes.products
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default ProductSearchSubscriber
|
||||
+700
-968
File diff suppressed because it is too large.
Load diff
Reference in new issue
Block a user