mirror of
https://gitlab.com/mbugroup/lti-web-client.git
synced 2026-05-20 13:32:00 +00:00
refactor(FE-208,212): implement grouping of goods receipt items and enhance table rendering in PurchaseOrderDetail
This commit is contained in:
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user