feat: enhance locomotive creation and management with optional code generation and default max pull weight

This commit is contained in:
Marshal
2026-06-28 23:02:40 +00:00
parent d6ae1ac18d
commit 47c91cad03
9 changed files with 66 additions and 80 deletions

View File

@@ -8,10 +8,13 @@ import {
} from '../entities/locomotive.entity';
export class CreateLocomotiveDto {
@ApiProperty({ example: 'LOCO-001' })
// Optional on input — the service auto-generates a sequential LOCO-NNN code
// when none is supplied.
@ApiPropertyOptional({ example: 'LOCO-001' })
@IsOptional()
@IsString()
@MaxLength(32)
code!: string;
code?: string;
@ApiPropertyOptional()
@IsOptional()
@@ -32,11 +35,13 @@ export class CreateLocomotiveDto {
@IsUUID()
currentYardId?: string;
@ApiProperty({ example: 3500 })
@Transform(({ value }) => Number(value))
// Defaults to 2500 tons when omitted (see service).
@ApiPropertyOptional({ example: 2500, default: 2500 })
@IsOptional()
@Transform(({ value }) => (value === '' || value == null ? undefined : Number(value)))
@IsNumber()
@Min(0)
maxPullWeightTons!: number;
maxPullWeightTons?: number;
@ApiProperty({ example: 760 })
@Transform(({ value }) => Number(value))

View File

@@ -29,20 +29,40 @@ export class LocomotivesService {
});
}
async create(dto: CreateLocomotiveDto): Promise<Locomotive> {
const [existing] = await this.locomotivesRepository.findAll({ where: { code: dto.code } });
/** Default max pull weight (tons) applied when the caller omits it. */
private static readonly DEFAULT_MAX_PULL_WEIGHT_TONS = 2500;
/**
* Generate the next sequential locomotive code (LOCO-001, LOCO-002, …) by
* scanning the highest existing LOCO-NNN number. Used when the caller does not
* supply a code.
*/
private async generateCode(): Promise<string> {
const all = await this.locomotivesRepository.findAll({});
let max = 0;
for (const loco of all) {
const match = /^LOCO-(\d+)$/.exec(loco.code ?? '');
if (match) max = Math.max(max, Number(match[1]));
}
return `LOCO-${String(max + 1).padStart(3, '0')}`;
}
async create(dto: CreateLocomotiveDto): Promise<Locomotive> {
const code = dto.code?.trim() || (await this.generateCode());
const [existing] = await this.locomotivesRepository.findAll({ where: { code } });
if (existing) {
throw new ConflictException(`Locomotive code ${dto.code} already exists`);
throw new ConflictException(`Locomotive code ${code} already exists`);
}
return this.locomotivesRepository.create({
code: dto.code,
code,
name: dto.name?.trim() || null,
locomotiveType: dto.locomotiveType as LocomotiveType,
status: dto.status as LocomotiveStatus,
currentYardId: dto.currentYardId ?? null,
maxPullWeightTons: dto.maxPullWeightTons,
maxPullWeightTons:
dto.maxPullWeightTons ?? LocomotivesService.DEFAULT_MAX_PULL_WEIGHT_TONS,
maxTrainLengthMeters: dto.maxTrainLengthMeters,
powerKw: dto.powerKw ?? null,
tractionForceKn: dto.tractionForceKn ?? null,

View File

@@ -77,7 +77,10 @@ export class WagonsService {
async update(id: string, dto: UpdateWagonDto): Promise<Wagon> {
const wagon = await this.findById(id);
Object.assign(wagon, dto);
return this.wagonRepo.save(wagon);
await this.wagonRepo.save(wagon);
// Re-read with the relation so the response reflects the new yard label
// instead of the stale relation object loaded before the assign.
return this.findById(id);
}
async remove(id: string): Promise<void> {