fix: Shipping profile deletion with options (#8910)
* fix(fulfillment): Prevent deletion of profiles associated to options * add tests * typo * address feedback
This commit is contained in:
@@ -29,7 +29,7 @@ export function InjectTransactionManager(
|
|||||||
: () => false
|
: () => false
|
||||||
managerProperty = isString(shouldForceTransactionOrManagerProperty)
|
managerProperty = isString(shouldForceTransactionOrManagerProperty)
|
||||||
? shouldForceTransactionOrManagerProperty
|
? shouldForceTransactionOrManagerProperty
|
||||||
: managerProperty
|
: managerProperty ?? "baseRepository_"
|
||||||
|
|
||||||
const argIndex = target.MedusaContextIndex_[propertyKey]
|
const argIndex = target.MedusaContextIndex_[propertyKey]
|
||||||
descriptor.value = async function (...args: any[]) {
|
descriptor.value = async function (...args: any[]) {
|
||||||
|
|||||||
+91
-1
@@ -6,13 +6,37 @@ import {
|
|||||||
MockEventBusService,
|
MockEventBusService,
|
||||||
moduleIntegrationTestRunner,
|
moduleIntegrationTestRunner,
|
||||||
} from "medusa-test-utils"
|
} from "medusa-test-utils"
|
||||||
import { buildExpectedEventMessageShape } from "../../__fixtures__"
|
import {
|
||||||
|
buildExpectedEventMessageShape,
|
||||||
|
generateCreateShippingOptionsData,
|
||||||
|
} from "../../__fixtures__"
|
||||||
import { FulfillmentEvents, Modules } from "@medusajs/utils"
|
import { FulfillmentEvents, Modules } from "@medusajs/utils"
|
||||||
|
import { resolve } from "path"
|
||||||
|
import { FulfillmentProviderService } from "@services"
|
||||||
|
import { FulfillmentProviderServiceFixtures } from "../../__fixtures__/providers"
|
||||||
|
|
||||||
jest.setTimeout(100000)
|
jest.setTimeout(100000)
|
||||||
|
|
||||||
|
const moduleOptions = {
|
||||||
|
providers: [
|
||||||
|
{
|
||||||
|
resolve: resolve(
|
||||||
|
process.cwd() +
|
||||||
|
"/integration-tests/__fixtures__/providers/default-provider"
|
||||||
|
),
|
||||||
|
id: "test-provider",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
const providerId = FulfillmentProviderService.getRegistrationIdentifier(
|
||||||
|
FulfillmentProviderServiceFixtures,
|
||||||
|
"test-provider"
|
||||||
|
)
|
||||||
|
|
||||||
moduleIntegrationTestRunner<IFulfillmentModuleService>({
|
moduleIntegrationTestRunner<IFulfillmentModuleService>({
|
||||||
moduleName: Modules.FULFILLMENT,
|
moduleName: Modules.FULFILLMENT,
|
||||||
|
moduleOptions,
|
||||||
testSuite: ({ service }) => {
|
testSuite: ({ service }) => {
|
||||||
let eventBusEmitSpy
|
let eventBusEmitSpy
|
||||||
|
|
||||||
@@ -121,6 +145,72 @@ moduleIntegrationTestRunner<IFulfillmentModuleService>({
|
|||||||
expect(err.message).toContain("exists")
|
expect(err.message).toContain("exists")
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe("on delete", () => {
|
||||||
|
it("should delete a shipping profile", async function () {
|
||||||
|
const createData: CreateShippingProfileDTO = {
|
||||||
|
name: "test-default-profile",
|
||||||
|
type: "default",
|
||||||
|
}
|
||||||
|
|
||||||
|
const createdShippingProfile = await service.createShippingProfiles(
|
||||||
|
createData
|
||||||
|
)
|
||||||
|
|
||||||
|
await service.deleteShippingProfiles(createdShippingProfile.id)
|
||||||
|
|
||||||
|
const [shippingProfile] = await service.listShippingProfiles({
|
||||||
|
id: createdShippingProfile.id,
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(shippingProfile).toBeUndefined()
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should not allow to delete a shipping profile that is associated to any shipping options", async function () {
|
||||||
|
const createData: CreateShippingProfileDTO = {
|
||||||
|
name: "test-default-profile",
|
||||||
|
type: "default",
|
||||||
|
}
|
||||||
|
|
||||||
|
const createdShippingProfile = await service.createShippingProfiles(
|
||||||
|
createData
|
||||||
|
)
|
||||||
|
|
||||||
|
const fulfillmentSet = await service.createFulfillmentSets({
|
||||||
|
name: "test",
|
||||||
|
type: "test-type",
|
||||||
|
service_zones: [
|
||||||
|
{
|
||||||
|
name: "test",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
await service.createShippingOptions([
|
||||||
|
generateCreateShippingOptionsData({
|
||||||
|
service_zone_id: fulfillmentSet.service_zones[0].id,
|
||||||
|
shipping_profile_id: createdShippingProfile.id,
|
||||||
|
provider_id: providerId,
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
attribute: "test-attribute",
|
||||||
|
operator: "in",
|
||||||
|
value: ["test"],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
])
|
||||||
|
|
||||||
|
const err = await service
|
||||||
|
.deleteShippingProfiles(createdShippingProfile.id)
|
||||||
|
.catch((e) => e)
|
||||||
|
|
||||||
|
expect(err).toBeTruthy()
|
||||||
|
expect(err.message).toContain(
|
||||||
|
`Cannot delete Shipping Profiles ${createdShippingProfile.id} with associated Shipping Options. Delete Shipping Options first and try again.`
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import {
|
|||||||
ModuleJoinerConfig,
|
ModuleJoinerConfig,
|
||||||
ModulesSdkTypes,
|
ModulesSdkTypes,
|
||||||
ShippingOptionDTO,
|
ShippingOptionDTO,
|
||||||
|
SoftDeleteReturn,
|
||||||
UpdateFulfillmentSetDTO,
|
UpdateFulfillmentSetDTO,
|
||||||
UpdateServiceZoneDTO,
|
UpdateServiceZoneDTO,
|
||||||
} from "@medusajs/types"
|
} from "@medusajs/types"
|
||||||
@@ -1953,6 +1954,63 @@ export default class FulfillmentModuleService
|
|||||||
return !!shippingOptions.length
|
return !!shippingOptions.length
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@InjectTransactionManager()
|
||||||
|
// @ts-expect-error
|
||||||
|
async deleteShippingProfiles(
|
||||||
|
ids: string | string[],
|
||||||
|
@MedusaContext() sharedContext: Context = {}
|
||||||
|
) {
|
||||||
|
const shippingProfileIds = Array.isArray(ids) ? ids : [ids]
|
||||||
|
await this.validateShippingProfileDeletion(
|
||||||
|
shippingProfileIds,
|
||||||
|
sharedContext
|
||||||
|
)
|
||||||
|
|
||||||
|
return await super.deleteShippingProfiles(shippingProfileIds, sharedContext)
|
||||||
|
}
|
||||||
|
|
||||||
|
@InjectTransactionManager()
|
||||||
|
// @ts-expect-error
|
||||||
|
async softDeleteShippingProfiles<
|
||||||
|
TReturnableLinkableKeys extends string = string
|
||||||
|
>(
|
||||||
|
ids: string[],
|
||||||
|
config?: SoftDeleteReturn<TReturnableLinkableKeys>,
|
||||||
|
@MedusaContext() sharedContext: Context = {}
|
||||||
|
): Promise<Record<string, string[]> | void> {
|
||||||
|
await this.validateShippingProfileDeletion(ids, sharedContext)
|
||||||
|
|
||||||
|
return await super.softDeleteShippingProfiles(ids, config, sharedContext)
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async validateShippingProfileDeletion(
|
||||||
|
ids: string[],
|
||||||
|
sharedContext: Context
|
||||||
|
) {
|
||||||
|
const shippingProfileIds = Array.isArray(ids) ? ids : [ids]
|
||||||
|
const shippingProfiles = await this.shippingProfileService_.list(
|
||||||
|
{ id: shippingProfileIds },
|
||||||
|
{
|
||||||
|
relations: ["shipping_options.id"],
|
||||||
|
},
|
||||||
|
sharedContext
|
||||||
|
)
|
||||||
|
|
||||||
|
const undeletableShippingProfiles = shippingProfiles.filter(
|
||||||
|
(profile) => profile.shipping_options.length > 0
|
||||||
|
)
|
||||||
|
if (undeletableShippingProfiles.length) {
|
||||||
|
const undeletableShippingProfileIds = undeletableShippingProfiles.map(
|
||||||
|
(profile) => profile.id
|
||||||
|
)
|
||||||
|
|
||||||
|
throw new MedusaError(
|
||||||
|
MedusaError.Types.INVALID_DATA,
|
||||||
|
`Cannot delete Shipping Profiles ${undeletableShippingProfileIds} with associated Shipping Options. Delete Shipping Options first and try again.`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected static canCancelFulfillmentOrThrow(fulfillment: Fulfillment) {
|
protected static canCancelFulfillmentOrThrow(fulfillment: Fulfillment) {
|
||||||
if (fulfillment.shipped_at) {
|
if (fulfillment.shipped_at) {
|
||||||
throw new MedusaError(
|
throw new MedusaError(
|
||||||
|
|||||||
Reference in New Issue
Block a user