mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-26 18:42:49 +00:00
124 lines
4.2 KiB
TypeScript
124 lines
4.2 KiB
TypeScript
import { PaginatedResponse } from '@edr/types';
|
|
import {
|
|
BadRequestException,
|
|
ConflictException,
|
|
Inject,
|
|
Injectable,
|
|
NotFoundException,
|
|
} from '@nestjs/common';
|
|
import { CreateYardDistanceDto } from '../dto/create-yard-distance.dto';
|
|
import { ListYardDistancesQueryDto } from '../dto/list-rule-engine-query.dto';
|
|
import { UpdateYardDistanceDto } from '../dto/update-yard-distance.dto';
|
|
import { YardDistance } from '../entities/yard-distance.entity';
|
|
import {
|
|
IYardDistancesRepository,
|
|
YARD_DISTANCES_REPOSITORY,
|
|
} from '../interfaces/yard-distances.repository.interface';
|
|
import { IYardsRepository, YARDS_REPOSITORY } from '../interfaces/yards.repository.interface';
|
|
|
|
/**
|
|
* Flat row shape for the backoffice config table: the yard relations stay for
|
|
* API consumers, plus label fields the generic rule-engine grid can render.
|
|
*/
|
|
export type YardDistanceRow = YardDistance & {
|
|
fromYardLabel: string;
|
|
toYardLabel: string;
|
|
};
|
|
|
|
const yardDisplay = (yard?: { label?: string; code?: string } | null): string =>
|
|
yard?.label ?? yard?.code ?? '—';
|
|
|
|
const toRow = (entity: YardDistance): YardDistanceRow =>
|
|
Object.assign(entity, {
|
|
fromYardLabel: yardDisplay(entity.fromYard),
|
|
toYardLabel: yardDisplay(entity.toYard),
|
|
});
|
|
|
|
@Injectable()
|
|
export class YardDistancesService {
|
|
constructor(
|
|
@Inject(YARD_DISTANCES_REPOSITORY)
|
|
private readonly repository: IYardDistancesRepository,
|
|
@Inject(YARDS_REPOSITORY)
|
|
private readonly yardsRepository: IYardsRepository,
|
|
) {}
|
|
|
|
async findAll(query: ListYardDistancesQueryDto): Promise<PaginatedResponse<YardDistanceRow>> {
|
|
const page = await this.repository.findPaged(query);
|
|
return { ...page, items: page.items.map(toRow) };
|
|
}
|
|
|
|
async findById(id: string): Promise<YardDistanceRow> {
|
|
const entity = await this.repository.findById(id);
|
|
if (!entity) throw new NotFoundException(`Yard distance ${id} not found`);
|
|
return toRow(entity);
|
|
}
|
|
|
|
async create(dto: CreateYardDistanceDto): Promise<YardDistanceRow> {
|
|
await this.assertValidPair(dto.fromYardId, dto.toYardId);
|
|
|
|
const created = await this.repository.create({
|
|
fromYardId: dto.fromYardId,
|
|
toYardId: dto.toYardId,
|
|
distanceKm: dto.distanceKm.toFixed(2),
|
|
standardHours: dto.standardHours != null ? dto.standardHours.toFixed(2) : null,
|
|
});
|
|
return toRow(created);
|
|
}
|
|
|
|
async update(id: string, dto: UpdateYardDistanceDto): Promise<YardDistanceRow> {
|
|
const existing = await this.findById(id);
|
|
|
|
const fromYardId = dto.fromYardId ?? existing.fromYardId;
|
|
const toYardId = dto.toYardId ?? existing.toYardId;
|
|
if (fromYardId !== existing.fromYardId || toYardId !== existing.toYardId) {
|
|
await this.assertValidPair(fromYardId, toYardId, id);
|
|
}
|
|
|
|
const updated = await this.repository.update(id, {
|
|
fromYardId,
|
|
toYardId,
|
|
...(dto.distanceKm != null ? { distanceKm: dto.distanceKm.toFixed(2) } : {}),
|
|
...(dto.standardHours !== undefined
|
|
? { standardHours: dto.standardHours != null ? dto.standardHours.toFixed(2) : null }
|
|
: {}),
|
|
});
|
|
if (!updated) throw new NotFoundException(`Yard distance ${id} not found`);
|
|
return toRow(updated);
|
|
}
|
|
|
|
async remove(id: string): Promise<void> {
|
|
await this.findById(id);
|
|
await this.repository.softDelete(id);
|
|
}
|
|
|
|
/**
|
|
* Both yards must exist and differ, and the pair must not already be
|
|
* configured in either direction — distances are symmetric, so an A→B row
|
|
* already covers B→A.
|
|
*/
|
|
private async assertValidPair(
|
|
fromYardId: string,
|
|
toYardId: string,
|
|
ignoreId?: string,
|
|
): Promise<void> {
|
|
if (fromYardId === toYardId) {
|
|
throw new BadRequestException('From and to yards must be different');
|
|
}
|
|
|
|
const [fromYard, toYard] = await Promise.all([
|
|
this.yardsRepository.findById(fromYardId),
|
|
this.yardsRepository.findById(toYardId),
|
|
]);
|
|
if (!fromYard) throw new BadRequestException(`Yard ${fromYardId} does not exist`);
|
|
if (!toYard) throw new BadRequestException(`Yard ${toYardId} does not exist`);
|
|
|
|
const existing = await this.repository.findBetween(fromYardId, toYardId);
|
|
if (existing && existing.id !== ignoreId) {
|
|
throw new ConflictException(
|
|
`A distance between ${fromYard.label} and ${toYard.label} is already configured`,
|
|
);
|
|
}
|
|
}
|
|
}
|