complete rule engine and booking flow

This commit is contained in:
marshal
2026-05-30 10:27:59 +03:00
parent 800f036005
commit 430dc44937
74 changed files with 4304 additions and 1248 deletions

View File

@@ -0,0 +1,46 @@
import { Injectable } from '@nestjs/common';
import { DataSource, FindManyOptions, Repository } from 'typeorm';
import { ApprovalRule } from '../entities/approval-rule.entity';
import { IApprovalRulesRepository } from '../interfaces/approval-rules.repository.interface';
@Injectable()
export class ApprovalRulesRepository implements IApprovalRulesRepository {
private readonly repo: Repository<ApprovalRule>;
constructor(private readonly dataSource: DataSource) {
this.repo = this.dataSource.getRepository(ApprovalRule);
}
findById(id: string): Promise<ApprovalRule | null> {
return this.repo.findOne({ where: { id } });
}
findChainForCargo(requiresDirectorApproval: boolean): Promise<ApprovalRule[]> {
return this.repo.find({
where: { requiresDirectorApproval },
order: { stepOrder: 'ASC' },
});
}
findAll(options?: FindManyOptions<ApprovalRule>): Promise<ApprovalRule[]> {
return this.repo.find(options);
}
findAndCount(options?: FindManyOptions<ApprovalRule>): Promise<[ApprovalRule[], number]> {
return this.repo.findAndCount(options);
}
async create(data: Partial<ApprovalRule>): Promise<ApprovalRule> {
const entity = this.repo.create(data);
return this.repo.save(entity);
}
async update(id: string, data: Partial<ApprovalRule>): Promise<ApprovalRule | null> {
await this.repo.update(id, data);
return this.findById(id);
}
async softDelete(id: string): Promise<void> {
await this.repo.softDelete(id);
}
}

View File

@@ -15,8 +15,8 @@ export class ContainerTypesRepository implements IContainerTypesRepository {
return this.repo.findOne({ where: { id } });
}
findBySizeCode(sizeCode: string): Promise<ContainerType | null> {
return this.repo.findOne({ where: { sizeCode } });
findByCode(code: string): Promise<ContainerType | null> {
return this.repo.findOne({ where: { code } });
}
findAll(options?: FindManyOptions<ContainerType>): Promise<ContainerType[]> {

View File

@@ -0,0 +1,49 @@
import { Injectable } from '@nestjs/common';
import { DataSource, FindManyOptions, Repository } from 'typeorm';
import { Rate } from '../entities/rate.entity';
import { IRatesRepository } from '../interfaces/rates.repository.interface';
@Injectable()
export class RatesRepository implements IRatesRepository {
private readonly repo: Repository<Rate>;
constructor(private readonly dataSource: DataSource) {
this.repo = this.dataSource.getRepository(Rate);
}
findById(id: string): Promise<Rate | null> {
return this.repo.findOne({ where: { id } });
}
findLiveRates(): Promise<Rate[]> {
const now = new Date();
return this.repo
.createQueryBuilder('rate')
.where('rate.status = :status', { status: 'LIVE' })
.andWhere('rate.effective_from <= :now', { now })
.andWhere('(rate.effective_to IS NULL OR rate.effective_to > :now)', { now })
.getMany();
}
findAll(options?: FindManyOptions<Rate>): Promise<Rate[]> {
return this.repo.find(options);
}
findAndCount(options?: FindManyOptions<Rate>): Promise<[Rate[], number]> {
return this.repo.findAndCount(options);
}
async create(data: Partial<Rate>): Promise<Rate> {
const entity = this.repo.create(data);
return this.repo.save(entity);
}
async update(id: string, data: Partial<Rate>): Promise<Rate | 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 { ShippingLine } from '../entities/shipping-line.entity';
import { IShippingLinesRepository } from '../interfaces/shipping-lines.repository.interface';
@Injectable()
export class ShippingLinesRepository implements IShippingLinesRepository {
private readonly repo: Repository<ShippingLine>;
constructor(private readonly dataSource: DataSource) {
this.repo = this.dataSource.getRepository(ShippingLine);
}
findById(id: string): Promise<ShippingLine | null> {
return this.repo.findOne({ where: { id } });
}
findByCode(code: string): Promise<ShippingLine | null> {
return this.repo.findOne({ where: { code } });
}
findAll(options?: FindManyOptions<ShippingLine>): Promise<ShippingLine[]> {
return this.repo.find(options);
}
findAndCount(options?: FindManyOptions<ShippingLine>): Promise<[ShippingLine[], number]> {
return this.repo.findAndCount(options);
}
async create(data: Partial<ShippingLine>): Promise<ShippingLine> {
const entity = this.repo.create(data);
return this.repo.save(entity);
}
async update(id: string, data: Partial<ShippingLine>): Promise<ShippingLine | null> {
await this.repo.update(id, data);
return this.findById(id);
}
async softDelete(id: string): Promise<void> {
await this.repo.softDelete(id);
}
}

View File

@@ -19,6 +19,13 @@ export class SurchargeTypesRepository implements ISurchargeTypesRepository {
return this.repo.findOne({ where: { code } });
}
findAllActiveWithRate(): Promise<SurchargeType[]> {
return this.repo.find({
where: { isActive: true },
relations: { rate: true },
});
}
findAll(options?: FindManyOptions<SurchargeType>): Promise<SurchargeType[]> {
return this.repo.find(options);
}

View File

@@ -1,46 +0,0 @@
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

@@ -14,25 +14,25 @@ export class WeightLimitRulesRepository implements IWeightLimitRulesRepository {
findById(id: string): Promise<WeightLimitRule | null> {
return this.repo.findOne({
where: { id },
relations: { containerType: true, surcharge: { surchargeType: true } },
relations: { containerType: true },
});
}
findActiveByContainerTypeAndDirection(
sizeCode: string,
findActiveByContainerTypeId(
containerTypeId: string,
tradeDirection: string,
): Promise<WeightLimitRule[]> {
const now = new Date();
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)', {
.where('rule.container_type_id = :containerTypeId', { containerTypeId })
.andWhere('(rule.trade_direction = :dir OR rule.trade_direction = :any)', {
dir: tradeDirection,
both: 'BOTH',
any: 'ANY',
})
.andWhere('rule.is_active = true')
.andWhere('rule.effective_from <= :now', { now })
.andWhere('(rule.effective_to IS NULL OR rule.effective_to > :now)', { now })
.getMany();
}

View File

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