mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-30 04:08:11 +00:00
update locaomotive and fix wagon transfer issue
This commit is contained in:
@@ -1,7 +1,17 @@
|
||||
import { WagonStatus } from '@edr/types';
|
||||
import { ApiPropertyOptional } from '@nestjs/swagger';
|
||||
import { Type } from 'class-transformer';
|
||||
import { IsEnum, IsInt, IsOptional, IsString, IsUUID, Max, Min } from 'class-validator';
|
||||
import { Transform, Type } from 'class-transformer';
|
||||
import {
|
||||
IsBoolean,
|
||||
IsDateString,
|
||||
IsEnum,
|
||||
IsInt,
|
||||
IsOptional,
|
||||
IsString,
|
||||
IsUUID,
|
||||
Max,
|
||||
Min,
|
||||
} from 'class-validator';
|
||||
|
||||
export class ListWagonsQueryDto {
|
||||
@ApiPropertyOptional({ description: 'Search wagon number (partial match)' })
|
||||
@@ -29,6 +39,15 @@ export class ListWagonsQueryDto {
|
||||
@IsUUID()
|
||||
trainId?: string;
|
||||
|
||||
@ApiPropertyOptional({
|
||||
description:
|
||||
'Only loose wagons (not coupled to a built train) — what a picker can actually take.',
|
||||
})
|
||||
@IsOptional()
|
||||
@Transform(({ value }: { value: unknown }) => value === true || value === 'true')
|
||||
@IsBoolean()
|
||||
unassigned?: boolean;
|
||||
|
||||
@ApiPropertyOptional({
|
||||
description: 'Filter by run number — matches export OR import run (e.g. 8001).',
|
||||
})
|
||||
@@ -60,4 +79,23 @@ export class ListWagonsQueryDto {
|
||||
@Min(1)
|
||||
@Max(500)
|
||||
limit?: number;
|
||||
|
||||
/** Page size for `GET /wagons/paged`; the legacy `limit` still drives `GET /wagons`. */
|
||||
@ApiPropertyOptional({ default: 20, minimum: 1, maximum: 100 })
|
||||
@IsOptional()
|
||||
@Type(() => Number)
|
||||
@IsInt()
|
||||
@Min(1)
|
||||
@Max(100)
|
||||
pageSize?: number;
|
||||
|
||||
@ApiPropertyOptional({ description: 'Registered on or after this day (YYYY-MM-DD)' })
|
||||
@IsOptional()
|
||||
@IsDateString()
|
||||
createdFrom?: string;
|
||||
|
||||
@ApiPropertyOptional({ description: 'Registered on or before this day (YYYY-MM-DD)' })
|
||||
@IsOptional()
|
||||
@IsDateString()
|
||||
createdTo?: string;
|
||||
}
|
||||
|
||||
@@ -114,6 +114,8 @@ export class WagonTransferRequestsService {
|
||||
currentYardId: yardId,
|
||||
wagonTypeId,
|
||||
status: WagonStatus.Available,
|
||||
// Coupled to a built train = not movable; bulkTransfer rejects it too.
|
||||
trainId: IsNull(),
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -399,6 +401,7 @@ export class WagonTransferRequestsService {
|
||||
currentYardId: request.fromYardId,
|
||||
wagonTypeId: request.wagonTypeId,
|
||||
status: WagonStatus.Available,
|
||||
trainId: IsNull(),
|
||||
},
|
||||
order: { wagonNumber: 'ASC' },
|
||||
take: remaining,
|
||||
|
||||
@@ -44,6 +44,14 @@ export class WagonsController {
|
||||
return this.wagonsService.findAll(query);
|
||||
}
|
||||
|
||||
// Must be declared before @Get(':id') so the path isn't captured as an id.
|
||||
@Get('paged')
|
||||
@StaffReference()
|
||||
@ApiOperation({ summary: 'List wagons, paginated ({items, meta})' })
|
||||
findAllPaged(@Query() query: ListWagonsQueryDto) {
|
||||
return this.wagonsService.findAllPaged(query);
|
||||
}
|
||||
|
||||
@Get(':id')
|
||||
@StaffReference()
|
||||
@ApiOperation({ summary: 'Get a wagon by ID' })
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Freight, WagonMovementKind, WagonStatus } from '@edr/types';
|
||||
import { Freight, PaginatedResponse, WagonMovementKind, WagonStatus } from '@edr/types';
|
||||
import {
|
||||
BadRequestException,
|
||||
Injectable,
|
||||
@@ -6,7 +6,8 @@ import {
|
||||
ConflictException,
|
||||
} from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { Repository, DataSource, In } from 'typeorm';
|
||||
import { Repository, DataSource, In, SelectQueryBuilder } from 'typeorm';
|
||||
import { paginateQuery } from '../../common/utils/pagination.util';
|
||||
import { CreateWagonDto } from './dto/create-wagon.dto';
|
||||
import { ListWagonsQueryDto } from './dto/list-wagons-query.dto';
|
||||
import { UpdateWagonDto } from './dto/update-wagon.dto';
|
||||
@@ -42,7 +43,8 @@ export class WagonsService {
|
||||
return this.wagonRepo.save(wagon);
|
||||
}
|
||||
|
||||
async findAll(query: ListWagonsQueryDto = {}): Promise<Wagon[]> {
|
||||
/** Shared filter/sort builder behind `findAll` (array) and `findAllPaged` (envelope). */
|
||||
private buildListQuery(query: ListWagonsQueryDto): SelectQueryBuilder<Wagon> {
|
||||
const search = query.search?.trim();
|
||||
const trainId = query.trainId?.trim();
|
||||
const wagonTypeId = query.wagonTypeId?.trim();
|
||||
@@ -61,6 +63,9 @@ export class WagonsService {
|
||||
if (query.currentYardId)
|
||||
qb.andWhere('w.currentYardId = :currentYardId', { currentYardId: query.currentYardId });
|
||||
if (trainId) qb.andWhere('w.trainId = :trainId', { trainId });
|
||||
// Pickers (train-builder, transfer fulfilment) can only take a wagon that is
|
||||
// not already coupled to a built train — never offer one the API will reject.
|
||||
if (query.unassigned) qb.andWhere('w.trainId IS NULL');
|
||||
if (wagonTypeId) qb.andWhere('w.wagonTypeId = :wagonTypeId', { wagonTypeId });
|
||||
|
||||
// Filter by run: the odd export run identifies the pair, so match either
|
||||
@@ -72,6 +77,18 @@ export class WagonsService {
|
||||
);
|
||||
}
|
||||
|
||||
// Registration-day range, both ends inclusive (the UI picks whole days).
|
||||
if (query.createdFrom) {
|
||||
qb.andWhere('w.createdAt >= CAST(:createdFrom AS date)', {
|
||||
createdFrom: query.createdFrom,
|
||||
});
|
||||
}
|
||||
if (query.createdTo) {
|
||||
qb.andWhere("w.createdAt < CAST(:createdTo AS date) + INTERVAL '1 day'", {
|
||||
createdTo: query.createdTo,
|
||||
});
|
||||
}
|
||||
|
||||
// Search matches the wagon number or either run number.
|
||||
if (search) {
|
||||
qb.andWhere(
|
||||
@@ -95,6 +112,12 @@ export class WagonsService {
|
||||
const sortOrder = query.sortOrder?.toUpperCase() === 'DESC' ? 'DESC' : 'ASC';
|
||||
qb.orderBy(`w.${sortBy}`, sortOrder);
|
||||
|
||||
return qb;
|
||||
}
|
||||
|
||||
async findAll(query: ListWagonsQueryDto = {}): Promise<Wagon[]> {
|
||||
const qb = this.buildListQuery(query);
|
||||
|
||||
if (query.page && query.limit) {
|
||||
qb.skip((Number(query.page) - 1) * Number(query.limit));
|
||||
}
|
||||
@@ -103,6 +126,11 @@ export class WagonsService {
|
||||
return qb.getMany();
|
||||
}
|
||||
|
||||
/** Same filters as `findAll`, on the shared `{items, meta}` list envelope. */
|
||||
findAllPaged(query: ListWagonsQueryDto = {}): Promise<PaginatedResponse<Wagon>> {
|
||||
return paginateQuery(this.buildListQuery(query), query);
|
||||
}
|
||||
|
||||
async findById(id: string): Promise<Wagon> {
|
||||
const wagon = await this.wagonRepo.findOne({
|
||||
where: { id },
|
||||
|
||||
Reference in New Issue
Block a user