mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-29 04:50:54 +00:00
per-user trade-direction access scope
This commit is contained in:
@@ -37,6 +37,27 @@ export class YardFacility extends BaseEntity {
|
||||
@Column({ name: 'handles_bulk', type: 'boolean', default: true })
|
||||
handlesBulk!: boolean;
|
||||
|
||||
/**
|
||||
* Per-side capability. Loading a type onto a train and receiving it off one
|
||||
* need different ground: a facility can be equipped to send containers but
|
||||
* have no space to stage arriving ones. `handles_container` / `handles_bulk`
|
||||
* stay the coarse "is this type handled here at all" switch; these four say
|
||||
* on which side. A yard is offered as a contract origin for a freight type
|
||||
* when it handles the type AND the matching `_origin` flag is set, and as a
|
||||
* destination on the same rule with `_destination`.
|
||||
*/
|
||||
@Column({ name: 'has_container_facility_origin', type: 'boolean', default: false })
|
||||
hasContainerFacilityOrigin!: boolean;
|
||||
|
||||
@Column({ name: 'has_bulk_facility_origin', type: 'boolean', default: false })
|
||||
hasBulkFacilityOrigin!: boolean;
|
||||
|
||||
@Column({ name: 'has_container_facility_destination', type: 'boolean', default: false })
|
||||
hasContainerFacilityDestination!: boolean;
|
||||
|
||||
@Column({ name: 'has_bulk_facility_destination', type: 'boolean', default: false })
|
||||
hasBulkFacilityDestination!: boolean;
|
||||
|
||||
@Column({ name: 'equipment_notes', type: 'text', nullable: true })
|
||||
equipmentNotes?: string | null;
|
||||
|
||||
|
||||
@@ -13,8 +13,20 @@ export interface YardFacilityInfo {
|
||||
/** Containers need a reach stacker/gantry — not every facility has one. */
|
||||
handlesContainer: boolean;
|
||||
handlesBulk: boolean;
|
||||
/**
|
||||
* The same capability split by side of the trip — loading onto a train and
|
||||
* receiving off one need different ground. Always false where the coarse
|
||||
* `handles*` flag for that type is false.
|
||||
*/
|
||||
hasContainerFacilityOrigin: boolean;
|
||||
hasBulkFacilityOrigin: boolean;
|
||||
hasContainerFacilityDestination: boolean;
|
||||
hasBulkFacilityDestination: boolean;
|
||||
}
|
||||
|
||||
/** Which side of the trip a yard is being considered for. */
|
||||
export type YardSide = 'ORIGIN' | 'DESTINATION';
|
||||
|
||||
/**
|
||||
* Which yards can handle cargo, and what kind.
|
||||
*
|
||||
@@ -38,7 +50,11 @@ export class YardFacilitiesService {
|
||||
y.has_facility AS "hasFacility",
|
||||
f.has_warehouse AS "hasWarehouse",
|
||||
f.handles_container AS "handlesContainer",
|
||||
f.handles_bulk AS "handlesBulk"
|
||||
f.handles_bulk AS "handlesBulk",
|
||||
f.has_container_facility_origin AS "hasContainerFacilityOrigin",
|
||||
f.has_bulk_facility_origin AS "hasBulkFacilityOrigin",
|
||||
f.has_container_facility_destination AS "hasContainerFacilityDestination",
|
||||
f.has_bulk_facility_destination AS "hasBulkFacilityDestination"
|
||||
FROM freight.yards y
|
||||
LEFT JOIN freight.yard_facilities f
|
||||
ON f.yard_id = y.id AND f.deleted_at IS NULL AND f.is_active = true`;
|
||||
@@ -51,17 +67,33 @@ export class YardFacilitiesService {
|
||||
hasWarehouse: boolean | null;
|
||||
handlesContainer: boolean | null;
|
||||
handlesBulk: boolean | null;
|
||||
hasContainerFacilityOrigin: boolean | null;
|
||||
hasBulkFacilityOrigin: boolean | null;
|
||||
hasContainerFacilityDestination: boolean | null;
|
||||
hasBulkFacilityDestination: boolean | null;
|
||||
}): YardFacilityInfo {
|
||||
// No facility record means no capability, whatever the flag says.
|
||||
const hasFacility = Boolean(row.hasFacility);
|
||||
const handlesContainer = hasFacility && Boolean(row.handlesContainer);
|
||||
const handlesBulk = hasFacility && Boolean(row.handlesBulk);
|
||||
return {
|
||||
yardId: row.yardId,
|
||||
yardCode: row.yardCode,
|
||||
yardLabel: row.yardLabel,
|
||||
hasFacility,
|
||||
hasWarehouse: hasFacility && Boolean(row.hasWarehouse),
|
||||
handlesContainer: hasFacility && Boolean(row.handlesContainer),
|
||||
handlesBulk: hasFacility && Boolean(row.handlesBulk),
|
||||
handlesContainer,
|
||||
handlesBulk,
|
||||
// Gated on the coarse flag so the two can't contradict each other: a
|
||||
// per-side flag left set on a type the facility no longer handles at all
|
||||
// never resurrects that type.
|
||||
hasContainerFacilityOrigin:
|
||||
handlesContainer && Boolean(row.hasContainerFacilityOrigin),
|
||||
hasBulkFacilityOrigin: handlesBulk && Boolean(row.hasBulkFacilityOrigin),
|
||||
hasContainerFacilityDestination:
|
||||
handlesContainer && Boolean(row.hasContainerFacilityDestination),
|
||||
hasBulkFacilityDestination:
|
||||
handlesBulk && Boolean(row.hasBulkFacilityDestination),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -97,4 +129,26 @@ export class YardFacilitiesService {
|
||||
? facility.handlesContainer
|
||||
: facility.handlesBulk;
|
||||
}
|
||||
|
||||
/**
|
||||
* Can this facility take this cargo on this side of the trip? The rule behind
|
||||
* the contract's origin/destination yard pickers — keep it here so the API
|
||||
* and the forms can't drift apart on what is offerable.
|
||||
*/
|
||||
canHandleFreightOnSide(
|
||||
facility: YardFacilityInfo | null,
|
||||
freightType: string | null | undefined,
|
||||
side: YardSide,
|
||||
): boolean {
|
||||
if (!facility?.hasFacility) return false;
|
||||
const isContainer = String(freightType).toUpperCase() === 'CONTAINER';
|
||||
if (side === 'ORIGIN') {
|
||||
return isContainer
|
||||
? facility.hasContainerFacilityOrigin
|
||||
: facility.hasBulkFacilityOrigin;
|
||||
}
|
||||
return isContainer
|
||||
? facility.hasContainerFacilityDestination
|
||||
: facility.hasBulkFacilityDestination;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user