Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
14200b1
fix(core): Remove shipping lines from active orders when deleted or u…
kwerie Mar 11, 2026
dbb35d6
Merge branch 'master' into fix/shipping-line-deletion
kwerie Mar 11, 2026
dfcbf6e
chore(core): Update issue ref
kwerie Mar 11, 2026
a8ed3be
fix(core): Do not remove shipping lines from order when shipping meth…
kwerie Mar 11, 2026
7975d82
fix(core): Recalculate active orders when shipping method is unassign…
kwerie Mar 13, 2026
d97b39c
test(core): Remove duplicate query
kwerie Mar 13, 2026
23a1826
test(core): Remove mutation import
kwerie Mar 13, 2026
1984363
chore(core): Construct per channel context to avoid default channel c…
kwerie Mar 13, 2026
096c66c
test(core): Resolve CodeRabbit nitpick
kwerie Mar 13, 2026
f6cc8f8
fix(core): Use event ctx directly and manually remove shipping lines
kwerie Mar 13, 2026
ec6a1ae
Merge branch 'master' into fix/shipping-line-deletion
kwerie Mar 13, 2026
0c8567c
Merge branch 'master' into fix/shipping-line-deletion
kwerie Mar 16, 2026
3d7a333
fix(core): Properly delete shipping lines on channel unassignment
kwerie Mar 16, 2026
e3f5dbe
Merge remote-tracking branch 'refs/remotes/origin/fix/shipping-line-d…
kwerie Mar 17, 2026
83f03e6
Merge branch 'master' into fix/shipping-line-deletion
kwerie Mar 17, 2026
34e61f4
Merge branch 'master' into fix/shipping-line-deletion
kwerie Mar 23, 2026
149352e
Merge branch 'master' into fix/shipping-line-deletion
kwerie Apr 2, 2026
52237e7
Merge branch 'master' into fix/shipping-line-deletion
kwerie Apr 2, 2026
993ce26
Merge branch 'master' into fix/shipping-line-deletion
kwerie Apr 3, 2026
21094c1
Merge branch 'master' into fix/shipping-line-deletion
kwerie Apr 10, 2026
bfded41
Merge branch 'master' into fix/shipping-line-deletion
kwerie Apr 16, 2026
3212a80
Merge branch 'master' into fix/shipping-line-deletion
kwerie Apr 16, 2026
f9d7833
Merge branch 'master' into fix/shipping-line-deletion
kwerie Apr 30, 2026
1494e6e
Merge branch 'master' into fix/shipping-line-deletion
kwerie May 29, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
172 changes: 171 additions & 1 deletion packages/core/e2e/shipping-method.e2e-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,19 @@ import { SHIPPING_METHOD_FRAGMENT } from './graphql/fragments';
import * as Codegen from './graphql/generated-e2e-admin-types';
import { DeletionResult, LanguageCode } from './graphql/generated-e2e-admin-types';
import {
ASSIGN_PRODUCTVARIANT_TO_CHANNEL,
CREATE_CHANNEL,
CREATE_SHIPPING_METHOD,
DELETE_SHIPPING_METHOD,
GET_SHIPPING_METHOD_LIST,
UPDATE_SHIPPING_METHOD,
} from './graphql/shared-definitions';
import { GET_ACTIVE_SHIPPING_METHODS } from './graphql/shop-definitions';
import {
ADD_ITEM_TO_ORDER,
GET_ACTIVE_ORDER,
GET_ACTIVE_SHIPPING_METHODS,
SET_SHIPPING_METHOD,
} from './graphql/shop-definitions';

