diff --git a/src/modules/order/controllers/orders.controller.ts b/src/modules/order/controllers/orders.controller.ts index 58e171d..fd40716 100644 --- a/src/modules/order/controllers/orders.controller.ts +++ b/src/modules/order/controllers/orders.controller.ts @@ -10,6 +10,7 @@ import { UpdateOrderStatusDto } from '../dto/update-order-status.dto'; import { Permissions } from 'src/common/decorators/permissions.decorator'; import { Permission } from 'src/common/enums/permission.enum'; import { CreateOrderDto } from '../dto/create-order.dto'; +import { CreateInvoiceDto } from '../dto/create-invoice.dto'; @ApiTags('orders') @ApiBearerAuth() @@ -37,18 +38,17 @@ export class OrdersController { @ApiQuery({ name: 'to', required: false, type: Date }) @ApiOperation({ summary: 'Get all orders with pagination and filters' }) async findAll(@Query() dto: FindOrdersDto, @UserId() userId: string) { - const orders=await this.ordersService.findAllForUser(userId, dto); + const orders = await this.ordersService.findAllForUser(userId, dto); return orders } - // @UseGuards(AuthGuard) - // @ApiOperation({ summary: 'Get an order By id for User' }) - // @ApiParam({ name: 'orderId', description: 'Order ID' }) - - // @Get('public/orders/:orderId') - // findOne(@Param('orderId') orderId: string,) { - // return this.ordersService.findOne(orderId,); - // } + @UseGuards(AuthGuard) + @ApiOperation({ summary: 'Create invoice for new order' }) + @ApiParam({ name: 'orderId', description: 'Order ID' }) + @Post('admin/orders/:orderId/create-invoice') + createInvoice(@Param('orderId') orderId: string, @Body() body: CreateInvoiceDto) { + return this.ordersService.createInvoice(orderId, body); + } // @UseGuards(AuthGuard) // @Patch('public/orders/:id/:status') diff --git a/src/modules/order/dto/create-invoice.dto.ts b/src/modules/order/dto/create-invoice.dto.ts new file mode 100644 index 0000000..c295f37 --- /dev/null +++ b/src/modules/order/dto/create-invoice.dto.ts @@ -0,0 +1,40 @@ +import { + IsInt, IsArray, IsNumber, + IsNotEmpty, + ArrayMinSize +} from 'class-validator'; +import { ApiProperty } from '@nestjs/swagger'; +import { Type } from 'class-transformer'; + +export class InvoiceItemDto { + @IsInt() + @ApiProperty() + orderItemId: bigint; + + @ApiProperty() + @IsNumber() + unitPrice: number + +} + +export class CreateInvoiceDto { + @IsArray() + @ApiProperty({ + isArray: true, type: [InvoiceItemDto], example: [ + { + productId: 1, + quantity: 100, + attributesValues: [] + } + ] + }) + @IsNotEmpty() + @ArrayMinSize(1, { message: 'At least one product is required' }) + @Type(() => InvoiceItemDto) + items: InvoiceItemDto[]; + + @ApiProperty() + @IsNumber() + discount: number +} + diff --git a/src/modules/order/entities/order-item.entity.ts b/src/modules/order/entities/order-item.entity.ts index a19f412..caca531 100644 --- a/src/modules/order/entities/order-item.entity.ts +++ b/src/modules/order/entities/order-item.entity.ts @@ -26,8 +26,6 @@ export class OrderItem extends BaseEntity { @Property({ type: 'decimal', precision: 10, scale: 0 }) unitPrice!: number; - @Property({ type: 'decimal', precision: 10, scale: 0, default: 0 }) - discount: number = 0; @Property({ type: 'decimal', precision: 10, scale: 0 }) totalPrice!: number; diff --git a/src/modules/order/interface/order.interface.ts b/src/modules/order/interface/order.interface.ts index 3914d08..05dd206 100644 --- a/src/modules/order/interface/order.interface.ts +++ b/src/modules/order/interface/order.interface.ts @@ -1,6 +1,6 @@ export enum OrderStatusEnum { NEW = 'new', - INVOICE = 'invoice', + INVOICED = 'invoiced', PREPARING = 'preparing', COMPLETED = 'completed', CANCELED = 'canceled', diff --git a/src/modules/order/providers/orders.service.ts b/src/modules/order/providers/orders.service.ts index d57e65a..2a10902 100644 --- a/src/modules/order/providers/orders.service.ts +++ b/src/modules/order/providers/orders.service.ts @@ -21,6 +21,7 @@ import { ProductRepository } from 'src/modules/product/repositories/product.repo import { TicketService } from 'src/modules/ticket/providers/tickets.service'; import { TicketRepository } from 'src/modules/ticket/repositories/tickets.repository'; import { TicketStatus } from 'src/modules/ticket/enums/ticket-status.enum'; +import { CreateInvoiceDto } from '../dto/create-invoice.dto'; @Injectable() @@ -77,7 +78,6 @@ export class OrdersService { const orderItem = this.orderItemRepository.create({ order, attributesValues: item.attributesValues, - discount: 0, product, quantity: item.quantity, status: OrderItemStatus.PENDING, @@ -108,4 +108,65 @@ export class OrdersService { const orders = await this.orderRepository.findAllPaginated({ userId, ...dto }) return orders } + + async findOneOrFail(orderId: string) { + const order = await this.orderRepository.findOne({ id: orderId }, + { populate: ['items', 'items.product', 'payments'] }) + if (!order) { + throw new BadRequestException('Order not found') + } + return order + } + + async createInvoice(orderId: string, dto: CreateInvoiceDto) { + const { items, discount } = dto + + const targetOrder = await this.findOneOrFail(orderId) + + // set price for order items + // calculate order financials + // change status + + if (items.length !== targetOrder.items.length) { + throw new BadRequestException("One or more order items price is not set") + } + + + const order = await this.em.transactional(async (em) => { + + let subTotal = 0 + // Calculate order item finnicials + for (const orderItem of targetOrder.items) { + + const found = items.find(it => it.orderItemId === orderItem.id) + if (!found) { + throw new BadRequestException(`order item ${orderItem} not found`) + } + // TODO use Deciaml js to calculation + orderItem.unitPrice = found.unitPrice + orderItem.totalPrice = (found.unitPrice) * orderItem.quantity + + subTotal += orderItem.totalPrice + + em.persist(orderItem) + + } + + // calculate order financials + targetOrder.subTotal = subTotal + targetOrder.discount = discount + + targetOrder.total = subTotal - discount + + // change status + targetOrder.status = OrderStatusEnum.INVOICED + + em.persist(targetOrder) + + + return targetOrder + }) + + return order + } }