mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-30 04:08:11 +00:00
implement rule engine module with dynamic booking evaluation, 7 entities, and Postman endpoints
This commit is contained in:
@@ -0,0 +1,277 @@
|
||||
import {
|
||||
MigrationInterface,
|
||||
QueryRunner,
|
||||
Table,
|
||||
TableIndex,
|
||||
TableForeignKey,
|
||||
TableColumn,
|
||||
} from 'typeorm';
|
||||
|
||||
export class AddRuleEngineTablesAndCodes1748514000000 implements MigrationInterface {
|
||||
name = 'AddRuleEngineTablesAndCodes1748514000000';
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
// ── 1. Add `code` column to existing tables ───────────────────────────
|
||||
|
||||
await queryRunner.addColumn(
|
||||
'freight.service_types',
|
||||
new TableColumn({
|
||||
name: 'code',
|
||||
type: 'varchar',
|
||||
length: '50',
|
||||
isNullable: true,
|
||||
}),
|
||||
);
|
||||
await queryRunner.query(
|
||||
`UPDATE freight.service_types SET code = upper(replace(service_name, ' ', '_')) WHERE code IS NULL`,
|
||||
);
|
||||
await queryRunner.changeColumn(
|
||||
'freight.service_types',
|
||||
'code',
|
||||
new TableColumn({ name: 'code', type: 'varchar', length: '50', isNullable: false }),
|
||||
);
|
||||
await queryRunner.createIndex(
|
||||
'freight.service_types',
|
||||
new TableIndex({ name: 'IDX_service_types_code', columnNames: ['code'], isUnique: true }),
|
||||
);
|
||||
|
||||
await queryRunner.addColumn(
|
||||
'freight.cargo_types',
|
||||
new TableColumn({
|
||||
name: 'code',
|
||||
type: 'varchar',
|
||||
length: '50',
|
||||
isNullable: true,
|
||||
}),
|
||||
);
|
||||
await queryRunner.query(
|
||||
`UPDATE freight.cargo_types SET code = upper(replace(cargo_type_name, ' ', '_')) WHERE code IS NULL`,
|
||||
);
|
||||
await queryRunner.changeColumn(
|
||||
'freight.cargo_types',
|
||||
'code',
|
||||
new TableColumn({ name: 'code', type: 'varchar', length: '50', isNullable: false }),
|
||||
);
|
||||
await queryRunner.createIndex(
|
||||
'freight.cargo_types',
|
||||
new TableIndex({ name: 'IDX_cargo_types_code', columnNames: ['code'], isUnique: true }),
|
||||
);
|
||||
|
||||
// ── 2. surcharge_types ────────────────────────────────────────────────
|
||||
|
||||
await queryRunner.createTable(
|
||||
new Table({
|
||||
name: 'surcharge_types',
|
||||
schema: 'freight',
|
||||
columns: [
|
||||
{ name: 'id', type: 'uuid', isPrimary: true, generationStrategy: 'uuid', default: 'uuid_generate_v4()' },
|
||||
{ name: 'code', type: 'varchar', length: '50', isNullable: false },
|
||||
{ name: 'name', type: 'varchar', length: '100', isNullable: false },
|
||||
{ name: 'description', type: 'text', isNullable: true },
|
||||
{ name: 'is_active', type: 'boolean', default: true },
|
||||
{ name: 'created_at', type: 'timestamptz', default: 'now()' },
|
||||
{ name: 'updated_at', type: 'timestamptz', default: 'now()' },
|
||||
{ name: 'deleted_at', type: 'timestamptz', isNullable: true },
|
||||
],
|
||||
}),
|
||||
true,
|
||||
);
|
||||
await queryRunner.createIndex(
|
||||
'freight.surcharge_types',
|
||||
new TableIndex({ name: 'IDX_surcharge_types_code', columnNames: ['code'], isUnique: true }),
|
||||
);
|
||||
await queryRunner.createIndex(
|
||||
'freight.surcharge_types',
|
||||
new TableIndex({ name: 'IDX_surcharge_types_is_active', columnNames: ['is_active'] }),
|
||||
);
|
||||
|
||||
// ── 3. surcharges ─────────────────────────────────────────────────────
|
||||
|
||||
await queryRunner.createTable(
|
||||
new Table({
|
||||
name: 'surcharges',
|
||||
schema: 'freight',
|
||||
columns: [
|
||||
{ name: 'id', type: 'uuid', isPrimary: true, generationStrategy: 'uuid', default: 'uuid_generate_v4()' },
|
||||
{ name: 'surcharge_type_id', type: 'uuid', isNullable: false },
|
||||
{ name: 'fee_name', type: 'varchar', length: '255', isNullable: false },
|
||||
{ name: 'trigger_description', type: 'text', isNullable: true },
|
||||
{
|
||||
name: 'calculation_method',
|
||||
type: 'enum',
|
||||
enum: ['PER_TON', 'FLAT_FEE', 'PERCENTAGE'],
|
||||
default: `'PER_TON'`,
|
||||
},
|
||||
{ name: 'rate', type: 'numeric', precision: 10, scale: 2, isNullable: false },
|
||||
{ name: 'currency', type: 'char', length: '3', default: `'USD'` },
|
||||
{ name: 'apply_to_rail', type: 'boolean', default: false },
|
||||
{ name: 'apply_to_first_mile', type: 'boolean', default: false },
|
||||
{ name: 'apply_to_last_mile', type: 'boolean', default: false },
|
||||
{ name: 'is_active', type: 'boolean', default: true },
|
||||
{ name: 'created_at', type: 'timestamptz', default: 'now()' },
|
||||
{ name: 'updated_at', type: 'timestamptz', default: 'now()' },
|
||||
{ name: 'deleted_at', type: 'timestamptz', isNullable: true },
|
||||
],
|
||||
}),
|
||||
true,
|
||||
);
|
||||
await queryRunner.createForeignKey(
|
||||
'freight.surcharges',
|
||||
new TableForeignKey({
|
||||
name: 'FK_surcharges_surcharge_type',
|
||||
columnNames: ['surcharge_type_id'],
|
||||
referencedTableName: 'freight.surcharge_types',
|
||||
referencedColumnNames: ['id'],
|
||||
onDelete: 'RESTRICT',
|
||||
}),
|
||||
);
|
||||
await queryRunner.createIndex(
|
||||
'freight.surcharges',
|
||||
new TableIndex({ name: 'IDX_surcharges_surcharge_type_id', columnNames: ['surcharge_type_id'] }),
|
||||
);
|
||||
await queryRunner.createIndex(
|
||||
'freight.surcharges',
|
||||
new TableIndex({ name: 'IDX_surcharges_is_active', columnNames: ['is_active'] }),
|
||||
);
|
||||
|
||||
// ── 4. container_types ────────────────────────────────────────────────
|
||||
|
||||
await queryRunner.createTable(
|
||||
new Table({
|
||||
name: 'container_types',
|
||||
schema: 'freight',
|
||||
columns: [
|
||||
{ name: 'id', type: 'uuid', isPrimary: true, generationStrategy: 'uuid', default: 'uuid_generate_v4()' },
|
||||
{ name: 'size_code', type: 'varchar', length: '20', isNullable: false },
|
||||
{ name: 'description', type: 'varchar', length: '100', isNullable: true },
|
||||
{ name: 'containers_per_wagon', type: 'int', isNullable: false },
|
||||
{ name: 'is_active', type: 'boolean', default: true },
|
||||
{ name: 'created_at', type: 'timestamptz', default: 'now()' },
|
||||
{ name: 'updated_at', type: 'timestamptz', default: 'now()' },
|
||||
{ name: 'deleted_at', type: 'timestamptz', isNullable: true },
|
||||
],
|
||||
}),
|
||||
true,
|
||||
);
|
||||
await queryRunner.createIndex(
|
||||
'freight.container_types',
|
||||
new TableIndex({ name: 'IDX_container_types_size_code', columnNames: ['size_code'], isUnique: true }),
|
||||
);
|
||||
await queryRunner.createIndex(
|
||||
'freight.container_types',
|
||||
new TableIndex({ name: 'IDX_container_types_is_active', columnNames: ['is_active'] }),
|
||||
);
|
||||
|
||||
// ── 5. weight_limit_rules ─────────────────────────────────────────────
|
||||
|
||||
await queryRunner.createTable(
|
||||
new Table({
|
||||
name: 'weight_limit_rules',
|
||||
schema: 'freight',
|
||||
columns: [
|
||||
{ name: 'id', type: 'uuid', isPrimary: true, generationStrategy: 'uuid', default: 'uuid_generate_v4()' },
|
||||
{ name: 'container_type_id', type: 'uuid', isNullable: false },
|
||||
{
|
||||
name: 'trade_direction',
|
||||
type: 'enum',
|
||||
enum: ['IMPORT', 'EXPORT', 'BOTH'],
|
||||
isNullable: false,
|
||||
},
|
||||
{ name: 'max_weight_tons', type: 'numeric', precision: 10, scale: 2, isNullable: false },
|
||||
{ name: 'warning_threshold_tons', type: 'numeric', precision: 10, scale: 2, isNullable: false },
|
||||
{
|
||||
name: 'exceeded_action',
|
||||
type: 'enum',
|
||||
enum: ['WARNING_ONLY', 'HARD_BLOCK'],
|
||||
default: `'WARNING_ONLY'`,
|
||||
},
|
||||
{ name: 'surcharge_id', type: 'uuid', isNullable: true },
|
||||
{ name: 'is_active', type: 'boolean', default: true },
|
||||
{ name: 'created_at', type: 'timestamptz', default: 'now()' },
|
||||
{ name: 'updated_at', type: 'timestamptz', default: 'now()' },
|
||||
{ name: 'deleted_at', type: 'timestamptz', isNullable: true },
|
||||
],
|
||||
}),
|
||||
true,
|
||||
);
|
||||
await queryRunner.createForeignKey(
|
||||
'freight.weight_limit_rules',
|
||||
new TableForeignKey({
|
||||
name: 'FK_weight_limit_rules_container_type',
|
||||
columnNames: ['container_type_id'],
|
||||
referencedTableName: 'freight.container_types',
|
||||
referencedColumnNames: ['id'],
|
||||
onDelete: 'RESTRICT',
|
||||
}),
|
||||
);
|
||||
await queryRunner.createForeignKey(
|
||||
'freight.weight_limit_rules',
|
||||
new TableForeignKey({
|
||||
name: 'FK_weight_limit_rules_surcharge',
|
||||
columnNames: ['surcharge_id'],
|
||||
referencedTableName: 'freight.surcharges',
|
||||
referencedColumnNames: ['id'],
|
||||
onDelete: 'SET NULL',
|
||||
}),
|
||||
);
|
||||
await queryRunner.createIndex(
|
||||
'freight.weight_limit_rules',
|
||||
new TableIndex({ name: 'IDX_weight_limit_rules_container_type_id', columnNames: ['container_type_id'] }),
|
||||
);
|
||||
await queryRunner.createIndex(
|
||||
'freight.weight_limit_rules',
|
||||
new TableIndex({ name: 'IDX_weight_limit_rules_surcharge_id', columnNames: ['surcharge_id'] }),
|
||||
);
|
||||
await queryRunner.createIndex(
|
||||
'freight.weight_limit_rules',
|
||||
new TableIndex({ name: 'IDX_weight_limit_rules_is_active', columnNames: ['is_active'] }),
|
||||
);
|
||||
|
||||
// ── 6. priority_rules ─────────────────────────────────────────────────
|
||||
|
||||
await queryRunner.createTable(
|
||||
new Table({
|
||||
name: 'priority_rules',
|
||||
schema: 'freight',
|
||||
columns: [
|
||||
{ name: 'id', type: 'uuid', isPrimary: true, generationStrategy: 'uuid', default: 'uuid_generate_v4()' },
|
||||
{
|
||||
name: 'priority_type',
|
||||
type: 'enum',
|
||||
enum: ['USD_PAYER', 'RAIL_AND_FORWARDING', 'GOVERNMENT_ACCOUNT', 'HIGH_VOLUME_SHIPMENT'],
|
||||
isNullable: false,
|
||||
},
|
||||
{ name: 'rule_name', type: 'varchar', length: '255', isNullable: false },
|
||||
{ name: 'description', type: 'text', isNullable: true },
|
||||
{ name: 'activation_condition', type: 'text', isNullable: true },
|
||||
{ name: 'bonus_points', type: 'int', default: 0 },
|
||||
{ name: 'is_active', type: 'boolean', default: false },
|
||||
{ name: 'created_at', type: 'timestamptz', default: 'now()' },
|
||||
{ name: 'updated_at', type: 'timestamptz', default: 'now()' },
|
||||
{ name: 'deleted_at', type: 'timestamptz', isNullable: true },
|
||||
],
|
||||
}),
|
||||
true,
|
||||
);
|
||||
await queryRunner.createIndex(
|
||||
'freight.priority_rules',
|
||||
new TableIndex({ name: 'IDX_priority_rules_priority_type', columnNames: ['priority_type'], isUnique: true }),
|
||||
);
|
||||
await queryRunner.createIndex(
|
||||
'freight.priority_rules',
|
||||
new TableIndex({ name: 'IDX_priority_rules_is_active', columnNames: ['is_active'] }),
|
||||
);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.dropTable('freight.priority_rules', true);
|
||||
await queryRunner.dropTable('freight.weight_limit_rules', true);
|
||||
await queryRunner.dropTable('freight.container_types', true);
|
||||
await queryRunner.dropTable('freight.surcharges', true);
|
||||
await queryRunner.dropTable('freight.surcharge_types', true);
|
||||
await queryRunner.dropIndex('freight.cargo_types', 'IDX_cargo_types_code');
|
||||
await queryRunner.dropColumn('freight.cargo_types', 'code');
|
||||
await queryRunner.dropIndex('freight.service_types', 'IDX_service_types_code');
|
||||
await queryRunner.dropColumn('freight.service_types', 'code');
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user