refactor(FE-208,212): implement grouping of goods receipt items and enhance table rendering in PurchaseOrderDetail

This commit is contained in:
rstubryan
2025-11-19 10:50:53 +07:00
parent b520b4ee54
commit af5dfa9292
@@ -106,6 +106,49 @@ const PurchaseOrderDetail = ({
return purchaseOrderItems.filter((item) => item.received_date);
}, [purchaseOrderItems]);
const groupedGoodsReceiptItems = useMemo(() => {
const uniqueProducts = Array.from(
new Map(
goodsReceiptItems.map((item) => [item.product?.id, item])
).values()
);
return uniqueProducts.map((item, index) => {
const productGroupItems = goodsReceiptItems.filter(
(groupItem) => groupItem.product?.id === item.product?.id
);
const totalQty = productGroupItems.reduce(
(sum, item) => sum + (item.total_qty || 0),
0
);
const receivedQty = productGroupItems.reduce(
(sum, item) => sum + (item.sub_qty || 0),
0
);
const unreceivedQty = totalQty - receivedQty;
const nominalReceived = productGroupItems.reduce(
(sum, item) => sum + (item.sub_qty || 0) * (item.price || 0),
0
);
const nominalUnreceived = productGroupItems.reduce(
(sum, item) => sum + unreceivedQty * (item.price || 0),
0
);
return {
productIndex: index + 1,
productName: item.product?.name || 'Unknown Product',
productGroupItems,
totalQty,
receivedQty,
unreceivedQty,
nominalReceived,
nominalUnreceived,
};
});
}, [goodsReceiptItems]);
const canUpdatePurchaseItems = useMemo(() => {
if (!initialValues?.approval) return false;
@@ -337,36 +380,29 @@ const PurchaseOrderDetail = ({
const goodsReceiptColumns: ColumnDef<PurchaseItem>[] = [
{
header: 'Header Placeholder untuk tiap Produk Penerimaan Barang',
columns: [
{
header: 'No',
cell: (props) => props.row.index + 1,
},
{
accessorKey: 'received_date',
header: 'Tanggal Penerimaan',
accessorKey: 'received_date',
cell: (props) =>
props.row.original.received_date
? formatDate(props.row.original.received_date, 'DD MMM YYYY')
: '-',
},
{
accessorKey: 'warehouse.name',
header: 'Gudang Tujuan',
accessorKey: 'warehouse.name',
cell: (props) => {
const warehouse = props.row.original.warehouse;
return warehouse?.name || '-';
},
},
{
accessorKey: 'travel_number',
header: 'No. Surat Jalan',
accessorKey: 'travel_number',
cell: (props) => props.row.original.travel_number || '-',
},
{
accessorKey: 'travel_document_path',
header: 'Dokumen Surat Jalan',
accessorKey: 'travel_document_path',
cell: (props) => {
const documentPath = props.row.original.travel_document_path;
return documentPath ? (
@@ -390,32 +426,40 @@ const PurchaseOrderDetail = ({
},
},
{
header: 'No. Armada Pengangkut',
accessorKey: 'vehicle_number',
header: 'No. Armada',
cell: (props) => props.row.original.vehicle_number || '-',
},
{
accessorKey: 'total_qty',
header: 'Jumlah Total',
accessorKey: 'total_qty',
cell: (props) => formatNumber(props.getValue() as number),
},
{
accessorKey: 'sub_qty',
header: 'Jumlah Diterima',
accessorKey: 'sub_qty',
cell: (props) => formatNumber(props.getValue() as number),
},
{
accessorKey: 'transport_per_item',
header: 'Jumlah Retur',
accessorKey: 'return_qty',
cell: (props) => formatNumber((props.getValue() as number) || 0),
},
{
header: 'Ekspedisi',
accessorKey: 'expedition_name',
cell: (props) => '-',
},
{
header: 'Transport /Item',
accessorKey: 'transport_per_item',
cell: (props) => formatCurrency(props.getValue() as number),
},
{
accessorKey: 'transport_total',
header: 'Transport Total',
accessorKey: 'transport_total',
cell: (props) => formatCurrency(props.getValue() as number),
},
],
},
];
const summaryData = [
@@ -732,15 +776,30 @@ const PurchaseOrderDetail = ({
</div>
<div className='rounded-lg border border-gray-200 overflow-hidden'>
<div className='overflow-x-auto'>
{groupedGoodsReceiptItems.length > 0 ? (
<div className='space-y-8'>
{groupedGoodsReceiptItems.map((productData) => (
<div
key={productData.productIndex}
className='border border-gray-200 rounded-lg overflow-hidden'
>
{/* Product Header */}
<div className='font-semibold text-gray-900 bg-gray-100 px-6 py-4 text-lg'>
{productData.productIndex}.{' '}
{productData.productName.toUpperCase()}
</div>
{/* Product Table */}
<Table<PurchaseItem>
data={goodsReceiptItems}
data={productData.productGroupItems}
columns={goodsReceiptColumns}
isLoading={false}
className={{
containerClassName: 'm-0',
tableWrapperClassName: 'overflow-x-auto',
tableClassName: 'w-full table-auto',
headerRowClassName: 'bg-gray-50 border-b border-gray-200',
headerRowClassName:
'bg-gray-50 border-b border-gray-200',
headerColumnClassName:
'px-4 py-3 text-sm font-semibold text-gray-700 text-left whitespace-nowrap',
bodyRowClassName:
@@ -751,6 +810,14 @@ const PurchaseOrderDetail = ({
}}
/>
</div>
))}
</div>
) : (
<div className='text-center py-8 text-gray-500'>
Tidak ada data penerimaan barang
</div>
)}
</div>
</div>
</div>
</Card>