mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-29 09:30:59 +00:00
fix(freight): filter container returns by returned-by truck type
Top Returned Containers table ignored the EDR/Customer tab because the backend never persisted which truck type performed the return. Added returned_by column + DTO/entity field, wired create payload to send it, and filtered the table by the active tab.
This commit is contained in:
@@ -0,0 +1,54 @@
|
||||
import { useState, type MouseEvent } from 'react';
|
||||
import { Button } from '@mantine/core';
|
||||
import { FileText } from 'lucide-react';
|
||||
|
||||
import { useToast } from '@/hooks/use-toast';
|
||||
import { warehouseService } from '@/services/warehouse.service';
|
||||
import { extractDownloadErrorMessage } from './options';
|
||||
import { openPdfBlob } from './pdf';
|
||||
|
||||
/** Opens (or downloads) an inventory item's GRN document — same button everywhere it appears. */
|
||||
export function GrnDocumentButton({
|
||||
inventoryId,
|
||||
grnNumber,
|
||||
}: {
|
||||
inventoryId: string;
|
||||
grnNumber?: string | null;
|
||||
}) {
|
||||
const { toast } = useToast();
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
const openDocument = async (event: MouseEvent<HTMLButtonElement>) => {
|
||||
event.stopPropagation();
|
||||
if (!grnNumber) {
|
||||
toast({ variant: 'destructive', title: 'GRN document unavailable', description: 'This item has no GRN number yet.' });
|
||||
return;
|
||||
}
|
||||
setLoading(true);
|
||||
const pdfWindow = window.open('', '_blank');
|
||||
try {
|
||||
const response = await warehouseService.downloadGrnDocument(inventoryId);
|
||||
const opened = openPdfBlob(response.data, `grn-${grnNumber}.pdf`, pdfWindow);
|
||||
toast({ title: opened ? 'GRN document opened' : 'GRN document downloaded' });
|
||||
} catch (error) {
|
||||
pdfWindow?.close();
|
||||
toast({ variant: 'destructive', title: 'GRN document failed', description: await extractDownloadErrorMessage(error) });
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Button
|
||||
size="compact-xs"
|
||||
variant="subtle"
|
||||
color="teal"
|
||||
leftSection={<FileText size={12} />}
|
||||
disabled={!grnNumber}
|
||||
loading={loading}
|
||||
onClick={openDocument}
|
||||
>
|
||||
{grnNumber ?? 'No GRN'}
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Fragment, useEffect, useMemo, useState, type MouseEvent } from 'react';
|
||||
import { Fragment, useEffect, useMemo, useState } from 'react';
|
||||
import {
|
||||
ActionIcon,
|
||||
Alert,
|
||||
@@ -71,6 +71,7 @@ import { BookingSelect } from './BookingSelect';
|
||||
import { DeliverInventoryModal } from './DeliverInventoryModal';
|
||||
import { ContainerItemsModal } from './ContainerItemsModal';
|
||||
import { FeePreviewModal } from './FeePreviewModal';
|
||||
import { GrnDocumentButton } from './GrnDocumentButton';
|
||||
import { InspectionReportModal } from './InspectionReportModal';
|
||||
import { InventoryDetailModal } from './InventoryDetailModal';
|
||||
import { InventoryHistoryModal } from './InventoryHistoryModal';
|
||||
@@ -98,44 +99,6 @@ interface ReceiveInventoryModalProps {
|
||||
onReceived?: () => void;
|
||||
}
|
||||
|
||||
function GrnDocumentButton({ inventoryId, grnNumber }: { inventoryId: string; grnNumber?: string | null }) {
|
||||
const { toast } = useToast();
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
const openDocument = async (event: MouseEvent<HTMLButtonElement>) => {
|
||||
event.stopPropagation();
|
||||
if (!grnNumber) {
|
||||
toast({ variant: 'destructive', title: 'GRN document unavailable', description: 'This item has no GRN number yet.' });
|
||||
return;
|
||||
}
|
||||
setLoading(true);
|
||||
const pdfWindow = window.open('', '_blank');
|
||||
try {
|
||||
const response = await warehouseService.downloadGrnDocument(inventoryId);
|
||||
const opened = openPdfBlob(response.data, `grn-${grnNumber}.pdf`, pdfWindow);
|
||||
toast({ title: opened ? 'GRN document opened' : 'GRN document downloaded' });
|
||||
} catch (error) {
|
||||
pdfWindow?.close();
|
||||
toast({ variant: 'destructive', title: 'GRN document failed', description: await extractDownloadErrorMessage(error) });
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Button
|
||||
size="compact-xs"
|
||||
variant="subtle"
|
||||
color="teal"
|
||||
leftSection={<FileText size={12} />}
|
||||
disabled={!grnNumber}
|
||||
loading={loading}
|
||||
onClick={openDocument}
|
||||
>
|
||||
{grnNumber ?? 'No GRN'}
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
|
||||
interface Location {
|
||||
warehouseId: string;
|
||||
@@ -1513,7 +1476,7 @@ function ExportReceivedTab({ enabled, onChanged }: { enabled: boolean; onChanged
|
||||
</Table.Td>
|
||||
<Table.Td ta="right">
|
||||
<Button size="compact-xs" variant="light" color="orange" onClick={() => setInspectId(r.id)}>
|
||||
Inspect / Report
|
||||
Inspection / Report
|
||||
</Button>
|
||||
</Table.Td>
|
||||
</Table.Tr>
|
||||
@@ -2228,7 +2191,7 @@ const getPendingUnloadBookings = (train: ImportTrain) =>
|
||||
const isFullyUnloaded = (train: ImportTrain) =>
|
||||
Boolean(train.fullyUnloaded) || (train.totalBookings > 0 && getPendingUnloadBookings(train) === 0);
|
||||
|
||||
function ImportArriveQueueTab({
|
||||
export function ImportArriveQueueTab({
|
||||
enabled,
|
||||
onChanged,
|
||||
}: {
|
||||
@@ -2769,7 +2732,7 @@ function ImportUnloadedQueueTab({ enabled }: { enabled: boolean }) {
|
||||
{r.handoverDocumentReference ? 'View handover' : 'Handover'}
|
||||
</Menu.Item>
|
||||
)}
|
||||
<Menu.Item onClick={() => setInspectId(r.id)}>Inspect / report</Menu.Item>
|
||||
<Menu.Item onClick={() => setInspectId(r.id)}>Inspection / Report</Menu.Item>
|
||||
{/* Double handling is decided once the goods are off
|
||||
the wagon (every row here is unloaded) — Yes is
|
||||
what makes the fee rule bill this booking. */}
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
import { Fragment, useState, type MouseEvent } from 'react';
|
||||
import { Fragment, useState } from 'react';
|
||||
import { ActionIcon, Badge, Button, Checkbox, Group, Table, Text, Tooltip } from '@mantine/core';
|
||||
import { ArrowRightLeft, ChevronDown, ChevronRight, ClipboardList, Coins, Download, Eye, FileText, History, MapPin } from 'lucide-react';
|
||||
|
||||
import { useToast } from '@/hooks/use-toast';
|
||||
import { warehouseService } from '@/services/warehouse.service';
|
||||
import {
|
||||
getNextInventoryAction,
|
||||
type InventoryAction,
|
||||
@@ -11,8 +9,8 @@ import {
|
||||
} from '@/types/warehouse';
|
||||
import { InventoryStatusBadge } from './badges';
|
||||
import { TruckBreakdownRow } from './TruckBreakdownRow';
|
||||
import { extractDownloadErrorMessage, formatDate, formatNumber, humanizeEnum } from './options';
|
||||
import { openPdfBlob } from './pdf';
|
||||
import { GrnDocumentButton } from './GrnDocumentButton';
|
||||
import { formatDate, formatNumber, humanizeEnum } from './options';
|
||||
|
||||
interface WarehouseInventoryTableProps {
|
||||
items: WarehouseInventoryItem[];
|
||||
@@ -64,45 +62,6 @@ const noteLineValue = (notes: string | null | undefined, label: string) => {
|
||||
const handoverDocumentReference = (item: WarehouseInventoryItem) =>
|
||||
item.handoverDocumentReference ?? noteLineValue(item.notes, 'Handover Reference');
|
||||
|
||||
function GrnDocumentButton({ item }: { item: WarehouseInventoryItem }) {
|
||||
const { toast } = useToast();
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
const openDocument = async (event: MouseEvent<HTMLButtonElement>) => {
|
||||
event.stopPropagation();
|
||||
if (!item.grnNumber) {
|
||||
toast({ variant: 'destructive', title: 'GRN document unavailable', description: 'This item has no GRN number yet.' });
|
||||
return;
|
||||
}
|
||||
setLoading(true);
|
||||
const pdfWindow = window.open('', '_blank');
|
||||
try {
|
||||
const response = await warehouseService.downloadGrnDocument(item.id);
|
||||
const opened = openPdfBlob(response.data, `grn-${item.grnNumber}.pdf`, pdfWindow);
|
||||
toast({ title: opened ? 'GRN document opened' : 'GRN document downloaded' });
|
||||
} catch (error) {
|
||||
pdfWindow?.close();
|
||||
toast({ variant: 'destructive', title: 'GRN document failed', description: await extractDownloadErrorMessage(error) });
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Button
|
||||
size="compact-xs"
|
||||
variant="subtle"
|
||||
color="teal"
|
||||
leftSection={<FileText size={12} />}
|
||||
disabled={!item.grnNumber}
|
||||
loading={loading}
|
||||
onClick={openDocument}
|
||||
>
|
||||
{item.grnNumber ?? 'No GRN'}
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
|
||||
export function WarehouseInventoryTable({
|
||||
items,
|
||||
busyId,
|
||||
@@ -239,7 +198,7 @@ export function WarehouseInventoryTable({
|
||||
)}
|
||||
</Table.Td>
|
||||
<Table.Td>
|
||||
<GrnDocumentButton item={item} />
|
||||
<GrnDocumentButton inventoryId={item.id} grnNumber={item.grnNumber} />
|
||||
</Table.Td>
|
||||
<Table.Td>{item.warehouse?.facility?.name ?? '-'}</Table.Td>
|
||||
<Table.Td>{item.warehouse?.code ?? '-'}</Table.Td>
|
||||
@@ -341,7 +300,7 @@ export function WarehouseInventoryTable({
|
||||
</Tooltip>
|
||||
)}
|
||||
{onDownloadBundle && item.grnNumber && (
|
||||
<Tooltip label="Download document bundle (GRN + gate clearance + handover)" withArrow>
|
||||
<Tooltip label="Download document bundle (GRN + exit paper + handover)" withArrow>
|
||||
<ActionIcon variant="subtle" color="grape" onClick={() => onDownloadBundle(item)}>
|
||||
<Download size={16} />
|
||||
</ActionIcon>
|
||||
|
||||
Reference in New Issue
Block a user