This commit is contained in:
2026-01-25 12:36:56 +03:30
parent 2449ac447d
commit a7bd26c7ca
9 changed files with 197 additions and 24 deletions
@@ -70,12 +70,12 @@ export class OrderController {
// return this.orderService.addOrderItem(orderId, body);
// }
@Post('admin/orders/:orderId/create-invoice')
@UseGuards(AuthGuard)
@ApiOperation({ summary: 'Create invoice for new order' })
createInvoice(@Param('orderId') orderId: string, @Body() body: CreateInvoiceDto) {
return this.orderService.createInvoice(orderId, body);
}
// @Post('admin/orders/:orderId/create-invoice')
// @UseGuards(AuthGuard)
// @ApiOperation({ summary: 'Create invoice for new order' })
// createInvoice(@Param('orderId') orderId: string, @Body() body: CreateInvoiceDto) {
// return this.orderService.calculateOrder(orderId, body);
// }
@Post('admin/orders/:orderId/assign-designer')
@UseGuards(AuthGuard)
@@ -0,0 +1,91 @@
import {
IsArray, IsNumber,
IsNotEmpty,
ArrayMinSize,
IsBoolean,
IsString
} from 'class-validator';
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
import { Type } from 'class-transformer';
export class InvoiceOrderItemDto {
@IsNumber()
@ApiProperty()
orderItemId: number;
@ApiProperty()
@IsNumber()
unitPrice: number
@ApiProperty()
@IsNumber()
discount: number
@ApiProperty()
@IsNumber()
quantity: number
}
export class InvoiceOrderNewItemDto {
@IsNumber()
@ApiProperty()
productId: number;
@ApiProperty()
@IsArray()
attributesValues: string[]
@ApiProperty()
@IsNumber()
quantity: number
@ApiProperty()
@IsNumber()
unitPrice: number
@ApiProperty()
@IsNumber()
discount: number
}
export class UpdateInvoicedOrderDto {
@IsArray()
@ApiProperty({
isArray: true, type: [InvoiceOrderItemDto], example: [
{
orderItemId: 1,
unitPrice: 100,
}
]
})
@IsNotEmpty()
@ArrayMinSize(1, { message: 'At least one product is required' })
@Type(() => InvoiceOrderItemDto)
items: InvoiceOrderItemDto[];
@IsArray()
@ApiProperty({
isArray: true, type: [InvoiceOrderNewItemDto], example: [
{
orderItemId: 1,
unitPrice: 100,
}
]
})
@Type(() => InvoiceOrderNewItemDto)
newItems: InvoiceOrderNewItemDto[];
@ApiProperty()
@IsBoolean()
enableTax: boolean
@ApiPropertyOptional({ example: [] })
@IsArray()
@IsString({ each: true })
attachments: string[]
}
@@ -1,5 +1 @@
// import { PartialType } from '@nestjs/swagger';
// import { CreateOrderDto } from './create-order.dto';
// export class UpdateOrderDto extends PartialType(CreateOrderDto) {}
export class UpdateOrderDto {}
@@ -26,14 +26,16 @@ export class OrderItem extends BaseEntity {
@Property({ type: 'decimal', precision: 10, scale: 0 })
unitPrice!: number;
@Property({ type: 'decimal', precision: 10, scale: 0 })
subTotal!: number;
get subTotal(): number {
return Number(this.unitPrice) * (this.quantity)
}
@Property({ type: 'decimal', precision: 10, scale: 0, default: 0 })
discount: number = 0;
@Property({ type: 'decimal', precision: 10, scale: 0 })
total!: number;
get total(): number {
return Number(this.subTotal) - Number(this.discount || 0)
}
@Property({ nullable: true })
description?: string;
@@ -102,6 +102,12 @@ export class Order extends BaseEntity {
@Property({ type: 'string', nullable: true })
paymentMethod?: string;
@Property({ nullable: true, columnType: 'timestamptz' })
invoicedAt?: Date;
@Property({ type: 'boolean', default: false })
enableTax?: Boolean = false
@BeforeCreate()
async generateOrderNumber(args: EventArgs<Order>) {
const em = args.em;
+16 -3
View File
@@ -1,15 +1,28 @@
export enum OrderStatusEnum {
DRAFT = 'draft',
CREATED = 'created',
INVOICED = 'invoiced',
WAITING_to_CONFIRM_INVOICE = 'waiting_to_confirm_invoice',
IN_DESIGN = 'in_design',
DESIGN_INITIATED = 'design_initiated',
DESIGN_FINISHED = 'design_finished',
DESIGN_REVISION = 'design_revision',
READY_TO_PRINTING = 'ready_to_printing',
IN_PRINTING = 'in_printing',
READY_FOR_DELIVERY = 'ready_for_delivery',
DELIVERED = 'delivered',
AFTER_PRINT_COVER = 'after_print_cover',
AFTER_PRINT_BOXING = 'after_print_boxing',
AFTER_PRINT_CARTONING = 'after_print_cartoning',
FINISHED = 'finished',
CANCELED = 'canceled',
}
+60 -6
View File
@@ -15,7 +15,7 @@ import { TicketRepository } from 'src/modules/ticket/repositories/tickets.reposi
import { CreateInvoiceDto } from '../dto/create-invoice.dto';
import { AdminRepository } from 'src/modules/admin/repositories/admin.repository';
import { CreateOrderAsAdminDto } from '../dto/create-order-as-admin.dto';
@Injectable()
export class OrderService {
@@ -52,8 +52,9 @@ export class OrderService {
total: 0,
paidAmount: 0,
balance: 0,
status: OrderStatusEnum.DRAFT,
status: OrderStatusEnum.CREATED,
taxAmount: 0,
enableTax: false
})
em.persist(order)
@@ -198,7 +199,47 @@ export class OrderService {
return order
}
async createInvoice(orderId: string, dto: CreateInvoiceDto) {
async calculateOrder(orderId: string, em: EntityManager) {
const targetOrder = await this.findOneOrFail(orderId)
const order = await this.em.transactional(async (em) => {
// calculate order financials
let subTotal = 0
let totalDiscount = 0
// TODO : use reduce
for (let orderItem of targetOrder.items) {
subTotal += orderItem.total
totalDiscount += orderItem.discount
}
const totalBeforeTax = subTotal - totalDiscount
let tax = 0
if (targetOrder.enableTax) {
tax = 0.1 * totalBeforeTax
}
const total = totalBeforeTax + tax
// Update Order financial values
targetOrder.subTotal = subTotal
targetOrder.discount = totalDiscount
targetOrder.taxAmount = tax
targetOrder.total = total
em.persist(targetOrder)
return targetOrder
})
return order
}
async updateOrder(orderId: string, dto: CreateInvoiceDto) {
const { items, enableTax, attachments, newItems } = dto
const targetOrder = await this.findOneOrFail(orderId)
@@ -222,7 +263,7 @@ export class OrderService {
}
// TODO use Deciaml js to calculation
orderItem.unitPrice = found.unitPrice
orderItem.subTotal = (found.unitPrice) * orderItem.quantity
// orderItem.subTotal = (found.unitPrice) * orderItem.quantity
subTotal += orderItem.subTotal
@@ -279,6 +320,7 @@ export class OrderService {
// change status
targetOrder.status = OrderStatusEnum.INVOICED
targetOrder.invoicedAt = new Date()
em.persist(targetOrder)
@@ -321,7 +363,7 @@ export class OrderService {
}
order.designer = designer
order.status = OrderStatusEnum.IN_DESIGN
// order.status = OrderStatusEnum.IN_DESIGN
await this.em.persistAndFlush(order)
@@ -335,7 +377,7 @@ export class OrderService {
if (!order) {
throw new BadRequestException("Order not found")
}
if (![OrderStatusEnum.DRAFT, OrderStatusEnum.INVOICED].includes(order.status)) {
if (![OrderStatusEnum.CREATED, OrderStatusEnum.INVOICED].includes(order.status)) {
throw new BadRequestException("Order status must be of of Drfat or Invoiced")
}
if (order.payments.length > 0) {
@@ -352,6 +394,18 @@ export class OrderService {
}
async updateStatus(orderId: string, newStatus: OrderStatusEnum) {
const order = await this.orderRepository.findOne({ id: orderId })
if (!order) {
throw new BadRequestException("Order not found")
}
order.status = newStatus
await this.em.flush()
return order
}
// async addOrderItem(orderId: string, dto: AddOrderItemDto) {
// const order = await this.orderRepository.findOne({ id: orderId })
// if (!order) {
@@ -99,6 +99,16 @@ export class productController {
}
/* =============================== PRODUCT ================================= */
@Get('public/products')
// @UseGuards(AdminAuthGuard)
@ApiBearerAuth()
@Permissions(Permission.MANAGE_PRODUCTS)
@ApiOperation({ summary: 'Get list of products' })
async findAllAsUser() {
const result = await this.productService.findAll({ limit: 1000 });
return result;
}
@Post('admin/product')
// @UseGuards(AdminAuthGuard)
@ApiBearerAuth()
@@ -160,6 +170,7 @@ export class productController {
/*=============================== Attibute ==========================*/
@Post('admin/product/:id/attribute')
// @UseGuards(AdminAuthGuard)
@ApiBearerAuth()
@@ -86,7 +86,7 @@ export class AttributeService {
}
async remove(id: number): Promise<void> {
const attribute = await this.findById(id);
const attribute = await this.findById(id);
if (!attribute) {
throw new NotFoundException('attribute not found');
}