invoice
This commit is contained in:
@@ -8,6 +8,12 @@ export enum PermissionEnum {
|
|||||||
DELETE_REQUEST = 'delete_request',
|
DELETE_REQUEST = 'delete_request',
|
||||||
UPDATE_REQUEST = 'update_request',
|
UPDATE_REQUEST = 'update_request',
|
||||||
|
|
||||||
|
// invoice
|
||||||
|
VIEW_INVOICES = 'view_invoices',
|
||||||
|
CREATE_INVOICE = 'create_invoice',
|
||||||
|
UPDATE_INVOICE = 'update_invoice',
|
||||||
|
DELETE_INVOICE = 'delete_invoice',
|
||||||
|
|
||||||
VIEW_All_INVOICED_ORDERS = 'view_all_invoiced_orders',
|
VIEW_All_INVOICED_ORDERS = 'view_all_invoiced_orders',
|
||||||
VIEW_MY_ASSIGNED_INVOICED_REQUESTS = 'view_my_assigned_invoiced_requests',
|
VIEW_MY_ASSIGNED_INVOICED_REQUESTS = 'view_my_assigned_invoiced_requests',
|
||||||
|
|
||||||
@@ -63,5 +69,9 @@ export const PermissionTitles: Record<PermissionEnum, string> = {
|
|||||||
[PermissionEnum.VIEW_MY_ASSIGNED_INVOICED_REQUESTS]: "مشاهده همه پیش فاکتور های من",
|
[PermissionEnum.VIEW_MY_ASSIGNED_INVOICED_REQUESTS]: "مشاهده همه پیش فاکتور های من",
|
||||||
[PermissionEnum.CREATE_ORDER_PRINT]: "ایجاد سفارش چاپ",
|
[PermissionEnum.CREATE_ORDER_PRINT]: "ایجاد سفارش چاپ",
|
||||||
[PermissionEnum.DELETE_REQUEST]: "",
|
[PermissionEnum.DELETE_REQUEST]: "",
|
||||||
[PermissionEnum.UPDATE_REQUEST]: ""
|
[PermissionEnum.UPDATE_REQUEST]: "",
|
||||||
|
[PermissionEnum.VIEW_INVOICES]: "",
|
||||||
|
[PermissionEnum.CREATE_INVOICE]: "",
|
||||||
|
[PermissionEnum.UPDATE_INVOICE]: "",
|
||||||
|
[PermissionEnum.DELETE_INVOICE]: ""
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1 +1,62 @@
|
|||||||
export class CreateInvoiceDto {}
|
import {
|
||||||
|
IsString,
|
||||||
|
IsInt,
|
||||||
|
IsArray,
|
||||||
|
IsNumber,
|
||||||
|
IsOptional,
|
||||||
|
IsBoolean,
|
||||||
|
IsDateString,
|
||||||
|
ArrayMinSize,
|
||||||
|
Min,
|
||||||
|
} from 'class-validator';
|
||||||
|
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
|
||||||
|
import { Type } from 'class-transformer';
|
||||||
|
|
||||||
|
export class CreateInvoiceItemDto {
|
||||||
|
@IsString()
|
||||||
|
@ApiProperty({ description: 'Product ID' })
|
||||||
|
productId!: string;
|
||||||
|
|
||||||
|
@IsInt()
|
||||||
|
@Min(1)
|
||||||
|
@ApiProperty({ example: 1 })
|
||||||
|
quantity!: number;
|
||||||
|
|
||||||
|
@IsNumber()
|
||||||
|
@Min(0)
|
||||||
|
@ApiProperty({ example: 10000 })
|
||||||
|
unitPrice: number;
|
||||||
|
|
||||||
|
@IsOptional()
|
||||||
|
@IsNumber()
|
||||||
|
@Min(0)
|
||||||
|
@ApiPropertyOptional({ example: 0 })
|
||||||
|
discount?: number;
|
||||||
|
|
||||||
|
@IsOptional()
|
||||||
|
@IsString()
|
||||||
|
@ApiPropertyOptional()
|
||||||
|
description?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class CreateInvoiceDto {
|
||||||
|
@IsString()
|
||||||
|
@ApiProperty({ description: 'Request ID to create invoice for' })
|
||||||
|
requestId!: string;
|
||||||
|
|
||||||
|
@IsArray()
|
||||||
|
@ArrayMinSize(1)
|
||||||
|
@Type(() => CreateInvoiceItemDto)
|
||||||
|
@ApiProperty({ type: [CreateInvoiceItemDto] })
|
||||||
|
items!: CreateInvoiceItemDto[];
|
||||||
|
|
||||||
|
@IsOptional()
|
||||||
|
@IsBoolean()
|
||||||
|
@ApiPropertyOptional({ example: false })
|
||||||
|
enableTax?: boolean;
|
||||||
|
|
||||||
|
@IsOptional()
|
||||||
|
@IsDateString()
|
||||||
|
@ApiPropertyOptional({ description: 'Approval deadline (ISO date string)' })
|
||||||
|
approvalDeadline?: string;
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,74 @@
|
|||||||
|
import {
|
||||||
|
IsOptional,
|
||||||
|
IsString,
|
||||||
|
IsNumber,
|
||||||
|
Min,
|
||||||
|
Max,
|
||||||
|
IsIn,
|
||||||
|
IsDateString,
|
||||||
|
IsBoolean,
|
||||||
|
} from 'class-validator';
|
||||||
|
import { Type, Transform } from 'class-transformer';
|
||||||
|
import { ApiPropertyOptional } from '@nestjs/swagger';
|
||||||
|
|
||||||
|
const sortOrderOptions = ['asc', 'desc'] as const;
|
||||||
|
type SortOrder = (typeof sortOrderOptions)[number];
|
||||||
|
|
||||||
|
export class FindInvoicesDto {
|
||||||
|
@ApiPropertyOptional({ default: 1, minimum: 1 })
|
||||||
|
@IsOptional()
|
||||||
|
@Type(() => Number)
|
||||||
|
@IsNumber()
|
||||||
|
@Min(1)
|
||||||
|
page: number = 1;
|
||||||
|
|
||||||
|
@ApiPropertyOptional({ default: 10, minimum: 1, maximum: 50 })
|
||||||
|
@IsOptional()
|
||||||
|
@Type(() => Number)
|
||||||
|
@IsNumber()
|
||||||
|
@Min(1)
|
||||||
|
@Max(50)
|
||||||
|
limit: number = 10;
|
||||||
|
|
||||||
|
@ApiPropertyOptional({
|
||||||
|
description: 'Search by invoice number or user info',
|
||||||
|
})
|
||||||
|
@IsOptional()
|
||||||
|
@IsString()
|
||||||
|
search?: string;
|
||||||
|
|
||||||
|
@ApiPropertyOptional({ format: 'date-time' })
|
||||||
|
@IsOptional()
|
||||||
|
@IsDateString()
|
||||||
|
from?: string;
|
||||||
|
|
||||||
|
@ApiPropertyOptional({ format: 'date-time' })
|
||||||
|
@IsOptional()
|
||||||
|
@IsDateString()
|
||||||
|
to?: string;
|
||||||
|
|
||||||
|
@ApiPropertyOptional({
|
||||||
|
description: 'Filter by enableTax',
|
||||||
|
})
|
||||||
|
@IsOptional()
|
||||||
|
@Transform(({ value }) => {
|
||||||
|
if (value === 'true') return true;
|
||||||
|
if (value === 'false') return false;
|
||||||
|
return value;
|
||||||
|
})
|
||||||
|
@IsBoolean()
|
||||||
|
enableTax?: boolean;
|
||||||
|
|
||||||
|
@ApiPropertyOptional({ default: 'createdAt' })
|
||||||
|
@IsOptional()
|
||||||
|
@IsString()
|
||||||
|
orderBy: string = 'createdAt';
|
||||||
|
|
||||||
|
@ApiPropertyOptional({
|
||||||
|
enum: sortOrderOptions,
|
||||||
|
default: 'desc',
|
||||||
|
})
|
||||||
|
@IsOptional()
|
||||||
|
@IsIn(sortOrderOptions)
|
||||||
|
order: SortOrder = 'desc';
|
||||||
|
}
|
||||||
@@ -18,8 +18,8 @@ export class InvoiceItem extends BaseEntity {
|
|||||||
@Property({ type: 'int' })
|
@Property({ type: 'int' })
|
||||||
quantity!: number;
|
quantity!: number;
|
||||||
|
|
||||||
@Property({ type: 'int', nullable: true, })
|
@Property({ type: 'int'})
|
||||||
unitPrice?: number;
|
unitPrice: number;
|
||||||
|
|
||||||
@Property({
|
@Property({
|
||||||
type: 'int',
|
type: 'int',
|
||||||
|
|||||||
@@ -1,34 +1,73 @@
|
|||||||
import { Controller, Get, Post, Body, Patch, Param, Delete } from '@nestjs/common';
|
import { Controller, Get, Post, Body, Patch, Param, Delete, UseGuards, Query } from '@nestjs/common';
|
||||||
|
import { ApiTags, ApiBearerAuth, ApiOperation, ApiBody } from '@nestjs/swagger';
|
||||||
import { InvoiceService } from './invoice.service';
|
import { InvoiceService } from './invoice.service';
|
||||||
import { CreateInvoiceDto } from './dto/create-invoice.dto';
|
import { CreateInvoiceDto } from './dto/create-invoice.dto';
|
||||||
import { UpdateInvoiceDto } from './dto/update-invoice.dto';
|
import { UpdateInvoiceDto } from './dto/update-invoice.dto';
|
||||||
|
import { AdminAuthGuard } from '../auth/guards/adminAuth.guard';
|
||||||
|
import { Permissions } from 'src/common/decorators/permissions.decorator';
|
||||||
|
import { PermissionEnum } from 'src/common/enums/permission.enum';
|
||||||
|
import { AuthGuard } from '../auth/guards/auth.guard';
|
||||||
|
import { FindInvoicesDto } from './dto/find-invoices.dto';
|
||||||
|
import { UserId } from 'src/common/decorators';
|
||||||
|
|
||||||
@Controller('invoice')
|
@Controller()
|
||||||
|
@ApiTags('invoice')
|
||||||
|
@ApiBearerAuth()
|
||||||
export class InvoiceController {
|
export class InvoiceController {
|
||||||
constructor(private readonly invoiceService: InvoiceService) {}
|
constructor(private readonly invoiceService: InvoiceService) { }
|
||||||
|
|
||||||
@Post()
|
@Get('public/invoice')
|
||||||
create(@Body() createInvoiceDto: CreateInvoiceDto) {
|
@UseGuards(AuthGuard)
|
||||||
return this.invoiceService.create(createInvoiceDto);
|
@ApiOperation({ summary: 'Get all invoices with pagination and filters' })
|
||||||
|
findAll(@Query() dto: FindInvoicesDto, @UserId() userId: string) {
|
||||||
|
return this.invoiceService.findAll(userId, dto);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Get()
|
@Get('public/invoice/:id')
|
||||||
findAll() {
|
@UseGuards(AuthGuard)
|
||||||
return this.invoiceService.findAll();
|
@ApiOperation({ summary: 'Get one invoice by id' })
|
||||||
|
findOne(@Param('id') id: string,@UserId() userId: string) {
|
||||||
|
return this.invoiceService.findOne(id,userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Get(':id')
|
@Get('public/invoice/item/:id')
|
||||||
findOne(@Param('id') id: string) {
|
@UseGuards(AuthGuard)
|
||||||
return this.invoiceService.findOne(+id);
|
@ApiOperation({ summary: 'Get one invoice item by id' })
|
||||||
|
confirmInvoiceItem(@Param('id') id: string,@UserId() userId: string) {
|
||||||
|
return this.invoiceService.confirmInvoiceItem(id,userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Patch(':id')
|
//------------------- Admin Routes-------------
|
||||||
|
@Post('admin/invoice')
|
||||||
|
@UseGuards(AdminAuthGuard)
|
||||||
|
@Permissions(PermissionEnum.CREATE_INVOICE)
|
||||||
|
@ApiOperation({ summary: 'Create a new invoice (admin)' })
|
||||||
|
@ApiBody({ type: CreateInvoiceDto })
|
||||||
|
create(@Body() dto: CreateInvoiceDto) {
|
||||||
|
return this.invoiceService.create(dto);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get('admin/invoice')
|
||||||
|
@UseGuards(AuthGuard)
|
||||||
|
@ApiOperation({ summary: 'Get all invoices with pagination and filters' })
|
||||||
|
findAllAsAdmin(@Query() dto: FindInvoicesDto) {
|
||||||
|
return this.invoiceService.findAllAsAdmin(dto);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Patch('admin/invoice/:id')
|
||||||
|
@UseGuards(AdminAuthGuard)
|
||||||
|
@Permissions(PermissionEnum.UPDATE_INVOICE)
|
||||||
|
@ApiOperation({ summary: 'Update an invoice (admin)' })
|
||||||
|
@ApiBody({ type: UpdateInvoiceDto })
|
||||||
update(@Param('id') id: string, @Body() updateInvoiceDto: UpdateInvoiceDto) {
|
update(@Param('id') id: string, @Body() updateInvoiceDto: UpdateInvoiceDto) {
|
||||||
return this.invoiceService.update(+id, updateInvoiceDto);
|
return this.invoiceService.update(id, updateInvoiceDto);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Delete(':id')
|
@Delete('admin/invoice/:id')
|
||||||
|
@UseGuards(AdminAuthGuard)
|
||||||
|
@Permissions(PermissionEnum.DELETE_INVOICE)
|
||||||
|
@ApiOperation({ summary: 'Remove an invoice (admin)' })
|
||||||
remove(@Param('id') id: string) {
|
remove(@Param('id') id: string) {
|
||||||
return this.invoiceService.remove(+id);
|
return this.invoiceService.remove(id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,18 @@
|
|||||||
import { Module } from '@nestjs/common';
|
import { Module } from '@nestjs/common';
|
||||||
|
import { MikroOrmModule } from '@mikro-orm/nestjs';
|
||||||
import { InvoiceService } from './invoice.service';
|
import { InvoiceService } from './invoice.service';
|
||||||
import { InvoiceController } from './invoice.controller';
|
import { InvoiceController } from './invoice.controller';
|
||||||
|
import { Invoice } from './entities/invoice.entity';
|
||||||
|
import { InvoiceItem } from './entities/invoice-item.entity';
|
||||||
|
import { ProductModule } from '../product/product.module';
|
||||||
|
import { InvoiceRepository } from './repositories/invoice.repository';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
|
imports: [
|
||||||
|
MikroOrmModule.forFeature([Invoice, InvoiceItem]),
|
||||||
|
ProductModule,
|
||||||
|
],
|
||||||
controllers: [InvoiceController],
|
controllers: [InvoiceController],
|
||||||
providers: [InvoiceService],
|
providers: [InvoiceService, InvoiceRepository],
|
||||||
})
|
})
|
||||||
export class InvoiceModule {}
|
export class InvoiceModule {}
|
||||||
|
|||||||
@@ -1,26 +1,152 @@
|
|||||||
import { Injectable } from '@nestjs/common';
|
import { Injectable, NotFoundException, BadRequestException } from '@nestjs/common';
|
||||||
|
import { EntityManager } from '@mikro-orm/postgresql';
|
||||||
import { CreateInvoiceDto } from './dto/create-invoice.dto';
|
import { CreateInvoiceDto } from './dto/create-invoice.dto';
|
||||||
import { UpdateInvoiceDto } from './dto/update-invoice.dto';
|
import { UpdateInvoiceDto } from './dto/update-invoice.dto';
|
||||||
|
import { FindInvoicesDto } from './dto/find-invoices.dto';
|
||||||
|
import { Invoice } from './entities/invoice.entity';
|
||||||
|
import { InvoiceItem } from './entities/invoice-item.entity';
|
||||||
|
import { Request } from '../request/entities/request.entity';
|
||||||
|
import { ProductService } from '../product/providers/product.service';
|
||||||
|
import { InvoiceRepository } from './repositories/invoice.repository';
|
||||||
|
|
||||||
|
const TAX_RATE = 0.09;
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class InvoiceService {
|
export class InvoiceService {
|
||||||
create(createInvoiceDto: CreateInvoiceDto) {
|
constructor(
|
||||||
return 'This action adds a new invoice';
|
private readonly em: EntityManager,
|
||||||
|
private readonly productService: ProductService,
|
||||||
|
private readonly invoiceRepository: InvoiceRepository,
|
||||||
|
) { }
|
||||||
|
|
||||||
|
|
||||||
|
async create(dto: CreateInvoiceDto) {
|
||||||
|
const request = await this.em.findOne(
|
||||||
|
Request,
|
||||||
|
{ id: dto.requestId },
|
||||||
|
{ populate: ['user'] },
|
||||||
|
);
|
||||||
|
if (!request) {
|
||||||
|
throw new NotFoundException('Request not found');
|
||||||
}
|
}
|
||||||
|
|
||||||
findAll() {
|
const enableTax = dto.enableTax ?? false;
|
||||||
return `This action returns all invoice`;
|
const approvalDeadline = dto.approvalDeadline
|
||||||
|
? new Date(dto.approvalDeadline)
|
||||||
|
: undefined;
|
||||||
|
|
||||||
|
const discount = dto.items.reduce((acc, item) => acc + (item.discount ?? 0), 0);
|
||||||
|
|
||||||
|
return this.em.transactional(async (em) => {
|
||||||
|
const invoice = em.create(Invoice, {
|
||||||
|
user: request.user,
|
||||||
|
request,
|
||||||
|
enableTax,
|
||||||
|
approvalDeadline,
|
||||||
|
discount,
|
||||||
|
paidAmount: 0,
|
||||||
|
balance: 0,
|
||||||
|
});
|
||||||
|
em.persist(invoice);
|
||||||
|
|
||||||
|
let subTotal = 0;
|
||||||
|
|
||||||
|
for (const item of dto.items) {
|
||||||
|
const product = await this.productService.findOneOrFail(item.productId);
|
||||||
|
const unitPrice = item.unitPrice;
|
||||||
|
const itemDiscount = item.discount ?? 0;
|
||||||
|
const itemTotal = Math.max(0, unitPrice * item.quantity - itemDiscount);
|
||||||
|
subTotal += itemTotal;
|
||||||
|
|
||||||
|
const invoiceItem = em.create(InvoiceItem, {
|
||||||
|
invoice,
|
||||||
|
product,
|
||||||
|
quantity: item.quantity,
|
||||||
|
unitPrice: unitPrice,
|
||||||
|
discount: itemDiscount || undefined,
|
||||||
|
description: item.description,
|
||||||
|
});
|
||||||
|
invoice.items.add(invoiceItem);
|
||||||
}
|
}
|
||||||
|
|
||||||
findOne(id: number) {
|
const taxAmount = enableTax
|
||||||
return `This action returns a #${id} invoice`;
|
? Math.round((subTotal - discount) * TAX_RATE)
|
||||||
|
: 0;
|
||||||
|
const total = Math.max(0, subTotal - discount + taxAmount);
|
||||||
|
|
||||||
|
invoice.subTotal = subTotal;
|
||||||
|
invoice.taxAmount = taxAmount;
|
||||||
|
invoice.total = total;
|
||||||
|
invoice.balance = total - invoice.paidAmount;
|
||||||
|
|
||||||
|
await em.flush();
|
||||||
|
return invoice;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
update(id: number, updateInvoiceDto: UpdateInvoiceDto) {
|
findAll(userId: string, dto: FindInvoicesDto) {
|
||||||
return `This action updates a #${id} invoice`;
|
return this.invoiceRepository.findAllPaginated({ userId, ...dto });
|
||||||
}
|
}
|
||||||
|
|
||||||
remove(id: number) {
|
findAllAsAdmin(dto: FindInvoicesDto) {
|
||||||
return `This action removes a #${id} invoice`;
|
return this.invoiceRepository.findAllPaginated(dto);
|
||||||
|
}
|
||||||
|
|
||||||
|
async findOne(id: string, userId: string) {
|
||||||
|
const invoice = await this.em.findOne(
|
||||||
|
Invoice,
|
||||||
|
{ id, user: { id: userId } },
|
||||||
|
{ populate: ['user', 'request', 'items', 'items.product'] },
|
||||||
|
);
|
||||||
|
if (!invoice) {
|
||||||
|
throw new NotFoundException('Invoice not found');
|
||||||
|
}
|
||||||
|
return invoice;
|
||||||
|
}
|
||||||
|
|
||||||
|
async confirmInvoiceItem(id: string, userId: string) {
|
||||||
|
const item = await this.em.findOne(
|
||||||
|
InvoiceItem,
|
||||||
|
{ id, invoice: { user: { id: userId } } },
|
||||||
|
{ populate: ['invoice', 'product'] },
|
||||||
|
);
|
||||||
|
if (!item) {
|
||||||
|
throw new NotFoundException('Invoice item not found');
|
||||||
|
}
|
||||||
|
if(item.confirmedAt){
|
||||||
|
throw new BadRequestException('Invoice item already confirmed');
|
||||||
|
}
|
||||||
|
item.confirmedAt = new Date();
|
||||||
|
await this.em.persistAndFlush(item);
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
async update(id: string, updateInvoiceDto: UpdateInvoiceDto) {
|
||||||
|
const invoice = await this.findOneOrFail(id);
|
||||||
|
if (updateInvoiceDto.enableTax !== undefined) invoice.enableTax = updateInvoiceDto.enableTax;
|
||||||
|
if (updateInvoiceDto.approvalDeadline !== undefined) {
|
||||||
|
invoice.approvalDeadline = new Date(updateInvoiceDto.approvalDeadline);
|
||||||
|
}
|
||||||
|
await this.em.flush();
|
||||||
|
return invoice;
|
||||||
|
}
|
||||||
|
|
||||||
|
async remove(id: string) {
|
||||||
|
const invoice = await this.findOneOrFail(id);
|
||||||
|
this.em.remove(invoice);
|
||||||
|
await this.em.flush();
|
||||||
|
return invoice;
|
||||||
|
}
|
||||||
|
|
||||||
|
async findOneOrFail(id: string) {
|
||||||
|
const invoice = await this.em.findOne(
|
||||||
|
Invoice,
|
||||||
|
{ id },
|
||||||
|
{ populate: ['user', 'request', 'items', 'items.product'] },
|
||||||
|
);
|
||||||
|
if (!invoice) {
|
||||||
|
throw new NotFoundException('Invoice not found');
|
||||||
|
}
|
||||||
|
return invoice;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,88 @@
|
|||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
import { EntityManager, EntityRepository } from '@mikro-orm/postgresql';
|
||||||
|
import { FilterQuery } from '@mikro-orm/core';
|
||||||
|
import { Invoice } from '../entities/invoice.entity';
|
||||||
|
import { PaginatedResult } from 'src/common/interfaces/pagination.interface';
|
||||||
|
import { FindInvoicesDto } from '../dto/find-invoices.dto';
|
||||||
|
|
||||||
|
interface FindInvoicesOpts extends FindInvoicesDto {
|
||||||
|
userId?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class InvoiceRepository extends EntityRepository<Invoice> {
|
||||||
|
constructor(readonly em: EntityManager) {
|
||||||
|
super(em, Invoice);
|
||||||
|
}
|
||||||
|
|
||||||
|
async findAllPaginated(opts: FindInvoicesOpts): Promise<PaginatedResult<Invoice>> {
|
||||||
|
const {
|
||||||
|
page = 1,
|
||||||
|
limit = 10,
|
||||||
|
search,
|
||||||
|
orderBy = 'createdAt',
|
||||||
|
order = 'desc',
|
||||||
|
userId,
|
||||||
|
from,
|
||||||
|
to,
|
||||||
|
enableTax,
|
||||||
|
} = opts;
|
||||||
|
|
||||||
|
const offset = (page - 1) * limit;
|
||||||
|
|
||||||
|
const where: FilterQuery<Invoice> = {};
|
||||||
|
|
||||||
|
if (userId) {
|
||||||
|
where.user = { id: userId };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof enableTax === 'boolean') {
|
||||||
|
where.enableTax = enableTax;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (from || to) {
|
||||||
|
where.createdAt = {};
|
||||||
|
if (from) {
|
||||||
|
where.createdAt.$gte = new Date(from);
|
||||||
|
}
|
||||||
|
if (to) {
|
||||||
|
where.createdAt.$lte = new Date(to);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (search) {
|
||||||
|
const searchPattern = `%${search}%`;
|
||||||
|
const searchConditions: FilterQuery<Invoice>[] = [
|
||||||
|
{ user: { firstName: { $ilike: searchPattern } } } as FilterQuery<Invoice>,
|
||||||
|
{ user: { lastName: { $ilike: searchPattern } } } as FilterQuery<Invoice>,
|
||||||
|
{ user: { phone: { $ilike: searchPattern } } } as FilterQuery<Invoice>,
|
||||||
|
];
|
||||||
|
|
||||||
|
const numericSearch = Number(search);
|
||||||
|
if (!isNaN(numericSearch) && isFinite(numericSearch)) {
|
||||||
|
searchConditions.push({ invoiceNumber: numericSearch } as FilterQuery<Invoice>);
|
||||||
|
}
|
||||||
|
|
||||||
|
where.$or = searchConditions;
|
||||||
|
}
|
||||||
|
|
||||||
|
const [data, total] = await this.findAndCount(where, {
|
||||||
|
limit,
|
||||||
|
offset,
|
||||||
|
orderBy: { [orderBy]: order.toLowerCase() as 'asc' | 'desc' },
|
||||||
|
populate: ['user', 'request', 'items', 'items.product'],
|
||||||
|
});
|
||||||
|
|
||||||
|
const totalPages = Math.ceil(total / limit);
|
||||||
|
|
||||||
|
return {
|
||||||
|
data,
|
||||||
|
meta: {
|
||||||
|
total,
|
||||||
|
page,
|
||||||
|
limit,
|
||||||
|
totalPages,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
import { Controller, Get, Post, Body, Patch, Param, Delete, UseGuards, Query } from '@nestjs/common';
|
import { Controller, Get, Post, Body, Patch, Param, Delete, UseGuards, Query } from '@nestjs/common';
|
||||||
|
import { ApiTags,ApiBearerAuth } from '@nestjs/swagger';
|
||||||
import { RequestService } from './request.service';
|
import { RequestService } from './request.service';
|
||||||
import { CreateRequestDto } from './dto/create-request.dto';
|
import { CreateRequestDto } from './dto/create-request.dto';
|
||||||
import { UpdateRequestDto } from './dto/update-request.dto';
|
import { UpdateRequestDto } from './dto/update-request.dto';
|
||||||
@@ -10,6 +11,8 @@ import { Permissions } from 'src/common/decorators/permissions.decorator';
|
|||||||
import { PermissionEnum } from 'src/common/enums/permission.enum';
|
import { PermissionEnum } from 'src/common/enums/permission.enum';
|
||||||
|
|
||||||
@Controller()
|
@Controller()
|
||||||
|
@ApiTags('request')
|
||||||
|
@ApiBearerAuth()
|
||||||
export class RequestController {
|
export class RequestController {
|
||||||
constructor(private readonly requestService: RequestService) { }
|
constructor(private readonly requestService: RequestService) { }
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user