implement rule engine module with dynamic booking evaluation, 7 entities, and Postman endpoints

This commit is contained in:
marshal
2026-05-29 10:00:45 +03:00
parent 3b53042cf8
commit 800f036005
72 changed files with 2544 additions and 838 deletions

View File

@@ -0,0 +1,43 @@
import { Injectable } from '@nestjs/common';
import { DataSource, FindManyOptions, Repository } from 'typeorm';
import { CargoType } from '../entities/cargo-type.entity';
import { ICargoTypesRepository } from '../interfaces/cargo-types.repository.interface';
@Injectable()
export class CargoTypesRepository implements ICargoTypesRepository {
private readonly repo: Repository<CargoType>;
constructor(private readonly dataSource: DataSource) {
this.repo = this.dataSource.getRepository(CargoType);
}
findById(id: string): Promise<CargoType | null> {
return this.repo.findOne({ where: { id }, relations: { parent: true } });
}
findByCode(code: string): Promise<CargoType | null> {
return this.repo.findOne({ where: { code } });
}
findAll(options?: FindManyOptions<CargoType>): Promise<CargoType[]> {
return this.repo.find(options);
}
findAndCount(options?: FindManyOptions<CargoType>): Promise<[CargoType[], number]> {
return this.repo.findAndCount(options);
}
async create(data: Partial<CargoType>): Promise<CargoType> {
const entity = this.repo.create(data);
return this.repo.save(entity);
}
async update(id: string, data: Partial<CargoType>): Promise<CargoType | null> {
await this.repo.update(id, data);
return this.findById(id);
}
async softDelete(id: string): Promise<void> {
await this.repo.softDelete(id);
}
}

View File

@@ -0,0 +1,43 @@
import { Injectable } from '@nestjs/common';
import { DataSource, FindManyOptions, Repository } from 'typeorm';
import { ContainerType } from '../entities/container-type.entity';
import { IContainerTypesRepository } from '../interfaces/container-types.repository.interface';
@Injectable()
export class ContainerTypesRepository implements IContainerTypesRepository {
private readonly repo: Repository<ContainerType>;
constructor(private readonly dataSource: DataSource) {
this.repo = this.dataSource.getRepository(ContainerType);
}
findById(id: string): Promise<ContainerType | null> {
return this.repo.findOne({ where: { id } });
}
findBySizeCode(sizeCode: string): Promise<ContainerType | null> {
return this.repo.findOne({ where: { sizeCode } });
}
findAll(options?: FindManyOptions<ContainerType>): Promise<ContainerType[]> {
return this.repo.find(options);
}
findAndCount(options?: FindManyOptions<ContainerType>): Promise<[ContainerType[], number]> {
return this.repo.findAndCount(options);
}
async create(data: Partial<ContainerType>): Promise<ContainerType> {
const entity = this.repo.create(data);
return this.repo.save(entity);
}
async update(id: string, data: Partial<ContainerType>): Promise<ContainerType | null> {
await this.repo.update(id, data);
return this.findById(id);
}
async softDelete(id: string): Promise<void> {
await this.repo.softDelete(id);
}
}

View File

@@ -0,0 +1,43 @@
import { Injectable } from '@nestjs/common';
import { DataSource, FindManyOptions, Repository } from 'typeorm';
import { PriorityRule } from '../entities/priority-rule.entity';
import { IPriorityRulesRepository } from '../interfaces/priority-rules.repository.interface';
@Injectable()
export class PriorityRulesRepository implements IPriorityRulesRepository {
private readonly repo: Repository<PriorityRule>;
constructor(private readonly dataSource: DataSource) {
this.repo = this.dataSource.getRepository(PriorityRule);
}
findById(id: string): Promise<PriorityRule | null> {
return this.repo.findOne({ where: { id } });
}
findAllActive(): Promise<PriorityRule[]> {
return this.repo.find({ where: { isActive: true } });
}
findAll(options?: FindManyOptions<PriorityRule>): Promise<PriorityRule[]> {
return this.repo.find(options);
}
findAndCount(options?: FindManyOptions<PriorityRule>): Promise<[PriorityRule[], number]> {
return this.repo.findAndCount(options);
}
async create(data: Partial<PriorityRule>): Promise<PriorityRule> {
const entity = this.repo.create(data);
return this.repo.save(entity);
}
async update(id: string, data: Partial<PriorityRule>): Promise<PriorityRule | null> {
await this.repo.update(id, data);
return this.findById(id);
}
async softDelete(id: string): Promise<void> {
await this.repo.softDelete(id);
}
}

View File

@@ -0,0 +1,43 @@
import { Injectable } from '@nestjs/common';
import { DataSource, FindManyOptions, Repository } from 'typeorm';
import { ServiceType } from '../entities/service-type.entity';
import { IServiceTypesRepository } from '../interfaces/service-types.repository.interface';
@Injectable()
export class ServiceTypesRepository implements IServiceTypesRepository {
private readonly repo: Repository<ServiceType>;
constructor(private readonly dataSource: DataSource) {
this.repo = this.dataSource.getRepository(ServiceType);
}
findById(id: string): Promise<ServiceType | null> {
return this.repo.findOne({ where: { id } });
}
findByCode(code: string): Promise<ServiceType | null> {
return this.repo.findOne({ where: { code } });
}
findAll(options?: FindManyOptions<ServiceType>): Promise<ServiceType[]> {
return this.repo.find(options);
}
findAndCount(options?: FindManyOptions<ServiceType>): Promise<[ServiceType[], number]> {
return this.repo.findAndCount(options);
}
async create(data: Partial<ServiceType>): Promise<ServiceType> {
const entity = this.repo.create(data);
return this.repo.save(entity);
}
async update(id: string, data: Partial<ServiceType>): Promise<ServiceType | null> {
await this.repo.update(id, data);
return this.findById(id);
}
async softDelete(id: string): Promise<void> {
await this.repo.softDelete(id);
}
}

