This commit is contained in:
2026-03-07 10:20:37 +03:30
parent f437485855
commit af8367340e
10 changed files with 340 additions and 0 deletions
+2
View File
@@ -31,6 +31,7 @@ import { TicketsModule } from "./modules/tickets/tickets.module";
import { UploaderModule } from "./modules/uploader/uploader.module";
import { UsersModule } from "./modules/users/users.module";
import { UtilsModule } from "./modules/utils/utils.module";
import { BarnameModule } from "./modules/barname/barname.module";
@Module({
imports: [
@@ -67,6 +68,7 @@ import { UtilsModule } from "./modules/utils/utils.module";
ChatbotModule,
SearchModule,
BillsModule,
BarnameModule
],
})
export class AppModule implements NestModule {
+56
View File
@@ -0,0 +1,56 @@
import {
Body, Controller, Delete, Get, Param, Post,
Query, UseInterceptors
} from "@nestjs/common";
import { ApiHeader, ApiOperation } from "@nestjs/swagger";
import { BarnameService } from "./barname.service";
import { BillListQueryDto } from "./dto/bill-list-query.dto";
import { CreateBarnameDto } from "./dto/create-barname.dto";
import { AuthGuards } from "../../common/decorators/auth-guard.decorator";
import { BusinessDec } from "../../common/decorators/business.decorator";
import { ParamDto } from "../../common/DTO/param.dto";
import { BusinessInterceptor } from "../../core/interceptors/business.interceptor";
import { Business } from "../businesses/entities/business.entity";
import { UserDec } from "../../common/decorators/user.decorator";
@Controller("barname")
@UseInterceptors(BusinessInterceptor)
@AuthGuards()
export class BarnameController {
constructor(private readonly billsService: BarnameService) { }
@ApiOperation({ summary: "Create water bill" })
@Post("water")
@ApiHeader({ name: "x-business-id" })
createWaterBill(@Body() dto: CreateBarnameDto, @BusinessDec() business: Business, @UserDec("id") userId: string,) {
return this.billsService.createBarname(dto, business, userId);
}
@ApiOperation({ summary: "Create" })
@ApiHeader({ name: "x-business-id" })
@Post("charge")
create(@Body() dto: CreateBarnameDto, @BusinessDec() business: Business, @UserDec("id") userId: string) {
return this.billsService.createBarname(dto, business,userId);
}
@ApiOperation({ summary: "Get all bills with filters and pagination" })
@Get()
@ApiHeader({ name: "x-business-id" })
findAll(@Query() queryDto: BillListQueryDto, @BusinessDec("id") businessId: string) {
return this.billsService.findAll(queryDto, businessId);
}
@ApiOperation({ summary: "Get a bill by id" })
@Get(":id")
findOne(@Param() paramDto: ParamDto, @BusinessDec("id") businessId: string) {
return this.billsService.findOne(paramDto.id, businessId);
}
@ApiOperation({ summary: "Remove a bill" })
@Delete(":id")
remove(@Param() paramDto: ParamDto, @BusinessDec("id") businessId: string) {
return this.billsService.remove(paramDto.id, businessId);
}
}
+26
View File
@@ -0,0 +1,26 @@
import { MikroOrmModule } from "@mikro-orm/nestjs";
import { BullModule } from "@nestjs/bullmq";
import { Module } from "@nestjs/common";
import { BarnameController } from "./barname.controller";
import { BarnameService } from "./barname.service";
import { Barname } from "./entities/barname.entity";
import { BusinessesModule } from "../businesses/businesses.module";
import { CompaniesModule } from "../companies/companies.module";
import { INVOICE } from "../invoices/constants";
import { InvoicesModule } from "../invoices/invoices.module";
import { NotificationModule } from "../notifications/notifications.module";
@Module({
imports: [
BullModule.registerQueue({ name: INVOICE.QUEUE_NAME }),
MikroOrmModule.forFeature([Barname]),
CompaniesModule,
BusinessesModule,
NotificationModule,
InvoicesModule,
],
controllers: [BarnameController],
providers: [BarnameService],
})
export class BarnameModule {}
+65
View File
@@ -0,0 +1,65 @@
import { EntityManager } from "@mikro-orm/postgresql";
import { BadRequestException, Injectable } from "@nestjs/common";
import { BillListQueryDto } from "./dto/bill-list-query.dto";
import { CreateBarnameDto } from "./dto/create-barname.dto";
import { Barname } from "./entities/barname.entity";
import { BarnameRepository } from "./repositories/barname.repository";
import { CompanyMessage } from "../../common/enums/message.enum";
import { Business } from "../businesses/entities/business.entity";
import { Company } from "../companies/entities/company.entity";
// import { CompaniesService } from "../companies/services/companies.service";
@Injectable()
export class BarnameService {
readonly SEWAGE_RATE = 0.3;
readonly TAX_RATE = 0.1;
constructor(
private readonly em: EntityManager,
// private readonly companiesService: CompaniesService,
private readonly billRepository: BarnameRepository,
) { }
async createBarname(dto: CreateBarnameDto, business: Business, userId: string) {
const { description, driverName, driverPhone, carType, exitAt, origin, plaque, weight,attachments } = dto;
const company = await this.em.findOne(Company, { business, user: { id: userId }, deletedAt: null });
if (!company) throw new BadRequestException(CompanyMessage.COMPANY_NOT_FOUND);
const bill = this.em.create(Barname,
{ carType, exitAt, origin, plaque, weight, business, description, driverName, driverPhone, company,attachments });
await this.em.persistAndFlush(bill);
return bill
}
async findAll(queryDto: BillListQueryDto, businessId: string) {
const [bills, count] = await this.billRepository.getBillsList(queryDto, businessId);
return {
bills,
count,
paginate: true,
};
}
async findOne(id: string, businessId: string) {
const bill = await this.billRepository.findOne({ id, business: { id: businessId } }, { populate: [] });
if (!bill) {
throw new BadRequestException("Bill not found");
}
return { bill };
}
async remove(id: string, businessId: string) {
const bill = await this.billRepository.findOne({ id, business: { id: businessId } }, { populate: [] });
if (!bill) {
throw new BadRequestException("Bill not found");
}
await this.em.removeAndFlush(bill);
return { deleted: true };
}
}
@@ -0,0 +1,22 @@
import { ApiPropertyOptional } from "@nestjs/swagger";
import { IsDateString, IsEnum, IsOptional } from "class-validator";
import { PaginationDto } from "../../../common/DTO/pagination.dto";
import { BarnameStatus } from "../enums/bill-type.enum";
export class BillListQueryDto extends PaginationDto {
@IsOptional()
@IsEnum(BarnameStatus)
@ApiPropertyOptional({ enum: BarnameStatus, description: "Filter by bill type" })
status?: BarnameStatus;
@IsOptional()
@IsDateString()
@ApiPropertyOptional({ description: "Filter bills created since this date", example: "2025-01-01" })
since?: string;
@IsOptional()
@IsDateString()
@ApiPropertyOptional({ description: "Filter bills created until this date", example: "2025-12-31" })
to?: string;
}
@@ -0,0 +1,50 @@
import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger";
import { IsArray, IsDateString, IsNotEmpty, IsOptional, IsString, } from "class-validator";
export class CreateBarnameDto {
@ApiProperty({ description: "Barname title" })
@IsString()
@IsNotEmpty()
plaque: string;
@ApiProperty({ description: "Barname description" })
@IsString()
@IsNotEmpty()
description: string;
@ApiProperty()
@IsString()
driverName: string;
@ApiProperty()
@IsString()
driverPhone: string;
@ApiPropertyOptional()
@IsString()
@IsOptional()
origin: string;
@ApiPropertyOptional()
@IsString()
@IsOptional()
weight: string;
@ApiPropertyOptional()
@IsString()
@IsOptional()
carType: string;
@ApiPropertyOptional()
@IsString({each:true})
@IsArray()
attachments: string[];
@ApiProperty()
@IsDateString()
exitAt: string;
}
@@ -0,0 +1,5 @@
import { PartialType } from "@nestjs/swagger";
import { CreateBarnameDto } from "./create-barname.dto";
export class UpdateBillDto extends PartialType(CreateBarnameDto) {}
@@ -0,0 +1,52 @@
import { Entity, EntityRepositoryType, ManyToOne, Opt, Property } from "@mikro-orm/core";
import { BaseEntity } from "../../../common/entities/base.entity";
import { Business } from "../../businesses/entities/business.entity";
import { BarnameRepository } from "../repositories/barname.repository";
import { Company } from "../../companies/entities/company.entity";
import { BarnameStatus } from "../enums/bill-type.enum";
@Entity({ repository: () => BarnameRepository })
export class Barname extends BaseEntity {
@ManyToOne(() => Business, { nullable: false, deleteRule: "cascade" })
business!: Business;
@ManyToOne(() => Company, { nullable: false, deleteRule: "cascade" })
company!: Company;
@Property({ type: 'numeric', autoincrement: true })
number!: number & Opt;
@Property({ type: 'string' })
driverName: string;
@Property({ type: 'string' })
driverPhone: string;
@Property({ type: 'string' })
origin: string;
@Property({ type: 'string' })
weight: string;
@Property({ type: 'string' })
carType: string;
@Property({ type: 'string' })
plaque: string;
@Property({ type: 'string' })
description: string;
@Property({ type: 'json' })
attachments: string[];
@Property({ nullable: true })
exitAt?: Date;
@Property()
status: BarnameStatus & Opt
[EntityRepositoryType]?: BarnameRepository;
}
+4
View File
@@ -0,0 +1,4 @@
export enum BarnameStatus {
PENDING='pending',
COMPLETED='completed'
}
+58
View File
@@ -0,0 +1,58 @@
import { EntityRepository } from "@mikro-orm/postgresql";
import { PaginationUtils } from "../../utils/providers/pagination.utils";
import { BillListQueryDto } from "../dto/bill-list-query.dto";
import { Barname } from "../entities/barname.entity";
export class BarnameRepository extends EntityRepository<Barname> {
async getBillsList(queryDto: BillListQueryDto, businessId: string) {
const { limit, skip } = PaginationUtils(queryDto);
const knex = this.em.getKnex();
const query = knex
.select("b.*")
.select(knex.raw("COUNT(i.id) as invoice_count"))
.from("bill as b")
.leftJoin("invoice as i", "i.bill_id", "b.id")
.where("b.business_id", businessId)
.groupBy("b.id")
.orderBy("b.created_at", "desc")
.limit(limit)
.offset(skip);
// Add filters
if (queryDto.status) {
query.andWhere("b.status", queryDto.status);
}
if (queryDto.since) {
query.andWhere("b.created_at", ">=", new Date(queryDto.since));
}
if (queryDto.to) {
query.andWhere("b.created_at", "<=", new Date(queryDto.to));
}
// Execute the query
const barname = await query;
// Get total count for pagination
const countQuery = knex("bill").count("* as total").where("business_id", businessId);
if (queryDto.since) {
countQuery.andWhere("created_at", ">=", new Date(queryDto.since));
}
if (queryDto.to) {
countQuery.andWhere("created_at", "<=", new Date(queryDto.to));
}
const totalResult = await countQuery;
const total = Number(totalResult[0].total);
return [barname, total];
}
}