const TEST_METADATA = {
foo: 'bar',
Expand Down Expand Up @@ -515,6 +522,151 @@ describe('ShippingMethod resolver', () => {
expect(activeShippingMethods[0].name).toBe('Active Method');
expect(activeShippingMethods[0].description).toBe('This is an active shipping method');
});

// https://github.com/vendure-ecommerce/vendure/issues/XXXX
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
describe('shipping line removal on delete/unassign', () => {
let shippingMethodId: string;

beforeAll(async () => {
// Create a fresh shipping method for these tests
const { createShippingMethod } = await adminClient.query<
Codegen.CreateShippingMethodMutation,
Codegen.CreateShippingMethodMutationVariables
>(CREATE_SHIPPING_METHOD, {
input: {
code: 'delete-test-method',
fulfillmentHandler: manualFulfillmentHandler.code,
checker: {
code: defaultShippingEligibilityChecker.code,
arguments: [{ name: 'orderMinimum', value: '0' }],
},
calculator: {
code: defaultShippingCalculator.code,
arguments: [
{ name: 'rate', value: '500' },
{ name: 'includesTax', value: 'auto' },
{ name: 'taxRate', value: '0' },
],
},
translations: [
{ languageCode: LanguageCode.en, name: 'Delete Test Method', description: '' },
],
},
});
shippingMethodId = createShippingMethod.id;
});

it('removes shipping lines from active orders when shipping method is deleted', async () => {
// Create an active order with the shipping method
await shopClient.asAnonymousUser();
await shopClient.query(ADD_ITEM_TO_ORDER, {
productVariantId: 'T_1',
quantity: 1,
});
await shopClient.query(SET_SHIPPING_METHOD, { id: [shippingMethodId] });

// Verify the shipping line is present
const { activeOrder: orderBefore } = await shopClient.query(GET_ACTIVE_ORDER);
expect(orderBefore.shippingLines).toHaveLength(1);
expect(orderBefore.shippingLines[0].shippingMethod.id).toBe(shippingMethodId);

// Delete the shipping method
await adminClient.query<
Codegen.DeleteShippingMethodMutation,
Codegen.DeleteShippingMethodMutationVariables
>(DELETE_SHIPPING_METHOD, { id: shippingMethodId });

// Verify the shipping line has been removed from the active order
const { activeOrder: orderAfter } = await shopClient.query(GET_ACTIVE_ORDER);
expect(orderAfter.shippingLines).toHaveLength(0);
});
Comment thread
kwerie marked this conversation as resolved.
Outdated

it('removes shipping lines from active orders when shipping method is unassigned from channel', async () => {
// Create a new channel
const { createChannel } = await adminClient.query(CREATE_CHANNEL, {
input: {
code: 'shipping-test-channel',
token: 'shipping-test-channel-token',
defaultLanguageCode: LanguageCode.en,
currencyCode: 'USD',
pricesIncludeTax: false,
defaultShippingZoneId: 'T_1',
defaultTaxZoneId: 'T_1',
},
});
const channelId = createChannel.id;

// Create a shipping method and assign it to the new channel
const { createShippingMethod } = await adminClient.query<
Codegen.CreateShippingMethodMutation,
Codegen.CreateShippingMethodMutationVariables
>(CREATE_SHIPPING_METHOD, {
input: {
code: 'channel-test-method',
fulfillmentHandler: manualFulfillmentHandler.code,
checker: {
code: defaultShippingEligibilityChecker.code,
arguments: [{ name: 'orderMinimum', value: '0' }],
},
calculator: {
code: defaultShippingCalculator.code,
arguments: [
{ name: 'rate', value: '500' },
{ name: 'includesTax', value: 'auto' },
{ name: 'taxRate', value: '0' },
],
},
translations: [
{ languageCode: LanguageCode.en, name: 'Channel Test Method', description: '' },
],
},
});

await adminClient.query(ASSIGN_SHIPPING_METHODS_TO_CHANNEL, {
input: {
channelId,
shippingMethodIds: [createShippingMethod.id],
},
});

// Assign product variant to the new channel
await adminClient.query(ASSIGN_PRODUCTVARIANT_TO_CHANNEL, {
input: {
channelId,
productVariantIds: ['T_1'],
},
});

// Create an active order in the new channel with the shipping method
shopClient.setChannelToken('shipping-test-channel-token');
await shopClient.asAnonymousUser();
await shopClient.query(ADD_ITEM_TO_ORDER, {
productVariantId: 'T_1',
quantity: 1,
});
await shopClient.query(SET_SHIPPING_METHOD, { id: [createShippingMethod.id] });

// Verify shipping line is present
const { activeOrder: orderBefore } = await shopClient.query(GET_ACTIVE_ORDER);
expect(orderBefore.shippingLines).toHaveLength(1);
expect(orderBefore.shippingLines[0].shippingMethod.id).toBe(createShippingMethod.id);

// Remove the shipping method from the channel
await adminClient.query(REMOVE_SHIPPING_METHODS_FROM_CHANNEL, {
input: {
channelId,
shippingMethodIds: [createShippingMethod.id],
},
});

// Verify the shipping line has been removed from the active order
const { activeOrder: orderAfter } = await shopClient.query(GET_ACTIVE_ORDER);
expect(orderAfter.shippingLines).toHaveLength(0);

// Reset shop client to default channel
shopClient.setChannelToken('e2e-default-channel');
});
});
});

