@vendure/core 3.1.0-next.3 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (194) hide show
  1. package/cli/populate.js +3 -1
  2. package/cli/populate.js.map +1 -1
  3. package/dist/api/common/request-context.d.ts +1 -1
  4. package/dist/api/common/request-context.js +1 -1
  5. package/dist/api/common/validate-custom-field-value.js +28 -0
  6. package/dist/api/common/validate-custom-field-value.js.map +1 -1
  7. package/dist/api/config/generate-active-order-types.js +2 -0
  8. package/dist/api/config/generate-active-order-types.js.map +1 -1
  9. package/dist/api/config/generate-resolvers.d.ts +10 -4
  10. package/dist/api/config/generate-resolvers.js +73 -7
  11. package/dist/api/config/generate-resolvers.js.map +1 -1
  12. package/dist/api/config/get-custom-fields-config-without-interfaces.d.ts +2 -0
  13. package/dist/api/config/get-custom-fields-config-without-interfaces.js +16 -11
  14. package/dist/api/config/get-custom-fields-config-without-interfaces.js.map +1 -1
  15. package/dist/api/config/graphql-custom-fields.js +116 -22
  16. package/dist/api/config/graphql-custom-fields.js.map +1 -1
  17. package/dist/api/resolvers/admin/draft-order.resolver.d.ts +3 -1
  18. package/dist/api/resolvers/admin/draft-order.resolver.js +26 -0
  19. package/dist/api/resolvers/admin/draft-order.resolver.js.map +1 -1
  20. package/dist/api/resolvers/admin/global-settings.resolver.d.ts +2 -0
  21. package/dist/api/resolvers/admin/global-settings.resolver.js +12 -0
  22. package/dist/api/resolvers/admin/global-settings.resolver.js.map +1 -1
  23. package/dist/api/resolvers/entity/order-line-entity.resolver.js +7 -3
  24. package/dist/api/resolvers/entity/order-line-entity.resolver.js.map +1 -1
  25. package/dist/api/resolvers/shop/shop-order.resolver.d.ts +2 -0
  26. package/dist/api/resolvers/shop/shop-order.resolver.js +38 -0
  27. package/dist/api/resolvers/shop/shop-order.resolver.js.map +1 -1
  28. package/dist/api/schema/admin-api/order.api.graphql +14 -12
  29. package/dist/api/schema/common/common-error-results.graphql +9 -0
  30. package/dist/api/schema/common/common-types.graphql +2 -1
  31. package/dist/api/schema/common/custom-field-types.graphql +99 -0
  32. package/dist/api/schema/shop-api/shop.api.graphql +30 -13
  33. package/dist/bootstrap.d.ts +56 -3
  34. package/dist/bootstrap.js +36 -6
  35. package/dist/bootstrap.js.map +1 -1
  36. package/dist/cache/cache-ttl-provider.d.ts +38 -0
  37. package/dist/cache/cache-ttl-provider.js +39 -0
  38. package/dist/cache/cache-ttl-provider.js.map +1 -0
  39. package/dist/cache/cache.d.ts +88 -0
  40. package/dist/cache/cache.js +79 -0
  41. package/dist/cache/cache.js.map +1 -0
  42. package/dist/cache/cache.service.d.ts +15 -0
  43. package/dist/cache/cache.service.js +25 -0
  44. package/dist/cache/cache.service.js.map +1 -1
  45. package/dist/cache/index.d.ts +2 -0
  46. package/dist/cache/index.js +2 -0
  47. package/dist/cache/index.js.map +1 -1
  48. package/dist/cache/request-context-cache.service.d.ts +14 -0
  49. package/dist/cache/request-context-cache.service.js +9 -0
  50. package/dist/cache/request-context-cache.service.js.map +1 -1
  51. package/dist/common/error/generated-graphql-admin-errors.d.ts +9 -0
  52. package/dist/common/error/generated-graphql-admin-errors.js +12 -2
  53. package/dist/common/error/generated-graphql-admin-errors.js.map +1 -1
  54. package/dist/common/error/generated-graphql-shop-errors.d.ts +9 -0
  55. package/dist/common/error/generated-graphql-shop-errors.js +12 -2
  56. package/dist/common/error/generated-graphql-shop-errors.js.map +1 -1
  57. package/dist/common/ttl-cache.d.ts +3 -0
  58. package/dist/common/ttl-cache.js +3 -0
  59. package/dist/common/ttl-cache.js.map +1 -1
  60. package/dist/common/types/entity-relation-paths.d.ts +2 -1
  61. package/dist/common/utils.d.ts +1 -1
  62. package/dist/common/utils.js +1 -1
  63. package/dist/common/utils.js.map +1 -1
  64. package/dist/config/catalog/default-stock-location-strategy.d.ts +15 -8
  65. package/dist/config/catalog/default-stock-location-strategy.js +30 -22
  66. package/dist/config/catalog/default-stock-location-strategy.js.map +1 -1
  67. package/dist/config/catalog/multi-channel-stock-location-strategy.d.ts +55 -0
  68. package/dist/config/catalog/multi-channel-stock-location-strategy.js +144 -0
  69. package/dist/config/catalog/multi-channel-stock-location-strategy.js.map +1 -0
  70. package/dist/config/config.module.js +2 -1
  71. package/dist/config/config.module.js.map +1 -1
  72. package/dist/config/custom-field/custom-field-types.d.ts +69 -5
  73. package/dist/config/default-config.js +12 -4
  74. package/dist/config/default-config.js.map +1 -1
  75. package/dist/config/index.d.ts +4 -0
  76. package/dist/config/index.js +4 -0
  77. package/dist/config/index.js.map +1 -1
  78. package/dist/config/order/order-interceptor.d.ts +233 -0
  79. package/dist/config/order/order-interceptor.js +3 -0
  80. package/dist/config/order/order-interceptor.js.map +1 -0
  81. package/dist/config/promotion/conditions/customer-group-condition.js +17 -12
  82. package/dist/config/promotion/conditions/customer-group-condition.js.map +1 -1
  83. package/dist/config/session-cache/default-session-cache-strategy.d.ts +34 -0
  84. package/dist/config/session-cache/default-session-cache-strategy.js +63 -0
  85. package/dist/config/session-cache/default-session-cache-strategy.js.map +1 -0
  86. package/dist/config/session-cache/session-cache-strategy.d.ts +13 -4
  87. package/dist/config/shipping-method/shipping-eligibility-checker.d.ts +4 -2
  88. package/dist/config/shipping-method/shipping-eligibility-checker.js +8 -5
  89. package/dist/config/shipping-method/shipping-eligibility-checker.js.map +1 -1
  90. package/dist/config/system/cache-strategy.d.ts +12 -0
  91. package/dist/config/system/in-memory-cache-strategy.d.ts +8 -0
  92. package/dist/config/system/in-memory-cache-strategy.js +26 -3
  93. package/dist/config/system/in-memory-cache-strategy.js.map +1 -1
  94. package/dist/config/tax/address-based-tax-zone-strategy.d.ts +24 -0
  95. package/dist/config/tax/address-based-tax-zone-strategy.js +39 -0
  96. package/dist/config/tax/address-based-tax-zone-strategy.js.map +1 -0
  97. package/dist/config/vendure-config.d.ts +15 -6
  98. package/dist/data-import/providers/importer/importer.js +8 -0
  99. package/dist/data-import/providers/importer/importer.js.map +1 -1
  100. package/dist/data-import/providers/populator/populator.js +0 -1
  101. package/dist/data-import/providers/populator/populator.js.map +1 -1
  102. package/dist/entity/custom-entity-fields.d.ts +14 -0
  103. package/dist/entity/custom-entity-fields.js +22 -1
  104. package/dist/entity/custom-entity-fields.js.map +1 -1
  105. package/dist/entity/history-entry/history-entry.entity.d.ts +4 -1
  106. package/dist/entity/history-entry/history-entry.entity.js +5 -0
  107. package/dist/entity/history-entry/history-entry.entity.js.map +1 -1
  108. package/dist/entity/payment/payment.entity.d.ts +4 -1
  109. package/dist/entity/payment/payment.entity.js +5 -0
  110. package/dist/entity/payment/payment.entity.js.map +1 -1
  111. package/dist/entity/refund/refund.entity.d.ts +4 -1
  112. package/dist/entity/refund/refund.entity.js +5 -0
  113. package/dist/entity/refund/refund.entity.js.map +1 -1
  114. package/dist/entity/register-custom-entity-fields.js +17 -2
  115. package/dist/entity/register-custom-entity-fields.js.map +1 -1
  116. package/dist/entity/session/session.entity.d.ts +4 -1
  117. package/dist/entity/session/session.entity.js +5 -0
  118. package/dist/entity/session/session.entity.js.map +1 -1
  119. package/dist/entity/shipping-line/shipping-line.entity.d.ts +5 -2
  120. package/dist/entity/shipping-line/shipping-line.entity.js +7 -2
  121. package/dist/entity/shipping-line/shipping-line.entity.js.map +1 -1
  122. package/dist/entity/stock-level/stock-level.entity.d.ts +4 -1
  123. package/dist/entity/stock-level/stock-level.entity.js +5 -0
  124. package/dist/entity/stock-level/stock-level.entity.js.map +1 -1
  125. package/dist/entity/stock-movement/stock-movement.entity.d.ts +4 -1
  126. package/dist/entity/stock-movement/stock-movement.entity.js +5 -0
  127. package/dist/entity/stock-movement/stock-movement.entity.js.map +1 -1
  128. package/dist/event-bus/events/stock-location-event.d.ts +18 -0
  129. package/dist/event-bus/events/stock-location-event.js +19 -0
  130. package/dist/event-bus/events/stock-location-event.js.map +1 -0
  131. package/dist/event-bus/index.d.ts +1 -0
  132. package/dist/event-bus/index.js +1 -0
  133. package/dist/event-bus/index.js.map +1 -1
  134. package/dist/i18n/messages/de.json +1 -0
  135. package/dist/i18n/messages/en.json +2 -0
  136. package/dist/plugin/default-cache-plugin/cache-item.entity.d.ts +1 -0
  137. package/dist/plugin/default-cache-plugin/cache-item.entity.js +4 -0
  138. package/dist/plugin/default-cache-plugin/cache-item.entity.js.map +1 -1
  139. package/dist/plugin/default-cache-plugin/cache-tag.entity.d.ts +9 -0
  140. package/dist/plugin/default-cache-plugin/cache-tag.entity.js +41 -0
  141. package/dist/plugin/default-cache-plugin/cache-tag.entity.js.map +1 -0
  142. package/dist/plugin/default-cache-plugin/default-cache-plugin.d.ts +21 -0
  143. package/dist/plugin/default-cache-plugin/default-cache-plugin.js +13 -1
  144. package/dist/plugin/default-cache-plugin/default-cache-plugin.js.map +1 -1
  145. package/dist/plugin/default-cache-plugin/sql-cache-strategy.d.ts +7 -6
  146. package/dist/plugin/default-cache-plugin/sql-cache-strategy.js +57 -13
  147. package/dist/plugin/default-cache-plugin/sql-cache-strategy.js.map +1 -1
  148. package/dist/plugin/default-search-plugin/default-search-plugin.js +4 -0
  149. package/dist/plugin/default-search-plugin/default-search-plugin.js.map +1 -1
  150. package/dist/plugin/default-search-plugin/indexer/indexer.controller.js +1 -0
  151. package/dist/plugin/default-search-plugin/indexer/indexer.controller.js.map +1 -1
  152. package/dist/plugin/index.d.ts +3 -0
  153. package/dist/plugin/index.js +3 -0
  154. package/dist/plugin/index.js.map +1 -1
  155. package/dist/plugin/redis-cache-plugin/constants.d.ts +4 -0
  156. package/dist/plugin/redis-cache-plugin/constants.js +8 -0
  157. package/dist/plugin/redis-cache-plugin/constants.js.map +1 -0
  158. package/dist/plugin/redis-cache-plugin/redis-cache-plugin.d.ts +14 -0
  159. package/dist/plugin/redis-cache-plugin/redis-cache-plugin.js +47 -0
  160. package/dist/plugin/redis-cache-plugin/redis-cache-plugin.js.map +1 -0
  161. package/dist/plugin/redis-cache-plugin/redis-cache-strategy.d.ts +24 -0
  162. package/dist/plugin/redis-cache-plugin/redis-cache-strategy.js +104 -0
  163. package/dist/plugin/redis-cache-plugin/redis-cache-strategy.js.map +1 -0
  164. package/dist/plugin/redis-cache-plugin/types.d.ts +12 -0
  165. package/dist/plugin/redis-cache-plugin/types.js +3 -0
  166. package/dist/plugin/redis-cache-plugin/types.js.map +1 -0
  167. package/dist/plugin/vendure-plugin.d.ts +5 -0
  168. package/dist/plugin/vendure-plugin.js.map +1 -1
  169. package/dist/service/helpers/external-authentication/external-authentication.service.d.ts +1 -1
  170. package/dist/service/helpers/external-authentication/external-authentication.service.js +1 -1
  171. package/dist/service/helpers/facet-value-checker/facet-value-checker.d.ts +3 -4
  172. package/dist/service/helpers/facet-value-checker/facet-value-checker.js +11 -17
  173. package/dist/service/helpers/facet-value-checker/facet-value-checker.js.map +1 -1
  174. package/dist/service/helpers/order-modifier/order-modifier.js +14 -6
  175. package/dist/service/helpers/order-modifier/order-modifier.js.map +1 -1
  176. package/dist/service/helpers/utils/tree-relations-qb-joiner.js +1 -1
  177. package/dist/service/helpers/utils/tree-relations-qb-joiner.js.map +1 -1
  178. package/dist/service/services/channel.service.js +5 -0
  179. package/dist/service/services/channel.service.js.map +1 -1
  180. package/dist/service/services/order.service.d.ts +71 -1
  181. package/dist/service/services/order.service.js +270 -83
  182. package/dist/service/services/order.service.js.map +1 -1
  183. package/dist/service/services/payment.service.js +6 -4
  184. package/dist/service/services/payment.service.js.map +1 -1
  185. package/dist/service/services/product-variant.service.js +1 -1
  186. package/dist/service/services/product-variant.service.js.map +1 -1
  187. package/dist/service/services/promotion.service.js +1 -0
  188. package/dist/service/services/promotion.service.js.map +1 -1
  189. package/dist/service/services/role.service.js +6 -5
  190. package/dist/service/services/role.service.js.map +1 -1
  191. package/dist/service/services/stock-location.service.d.ts +3 -1
  192. package/dist/service/services/stock-location.service.js +9 -2
  193. package/dist/service/services/stock-location.service.js.map +1 -1
  194. package/package.json +22 -22
