diff --git a/src/modules/order/controllers/order.controller.ts b/src/modules/order/controllers/order.controller.ts index dab70ab..035532b 100644 --- a/src/modules/order/controllers/order.controller.ts +++ b/src/modules/order/controllers/order.controller.ts @@ -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) diff --git a/src/modules/order/dto/update-invoiced-order.dto.ts b/src/modules/order/dto/update-invoiced-order.dto.ts new file mode 100644 index 0000000..8a755a0 --- /dev/null +++ b/src/modules/order/dto/update-invoiced-order.dto.ts @@ -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[] + +} + diff --git a/src/modules/order/dto/update-order.dto.ts b/src/modules/order/dto/update-order.dto.ts index e64eebc..14779bd 100644 --- a/src/modules/order/dto/update-order.dto.ts +++ b/src/modules/order/dto/update-order.dto.ts @@ -1,5 +1 @@ -// import { PartialType } from '@nestjs/swagger'; -// import { CreateOrderDto } from './create-order.dto'; - -// export class UpdateOrderDto extends PartialType(CreateOrderDto) {} export class UpdateOrderDto {} diff --git a/src/modules/order/entities/order-item.entity.ts b/src/modules/order/entities/order-item.entity.ts index 22d7e6d..6217d01 100644 --- a/src/modules/order/entities/order-item.entity.ts +++ b/src/modules/order/entities/order-item.entity.ts @@ -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; diff --git a/src/modules/order/entities/order.entity.ts b/src/modules/order/entities/order.entity.ts index 6a86bf8..b991375 100644 --- a/src/modules/order/entities/order.entity.ts +++ b/src/modules/order/entities/order.entity.ts @@ -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) { const em = args.em; diff --git a/src/modules/order/interface/order.interface.ts b/src/modules/order/interface/order.interface.ts index 9873bb7..8be865e 100644 --- a/src/modules/order/interface/order.interface.ts +++ b/src/modules/order/interface/order.interface.ts @@ -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', - } diff --git a/src/modules/order/providers/order.service.ts b/src/modules/order/providers/order.service.ts index e0c5773..986a2a6 100644 --- a/src/modules/order/providers/order.service.ts +++ b/src/modules/order/providers/order.service.ts @@ -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) { diff --git a/src/modules/product/controllers/product.controller.ts b/src/modules/product/controllers/product.controller.ts index 7576f3d..e471a6d 100644 --- a/src/modules/product/controllers/product.controller.ts +++ b/src/modules/product/controllers/product.controller.ts @@ -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() diff --git a/src/modules/product/providers/attribute.service.ts b/src/modules/product/providers/attribute.service.ts index 689b167..dc0d37f 100644 --- a/src/modules/product/providers/attribute.service.ts +++ b/src/modules/product/providers/attribute.service.ts @@ -86,7 +86,7 @@ export class AttributeService { } async remove(id: number): Promise { - const attribute = await this.findById(id); + const attribute = await this.findById(id); if (!attribute) { throw new NotFoundException('attribute not found'); }