feat(medusa): filter shipping options by location (#8511)
what: - filters shipping options by location ID RESOLVES CC-328
This commit is contained in:
1 parent
d4c5f6593d
commit
a7cace90e0
4 files changed
+118
-22
No files matched your search
@@ -43,9 +43,7 @@ medusaIntegrationTestRunner({
|
|||||||
location = (
|
location = (
|
||||||
await api.post(
|
await api.post(
|
||||||
`/admin/stock-locations`,
|
`/admin/stock-locations`,
|
||||||
{
|
{ name: "Test location" },
|
||||||
name: "Test location",
|
|
||||||
},
|
|
||||||
adminHeaders
|
adminHeaders
|
||||||
)
|
)
|
||||||
).data.stock_location
|
).data.stock_location
|
||||||
@@ -53,10 +51,15 @@ medusaIntegrationTestRunner({
|
|||||||
location = (
|
location = (
|
||||||
await api.post(
|
await api.post(
|
||||||
`/admin/stock-locations/${location.id}/fulfillment-sets?fields=*fulfillment_sets`,
|
`/admin/stock-locations/${location.id}/fulfillment-sets?fields=*fulfillment_sets`,
|
||||||
{
|
{ name: "Test", type: "test-type" },
|
||||||
name: "Test",
|
adminHeaders
|
||||||
type: "test-type",
|
)
|
||||||
},
|
).data.stock_location
|
||||||
|
|
||||||
|
location2 = (
|
||||||
|
await api.post(
|
||||||
|
`/admin/stock-locations`,
|
||||||
|
{ name: "Test location 2" },
|
||||||
adminHeaders
|
adminHeaders
|
||||||
)
|
)
|
||||||
).data.stock_location
|
).data.stock_location
|
||||||
@@ -91,6 +94,46 @@ medusaIntegrationTestRunner({
|
|||||||
).data.region
|
).data.region
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe("GET /admin/shipping-options", () => {
|
||||||
|
it("should filters options by stock_location_id", async () => {
|
||||||
|
const shippingOptionPayload = {
|
||||||
|
name: "Test shipping option",
|
||||||
|
service_zone_id: fulfillmentSet.service_zones[0].id,
|
||||||
|
shipping_profile_id: shippingProfile.id,
|
||||||
|
provider_id: "manual_test-provider",
|
||||||
|
price_type: "flat",
|
||||||
|
type: {
|
||||||
|
label: "Test type",
|
||||||
|
description: "Test description",
|
||||||
|
code: "test-code",
|
||||||
|
},
|
||||||
|
prices: [{ currency_code: "usd", amount: 1000 }],
|
||||||
|
}
|
||||||
|
|
||||||
|
const {
|
||||||
|
data: { shipping_option: shippingOption },
|
||||||
|
} = await api.post(
|
||||||
|
`/admin/shipping-options`,
|
||||||
|
shippingOptionPayload,
|
||||||
|
adminHeaders
|
||||||
|
)
|
||||||
|
|
||||||
|
const shippingOptions = await api.get(
|
||||||
|
`/admin/shipping-options?stock_location_id=${location.id}`,
|
||||||
|
adminHeaders
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(shippingOptions.data.shipping_options).toHaveLength(1)
|
||||||
|
|
||||||
|
const shippingOptions2 = await api.get(
|
||||||
|
`/admin/shipping-options?stock_location_id=${location2.id}`,
|
||||||
|
adminHeaders
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(shippingOptions2.data.shipping_options).toHaveLength(0)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
describe("POST /admin/shipping-options", () => {
|
describe("POST /admin/shipping-options", () => {
|
||||||
it("should throw error when required params are missing", async () => {
|
it("should throw error when required params are missing", async () => {
|
||||||
const shippingOptionPayload = {
|
const shippingOptionPayload = {
|
||||||
|
|||||||
@@ -1,4 +1,13 @@
|
|||||||
import { MiddlewareRoute } from "@medusajs/framework"
|
import { MiddlewareRoute } from "@medusajs/framework"
|
||||||
|
import { maybeApplyLinkFilter } from "../../utils/maybe-apply-link-filter"
|
||||||
|
import { validateAndTransformBody } from "../../utils/validate-body"
|
||||||
|
import { validateAndTransformQuery } from "../../utils/validate-query"
|
||||||
|
import { createBatchBody } from "../../utils/validators"
|
||||||
|
import {
|
||||||
|
listTransformQueryConfig,
|
||||||
|
retrieveRuleTransformQueryConfig,
|
||||||
|
retrieveTransformQueryConfig,
|
||||||
|
} from "./query-config"
|
||||||
import {
|
import {
|
||||||
AdminCreateShippingOption,
|
AdminCreateShippingOption,
|
||||||
AdminCreateShippingOptionRule,
|
AdminCreateShippingOptionRule,
|
||||||
@@ -8,14 +17,6 @@ import {
|
|||||||
AdminUpdateShippingOption,
|
AdminUpdateShippingOption,
|
||||||
AdminUpdateShippingOptionRule,
|
AdminUpdateShippingOptionRule,
|
||||||
} from "./validators"
|
} from "./validators"
|
||||||
import {
|
|
||||||
listTransformQueryConfig,
|
|
||||||
retrieveRuleTransformQueryConfig,
|
|
||||||
retrieveTransformQueryConfig,
|
|
||||||
} from "./query-config"
|
|
||||||
import { validateAndTransformBody } from "../../utils/validate-body"
|
|
||||||
import { validateAndTransformQuery } from "../../utils/validate-query"
|
|
||||||
import { createBatchBody } from "../../utils/validators"
|
|
||||||
|
|
||||||
export const adminShippingOptionRoutesMiddlewares: MiddlewareRoute[] = [
|
export const adminShippingOptionRoutesMiddlewares: MiddlewareRoute[] = [
|
||||||
{
|
{
|
||||||
@@ -26,6 +27,12 @@ export const adminShippingOptionRoutesMiddlewares: MiddlewareRoute[] = [
|
|||||||
AdminGetShippingOptionsParams,
|
AdminGetShippingOptionsParams,
|
||||||
listTransformQueryConfig
|
listTransformQueryConfig
|
||||||
),
|
),
|
||||||
|
maybeApplyLinkFilter({
|
||||||
|
entryPoint: "location_fulfillment_set",
|
||||||
|
resourceId: "fulfillment_set_id",
|
||||||
|
filterableField: "stock_location_id",
|
||||||
|
filterByField: "service_zone.fulfillment_set_id",
|
||||||
|
}),
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ export const AdminGetShippingOptionsParams = createFindParams({
|
|||||||
service_zone_id: z.union([z.string(), z.array(z.string())]).optional(),
|
service_zone_id: z.union([z.string(), z.array(z.string())]).optional(),
|
||||||
shipping_profile_id: z.union([z.string(), z.array(z.string())]).optional(),
|
shipping_profile_id: z.union([z.string(), z.array(z.string())]).optional(),
|
||||||
provider_id: z.union([z.string(), z.array(z.string())]).optional(),
|
provider_id: z.union([z.string(), z.array(z.string())]).optional(),
|
||||||
|
stock_location_id: z.union([z.string(), z.array(z.string())]).optional(),
|
||||||
shipping_option_type_id: z
|
shipping_option_type_id: z
|
||||||
.union([z.string(), z.array(z.string())])
|
.union([z.string(), z.array(z.string())])
|
||||||
.optional(),
|
.optional(),
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ export function maybeApplyLinkFilter({
|
|||||||
entryPoint,
|
entryPoint,
|
||||||
resourceId,
|
resourceId,
|
||||||
filterableField,
|
filterableField,
|
||||||
|
filterByField = "id",
|
||||||
}) {
|
}) {
|
||||||
return async (req: MedusaRequest, _, next: NextFunction) => {
|
return async (req: MedusaRequest, _, next: NextFunction) => {
|
||||||
const filterableFields = req.filterableFields
|
const filterableFields = req.filterableFields
|
||||||
@@ -37,21 +38,65 @@ export function maybeApplyLinkFilter({
|
|||||||
})
|
})
|
||||||
|
|
||||||
const resources = await remoteQuery(queryObject)
|
const resources = await remoteQuery(queryObject)
|
||||||
|
let existingFilters = filterableFields[filterByField] as
|
||||||
|
| string[]
|
||||||
|
| string
|
||||||
|
| undefined
|
||||||
|
|
||||||
let existingIdFilters = filterableFields.id as string[] | string | undefined
|
if (existingFilters) {
|
||||||
if (existingIdFilters) {
|
if (typeof existingFilters === "string") {
|
||||||
if (typeof existingIdFilters === "string") {
|
existingFilters = [existingFilters]
|
||||||
existingIdFilters = [existingIdFilters]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
filterableFields.id = arrayIntersection(
|
filterableFields[filterByField] = arrayIntersection(
|
||||||
existingIdFilters,
|
existingFilters,
|
||||||
resources.map((p) => p[resourceId])
|
resources.map((p) => p[resourceId])
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
filterableFields.id = resources.map((p) => p[resourceId])
|
filterableFields[filterByField] = resources.map((p) => p[resourceId])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
req.filterableFields = transformFilterableFields(filterableFields)
|
||||||
|
|
||||||
return next()
|
return next()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
|
Transforms an object key string into nested objects
|
||||||
|
before = {
|
||||||
|
"test.something.another": []
|
||||||
|
}
|
||||||
|
|
||||||
|
after = {
|
||||||
|
test: {
|
||||||
|
something: {
|
||||||
|
another: []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
function transformFilterableFields(filterableFields: Record<string, unknown>) {
|
||||||
|
const result = {};
|
||||||
|
for (const key of Object.keys(filterableFields)) {
|
||||||
|
const value = filterableFields[key];
|
||||||
|
const keys = key.split(".");
|
||||||
|
let current = result;
|
||||||
|
|
||||||
|
// Iterate over the keys, creating nested objects as needed
|
||||||
|
for (let i = 0; i < keys.length; i++) {
|
||||||
|
const part = keys[i];
|
||||||
|
current[part] ??= {};
|
||||||
|
|
||||||
|
if (i === keys.length - 1) {
|
||||||
|
// If its the last key, assign the value
|
||||||
|
current[part] = value;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
current = current[part];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
Reference in new issue
Block a user