@@ -25,6 +25,7 @@ const generated_graphql_shop_errors_1 = require("../../common/error/generated-gr
25
25
  const tax_utils_1 = require("../../common/tax-utils");
26
26
  const utils_1 = require("../../common/utils");
27
27
  const config_service_1 = require("../../config/config.service");
28
+ const vendure_logger_1 = require("../../config/logger/vendure-logger");
28
29
  const transactional_connection_1 = require("../../connection/transactional-connection");
29
30
  const order_entity_1 = require("../../entity/order/order.entity");
30
31
  const order_line_entity_1 = require("../../entity/order-line/order-line.entity");
@@ -407,113 +408,249 @@ let OrderService = class OrderService {
407
408
  * @description
408
409
  * Adds an item to the Order, either creating a new OrderLine or
409
410
  * incrementing an existing one.
411
+ *
412
+ * If you need to add multiple items to an Order, use `addItemsToOrder()` instead.
410
413
  */
411
414
  async addItemToOrder(ctx, orderId, productVariantId, quantity, customFields, relations) {
412
- const order = await this.getOrderOrThrow(ctx, orderId);
413
- const existingOrderLine = await this.orderModifier.getExistingOrderLine(ctx, order, productVariantId, customFields);
414
- const validationError = this.assertQuantityIsPositive(quantity) ||
415
- this.assertAddingItemsState(order) ||
416
- this.assertNotOverOrderItemsLimit(order, quantity) ||
417
- this.assertNotOverOrderLineItemsLimit(existingOrderLine, quantity);
418
- if (validationError) {
419
- return validationError;
420
- }
421
- const variant = await this.connection.getEntityOrThrow(ctx, product_variant_entity_1.ProductVariant, productVariantId, {
422
- relations: ['product'],
423
- where: {
424
- enabled: true,
425
- deletedAt: (0, typeorm_1.IsNull)(),
426
- },
427
- loadEagerRelations: false,
428
- });
429
- if (variant.product.enabled === false) {
430
- throw new errors_1.EntityNotFoundError('ProductVariant', productVariantId);
431
- }
432
- const existingQuantityInOtherLines = (0, shared_utils_1.summate)(order.lines.filter(l => (0, utils_1.idsAreEqual)(l.productVariantId, productVariantId) &&
433
- !(0, utils_1.idsAreEqual)(l.id, existingOrderLine === null || existingOrderLine === void 0 ? void 0 : existingOrderLine.id)), 'quantity');
434
- const correctedQuantity = await this.orderModifier.constrainQuantityToSaleable(ctx, variant, quantity, existingOrderLine === null || existingOrderLine === void 0 ? void 0 : existingOrderLine.quantity, existingQuantityInOtherLines);
435
- if (correctedQuantity === 0) {
436
- return new generated_graphql_shop_errors_1.InsufficientStockError({ order, quantityAvailable: correctedQuantity });
437
- }
438
- const orderLine = await this.orderModifier.getOrCreateOrderLine(ctx, order, productVariantId, customFields);
439
- if (correctedQuantity < quantity) {
440
- const newQuantity = (existingOrderLine ? existingOrderLine === null || existingOrderLine === void 0 ? void 0 : existingOrderLine.quantity : 0) + correctedQuantity;
441
- await this.orderModifier.updateOrderLineQuantity(ctx, orderLine, newQuantity, order);
415
+ const result = await this.addItemsToOrder(ctx, orderId, [{ productVariantId, quantity, customFields }], relations);
416
+ if (result.errorResults.length) {
417
+ return result.errorResults[0];
442
418
  }
443
419
  else {
444
- await this.orderModifier.updateOrderLineQuantity(ctx, orderLine, correctedQuantity, order);
420
+ return result.order;
445
421
  }
446
- const quantityWasAdjustedDown = correctedQuantity < quantity;
447
- const updatedOrder = await this.applyPriceAdjustments(ctx, order, [orderLine], relations);
448
- if (quantityWasAdjustedDown) {
449
- return new generated_graphql_shop_errors_1.InsufficientStockError({ quantityAvailable: correctedQuantity, order: updatedOrder });
422
+ }
423
+ /**
424
+ * @description
425
+ * Adds multiple items to an Order. This method is more efficient than calling `addItemToOrder`
426
+ * multiple times, as it only needs to fetch the entire Order once, and only performs
427
+ * price adjustments once at the end.
428
+ *
429
+ * Since this method can return multiple error results, it is recommended to check the `errorResults`
430
+ * array to determine if any errors occurred.
431
+ *
432
+ * @since 3.1.0
433
+ */
434
+ async addItemsToOrder(ctx, orderId, items, relations) {
435
+ const order = await this.getOrderOrThrow(ctx, orderId);
436
+ const errorResults = [];
437
+ const updatedOrderLines = [];
438
+ addItem: for (const item of items) {
439
+ const { productVariantId, quantity, customFields } = item;
440
+ const existingOrderLine = await this.orderModifier.getExistingOrderLine(ctx, order, productVariantId, customFields);
441
+ const validationError = this.assertQuantityIsPositive(quantity) ||
442
+ this.assertAddingItemsState(order) ||
443
+ this.assertNotOverOrderItemsLimit(order, quantity) ||
444
+ this.assertNotOverOrderLineItemsLimit(existingOrderLine, quantity);
445
+ if (validationError) {
446
+ errorResults.push(validationError);
447
+ continue;
448
+ }
449
+ const variant = await this.connection.getEntityOrThrow(ctx, product_variant_entity_1.ProductVariant, productVariantId, {
450
+ relations: ['product'],
451
+ where: {
452
+ enabled: true,
453
+ deletedAt: (0, typeorm_1.IsNull)(),
454
+ },
455
+ loadEagerRelations: false,
456
+ });
457
+ if (variant.product.enabled === false) {
458
+ throw new errors_1.EntityNotFoundError('ProductVariant', productVariantId);
459
+ }
460
+ const existingQuantityInOtherLines = (0, shared_utils_1.summate)(order.lines.filter(l => (0, utils_1.idsAreEqual)(l.productVariantId, productVariantId) &&
461
+ !(0, utils_1.idsAreEqual)(l.id, existingOrderLine === null || existingOrderLine === void 0 ? void 0 : existingOrderLine.id)), 'quantity');
462
+ const correctedQuantity = await this.orderModifier.constrainQuantityToSaleable(ctx, variant, quantity, existingOrderLine === null || existingOrderLine === void 0 ? void 0 : existingOrderLine.quantity, existingQuantityInOtherLines);
463
+ if (correctedQuantity === 0) {
464
+ errorResults.push(new generated_graphql_shop_errors_1.InsufficientStockError({ order, quantityAvailable: correctedQuantity }));
465
+ continue;
466
+ }
467
+ const { orderInterceptors } = this.configService.orderOptions;
468
+ for (const interceptor of orderInterceptors) {
469
+ if (interceptor.willAddItemToOrder) {
470
+ const error = await interceptor.willAddItemToOrder(ctx, order, {
471
+ productVariant: variant,
472
+ quantity: correctedQuantity,
473
+ customFields,
474
+ });
475
+ if (error) {
476
+ errorResults.push(new generated_graphql_shop_errors_1.OrderInterceptorError({ interceptorError: error }));
477
+ continue addItem;
478
+ }
479
+ }
480
+ }
481
+ const orderLine = await this.orderModifier.getOrCreateOrderLine(ctx, order, productVariantId, customFields);
482
+ if (correctedQuantity < quantity) {
483
+ const newQuantity = (existingOrderLine ? existingOrderLine === null || existingOrderLine === void 0 ? void 0 : existingOrderLine.quantity : 0) + correctedQuantity;
484
+ await this.orderModifier.updateOrderLineQuantity(ctx, orderLine, newQuantity, order);
485
+ }
486
+ else {
487
+ await this.orderModifier.updateOrderLineQuantity(ctx, orderLine, correctedQuantity, order);
488
+ }
489
+ updatedOrderLines.push(orderLine);
490
+ const quantityWasAdjustedDown = correctedQuantity < quantity;
491
+ if (quantityWasAdjustedDown) {
492
+ errorResults.push(new generated_graphql_shop_errors_1.InsufficientStockError({ quantityAvailable: correctedQuantity, order }));
493
+ continue;
494
+ }
450
495
  }
451
- else {
452
- return updatedOrder;
496
+ const updatedOrder = await this.applyPriceAdjustments(ctx, order, updatedOrderLines, relations);
497
+ // for any InsufficientStockError errors, we want to make sure we use the final updatedOrder
498
+ // after having applied all price adjustments
499
+ for (const [i, errorResult] of Object.entries(errorResults)) {
500
+ if (errorResult.__typename === 'InsufficientStockError') {
501
+ errorResults[+i] = new generated_graphql_shop_errors_1.InsufficientStockError({
502
+ quantityAvailable: errorResult.quantityAvailable,
503
+ order: updatedOrder,
504
+ });
505
+ }
453
506
  }
507
+ return {
508
+ order: updatedOrder,
509
+ errorResults,
510
+ };
454
511
  }
455
512
  /**
456
513
  * @description
457
514
  * Adjusts the quantity and/or custom field values of an existing OrderLine.
515
+ *
516
+ * If you need to adjust multiple OrderLines, use `adjustOrderLines()` instead.
458
517
  */
459
518
  async adjustOrderLine(ctx, orderId, orderLineId, quantity, customFields, relations) {
460
- const order = await this.getOrderOrThrow(ctx, orderId);
461
- const orderLine = this.getOrderLineOrThrow(order, orderLineId);
462
- const validationError = this.assertAddingItemsState(order) ||
463
- this.assertQuantityIsPositive(quantity) ||
464
- this.assertNotOverOrderItemsLimit(order, quantity - orderLine.quantity) ||
465
- this.assertNotOverOrderLineItemsLimit(orderLine, quantity - orderLine.quantity);
466
- if (validationError) {
467
- return validationError;
468
- }
469
- if (customFields != null) {
470
- orderLine.customFields = customFields;
471
- await this.customFieldRelationService.updateRelations(ctx, order_line_entity_1.OrderLine, { customFields }, orderLine);
472
- }
473
- const existingQuantityInOtherLines = (0, shared_utils_1.summate)(order.lines.filter(l => (0, utils_1.idsAreEqual)(l.productVariantId, orderLine.productVariantId) &&
474
- !(0, utils_1.idsAreEqual)(l.id, orderLineId)), 'quantity');
475
- const correctedQuantity = await this.orderModifier.constrainQuantityToSaleable(ctx, orderLine.productVariant, quantity, 0, existingQuantityInOtherLines);
476
- let updatedOrderLines = [orderLine];
477
- if (correctedQuantity === 0) {
478
- order.lines = order.lines.filter(l => !(0, utils_1.idsAreEqual)(l.id, orderLine.id));
479
- const deletedOrderLine = new order_line_entity_1.OrderLine(orderLine);
480
- await this.connection.getRepository(ctx, order_line_entity_1.OrderLine).remove(orderLine);
481
- await this.eventBus.publish(new order_line_event_1.OrderLineEvent(ctx, order, deletedOrderLine, 'deleted'));
482
- updatedOrderLines = [];
519
+ const result = await this.adjustOrderLines(ctx, orderId, [{ orderLineId, quantity, customFields }], relations);
520
+ if (result.errorResults.length) {
521
+ return result.errorResults[0];
483
522
  }
484
523
  else {
485
- await this.orderModifier.updateOrderLineQuantity(ctx, orderLine, correctedQuantity, order);
524
+ return result.order;
486
525
  }
487
- const quantityWasAdjustedDown = correctedQuantity < quantity;
488
- const updatedOrder = await this.applyPriceAdjustments(ctx, order, updatedOrderLines, relations);
489
- if (quantityWasAdjustedDown) {
490
- return new generated_graphql_shop_errors_1.InsufficientStockError({ quantityAvailable: correctedQuantity, order: updatedOrder });
526
+ }
527
+ /**
528
+ * @description
529
+ * Adjusts the quantity and/or custom field values of existing OrderLines.
530
+ * This method is more efficient than calling `adjustOrderLine` multiple times, as it only needs to fetch
531
+ * the entire Order once, and only performs price adjustments once at the end.
532
+ * Since this method can return multiple error results, it is recommended to check the `errorResults`
533
+ * array to determine if any errors occurred.
534
+ *
535
+ * @since 3.1.0
536
+ */
537
+ async adjustOrderLines(ctx, orderId, lines, relations) {
538
+ const order = await this.getOrderOrThrow(ctx, orderId);
539
+ const errorResults = [];
540
+ const updatedOrderLines = [];
541
+ adjustLine: for (const line of lines) {
542
+ const { orderLineId, quantity, customFields } = line;
543
+ const orderLine = this.getOrderLineOrThrow(order, orderLineId);
544
+ const validationError = this.assertAddingItemsState(order) ||
545
+ this.assertQuantityIsPositive(quantity) ||
546
+ this.assertNotOverOrderItemsLimit(order, quantity - orderLine.quantity) ||
547
+ this.assertNotOverOrderLineItemsLimit(orderLine, quantity - orderLine.quantity);
548
+ if (validationError) {
549
+ errorResults.push(validationError);
550
+ continue;
551
+ }
552
+ const { orderInterceptors } = this.configService.orderOptions;
553
+ for (const interceptor of orderInterceptors) {
554
+ if (interceptor.willAdjustOrderLine) {
555
+ const error = await interceptor.willAdjustOrderLine(ctx, order, {
556
+ orderLine,
557
+ quantity,
558
+ customFields,
559
+ });
560
+ if (error) {
561
+ errorResults.push(new generated_graphql_shop_errors_1.OrderInterceptorError({ interceptorError: error }));
562
+ continue adjustLine;
563
+ }
564
+ }
565
+ }
566
+ if (customFields != null) {
567
+ orderLine.customFields = customFields;
568
+ await this.customFieldRelationService.updateRelations(ctx, order_line_entity_1.OrderLine, { customFields }, orderLine);
569
+ }
570
+ const existingQuantityInOtherLines = (0, shared_utils_1.summate)(order.lines.filter(l => (0, utils_1.idsAreEqual)(l.productVariantId, orderLine.productVariantId) &&
571
+ !(0, utils_1.idsAreEqual)(l.id, orderLineId)), 'quantity');
572
+ const correctedQuantity = await this.orderModifier.constrainQuantityToSaleable(ctx, orderLine.productVariant, quantity, 0, existingQuantityInOtherLines);
573
+ if (correctedQuantity === 0) {
574
+ order.lines = order.lines.filter(l => !(0, utils_1.idsAreEqual)(l.id, orderLine.id));
575
+ const deletedOrderLine = new order_line_entity_1.OrderLine(orderLine);
576
+ await this.connection.getRepository(ctx, order_line_entity_1.OrderLine).remove(orderLine);
577
+ await this.eventBus.publish(new order_line_event_1.OrderLineEvent(ctx, order, deletedOrderLine, 'deleted'));
578
+ }
579
+ else {
580
+ await this.orderModifier.updateOrderLineQuantity(ctx, orderLine, correctedQuantity, order);
581
+ updatedOrderLines.push(orderLine);
582
+ }
583
+ const quantityWasAdjustedDown = correctedQuantity < quantity;
584
+ if (quantityWasAdjustedDown) {
585
+ errorResults.push(new generated_graphql_shop_errors_1.InsufficientStockError({
586
+ quantityAvailable: correctedQuantity,
587
+ order,
588
+ }));
589
+ }
491
590
  }
492
- else {
493
- return updatedOrder;
591
+ const updatedOrder = await this.applyPriceAdjustments(ctx, order, updatedOrderLines, relations);
592
+ for (const [i, errorResult] of Object.entries(errorResults)) {
593
+ if (errorResult.__typename === 'InsufficientStockError') {
594
+ errorResults[+i] = new generated_graphql_shop_errors_1.InsufficientStockError({
595
+ quantityAvailable: errorResult.quantityAvailable,
596
+ order: updatedOrder,
597
+ });
598
+ }
494
599
  }
600
+ return {
601
+ order: updatedOrder,
602
+ errorResults,
603
+ };
495
604
  }
496
605
  /**
497
606
  * @description
498
607
  * Removes the specified OrderLine from the Order.
608
+ *
609
+ * If you need to remove multiple OrderLines, use `removeItemsFromOrder()` instead.
499
610
  */
500
611
  async removeItemFromOrder(ctx, orderId, orderLineId) {
612
+ return this.removeItemsFromOrder(ctx, orderId, [orderLineId]);
613
+ }
614
+ /**
615
+ * @description
616
+ * Removes the specified OrderLines from the Order.
617
+ * This method is more efficient than calling `removeItemFromOrder` multiple times, as it only needs to fetch
618
+ * the entire Order once, and only performs price adjustments once at the end.
619
+ *
620
+ * @since 3.1.0
621
+ */
622
+ async removeItemsFromOrder(ctx, orderId, orderLineIds) {
501
623
  const order = await this.getOrderOrThrow(ctx, orderId);
502
624
  const validationError = this.assertAddingItemsState(order);
503
625
  if (validationError) {
504
626
  return validationError;
505
627
  }
506
- const orderLine = this.getOrderLineOrThrow(order, orderLineId);
507
- order.lines = order.lines.filter(line => !(0, utils_1.idsAreEqual)(line.id, orderLineId));
628
+ const orderLinesToDelete = [];
629
+ for (const orderLineId of orderLineIds) {
630
+ const orderLine = this.getOrderLineOrThrow(order, orderLineId);
631
+ const { orderInterceptors } = this.configService.orderOptions;
632
+ for (const interceptor of orderInterceptors) {
633
+ if (interceptor.willRemoveItemFromOrder) {
634
+ const error = await interceptor.willRemoveItemFromOrder(ctx, order, orderLine);
635
+ if (error) {
636
+ return new generated_graphql_shop_errors_1.OrderInterceptorError({ interceptorError: error });
637
+ }
638
+ }
639
+ }
640
+ orderLinesToDelete.push(orderLine);
641
+ }
642
+ order.lines = order.lines.filter(line => !orderLineIds.find(olId => (0, utils_1.idsAreEqual)(line.id, olId)));
508
643
  // Persist the orderLine removal before applying price adjustments
509
644
  // so that any hydration of the Order entity during the course of the
510
645
  // `applyPriceAdjustments()` (e.g. in a ShippingEligibilityChecker etc)
511
646
  // will not re-add the OrderLine.
512
647
  await this.connection.getRepository(ctx, order_entity_1.Order).save(order, { reload: false });
513
648
  const updatedOrder = await this.applyPriceAdjustments(ctx, order);
514
- const deletedOrderLine = new order_line_entity_1.OrderLine(orderLine);
515
- await this.connection.getRepository(ctx, order_line_entity_1.OrderLine).remove(orderLine);
516
- await this.eventBus.publish(new order_line_event_1.OrderLineEvent(ctx, order, deletedOrderLine, 'deleted'));
649
+ for (const orderLine of orderLinesToDelete) {
650
+ const deletedOrderLine = new order_line_entity_1.OrderLine(orderLine);
651
+ await this.connection.getRepository(ctx, order_line_entity_1.OrderLine).remove(orderLine);
652
+ await this.eventBus.publish(new order_line_event_1.OrderLineEvent(ctx, order, deletedOrderLine, 'deleted'));
653
+ }
517
654
  return updatedOrder;
518
655
  }
519
656
  /**
@@ -675,6 +812,52 @@ let OrderService = class OrderService {
675
812
  this.requestCache.set(ctx, constants_1.CacheKey.ActiveTaxZone_PPA, undefined);
676
813
  return this.applyPriceAdjustments(ctx, order, order.lines);
677
814
  }
815
+ /**
816
+ * @description
817
+ * Unsets the shipping address for the Order.
818
+ *
819
+ * @since 3.1.0
820
+ */
821
+ async unsetShippingAddress(ctx, orderId) {
822
+ const order = await this.getOrderOrThrow(ctx, orderId);
823
+ await this.connection
824
+ .getRepository(ctx, order_entity_1.Order)
825
+ .createQueryBuilder('order')
826
+ .update(order_entity_1.Order)
827
+ .set({ shippingAddress: {} })
828
+ .where('id = :id', { id: order.id })
829
+ .execute();
830
+ order.shippingAddress = {};
831
+ // Since a changed ShippingAddress could alter the activeTaxZone,
832
+ // we will remove any cached activeTaxZone, so it can be re-calculated
833
+ // as needed.
834
+ this.requestCache.set(ctx, constants_1.CacheKey.ActiveTaxZone, undefined);
835
+ this.requestCache.set(ctx, constants_1.CacheKey.ActiveTaxZone_PPA, undefined);
836
+ return this.applyPriceAdjustments(ctx, order, order.lines);
837
+ }
838
+ /**
839
+ * @description
840
+ * Unsets the billing address for the Order.
841
+ *
842
+ * @since 3.1.0
843
+ */
844
+ async unsetBillingAddress(ctx, orderId) {
845
+ const order = await this.getOrderOrThrow(ctx, orderId);
846
+ await this.connection
847
+ .getRepository(ctx, order_entity_1.Order)
848
+ .createQueryBuilder('order')
849
+ .update(order_entity_1.Order)
850
+ .set({ billingAddress: {} })
851
+ .where('id = :id', { id: order.id })
852
+ .execute();
853
+ order.billingAddress = {};
854
+ // Since a changed BillingAddress could alter the activeTaxZone,
855
+ // we will remove any cached activeTaxZone, so it can be re-calculated
856
+ // as needed.
857
+ this.requestCache.set(ctx, constants_1.CacheKey.ActiveTaxZone, undefined);
858
+ this.requestCache.set(ctx, constants_1.CacheKey.ActiveTaxZone_PPA, undefined);
859
+ return this.applyPriceAdjustments(ctx, order, order.lines);
860
+ }
678
861
  /**
679
862
  * @description
680
863
  * Returns an array of quotes stating which {@link ShippingMethod}s may be applied to this Order.
@@ -1246,32 +1429,36 @@ let OrderService = class OrderService {
1246
1429
  if (orderToDelete) {
1247
1430
  await this.deleteOrder(ctx, orderToDelete);
1248
1431
  }
1249
- if (order && linesToInsert) {
1432
+ if (order && linesToDelete) {
1250
1433
  const orderId = order.id;
1251
- for (const line of linesToInsert) {
1252
- const result = await this.addItemToOrder(ctx, orderId, line.productVariantId, line.quantity, line.customFields);
1434
+ for (const line of linesToDelete) {
1435
+ const result = await this.removeItemFromOrder(ctx, orderId, line.orderLineId);
1253
1436
  if (!(0, error_result_1.isGraphQlErrorResult)(result)) {
1254
1437
  order = result;
1255
1438
  }
1256
1439
  }
1257
1440
  }
1441
+ if (order && linesToInsert) {
1442
+ const orderId = order.id;
1443
+ const result = await this.addItemsToOrder(ctx, orderId, linesToInsert);
1444
+ order = result.order;
1445
+ }
1258
1446
  if (order && linesToModify) {
1259
1447
  const orderId = order.id;
1260
- for (const line of linesToModify) {
1261
- const result = await this.adjustOrderLine(ctx, orderId, line.orderLineId, line.quantity, line.customFields);
1262
- if (!(0, error_result_1.isGraphQlErrorResult)(result)) {
1263
- order = result;
1264
- }
1265
- }
1448
+ const result = await this.adjustOrderLines(ctx, orderId, linesToModify);
1449
+ order = result.order;
1266
1450
  }
1267
1451
  if (order && linesToDelete) {
1268
1452
  const orderId = order.id;
1269
- for (const line of linesToDelete) {
1270
- const result = await this.removeItemFromOrder(ctx, orderId, line.orderLineId);
1453
+ try {
1454
+ const result = await this.removeItemsFromOrder(ctx, orderId, linesToDelete.map(l => l.orderLineId));
1271
1455
  if (!(0, error_result_1.isGraphQlErrorResult)(result)) {
1272
1456
  order = result;
1273
1457
  }
1274
1458
  }
1459
+ catch (e) {
1460
+ vendure_logger_1.Logger.error(e.message, undefined, e.stack);
1461
+ }
1275
1462
  }
1276
1463
  const customer = await this.customerService.findOneByUserId(ctx, user.id);
1277
1464
  if (order && customer) {