mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-30 06:28:12 +00:00
Customer Name + Customer ID columns removed from the Export Marshalling Document
This commit is contained in:
@@ -2811,13 +2811,12 @@ export class TrainSchedulingService {
|
|||||||
return [
|
return [
|
||||||
`<tr class="empty">
|
`<tr class="empty">
|
||||||
${wagonCells}
|
${wagonCells}
|
||||||
<td colspan="6">EMPTY — no cargo allocated</td>
|
<td colspan="4">EMPTY — no cargo allocated</td>
|
||||||
</tr>`,
|
</tr>`,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
return allocations.map((allocation) => {
|
return allocations.map((allocation) => {
|
||||||
const booking = allocation.booking ?? bookingById.get(allocation.bookingId);
|
const booking = allocation.booking ?? bookingById.get(allocation.bookingId);
|
||||||
const company = booking?.company as Record<string, unknown> | null | undefined;
|
|
||||||
const cargoType = (booking as unknown as { cargoType?: { name?: string; code?: string } } | undefined)?.cargoType;
|
const cargoType = (booking as unknown as { cargoType?: { name?: string; code?: string } } | undefined)?.cargoType;
|
||||||
const containerItems = allocation.containerItems ?? [];
|
const containerItems = allocation.containerItems ?? [];
|
||||||
const firstContainer = containerItems[0];
|
const firstContainer = containerItems[0];
|
||||||
@@ -2826,8 +2825,6 @@ export class TrainSchedulingService {
|
|||||||
const chassisNumbers = containerItems.map((item) => item.chassisNumber).filter(Boolean).join(', ');
|
const chassisNumbers = containerItems.map((item) => item.chassisNumber).filter(Boolean).join(', ');
|
||||||
return `<tr>
|
return `<tr>
|
||||||
${wagonCells}
|
${wagonCells}
|
||||||
<td>${esc(company?.name ?? company?.legalName ?? company?.tradeName ?? booking?.companyId)}</td>
|
|
||||||
<td>${esc(booking?.companyId)}</td>
|
|
||||||
<td>${esc(cargoType?.name ?? cargoType?.code ?? allocation.loadType)}</td>
|
<td>${esc(cargoType?.name ?? cargoType?.code ?? allocation.loadType)}</td>
|
||||||
<td>${esc(containerNumbers || firstContainer?.containerNumber)}</td>
|
<td>${esc(containerNumbers || firstContainer?.containerNumber)}</td>
|
||||||
<td>${esc(chassisNumbers)}</td>
|
<td>${esc(chassisNumbers)}</td>
|
||||||
@@ -2909,8 +2906,6 @@ export class TrainSchedulingService {
|
|||||||
<th class="num">Equated Length</th>
|
<th class="num">Equated Length</th>
|
||||||
<th class="num">Tare Weight</th>
|
<th class="num">Tare Weight</th>
|
||||||
<th class="num">Load Capacity</th>
|
<th class="num">Load Capacity</th>
|
||||||
<th>Customer Name</th>
|
|
||||||
<th>Customer ID</th>
|
|
||||||
<th>Cargo Type</th>
|
<th>Cargo Type</th>
|
||||||
<th>Container No</th>
|
<th>Container No</th>
|
||||||
<th>Chassis No</th>
|
<th>Chassis No</th>
|
||||||
@@ -2918,7 +2913,7 @@ export class TrainSchedulingService {
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
${rows || '<tr><td colspan="12">No wagons on this train set.</td></tr>'}
|
${rows || '<tr><td colspan="10">No wagons on this train set.</td></tr>'}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import { Textarea } from '@/components/ui/textarea';
|
|||||||
import { useMutation } from '@tanstack/react-query';
|
import { useMutation } from '@tanstack/react-query';
|
||||||
import { api } from '@/services/api';
|
import { api } from '@/services/api';
|
||||||
import { useToast } from '@/hooks/use-toast';
|
import { useToast } from '@/hooks/use-toast';
|
||||||
|
import { isBackdated, nowLocalDateTimeInput } from '@/lib/no-backdate';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Customer Pickup + Proof of Delivery capture for a LOADED cargo.
|
* Customer Pickup + Proof of Delivery capture for a LOADED cargo.
|
||||||
@@ -27,6 +28,10 @@ export function DeliverCargoDialog({ cargoId, onSuccess }: { cargoId: string; on
|
|||||||
toast({ title: 'Receiver name is required', variant: 'destructive' });
|
toast({ title: 'Receiver name is required', variant: 'destructive' });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (isBackdated(pickupDate)) {
|
||||||
|
toast({ title: 'Pickup date cannot be in the past', variant: 'destructive' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
await deliver.mutateAsync({
|
await deliver.mutateAsync({
|
||||||
id: cargoId,
|
id: cargoId,
|
||||||
@@ -75,6 +80,7 @@ export function DeliverCargoDialog({ cargoId, onSuccess }: { cargoId: string; on
|
|||||||
<Label>Pickup date</Label>
|
<Label>Pickup date</Label>
|
||||||
<Input
|
<Input
|
||||||
type="datetime-local"
|
type="datetime-local"
|
||||||
|
min={nowLocalDateTimeInput()}
|
||||||
value={pickupDate}
|
value={pickupDate}
|
||||||
onChange={(e) => setPickupDate(e.target.value)}
|
onChange={(e) => setPickupDate(e.target.value)}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import { useEffect, useState } from 'react';
|
|||||||
|
|
||||||
import { QUERY_KEYS } from '@/constants/QUERY_KEYS';
|
import { QUERY_KEYS } from '@/constants/QUERY_KEYS';
|
||||||
import { useToast } from '@/hooks/use-toast';
|
import { useToast } from '@/hooks/use-toast';
|
||||||
|
import { isBackdated } from '@/lib/no-backdate';
|
||||||
import { lastMileService, type LastMileRecord } from '@/services/last-mile.service';
|
import { lastMileService, type LastMileRecord } from '@/services/last-mile.service';
|
||||||
|
|
||||||
interface TruckDetentionModalProps {
|
interface TruckDetentionModalProps {
|
||||||
@@ -111,6 +112,7 @@ export function TruckDetentionModal({ opened, onClose, record }: TruckDetentionM
|
|||||||
description="Detention clock start"
|
description="Detention clock start"
|
||||||
value={arrived}
|
value={arrived}
|
||||||
onChange={(v) => setArrived(v ? new Date(v) : null)}
|
onChange={(v) => setArrived(v ? new Date(v) : null)}
|
||||||
|
minDate={new Date()}
|
||||||
clearable
|
clearable
|
||||||
/>
|
/>
|
||||||
<DateTimePicker
|
<DateTimePicker
|
||||||
@@ -118,11 +120,26 @@ export function TruckDetentionModal({ opened, onClose, record }: TruckDetentionM
|
|||||||
description="Clock end (blank = still out)"
|
description="Clock end (blank = still out)"
|
||||||
value={delivered}
|
value={delivered}
|
||||||
onChange={(v) => setDelivered(v ? new Date(v) : null)}
|
onChange={(v) => setDelivered(v ? new Date(v) : null)}
|
||||||
|
minDate={new Date()}
|
||||||
clearable
|
clearable
|
||||||
/>
|
/>
|
||||||
</Group>
|
</Group>
|
||||||
<Group justify="flex-end">
|
<Group justify="flex-end">
|
||||||
<Button variant="light" loading={saveTimes.isPending} onClick={() => saveTimes.mutate()}>
|
<Button
|
||||||
|
variant="light"
|
||||||
|
loading={saveTimes.isPending}
|
||||||
|
onClick={() => {
|
||||||
|
// No backdating: detention times are recorded as they happen.
|
||||||
|
if (isBackdated(arrived) || isBackdated(delivered)) {
|
||||||
|
toast({
|
||||||
|
variant: 'destructive',
|
||||||
|
title: 'Detention times cannot be in the past',
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
saveTimes.mutate();
|
||||||
|
}}
|
||||||
|
>
|
||||||
Save times
|
Save times
|
||||||
</Button>
|
</Button>
|
||||||
</Group>
|
</Group>
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
|
|||||||
|
|
||||||
import { api } from '@/services/api';
|
import { api } from '@/services/api';
|
||||||
import { useToast } from '@/hooks/use-toast';
|
import { useToast } from '@/hooks/use-toast';
|
||||||
|
import { isBackdated, nowLocalDateTimeInput } from '@/lib/no-backdate';
|
||||||
import { warehouseService } from '@/services/warehouse.service';
|
import { warehouseService } from '@/services/warehouse.service';
|
||||||
import type { WarehouseInventoryItem } from '@/types/warehouse';
|
import type { WarehouseInventoryItem } from '@/types/warehouse';
|
||||||
import { extractErrorMessage } from './options';
|
import { extractErrorMessage } from './options';
|
||||||
@@ -440,6 +441,12 @@ export function ReleaseOrderModal({ opened, onClose, item, truckPrefill }: Relea
|
|||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// No backdating: gate times are recorded as they happen. The locked
|
||||||
|
// entrance (exit step) keeps its original past gate-in untouched.
|
||||||
|
if (!isEntranceLocked && isBackdated(gateInTime)) {
|
||||||
|
toast({ variant: 'destructive', title: 'Gate in time cannot be in the past' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (isExitStep && (!gateOutTime || (!skipWeighing && grossWeight === ''))) {
|
if (isExitStep && (!gateOutTime || (!skipWeighing && grossWeight === ''))) {
|
||||||
toast({
|
toast({
|
||||||
variant: 'destructive',
|
variant: 'destructive',
|
||||||
@@ -447,6 +454,10 @@ export function ReleaseOrderModal({ opened, onClose, item, truckPrefill }: Relea
|
|||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (isExitStep && isBackdated(gateOutTime)) {
|
||||||
|
toast({ variant: 'destructive', title: 'Gate out time cannot be in the past' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (isExitStep && hasContainerWeights && selectedContainerNumbers.length === 0) {
|
if (isExitStep && hasContainerWeights && selectedContainerNumbers.length === 0) {
|
||||||
toast({ variant: 'destructive', title: 'Select the containers loaded on this truck' });
|
toast({ variant: 'destructive', title: 'Select the containers loaded on this truck' });
|
||||||
return;
|
return;
|
||||||
@@ -646,7 +657,7 @@ export function ReleaseOrderModal({ opened, onClose, item, truckPrefill }: Relea
|
|||||||
</SimpleGrid>
|
</SimpleGrid>
|
||||||
</Stack>
|
</Stack>
|
||||||
)}
|
)}
|
||||||
<TextInput label="Gate in time" type="datetime-local" value={gateInTime} onChange={(e) => setGateInTime(e.currentTarget.value)} readOnly={isEntranceLocked} />
|
<TextInput label="Gate in time" type="datetime-local" min={isEntranceLocked ? undefined : nowLocalDateTimeInput()} value={gateInTime} onChange={(e) => setGateInTime(e.currentTarget.value)} readOnly={isEntranceLocked} />
|
||||||
</Group>
|
</Group>
|
||||||
{hasContainerWeights && (
|
{hasContainerWeights && (
|
||||||
<Group gap="md" align="center">
|
<Group gap="md" align="center">
|
||||||
@@ -679,7 +690,7 @@ export function ReleaseOrderModal({ opened, onClose, item, truckPrefill }: Relea
|
|||||||
<Text size="sm" c={weightMismatch ? 'red' : 'dimmed'}>
|
<Text size="sm" c={weightMismatch ? 'red' : 'dimmed'}>
|
||||||
Computed net: <b>{computedNetWeight == null ? '-' : `${computedNetWeight.toLocaleString()} t`}</b>
|
Computed net: <b>{computedNetWeight == null ? '-' : `${computedNetWeight.toLocaleString()} t`}</b>
|
||||||
</Text>
|
</Text>
|
||||||
<TextInput label="Gate out time" type="datetime-local" value={gateOutTime} onChange={(e) => setGateOutTime(e.currentTarget.value)} disabled={!isExitStep || hasTruckLeft} />
|
<TextInput label="Gate out time" type="datetime-local" min={nowLocalDateTimeInput()} value={gateOutTime} onChange={(e) => setGateOutTime(e.currentTarget.value)} disabled={!isExitStep || hasTruckLeft} />
|
||||||
</Group>
|
</Group>
|
||||||
{weightMismatch && (
|
{weightMismatch && (
|
||||||
<Alert icon={<Scale size={16} />} color="red" variant="light">
|
<Alert icon={<Scale size={16} />} color="red" variant="light">
|
||||||
|
|||||||
20
apps/edr-freight-web/backoffice/src/lib/no-backdate.ts
Normal file
20
apps/edr-freight-web/backoffice/src/lib/no-backdate.ts
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
/**
|
||||||
|
* Backdating guard for operational time entries (gate in/out, mile truck
|
||||||
|
* times, delivery pickups): times must be recorded as they happen, never
|
||||||
|
* dated back. A one-hour grace covers real-world lag (weighbridge queue,
|
||||||
|
* operator finishing the form after the event).
|
||||||
|
*/
|
||||||
|
export const BACKDATE_GRACE_MS = 60 * 60 * 1000;
|
||||||
|
|
||||||
|
/** Local-time "YYYY-MM-DDTHH:mm" for a datetime-local input's `min`. */
|
||||||
|
export const nowLocalDateTimeInput = (): string =>
|
||||||
|
new Date(Date.now() - new Date().getTimezoneOffset() * 60_000)
|
||||||
|
.toISOString()
|
||||||
|
.slice(0, 16);
|
||||||
|
|
||||||
|
/** True when the value is more than the grace period in the past. */
|
||||||
|
export const isBackdated = (value: string | Date | null | undefined): boolean => {
|
||||||
|
if (!value) return false;
|
||||||
|
const t = value instanceof Date ? value.getTime() : new Date(value).getTime();
|
||||||
|
return Number.isFinite(t) && t < Date.now() - BACKDATE_GRACE_MS;
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user