View File

@@ -0,0 +1,43 @@
import { Injectable } from '@nestjs/common';
import { DataSource, FindManyOptions, Repository } from 'typeorm';
import { SurchargeType } from '../entities/surcharge-type.entity';
import { ISurchargeTypesRepository } from '../interfaces/surcharge-types.repository.interface';
@Injectable()
export class SurchargeTypesRepository implements ISurchargeTypesRepository {
private readonly repo: Repository<SurchargeType>;
constructor(private readonly dataSource: DataSource) {
this.repo = this.dataSource.getRepository(SurchargeType);
}
findById(id: string): Promise<SurchargeType | null> {
return this.repo.findOne({ where: { id } });
}
findByCode(code: string): Promise<SurchargeType | null> {
return this.repo.findOne({ where: { code } });
}
findAll(options?: FindManyOptions<SurchargeType>): Promise<SurchargeType[]> {
return this.repo.find(options);
}
findAndCount(options?: FindManyOptions<SurchargeType>): Promise<[SurchargeType[], number]> {
return this.repo.findAndCount(options);
}
async create(data: Partial<SurchargeType>): Promise<SurchargeType> {
const entity = this.repo.create(data);
return this.repo.save(entity);
}
async update(id: string, data: Partial<SurchargeType>): Promise<SurchargeType | null> {
await this.repo.update(id, data);
return this.findById(id);
}
async softDelete(id: string): Promise<void> {
await this.repo.softDelete(id);
}
}

View File

@@ -0,0 +1,46 @@
import { Injectable } from '@nestjs/common';
import { DataSource, FindManyOptions, Repository } from 'typeorm';
import { Surcharge } from '../entities/surcharge.entity';
import { ISurchargesRepository } from '../interfaces/surcharges.repository.interface';
@Injectable()
export class SurchargesRepository implements ISurchargesRepository {
private readonly repo: Repository<Surcharge>;
constructor(private readonly dataSource: DataSource) {
this.repo = this.dataSource.getRepository(Surcharge);
}
findById(id: string): Promise<Surcharge | null> {
return this.repo.findOne({ where: { id }, relations: { surchargeType: true } });
}
findByTypeCode(typeCode: string): Promise<Surcharge | null> {
return this.repo.findOne({
where: { isActive: true, surchargeType: { code: typeCode } },
relations: { surchargeType: true },
});
}
findAll(options?: FindManyOptions<Surcharge>): Promise<Surcharge[]> {
return this.repo.find(options);
}
findAndCount(options?: FindManyOptions<Surcharge>): Promise<[Surcharge[], number]> {
return this.repo.findAndCount(options);
}
async create(data: Partial<Surcharge>): Promise<Surcharge> {
const entity = this.repo.create(data);
return this.repo.save(entity);
}
async update(id: string, data: Partial<Surcharge>): Promise<Surcharge | null> {
await this.repo.update(id, data);
return this.findById(id);
}
async softDelete(id: string): Promise<void> {
await this.repo.softDelete(id);
}
}

View File

@@ -0,0 +1,60 @@
import { Injectable } from '@nestjs/common';
import { DataSource, FindManyOptions, Repository } from 'typeorm';
import { WeightLimitRule } from '../entities/weight-limit-rule.entity';
import { IWeightLimitRulesRepository } from '../interfaces/weight-limit-rules.repository.interface';
@Injectable()
export class WeightLimitRulesRepository implements IWeightLimitRulesRepository {
private readonly repo: Repository<WeightLimitRule>;
constructor(private readonly dataSource: DataSource) {
this.repo = this.dataSource.getRepository(WeightLimitRule);
}
findById(id: string): Promise<WeightLimitRule | null> {
return this.repo.findOne({
where: { id },
relations: { containerType: true, surcharge: { surchargeType: true } },
});
}
findActiveByContainerTypeAndDirection(
sizeCode: string,
tradeDirection: string,
): Promise<WeightLimitRule[]> {
return this.repo
.createQueryBuilder('rule')
.innerJoinAndSelect('rule.containerType', 'ct')
.leftJoinAndSelect('rule.surcharge', 'surcharge')
.leftJoinAndSelect('surcharge.surchargeType', 'surchargeType')
.where('ct.size_code = :sizeCode', { sizeCode })
.andWhere('(rule.trade_direction = :dir OR rule.trade_direction = :both)', {
dir: tradeDirection,
both: 'BOTH',
})
.andWhere('rule.is_active = true')
.getMany();
}
findAll(options?: FindManyOptions<WeightLimitRule>): Promise<WeightLimitRule[]> {
return this.repo.find(options);
}
findAndCount(options?: FindManyOptions<WeightLimitRule>): Promise<[WeightLimitRule[], number]> {
return this.repo.findAndCount(options);
}
async create(data: Partial<WeightLimitRule>): Promise<WeightLimitRule> {
const entity = this.repo.create(data);
return this.repo.save(entity);
}
async update(id: string, data: Partial<WeightLimitRule>): Promise<WeightLimitRule | null> {
await this.repo.update(id, data);
return this.findById(id);
}
async softDelete(id: string): Promise<void> {
await this.repo.softDelete(id);
}
}