diff --git a/src/components/pages/report/finance/export/CustomerPaymentExportPDF.tsx b/src/components/pages/report/finance/export/CustomerPaymentExportPDF.tsx index b0cb5c8d..4a6f0238 100644 --- a/src/components/pages/report/finance/export/CustomerPaymentExportPDF.tsx +++ b/src/components/pages/report/finance/export/CustomerPaymentExportPDF.tsx @@ -7,6 +7,7 @@ import { StyleSheet, Font, pdf, + Text, } from '@react-pdf/renderer'; import { @@ -16,12 +17,7 @@ import { formatTitleCase, } from '@/lib/helper'; import { CustomerPaymentReport } from '@/types/api/report/customer-payment'; -import { - PdfTable, - PdfColumn, - PdfTbodyCell, - PdfTfootCell, -} from '@/components/helper/pdf/table'; +import { PdfTable, PdfColumn } from '@/components/helper/pdf/table'; import { PdfParamBadge } from '@/components/helper/pdf/badge/PdfParamBadge'; import { PdfStatusBadge } from '@/components/helper/pdf/badge/PdfStatusBadge'; import { PdfTypography } from '@/components/helper/pdf/typography/PdfTypography'; @@ -61,172 +57,183 @@ interface CustomerPaymentExportPDFParams { }; } -const getTableColumns = (): PdfColumn[] => [ - { key: 'no', header: 'No', flex: 0.5, align: 'center' }, - { key: 'trans_date', header: 'Tanggal DO', flex: 1.2, align: 'center' }, +const getTableColumns = ( + summary?: CustomerPaymentReport['summary'] +): PdfColumn[] => [ + { + key: 'no', + header: 'No', + flex: 0.5, + align: 'center', + cell: ({ row, index }) => index + 1, + footer: 'Total', + }, + { + key: 'trans_date', + header: 'Tanggal DO', + flex: 1.2, + align: 'center', + cell: ({ row }) => + row.trans_date ? formatDate(row.trans_date, 'DD MMM YY') : '-', + footer: '', + }, { key: 'delivery_date', header: 'Tanggal Realisasi', flex: 1.2, align: 'center', + cell: ({ row }) => + row.delivery_date ? formatDate(row.delivery_date, 'DD MMM YY') : '-', + footer: '', + }, + { + key: 'aging', + header: 'Aging', + flex: 0.8, + align: 'center', + cell: ({ row }) => + row.aging_day != null ? `${formatNumber(row.aging_day)} hari` : '-', + footer: '', + }, + { + key: 'reference', + header: 'Referensi', + flex: 1.5, + align: 'left', + cell: ({ row }) => row.reference || '-', + footer: '', + }, + { + key: 'vehicle_numbers', + header: 'No Polisi', + flex: 1.2, + align: 'left', + cell: ({ row }) => + Array.isArray(row.vehicle_numbers) && row.vehicle_numbers.length > 0 + ? row.vehicle_numbers.join(', ') + : '-', + footer: '', + }, + { + key: 'qty', + header: 'Qty', + flex: 0.8, + align: 'right', + cell: ({ row }) => formatNumber(row.qty), + footer: summary ? formatNumber(summary.total_qty || 0) : '', + footerAlign: 'right', + }, + { + key: 'weight', + header: 'Berat', + flex: 1, + align: 'right', + cell: ({ row }) => formatNumber(row.weight), + footer: summary ? formatNumber(summary.total_weight || 0) : '', + footerAlign: 'right', + }, + { + key: 'average_weight', + header: 'Rata-Rata', + flex: 0.8, + align: 'right', + cell: ({ row }) => formatNumber(row.average_weight), + footer: '', + }, + { + key: 'unit_price', + header: 'Harga/Unit (Rp)', + flex: 1.2, + align: 'right', + cell: ({ row }) => formatCurrency(row.unit_price), + footer: '', + }, + { + key: 'final_price', + header: 'Harga Akhir (Rp)', + flex: 1.2, + align: 'right', + cell: ({ row }) => formatCurrency(row.final_price), + footer: summary ? formatCurrency(summary.total_final_amount || 0) : '', + footerAlign: 'right', + }, + { + key: 'total_price', + header: 'Total (Rp)', + flex: 1.2, + align: 'right', + cell: ({ row }) => formatCurrency(row.total_price), + footer: summary ? formatCurrency(summary.total_grand_amount || 0) : '', + footerAlign: 'right', }, - { key: 'aging', header: 'Aging', flex: 0.8, align: 'center' }, - { key: 'reference', header: 'Referensi', flex: 1.5, align: 'left' }, - { key: 'vehicle_numbers', header: 'No Polisi', flex: 1.2, align: 'left' }, - { key: 'qty', header: 'Qty', flex: 0.8, align: 'right' }, - { key: 'weight', header: 'Berat', flex: 1, align: 'right' }, - { key: 'average_weight', header: 'Rata-Rata', flex: 0.8, align: 'right' }, - { key: 'unit_price', header: 'Harga/Unit (Rp)', flex: 1.2, align: 'right' }, - { key: 'final_price', header: 'Harga Akhir (Rp)', flex: 1.2, align: 'right' }, - { key: 'total_price', header: 'Total (Rp)', flex: 1.2, align: 'right' }, { key: 'payment_amount', header: 'Pembayaran (Rp)', flex: 1.2, align: 'right', + cell: ({ row }) => formatCurrency(row.payment_amount), + footer: summary ? formatCurrency(summary.total_payment || 0) : '', + footerAlign: 'right', }, { key: 'accounts_receivable', header: 'Saldo (Rp)', flex: 1.2, align: 'right', + cell: ({ row }) => ( + + {formatCurrency(row.accounts_receivable)} + + ), + footer: summary + ? formatCurrency(summary.total_accounts_receivable || 0) + : '', + footerAlign: 'right', + footerColor: + (summary?.total_accounts_receivable || 0) < 0 ? 'red' : undefined, }, - { key: 'status', header: 'Keterangan', flex: 1.5, align: 'center' }, - { key: 'pickup_info', header: 'Pengambilan', flex: 1, align: 'left' }, - { key: 'sales_person', header: 'Sales', flex: 1.5, align: 'left' }, -]; - -const getTableData = ( - rows: CustomerPaymentReport['rows'] -): PdfTbodyCell[][] => { - return rows.map((item, index) => [ - { key: 'no', value: index + 1 }, - { - key: 'trans_date', - value: item.trans_date ? formatDate(item.trans_date, 'DD MMM YY') : '-', - }, - { - key: 'delivery_date', - value: item.delivery_date - ? formatDate(item.delivery_date, 'DD MMM YY') - : '-', - }, - { - key: 'aging', - value: - item.aging_day != null ? `${formatNumber(item.aging_day)} hari` : '-', - }, - { key: 'reference', value: item.reference || '-' }, - { - key: 'vehicle_numbers', - value: - Array.isArray(item.vehicle_numbers) && item.vehicle_numbers.length > 0 - ? item.vehicle_numbers.join(', ') - : '-', - }, - { key: 'qty', value: formatNumber(item.qty), align: 'right' }, - { key: 'weight', value: formatNumber(item.weight), align: 'right' }, - { - key: 'average_weight', - value: formatNumber(item.average_weight), - align: 'right', - }, - { - key: 'unit_price', - value: formatCurrency(item.unit_price), - align: 'right', - }, - { - key: 'final_price', - value: formatCurrency(item.final_price), - align: 'right', - }, - { - key: 'total_price', - value: formatCurrency(item.total_price), - align: 'right', - }, - { - key: 'payment_amount', - value: formatCurrency(item.payment_amount), - align: 'right', - }, - { - key: 'accounts_receivable', - value: formatCurrency(item.accounts_receivable), - align: 'right', - color: item.accounts_receivable < 0 ? 'red' : undefined, - }, - { - key: 'status', - value: item.status ? ( + { + key: 'status', + header: 'Keterangan', + flex: 1.5, + align: 'center', + cell: ({ row }) => + row.status ? ( - {formatTitleCase(item.status)} + {formatTitleCase(row.status)} ) : ( '-' ), - }, - { - key: 'pickup_info', - value: - Array.isArray(item.pickup_info) && item.pickup_info.length > 0 - ? item.pickup_info.join(', ') - : '-', - }, - { key: 'sales_person', value: item.sales_person || '-' }, - ]); -}; - -const getTableFooter = ( - summary: CustomerPaymentReport['summary'] -): PdfTfootCell[] => [ - { key: 'no', value: 'Total' }, - { key: 'trans_date', value: '' }, - { key: 'delivery_date', value: '' }, - { key: 'aging', value: '' }, - { key: 'reference', value: '' }, - { key: 'vehicle_numbers', value: '' }, - { key: 'qty', value: formatNumber(summary?.total_qty || 0), align: 'right' }, - { - key: 'weight', - value: formatNumber(summary?.total_weight || 0), - align: 'right', - }, - { key: 'average_weight', value: '' }, - { key: 'unit_price', value: '' }, - { - key: 'final_price', - value: formatCurrency(summary?.total_final_amount || 0), - align: 'right', + footer: '', }, { - key: 'total_price', - value: formatCurrency(summary?.total_grand_amount || 0), - align: 'right', + key: 'pickup_info', + header: 'Pengambilan', + flex: 1, + align: 'left', + cell: ({ row }) => + Array.isArray(row.pickup_info) && row.pickup_info.length > 0 + ? row.pickup_info.join(', ') + : '-', + footer: '', }, { - key: 'payment_amount', - value: formatCurrency(summary?.total_payment || 0), - align: 'right', + key: 'sales_person', + header: 'Sales', + flex: 1.5, + align: 'left', + cell: ({ row }) => row.sales_person || '-', + footer: '', }, - { - key: 'accounts_receivable', - value: formatCurrency(summary?.total_accounts_receivable || 0), - align: 'right', - color: (summary?.total_accounts_receivable || 0) < 0 ? 'red' : undefined, - }, - { key: 'status', value: '' }, - { key: 'pickup_info', value: '' }, - { key: 'sales_person', value: '' }, ]; const createPDFDocument = (params: CustomerPaymentExportPDFParams) => { @@ -276,13 +283,9 @@ const createPDFDocument = (params: CustomerPaymentExportPDFParams) => { {/* Table */} [ - { key: 'no', header: 'No', flex: 0.5, align: 'center' }, - { key: 'pr_number', header: 'No. PR', flex: 1, align: 'left' }, - { key: 'po_number', header: 'No. PO', flex: 1, align: 'left' }, - { - key: 'received_date', - header: 'Tgl Terima/Bayar', - flex: 0.7, - align: 'center', - }, - { key: 'po_date', header: 'Tgl PO', flex: 0.7, align: 'center' }, - { key: 'aging', header: 'Aging', flex: 0.6, align: 'center' }, - { key: 'area', header: 'Area', flex: 1, align: 'left' }, - { key: 'warehouse', header: 'Gudang', flex: 1, align: 'left' }, - { key: 'due_date', header: 'Jatuh Tempo', flex: 1, align: 'center' }, - { key: 'due_status', header: 'Status Jatuh Tempo', flex: 2, align: 'center' }, - { - key: 'total_price', - header: 'Nominal Pembelian (Rp)', - flex: 1.5, - align: 'right', - }, - { - key: 'payment_price', - header: 'Pembayaran (Rp)', - flex: 1.5, - align: 'right', - }, - { - key: 'balance', - header: 'Sisa Saldo Hutang (Rp)', - flex: 1.5, - align: 'right', - }, - { key: 'status', header: 'Status', flex: 1.2, align: 'center' }, - { key: 'travel_number', header: 'No. Perjalanan', flex: 1, align: 'left' }, -]; +const getTableColumns = (total?: DebtSupplier['total']): PdfColumn[] => { + type DebtRow = DebtSupplier['rows'][number]; -const getTableData = (rows: DebtSupplier['rows']): PdfTbodyCell[][] => { - return rows.map((item, index) => [ - { key: 'no', value: index + 1 }, - { key: 'pr_number', value: item.pr_number || '-' }, - { key: 'po_number', value: item.po_number || '-' }, + return [ + { + key: 'no', + header: 'No', + flex: 0.5, + align: 'center', + cell: ({ row, index }) => index + 1, + footer: 'Total', + }, + { + key: 'pr_number', + header: 'No. PR', + flex: 1, + align: 'left', + cell: ({ row }) => (row as unknown as DebtRow).pr_number || '-', + footer: '', + }, + { + key: 'po_number', + header: 'No. PO', + flex: 1, + align: 'left', + cell: ({ row }) => (row as unknown as DebtRow).po_number || '-', + footer: '', + }, { key: 'received_date', - value: item.received_date - ? formatDate(item.received_date, 'DD MMM YY') - : '-', + header: 'Tgl Terima/Bayar', + flex: 0.7, + align: 'center', + cell: ({ row }) => + (row as unknown as DebtRow).received_date + ? formatDate((row as unknown as DebtRow).received_date, 'DD MMM YY') + : '-', + footer: '', }, { key: 'po_date', - value: item.po_date ? formatDate(item.po_date, 'DD MMM YY') : '-', + header: 'Tgl PO', + flex: 0.7, + align: 'center', + cell: ({ row }) => + (row as unknown as DebtRow).po_date + ? formatDate((row as unknown as DebtRow).po_date, 'DD MMM YY') + : '-', + footer: '', }, { key: 'aging', - value: item.aging != null ? `${formatNumber(item.aging)}` : '-', + header: 'Aging', + flex: 0.6, + align: 'center', + cell: ({ row }) => + (row as unknown as DebtRow).aging != null + ? `${formatNumber((row as unknown as DebtRow).aging)}` + : '-', + footer: total ? formatNumber(total.aging || 0) + ' Hari' : '', + }, + { + key: 'area', + header: 'Area', + flex: 1, + align: 'left', + cell: ({ row }) => (row as unknown as DebtRow).area?.name || '-', + footer: '', + }, + { + key: 'warehouse', + header: 'Gudang', + flex: 1, + align: 'left', + cell: ({ row }) => (row as unknown as DebtRow).warehouse?.name || '-', + footer: '', }, - { key: 'area', value: item.area?.name || '-' }, - { key: 'warehouse', value: item.warehouse?.name || '-' }, { key: 'due_date', - value: item.due_date ? formatDate(item.due_date, 'DD MMM YY') : '-', + header: 'Jatuh Tempo', + flex: 1, + align: 'center', + cell: ({ row }) => + (row as unknown as DebtRow).due_date + ? formatDate((row as unknown as DebtRow).due_date, 'DD MMM YY') + : '-', + footer: '', }, { key: 'due_status', - value: - item.due_status && item.due_status !== '-' ? ( + header: 'Status Jatuh Tempo', + flex: 2, + align: 'center', + cell: ({ row }) => + (row as unknown as DebtRow).due_status && + (row as unknown as DebtRow).due_status !== '-' ? ( - {item.due_status} + {(row as unknown as DebtRow).due_status} ) : ( '-' ), + footer: '', }, { key: 'total_price', - value: formatCurrency(item.total_price), + header: 'Nominal Pembelian (Rp)', + flex: 1.5, align: 'right', - color: item.total_price < 0 ? 'red' : undefined, + cell: ({ row }) => ( + + {formatCurrency((row as unknown as DebtRow).total_price)} + + ), + footer: total ? formatCurrency(total.total_price || 0) : '', + footerAlign: 'right', }, { key: 'payment_price', - value: formatCurrency(item.payment_price), + header: 'Pembayaran (Rp)', + flex: 1.5, align: 'right', - color: item.payment_price < 0 ? 'red' : undefined, + cell: ({ row }) => ( + + {formatCurrency((row as unknown as DebtRow).payment_price)} + + ), + footer: total ? formatCurrency(total.payment_price || 0) : '', + footerAlign: 'right', }, { key: 'balance', - value: formatCurrency(item.balance), + header: 'Sisa Saldo Hutang (Rp)', + flex: 1.5, align: 'right', - color: item.balance < 0 ? 'red' : undefined, + cell: ({ row }) => ( + + {formatCurrency((row as unknown as DebtRow).balance)} + + ), + footer: total ? formatCurrency(total.debt_price || 0) : '', + footerAlign: 'right', + footerColor: (total?.debt_price || 0) < 0 ? 'red' : undefined, }, { key: 'status', - value: - item.status && item.status !== '-' ? ( + header: 'Status', + flex: 1.2, + align: 'center', + cell: ({ row }) => + (row as unknown as DebtRow).status && + (row as unknown as DebtRow).status !== '-' ? ( - {item.status} + {(row as unknown as DebtRow).status} ) : ( '-' ), + footer: '', }, - { key: 'travel_number', value: item.travel_number || '-' }, - ]); + { + key: 'travel_number', + header: 'No. Perjalanan', + flex: 1, + align: 'left', + cell: ({ row }) => (row as unknown as DebtRow).travel_number || '-', + footer: '', + }, + ]; }; -const getTableFooter = (total: DebtSupplier['total']): PdfTfootCell[] => [ - { key: 'no', value: 'Total' }, - { key: 'pr_number', value: '' }, - { key: 'po_number', value: '' }, - { key: 'received_date', value: '' }, - { key: 'po_date', value: '' }, - { key: 'aging', value: formatNumber(total?.aging || 0) + ' Hari' }, - { key: 'area', value: '' }, - { key: 'warehouse', value: '' }, - { key: 'due_date', value: '' }, - { key: 'due_status', value: '' }, - { - key: 'total_price', - value: formatCurrency(total?.total_price || 0), - align: 'right', - }, - { - key: 'payment_price', - value: formatCurrency(total?.payment_price || 0), - align: 'right', - }, - { - key: 'balance', - value: formatCurrency(total?.debt_price || 0), - align: 'right', - color: (total?.debt_price || 0) < 0 ? 'red' : undefined, - }, - { key: 'status', value: '' }, - { key: 'travel_number', value: '' }, -]; - interface DebtSupplierExportPDFParams { data: DebtSupplier[]; params?: { @@ -263,13 +324,9 @@ const createPDFDocument = (params: DebtSupplierExportPDFParams) => { {/* Table */} []} + showFooter={!!supplierReport.total} firstRow={ typeof supplierReport.initial_balance === 'number' && supplierReport.initial_balance !== 0 diff --git a/src/components/pages/report/logistic-stock/export/PurchasesPerSupplierExportPDF.tsx b/src/components/pages/report/logistic-stock/export/PurchasesPerSupplierExportPDF.tsx index 3423ca69..d265d08f 100644 --- a/src/components/pages/report/logistic-stock/export/PurchasesPerSupplierExportPDF.tsx +++ b/src/components/pages/report/logistic-stock/export/PurchasesPerSupplierExportPDF.tsx @@ -10,12 +10,7 @@ import { } from '@react-pdf/renderer'; import { LogisticPurchasePerSupplierReport } from '@/types/api/report/logistic-stock'; import { formatCurrency, formatDate, formatNumber } from '@/lib/helper'; -import { - PdfTable, - PdfColumn, - PdfTbodyCell, - PdfTfootCell, -} from '@/components/helper/pdf/table'; +import { PdfTable, PdfColumn } from '@/components/helper/pdf/table'; import { PdfParamBadge } from '@/components/helper/pdf/badge/PdfParamBadge'; import { PdfStatusBadge } from '@/components/helper/pdf/badge/PdfStatusBadge'; import { PdfTypography } from '@/components/helper/pdf/typography/PdfTypography'; @@ -58,90 +53,118 @@ interface PurchasesPerSupplierExportParams { }; } -const getTableColumns = (): PdfColumn[] => [ - { key: 'no', header: 'No', flex: 0.5, align: 'center' }, +const getTableColumns = ( + summary?: LogisticPurchasePerSupplierReport['summary'] +): PdfColumn[] => [ + { + key: 'no', + header: 'No', + flex: 0.5, + align: 'center', + cell: ({ row, index }) => index + 1, + footer: 'Total', + }, { key: 'receive_date', header: 'Tanggal Terima', flex: 1.2, align: 'center', + cell: ({ row }) => + row.receive_date ? formatDate(row.receive_date, 'DD MMM YY') : '-', + footer: '', + }, + { + key: 'po_date', + header: 'Tanggal PO', + flex: 1.2, + align: 'center', + cell: ({ row }) => + row.po_date ? formatDate(row.po_date, 'DD MMM YY') : '-', + footer: '', + }, + { + key: 'po_number', + header: 'No. Referensi', + flex: 1.5, + align: 'left', + cell: ({ row }) => row.po_number || '-', + footer: '', + }, + { + key: 'product', + header: 'Nama Produk', + flex: 2, + align: 'left', + cell: ({ row }) => row.product?.name || '-', + footer: '', + }, + { + key: 'warehouse', + header: 'Tujuan', + flex: 1.5, + align: 'left', + cell: ({ row }) => row.warehouse?.name || '-', + footer: '', + }, + { + key: 'qty', + header: 'QTY', + flex: 0.8, + align: 'right', + cell: ({ row }) => formatNumber(row.qty || 0), + footer: summary ? formatNumber(summary.total_qty || 0) : '', + footerAlign: 'right', + }, + { + key: 'unit_price', + header: 'Harga Beli (Rp)', + flex: 1.5, + align: 'right', + cell: ({ row }) => formatCurrency(row.unit_price || 0), + footer: '', }, - { key: 'po_date', header: 'Tanggal PO', flex: 1.2, align: 'center' }, - { key: 'po_number', header: 'No. Referensi', flex: 1.5, align: 'left' }, - { key: 'product', header: 'Nama Produk', flex: 2, align: 'left' }, - { key: 'warehouse', header: 'Tujuan', flex: 1.5, align: 'left' }, - { key: 'qty', header: 'QTY', flex: 0.8, align: 'right' }, - { key: 'unit_price', header: 'Harga Beli (Rp)', flex: 1.5, align: 'right' }, { key: 'purchase_value', header: 'Value Harga Beli (Rp)', flex: 1.8, align: 'right', + cell: ({ row }) => formatCurrency(row.purchase_value || 0), + footer: summary ? formatCurrency(summary.total_purchase_value || 0) : '', + footerAlign: 'right', }, { key: 'transport_unit_price', header: 'Transport (Rp)', flex: 1.3, align: 'right', + cell: ({ row }) => formatCurrency(row.transport_unit_price || 0), + footer: '', }, { key: 'transport_value', header: 'Value Transport (Rp)', flex: 1.8, align: 'right', + cell: ({ row }) => formatCurrency(row.transport_value || 0), + footer: summary ? formatCurrency(summary.total_transport_value || 0) : '', + footerAlign: 'right', }, - { key: 'total_amount', header: 'Jumlah (Rp)', flex: 1.5, align: 'right' }, - { key: 'expedition', header: 'Ekspedisi', flex: 1.2, align: 'center' }, - { key: 'delivery_number', header: 'Surat Jalan', flex: 1.2, align: 'left' }, -]; - -const getTableData = ( - rows: LogisticPurchasePerSupplierReport['rows'] -): PdfTbodyCell[][] => { - return rows.map((item, index) => [ - { key: 'no', value: index + 1 }, - { - key: 'receive_date', - value: item.receive_date - ? formatDate(item.receive_date, 'DD MMM YY') - : '-', - }, - { - key: 'po_date', - value: item.po_date ? formatDate(item.po_date, 'DD MMM YY') : '-', - }, - { key: 'po_number', value: item.po_number || '-' }, - { key: 'product', value: item.product?.name || '-' }, - { key: 'warehouse', value: item.warehouse?.name || '-' }, - { key: 'qty', value: formatNumber(item.qty || 0), align: 'right' }, - { - key: 'unit_price', - value: formatCurrency(item.unit_price || 0), - align: 'right', - }, - { - key: 'purchase_value', - value: formatCurrency(item.purchase_value || 0), - align: 'right', - }, - { - key: 'transport_unit_price', - value: formatCurrency(item.transport_unit_price || 0), - align: 'right', - }, - { - key: 'transport_value', - value: formatCurrency(item.transport_value || 0), - align: 'right', - }, - { - key: 'total_amount', - value: formatCurrency(item.total_amount || 0), - align: 'right', - }, - { - key: 'expedition', - value: item.expedition ? ( + { + key: 'total_amount', + header: 'Jumlah (Rp)', + flex: 1.5, + align: 'right', + cell: ({ row }) => formatCurrency(row.total_amount || 0), + footer: summary ? formatCurrency(summary.total_amount || 0) : '', + footerAlign: 'right', + }, + { + key: 'expedition', + header: 'Ekspedisi', + flex: 1.2, + align: 'center', + cell: ({ row }) => + row.expedition ? ( - {item.expedition} + {row.expedition} ) : ( '-' ), - }, - { key: 'delivery_number', value: item.delivery_number || '-' }, - ]); -}; - -const getTableFooter = ( - summary: LogisticPurchasePerSupplierReport['summary'] -): PdfTfootCell[] => [ - { key: 'no', value: 'Total' }, - { key: 'receive_date', value: '' }, - { key: 'po_date', value: '' }, - { key: 'po_number', value: '' }, - { key: 'product', value: '' }, - { key: 'warehouse', value: '' }, - { - key: 'qty', - value: formatNumber(summary?.total_qty || 0), - align: 'right', - }, - { key: 'unit_price', value: '' }, - { - key: 'purchase_value', - value: formatCurrency(summary?.total_purchase_value || 0), - align: 'right', - }, - { key: 'transport_unit_price', value: '' }, - { - key: 'transport_value', - value: formatCurrency(summary?.total_transport_value || 0), - align: 'right', + footer: '', }, { - key: 'total_amount', - value: formatCurrency(summary?.total_amount || 0), - align: 'right', + key: 'delivery_number', + header: 'Surat Jalan', + flex: 1.2, + align: 'left', + cell: ({ row }) => row.delivery_number || '-', + footer: '', }, - { key: 'expedition', value: '' }, - { key: 'delivery_number', value: '' }, ]; const createPDFDocument = (params: PurchasesPerSupplierExportParams) => { @@ -253,13 +248,9 @@ const createPDFDocument = (params: PurchasesPerSupplierExportParams) => { {/* Table */} ))} diff --git a/src/components/pages/report/marketing/export/DailyMarketingReportPDF.tsx b/src/components/pages/report/marketing/export/DailyMarketingReportPDF.tsx index 53b36b80..29c1d619 100644 --- a/src/components/pages/report/marketing/export/DailyMarketingReportPDF.tsx +++ b/src/components/pages/report/marketing/export/DailyMarketingReportPDF.tsx @@ -11,12 +11,7 @@ import { formatNumber, formatTitleCase, } from '@/lib/helper'; -import { - PdfTable, - PdfColumn, - PdfTbodyCell, - PdfTfootCell, -} from '@/components/helper/pdf/table'; +import { PdfTable, PdfColumn } from '@/components/helper/pdf/table'; import { PdfParamBadge } from '@/components/helper/pdf/badge/PdfParamBadge'; import { PdfStatusBadge } from '@/components/helper/pdf/badge/PdfStatusBadge'; import { PdfTypography } from '@/components/helper/pdf/typography/PdfTypography'; @@ -49,60 +44,90 @@ interface DailyMarketingReportPDFProps { total?: SalesSummary; } -const getTableColumns = (): PdfColumn[] => [ - { key: 'no', header: 'No', flex: 0.5, align: 'center' }, - { key: 'so_date', header: 'Tanggal Sales Order', flex: 1.3, align: 'center' }, +const getTableColumns = ( + summary?: SalesSummary +): PdfColumn[] => [ + { + key: 'no', + header: 'No', + flex: 0.5, + align: 'center', + cell: ({ row, index }) => index + 1, + }, + { + key: 'so_date', + header: 'Tanggal Sales Order', + flex: 1.3, + align: 'center', + cell: ({ row }) => + row.so_date ? formatDate(row.so_date, 'DD MMM YY') : '-', + }, { key: 'do_date', header: 'Tanggal Delivery Order', flex: 1.3, align: 'center', - }, - { key: 'aging', header: 'Aging (Hari)', flex: 0.7, align: 'center' }, - { key: 'warehouse', header: 'Gudang', flex: 1.2, align: 'left' }, - { key: 'customer', header: 'Pelanggan', flex: 1.5, align: 'left' }, - { key: 'sales', header: 'Sales', flex: 1, align: 'left' }, - { key: 'product', header: 'Produk', flex: 1.3, align: 'left' }, - { key: 'do_number', header: 'Nomor DO', flex: 1.2, align: 'left' }, - { key: 'vehicle', header: 'Nomor Polisi', flex: 1, align: 'left' }, - { key: 'marketing_type', header: 'Tipe Marketing', flex: 1, align: 'center' }, - { key: 'qty', header: 'Quantity', flex: 0.7, align: 'right' }, - { key: 'avg_weight', header: 'Rata-Rata (Kg)', flex: 0.8, align: 'right' }, - { - key: 'total_weight', - header: 'Total Berat (Kg)', - flex: 0.9, - align: 'right', - }, - { key: 'sales_price', header: 'Harga Jual (Rp)', flex: 0.9, align: 'right' }, - { key: 'hpp_price', header: 'HPP (Rp)', flex: 1.3, align: 'right' }, - { key: 'sales_amount', header: 'Total Jual (Rp)', flex: 1, align: 'right' }, - { key: 'hpp_amount', header: 'Total HPP (Rp)', flex: 1.3, align: 'right' }, -]; - -const getTableData = (rows: DailyMarketingReport): PdfTbodyCell[][] => { - return rows.map((row, index) => [ - { key: 'no', value: index + 1 }, - { - key: 'so_date', - value: row.so_date ? formatDate(row.so_date, 'DD MMM YY') : '-', - }, - { - key: 'do_date', - value: row.realization_date + cell: ({ row }) => + row.realization_date ? formatDate(row.realization_date, 'DD MMM YY') : '-', - }, - { key: 'aging', value: row.aging_days ?? '-' }, - { key: 'warehouse', value: row.warehouse?.name ?? '-' }, - { key: 'customer', value: row.customer?.name ?? '-' }, - { key: 'sales', value: row.sales?.name ?? '-' }, - { key: 'product', value: row.product?.name ?? '-' }, - { key: 'do_number', value: row.do_number ?? '-' }, - { key: 'vehicle', value: row.vehicle_number ?? '-' }, - { - key: 'marketing_type', - value: row.marketing_type ? ( + }, + { + key: 'aging', + header: 'Aging (Hari)', + flex: 0.7, + align: 'center', + cell: ({ row }) => row.aging_days ?? '-', + }, + { + key: 'warehouse', + header: 'Gudang', + flex: 1.2, + align: 'left', + cell: ({ row }) => row.warehouse?.name ?? '-', + }, + { + key: 'customer', + header: 'Pelanggan', + flex: 1.5, + align: 'left', + cell: ({ row }) => row.customer?.name ?? '-', + }, + { + key: 'sales', + header: 'Sales', + flex: 1, + align: 'left', + cell: ({ row }) => row.sales?.name ?? '-', + }, + { + key: 'product', + header: 'Produk', + flex: 1.3, + align: 'left', + cell: ({ row }) => row.product?.name ?? '-', + }, + { + key: 'do_number', + header: 'Nomor DO', + flex: 1.2, + align: 'left', + cell: ({ row }) => row.do_number ?? '-', + }, + { + key: 'vehicle', + header: 'Nomor Polisi', + flex: 1, + align: 'left', + cell: ({ row }) => row.vehicle_number ?? '-', + }, + { + key: 'marketing_type', + header: 'Tipe Marketing', + flex: 1, + align: 'center', + cell: ({ row }) => + row.marketing_type ? ( { ) : ( '-' ), - }, - { key: 'qty', value: formatNumber(row.qty ?? 0), align: 'right' }, - { - key: 'avg_weight', - value: formatNumber(row.average_weight_kg ?? 0), - align: 'right', - }, - { - key: 'total_weight', - value: formatNumber(row.total_weight_kg ?? 0), - align: 'right', - }, - { - key: 'sales_price', - value: formatCurrency(row.sales_price_per_kg ?? 0), - align: 'right', - }, - { - key: 'hpp_price', - value: formatCurrency(row.hpp_price_per_kg ?? 0), - align: 'right', - }, - { - key: 'sales_amount', - value: formatCurrency(row.sales_amount ?? 0), - align: 'right', - }, - { - key: 'hpp_amount', - value: formatCurrency(row.hpp_amount ?? 0), - align: 'right', - }, - ]); -}; - -const getTableFooter = (summary?: SalesSummary): PdfTfootCell[] => { - if (!summary) return []; - - return [ - { key: 'no', value: 'TOTAL' }, - { key: 'so_date', value: '' }, - { key: 'do_date', value: '' }, - { key: 'aging', value: '' }, - { key: 'warehouse', value: '' }, - { key: 'customer', value: '' }, - { key: 'sales', value: '' }, - { key: 'product', value: '' }, - { key: 'do_number', value: '' }, - { key: 'vehicle', value: '' }, - { key: 'marketing_type', value: '' }, - { - key: 'qty', - value: formatNumber(summary.total_qty ?? 0), - align: 'right', - }, - { - key: 'avg_weight', - value: formatNumber(summary.total_weight_kg ?? 0), - align: 'right', - }, - { - key: 'total_weight', - value: formatNumber(summary.total_weight_kg ?? 0), - align: 'right', - }, - { key: 'sales_price', value: '' }, - { - key: 'hpp_price', - value: formatCurrency(summary.total_hpp_price_per_kg ?? 0), - align: 'right', - }, - { - key: 'sales_amount', - value: formatCurrency(summary.total_sales_amount ?? 0), - align: 'right', - }, - { - key: 'hpp_amount', - value: formatCurrency(summary.total_hpp_amount ?? 0), - align: 'right', - }, - ]; -}; + }, + { + key: 'qty', + header: 'Quantity', + flex: 0.7, + align: 'right', + cell: ({ row }) => formatNumber(row.qty ?? 0), + footer: summary ? formatNumber(summary.total_qty ?? 0) : '', + footerAlign: 'right', + }, + { + key: 'avg_weight', + header: 'Rata-Rata (Kg)', + flex: 0.8, + align: 'right', + cell: ({ row }) => formatNumber(row.average_weight_kg ?? 0), + footer: summary ? formatNumber(summary.total_weight_kg ?? 0) : '', + footerAlign: 'right', + }, + { + key: 'total_weight', + header: 'Total Berat (Kg)', + flex: 0.9, + align: 'right', + cell: ({ row }) => formatNumber(row.total_weight_kg ?? 0), + footer: summary ? formatNumber(summary.total_weight_kg ?? 0) : '', + footerAlign: 'right', + }, + { + key: 'sales_price', + header: 'Harga Jual (Rp)', + flex: 0.9, + align: 'right', + cell: ({ row }) => formatCurrency(row.sales_price_per_kg ?? 0), + footer: '', + }, + { + key: 'hpp_price', + header: 'HPP (Rp)', + flex: 1.3, + align: 'right', + cell: ({ row }) => formatCurrency(row.hpp_price_per_kg ?? 0), + footer: summary ? formatCurrency(summary.total_hpp_price_per_kg ?? 0) : '', + footerAlign: 'right', + }, + { + key: 'sales_amount', + header: 'Total Jual (Rp)', + flex: 1, + align: 'right', + cell: ({ row }) => formatCurrency(row.sales_amount ?? 0), + footer: summary ? formatCurrency(summary.total_sales_amount ?? 0) : '', + footerAlign: 'right', + }, + { + key: 'hpp_amount', + header: 'Total HPP (Rp)', + flex: 1.3, + align: 'right', + cell: ({ row }) => formatCurrency(row.hpp_amount ?? 0), + footer: summary ? formatCurrency(summary.total_hpp_amount ?? 0) : '', + footerAlign: 'right', + }, +]; const DailyMarketingReportPDF = ({ data, @@ -249,9 +255,9 @@ const DailyMarketingReportPDF = ({ {/* Table */} diff --git a/src/components/pages/report/marketing/export/HppPerkandangExportPDF.tsx b/src/components/pages/report/marketing/export/HppPerkandangExportPDF.tsx index d9f33f27..f2a3c835 100644 --- a/src/components/pages/report/marketing/export/HppPerkandangExportPDF.tsx +++ b/src/components/pages/report/marketing/export/HppPerkandangExportPDF.tsx @@ -14,12 +14,7 @@ import { HppPerKandangPerWeightRange, } from '@/types/api/report/hpp-per-kandang'; import { formatDate, formatNumber, formatCurrency } from '@/lib/helper'; -import { - PdfTable, - PdfColumn, - PdfTbodyCell, - PdfTfootCell, -} from '@/components/helper/pdf/table'; +import { PdfTable, PdfColumn } from '@/components/helper/pdf/table'; import { PdfParamBadge } from '@/components/helper/pdf/badge/PdfParamBadge'; import { PdfTypography } from '@/components/helper/pdf/typography/PdfTypography'; @@ -70,234 +65,185 @@ const formatSuppliers = ( }; // Helper functions for PdfTable - Rekapitulasi -const getRekapitulasiColumns = (): PdfColumn[] => [ - { key: 'rentang_bw', header: 'Rentang BW', flex: 1.2, align: 'center' }, - { key: 'sisa_butir', header: 'Sisa Butir', flex: 1, align: 'right' }, - { key: 'sisa_kg', header: 'Sisa Kg', flex: 1, align: 'right' }, +const getRekapitulasiColumns = (): PdfColumn[] => [ + { + key: 'rentang_bw', + header: 'Rentang BW', + flex: 1.2, + align: 'center', + cell: ({ row }) => row.label || '-', + }, + { + key: 'sisa_butir', + header: 'Sisa Butir', + flex: 1, + align: 'right', + cell: ({ row }) => formatNumber(row.egg_production_pieces || 0), + }, + { + key: 'sisa_kg', + header: 'Sisa Kg', + flex: 1, + align: 'right', + cell: ({ row }) => formatNumber(row.egg_production_kg || 0), + }, { key: 'rata_rata_bobot', header: 'Rata-Rata Bobot (Kg)', flex: 1.2, align: 'right', + cell: ({ row }) => formatNumber(row.avg_weight_kg || 0), }, { key: 'feed_supplier', header: 'Feed (Supplier)', flex: 1.5, align: 'left', + cell: ({ row }) => formatSuppliers(row.feed_suppliers), }, { key: 'doc_supplier', header: 'DOC (Supplier)', flex: 1.2, align: 'left', + cell: ({ row }) => formatSuppliers(row.doc_suppliers), }, { key: 'rata_harga_doc', header: 'Rata-Rata Harga DOC', flex: 1.2, align: 'right', + cell: ({ row }) => formatCurrency(row.average_doc_price_rp || 0), }, { key: 'hpp_telur', header: 'HPP Telur (Rp/Kg)', flex: 1.2, align: 'right', + cell: ({ row }) => formatCurrency(row.egg_hpp_rp_per_kg || 0), }, { key: 'nominal_sisa', header: 'Nominal Sisa', flex: 1.2, align: 'right', + cell: ({ row }) => formatCurrency(row.egg_value_rp || 0), }, ]; -const getRekapitulasiData = ( - perWeightRange: HppPerKandangPerWeightRange[] -): PdfTbodyCell[][] => { - return perWeightRange.map((group) => [ - { key: 'rentang_bw', value: group.label || '-' }, - { - key: 'sisa_butir', - value: formatNumber(group.egg_production_pieces || 0), - align: 'right', - }, - { - key: 'sisa_kg', - value: formatNumber(group.egg_production_kg || 0), - align: 'right', - }, - { - key: 'rata_rata_bobot', - value: formatNumber(group.avg_weight_kg || 0), - align: 'right', - }, - { - key: 'feed_supplier', - value: formatSuppliers(group.feed_suppliers), - }, - { - key: 'doc_supplier', - value: formatSuppliers(group.doc_suppliers), - }, - { - key: 'rata_harga_doc', - value: formatCurrency(group.average_doc_price_rp || 0), - align: 'right', - }, - { - key: 'hpp_telur', - value: formatCurrency(group.egg_hpp_rp_per_kg || 0), - align: 'right', - }, - { - key: 'nominal_sisa', - value: formatCurrency(group.egg_value_rp || 0), - align: 'right', - }, - ]); -}; - // Helper functions for PdfTable - Detail Per Kandang -const getDetailColumns = (): PdfColumn[] => [ - { key: 'no', header: 'No', flex: 0.5, align: 'center' }, - { key: 'kandang', header: 'Kandang', flex: 1.5, align: 'left' }, - { key: 'rentang_bw', header: 'Rentang BW', flex: 1, align: 'left' }, +const getDetailColumns = ( + summary?: HppPerKandangReport['summary'], + allFeedSuppliers?: string, + allDocSuppliers?: string +): PdfColumn[] => [ + { + key: 'no', + header: 'No', + flex: 0.5, + align: 'center', + cell: ({ row, index }) => index + 1, + footer: 'TOTAL', + }, + { + key: 'kandang', + header: 'Kandang', + flex: 1.5, + align: 'left', + cell: ({ row }) => row.kandang?.name || '-', + footer: 'ALL', + }, + { + key: 'rentang_bw', + header: 'Rentang BW', + flex: 1, + align: 'left', + cell: ({ row }) => + `${row.weight_range.weight_min.toFixed(2)} - ${row.weight_range.weight_max.toFixed(2)}`, + footer: '-', + }, { key: 'rata_rata_bobot', header: 'Rata-Rata Bobot (Kg)', flex: 1, align: 'right', + cell: ({ row }) => formatNumber(row.avg_weight_kg || 0), + footer: summary ? formatNumber(summary.total.average_weight_kg || 0) : '', + footerAlign: 'right', + }, + { + key: 'sisa_butir', + header: 'Sisa Butir', + flex: 0.8, + align: 'right', + cell: ({ row }) => formatNumber(row.egg_production_pieces || 0), + footer: summary + ? formatNumber(summary.total.total_egg_production_pieces || 0) + : '', + footerAlign: 'right', + }, + { + key: 'sisa_kg', + header: 'Sisa Kg (Telur)', + flex: 0.8, + align: 'right', + cell: ({ row }) => formatNumber(row.egg_production_kg || 0), + footer: summary + ? formatNumber(summary.total.total_egg_production_kg || 0) + : '', + footerAlign: 'right', }, - { key: 'sisa_butir', header: 'Sisa Butir', flex: 0.8, align: 'right' }, - { key: 'sisa_kg', header: 'Sisa Kg (Telur)', flex: 0.8, align: 'right' }, { key: 'feed_supplier', header: 'Feed (Supplier)', flex: 1.2, align: 'left', + cell: ({ row }) => formatSuppliers(row.feed_suppliers), + footer: allFeedSuppliers || '-', }, { key: 'doc_supplier', header: 'DOC (Supplier)', flex: 1, align: 'left', + cell: ({ row }) => formatSuppliers(row.doc_suppliers), + footer: allDocSuppliers || '-', }, { key: 'rata_harga_doc', header: 'Rata-Rata Harga DOC', flex: 1.2, align: 'right', + cell: ({ row }) => formatCurrency(row.average_doc_price_rp || 0), + footer: summary + ? formatCurrency(summary.total.total_average_doc_price_rp || 0) + : '', + footerAlign: 'right', }, { key: 'hpp_telur', header: 'HPP Telur (Rp/Kg)', flex: 1, align: 'right', + cell: ({ row }) => formatCurrency(row.egg_hpp_rp_per_kg || 0), + footer: summary + ? formatCurrency(summary.total.average_egg_hpp_rp_per_kg || 0) + : '', + footerAlign: 'right', }, { key: 'nominal_sisa', header: 'Nominal Sisa', flex: 1.2, align: 'right', + cell: ({ row }) => formatCurrency(row.egg_value_rp || 0), + footer: summary + ? formatCurrency(summary.total.total_egg_value_rp || 0) + : '', + footerAlign: 'right', }, ]; -const getDetailData = (rows: HppPerKandangRow[]): PdfTbodyCell[][] => { - return rows.map((item, index) => [ - { key: 'no', value: index + 1 }, - { key: 'kandang', value: item.kandang?.name || '-' }, - { - key: 'rentang_bw', - value: `${item.weight_range.weight_min.toFixed(2)} - ${item.weight_range.weight_max.toFixed(2)}`, - }, - { - key: 'rata_rata_bobot', - value: formatNumber(item.avg_weight_kg || 0), - align: 'right', - }, - { - key: 'sisa_butir', - value: formatNumber(item.egg_production_pieces || 0), - align: 'right', - }, - { - key: 'sisa_kg', - value: formatNumber(item.egg_production_kg || 0), - align: 'right', - }, - { - key: 'feed_supplier', - value: formatSuppliers(item.feed_suppliers), - }, - { - key: 'doc_supplier', - value: formatSuppliers(item.doc_suppliers), - }, - { - key: 'rata_harga_doc', - value: formatCurrency(item.average_doc_price_rp || 0), - align: 'right', - }, - { - key: 'hpp_telur', - value: formatCurrency(item.egg_hpp_rp_per_kg || 0), - align: 'right', - }, - { - key: 'nominal_sisa', - value: formatCurrency(item.egg_value_rp || 0), - align: 'right', - }, - ]); -}; - -const getDetailFooter = ( - summary: HppPerKandangReport['summary'], - allFeedSuppliers: string, - allDocSuppliers: string -): PdfTfootCell[] => { - if (!summary?.total) return []; - - return [ - { key: 'no', value: 'TOTAL' }, - { key: 'kandang', value: 'ALL' }, - { key: 'rentang_bw', value: '-' }, - { - key: 'rata_rata_bobot', - value: formatNumber(summary.total.average_weight_kg || 0), - align: 'right', - }, - { - key: 'sisa_butir', - value: formatNumber(summary.total.total_egg_production_pieces || 0), - align: 'right', - }, - { - key: 'sisa_kg', - value: formatNumber(summary.total.total_egg_production_kg || 0), - align: 'right', - }, - { key: 'feed_supplier', value: allFeedSuppliers }, - { key: 'doc_supplier', value: allDocSuppliers }, - { - key: 'rata_harga_doc', - value: formatCurrency(summary.total.total_average_doc_price_rp || 0), - align: 'right', - }, - { - key: 'hpp_telur', - value: formatCurrency(summary.total.average_egg_hpp_rp_per_kg || 0), - align: 'right', - }, - { - key: 'nominal_sisa', - value: formatCurrency(summary.total.total_egg_value_rp || 0), - align: 'right', - }, - ]; -}; - const createPDFDocument = ( params: HppPerKandangExportParams, allFeedSuppliers: string, @@ -355,7 +301,7 @@ const createPDFDocument = ( @@ -365,17 +311,13 @@ const createPDFDocument = ( Detail Per Kandang diff --git a/src/components/pages/report/production-result/ProductionResultReportPDF.tsx b/src/components/pages/report/production-result/ProductionResultReportPDF.tsx index 6f7b8313..eabb03bf 100644 --- a/src/components/pages/report/production-result/ProductionResultReportPDF.tsx +++ b/src/components/pages/report/production-result/ProductionResultReportPDF.tsx @@ -9,11 +9,7 @@ import { ProductionResult } from '@/types/api/report/production-result'; import { PdfTypography } from '@/components/helper/pdf/typography/PdfTypography'; import { PdfParamBadge } from '@/components/helper/pdf/badge/PdfParamBadge'; import { PdfPageNumber } from '@/components/helper/pdf/layout/PdfPageNumber'; -import { - PdfTable, - PdfColumn, - PdfTbodyCell, -} from '@/components/helper/pdf/table'; +import { PdfTable, PdfColumn } from '@/components/helper/pdf/table'; type MappedProductionResultsItem = { projectFlockKandang: BaseProjectFlockKandang; @@ -64,246 +60,339 @@ function valueText(v: unknown) { // ======================================== // TABLE 1: WOA & BW // ======================================== -const getBwTableColumns = (): PdfColumn[] => [ - { key: 'no', header: 'No', flex: 0.5, align: 'center' }, - { key: 'woa', header: 'Week of Age', flex: 0.8, align: 'center' }, - { key: 'bw', header: 'Body Weight', flex: 1, align: 'right' }, - { key: 'std_bw', header: 'Std Body Weight', flex: 1, align: 'right' }, - { key: 'uniformity', header: 'Uniformity', flex: 1.2, align: 'right' }, +const getBwTableColumns = (): PdfColumn[] => [ + { + key: 'no', + header: 'No', + flex: 0.5, + align: 'center', + cell: ({ row, index }) => index + 1, + }, + { + key: 'woa', + header: 'Week of Age', + flex: 0.8, + align: 'center', + cell: ({ row }) => valueText(row.woa), + }, + { + key: 'bw', + header: 'Body Weight', + flex: 1, + align: 'right', + cell: ({ row }) => valueText(row.bw), + }, + { + key: 'std_bw', + header: 'Std Body Weight', + flex: 1, + align: 'right', + cell: ({ row }) => valueText(row.std_bw), + }, + { + key: 'uniformity', + header: 'Uniformity', + flex: 1.2, + align: 'right', + cell: ({ row }) => valueText(row.uniformity), + }, { key: 'std_uniformity', header: 'Std Uniformity', flex: 1.3, align: 'right', + cell: ({ row }) => valueText(row.std_uniformity), }, ]; -const getBwTableData = ( - productionResults: ProductionResult[] -): PdfTbodyCell[][] => { - return productionResults.map((pr, index) => { - return [ - { key: 'no', value: index + 1 }, - { key: 'woa', value: valueText(pr.woa) }, - { key: 'bw', value: valueText(pr.bw), align: 'right' }, - { key: 'std_bw', value: valueText(pr.std_bw), align: 'right' }, - { key: 'uniformity', value: valueText(pr.uniformity), align: 'right' }, - { - key: 'std_uniformity', - value: valueText(pr.std_uniformity), - align: 'right', - }, - ]; - }); -}; - // ======================================== // TABLE 2: DEPLESI // ======================================== -const getDepTableColumns = (): PdfColumn[] => [ - { key: 'no', header: 'No', flex: 0.5, align: 'center' }, +const getDepTableColumns = (): PdfColumn[] => [ + { + key: 'no', + header: 'No', + flex: 0.5, + align: 'center', + cell: ({ row, index }) => index + 1, + }, { key: 'dep_kum', header: 'Depletion Cummulative', flex: 1.5, align: 'right', + cell: ({ row }) => valueText(row.dep_kum), + }, + { + key: 'dep_std', + header: 'Depletion Std', + flex: 1.5, + align: 'right', + cell: ({ row }) => valueText(row.dep_std), }, - { key: 'dep_std', header: 'Depletion Std', flex: 1.5, align: 'right' }, ]; -const getDepTableData = ( - productionResults: ProductionResult[] -): PdfTbodyCell[][] => { - return productionResults.map((pr, index) => { - return [ - { key: 'no', value: index + 1 }, - { key: 'dep_kum', value: valueText(pr.dep_kum), align: 'right' }, - { key: 'dep_std', value: valueText(pr.dep_std), align: 'right' }, - ]; - }); -}; - // ======================================== // TABLE 3: BUTIRAN // ======================================== -const getButiranTableColumns = (): PdfColumn[] => [ - { key: 'no', header: 'No', flex: 0.5, align: 'center' }, - { key: 'butiran_utuh', header: 'Utuh', flex: 1.2, align: 'right' }, - { key: 'butiran_putih', header: 'Putih', flex: 1.2, align: 'right' }, - { key: 'butiran_retak', header: 'Retak', flex: 1.2, align: 'right' }, - { key: 'butiran_pecah', header: 'Pecah', flex: 1.2, align: 'right' }, - { key: 'butiran_jumlah', header: 'Jumlah', flex: 1.2, align: 'right' }, - { key: 'total_butir', header: 'Total Butir', flex: 1.3, align: 'right' }, +const getButiranTableColumns = (): PdfColumn[] => [ + { + key: 'no', + header: 'No', + flex: 0.5, + align: 'center', + cell: ({ row, index }) => index + 1, + }, + { + key: 'butiran_utuh', + header: 'Utuh', + flex: 1.2, + align: 'right', + cell: ({ row }) => valueText(row.butiran_utuh), + }, + { + key: 'butiran_putih', + header: 'Putih', + flex: 1.2, + align: 'right', + cell: ({ row }) => valueText(row.butiran_putih), + }, + { + key: 'butiran_retak', + header: 'Retak', + flex: 1.2, + align: 'right', + cell: ({ row }) => valueText(row.butiran_retak), + }, + { + key: 'butiran_pecah', + header: 'Pecah', + flex: 1.2, + align: 'right', + cell: ({ row }) => valueText(row.butiran_pecah), + }, + { + key: 'butiran_jumlah', + header: 'Jumlah', + flex: 1.2, + align: 'right', + cell: ({ row }) => valueText(row.butiran_jumlah), + }, + { + key: 'total_butir', + header: 'Total Butir', + flex: 1.3, + align: 'right', + cell: ({ row }) => valueText(row.total_butir), + }, ]; -const getButiranTableData = ( - productionResults: ProductionResult[] -): PdfTbodyCell[][] => { - return productionResults.map((pr, index) => { - return [ - { key: 'no', value: index + 1 }, - { - key: 'butiran_utuh', - value: valueText(pr.butiran_utuh), - align: 'right', - }, - { - key: 'butiran_putih', - value: valueText(pr.butiran_putih), - align: 'right', - }, - { - key: 'butiran_retak', - value: valueText(pr.butiran_retak), - align: 'right', - }, - { - key: 'butiran_pecah', - value: valueText(pr.butiran_pecah), - align: 'right', - }, - { - key: 'butiran_jumlah', - value: valueText(pr.butiran_jumlah), - align: 'right', - }, - { - key: 'total_butir', - value: valueText(pr.total_butir), - align: 'right', - }, - ]; - }); -}; - // ======================================== // TABLE 4: BERAT (KG) // ======================================== -const getKgTableColumns = (): PdfColumn[] => [ - { key: 'no', header: 'No', flex: 0.5, align: 'center' }, - { key: 'kg_utuh', header: 'Utuh (Kg)', flex: 1.2, align: 'right' }, - { key: 'kg_putih', header: 'Putih (Kg)', flex: 1.2, align: 'right' }, - { key: 'kg_retak', header: 'Retak (Kg)', flex: 1.2, align: 'right' }, - { key: 'kg_pecah', header: 'Pecah (Kg)', flex: 1.2, align: 'right' }, - { key: 'kg_jumlah', header: 'Jumlah (Kg)', flex: 1.3, align: 'right' }, - { key: 'total_kg', header: 'Total (Kg)', flex: 1.3, align: 'right' }, +const getKgTableColumns = (): PdfColumn[] => [ + { + key: 'no', + header: 'No', + flex: 0.5, + align: 'center', + cell: ({ row, index }) => index + 1, + }, + { + key: 'kg_utuh', + header: 'Utuh (Kg)', + flex: 1.2, + align: 'right', + cell: ({ row }) => valueText(row.kg_utuh), + }, + { + key: 'kg_putih', + header: 'Putih (Kg)', + flex: 1.2, + align: 'right', + cell: ({ row }) => valueText(row.kg_putih), + }, + { + key: 'kg_retak', + header: 'Retak (Kg)', + flex: 1.2, + align: 'right', + cell: ({ row }) => valueText(row.kg_retak), + }, + { + key: 'kg_pecah', + header: 'Pecah (Kg)', + flex: 1.2, + align: 'right', + cell: ({ row }) => valueText(row.kg_pecah), + }, + { + key: 'kg_jumlah', + header: 'Jumlah (Kg)', + flex: 1.3, + align: 'right', + cell: ({ row }) => valueText(row.kg_jumlah), + }, + { + key: 'total_kg', + header: 'Total (Kg)', + flex: 1.3, + align: 'right', + cell: ({ row }) => valueText(row.total_kg), + }, ]; -const getKgTableData = ( - productionResults: ProductionResult[] -): PdfTbodyCell[][] => { - return productionResults.map((pr, index) => { - return [ - { key: 'no', value: index + 1 }, - { key: 'kg_utuh', value: valueText(pr.kg_utuh), align: 'right' }, - { key: 'kg_putih', value: valueText(pr.kg_putih), align: 'right' }, - { key: 'kg_retak', value: valueText(pr.kg_retak), align: 'right' }, - { key: 'kg_pecah', value: valueText(pr.kg_pecah), align: 'right' }, - { key: 'kg_jumlah', value: valueText(pr.kg_jumlah), align: 'right' }, - { key: 'total_kg', value: valueText(pr.total_kg), align: 'right' }, - ]; - }); -}; - // ======================================== // TABLE 5: PERSENTASE // ======================================== -const getPersenTableColumns = (): PdfColumn[] => [ - { key: 'no', header: 'No', flex: 0.5, align: 'center' }, - { key: 'persen_utuh', header: 'Utuh (%)', flex: 1.5, align: 'right' }, - { key: 'persen_putih', header: 'Putih (%)', flex: 1.5, align: 'right' }, - { key: 'persen_retak', header: '% Retak (%)', flex: 1.5, align: 'right' }, - { key: 'persen_pecah', header: '% Pecah (%)', flex: 1.5, align: 'right' }, +const getPersenTableColumns = (): PdfColumn[] => [ + { + key: 'no', + header: 'No', + flex: 0.5, + align: 'center', + cell: ({ row, index }) => index + 1, + }, + { + key: 'persen_utuh', + header: 'Utuh (%)', + flex: 1.5, + align: 'right', + cell: ({ row }) => valueText(row.persen_utuh), + }, + { + key: 'persen_putih', + header: 'Putih (%)', + flex: 1.5, + align: 'right', + cell: ({ row }) => valueText(row.persen_putih), + }, + { + key: 'persen_retak', + header: '% Retak (%)', + flex: 1.5, + align: 'right', + cell: ({ row }) => valueText(row.persen_retak), + }, + { + key: 'persen_pecah', + header: '% Pecah (%)', + flex: 1.5, + align: 'right', + cell: ({ row }) => valueText(row.persen_pecah), + }, ]; -const getPersenTableData = ( - productionResults: ProductionResult[] -): PdfTbodyCell[][] => { - return productionResults.map((pr, index) => { - return [ - { key: 'no', value: index + 1 }, - { - key: 'persen_utuh', - value: valueText(pr.persen_utuh), - align: 'right', - }, - { - key: 'persen_putih', - value: valueText(pr.persen_putih), - align: 'right', - }, - { - key: 'persen_retak', - value: valueText(pr.persen_retak), - align: 'right', - }, - { - key: 'persen_pecah', - value: valueText(pr.persen_pecah), - align: 'right', - }, - ]; - }); -}; - // ======================================== // TABLE 6: PRODUKSI (HD, FI, EM, EW) // ======================================== -const getProduksi1TableColumns = (): PdfColumn[] => [ - { key: 'no', header: 'No', flex: 0.5, align: 'center' }, - { key: 'hd', header: 'Hen Day', flex: 0.8, align: 'right' }, - { key: 'hd_std', header: 'Hen Day Std', flex: 1, align: 'right' }, - { key: 'fi', header: 'Feed Intake', flex: 0.8, align: 'right' }, - { key: 'fi_std', header: 'Feed Intake Std', flex: 1, align: 'right' }, - { key: 'em', header: 'Egg Mass', flex: 0.8, align: 'right' }, - { key: 'em_std', header: 'Egg Mass Std', flex: 1, align: 'right' }, - { key: 'ew', header: 'Egg Weight', flex: 0.8, align: 'right' }, - { key: 'ew_std', header: 'Egg Weight Std', flex: 1, align: 'right' }, +const getProduksi1TableColumns = (): PdfColumn[] => [ + { + key: 'no', + header: 'No', + flex: 0.5, + align: 'center', + cell: ({ row, index }) => index + 1, + }, + { + key: 'hd', + header: 'Hen Day', + flex: 0.8, + align: 'right', + cell: ({ row }) => valueText(row.hd), + }, + { + key: 'hd_std', + header: 'Hen Day Std', + flex: 1, + align: 'right', + cell: ({ row }) => valueText(row.hd_std), + }, + { + key: 'fi', + header: 'Feed Intake', + flex: 0.8, + align: 'right', + cell: ({ row }) => valueText(row.fi), + }, + { + key: 'fi_std', + header: 'Feed Intake Std', + flex: 1, + align: 'right', + cell: ({ row }) => valueText(row.fi_std), + }, + { + key: 'em', + header: 'Egg Mass', + flex: 0.8, + align: 'right', + cell: ({ row }) => valueText(row.em), + }, + { + key: 'em_std', + header: 'Egg Mass Std', + flex: 1, + align: 'right', + cell: ({ row }) => valueText(row.em_std), + }, + { + key: 'ew', + header: 'Egg Weight', + flex: 0.8, + align: 'right', + cell: ({ row }) => valueText(row.ew), + }, + { + key: 'ew_std', + header: 'Egg Weight Std', + flex: 1, + align: 'right', + cell: ({ row }) => valueText(row.ew_std), + }, ]; -const getProduksi1TableData = ( - productionResults: ProductionResult[] -): PdfTbodyCell[][] => { - return productionResults.map((pr, index) => { - return [ - { key: 'no', value: index + 1 }, - { key: 'hd', value: valueText(pr.hd), align: 'right' }, - { key: 'hd_std', value: valueText(pr.hd_std), align: 'right' }, - { key: 'fi', value: valueText(pr.fi), align: 'right' }, - { key: 'fi_std', value: valueText(pr.fi_std), align: 'right' }, - { key: 'em', value: valueText(pr.em), align: 'right' }, - { key: 'em_std', value: valueText(pr.em_std), align: 'right' }, - { key: 'ew', value: valueText(pr.ew), align: 'right' }, - { key: 'ew_std', value: valueText(pr.ew_std), align: 'right' }, - ]; - }); -}; - // ======================================== // TABLE 7: PRODUKSI (FCR, HH) // ======================================== -const getProduksi2TableColumns = (): PdfColumn[] => [ - { key: 'no', header: 'No', flex: 0.5, align: 'center' }, - { key: 'fcr', header: 'FCR', flex: 1, align: 'right' }, - { key: 'fcr_std', header: 'FCR Std', flex: 1.2, align: 'right' }, - { key: 'hh', header: 'Hen House', flex: 1, align: 'right' }, - { key: 'hh_std', header: 'Hen House Std', flex: 1.2, align: 'right' }, +const getProduksi2TableColumns = (): PdfColumn[] => [ + { + key: 'no', + header: 'No', + flex: 0.5, + align: 'center', + cell: ({ row, index }) => index + 1, + }, + { + key: 'fcr', + header: 'FCR', + flex: 1, + align: 'right', + cell: ({ row }) => valueText(row.fcr), + }, + { + key: 'fcr_std', + header: 'FCR Std', + flex: 1.2, + align: 'right', + cell: ({ row }) => valueText(row.fcr_std), + }, + { + key: 'hh', + header: 'Hen House', + flex: 1, + align: 'right', + cell: ({ row }) => valueText(row.hh), + }, + { + key: 'hh_std', + header: 'Hen House Std', + flex: 1.2, + align: 'right', + cell: ({ row }) => valueText(row.hh_std), + }, ]; -const getProduksi2TableData = ( - productionResults: ProductionResult[] -): PdfTbodyCell[][] => { - return productionResults.map((pr, index) => { - return [ - { key: 'no', value: index + 1 }, - { key: 'fcr', value: valueText(pr.fcr), align: 'right' }, - { key: 'fcr_std', value: valueText(pr.fcr_std), align: 'right' }, - { key: 'hh', value: valueText(pr.hh), align: 'right' }, - { key: 'hh_std', value: valueText(pr.hh_std), align: 'right' }, - ]; - }); -}; - /** * ✅ Main PDF Component */ @@ -383,7 +472,7 @@ const ProductionResultReportPDF = ({ 1. WOA & Body Weight @@ -392,7 +481,7 @@ const ProductionResultReportPDF = ({ 2. Deplesi @@ -401,7 +490,7 @@ const ProductionResultReportPDF = ({ 3. Butiran @@ -410,7 +499,7 @@ const ProductionResultReportPDF = ({ 4. Berat (Kg) @@ -419,7 +508,7 @@ const ProductionResultReportPDF = ({ 5. Persentase @@ -430,7 +519,7 @@ const ProductionResultReportPDF = ({ @@ -439,7 +528,7 @@ const ProductionResultReportPDF = ({ 7. Produksi (FCR, HH)