order.resolver.ts 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250
  1. import { Args, Mutation, Parent, Query, ResolveProperty, Resolver } from '@nestjs/graphql';
  2. import {
  3. AddItemToOrderMutationArgs,
  4. AddPaymentToOrderMutationArgs,
  5. AdjustItemQuantityMutationArgs,
  6. OrderByCodeQueryArgs,
  7. OrderQueryArgs,
  8. OrdersQueryArgs,
  9. Permission,
  10. RemoveItemFromOrderMutationArgs,
  11. SetOrderShippingAddressMutationArgs,
  12. SetOrderShippingMethodMutationArgs,
  13. ShippingMethodQuote,
  14. TransitionOrderToStateMutationArgs,
  15. } from 'shared/generated-types';
  16. import { PaginatedList } from 'shared/shared-types';
  17. import { Order } from '../../entity/order/order.entity';
  18. import { I18nError } from '../../i18n/i18n-error';
  19. import { OrderState } from '../../service/helpers/order-state-machine/order-state';
  20. import { AuthService } from '../../service/services/auth.service';
  21. import { OrderService } from '../../service/services/order.service';
  22. import { ShippingMethodService } from '../../service/services/shipping-method.service';
  23. import { IdCodecService } from '../common/id-codec.service';
  24. import { RequestContext } from '../common/request-context';
  25. import { Allow } from '../decorators/allow.decorator';
  26. import { Decode } from '../decorators/decode.decorator';
  27. import { Ctx } from '../decorators/request-context.decorator';
  28. @Resolver('Order')
  29. export class OrderResolver {
  30. constructor(
  31. private orderService: OrderService,
  32. private shippingMethodService: ShippingMethodService,
  33. private authService: AuthService,
  34. private idCodecService: IdCodecService,
  35. ) {}
  36. @Query()
  37. @Allow(Permission.ReadOrder)
  38. orders(@Ctx() ctx: RequestContext, @Args() args: OrdersQueryArgs): Promise<PaginatedList<Order>> {
  39. return this.orderService.findAll(ctx, args.options || undefined);
  40. }
  41. @Query()
  42. @Allow(Permission.ReadOrder, Permission.Owner)
  43. async order(@Ctx() ctx: RequestContext, @Args() args: OrderQueryArgs): Promise<Order | undefined> {
  44. const order = await this.orderService.findOne(ctx, args.id);
  45. if (order && ctx.authorizedAsOwnerOnly) {
  46. if (ctx.session && ctx.session.activeOrder && ctx.session.activeOrder.id === order.id) {
  47. return order;
  48. } else {
  49. return;
  50. }
  51. }
  52. return order;
  53. }
  54. @ResolveProperty()
  55. async payments(@Parent() order: Order) {
  56. const orderId = this.idCodecService.decode(order.id);
  57. return this.orderService.getOrderPayments(orderId);
  58. }
  59. @ResolveProperty()
  60. async shippingMethod(@Parent() order: Order) {
  61. if (order.shippingMethodId) {
  62. // Does not need to be decoded because it is an internal property
  63. // which is never exposed to the outside world.
  64. const shippingMethodId = order.shippingMethodId;
  65. return this.shippingMethodService.findOne(shippingMethodId);
  66. } else {
  67. return null;
  68. }
  69. }
  70. @Query()
  71. @Allow(Permission.Owner)
  72. async activeOrder(@Ctx() ctx: RequestContext): Promise<Order | undefined> {
  73. if (ctx.authorizedAsOwnerOnly) {
  74. const sessionOrder = await this.getOrderFromContext(ctx);
  75. if (sessionOrder) {
  76. return this.orderService.findOne(ctx, sessionOrder.id);
  77. } else {
  78. return;
  79. }
  80. }
  81. }
  82. @Query()
  83. @Allow(Permission.Owner)
  84. async orderByCode(
  85. @Ctx() ctx: RequestContext,
  86. @Args() args: OrderByCodeQueryArgs,
  87. ): Promise<Order | undefined> {
  88. if (ctx.authorizedAsOwnerOnly) {
  89. const order = await this.orderService.findOneByCode(ctx, args.code);
  90. if (order && order.customer.user && order.customer.user.id === ctx.activeUserId) {
  91. return this.orderService.findOne(ctx, order.id);
  92. } else {
  93. throw new I18nError(`error.forbidden`);
  94. }
  95. }
  96. }
  97. @Mutation()
  98. @Allow(Permission.Owner)
  99. async setOrderShippingAddress(
  100. @Ctx() ctx: RequestContext,
  101. @Args() args: SetOrderShippingAddressMutationArgs,
  102. ): Promise<Order | undefined> {
  103. if (ctx.authorizedAsOwnerOnly) {
  104. const sessionOrder = await this.getOrderFromContext(ctx);
  105. if (sessionOrder) {
  106. return this.orderService.setShippingAddress(ctx, sessionOrder.id, args.input);
  107. } else {
  108. return;
  109. }
  110. }
  111. }
  112. @Query()
  113. @Allow(Permission.Owner)
  114. async eligibleShippingMethods(@Ctx() ctx: RequestContext): Promise<ShippingMethodQuote[]> {
  115. if (ctx.authorizedAsOwnerOnly) {
  116. const sessionOrder = await this.getOrderFromContext(ctx);
  117. if (sessionOrder) {
  118. return this.orderService.getEligibleShippingMethods(ctx, sessionOrder.id);
  119. }
  120. }
  121. return [];
  122. }
  123. @Mutation()
  124. @Allow(Permission.Owner)
  125. @Decode('shippingMethodId')
  126. async setOrderShippingMethod(
  127. @Ctx() ctx: RequestContext,
  128. @Args() args: SetOrderShippingMethodMutationArgs,
  129. ): Promise<Order | undefined> {
  130. if (ctx.authorizedAsOwnerOnly) {
  131. const sessionOrder = await this.getOrderFromContext(ctx);
  132. if (sessionOrder) {
  133. return this.orderService.setShippingMethod(ctx, sessionOrder.id, args.shippingMethodId);
  134. }
  135. }
  136. }
  137. @Query()
  138. @Allow(Permission.Owner)
  139. async nextOrderStates(@Ctx() ctx: RequestContext): Promise<string[]> {
  140. if (ctx.authorizedAsOwnerOnly) {
  141. const sessionOrder = await this.getOrderFromContext(ctx, true);
  142. return this.orderService.getNextOrderStates(sessionOrder);
  143. }
  144. return [];
  145. }
  146. @Mutation()
  147. @Allow(Permission.Owner)
  148. async transitionOrderToState(
  149. @Ctx() ctx: RequestContext,
  150. @Args() args: TransitionOrderToStateMutationArgs,
  151. ): Promise<Order | undefined> {
  152. if (ctx.authorizedAsOwnerOnly) {
  153. const sessionOrder = await this.getOrderFromContext(ctx, true);
  154. return this.orderService.transitionToState(ctx, sessionOrder.id, args.state as OrderState);
  155. }
  156. }
  157. @Mutation()
  158. @Allow(Permission.UpdateOrder, Permission.Owner)
  159. @Decode('productVariantId')
  160. async addItemToOrder(
  161. @Ctx() ctx: RequestContext,
  162. @Args() args: AddItemToOrderMutationArgs,
  163. ): Promise<Order> {
  164. const order = await this.getOrderFromContext(ctx, true);
  165. return this.orderService.addItemToOrder(ctx, order.id, args.productVariantId, args.quantity);
  166. }
  167. @Mutation()
  168. @Allow(Permission.UpdateOrder, Permission.Owner)
  169. @Decode('orderItemId')
  170. async adjustItemQuantity(
  171. @Ctx() ctx: RequestContext,
  172. @Args() args: AdjustItemQuantityMutationArgs,
  173. ): Promise<Order> {
  174. const order = await this.getOrderFromContext(ctx, true);
  175. return this.orderService.adjustItemQuantity(ctx, order.id, args.orderItemId, args.quantity);
  176. }
  177. @Mutation()
  178. @Allow(Permission.UpdateOrder, Permission.Owner)
  179. @Decode('orderItemId')
  180. async removeItemFromOrder(
  181. @Ctx() ctx: RequestContext,
  182. @Args() args: RemoveItemFromOrderMutationArgs,
  183. ): Promise<Order> {
  184. const order = await this.getOrderFromContext(ctx, true);
  185. return this.orderService.removeItemFromOrder(ctx, order.id, args.orderItemId);
  186. }
  187. @Mutation()
  188. @Allow(Permission.UpdateOrder, Permission.Owner)
  189. async addPaymentToOrder(@Ctx() ctx: RequestContext, @Args() args: AddPaymentToOrderMutationArgs) {
  190. if (ctx.authorizedAsOwnerOnly) {
  191. const sessionOrder = await this.getOrderFromContext(ctx);
  192. if (sessionOrder) {
  193. const order = await this.orderService.addPaymentToOrder(ctx, sessionOrder.id, args.input);
  194. if (
  195. order.active === false &&
  196. ctx.session &&
  197. ctx.session.activeOrder &&
  198. ctx.session.activeOrder.id === sessionOrder.id
  199. ) {
  200. await this.authService.unsetActiveOrder(ctx.session);
  201. }
  202. return order;
  203. }
  204. }
  205. }
  206. private async getOrderFromContext(ctx: RequestContext): Promise<Order | undefined>;
  207. private async getOrderFromContext(ctx: RequestContext, createIfNotExists: true): Promise<Order>;
  208. private async getOrderFromContext(
  209. ctx: RequestContext,
  210. createIfNotExists = false,
  211. ): Promise<Order | undefined> {
  212. if (!ctx.session) {
  213. throw new I18nError(`error.no-active-session`);
  214. }
  215. let order = ctx.session.activeOrder;
  216. if (!order) {
  217. if (ctx.activeUserId) {
  218. order = (await this.orderService.getActiveOrderForUser(ctx, ctx.activeUserId)) || null;
  219. }
  220. if (!order && createIfNotExists) {
  221. order = await this.orderService.create(ctx.activeUserId);
  222. }
  223. if (order) {
  224. await this.authService.setActiveOrder(ctx.session, order);
  225. }
  226. }
  227. return order || undefined;
  228. }
  229. }