mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-28 17:10:56 +00:00
feat(intercity): raise a GRN when a facility loads or unloads cargo
Every facility raises a GRN — the goods changed hands, whether or not anyone stores them. What differs is what happens next: Indode has a warehouse, so cargo left there goes through the existing warehouse flow and accrues storage and demurrage; Sebeta, Modjo, Adama and Dire Dawa only move cargo between train and truck, so the handling event and its GRN are the whole record. facility_handling_events carries that record because warehouse_inventory cannot: its warehouse/yard/zone are NOT NULL, so a facility with equipment but no warehouse could never have a row there. inventory_id links the storage record when the facility does keep the cargo, which is what ties an Indode handover to its demurrage. generateGrnNumber moves to common/grn.util.ts so a GRN raised at a facility is indistinguishable from one raised in a warehouse — the two live in different tables, and a second generator would let the formats drift. Recording is best-effort: the cargo moved regardless, so paperwork must never fail the journey. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,56 @@
|
||||
import { BaseEntity } from '@edr/api-common';
|
||||
import { Column, Entity, Index } from 'typeorm';
|
||||
|
||||
export const FACILITY_HANDLING_EVENT_TYPES = ['LOAD', 'UNLOAD'] as const;
|
||||
export type FacilityHandlingEventType = (typeof FACILITY_HANDLING_EVENT_TYPES)[number];
|
||||
|
||||
/**
|
||||
* Cargo loaded onto or unloaded off a train at a yard's facility, and the GRN
|
||||
* raised for it.
|
||||
*
|
||||
* This exists because warehouse_inventory can't do the job: its
|
||||
* warehouse/yard/zone are NOT NULL, so a facility that only has equipment and no
|
||||
* warehouse (Sebeta, Modjo, Adama, Dire Dawa) could never have a row there —
|
||||
* yet it still hands cargo over and still needs a GRN.
|
||||
*
|
||||
* `inventoryId` links to the warehouse record when the facility does store cargo
|
||||
* (Indode), which is what makes storage and demurrage accrue there and nowhere
|
||||
* else.
|
||||
*/
|
||||
@Entity({ schema: 'freight', name: 'facility_handling_events' })
|
||||
@Index(['bookingId'])
|
||||
@Index(['yardId'])
|
||||
export class FacilityHandlingEvent extends BaseEntity {
|
||||
@Column({ name: 'booking_id', type: 'uuid' })
|
||||
bookingId!: string;
|
||||
|
||||
/** The facility yard where the cargo was handled. */
|
||||
@Column({ name: 'yard_id', type: 'uuid' })
|
||||
yardId!: string;
|
||||
|
||||
/** The train the cargo came off / went onto. */
|
||||
@Column({ name: 'train_schedule_id', type: 'uuid', nullable: true })
|
||||
trainScheduleId?: string | null;
|
||||
|
||||
@Column({ name: 'event_type', type: 'varchar', length: 10 })
|
||||
eventType!: FacilityHandlingEventType;
|
||||
|
||||
@Column({ name: 'grn_number', type: 'varchar', length: 60, nullable: true })
|
||||
grnNumber?: string | null;
|
||||
|
||||
@Column({ name: 'quantity', type: 'numeric', precision: 14, scale: 3, nullable: true })
|
||||
quantity?: number | null;
|
||||
|
||||
@Column({ name: 'weight_tons', type: 'numeric', precision: 14, scale: 3, nullable: true })
|
||||
weightTons?: number | null;
|
||||
|
||||
/** Set only when the facility stores cargo (has_warehouse) — the storage record. */
|
||||
@Column({ name: 'inventory_id', type: 'uuid', nullable: true })
|
||||
inventoryId?: string | null;
|
||||
|
||||
@Column({ name: 'performed_by', type: 'varchar', length: 120, nullable: true })
|
||||
performedBy?: string | null;
|
||||
|
||||
@Column({ name: 'occurred_at', type: 'timestamptz', default: () => 'now()' })
|
||||
occurredAt!: Date;
|
||||
}
|
||||
Reference in New Issue
Block a user