From af5dfa92923e1a138edcbd0e44b0e9cc5bec8c57 Mon Sep 17 00:00:00 2001 From: rstubryan Date: Wed, 19 Nov 2025 10:50:53 +0700 Subject: [PATCH] refactor(FE-208,212): implement grouping of goods receipt items and enhance table rendering in PurchaseOrderDetail --- .../purchase/order/PurchaseOrderDetail.tsx | 259 +++++++++++------- 1 file changed, 163 insertions(+), 96 deletions(-) diff --git a/src/components/pages/purchase/order/PurchaseOrderDetail.tsx b/src/components/pages/purchase/order/PurchaseOrderDetail.tsx index 311c8bba..1d4867f5 100644 --- a/src/components/pages/purchase/order/PurchaseOrderDetail.tsx +++ b/src/components/pages/purchase/order/PurchaseOrderDetail.tsx @@ -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,84 +380,85 @@ const PurchaseOrderDetail = ({ const goodsReceiptColumns: ColumnDef[] = [ { - header: 'Header Placeholder untuk tiap Produk Penerimaan Barang', - columns: [ - { - header: 'No', - cell: (props) => props.row.index + 1, - }, - { - accessorKey: 'received_date', - header: 'Tanggal Penerimaan', - cell: (props) => - props.row.original.received_date - ? formatDate(props.row.original.received_date, 'DD MMM YYYY') - : '-', - }, - { - accessorKey: 'warehouse.name', - header: 'Gudang Tujuan', - cell: (props) => { - const warehouse = props.row.original.warehouse; - return warehouse?.name || '-'; - }, - }, - { - accessorKey: 'travel_number', - header: 'No. Surat Jalan', - cell: (props) => props.row.original.travel_number || '-', - }, - { - accessorKey: 'travel_document_path', - header: 'Dokumen Surat Jalan', - cell: (props) => { - const documentPath = props.row.original.travel_document_path; - return documentPath ? ( - - ) : ( - '-' - ); - }, - }, - { - accessorKey: 'vehicle_number', - header: 'No. Armada', - cell: (props) => props.row.original.vehicle_number || '-', - }, - { - accessorKey: 'total_qty', - header: 'Jumlah Total', - cell: (props) => formatNumber(props.getValue() as number), - }, - { - accessorKey: 'sub_qty', - header: 'Jumlah Diterima', - cell: (props) => formatNumber(props.getValue() as number), - }, - { - accessorKey: 'transport_per_item', - header: 'Transport /Item', - cell: (props) => formatCurrency(props.getValue() as number), - }, - { - accessorKey: 'transport_total', - header: 'Transport Total', - cell: (props) => formatCurrency(props.getValue() as number), - }, - ], + header: 'Tanggal Penerimaan', + accessorKey: 'received_date', + cell: (props) => + props.row.original.received_date + ? formatDate(props.row.original.received_date, 'DD MMM YYYY') + : '-', + }, + { + header: 'Gudang Tujuan', + accessorKey: 'warehouse.name', + cell: (props) => { + const warehouse = props.row.original.warehouse; + return warehouse?.name || '-'; + }, + }, + { + header: 'No. Surat Jalan', + accessorKey: 'travel_number', + cell: (props) => props.row.original.travel_number || '-', + }, + { + header: 'Dokumen Surat Jalan', + accessorKey: 'travel_document_path', + cell: (props) => { + const documentPath = props.row.original.travel_document_path; + return documentPath ? ( + + ) : ( + '-' + ); + }, + }, + { + header: 'No. Armada Pengangkut', + accessorKey: 'vehicle_number', + cell: (props) => props.row.original.vehicle_number || '-', + }, + { + header: 'Jumlah Total', + accessorKey: 'total_qty', + cell: (props) => formatNumber(props.getValue() as number), + }, + { + header: 'Jumlah Diterima', + accessorKey: 'sub_qty', + cell: (props) => formatNumber(props.getValue() as number), + }, + { + 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), + }, + { + header: 'Transport Total', + accessorKey: 'transport_total', + cell: (props) => formatCurrency(props.getValue() as number), }, ]; @@ -732,24 +776,47 @@ const PurchaseOrderDetail = ({
- - data={goodsReceiptItems} - 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', - headerColumnClassName: - 'px-4 py-3 text-sm font-semibold text-gray-700 text-left whitespace-nowrap', - bodyRowClassName: - 'border-b border-gray-100 hover:bg-gray-50 transition-colors', - bodyColumnClassName: - 'px-4 py-3 text-sm text-gray-900 whitespace-nowrap', - paginationClassName: 'hidden', - }} - /> + {groupedGoodsReceiptItems.length > 0 ? ( +
+ {groupedGoodsReceiptItems.map((productData) => ( +
+ {/* Product Header */} +
+ {productData.productIndex}.{' '} + {productData.productName.toUpperCase()} +
+ + {/* Product Table */} + + 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', + headerColumnClassName: + 'px-4 py-3 text-sm font-semibold text-gray-700 text-left whitespace-nowrap', + bodyRowClassName: + 'border-b border-gray-100 hover:bg-gray-50 transition-colors', + bodyColumnClassName: + 'px-4 py-3 text-sm text-gray-900 whitespace-nowrap', + paginationClassName: 'hidden', + }} + /> +
+ ))} +
+ ) : ( +
+ Tidak ada data penerimaan barang +
+ )}