feat: updateing the loading and unloading

This commit is contained in:
Nathnael
2026-08-24 14:01:19 +00:00
parent 17c2dca3cc
commit 562c8b48ba
9 changed files with 357 additions and 37 deletions

View File

@@ -4,9 +4,13 @@ import {
CARGO_CATEGORY_LABEL_EXPR,
CONTAINER_CLASSES,
CONTAINER_CLASS_EXPR,
HANDLING_STANDARD_HOURS_EXPR,
TARGET_DIMENSION_KEYS,
cycleRateExpr,
handlingHours,
implementRateExpr,
loadingHours,
otherActivityHours,
plannedRowsSql,
} from './operations-classification';
import { TARGET_DIMENSIONS, TARGET_METRICS } from '../operations-reporting/entities/operations-target.entity';
@@ -24,6 +28,41 @@ function emittedKeys(expr: string): string[] {
}
describe('operations classification', () => {
/**
* The loading window falls back to the bookings that boarded at the stop, off
* the DEPARTING schedule — a turnaround loads for the leg it leaves on, not
* the one it arrived on. Losing either half of that silently turns a
* populated report back into an empty one.
*/
it('falls back to the booking-derived loading window, off the departing leg', () => {
for (const expr of [loadingHours('s'), handlingHours('s')]) {
expect(expr).toContain('COALESCE(s.loading_started_at');
expect(expr).toContain('s.departed_schedule_id');
expect(expr).toContain('b.origin_yard_id = (s.yard_id)');
}
});
/** Unloading is never derived — auto-unload would report it as ~0 hours. */
it('never derives the unloading half', () => {
expect(handlingHours('s')).toContain('s.unloading_started_at');
expect(handlingHours('s')).not.toContain('b.destination_yard_id');
});
/**
* Every other standard coalesces to the spec's figure. This one must not:
* the spec names no handling standard, and a fallback would publish a rate
* against a number nobody agreed to.
*/
it('leaves the handling standard null when nobody has set one', () => {
expect(HANDLING_STANDARD_HOURS_EXPR).toContain('std.handling_standard_hours_container');
expect(HANDLING_STANDARD_HOURS_EXPR).not.toContain('COALESCE(std.handling');
});
/** An unmeasured stop reports unknown activity, not a full stay of it. */
it('keeps other activity null when there is no handling window', () => {
expect(otherActivityHours('stay', 'handling')).toContain('IS NULL THEN NULL');
});
it('offers every cargo category the expression can emit as a filter option', () => {
const offered = new Set(CARGO_CATEGORIES.map((o) => o.value));
const missing = [...new Set(emittedKeys(CARGO_CATEGORY_EXPR))].filter((k) => !offered.has(k));