create invoice

This commit is contained in:
2026-01-17 19:25:32 +03:30
parent 02ab8fbc6a
commit d6d371b13a
5 changed files with 112 additions and 13 deletions
@@ -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')
@@ -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
}
@@ -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;
@@ -1,6 +1,6 @@
export enum OrderStatusEnum {
NEW = 'new',
INVOICE = 'invoice',
INVOICED = 'invoiced',
PREPARING = 'preparing',
COMPLETED = 'completed',
CANCELED = 'canceled',
+62 -1
View File
@@ -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
}
}