const GET_SHIPPING_METHOD = gql`
Expand Down Expand Up @@ -583,3 +735,21 @@ export const TEST_ELIGIBLE_SHIPPING_METHODS = gql`
}
}
`;

const ASSIGN_SHIPPING_METHODS_TO_CHANNEL = gql`
mutation AssignShippingMethodsToChannel($input: AssignShippingMethodsToChannelInput!) {
assignShippingMethodsToChannel(input: $input) {
id
name
}
}
`;

const REMOVE_SHIPPING_METHODS_FROM_CHANNEL = gql`
mutation RemoveShippingMethodsFromChannel($input: RemoveShippingMethodsFromChannelInput!) {
removeShippingMethodsFromChannel(input: $input) {
id
name
}
}
`;
25 changes: 25 additions & 0 deletions packages/core/src/service/services/shipping-method.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import { assertFound, idsAreEqual } from '../../common/utils';
import { ConfigService } from '../../config/config.service';
import { Logger } from '../../config/logger/vendure-logger';
import { TransactionalConnection } from '../../connection/transactional-connection';
import { ShippingLine } from '../../entity/shipping-line/shipping-line.entity';
import { ShippingMethodTranslation } from '../../entity/shipping-method/shipping-method-translation.entity';
import { ShippingMethod } from '../../entity/shipping-method/shipping-method.entity';
import { EventBus } from '../../event-bus';
Expand Down Expand Up @@ -191,6 +192,7 @@ export class ShippingMethodService {
shippingMethod.deletedAt = new Date();
await this.connection.getRepository(ctx, ShippingMethod).save(shippingMethod, { reload: false });
await this.eventBus.publish(new ShippingMethodEvent(ctx, shippingMethod, 'deleted', id));
await this.removeShippingMethodFromActiveOrders(ctx, ctx.channelId, id);
return {
result: DeletionResult.DELETED,
};
Expand Down Expand Up @@ -247,6 +249,8 @@ export class ShippingMethodService {
await this.channelService.removeFromChannels(ctx, ShippingMethod, shippingMethodId, [
input.channelId,
]);

await this.removeShippingMethodFromActiveOrders(ctx, input.channelId, shippingMethodId);
Comment thread
kwerie marked this conversation as resolved.
Outdated
}
return this.connection
.findByIdsInChannel(ctx, ShippingMethod, input.shippingMethodIds, ctx.channelId, {})
Expand Down Expand Up @@ -309,4 +313,25 @@ export class ShippingMethodService {
}
return handler.code;
}

private async removeShippingMethodFromActiveOrders(
ctx: RequestContext,
channelId: ID,
shippingMethodId: ID,
) {
const shippingLinesToRemove = await this.connection.getRepository(ctx, ShippingLine).find({
where: {
shippingMethodId,
order: {
channels: { id: channelId },
active: true,
},
},
relations: ['order', 'order.channels'],
});

if (!shippingLinesToRemove.length) return;

await this.connection.getRepository(ctx, ShippingLine).remove(shippingLinesToRemove);
Comment thread
kwerie marked this conversation as resolved.
Outdated
}
}
Loading