mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-09-07 13:05:44 +00:00
add permanent purge functionality for wagons and routes
- Implemented a method in to permanently delete wagons without history. - Added corresponding permissions for hard delete actions in . - Updated the UI components to include purge actions, ensuring they are only available to users with the appropriate permissions. - Created modals for confirming permanent deletions in and . - Enhanced API services to handle purge requests for locomotives, wagons, and routes. - Added tests for the purge functionality in both and services to ensure proper behavior and error handling.
This commit is contained in:
@@ -212,6 +212,75 @@ export class WagonsService {
|
||||
await this.wagonRepo.softRemove(wagon);
|
||||
}
|
||||
|
||||
/**
|
||||
* Permanently purge a wagon — irreversible, and only for rows that carry no
|
||||
* history: a mistyped or duplicated entry someone wants gone for good.
|
||||
*
|
||||
* `wagon_movements` cascades on delete, so a wagon with movements would take
|
||||
* its ledger history down with it. Rather than allow that, every reference is
|
||||
* checked first and the purge is refused if any exist — soft delete (`remove`)
|
||||
* stays the answer for a wagon that has actually been used.
|
||||
*
|
||||
* Soft-deleted wagons are purgeable, so `withDeleted` is used to find them.
|
||||
*/
|
||||
async purge(id: string): Promise<void> {
|
||||
const wagon = await this.wagonRepo.findOne({
|
||||
where: { id },
|
||||
withDeleted: true,
|
||||
});
|
||||
if (!wagon) {
|
||||
throw new NotFoundException(`Wagon ${id} not found`);
|
||||
}
|
||||
|
||||
if (wagon.trainId != null) {
|
||||
throw new ConflictException(
|
||||
`Wagon ${wagon.wagonNumber} is coupled to a train; detach it via train-builder before deleting it permanently`,
|
||||
);
|
||||
}
|
||||
if (await this.isWagonPinnedToLiveSchedule(id)) {
|
||||
throw new ConflictException(
|
||||
`Wagon ${wagon.wagonNumber} is pinned to an active schedule and cannot be deleted permanently`,
|
||||
);
|
||||
}
|
||||
|
||||
// Each of these would either lose history (movements cascade) or silently
|
||||
// blank a live reference (containers / train-set slots are SET NULL).
|
||||
const blockers: string[] = [];
|
||||
const [movements, containers, trainSetSlots] = await Promise.all([
|
||||
this.dataSource.query(
|
||||
`SELECT count(*)::int AS count FROM freight.wagon_movements WHERE wagon_id = $1`,
|
||||
[id],
|
||||
),
|
||||
this.dataSource.query(
|
||||
`SELECT count(*)::int AS count FROM freight.containers WHERE wagon_id = $1`,
|
||||
[id],
|
||||
),
|
||||
this.dataSource.query(
|
||||
`SELECT count(*)::int AS count FROM freight.train_set_wagons WHERE physical_wagon_id = $1`,
|
||||
[id],
|
||||
),
|
||||
]);
|
||||
if (movements[0]?.count > 0) {
|
||||
blockers.push(`${movements[0].count} movement record(s)`);
|
||||
}
|
||||
if (containers[0]?.count > 0) {
|
||||
blockers.push(`${containers[0].count} container(s)`);
|
||||
}
|
||||
if (trainSetSlots[0]?.count > 0) {
|
||||
blockers.push(`${trainSetSlots[0].count} train-set slot(s)`);
|
||||
}
|
||||
|
||||
if (blockers.length > 0) {
|
||||
throw new ConflictException(
|
||||
`Wagon ${wagon.wagonNumber} cannot be permanently deleted — it still has ${blockers.join(
|
||||
', ',
|
||||
)}. Delete it normally instead, which keeps the history intact.`,
|
||||
);
|
||||
}
|
||||
|
||||
await this.wagonRepo.remove(wagon);
|
||||
}
|
||||
|
||||
/**
|
||||
* A wagon is busy when any live (DRAFT/SCHEDULED/DISPATCHED) schedule pins it
|
||||
* to one of its slots — schedule occupancy lives on TrainSetWagon rows, not
|
||||
|
||||
Reference in New Issue
Block a user