product-bundle.service.ts 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. import { Injectable } from '@nestjs/common';
  2. import { UpdateOrderItemsResult } from '@vendure/common/lib/generated-shop-types';
  3. import { DeletionResponse, DeletionResult } from '@vendure/common/lib/generated-types';
  4. import { ID, PaginatedList } from '@vendure/common/lib/shared-types';
  5. import {
  6. assertFound,
  7. EntityHydrator,
  8. ErrorResultUnion,
  9. ListQueryBuilder,
  10. ListQueryOptions,
  11. Order,
  12. OrderService,
  13. patchEntity,
  14. RelationPaths,
  15. RequestContext,
  16. TransactionalConnection,
  17. } from '@vendure/core';
  18. import { ProductBundle } from '../entities/product-bundle.entity';
  19. import { CreateProductBundleInput, UpdateProductBundleInput } from '../types';
  20. @Injectable()
  21. export class ProductBundleService {
  22. constructor(
  23. private connection: TransactionalConnection,
  24. private listQueryBuilder: ListQueryBuilder,
  25. private orderService: OrderService,
  26. private entityHydrator: EntityHydrator,
  27. ) {}
  28. findAll(
  29. ctx: RequestContext,
  30. options?: ListQueryOptions<ProductBundle>,
  31. relations?: RelationPaths<ProductBundle>,
  32. ): Promise<PaginatedList<ProductBundle>> {
  33. return this.listQueryBuilder
  34. .build(ProductBundle, options, {
  35. relations,
  36. ctx,
  37. })
  38. .getManyAndCount()
  39. .then(([items, totalItems]) => {
  40. return {
  41. items,
  42. totalItems,
  43. };
  44. });
  45. }
  46. findOne(
  47. ctx: RequestContext,
  48. id: ID,
  49. relations?: RelationPaths<ProductBundle>,
  50. ): Promise<ProductBundle | null> {
  51. return this.connection.getRepository(ctx, ProductBundle).findOne({
  52. where: { id },
  53. relations,
  54. });
  55. }
  56. async create(ctx: RequestContext, input: CreateProductBundleInput): Promise<ProductBundle> {
  57. const newEntity = await this.connection.getRepository(ctx, ProductBundle).save(input);
  58. return assertFound(this.findOne(ctx, newEntity.id));
  59. }
  60. async update(ctx: RequestContext, input: UpdateProductBundleInput): Promise<ProductBundle> {
  61. const entity = await this.connection.getEntityOrThrow(ctx, ProductBundle, input.id);
  62. const updatedEntity = patchEntity(entity, input);
  63. await this.connection.getRepository(ctx, ProductBundle).save(updatedEntity, { reload: false });
  64. return assertFound(this.findOne(ctx, updatedEntity.id));
  65. }
  66. async delete(ctx: RequestContext, id: ID): Promise<DeletionResponse> {
  67. const entity = await this.connection.getEntityOrThrow(ctx, ProductBundle, id);
  68. try {
  69. await this.connection.getRepository(ctx, ProductBundle).remove(entity);
  70. return {
  71. result: DeletionResult.DELETED,
  72. };
  73. } catch (e: any) {
  74. return {
  75. result: DeletionResult.NOT_DELETED,
  76. message: e.toString(),
  77. };
  78. }
  79. }
  80. async addProductBundleToOrder(
  81. ctx: RequestContext,
  82. order: Order,
  83. bundleId: ID,
  84. ): Promise<ErrorResultUnion<UpdateOrderItemsResult, Order>> {
  85. const bundle = await this.connection.getEntityOrThrow(ctx, ProductBundle, bundleId, {
  86. relations: {
  87. items: true,
  88. },
  89. });
  90. const result = await this.orderService.addItemsToOrder(
  91. ctx,
  92. order.id,
  93. bundle.items.map(item => ({
  94. productVariantId: item.productVariant.id,
  95. quantity: item.quantity,
  96. customFields: {
  97. fromBundle: {
  98. bundleId: bundleId.toString(),
  99. bundleName: bundle.name,
  100. },
  101. },
  102. })),
  103. );
  104. if (result.errorResults.length) {
  105. await this.connection.rollBackTransaction(ctx);
  106. return result.errorResults[0];
  107. }
  108. return result.order;
  109. }
  110. async removeProductBundleFromOrder(
  111. ctx: RequestContext,
  112. order: Order,
  113. bundleId: ID,
  114. ): Promise<ErrorResultUnion<UpdateOrderItemsResult, Order>> {
  115. await this.entityHydrator.hydrate(ctx, order, {
  116. relations: ['lines'],
  117. });
  118. const bundle = await this.connection.getEntityOrThrow(ctx, ProductBundle, bundleId, {
  119. relations: {
  120. items: true,
  121. },
  122. });
  123. const adjustment: Array<{ orderLineId: ID; quantity: number }> = [];
  124. for (const line of order.lines) {
  125. if (line.customFields?.fromBundle?.bundleId === bundleId.toString()) {
  126. const bundleItem = bundle.items.find(
  127. item => item.productVariant.id === line.productVariant.id,
  128. );
  129. if (bundleItem) {
  130. adjustment.push({
  131. orderLineId: line.id,
  132. quantity: -bundleItem.quantity,
  133. });
  134. }
  135. }
  136. }
  137. const result = await this.orderService.adjustOrderLines(ctx, order.id, adjustment);
  138. if (result.errorResults.length) {
  139. await this.connection.rollBackTransaction(ctx);
  140. return result.errorResults[0];
  141. }
  142. return result.order;
  143. }
  144. }