mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-30 15:48:11 +00:00
complete rule engine and booking flow
This commit is contained in:
@@ -0,0 +1,23 @@
|
||||
import { BaseEntity } from '@edr/api-common';
|
||||
import { Column, Entity, Index, Unique } from 'typeorm';
|
||||
|
||||
@Entity({ schema: 'freight', name: 'approval_rules' })
|
||||
@Unique(['requiresDirectorApproval', 'stepOrder'])
|
||||
@Index(['requiresDirectorApproval'])
|
||||
@Index(['stepOrder'])
|
||||
export class ApprovalRule extends BaseEntity {
|
||||
@Column({ name: 'requires_director_approval', type: 'boolean' })
|
||||
requiresDirectorApproval!: boolean;
|
||||
|
||||
@Column({ name: 'step_order', type: 'smallint' })
|
||||
stepOrder!: number;
|
||||
|
||||
@Column({ name: 'required_role', type: 'varchar', length: 30 })
|
||||
requiredRole!: string;
|
||||
|
||||
@Column({ name: 'action_label', type: 'varchar', length: 50 })
|
||||
actionLabel!: string;
|
||||
|
||||
@Column({ name: 'blocks_role', type: 'varchar', length: 30, nullable: true })
|
||||
blocksRole?: string | null;
|
||||
}
|
||||
@@ -3,21 +3,33 @@ import { Column, Entity, Index, OneToMany } from 'typeorm';
|
||||
import { WeightLimitRule } from './weight-limit-rule.entity';
|
||||
|
||||
@Entity({ schema: 'freight', name: 'container_types' })
|
||||
@Index(['sizeCode'])
|
||||
@Index(['code'])
|
||||
@Index(['isActive'])
|
||||
export class ContainerType extends BaseEntity {
|
||||
@Column({ name: 'size_code', type: 'varchar', length: 20, unique: true })
|
||||
sizeCode!: string;
|
||||
@Column({ name: 'code', type: 'varchar', length: 20, unique: true })
|
||||
code!: string;
|
||||
|
||||
@Column({ name: 'description', type: 'varchar', length: 100, nullable: true })
|
||||
description?: string | null;
|
||||
@Column({ name: 'label', type: 'varchar', length: 100, nullable: true })
|
||||
label!: string;
|
||||
|
||||
@Column({ name: 'containers_per_wagon', type: 'int' })
|
||||
containersPerWagon!: number;
|
||||
@Column({ name: 'size_ft', type: 'smallint', nullable: true })
|
||||
sizeFt!: number;
|
||||
|
||||
@Column({ name: 'wagons_per_unit', type: 'numeric', precision: 4, scale: 2, nullable: true })
|
||||
wagonsPerUnit!: number;
|
||||
|
||||
@Column({ name: 'is_reefer', type: 'boolean', default: false, nullable: true })
|
||||
isReefer!: boolean;
|
||||
|
||||
@Column({ name: 'is_open_top', type: 'boolean', default: false, nullable: true })
|
||||
isOpenTop!: boolean;
|
||||
|
||||
@Column({ name: 'is_active', type: 'boolean', default: true })
|
||||
isActive!: boolean;
|
||||
|
||||
@Column({ name: 'display_order', type: 'int', default: 1, nullable: true })
|
||||
displayOrder!: number;
|
||||
|
||||
@OneToMany(() => WeightLimitRule, (rule) => rule.containerType)
|
||||
weightLimitRules?: WeightLimitRule[];
|
||||
}
|
||||
|
||||
@@ -1,25 +1,21 @@
|
||||
import { BaseEntity } from '@edr/api-common';
|
||||
import { Freight } from '@edr/types';
|
||||
import { Column, Entity, Index } from 'typeorm';
|
||||
|
||||
@Entity({ schema: 'freight', name: 'priority_rules' })
|
||||
@Index(['priorityType'])
|
||||
@Index(['code'])
|
||||
@Index(['isActive'])
|
||||
export class PriorityRule extends BaseEntity {
|
||||
@Column({ name: 'priority_type', type: 'enum', enum: Freight.PriorityType, unique: true })
|
||||
priorityType!: Freight.PriorityType;
|
||||
@Column({ name: 'code', type: 'varchar', length: 40, unique: true })
|
||||
code!: string;
|
||||
|
||||
@Column({ name: 'rule_name', type: 'varchar', length: 255 })
|
||||
ruleName!: string;
|
||||
@Column({ name: 'label', type: 'varchar', length: 100, nullable: true })
|
||||
label!: string;
|
||||
|
||||
@Column({ name: 'description', type: 'text', nullable: true })
|
||||
description?: string | null;
|
||||
@Column({ name: 'score', type: 'int', default: 0, nullable: true })
|
||||
score!: number;
|
||||
|
||||
@Column({ name: 'activation_condition', type: 'text', nullable: true })
|
||||
activationCondition?: string | null;
|
||||
|
||||
@Column({ name: 'bonus_points', type: 'int', default: 0 })
|
||||
bonusPoints!: number;
|
||||
@Column({ name: 'condition_currency', type: 'varchar', length: 5, nullable: true })
|
||||
conditionCurrency?: string | null;
|
||||
|
||||
@Column({ name: 'is_active', type: 'boolean', default: false })
|
||||
isActive!: boolean;
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
import { BaseEntity } from '@edr/api-common';
|
||||
import { Column, Entity, Index, JoinColumn, ManyToOne } from 'typeorm';
|
||||
import { ContainerType } from './container-type.entity';
|
||||
|
||||
export const RATE_TYPES = [
|
||||
'CONTAINER_IMPORT',
|
||||
'CONTAINER_EXPORT',
|
||||
'BULK_IMPORT',
|
||||
'BULK_EXPORT',
|
||||
'INTERCITY_BULK',
|
||||
'INTERCITY_CONTAINER',
|
||||
'FIRST_MILE',
|
||||
'LAST_MILE',
|
||||
'DEMURRAGE',
|
||||
'LASHING',
|
||||
'DOUBLE_HANDLING',
|
||||
'CONTAINER_WITH_RETURN',
|
||||
'CANCELLATION_FEE',
|
||||
'OVERWEIGHT_PER_TON',
|
||||
'HAZARD_SURCHARGE',
|
||||
'REEFER_SURCHARGE',
|
||||
'PIL_EXTRA_FEE',
|
||||
] as const;
|
||||
|
||||
export type RateType = typeof RATE_TYPES[number];
|
||||
|
||||
export const RATE_STATUSES = ['DRAFT', 'PENDING_APPROVAL', 'LIVE', 'SUPERSEDED'] as const;
|
||||
export type RateStatus = typeof RATE_STATUSES[number];
|
||||
|
||||
export const RATE_UNITS = ['PER_WAGON', 'PER_TON', 'PER_CONTAINER', 'PER_KM', 'FLAT'] as const;
|
||||
export type RateUnit = typeof RATE_UNITS[number];
|
||||
|
||||
@Entity({ schema: 'freight', name: 'rates' })
|
||||
@Index(['rateType'])
|
||||
@Index(['status'])
|
||||
@Index(['effectiveFrom'])
|
||||
@Index(['containerTypeId'])
|
||||
export class Rate extends BaseEntity {
|
||||
@Column({ name: 'rate_type', type: 'varchar', length: 50 })
|
||||
rateType!: RateType;
|
||||
|
||||
@Column({ name: 'container_type_id', type: 'uuid', nullable: true })
|
||||
containerTypeId?: string | null;
|
||||
|
||||
@ManyToOne(() => ContainerType, { nullable: true, eager: false })
|
||||
@JoinColumn({ name: 'container_type_id' })
|
||||
containerType?: ContainerType | null;
|
||||
|
||||
@Column({ name: 'trade_direction', type: 'varchar', length: 10, nullable: true })
|
||||
tradeDirection?: string | null;
|
||||
|
||||
@Column({ name: 'currency', type: 'varchar', length: 5 })
|
||||
currency!: string;
|
||||
|
||||
@Column({ name: 'rate_value', type: 'numeric', precision: 14, scale: 4 })
|
||||
rateValue!: number;
|
||||
|
||||
@Column({ name: 'rate_unit', type: 'varchar', length: 30 })
|
||||
rateUnit!: RateUnit;
|
||||
|
||||
@Column({ name: 'status', type: 'varchar', length: 20, default: 'DRAFT' })
|
||||
status!: RateStatus;
|
||||
|
||||
@Column({ name: 'proposed_by_staff_id', type: 'uuid' })
|
||||
proposedByStaffId!: string;
|
||||
|
||||
@Column({ name: 'approved_by_ceo_id', type: 'uuid', nullable: true })
|
||||
approvedByCeoId?: string | null;
|
||||
|
||||
@Column({ name: 'approved_at', type: 'timestamptz', nullable: true })
|
||||
approvedAt?: Date | null;
|
||||
|
||||
@Column({ name: 'effective_from', type: 'date' })
|
||||
effectiveFrom!: Date;
|
||||
|
||||
@Column({ name: 'effective_to', type: 'date', nullable: true })
|
||||
effectiveTo?: Date | null;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
import { BaseEntity } from '@edr/api-common';
|
||||
import { Column, Entity, Index } from 'typeorm';
|
||||
|
||||
@Entity({ schema: 'freight', name: 'shipping_lines' })
|
||||
@Index(['code'])
|
||||
@Index(['isActive'])
|
||||
export class ShippingLine extends BaseEntity {
|
||||
@Column({ name: 'code', type: 'varchar', length: 20, unique: true })
|
||||
code!: string;
|
||||
|
||||
@Column({ name: 'label', type: 'varchar', length: 100 })
|
||||
label!: string;
|
||||
|
||||
@Column({ name: 'mapped_to_code', type: 'varchar', length: 20, nullable: true })
|
||||
mappedToCode?: string | null;
|
||||
|
||||
@Column({ name: 'show_extra_fee_notice', type: 'boolean', default: false })
|
||||
showExtraFeeNotice!: boolean;
|
||||
|
||||
@Column({ name: 'is_active', type: 'boolean', default: true })
|
||||
isActive!: boolean;
|
||||
}
|
||||
@@ -1,23 +1,38 @@
|
||||
import { BaseEntity } from '@edr/api-common';
|
||||
import { Column, Entity, Index, OneToMany } from 'typeorm';
|
||||
import { Surcharge } from './surcharge.entity';
|
||||
import { Column, Entity, Index, JoinColumn, ManyToOne } from 'typeorm';
|
||||
import { Rate } from './rate.entity';
|
||||
|
||||
const TRIGGER_CONDITIONS = [
|
||||
'CARGO_FLAG_HAZARDOUS',
|
||||
'CARGO_FLAG_REEFER',
|
||||
'VGM_EXCEEDS_LIMIT',
|
||||
'SHIPPING_LINE_MAPPED',
|
||||
'CONSOLIDATION_ENABLED',
|
||||
] as const;
|
||||
|
||||
export type TriggerCondition = typeof TRIGGER_CONDITIONS[number];
|
||||
|
||||
@Entity({ schema: 'freight', name: 'surcharge_types' })
|
||||
@Index(['code'])
|
||||
@Index(['isActive'])
|
||||
@Index(['rateId'])
|
||||
export class SurchargeType extends BaseEntity {
|
||||
@Column({ name: 'code', type: 'varchar', length: 50, unique: true })
|
||||
@Column({ name: 'code', type: 'varchar', length: 40, unique: true })
|
||||
code!: string;
|
||||
|
||||
@Column({ name: 'name', type: 'varchar', length: 100 })
|
||||
name!: string;
|
||||
@Column({ name: 'label', type: 'varchar', length: 100, nullable: true })
|
||||
label!: string;
|
||||
|
||||
@Column({ name: 'description', type: 'text', nullable: true })
|
||||
description?: string | null;
|
||||
@Column({ name: 'trigger_condition', type: 'varchar', length: 50, nullable: true })
|
||||
triggerCondition!: TriggerCondition;
|
||||
|
||||
@Column({ name: 'rate_id', type: 'uuid', nullable: true })
|
||||
rateId!: string;
|
||||
|
||||
@ManyToOne(() => Rate, { eager: false })
|
||||
@JoinColumn({ name: 'rate_id' })
|
||||
rate?: Rate;
|
||||
|
||||
@Column({ name: 'is_active', type: 'boolean', default: true })
|
||||
isActive!: boolean;
|
||||
|
||||
@OneToMany(() => Surcharge, (s) => s.surchargeType)
|
||||
surcharges?: Surcharge[];
|
||||
}
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
import { BaseEntity } from '@edr/api-common';
|
||||
import { Freight } from '@edr/types';
|
||||
import { Column, Entity, Index, JoinColumn, ManyToOne, OneToMany } from 'typeorm';
|
||||
import { SurchargeType } from './surcharge-type.entity';
|
||||
import { WeightLimitRule } from './weight-limit-rule.entity';
|
||||
|
||||
@Entity({ schema: 'freight', name: 'surcharges' })
|
||||
@Index(['surchargeTypeId'])
|
||||
@Index(['isActive'])
|
||||
export class Surcharge extends BaseEntity {
|
||||
@Column({ name: 'surcharge_type_id', type: 'uuid' })
|
||||
surchargeTypeId!: string;
|
||||
|
||||
@ManyToOne(() => SurchargeType, (st) => st.surcharges)
|
||||
@JoinColumn({ name: 'surcharge_type_id' })
|
||||
surchargeType!: SurchargeType;
|
||||
|
||||
@Column({ name: 'fee_name', type: 'varchar', length: 255 })
|
||||
feeName!: string;
|
||||
|
||||
@Column({ name: 'trigger_description', type: 'text', nullable: true })
|
||||
triggerDescription?: string | null;
|
||||
|
||||
@Column({
|
||||
name: 'calculation_method',
|
||||
type: 'enum',
|
||||
enum: Freight.CalculationMethod,
|
||||
default: Freight.CalculationMethod.PER_TON,
|
||||
})
|
||||
calculationMethod!: Freight.CalculationMethod;
|
||||
|
||||
@Column({ name: 'rate', type: 'numeric', precision: 10, scale: 2 })
|
||||
rate!: number;
|
||||
|
||||
@Column({ name: 'currency', type: 'char', length: 3, default: 'USD' })
|
||||
currency!: string;
|
||||
|
||||
@Column({ name: 'apply_to_rail', type: 'boolean', default: false })
|
||||
applyToRail!: boolean;
|
||||
|
||||
@Column({ name: 'apply_to_first_mile', type: 'boolean', default: false })
|
||||
applyToFirstMile!: boolean;
|
||||
|
||||
@Column({ name: 'apply_to_last_mile', type: 'boolean', default: false })
|
||||
applyToLastMile!: boolean;
|
||||
|
||||
@Column({ name: 'is_active', type: 'boolean', default: true })
|
||||
isActive!: boolean;
|
||||
|
||||
@OneToMany(() => WeightLimitRule, (rule) => rule.surcharge)
|
||||
weightLimitRules?: WeightLimitRule[];
|
||||
}
|
||||
@@ -1,13 +1,11 @@
|
||||
import { BaseEntity } from '@edr/api-common';
|
||||
import { Freight } from '@edr/types';
|
||||
import { Column, Entity, Index, JoinColumn, ManyToOne } from 'typeorm';
|
||||
import { ContainerType } from './container-type.entity';
|
||||
import { Surcharge } from './surcharge.entity';
|
||||
|
||||
@Entity({ schema: 'freight', name: 'weight_limit_rules' })
|
||||
@Index(['containerTypeId'])
|
||||
@Index(['surchargeId'])
|
||||
@Index(['isActive'])
|
||||
@Index(['tradeDirection'])
|
||||
@Index(['effectiveFrom'])
|
||||
export class WeightLimitRule extends BaseEntity {
|
||||
@Column({ name: 'container_type_id', type: 'uuid' })
|
||||
containerTypeId!: string;
|
||||
@@ -16,30 +14,15 @@ export class WeightLimitRule extends BaseEntity {
|
||||
@JoinColumn({ name: 'container_type_id' })
|
||||
containerType!: ContainerType;
|
||||
|
||||
@Column({ name: 'trade_direction', type: 'enum', enum: Freight.TradeDirection })
|
||||
tradeDirection!: Freight.TradeDirection;
|
||||
@Column({ name: 'trade_direction', type: 'varchar', length: 10 })
|
||||
tradeDirection!: string;
|
||||
|
||||
@Column({ name: 'max_weight_tons', type: 'numeric', precision: 10, scale: 2 })
|
||||
maxWeightTons!: number;
|
||||
@Column({ name: 'max_vgm_tons', type: 'numeric', precision: 8, scale: 3, nullable: true })
|
||||
maxVgmTons!: number;
|
||||
|
||||
@Column({ name: 'warning_threshold_tons', type: 'numeric', precision: 10, scale: 2 })
|
||||
warningThresholdTons!: number;
|
||||
@Column({ name: 'effective_from', type: 'date', nullable: true })
|
||||
effectiveFrom!: Date;
|
||||
|
||||
@Column({
|
||||
name: 'exceeded_action',
|
||||
type: 'enum',
|
||||
enum: Freight.ExceededAction,
|
||||
default: Freight.ExceededAction.WARNING_ONLY,
|
||||
})
|
||||
exceededAction!: Freight.ExceededAction;
|
||||
|
||||
@Column({ name: 'surcharge_id', type: 'uuid', nullable: true })
|
||||
surchargeId?: string | null;
|
||||
|
||||
@ManyToOne(() => Surcharge, (s) => s.weightLimitRules, { nullable: true })
|
||||
@JoinColumn({ name: 'surcharge_id' })
|
||||
surcharge?: Surcharge | null;
|
||||
|
||||
@Column({ name: 'is_active', type: 'boolean', default: true })
|
||||
isActive!: boolean;
|
||||
@Column({ name: 'effective_to', type: 'date', nullable: true })
|
||||
effectiveTo?: Date | null;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
import { BaseEntity } from '@edr/api-common';
|
||||
import { Column, Entity, Index } from 'typeorm';
|
||||
|
||||
@Entity({ schema: 'freight', name: 'yards' })
|
||||
@Index(['code'])
|
||||
@Index(['country'])
|
||||
@Index(['isActive'])
|
||||
export class Yard extends BaseEntity {
|
||||
@Column({ name: 'code', type: 'varchar', length: 20, unique: true })
|
||||
code!: string;
|
||||
|
||||
@Column({ name: 'label', type: 'varchar', length: 100 })
|
||||
label!: string;
|
||||
|
||||
@Column({ name: 'country', type: 'varchar', length: 50 })
|
||||
country!: string;
|
||||
|
||||
@Column({ name: 'is_active', type: 'boolean', default: true })
|
||||
isActive!: boolean;
|
||||
|
||||
@Column({ name: 'display_order', type: 'int', default: 1 })
|
||||
displayOrder!: number;
|
||||
}
|
||||
Reference in New Issue
Block a user