Order

Now it's time for the order logic. In the CreateDTO, we'll need to start from scratch. To create an order, a payload must be received with a customer and the order's items. We already know that the customer can be represented by the IdDto, but what about the items? How do we represent a single item?

An orderItem will be represented by its own DTO, containing the product and quantity. The price won't be present as we should not allow any possibility of arbitrary prices being sent, don't you think? It's safer to search for it directly in the database.

Let's then begin by creating it in orders -> dto -> order-item.dto with following content. We can see that, using everything we learned so far, our validation is becoming elegant and consistent.

export class OrderItemDto {
  @IsEntity()
  readonly product: IdDto;

  @IsCardinal()
  readonly quantity: number;
}

We can now conclude our CreateOrderDto.

export class CreateOrderDto {
  @IsEntity()
  readonly customer: IdDto;

  @ArrayNotEmpty()
  @ValidateNested()
  @Type(() => OrderItemDto)
  readonly items: OrderItemDto[];
}

An order cannot be changed once created. Because of that, we can delete the UpdateDTO file, along with the update() method in both the controller and service.

Going now into the service, we can copy the product's structure, as it will be pretty similar. An order will be fetched together with its items and their respective products, along with its customer and payment. Therefore, we can already add these relations to the findAll() and findOne() methods.

relations: {
  items: {
    product: true,
  },
  customer: true,
  payment: true,
},

We must now write an auxiliary method that will receive the orderItemDto and will then find its product's price in the database to then return an orderItem with a price. Let's do so at the bottom of the service.

private async createOrderItemWithPrice(orderItemDto: OrderItemDto) {
  const { id } = orderItemDto.product;

  const product = await this.productsRepository.findOneBy({ id });
  if (!product) {
    throw new NotFoundException('Product not found');
  }
  const { price } = product;

  const orderItem = this.orderItemsRepository.create({
    ...orderItemDto,
    price,
  });
  return orderItem;
}

Remember to inject a Repository for OrderItem and Product, and also register them inside the TypeOrmModule in the OrdersModule.

Finally, the create() method will be written. We shall transform all the items that came to also have a price. We'll do this with the help of Promise.all() as all the searches can be done in parallel, which is more performant. At last, the order is saved.

async create(createOrderDto: CreateOrderDto) {
  const { items } = createOrderDto;

  const itemsWithPrice = await Promise.all(
    items.map((item) => this.createOrderItemWithPrice(item)),
  );

  const order = this.ordersRepository.create({
    ...createOrderDto,
    items: itemsWithPrice,    
  });
  return this.ordersRepository.save(order);
}

Due to the cascade option we set earlier, we are saving in the database both the order and its items.

Commit - Implementing order logic and saving with cascade

Last updated