diff --git a/packages/core/src/service/services/order.service.ts b/packages/core/src/service/services/order.service.ts index bbcead57ee..9196a536ef 100644 --- a/packages/core/src/service/services/order.service.ts +++ b/packages/core/src/service/services/order.service.ts @@ -581,7 +581,7 @@ export class OrderService { } else { return result.order; } - } + } /** * @description @@ -594,36 +594,53 @@ export class OrderService { * * @since 3.1.0 */ + async addItemsToOrder( - ctx: RequestContext, - orderId: ID, - items: Array<{ - productVariantId: ID; - quantity: number; - customFields?: { [key: string]: any }; - }>, + ctx: RequestContext, + orderId: ID, + items: Array<{ + productVariantId: ID; + quantity: number; + customFields?: Record; + }>, relations?: RelationPaths, ): Promise<{ order: Order; errorResults: Array> }> { - const order = await this.getOrderOrThrow(ctx, orderId); + const order = await this.getOrderOrThrow(ctx, orderId, relations); + + // StateChecking Happens here Early + if (order.state !== 'Modifying') { + const canTransition = await this.orderStateMachine.canTransition(order.state, 'Modify'); + if (canTransition) { + await this.orderStateMachine.transition(ctx, order, 'Modify'); + } else { + throw new IllegalOperationError('Cannot add items unless order is in Modifying state'); + } + } + const errorResults: Array> = []; const updatedOrderLines: OrderLine[] = []; - addItem: for (const item of items) { + + for (const item of items) { const { productVariantId, quantity, customFields } = item; + const existingOrderLine = await this.orderModifier.getExistingOrderLine( ctx, order, productVariantId, customFields, ); + const validationError = this.assertQuantityIsPositive(quantity) || this.assertAddingItemsState(order) || this.assertNotOverOrderItemsLimit(order, quantity) || this.assertNotOverOrderLineItemsLimit(existingOrderLine, quantity); + if (validationError) { errorResults.push(validationError); continue; } + const variant = await this.connection.getEntityOrThrow(ctx, ProductVariant, productVariantId, { relations: ['product'], where: { @@ -632,9 +649,11 @@ export class OrderService { }, loadEagerRelations: false, }); - if (variant.product.enabled === false) { + + if (!variant.product.enabled) { throw new EntityNotFoundError('ProductVariant', productVariantId); } + const existingQuantityInOtherLines = summate( order.lines.filter( l => @@ -643,6 +662,7 @@ export class OrderService { ), 'quantity', ); + const correctedQuantity = await this.orderModifier.constrainQuantityToSaleable( ctx, variant, @@ -650,64 +670,61 @@ export class OrderService { existingOrderLine?.quantity, existingQuantityInOtherLines, ); + if (correctedQuantity === 0) { errorResults.push( - new InsufficientStockError({ order, quantityAvailable: correctedQuantity }), + new InsufficientStockError({ + quantityAvailable: correctedQuantity, + order, + }), ); continue; } - const { orderInterceptors } = this.configService.orderOptions; - for (const interceptor of orderInterceptors) { - if (interceptor.willAddItemToOrder) { - const error = await interceptor.willAddItemToOrder(ctx, order, { - productVariant: variant, - quantity: correctedQuantity, - customFields, - }); - if (error) { - errorResults.push(new OrderInterceptorError({ interceptorError: error })); - continue addItem; - } - } - } + const orderLine = await this.orderModifier.getOrCreateOrderLine( ctx, order, productVariantId, customFields, ); - if (correctedQuantity < quantity) { - const newQuantity = (existingOrderLine ? existingOrderLine?.quantity : 0) + correctedQuantity; - await this.orderModifier.updateOrderLineQuantity(ctx, orderLine, newQuantity, order); - } else { - await this.orderModifier.updateOrderLineQuantity(ctx, orderLine, correctedQuantity, order); + + if (customFields != null) { + const mergedCustomFields = { + ...(orderLine.customFields ?? {}), + ...customFields, + }; + orderLine.customFields = mergedCustomFields; + await this.customFieldRelationService.updateRelations( + ctx, + OrderLine, + { customFields: mergedCustomFields }, + orderLine, + ); } + + const newQuantity = (existingOrderLine?.quantity ?? 0) + correctedQuantity; + await this.orderModifier.updateOrderLineQuantity(ctx, orderLine, newQuantity, order); updatedOrderLines.push(orderLine); - const quantityWasAdjustedDown = correctedQuantity < quantity; - if (quantityWasAdjustedDown) { + + if (correctedQuantity < quantity) { errorResults.push( - new InsufficientStockError({ quantityAvailable: correctedQuantity, order }), + new InsufficientStockError({ + quantityAvailable: correctedQuantity, + order, + }), ); - continue; - } - } - const updatedOrder = await this.applyPriceAdjustments(ctx, order, updatedOrderLines, relations); - // for any InsufficientStockError errors, we want to make sure we use the final updatedOrder - // after having applied all price adjustments - for (const [i, errorResult] of Object.entries(errorResults)) { - if (errorResult.__typename === 'InsufficientStockError') { - errorResults[+i] = new InsufficientStockError({ - quantityAvailable: errorResult.quantityAvailable, - order: updatedOrder, - }); } } + + const updatedOrder = await this.getOrderOrThrow(ctx, orderId, relations); return { order: updatedOrder, errorResults, }; } + + /** * @description * Adjusts the quantity and/or custom field values of an existing OrderLine.