From 84270ccb46501d80878a8ab7582de0c326775ccc Mon Sep 17 00:00:00 2001 From: morteza-mortezai Date: Wed, 18 Feb 2026 22:00:00 +0330 Subject: [PATCH] up --- src/app.module.ts | 4 + src/modules/invoice/dto/create-invoice.dto.ts | 1 + src/modules/invoice/dto/update-invoice.dto.ts | 4 + .../invoice/entities/invoice-item.entity.ts | 56 ++++++++++++ .../invoice/entities/invoice.entity.ts | 90 +++++++++++++++++++ .../invoice/enum/invoice-status.enum.ts | 5 ++ src/modules/invoice/invoice.controller.ts | 34 +++++++ src/modules/invoice/invoice.module.ts | 9 ++ src/modules/invoice/invoice.service.ts | 26 ++++++ src/modules/order/entities/order.entity.ts | 3 +- src/modules/request/dto/create-request.dto.ts | 1 + src/modules/request/dto/update-request.dto.ts | 4 + .../request/entities/request-item.entity.ts | 28 ++++++ .../request/entities/request.entity.ts | 49 ++++++++++ src/modules/request/enum/request.ts | 5 ++ .../request/interface/request.interface.ts | 1 + src/modules/request/request.controller.ts | 34 +++++++ src/modules/request/request.module.ts | 9 ++ src/modules/request/request.service.ts | 26 ++++++ 19 files changed, 387 insertions(+), 2 deletions(-) create mode 100644 src/modules/invoice/dto/create-invoice.dto.ts create mode 100644 src/modules/invoice/dto/update-invoice.dto.ts create mode 100644 src/modules/invoice/entities/invoice-item.entity.ts create mode 100644 src/modules/invoice/entities/invoice.entity.ts create mode 100644 src/modules/invoice/enum/invoice-status.enum.ts create mode 100644 src/modules/invoice/invoice.controller.ts create mode 100644 src/modules/invoice/invoice.module.ts create mode 100644 src/modules/invoice/invoice.service.ts create mode 100644 src/modules/request/dto/create-request.dto.ts create mode 100644 src/modules/request/dto/update-request.dto.ts create mode 100644 src/modules/request/entities/request-item.entity.ts create mode 100644 src/modules/request/entities/request.entity.ts create mode 100644 src/modules/request/enum/request.ts create mode 100644 src/modules/request/interface/request.interface.ts create mode 100644 src/modules/request/request.controller.ts create mode 100644 src/modules/request/request.module.ts create mode 100644 src/modules/request/request.service.ts diff --git a/src/app.module.ts b/src/app.module.ts index 2e4cc15..5f5162a 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -18,6 +18,8 @@ import { EventEmitterModule } from '@nestjs/event-emitter'; import { CacheModule } from '@nestjs/cache-manager'; import { PrintModule } from './modules/print/print.module'; import { FormBuilderModule } from './modules/form-builder/form-builder.module'; +import { RequestModule } from './modules/request/request.module'; +import { InvoiceModule } from './modules/invoice/invoice.module'; @Module({ imports: [ @@ -43,6 +45,8 @@ import { FormBuilderModule } from './modules/form-builder/form-builder.module'; EventEmitterModule.forRoot(), PrintModule, FormBuilderModule, + RequestModule, + InvoiceModule, ], controllers: [], providers: [], diff --git a/src/modules/invoice/dto/create-invoice.dto.ts b/src/modules/invoice/dto/create-invoice.dto.ts new file mode 100644 index 0000000..22d9920 --- /dev/null +++ b/src/modules/invoice/dto/create-invoice.dto.ts @@ -0,0 +1 @@ +export class CreateInvoiceDto {} diff --git a/src/modules/invoice/dto/update-invoice.dto.ts b/src/modules/invoice/dto/update-invoice.dto.ts new file mode 100644 index 0000000..39983d1 --- /dev/null +++ b/src/modules/invoice/dto/update-invoice.dto.ts @@ -0,0 +1,4 @@ +import { PartialType } from '@nestjs/swagger'; +import { CreateInvoiceDto } from './create-invoice.dto'; + +export class UpdateInvoiceDto extends PartialType(CreateInvoiceDto) {} diff --git a/src/modules/invoice/entities/invoice-item.entity.ts b/src/modules/invoice/entities/invoice-item.entity.ts new file mode 100644 index 0000000..efb9788 --- /dev/null +++ b/src/modules/invoice/entities/invoice-item.entity.ts @@ -0,0 +1,56 @@ +import { Entity, Enum, Index, ManyToOne, OptionalProps, Property } from '@mikro-orm/core'; +import { Product } from 'src/modules/product/entities/product.entity'; +import { IField } from 'src/modules/order/interface/order.interface'; +import { BaseEntity } from 'src/common/entities/base.entity'; +import { Invoice } from './invoice.entity'; + + +@Entity({ tableName: 'invoice_items' }) +export class InvoiceItem extends BaseEntity { + [OptionalProps]?: 'createdAt' | 'deletedAt' | 'subTotal' | 'total'; + + @ManyToOne(() => Invoice) + invoice!: Invoice; + + @ManyToOne(() => Product) + product!: Product; + + @Property({ type: 'json', nullable: true }) + attributes?: IField[] + + @Property({ type: 'int' }) + quantity!: number; + + @Property({ type: 'int', nullable: true, }) + unitPrice?: number; + + @Property({ + type: 'int', + columnType: 'int GENERATED ALWAYS AS (COALESCE(unit_price, 0) * quantity) STORED', + persist: false, + nullable: true + }) + subTotal!: number; + + @Property({ + type: 'int', + nullable: true + }) + discount?: number; + + @Property({ + type: 'int', + nullable: true, + columnType: 'int GENERATED ALWAYS AS ((COALESCE(unit_price, 0) * quantity) - COALESCE(discount, 0)) STORED', + persist: false + }) + total!: number; + + @Property({ type: 'text', nullable: true }) + description?: string; + + + @Property({ nullable: true, columnType: 'timestamptz' }) + confirmedAt?: Date; + +} diff --git a/src/modules/invoice/entities/invoice.entity.ts b/src/modules/invoice/entities/invoice.entity.ts new file mode 100644 index 0000000..dfc9b6d --- /dev/null +++ b/src/modules/invoice/entities/invoice.entity.ts @@ -0,0 +1,90 @@ +import { + Entity, + Index, + ManyToOne, + OneToMany, + Property, + Collection, + Cascade, + Enum, + PrimaryKey, + OptionalProps, +} from '@mikro-orm/core'; +import { User } from '../../user/entities/user.entity'; +import { Payment } from 'src/modules/payment/entities/payment.entity'; +import { ulid } from 'ulid'; +import { Admin } from 'src/modules/admin/entities/admin.entity'; +import { BaseEntity } from 'src/common/entities/base.entity'; +import { InvoiceStatusEnum } from '../enum/invoice-status.enum'; +import { InvoiceItem } from './invoice-item.entity'; + +@Entity({ tableName: 'invoices' }) +@Index({ properties: ['user'] }) +export class Invoice extends BaseEntity { + [OptionalProps]?: 'createdAt' | 'deletedAt' | 'orderNumber' + + @PrimaryKey({ type: 'string', columnType: 'char(26)' }) + id: string = ulid() + + + @ManyToOne(() => User) + user!: User; + + @OneToMany(() => Payment, payment => payment.order, { + cascade: [Cascade.ALL], + orphanRemoval: true, + }) + payments = new Collection(this); + + @OneToMany(() => InvoiceItem, item => item.invoice, { + cascade: [Cascade.ALL], + orphanRemoval: true, + }) + items = new Collection(this); + + @ManyToOne(() => Admin, { nullable: true }) + creator?: Admin + + @Property({ + type: 'int', + autoincrement: true + }) + invoiceNumber: number; + + + @Property({ type: 'int', nullable: true }) + discount?: number; + + + @Property({ type: 'int', nullable: true }) + subTotal?: number; + + + @Property({ type: 'int', nullable: true }) + taxAmount?: number; + + + @Property({ type: 'int', nullable: true }) + total?: number; + + + @Property({ type: 'int', default: 0 }) + paidAmount: number = 0; + + + @Property({ type: 'int', default: 0 }) + balance: number = 0; + + + @Property({ type: 'int', nullable: true }) + estimatedDays?: number; // number of days to be done + + + @Enum(() => InvoiceStatusEnum) + status: InvoiceStatusEnum = InvoiceStatusEnum.PENDING; + + + @Property({ type: 'boolean', default: false }) + enableTax?: boolean = false + +} diff --git a/src/modules/invoice/enum/invoice-status.enum.ts b/src/modules/invoice/enum/invoice-status.enum.ts new file mode 100644 index 0000000..c0ae5c3 --- /dev/null +++ b/src/modules/invoice/enum/invoice-status.enum.ts @@ -0,0 +1,5 @@ +export enum InvoiceStatusEnum { + PENDING = 'pending', + PAID = 'paid', +} + diff --git a/src/modules/invoice/invoice.controller.ts b/src/modules/invoice/invoice.controller.ts new file mode 100644 index 0000000..7d9d0cc --- /dev/null +++ b/src/modules/invoice/invoice.controller.ts @@ -0,0 +1,34 @@ +import { Controller, Get, Post, Body, Patch, Param, Delete } from '@nestjs/common'; +import { InvoiceService } from './invoice.service'; +import { CreateInvoiceDto } from './dto/create-invoice.dto'; +import { UpdateInvoiceDto } from './dto/update-invoice.dto'; + +@Controller('invoice') +export class InvoiceController { + constructor(private readonly invoiceService: InvoiceService) {} + + @Post() + create(@Body() createInvoiceDto: CreateInvoiceDto) { + return this.invoiceService.create(createInvoiceDto); + } + + @Get() + findAll() { + return this.invoiceService.findAll(); + } + + @Get(':id') + findOne(@Param('id') id: string) { + return this.invoiceService.findOne(+id); + } + + @Patch(':id') + update(@Param('id') id: string, @Body() updateInvoiceDto: UpdateInvoiceDto) { + return this.invoiceService.update(+id, updateInvoiceDto); + } + + @Delete(':id') + remove(@Param('id') id: string) { + return this.invoiceService.remove(+id); + } +} diff --git a/src/modules/invoice/invoice.module.ts b/src/modules/invoice/invoice.module.ts new file mode 100644 index 0000000..94ff907 --- /dev/null +++ b/src/modules/invoice/invoice.module.ts @@ -0,0 +1,9 @@ +import { Module } from '@nestjs/common'; +import { InvoiceService } from './invoice.service'; +import { InvoiceController } from './invoice.controller'; + +@Module({ + controllers: [InvoiceController], + providers: [InvoiceService], +}) +export class InvoiceModule {} diff --git a/src/modules/invoice/invoice.service.ts b/src/modules/invoice/invoice.service.ts new file mode 100644 index 0000000..a28a234 --- /dev/null +++ b/src/modules/invoice/invoice.service.ts @@ -0,0 +1,26 @@ +import { Injectable } from '@nestjs/common'; +import { CreateInvoiceDto } from './dto/create-invoice.dto'; +import { UpdateInvoiceDto } from './dto/update-invoice.dto'; + +@Injectable() +export class InvoiceService { + create(createInvoiceDto: CreateInvoiceDto) { + return 'This action adds a new invoice'; + } + + findAll() { + return `This action returns all invoice`; + } + + findOne(id: number) { + return `This action returns a #${id} invoice`; + } + + update(id: number, updateInvoiceDto: UpdateInvoiceDto) { + return `This action updates a #${id} invoice`; + } + + remove(id: number) { + return `This action removes a #${id} invoice`; + } +} diff --git a/src/modules/order/entities/order.entity.ts b/src/modules/order/entities/order.entity.ts index a24738a..c3b5b0c 100644 --- a/src/modules/order/entities/order.entity.ts +++ b/src/modules/order/entities/order.entity.ts @@ -22,7 +22,7 @@ import { OrderStatusEnum } from '../interface/order.interface'; @Entity({ tableName: 'orders' }) @Index({ properties: ['user'] }) export class Order extends BaseEntity { - [OptionalProps]?: 'createdAt' | 'deletedAt' | 'orderNumber' | 'paidAmount' | 'balance'; + [OptionalProps]?: 'createdAt' | 'deletedAt' | 'orderNumber' @PrimaryKey({ type: 'string', columnType: 'char(26)' }) id: string = ulid() @@ -49,7 +49,6 @@ export class Order extends BaseEntity { @Property({ type: 'int', autoincrement: true - // defaultRaw: `nextval('order_number_seq')` }) orderNumber: number; diff --git a/src/modules/request/dto/create-request.dto.ts b/src/modules/request/dto/create-request.dto.ts new file mode 100644 index 0000000..0606908 --- /dev/null +++ b/src/modules/request/dto/create-request.dto.ts @@ -0,0 +1 @@ +export class CreateRequestDto {} diff --git a/src/modules/request/dto/update-request.dto.ts b/src/modules/request/dto/update-request.dto.ts new file mode 100644 index 0000000..435d9d8 --- /dev/null +++ b/src/modules/request/dto/update-request.dto.ts @@ -0,0 +1,4 @@ +import { PartialType } from '@nestjs/swagger'; +import { CreateRequestDto } from './create-request.dto'; + +export class UpdateRequestDto extends PartialType(CreateRequestDto) {} diff --git a/src/modules/request/entities/request-item.entity.ts b/src/modules/request/entities/request-item.entity.ts new file mode 100644 index 0000000..47b09f0 --- /dev/null +++ b/src/modules/request/entities/request-item.entity.ts @@ -0,0 +1,28 @@ +import { Entity, ManyToOne, OptionalProps, Property } from '@mikro-orm/core'; +import { Product } from 'src/modules/product/entities/product.entity'; +import { BaseEntity } from 'src/common/entities/base.entity'; +import { Request } from './request.entity'; +import { IAttachment } from '../interface/request.interface'; + + +@Entity({ tableName: 'request_items' }) +export class RequestItem extends BaseEntity { + [OptionalProps]?: 'createdAt' | 'deletedAt'; + + @ManyToOne(() => Product) + product!: Product; + + @ManyToOne(() => Request) + request!: Request; + + + @Property({ type: 'int' }) + quantity!: number; + + @Property({ type: 'text', nullable: true }) + description?: string; + + @Property({ type: 'json', nullable: true }) + attachments?: IAttachment[] // user attachments like voice and photos + +} diff --git a/src/modules/request/entities/request.entity.ts b/src/modules/request/entities/request.entity.ts new file mode 100644 index 0000000..5fc5e4f --- /dev/null +++ b/src/modules/request/entities/request.entity.ts @@ -0,0 +1,49 @@ +import { + Entity, + Index, + ManyToOne, + OneToMany, + Property, + Collection, + Cascade, + Enum, + PrimaryKey, + OptionalProps, +} from '@mikro-orm/core'; +import { User } from '../../user/entities/user.entity'; +import { ulid } from 'ulid'; +import { BaseEntity } from 'src/common/entities/base.entity'; +import { RequestItem } from './request-item.entity'; +import { RequestStatusEnum } from '../enum/request'; + +@Entity({ tableName: 'requests' }) +@Index({ properties: ['user'] }) +export class Request extends BaseEntity { + [OptionalProps]?: 'createdAt' | 'deletedAt' + + @PrimaryKey({ type: 'string', columnType: 'char(26)' }) + id: string = ulid() + + + @ManyToOne(() => User) + user!: User; + + + @OneToMany(() => RequestItem, item => item.request, { + cascade: [Cascade.ALL], + orphanRemoval: true, + }) + items = new Collection(this); + + + @Property({ + type: 'int', + autoincrement: true + }) + requestNumber: number; + + + @Enum(() => RequestStatusEnum) + status: RequestStatusEnum = RequestStatusEnum.PENDING; + +} diff --git a/src/modules/request/enum/request.ts b/src/modules/request/enum/request.ts new file mode 100644 index 0000000..06fab31 --- /dev/null +++ b/src/modules/request/enum/request.ts @@ -0,0 +1,5 @@ + +export enum RequestStatusEnum{ + PENDING = 'pending', + INVOICED = 'invoiced', +} \ No newline at end of file diff --git a/src/modules/request/interface/request.interface.ts b/src/modules/request/interface/request.interface.ts new file mode 100644 index 0000000..3e81846 --- /dev/null +++ b/src/modules/request/interface/request.interface.ts @@ -0,0 +1 @@ +export interface IAttachment { type: string, url: string } diff --git a/src/modules/request/request.controller.ts b/src/modules/request/request.controller.ts new file mode 100644 index 0000000..4794890 --- /dev/null +++ b/src/modules/request/request.controller.ts @@ -0,0 +1,34 @@ +import { Controller, Get, Post, Body, Patch, Param, Delete } from '@nestjs/common'; +import { RequestService } from './request.service'; +import { CreateRequestDto } from './dto/create-request.dto'; +import { UpdateRequestDto } from './dto/update-request.dto'; + +@Controller('request') +export class RequestController { + constructor(private readonly requestService: RequestService) {} + + @Post() + create(@Body() createRequestDto: CreateRequestDto) { + return this.requestService.create(createRequestDto); + } + + @Get() + findAll() { + return this.requestService.findAll(); + } + + @Get(':id') + findOne(@Param('id') id: string) { + return this.requestService.findOne(+id); + } + + @Patch(':id') + update(@Param('id') id: string, @Body() updateRequestDto: UpdateRequestDto) { + return this.requestService.update(+id, updateRequestDto); + } + + @Delete(':id') + remove(@Param('id') id: string) { + return this.requestService.remove(+id); + } +} diff --git a/src/modules/request/request.module.ts b/src/modules/request/request.module.ts new file mode 100644 index 0000000..a7e90db --- /dev/null +++ b/src/modules/request/request.module.ts @@ -0,0 +1,9 @@ +import { Module } from '@nestjs/common'; +import { RequestService } from './request.service'; +import { RequestController } from './request.controller'; + +@Module({ + controllers: [RequestController], + providers: [RequestService], +}) +export class RequestModule {} diff --git a/src/modules/request/request.service.ts b/src/modules/request/request.service.ts new file mode 100644 index 0000000..522f38c --- /dev/null +++ b/src/modules/request/request.service.ts @@ -0,0 +1,26 @@ +import { Injectable } from '@nestjs/common'; +import { CreateRequestDto } from './dto/create-request.dto'; +import { UpdateRequestDto } from './dto/update-request.dto'; + +@Injectable() +export class RequestService { + create(createRequestDto: CreateRequestDto) { + return 'This action adds a new request'; + } + + findAll() { + return `This action returns all request`; + } + + findOne(id: number) { + return `This action returns a #${id} request`; + } + + update(id: number, updateRequestDto: UpdateRequestDto) { + return `This action updates a #${id} request`; + } + + remove(id: number) { + return `This action removes a #${id} request`; + } +}