mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-28 21:50:57 +00:00
feat(warehouses): record container vs bulk freight type
Nullable freight_type on the warehouse, null meaning it takes both. No backfill: every existing warehouse is unrestricted today and writing a value would narrow allocation behind the operator's back. Reuses FREIGHT_TYPES from the booking entity rather than a third copy of the same two values.
This commit is contained in:
@@ -0,0 +1,21 @@
|
||||
import { MigrationInterface, QueryRunner } from 'typeorm';
|
||||
|
||||
/**
|
||||
* `freight.warehouses.freight_type` — CONTAINER or BULK, or null for a site
|
||||
* that takes both.
|
||||
*
|
||||
* Nullable with no backfill on purpose: every existing warehouse predates the
|
||||
* field and is unrestricted today, so writing a value would narrow live
|
||||
* allocation behind the operator's back.
|
||||
*/
|
||||
export class WarehouseFreightType3820000000000 implements MigrationInterface {
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE freight.warehouses ADD COLUMN IF NOT EXISTS freight_type varchar(16)`,
|
||||
);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`ALTER TABLE freight.warehouses DROP COLUMN IF EXISTS freight_type`);
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,14 @@
|
||||
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
|
||||
import { IsEnum, IsNumber, IsOptional, IsString, IsUUID, Matches, MaxLength, Min } from 'class-validator';
|
||||
|
||||
import { WAREHOUSE_STATUSES, WAREHOUSE_TYPES, WarehouseStatus, WarehouseType } from '../entities/warehouse.entity';
|
||||
import {
|
||||
FREIGHT_TYPES,
|
||||
FreightType,
|
||||
WAREHOUSE_STATUSES,
|
||||
WAREHOUSE_TYPES,
|
||||
WarehouseStatus,
|
||||
WarehouseType,
|
||||
} from '../entities/warehouse.entity';
|
||||
|
||||
export class CreateWarehouseDto {
|
||||
@ApiProperty()
|
||||
@@ -19,6 +26,11 @@ export class CreateWarehouseDto {
|
||||
@IsEnum(WAREHOUSE_TYPES)
|
||||
type!: WarehouseType;
|
||||
|
||||
@ApiPropertyOptional({ enum: FREIGHT_TYPES, description: 'Omit for a warehouse that takes both.' })
|
||||
@IsOptional()
|
||||
@IsEnum(FREIGHT_TYPES)
|
||||
freightType?: FreightType;
|
||||
|
||||
@ApiPropertyOptional({ format: 'uuid' })
|
||||
@IsOptional()
|
||||
@IsUUID()
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
import { BaseEntity } from '@edr/api-common';
|
||||
import { Column, Entity, Index, JoinColumn, ManyToOne, OneToMany } from 'typeorm';
|
||||
|
||||
import { FREIGHT_TYPES, FreightType } from '../../bookings/entities/booking.entity';
|
||||
import { Facility } from '../../facilities/entities/facility.entity';
|
||||
import { WarehouseYard } from './warehouse-yard.entity';
|
||||
|
||||
export const WAREHOUSE_TYPES = ['OPEN_WAREHOUSE', 'CLOSED_WAREHOUSE'] as const;
|
||||
export type WarehouseType = (typeof WAREHOUSE_TYPES)[number];
|
||||
|
||||
export { FREIGHT_TYPES };
|
||||
export type { FreightType };
|
||||
|
||||
export const WAREHOUSE_STATUSES = ['ACTIVE', 'INACTIVE'] as const;
|
||||
export type WarehouseStatus = (typeof WAREHOUSE_STATUSES)[number];
|
||||
|
||||
@@ -25,6 +29,14 @@ export class Warehouse extends BaseEntity {
|
||||
@Column({ name: 'type', type: 'varchar', length: 32 })
|
||||
type!: WarehouseType;
|
||||
|
||||
/**
|
||||
* What the warehouse handles. Null means unrestricted — the pre-existing
|
||||
* behaviour for every warehouse created before this field existed, so it
|
||||
* never narrows an already-configured site.
|
||||
*/
|
||||
@Column({ name: 'freight_type', type: 'varchar', length: 16, nullable: true })
|
||||
freightType?: FreightType | null;
|
||||
|
||||
@Column({ name: 'station_id', type: 'uuid', nullable: true })
|
||||
stationId?: string | null;
|
||||
|
||||
|
||||
@@ -54,6 +54,7 @@ export class WarehousesService {
|
||||
name: dto.name.trim(),
|
||||
code: dto.code.trim(),
|
||||
type: dto.type,
|
||||
freightType: dto.freightType ?? null,
|
||||
stationId: dto.stationId ?? null,
|
||||
facilityId: dto.facilityId ?? null,
|
||||
locationName: dto.locationName?.trim() ?? null,
|
||||
@@ -87,6 +88,7 @@ export class WarehousesService {
|
||||
name: dto.name?.trim() ?? existing.name,
|
||||
code: dto.code?.trim() ?? existing.code,
|
||||
type: dto.type ?? existing.type,
|
||||
freightType: dto.freightType ?? existing.freightType,
|
||||
stationId: dto.stationId ?? existing.stationId,
|
||||
facilityId: dto.facilityId ?? existing.facilityId,
|
||||
locationName: dto.locationName?.trim() ?? existing.locationName,
|
||||
|
||||
@@ -13,8 +13,19 @@ import { useMutation, useQuery } from '@tanstack/react-query';
|
||||
|
||||
import { api } from '@/services/api';
|
||||
import { useToast } from '@/hooks/use-toast';
|
||||
import type { SaveWarehousePayload, Warehouse, WarehouseType } from '@/types/warehouse';
|
||||
import { extractErrorMessage, lettersOnly, statusOptions, warehouseTypeOptions } from './options';
|
||||
import type {
|
||||
SaveWarehousePayload,
|
||||
Warehouse,
|
||||
WarehouseFreightType,
|
||||
WarehouseType,
|
||||
} from '@/types/warehouse';
|
||||
import {
|
||||
extractErrorMessage,
|
||||
lettersOnly,
|
||||
statusOptions,
|
||||
warehouseFreightTypeOptions,
|
||||
warehouseTypeOptions,
|
||||
} from './options';
|
||||
|
||||
interface CreateWarehouseModalProps {
|
||||
opened: boolean;
|
||||
@@ -26,6 +37,7 @@ interface FormState {
|
||||
name: string;
|
||||
code: string;
|
||||
type: WarehouseType;
|
||||
freightType: WarehouseFreightType | null;
|
||||
stationId: string | null;
|
||||
locationName: string;
|
||||
capacityWeight: number | '';
|
||||
@@ -38,6 +50,7 @@ const emptyForm = (): FormState => ({
|
||||
name: '',
|
||||
code: '',
|
||||
type: 'OPEN_WAREHOUSE',
|
||||
freightType: null,
|
||||
stationId: null,
|
||||
locationName: '',
|
||||
capacityWeight: '',
|
||||
@@ -66,6 +79,7 @@ export function CreateWarehouseModal({ opened, onClose, warehouse }: CreateWareh
|
||||
name: warehouse.name,
|
||||
code: warehouse.code,
|
||||
type: warehouse.type,
|
||||
freightType: warehouse.freightType ?? null,
|
||||
stationId: warehouse.stationId ?? null,
|
||||
locationName: warehouse.locationName ?? '',
|
||||
capacityWeight: warehouse.capacityWeight ?? '',
|
||||
@@ -90,6 +104,7 @@ export function CreateWarehouseModal({ opened, onClose, warehouse }: CreateWareh
|
||||
name: form.name.trim(),
|
||||
code: form.code.trim(),
|
||||
type: form.type,
|
||||
freightType: form.freightType,
|
||||
stationId: form.stationId ?? undefined,
|
||||
locationName: form.locationName.trim() || undefined,
|
||||
capacityWeight: form.capacityWeight === '' ? undefined : Number(form.capacityWeight),
|
||||
@@ -142,6 +157,14 @@ export function CreateWarehouseModal({ opened, onClose, warehouse }: CreateWareh
|
||||
/>
|
||||
|
||||
<Group grow>
|
||||
<Select
|
||||
label="Freight type"
|
||||
placeholder="Both"
|
||||
data={warehouseFreightTypeOptions}
|
||||
value={form.freightType}
|
||||
onChange={(value) => setForm((f) => ({ ...f, freightType: value as WarehouseFreightType | null }))}
|
||||
clearable
|
||||
/>
|
||||
<Select
|
||||
label="Type"
|
||||
data={warehouseTypeOptions}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import {
|
||||
WAREHOUSE_FREIGHT_TYPES,
|
||||
WAREHOUSE_TYPES,
|
||||
WAREHOUSE_YARD_TYPES,
|
||||
WAREHOUSE_ZONE_TYPES,
|
||||
@@ -73,6 +74,7 @@ export const yardsForBooking = (
|
||||
};
|
||||
|
||||
export const warehouseTypeOptions = toOptions(WAREHOUSE_TYPES);
|
||||
export const warehouseFreightTypeOptions = toOptions(WAREHOUSE_FREIGHT_TYPES);
|
||||
export const yardTypeOptions = toOptions(WAREHOUSE_YARD_TYPES);
|
||||
export const zoneTypeOptions = toOptions(WAREHOUSE_ZONE_TYPES);
|
||||
export const statusOptions = toOptions(WAREHOUSE_STATUSES);
|
||||
|
||||
@@ -180,11 +180,16 @@ export interface Facility {
|
||||
|
||||
export type WarehouseFacility = Facility;
|
||||
|
||||
/** What a warehouse handles. Null = both. */
|
||||
export const WAREHOUSE_FREIGHT_TYPES = ['CONTAINER', 'BULK'] as const;
|
||||
export type WarehouseFreightType = (typeof WAREHOUSE_FREIGHT_TYPES)[number];
|
||||
|
||||
export interface Warehouse {
|
||||
id: string;
|
||||
name: string;
|
||||
code: string;
|
||||
type: WarehouseType;
|
||||
freightType: WarehouseFreightType | null;
|
||||
stationId: string | null;
|
||||
facilityId: string | null;
|
||||
facility?: Facility | null;
|
||||
@@ -1043,6 +1048,7 @@ export interface SaveWarehousePayload {
|
||||
name: string;
|
||||
code: string;
|
||||
type: WarehouseType;
|
||||
freightType?: WarehouseFreightType | null;
|
||||
stationId?: string;
|
||||
facilityId?: string;
|
||||
locationName?: string;
|
||||
|
||||
Reference in New Issue
Block a user