Last mile confirmation request , approval, payment Feature

This commit is contained in:
Hagernesh
2026-08-05 19:58:41 +00:00
parent 37d0cc966a
commit 1f4d659ffb
26 changed files with 1334 additions and 8 deletions

View File

@@ -306,4 +306,5 @@ export const EDR_FREIGHT_POSITIONS: FreightSeedPosition[] = [
{ key: "operation", name: { en: "Operation" }, rank: 4, permissionKeys: [...POSITION_PERMISSION_PRESETS.operation] },
{ key: "operations_chief", name: { en: "Operations Chief" }, rank: 2, permissionKeys: [...POSITION_PERMISSION_PRESETS.operationsChief] },
{ key: "dispatcher", name: { en: "Dispatcher" }, rank: 4, permissionKeys: [...POSITION_PERMISSION_PRESETS.dispatcher] },
{ key: "truck_machinery_chief", name: { en: "Truck & Machinery Chief" }, rank: 2, permissionKeys: [...POSITION_PERMISSION_PRESETS.truckMachineryChief] },
];

View File

@@ -227,6 +227,9 @@ export const MILE_PERMISSIONS: FreightPermissionSeed[] = [
perm('d3b00001-0001-4000-8000-000000000006', 'edr_freight_app:last_mile:assign_vehicles', 'Assign last-mile vehicles'),
perm('d3b00001-0001-4000-8000-000000000007', 'edr_freight_app:last_mile:set_distances', 'Set last-mile distances'),
perm('d3b00001-0001-4000-8000-000000000008', 'edr_freight_app:last_mile:generate_invoice', 'Generate last-mile invoice'),
perm('d3b00001-0001-4000-8000-000000000009', 'edr_freight_app:last_mile:request_view', 'View last-mile confirmation requests'),
perm('d3b00001-0001-4000-8000-00000000000a', 'edr_freight_app:last_mile:request_review', 'Review last-mile confirmation requests (T&M dept)'),
perm('d3b00001-0001-4000-8000-00000000000b', 'edr_freight_app:last_mile:request_approve', 'Approve/reject last-mile confirmation requests'),
];
// F. Fleet — rail assets (splits the flat fleet:view/manage)
@@ -528,6 +531,11 @@ export const FREIGHT_PERMS = {
assignVehicles: 'edr_freight_app:last_mile:assign_vehicles',
setDistances: 'edr_freight_app:last_mile:set_distances',
generateInvoice: 'edr_freight_app:last_mile:generate_invoice',
// Pre-approval confirmation stage (Truck & Machinery department): view/review
// a submitted request, approve/reject it.
requestView: 'edr_freight_app:last_mile:request_view',
requestReview: 'edr_freight_app:last_mile:request_review',
requestApprove: 'edr_freight_app:last_mile:request_approve',
},
locomotives: {
view: 'edr_freight_app:locomotives:view',
@@ -1002,6 +1010,17 @@ export const POSITION_PERMISSION_PRESETS = {
FREIGHT_PERMS.trainScheduling.view,
FREIGHT_PERMS.bookings.operations,
]),
// Truck & Machinery chief: reviews and approves/rejects last-mile
// confirmation requests (the pre-approval gate ahead of vehicle assignment),
// plus enough fleet visibility to judge truck availability.
truckMachineryChief: dedupe([
FREIGHT_PERMS.lastMile.view,
FREIGHT_PERMS.lastMile.requestView,
FREIGHT_PERMS.lastMile.requestReview,
FREIGHT_PERMS.lastMile.requestApprove,
FREIGHT_PERMS.fleetDashboard.view,
FREIGHT_PERMS.vehicles.view,
]),
} as const;
/** Derive the module bucket from the resource segment of a permission key. */

View File

@@ -29,6 +29,7 @@ const STAFF_USERS = [
{ email: 'operation@edr.local', username: 'operation', roleKey: 'edr_operations_officer', positionKey: 'operation' },
{ email: 'gl-et@edr.local', username: 'gl_et', roleKey: 'edr_gl_ethiopia', positionKey: 'ethiopian_gl' },
{ email: 'gl-dj@edr.local', username: 'gl_dj', roleKey: 'edr_gl_djibouti', positionKey: 'djibouti_gl' },
{ email: 'tm-chief@edr.local', username: 'tm_chief', roleKey: 'edr_operations_officer', positionKey: 'truck_machinery_chief' },
] as const;
@Injectable()

View File

@@ -4,6 +4,11 @@ import { DataSource } from 'typeorm';
import { BookingContainer } from '../modules/bookings/entities/booking-container.entity';
import { Booking } from '../modules/bookings/entities/booking.entity';
import {
CompanyProfile,
ProfileStatus,
ProfileType,
} from '../modules/companies/entities/company-profile.entity';
import { Company, CompanyStatus, CompanyType } from '../modules/companies/entities/company.entity';
import { FirstMile } from '../modules/first-mile/entities/first-mile.entity';
import { LastMile } from '../modules/last-mile/entities/last-mile.entity';
@@ -13,7 +18,8 @@ import { ServiceType } from '../modules/rule-engine/entities/service-type.entity
import { Yard } from '../modules/rule-engine/entities/yard.entity';
const SERVICE_TYPE_CODE = 'RAIL_CONTAINER_PAID_MILE';
const COMPANY_TIN = 'PAIDMILE001';
// companies.tin is varchar(10) — an 11-char TIN 22001s the whole seeder.
const COMPANY_TIN = 'PAIDMILE01';
const COMPANY_EMAIL = 'paid-mile-demo@edr.local';
const YARDS = [
@@ -183,6 +189,22 @@ export class PaidImportExportMileDemoSeeder {
manager.getRepository(ContainerType).find(),
]);
// bookings.company_profile_id is NOT NULL — the demo company needs an
// approved importer profile of its own (no unique key to upsert on).
const profileRepo = manager.getRepository(CompanyProfile);
const companyProfile =
(await profileRepo.findOne({
where: { companyId: company.id, type: ProfileType.importer },
})) ??
(await profileRepo.save(
profileRepo.create({
companyId: company.id,
type: ProfileType.importer,
status: ProfileStatus.Active,
businessLicense: 'PMD-LIC-0001',
}),
));
const yardByCode = new Map(yards.map((yard) => [yard.code, yard]));
const containerTypeByCode = new Map(
containerTypes.map((containerType) => [containerType.code, containerType]),
@@ -206,6 +228,7 @@ export class PaidImportExportMileDemoSeeder {
{
reference: demoBooking.reference,
companyId: company.id,
companyProfileId: companyProfile.id,
status: 'APPROVED',
scheduledDate: new Date(demoBooking.scheduledDate),
estimatedShipmentDate: new Date(demoBooking.scheduledDate),