diff --git a/src/components/pages/report/logistic-stock/export/PurchasesPerSupplierExport.tsx b/src/components/pages/report/logistic-stock/export/PurchasesPerSupplierExportPDF.tsx similarity index 100% rename from src/components/pages/report/logistic-stock/export/PurchasesPerSupplierExport.tsx rename to src/components/pages/report/logistic-stock/export/PurchasesPerSupplierExportPDF.tsx diff --git a/src/components/pages/report/logistic-stock/export/PurchasesPerSupplierExportXLSX.tsx b/src/components/pages/report/logistic-stock/export/PurchasesPerSupplierExportXLSX.tsx new file mode 100644 index 00000000..110bd65e --- /dev/null +++ b/src/components/pages/report/logistic-stock/export/PurchasesPerSupplierExportXLSX.tsx @@ -0,0 +1,101 @@ +'use client'; + +import ExcelJS from 'exceljs'; +import { formatDate, formatCurrency, formatNumber } from '@/lib/helper'; +import { LogisticPurchasePerSupplierReport } from '@/types/api/report/logistic-stock'; + +interface PurchasesPerSupplierExportExcelParams { + data: LogisticPurchasePerSupplierReport[]; +} + +export const generatePurchasesPerSupplierExcel = async ( + params: PurchasesPerSupplierExportExcelParams +): Promise => { + if (!params.data || params.data.length === 0) { + return; + } + + const workbook = new ExcelJS.Workbook(); + + const columns = [ + { header: 'No', key: 'no', width: 5 }, + { header: 'Tanggal Terima', key: 'receiveDate', width: 15 }, + { header: 'Tanggal PO', key: 'poDate', width: 15 }, + { header: 'No. Referensi', key: 'poNumber', width: 15 }, + { header: 'Nama Produk', key: 'productName', width: 30 }, + { header: 'Tujuan', key: 'warehouse', width: 20 }, + { header: 'QTY', key: 'qty', width: 10 }, + { header: 'Harga Beli (Rp)', key: 'unitPrice', width: 18 }, + { header: 'Value Harga Beli (Rp)', key: 'purchaseValue', width: 20 }, + { header: 'Transport (Rp)', key: 'transportUnitPrice', width: 15 }, + { header: 'Value Transport (Rp)', key: 'transportValue', width: 20 }, + { header: 'Jumlah (Rp)', key: 'totalAmount', width: 18 }, + { header: 'Ekspedisi', key: 'expedition', width: 15 }, + { header: 'Surat Jalan', key: 'deliveryNumber', width: 15 }, + ]; + + for (const supplierReport of params.data) { + const supplierData = supplierReport.rows; + const supplierName = supplierReport.supplier?.name || 'Unknown Supplier'; + + const worksheet = workbook.addWorksheet(supplierName.substring(0, 31)); + worksheet.columns = columns; + + supplierData.forEach((item, index) => { + worksheet.addRow({ + no: index + 1, + receiveDate: item.receive_date + ? formatDate(item.receive_date, 'DD MMM YYYY') + : '', + poDate: item.po_date ? formatDate(item.po_date, 'DD MMM YYYY') : '', + poNumber: item.po_number || '', + productName: item.product?.name || '', + warehouse: item.warehouse?.name || '', + qty: formatNumber(item.qty || 0), + unitPrice: formatCurrency(item.unit_price || 0), + purchaseValue: formatCurrency(item.purchase_value || 0), + transportUnitPrice: formatCurrency(item.transport_unit_price || 0), + transportValue: formatCurrency(item.transport_value || 0), + totalAmount: formatCurrency(item.total_amount || 0), + expedition: item.expedition || '', + deliveryNumber: item.delivery_number || '', + }); + }); + + if (supplierReport.summary) { + worksheet.addRow({ + no: 'Total', + receiveDate: '', + poDate: '', + poNumber: '', + productName: '', + warehouse: '', + qty: formatNumber(supplierReport.summary.total_qty || 0), + unitPrice: '', + purchaseValue: formatCurrency( + supplierReport.summary.total_purchase_value || 0 + ), + transportUnitPrice: '', + transportValue: formatCurrency( + supplierReport.summary.total_transport_value || 0 + ), + totalAmount: formatCurrency(supplierReport.summary.total_amount || 0), + expedition: '', + deliveryNumber: '', + }); + } + } + + const filename = `laporan-pembelian-per-supplier-dicetak-pada-${formatDate(new Date(), 'YYYY-MM-DD-HHmm')}.xlsx`; + + const buffer = await workbook.xlsx.writeBuffer(); + const blob = new Blob([buffer], { + type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', + }); + const url = window.URL.createObjectURL(blob); + const link = document.createElement('a'); + link.href = url; + link.download = filename; + link.click(); + window.URL.revokeObjectURL(url); +}; diff --git a/src/components/pages/report/logistic-stock/tab/PurchasesPerSupplierTab.tsx b/src/components/pages/report/logistic-stock/tab/PurchasesPerSupplierTab.tsx index 5366f3cd..1c5a4f2d 100644 --- a/src/components/pages/report/logistic-stock/tab/PurchasesPerSupplierTab.tsx +++ b/src/components/pages/report/logistic-stock/tab/PurchasesPerSupplierTab.tsx @@ -26,9 +26,9 @@ import Button from '@/components/Button'; import Dropdown from '@/components/Dropdown'; import MenuItem from '@/components/menu/MenuItem'; import Menu from '@/components/menu/Menu'; -import { generatePurchasesPerSupplierPDF } from '@/components/pages/report/logistic-stock/export/PurchasesPerSupplierExport'; +import { generatePurchasesPerSupplierPDF } from '@/components/pages/report/logistic-stock/export/PurchasesPerSupplierExportPDF'; +import { generatePurchasesPerSupplierExcel } from '@/components/pages/report/logistic-stock/export/PurchasesPerSupplierExportXLSX'; import toast from 'react-hot-toast'; -import * as XLSX from 'xlsx'; import { Icon } from '@iconify/react'; const PurchasesPerSupplierTab = () => { @@ -355,98 +355,14 @@ const PurchasesPerSupplierTab = () => { return; } - const workbook = XLSX.utils.book_new(); - - allDataForExport.forEach((supplierReport) => { - const supplierData = supplierReport.rows; - const supplierName = - supplierReport.supplier?.name || 'Unknown Supplier'; - - const excelData: { [key: string]: string | number }[] = - supplierData.map((item, index) => ({ - No: index + 1, - 'Tanggal Terima': item.receive_date - ? formatDate(item.receive_date, 'DD MMM YYYY') - : '', - 'Tanggal PO': item.po_date - ? formatDate(item.po_date, 'DD MMM YYYY') - : '', - 'No. Referensi': item.po_number || '', - 'Nama Produk': item.product?.name || '', - Tujuan: item.warehouse?.name || '', - QTY: item.qty || 0, - 'Harga Beli (Rp)': item.unit_price || 0, - 'Value Harga Beli (Rp)': item.purchase_value || 0, - 'Transport (Rp)': item.transport_unit_price || 0, - 'Value Transport (Rp)': item.transport_value || 0, - 'Jumlah (Rp)': item.total_amount || 0, - Ekspedisi: item.expedition || '', - 'Surat Jalan': item.delivery_number || '', - })); - - if (supplierReport.summary) { - excelData.push({ - No: 'Total', - 'Tanggal Terima': '', - 'Tanggal PO': '', - 'No. Referensi': '', - 'Nama Produk': '', - Tujuan: '', - QTY: supplierReport.summary.total_qty || 0, - 'Harga Beli (Rp)': '', - 'Value Harga Beli (Rp)': - supplierReport.summary.total_purchase_value || 0, - 'Transport (Rp)': '', - 'Value Transport (Rp)': - supplierReport.summary.total_transport_value || 0, - 'Jumlah (Rp)': supplierReport.summary.total_amount || 0, - Ekspedisi: '', - 'Surat Jalan': '', - }); - } - - const worksheet = XLSX.utils.json_to_sheet(excelData); - - const colWidths = [ - { wch: 5 }, // No - { wch: 15 }, // Tanggal Terima - { wch: 15 }, // Tanggal PO - { wch: 15 }, // No. Referensi - { wch: 30 }, // Nama Produk - { wch: 20 }, // Tujuan - { wch: 10 }, // QTY - { wch: 18 }, // Harga Beli - { wch: 20 }, // Value Harga Beli - { wch: 15 }, // Transport - { wch: 20 }, // Value Transport - { wch: 18 }, // Jumlah - { wch: 15 }, // Ekspedisi - { wch: 15 }, // Surat Jalan - ]; - worksheet['!cols'] = colWidths; - - const sheetName = - supplierName.length > 31 - ? supplierName.substring(0, 31) - : supplierName; - XLSX.utils.book_append_sheet(workbook, worksheet, sheetName); - }); - - const filename = `laporan-pembelian-per-supplier-dicetak-pada-${formatDate(new Date(), 'YYYY-MM-DD-HHmm')}.xlsx`; - - XLSX.writeFile(workbook, filename); + await generatePurchasesPerSupplierExcel({ data: allDataForExport }); toast.success('Excel berhasil dibuat dan diunduh.'); } catch { toast.error('Gagal membuat Excel. Silakan coba lagi.'); } finally { setIsExcelExportLoading(false); } - }, [ - logisticPurchasePerSupplierExport, - tableFilterState, - areaOptions, - supplierOptions, - ]); + }, [logisticPurchasePerSupplierExport]); const handleExportPdf = useCallback(async () => { setIsPdfExportLoading(true);