|
|
@@ -53,7 +53,6 @@ import {
|
|
|
CancelPaymentError,
|
|
|
EmptyOrderLineSelectionError,
|
|
|
FulfillmentStateTransitionError,
|
|
|
- RefundStateTransitionError,
|
|
|
InsufficientStockOnHandError,
|
|
|
ItemsAlreadyFulfilledError,
|
|
|
ManualPaymentStateError,
|
|
|
@@ -61,6 +60,7 @@ import {
|
|
|
NothingToRefundError,
|
|
|
PaymentOrderMismatchError,
|
|
|
RefundOrderStateError,
|
|
|
+ RefundStateTransitionError,
|
|
|
SettlePaymentError,
|
|
|
} from '../../common/error/generated-graphql-admin-errors';
|
|
|
import {
|
|
|
@@ -489,7 +489,7 @@ export class OrderService {
|
|
|
* @since 2.2.0
|
|
|
*/
|
|
|
async updateOrderCustomer(ctx: RequestContext, { customerId, orderId, note }: SetOrderCustomerInput) {
|
|
|
- const order = await this.getOrderOrThrow(ctx, orderId);
|
|
|
+ const order = await this.getOrderOrThrow(ctx, orderId, ['channels', 'customer']);
|
|
|
const currentCustomer = order.customer;
|
|
|
if (currentCustomer?.id === customerId) {
|
|
|
// No change in customer, so just return the order as-is
|
|
|
@@ -539,6 +539,7 @@ export class OrderService {
|
|
|
productVariantId: ID,
|
|
|
quantity: number,
|
|
|
customFields?: { [key: string]: any },
|
|
|
+ relations?: RelationPaths<Order>,
|
|
|
): Promise<ErrorResultUnion<UpdateOrderItemsResult, Order>> {
|
|
|
const order = await this.getOrderOrThrow(ctx, orderId);
|
|
|
const existingOrderLine = await this.orderModifier.getExistingOrderLine(
|
|
|
@@ -561,6 +562,7 @@ export class OrderService {
|
|
|
enabled: true,
|
|
|
deletedAt: IsNull(),
|
|
|
},
|
|
|
+ loadEagerRelations: false,
|
|
|
});
|
|
|
if (variant.product.enabled === false) {
|
|
|
throw new EntityNotFoundError('ProductVariant', productVariantId);
|
|
|
@@ -596,7 +598,7 @@ export class OrderService {
|
|
|
await this.orderModifier.updateOrderLineQuantity(ctx, orderLine, correctedQuantity, order);
|
|
|
}
|
|
|
const quantityWasAdjustedDown = correctedQuantity < quantity;
|
|
|
- const updatedOrder = await this.applyPriceAdjustments(ctx, order, [orderLine]);
|
|
|
+ const updatedOrder = await this.applyPriceAdjustments(ctx, order, [orderLine], relations);
|
|
|
if (quantityWasAdjustedDown) {
|
|
|
return new InsufficientStockError({ quantityAvailable: correctedQuantity, order: updatedOrder });
|
|
|
} else {
|
|
|
@@ -614,6 +616,7 @@ export class OrderService {
|
|
|
orderLineId: ID,
|
|
|
quantity: number,
|
|
|
customFields?: { [key: string]: any },
|
|
|
+ relations?: RelationPaths<Order>,
|
|
|
): Promise<ErrorResultUnion<UpdateOrderItemsResult, Order>> {
|
|
|
const order = await this.getOrderOrThrow(ctx, orderId);
|
|
|
const orderLine = this.getOrderLineOrThrow(order, orderLineId);
|
|
|
@@ -660,7 +663,7 @@ export class OrderService {
|
|
|
await this.orderModifier.updateOrderLineQuantity(ctx, orderLine, correctedQuantity, order);
|
|
|
}
|
|
|
const quantityWasAdjustedDown = correctedQuantity < quantity;
|
|
|
- const updatedOrder = await this.applyPriceAdjustments(ctx, order, updatedOrderLines);
|
|
|
+ const updatedOrder = await this.applyPriceAdjustments(ctx, order, updatedOrderLines, relations);
|
|
|
if (quantityWasAdjustedDown) {
|
|
|
return new InsufficientStockError({ quantityAvailable: correctedQuantity, order: updatedOrder });
|
|
|
} else {
|
|
|
@@ -1663,8 +1666,23 @@ export class OrderService {
|
|
|
return order;
|
|
|
}
|
|
|
|
|
|
- private async getOrderOrThrow(ctx: RequestContext, orderId: ID): Promise<Order> {
|
|
|
- const order = await this.findOne(ctx, orderId);
|
|
|
+ private async getOrderOrThrow(
|
|
|
+ ctx: RequestContext,
|
|
|
+ orderId: ID,
|
|
|
+ relations?: RelationPaths<Order>,
|
|
|
+ ): Promise<Order> {
|
|
|
+ const order = await this.findOne(
|
|
|
+ ctx,
|
|
|
+ orderId,
|
|
|
+ relations ?? [
|
|
|
+ 'lines',
|
|
|
+ 'lines.productVariant',
|
|
|
+ 'lines.productVariant.productVariantPrices',
|
|
|
+ 'shippingLines',
|
|
|
+ 'surcharges',
|
|
|
+ 'customer',
|
|
|
+ ],
|
|
|
+ );
|
|
|
if (!order) {
|
|
|
throw new EntityNotFoundError('Order', orderId);
|
|
|
}
|
|
|
@@ -1730,6 +1748,7 @@ export class OrderService {
|
|
|
ctx: RequestContext,
|
|
|
order: Order,
|
|
|
updatedOrderLines?: OrderLine[],
|
|
|
+ relations?: RelationPaths<Order>,
|
|
|
): Promise<Order> {
|
|
|
const promotions = await this.promotionService.getActivePromotionsInChannel(ctx);
|
|
|
const activePromotionsPre = await this.promotionService.getActivePromotionsOnOrder(ctx, order.id);
|
|
|
@@ -1776,22 +1795,77 @@ export class OrderService {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ // Get the shipping line IDs before doing the order calculation
|
|
|
+ // step, which can in some cases change the applied shipping lines.
|
|
|
+ const shippingLineIdsPre = order.shippingLines.map(l => l.id);
|
|
|
+
|
|
|
const updatedOrder = await this.orderCalculator.applyPriceAdjustments(
|
|
|
ctx,
|
|
|
order,
|
|
|
promotions,
|
|
|
updatedOrderLines ?? [],
|
|
|
);
|
|
|
+
|
|
|
+ const shippingLineIdsPost = updatedOrder.shippingLines.map(l => l.id);
|
|
|
+ await this.applyChangesToShippingLines(ctx, updatedOrder, shippingLineIdsPre, shippingLineIdsPost);
|
|
|
+
|
|
|
+ // Explicitly omit the shippingAddress and billingAddress properties to avoid
|
|
|
+ // a race condition where changing one or the other in parallel can
|
|
|
+ // overwrite the other's changes. The other omissions prevent the save
|
|
|
+ // function from doing more work than necessary.
|
|
|
await this.connection
|
|
|
.getRepository(ctx, Order)
|
|
|
- // Explicitly omit the shippingAddress and billingAddress properties to avoid
|
|
|
- // a race condition where changing one or the other in parallel can
|
|
|
- // overwrite the other's changes.
|
|
|
- .save(omit(updatedOrder, ['shippingAddress', 'billingAddress']), { reload: false });
|
|
|
+ .save(
|
|
|
+ omit(updatedOrder, [
|
|
|
+ 'shippingAddress',
|
|
|
+ 'billingAddress',
|
|
|
+ 'lines',
|
|
|
+ 'shippingLines',
|
|
|
+ 'aggregateOrder',
|
|
|
+ 'sellerOrders',
|
|
|
+ 'customer',
|
|
|
+ 'modifications',
|
|
|
+ ]),
|
|
|
+ {
|
|
|
+ reload: false,
|
|
|
+ },
|
|
|
+ );
|
|
|
await this.connection.getRepository(ctx, OrderLine).save(updatedOrder.lines, { reload: false });
|
|
|
await this.connection.getRepository(ctx, ShippingLine).save(order.shippingLines, { reload: false });
|
|
|
await this.promotionService.runPromotionSideEffects(ctx, order, activePromotionsPre);
|
|
|
|
|
|
- return assertFound(this.findOne(ctx, order.id));
|
|
|
+ return assertFound(this.findOne(ctx, order.id, relations));
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Applies changes to the shipping lines of an order, adding or removing the relations
|
|
|
+ * in the database.
|
|
|
+ */
|
|
|
+ private async applyChangesToShippingLines(
|
|
|
+ ctx: RequestContext,
|
|
|
+ order: Order,
|
|
|
+ shippingLineIdsPre: ID[],
|
|
|
+ shippingLineIdsPost: ID[],
|
|
|
+ ) {
|
|
|
+ const removedShippingLineIds = shippingLineIdsPre.filter(id => !shippingLineIdsPost.includes(id));
|
|
|
+ const newlyAddedShippingLineIds = shippingLineIdsPost.filter(id => !shippingLineIdsPre.includes(id));
|
|
|
+
|
|
|
+ for (const idToRemove of removedShippingLineIds) {
|
|
|
+ await this.connection
|
|
|
+ .getRepository(ctx, Order)
|
|
|
+ .createQueryBuilder()
|
|
|
+ .relation('shippingLines')
|
|
|
+ .of(order)
|
|
|
+ .remove(idToRemove);
|
|
|
+ }
|
|
|
+
|
|
|
+ for (const idToAdd of newlyAddedShippingLineIds) {
|
|
|
+ await this.connection
|
|
|
+ .getRepository(ctx, Order)
|
|
|
+ .createQueryBuilder()
|
|
|
+ .relation('shippingLines')
|
|
|
+ .of(order)
|
|
|
+ .add(idToAdd);
|
|
|
+ }
|
|
|
}
|
|
|
}
|