feat(medusa): enable price list prices for product endpoints (#10197)

This commit is contained in:
Riqwan Thamir
2024-11-21 14:08:42 +01:00
committed by GitHub
parent 35afa04a93
commit d91da7b418
3 changed files with 412 additions and 24 deletions

View File

@@ -1,6 +1,12 @@
import { medusaIntegrationTestRunner } from "@medusajs/test-utils"
import { IStoreModuleService } from "@medusajs/types"
import { ApiKeyType, Modules, ProductStatus } from "@medusajs/utils"
import {
ApiKeyType,
Modules,
PriceListStatus,
PriceListType,
ProductStatus,
} from "@medusajs/utils"
import qs from "qs"
import {
adminHeaders,
@@ -9,6 +15,7 @@ import {
generateStoreHeaders,
} from "../../../../helpers/create-admin-user"
import { getProductFixture } from "../../../../helpers/fixtures"
import { createAuthenticatedCustomer } from "../../../../modules/helpers/create-authenticated-customer"
jest.setTimeout(30000)
@@ -27,10 +34,13 @@ medusaIntegrationTestRunner({
let variant2
let variant3
let variant4
let region
let inventoryItem1
let inventoryItem2
let storeHeaders
let publishableKey
let storeHeadersWithCustomer
let customer
const createProducts = async (data) => {
const response = await api.post(
@@ -86,6 +96,19 @@ medusaIntegrationTestRunner({
publishableKey = await generatePublishableKey(appContainer)
storeHeaders = generateStoreHeaders({ publishableKey })
await createAdminUser(dbConnection, adminHeaders, appContainer)
const result = await createAuthenticatedCustomer(api, storeHeaders, {
first_name: "tony",
last_name: "stark",
email: "tony@stark-industries.com",
})
customer = result.customer
storeHeadersWithCustomer = {
headers: {
...storeHeaders.headers,
authorization: `Bearer ${result.jwt}`,
},
}
const storeModule: IStoreModuleService = appContainer.resolve(
Modules.STORE
@@ -104,6 +127,14 @@ medusaIntegrationTestRunner({
{ currency_code: "dkk" },
],
})
region = (
await api.post(
"/admin/regions",
{ name: "Test Region", currency_code: "usd" },
adminHeaders
)
).data.region
})
describe("Get products based on publishable key", () => {
@@ -954,14 +985,6 @@ medusaIntegrationTestRunner({
})
it("should list products with prices when context is present", async () => {
const region = (
await api.post(
"/admin/regions",
{ name: "Test Region", currency_code: "usd" },
adminHeaders
)
).data.region
let response = await api.get(
`/store/products?fields=*variants.calculated_price&region_id=${region.id}`,
storeHeaders
@@ -1023,6 +1046,192 @@ medusaIntegrationTestRunner({
expect(response.data.products).toEqual(expectation)
})
describe("with price lists", () => {
let customerGroup
beforeEach(async () => {
customerGroup = (
await api.post(
"/admin/customer-groups",
{ name: "VIP" },
adminHeaders
)
).data.customer_group
await api.post(
`/admin/customer-groups/${customerGroup.id}/customers`,
{ add: [customer.id] },
adminHeaders
)
})
it("should list products with prices with a sale price list price", async () => {
const priceList = (
await api.post(
`/admin/price-lists`,
{
title: "test price list",
description: "test",
status: PriceListStatus.ACTIVE,
type: PriceListType.SALE,
prices: [
{
amount: 350,
currency_code: "usd",
variant_id: product.variants[0].id,
},
],
rules: { customer_group_id: [customerGroup.id] },
},
adminHeaders
)
).data.price_list
let response = await api.get(
`/store/products?fields=*variants.calculated_price&region_id=${region.id}`,
storeHeadersWithCustomer
)
const expectation = expect.arrayContaining([
expect.objectContaining({
id: product.id,
variants: [
expect.objectContaining({
calculated_price: {
id: expect.any(String),
is_calculated_price_price_list: true,
is_calculated_price_tax_inclusive: false,
calculated_amount: 350,
raw_calculated_amount: {
value: "350",
precision: 20,
},
is_original_price_price_list: false,
is_original_price_tax_inclusive: false,
original_amount: 3000,
raw_original_amount: {
value: "3000",
precision: 20,
},
currency_code: "usd",
calculated_price: {
id: expect.any(String),
price_list_id: priceList.id,
price_list_type: "sale",
min_quantity: null,
max_quantity: null,
},
original_price: {
id: expect.any(String),
price_list_id: null,
price_list_type: null,
min_quantity: null,
max_quantity: null,
},
},
}),
],
}),
])
expect(response.status).toEqual(200)
expect(response.data.count).toEqual(3)
expect(response.data.products).toEqual(expectation)
// with only region_id
response = await api.get(
`/store/products?region_id=${region.id}`,
storeHeadersWithCustomer
)
expect(response.status).toEqual(200)
expect(response.data.products).toEqual(expectation)
})
it("should list products with prices with a override price list price", async () => {
const priceList = (
await api.post(
`/admin/price-lists`,
{
title: "test price list",
description: "test",
status: PriceListStatus.ACTIVE,
type: PriceListType.OVERRIDE,
prices: [
{
amount: 350,
currency_code: "usd",
variant_id: product.variants[0].id,
},
],
rules: { customer_group_id: [customerGroup.id] },
},
adminHeaders
)
).data.price_list
let response = await api.get(
`/store/products?fields=*variants.calculated_price&region_id=${region.id}`,
storeHeadersWithCustomer
)
const expectation = expect.arrayContaining([
expect.objectContaining({
id: product.id,
variants: [
expect.objectContaining({
calculated_price: {
id: expect.any(String),
is_calculated_price_price_list: true,
is_calculated_price_tax_inclusive: false,
calculated_amount: 350,
raw_calculated_amount: {
value: "350",
precision: 20,
},
is_original_price_price_list: true,
is_original_price_tax_inclusive: false,
original_amount: 350,
raw_original_amount: {
value: "350",
precision: 20,
},
currency_code: "usd",
calculated_price: {
id: expect.any(String),
price_list_id: priceList.id,
price_list_type: "override",
min_quantity: null,
max_quantity: null,
},
original_price: {
id: expect.any(String),
price_list_id: priceList.id,
price_list_type: "override",
min_quantity: null,
max_quantity: null,
},
},
}),
],
}),
])
expect(response.status).toEqual(200)
expect(response.data.count).toEqual(3)
expect(response.data.products).toEqual(expectation)
// with only region_id
response = await api.get(
`/store/products?region_id=${region.id}`,
storeHeadersWithCustomer
)
expect(response.status).toEqual(200)
expect(response.data.products).toEqual(expectation)
})
})
describe("with inventory items", () => {
let location1
let location2
@@ -1291,14 +1500,6 @@ medusaIntegrationTestRunner({
})
it("should get product with prices when context is present", async () => {
const region = (
await api.post(
"/admin/regions",
{ name: "Test Region", currency_code: "usd" },
adminHeaders
)
).data.region
let response = await api.get(
`/store/products/${product.id}?fields=*variants.calculated_price&region_id=${region.id}`,
storeHeaders
@@ -1356,6 +1557,186 @@ medusaIntegrationTestRunner({
expect(response.status).toEqual(200)
expect(response.data.product).toEqual(expectation)
})
describe("with price lists", () => {
let customerGroup
beforeEach(async () => {
customerGroup = (
await api.post(
"/admin/customer-groups",
{ name: "VIP" },
adminHeaders
)
).data.customer_group
await api.post(
`/admin/customer-groups/${customerGroup.id}/customers`,
{ add: [customer.id] },
adminHeaders
)
})
it("should return product with sale price list prices", async () => {
const priceList = (
await api.post(
`/admin/price-lists`,
{
title: "test price list",
description: "test",
status: PriceListStatus.ACTIVE,
type: PriceListType.SALE,
prices: [
{
amount: 350,
currency_code: "usd",
variant_id: product.variants[0].id,
},
],
rules: { customer_group_id: [customerGroup.id] },
},
adminHeaders
)
).data.price_list
let response = await api.get(
`/store/products/${product.id}?fields=*variants.calculated_price&region_id=${region.id}`,
storeHeadersWithCustomer
)
const expectation = expect.objectContaining({
id: product.id,
variants: [
expect.objectContaining({
calculated_price: {
id: expect.any(String),
is_calculated_price_price_list: true,
is_calculated_price_tax_inclusive: false,
calculated_amount: 350,
raw_calculated_amount: {
value: "350",
precision: 20,
},
is_original_price_price_list: false,
is_original_price_tax_inclusive: false,
original_amount: 3000,
raw_original_amount: {
value: "3000",
precision: 20,
},
currency_code: "usd",
calculated_price: {
id: expect.any(String),
price_list_id: priceList.id,
price_list_type: "sale",
min_quantity: null,
max_quantity: null,
},
original_price: {
id: expect.any(String),
price_list_id: null,
price_list_type: null,
min_quantity: null,
max_quantity: null,
},
},
}),
],
})
expect(response.status).toEqual(200)
expect(response.data.product).toEqual(expectation)
// with only region_id
response = await api.get(
`/store/products/${product.id}?region_id=${region.id}`,
storeHeadersWithCustomer
)
expect(response.status).toEqual(200)
expect(response.data.product).toEqual(expectation)
})
it("should list products with prices with a override price list price", async () => {
const priceList = (
await api.post(
`/admin/price-lists`,
{
title: "test price list",
description: "test",
status: PriceListStatus.ACTIVE,
type: PriceListType.OVERRIDE,
prices: [
{
amount: 350,
currency_code: "usd",
variant_id: product.variants[0].id,
},
],
rules: { customer_group_id: [customerGroup.id] },
},
adminHeaders
)
).data.price_list
let response = await api.get(
`/store/products/${product.id}?fields=*variants.calculated_price&region_id=${region.id}`,
storeHeadersWithCustomer
)
const expectation = expect.objectContaining({
id: product.id,
variants: [
expect.objectContaining({
calculated_price: {
id: expect.any(String),
is_calculated_price_price_list: true,
is_calculated_price_tax_inclusive: false,
calculated_amount: 350,
raw_calculated_amount: {
value: "350",
precision: 20,
},
is_original_price_price_list: true,
is_original_price_tax_inclusive: false,
original_amount: 350,
raw_original_amount: {
value: "350",
precision: 20,
},
currency_code: "usd",
calculated_price: {
id: expect.any(String),
price_list_id: priceList.id,
price_list_type: "override",
min_quantity: null,
max_quantity: null,
},
original_price: {
id: expect.any(String),
price_list_id: priceList.id,
price_list_type: "override",
min_quantity: null,
max_quantity: null,
},
},
}),
],
})
expect(response.status).toEqual(200)
expect(response.data.product).toEqual(expectation)
// with only region_id
response = await api.get(
`/store/products/${product.id}?region_id=${region.id}`,
storeHeadersWithCustomer
)
expect(response.status).toEqual(200)
expect(response.data.product).toEqual(expectation)
})
})
})
describe("Tax handling", () => {

View File

@@ -1,19 +1,20 @@
import { isPresent, ProductStatus } from "@medusajs/framework/utils"
import { validateAndTransformQuery } from "@medusajs/framework"
import {
applyDefaultFilters,
applyParamsAsFilters,
authenticate,
clearFiltersByKey,
maybeApplyLinkFilter,
MiddlewareRoute,
setContext,
} from "@medusajs/framework/http"
import { isPresent, ProductStatus } from "@medusajs/framework/utils"
import {
filterByValidSalesChannels,
normalizeDataForContext,
setPricingContext,
setTaxContext,
} from "../../utils/middlewares"
import { validateAndTransformQuery } from "@medusajs/framework"
import { maybeApplyStockLocationId } from "./helpers"
import * as QueryConfig from "./query-config"
import { StoreGetProductsParams } from "./validators"
@@ -23,6 +24,9 @@ export const storeProductRoutesMiddlewares: MiddlewareRoute[] = [
method: ["GET"],
matcher: "/store/products",
middlewares: [
authenticate("customer", ["session", "bearer"], {
allowUnauthenticated: true,
}),
validateAndTransformQuery(
StoreGetProductsParams,
QueryConfig.listProductQueryConfig
@@ -60,6 +64,9 @@ export const storeProductRoutesMiddlewares: MiddlewareRoute[] = [
method: ["GET"],
matcher: "/store/products/:id",
middlewares: [
authenticate("customer", ["session", "bearer"], {
allowUnauthenticated: true,
}),
validateAndTransformQuery(
StoreGetProductsParams,
QueryConfig.retrieveProductQueryConfig

View File

@@ -1,11 +1,11 @@
import { MedusaPricingContext } from "@medusajs/framework/types"
import { MedusaError } from "@medusajs/framework/utils"
import { NextFunction } from "express"
import {
AuthenticatedMedusaRequest,
refetchEntities,
refetchEntity,
} from "@medusajs/framework/http"
import { MedusaPricingContext } from "@medusajs/framework/types"
import { MedusaError } from "@medusajs/framework/utils"
import { NextFunction } from "express"
export function setPricingContext() {
return async (req: AuthenticatedMedusaRequest, _, next: NextFunction) => {
@@ -41,10 +41,10 @@ export function setPricingContext() {
}
// Find all the customer groups the customer is a part of and set
if (req.user?.customer_id) {
if (req.auth_context?.actor_id) {
const customerGroups = await refetchEntities(
"customer_group",
{ customer_id: req.user?.customer_id },
{ customers: { id: req.auth_context.actor_id } },
req.scope,
["id"]
)