fix(medusa, medusa-js): Use price selection strategy for GET /admin/variants (#2270)
**What** - Adds the use of price selection strategy to the endpoint `GET /admin/variants` - Updates medusa-js to reflect this change (expanding the parameters). **Testing** - Adds a new integration test validating that returned variants are now of type PricedVariant, with the expected fields: original_price, calculated_price, etc. **Why** - Our current RMA flows (in our admin dashboard) relied heavily on simply using `order.tax_rate` to calculate variant prices in the different RMA menus. As taxes in Medusa, have become feature complete this approach had become very naive and has several potential issues. Moving the responsibility for calculating the correct prices guarantees that we always show the correct prices to admins.
This commit is contained in:
@@ -1,15 +1,26 @@
|
||||
import { Router } from "express"
|
||||
|
||||
import { PaginatedResponse } from "../../../../types/common"
|
||||
import { ProductVariant } from "../../../../models/product-variant"
|
||||
import middlewares from "../../../middlewares"
|
||||
import { PaginatedResponse } from "../../../../types/common"
|
||||
import { PricedVariant } from "../../../../types/pricing"
|
||||
import middlewares, { transformQuery } from "../../../middlewares"
|
||||
import { AdminGetVariantsParams } from "./list-variants"
|
||||
|
||||
const route = Router()
|
||||
|
||||
export default (app) => {
|
||||
app.use("/variants", route)
|
||||
|
||||
route.get("/", middlewares.wrap(require("./list-variants").default))
|
||||
route.get(
|
||||
"/",
|
||||
transformQuery(AdminGetVariantsParams, {
|
||||
defaultRelations: defaultAdminVariantRelations,
|
||||
defaultFields: defaultAdminVariantFields,
|
||||
allowedFields: allowedAdminVariantFields,
|
||||
isList: true,
|
||||
}),
|
||||
middlewares.wrap(require("./list-variants").default)
|
||||
)
|
||||
|
||||
return app
|
||||
}
|
||||
@@ -69,7 +80,7 @@ export const allowedAdminVariantRelations: (keyof ProductVariant)[] = [
|
||||
]
|
||||
|
||||
export type AdminVariantsListRes = PaginatedResponse & {
|
||||
variants: ProductVariant[]
|
||||
variants: PricedVariant[]
|
||||
}
|
||||
|
||||
export * from "./list-variants"
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
import { IsInt, IsOptional, IsString } from "class-validator"
|
||||
import { defaultAdminVariantFields, defaultAdminVariantRelations } from "./"
|
||||
|
||||
import { FilterableProductVariantProps } from "../../../../types/product-variant"
|
||||
import { FindConfig } from "../../../../types/common"
|
||||
import { ProductVariant } from "../../../../models/product-variant"
|
||||
import ProductVariantService from "../../../../services/product-variant"
|
||||
import { Type } from "class-transformer"
|
||||
import { validator } from "../../../../utils/validator"
|
||||
import { omit } from "lodash"
|
||||
import {
|
||||
CartService,
|
||||
PricingService,
|
||||
RegionService,
|
||||
} from "../../../../services"
|
||||
import ProductVariantService from "../../../../services/product-variant"
|
||||
import { NumericalComparisonOperator } from "../../../../types/common"
|
||||
import { AdminPriceSelectionParams } from "../../../../types/price-selection"
|
||||
import { IsType } from "../../../../utils/validators/is-type"
|
||||
|
||||
/**
|
||||
* @oas [get] /variants
|
||||
@@ -15,9 +19,51 @@ import { validator } from "../../../../utils/validator"
|
||||
* description: "Retrieves a list of Product Variants"
|
||||
* x-authenticated: true
|
||||
* parameters:
|
||||
* - (query) q {string} Query used for searching variants.
|
||||
* - (query) offset=0 {integer} How many variants to skip in the result.
|
||||
* - (query) limit=20 {integer} Limit the number of variants returned.
|
||||
* - (query) id {string} A Product Variant id to filter by.
|
||||
* - (query) ids {string} A comma separated list of Product Variant ids to filter by.
|
||||
* - (query) expand {string} A comma separated list of Product Variant relations to load.
|
||||
* - (query) fields {string} A comma separated list of Product Variant fields to include.
|
||||
* - (query) offset=0 {number} How many product variants to skip in the result.
|
||||
* - (query) limit=100 {number} Maximum number of Product Variants to return.
|
||||
* - (query) cart_id {string} The id of the cart to use for price selection.
|
||||
* - (query) region_id {string} The id of the region to use for price selection.
|
||||
* - (query) currency_code {string} The currency code to use for price selection.
|
||||
* - (query) customer_id {string} The id of the customer to use for price selection.
|
||||
* - in: query
|
||||
* name: title
|
||||
* style: form
|
||||
* explode: false
|
||||
* description: product variant title to search for.
|
||||
* schema:
|
||||
* oneOf:
|
||||
* - type: string
|
||||
* description: a single title to search by
|
||||
* - type: array
|
||||
* description: multiple titles to search by
|
||||
* items:
|
||||
* type: string
|
||||
* - in: query
|
||||
* name: inventory_quantity
|
||||
* description: Filter by available inventory quantity
|
||||
* schema:
|
||||
* oneOf:
|
||||
* - type: number
|
||||
* description: a specific number to search by.
|
||||
* - type: object
|
||||
* description: search using less and greater than comparisons.
|
||||
* properties:
|
||||
* lt:
|
||||
* type: number
|
||||
* description: filter by inventory quantity less than this number
|
||||
* gt:
|
||||
* type: number
|
||||
* description: filter by inventory quantity greater than this number
|
||||
* lte:
|
||||
* type: number
|
||||
* description: filter by inventory quantity less than or equal to this number
|
||||
* gte:
|
||||
* type: number
|
||||
* description: filter by inventory quantity greater than or equal to this number
|
||||
* x-codeSamples:
|
||||
* - lang: JavaScript
|
||||
* label: JS Client
|
||||
@@ -77,44 +123,84 @@ export default async (req, res) => {
|
||||
"productVariantService"
|
||||
)
|
||||
|
||||
const { offset, limit, q } = await validator(
|
||||
AdminGetVariantsParams,
|
||||
req.query
|
||||
const pricingService: PricingService = req.scope.resolve("pricingService")
|
||||
const cartService: CartService = req.scope.resolve("cartService")
|
||||
const regionService: RegionService = req.scope.resolve("regionService")
|
||||
|
||||
// We need to remove the price selection params from the array of fields
|
||||
const cleanFilterableFields = omit(req.filterableFields, [
|
||||
"cart_id",
|
||||
"region_id",
|
||||
"currency_code",
|
||||
"customer_id",
|
||||
])
|
||||
|
||||
const [rawVariants, count] = await variantService.listAndCount(
|
||||
cleanFilterableFields,
|
||||
req.listConfig
|
||||
)
|
||||
|
||||
const selector: FilterableProductVariantProps = {}
|
||||
|
||||
if ("q" in req.query) {
|
||||
selector.q = q
|
||||
let regionId = req.validatedQuery.region_id
|
||||
let currencyCode = req.validatedQuery.currency_code
|
||||
if (req.validatedQuery.cart_id) {
|
||||
const cart = await cartService.retrieve(req.validatedQuery.cart_id, {
|
||||
select: ["id", "region_id"],
|
||||
})
|
||||
const region = await regionService.retrieve(cart.region_id, {
|
||||
select: ["id", "currency_code"],
|
||||
})
|
||||
regionId = region.id
|
||||
currencyCode = region.currency_code
|
||||
}
|
||||
|
||||
const listConfig: FindConfig<ProductVariant> = {
|
||||
select: defaultAdminVariantFields,
|
||||
relations: defaultAdminVariantRelations,
|
||||
skip: offset,
|
||||
take: limit,
|
||||
}
|
||||
const variants = await pricingService.setVariantPrices(rawVariants, {
|
||||
cart_id: req.validatedQuery.cart_id,
|
||||
region_id: regionId,
|
||||
currency_code: currencyCode,
|
||||
customer_id: req.validatedQuery.customer_id,
|
||||
include_discount_prices: true,
|
||||
})
|
||||
|
||||
const [variants, count] = await variantService.listAndCount(
|
||||
selector,
|
||||
listConfig
|
||||
)
|
||||
|
||||
res.json({ variants, count, offset, limit })
|
||||
res.json({
|
||||
variants,
|
||||
count,
|
||||
offset: req.listConfig.offset,
|
||||
limit: req.listConfig.limit,
|
||||
})
|
||||
}
|
||||
|
||||
export class AdminGetVariantsParams {
|
||||
@IsString()
|
||||
export class AdminGetVariantsParams extends AdminPriceSelectionParams {
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
q?: string
|
||||
|
||||
@IsInt()
|
||||
@IsOptional()
|
||||
@IsInt()
|
||||
@Type(() => Number)
|
||||
limit?: number = 20
|
||||
|
||||
@IsInt()
|
||||
@IsOptional()
|
||||
@IsInt()
|
||||
@Type(() => Number)
|
||||
offset?: number = 0
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
expand?: string
|
||||
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
fields?: string
|
||||
|
||||
@IsOptional()
|
||||
@IsType([String, [String]])
|
||||
id?: string | string[]
|
||||
|
||||
@IsOptional()
|
||||
@IsType([String, [String]])
|
||||
title?: string | string[]
|
||||
|
||||
@IsOptional()
|
||||
@IsType([Number, NumericalComparisonOperator])
|
||||
inventory_quantity?: number | NumericalComparisonOperator
|
||||
}
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
import { IsInt, IsOptional, IsString } from "class-validator"
|
||||
import {
|
||||
CartService,
|
||||
PricingService,
|
||||
ProductVariantService,
|
||||
RegionService,
|
||||
} from "../../../../services"
|
||||
import { IsInt, IsOptional, IsString } from "class-validator"
|
||||
|
||||
import { FilterableProductVariantProps } from "../../../../types/product-variant"
|
||||
import { IsType } from "../../../../utils/validators/is-type"
|
||||
import { Type } from "class-transformer"
|
||||
import { omit } from "lodash"
|
||||
import { defaultStoreVariantRelations } from "."
|
||||
import { NumericalComparisonOperator } from "../../../../types/common"
|
||||
import { PriceSelectionParams } from "../../../../types/price-selection"
|
||||
import { Type } from "class-transformer"
|
||||
import { defaultStoreVariantRelations } from "."
|
||||
import { omit } from "lodash"
|
||||
import { FilterableProductVariantProps } from "../../../../types/product-variant"
|
||||
import { validator } from "../../../../utils/validator"
|
||||
import { IsType } from "../../../../utils/validators/is-type"
|
||||
|
||||
/**
|
||||
* @oas [get] /variants
|
||||
|
||||
@@ -13,3 +13,9 @@ export class PriceSelectionParams {
|
||||
@IsString()
|
||||
currency_code?: string
|
||||
}
|
||||
|
||||
export class AdminPriceSelectionParams extends PriceSelectionParams {
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
customer_id?: string
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user