refactor(FE): Refactor PDF table components to simplify imports

This commit is contained in:
rstubryan
2026-02-11 10:39:37 +07:00
parent 70b63f7773
commit 0f1d2ce477
6 changed files with 973 additions and 885 deletions
@@ -7,6 +7,7 @@ import {
StyleSheet, StyleSheet,
Font, Font,
pdf, pdf,
Text,
} from '@react-pdf/renderer'; } from '@react-pdf/renderer';
import { import {
@@ -16,12 +17,7 @@ import {
formatTitleCase, formatTitleCase,
} from '@/lib/helper'; } from '@/lib/helper';
import { CustomerPaymentReport } from '@/types/api/report/customer-payment'; import { CustomerPaymentReport } from '@/types/api/report/customer-payment';
import { import { PdfTable, PdfColumn } from '@/components/helper/pdf/table';
PdfTable,
PdfColumn,
PdfTbodyCell,
PdfTfootCell,
} from '@/components/helper/pdf/table';
import { PdfParamBadge } from '@/components/helper/pdf/badge/PdfParamBadge'; import { PdfParamBadge } from '@/components/helper/pdf/badge/PdfParamBadge';
import { PdfStatusBadge } from '@/components/helper/pdf/badge/PdfStatusBadge'; import { PdfStatusBadge } from '@/components/helper/pdf/badge/PdfStatusBadge';
import { PdfTypography } from '@/components/helper/pdf/typography/PdfTypography'; import { PdfTypography } from '@/components/helper/pdf/typography/PdfTypography';
@@ -61,172 +57,183 @@ interface CustomerPaymentExportPDFParams {
}; };
} }
const getTableColumns = (): PdfColumn[] => [ const getTableColumns = (
{ key: 'no', header: 'No', flex: 0.5, align: 'center' }, summary?: CustomerPaymentReport['summary']
{ key: 'trans_date', header: 'Tanggal DO', flex: 1.2, align: 'center' }, ): PdfColumn<CustomerPaymentReport['rows'][number]>[] => [
{
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', key: 'delivery_date',
header: 'Tanggal Realisasi', header: 'Tanggal Realisasi',
flex: 1.2, flex: 1.2,
align: 'center', 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', key: 'payment_amount',
header: 'Pembayaran (Rp)', header: 'Pembayaran (Rp)',
flex: 1.2, flex: 1.2,
align: 'right', align: 'right',
cell: ({ row }) => formatCurrency(row.payment_amount),
footer: summary ? formatCurrency(summary.total_payment || 0) : '',
footerAlign: 'right',
}, },
{ {
key: 'accounts_receivable', key: 'accounts_receivable',
header: 'Saldo (Rp)', header: 'Saldo (Rp)',
flex: 1.2, flex: 1.2,
align: 'right', align: 'right',
cell: ({ row }) => (
<Text style={{ color: row.accounts_receivable < 0 ? 'red' : 'black' }}>
{formatCurrency(row.accounts_receivable)}
</Text>
),
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: 'status',
{ key: 'sales_person', header: 'Sales', flex: 1.5, align: 'left' }, header: 'Keterangan',
]; flex: 1.5,
align: 'center',
const getTableData = ( cell: ({ row }) =>
rows: CustomerPaymentReport['rows'] row.status ? (
): 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 ? (
<View style={{ alignItems: 'center' }}> <View style={{ alignItems: 'center' }}>
<PdfStatusBadge <PdfStatusBadge
style={{ style={{
backgroundColor: getPDFBadgeStyle(item.status, 'payment').bg, backgroundColor: getPDFBadgeStyle(row.status, 'payment').bg,
color: getPDFBadgeStyle(item.status, 'payment').text, color: getPDFBadgeStyle(row.status, 'payment').text,
borderColor: getPDFBadgeStyle(item.status, 'payment').border, borderColor: getPDFBadgeStyle(row.status, 'payment').border,
}} }}
> >
{formatTitleCase(item.status)} {formatTitleCase(row.status)}
</PdfStatusBadge> </PdfStatusBadge>
</View> </View>
) : ( ) : (
'-' '-'
), ),
}, footer: '',
{
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',
}, },
{ {
key: 'total_price', key: 'pickup_info',
value: formatCurrency(summary?.total_grand_amount || 0), header: 'Pengambilan',
align: 'right', flex: 1,
align: 'left',
cell: ({ row }) =>
Array.isArray(row.pickup_info) && row.pickup_info.length > 0
? row.pickup_info.join(', ')
: '-',
footer: '',
}, },
{ {
key: 'payment_amount', key: 'sales_person',
value: formatCurrency(summary?.total_payment || 0), header: 'Sales',
align: 'right', 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) => { const createPDFDocument = (params: CustomerPaymentExportPDFParams) => {
@@ -276,13 +283,9 @@ const createPDFDocument = (params: CustomerPaymentExportPDFParams) => {
{/* Table */} {/* Table */}
<PdfTable <PdfTable
columns={getTableColumns()} columns={getTableColumns(customerReport.summary)}
data={getTableData(customerReport.rows)} data={customerReport.rows}
footer={ showFooter={!!customerReport.summary}
customerReport.summary
? getTableFooter(customerReport.summary)
: undefined
}
firstRow={ firstRow={
typeof customerReport.initial_balance === 'number' && typeof customerReport.initial_balance === 'number' &&
customerReport.initial_balance !== 0 customerReport.initial_balance !== 0
@@ -14,13 +14,9 @@ import { DebtSupplier } from '@/types/api/report/debt-supplier';
import { PdfParamBadge } from '@/components/helper/pdf/badge/PdfParamBadge'; import { PdfParamBadge } from '@/components/helper/pdf/badge/PdfParamBadge';
import { PdfTypography } from '@/components/helper/pdf/typography/PdfTypography'; import { PdfTypography } from '@/components/helper/pdf/typography/PdfTypography';
import { PdfStatusBadge } from '@/components/helper/pdf/badge/PdfStatusBadge'; import { PdfStatusBadge } from '@/components/helper/pdf/badge/PdfStatusBadge';
import { import { PdfTable, PdfColumn } from '@/components/helper/pdf/table';
PdfTable,
PdfColumn,
PdfTbodyCell,
PdfTfootCell,
} from '@/components/helper/pdf/table';
import { getPDFBadgeStyle } from '@/components/helper/pdf/utils/pdf-badge'; import { getPDFBadgeStyle } from '@/components/helper/pdf/utils/pdf-badge';
import { Text } from '@react-pdf/renderer';
Font.register({ Font.register({
family: 'Helvetica', family: 'Helvetica',
@@ -44,160 +40,225 @@ const pdfStyles = StyleSheet.create({
}, },
}); });
const getTableColumns = (): PdfColumn[] => [ const getTableColumns = (total?: DebtSupplier['total']): PdfColumn[] => {
{ key: 'no', header: 'No', flex: 0.5, align: 'center' }, type DebtRow = DebtSupplier['rows'][number];
{ 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 getTableData = (rows: DebtSupplier['rows']): PdfTbodyCell[][] => { return [
return rows.map((item, index) => [ {
{ key: 'no', value: index + 1 }, key: 'no',
{ key: 'pr_number', value: item.pr_number || '-' }, header: 'No',
{ key: 'po_number', value: item.po_number || '-' }, 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', key: 'received_date',
value: item.received_date header: 'Tgl Terima/Bayar',
? formatDate(item.received_date, 'DD MMM YY') 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', 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', 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', 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', key: 'due_status',
value: header: 'Status Jatuh Tempo',
item.due_status && item.due_status !== '-' ? ( flex: 2,
align: 'center',
cell: ({ row }) =>
(row as unknown as DebtRow).due_status &&
(row as unknown as DebtRow).due_status !== '-' ? (
<View style={{ alignItems: 'center' }}> <View style={{ alignItems: 'center' }}>
<PdfStatusBadge <PdfStatusBadge
style={{ style={{
backgroundColor: getPDFBadgeStyle(item.due_status, 'due').bg, backgroundColor: getPDFBadgeStyle(
color: getPDFBadgeStyle(item.due_status, 'due').text, (row as unknown as DebtRow).due_status,
borderColor: getPDFBadgeStyle(item.due_status, 'due').border, 'due'
).bg,
color: getPDFBadgeStyle(
(row as unknown as DebtRow).due_status,
'due'
).text,
borderColor: getPDFBadgeStyle(
(row as unknown as DebtRow).due_status,
'due'
).border,
}} }}
> >
{item.due_status} {(row as unknown as DebtRow).due_status}
</PdfStatusBadge> </PdfStatusBadge>
</View> </View>
) : ( ) : (
'-' '-'
), ),
footer: '',
}, },
{ {
key: 'total_price', key: 'total_price',
value: formatCurrency(item.total_price), header: 'Nominal Pembelian (Rp)',
flex: 1.5,
align: 'right', align: 'right',
color: item.total_price < 0 ? 'red' : undefined, cell: ({ row }) => (
<Text
style={{
color:
(row as unknown as DebtRow).total_price < 0 ? 'red' : 'black',
}}
>
{formatCurrency((row as unknown as DebtRow).total_price)}
</Text>
),
footer: total ? formatCurrency(total.total_price || 0) : '',
footerAlign: 'right',
}, },
{ {
key: 'payment_price', key: 'payment_price',
value: formatCurrency(item.payment_price), header: 'Pembayaran (Rp)',
flex: 1.5,
align: 'right', align: 'right',
color: item.payment_price < 0 ? 'red' : undefined, cell: ({ row }) => (
<Text
style={{
color:
(row as unknown as DebtRow).payment_price < 0 ? 'red' : 'black',
}}
>
{formatCurrency((row as unknown as DebtRow).payment_price)}
</Text>
),
footer: total ? formatCurrency(total.payment_price || 0) : '',
footerAlign: 'right',
}, },
{ {
key: 'balance', key: 'balance',
value: formatCurrency(item.balance), header: 'Sisa Saldo Hutang (Rp)',
flex: 1.5,
align: 'right', align: 'right',
color: item.balance < 0 ? 'red' : undefined, cell: ({ row }) => (
<Text
style={{
color: (row as unknown as DebtRow).balance < 0 ? 'red' : 'black',
}}
>
{formatCurrency((row as unknown as DebtRow).balance)}
</Text>
),
footer: total ? formatCurrency(total.debt_price || 0) : '',
footerAlign: 'right',
footerColor: (total?.debt_price || 0) < 0 ? 'red' : undefined,
}, },
{ {
key: 'status', key: 'status',
value: header: 'Status',
item.status && item.status !== '-' ? ( flex: 1.2,
align: 'center',
cell: ({ row }) =>
(row as unknown as DebtRow).status &&
(row as unknown as DebtRow).status !== '-' ? (
<View style={{ alignItems: 'center' }}> <View style={{ alignItems: 'center' }}>
<PdfStatusBadge <PdfStatusBadge
style={{ style={{
backgroundColor: getPDFBadgeStyle(item.status, 'payment').bg, backgroundColor: getPDFBadgeStyle(
color: getPDFBadgeStyle(item.status, 'payment').text, (row as unknown as DebtRow).status,
borderColor: getPDFBadgeStyle(item.status, 'payment').border, 'payment'
).bg,
color: getPDFBadgeStyle(
(row as unknown as DebtRow).status,
'payment'
).text,
borderColor: getPDFBadgeStyle(
(row as unknown as DebtRow).status,
'payment'
).border,
}} }}
> >
{item.status} {(row as unknown as DebtRow).status}
</PdfStatusBadge> </PdfStatusBadge>
</View> </View>
) : ( ) : (
'-' '-'
), ),
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 { interface DebtSupplierExportPDFParams {
data: DebtSupplier[]; data: DebtSupplier[];
params?: { params?: {
@@ -263,13 +324,9 @@ const createPDFDocument = (params: DebtSupplierExportPDFParams) => {
{/* Table */} {/* Table */}
<PdfTable <PdfTable
columns={getTableColumns()} columns={getTableColumns(supplierReport.total)}
data={getTableData(supplierReport.rows)} data={supplierReport.rows as unknown as Record<string, unknown>[]}
footer={ showFooter={!!supplierReport.total}
supplierReport.total
? getTableFooter(supplierReport.total)
: undefined
}
firstRow={ firstRow={
typeof supplierReport.initial_balance === 'number' && typeof supplierReport.initial_balance === 'number' &&
supplierReport.initial_balance !== 0 supplierReport.initial_balance !== 0
@@ -10,12 +10,7 @@ import {
} from '@react-pdf/renderer'; } from '@react-pdf/renderer';
import { LogisticPurchasePerSupplierReport } from '@/types/api/report/logistic-stock'; import { LogisticPurchasePerSupplierReport } from '@/types/api/report/logistic-stock';
import { formatCurrency, formatDate, formatNumber } from '@/lib/helper'; import { formatCurrency, formatDate, formatNumber } from '@/lib/helper';
import { import { PdfTable, PdfColumn } from '@/components/helper/pdf/table';
PdfTable,
PdfColumn,
PdfTbodyCell,
PdfTfootCell,
} from '@/components/helper/pdf/table';
import { PdfParamBadge } from '@/components/helper/pdf/badge/PdfParamBadge'; import { PdfParamBadge } from '@/components/helper/pdf/badge/PdfParamBadge';
import { PdfStatusBadge } from '@/components/helper/pdf/badge/PdfStatusBadge'; import { PdfStatusBadge } from '@/components/helper/pdf/badge/PdfStatusBadge';
import { PdfTypography } from '@/components/helper/pdf/typography/PdfTypography'; import { PdfTypography } from '@/components/helper/pdf/typography/PdfTypography';
@@ -58,90 +53,118 @@ interface PurchasesPerSupplierExportParams {
}; };
} }
const getTableColumns = (): PdfColumn[] => [ const getTableColumns = (
{ key: 'no', header: 'No', flex: 0.5, align: 'center' }, summary?: LogisticPurchasePerSupplierReport['summary']
): PdfColumn<LogisticPurchasePerSupplierReport['rows'][number]>[] => [
{
key: 'no',
header: 'No',
flex: 0.5,
align: 'center',
cell: ({ row, index }) => index + 1,
footer: 'Total',
},
{ {
key: 'receive_date', key: 'receive_date',
header: 'Tanggal Terima', header: 'Tanggal Terima',
flex: 1.2, flex: 1.2,
align: 'center', 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', key: 'purchase_value',
header: 'Value Harga Beli (Rp)', header: 'Value Harga Beli (Rp)',
flex: 1.8, flex: 1.8,
align: 'right', align: 'right',
cell: ({ row }) => formatCurrency(row.purchase_value || 0),
footer: summary ? formatCurrency(summary.total_purchase_value || 0) : '',
footerAlign: 'right',
}, },
{ {
key: 'transport_unit_price', key: 'transport_unit_price',
header: 'Transport (Rp)', header: 'Transport (Rp)',
flex: 1.3, flex: 1.3,
align: 'right', align: 'right',
cell: ({ row }) => formatCurrency(row.transport_unit_price || 0),
footer: '',
}, },
{ {
key: 'transport_value', key: 'transport_value',
header: 'Value Transport (Rp)', header: 'Value Transport (Rp)',
flex: 1.8, flex: 1.8,
align: 'right', 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: 'total_amount',
{ key: 'delivery_number', header: 'Surat Jalan', flex: 1.2, align: 'left' }, header: 'Jumlah (Rp)',
]; flex: 1.5,
align: 'right',
const getTableData = ( cell: ({ row }) => formatCurrency(row.total_amount || 0),
rows: LogisticPurchasePerSupplierReport['rows'] footer: summary ? formatCurrency(summary.total_amount || 0) : '',
): PdfTbodyCell[][] => { footerAlign: 'right',
return rows.map((item, index) => [ },
{ key: 'no', value: index + 1 }, {
{ key: 'expedition',
key: 'receive_date', header: 'Ekspedisi',
value: item.receive_date flex: 1.2,
? formatDate(item.receive_date, 'DD MMM YY') align: 'center',
: '-', cell: ({ row }) =>
}, row.expedition ? (
{
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 ? (
<View style={{ alignItems: 'center' }}> <View style={{ alignItems: 'center' }}>
<PdfStatusBadge <PdfStatusBadge
style={{ style={{
@@ -150,50 +173,22 @@ const getTableData = (
borderColor: '#60A5FA', borderColor: '#60A5FA',
}} }}
> >
{item.expedition} {row.expedition}
</PdfStatusBadge> </PdfStatusBadge>
</View> </View>
) : ( ) : (
'-' '-'
), ),
}, footer: '',
{ 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',
}, },
{ {
key: 'total_amount', key: 'delivery_number',
value: formatCurrency(summary?.total_amount || 0), header: 'Surat Jalan',
align: 'right', flex: 1.2,
align: 'left',
cell: ({ row }) => row.delivery_number || '-',
footer: '',
}, },
{ key: 'expedition', value: '' },
{ key: 'delivery_number', value: '' },
]; ];
const createPDFDocument = (params: PurchasesPerSupplierExportParams) => { const createPDFDocument = (params: PurchasesPerSupplierExportParams) => {
@@ -253,13 +248,9 @@ const createPDFDocument = (params: PurchasesPerSupplierExportParams) => {
{/* Table */} {/* Table */}
<PdfTable <PdfTable
columns={getTableColumns()} columns={getTableColumns(supplierReport.summary)}
data={getTableData(supplierReport.rows)} data={supplierReport.rows}
footer={ showFooter={!!supplierReport.summary}
supplierReport.summary
? getTableFooter(supplierReport.summary)
: undefined
}
/> />
</Page> </Page>
))} ))}
@@ -11,12 +11,7 @@ import {
formatNumber, formatNumber,
formatTitleCase, formatTitleCase,
} from '@/lib/helper'; } from '@/lib/helper';
import { import { PdfTable, PdfColumn } from '@/components/helper/pdf/table';
PdfTable,
PdfColumn,
PdfTbodyCell,
PdfTfootCell,
} from '@/components/helper/pdf/table';
import { PdfParamBadge } from '@/components/helper/pdf/badge/PdfParamBadge'; import { PdfParamBadge } from '@/components/helper/pdf/badge/PdfParamBadge';
import { PdfStatusBadge } from '@/components/helper/pdf/badge/PdfStatusBadge'; import { PdfStatusBadge } from '@/components/helper/pdf/badge/PdfStatusBadge';
import { PdfTypography } from '@/components/helper/pdf/typography/PdfTypography'; import { PdfTypography } from '@/components/helper/pdf/typography/PdfTypography';
@@ -49,60 +44,90 @@ interface DailyMarketingReportPDFProps {
total?: SalesSummary; total?: SalesSummary;
} }
const getTableColumns = (): PdfColumn[] => [ const getTableColumns = (
{ key: 'no', header: 'No', flex: 0.5, align: 'center' }, summary?: SalesSummary
{ key: 'so_date', header: 'Tanggal Sales Order', flex: 1.3, align: 'center' }, ): PdfColumn<DailyMarketingReport[number]>[] => [
{
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', key: 'do_date',
header: 'Tanggal Delivery Order', header: 'Tanggal Delivery Order',
flex: 1.3, flex: 1.3,
align: 'center', align: 'center',
}, cell: ({ row }) =>
{ key: 'aging', header: 'Aging (Hari)', flex: 0.7, align: 'center' }, row.realization_date
{ 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
? formatDate(row.realization_date, 'DD MMM YY') ? formatDate(row.realization_date, 'DD MMM YY')
: '-', : '-',
}, },
{ key: 'aging', value: row.aging_days ?? '-' }, {
{ key: 'warehouse', value: row.warehouse?.name ?? '-' }, key: 'aging',
{ key: 'customer', value: row.customer?.name ?? '-' }, header: 'Aging (Hari)',
{ key: 'sales', value: row.sales?.name ?? '-' }, flex: 0.7,
{ key: 'product', value: row.product?.name ?? '-' }, align: 'center',
{ key: 'do_number', value: row.do_number ?? '-' }, cell: ({ row }) => row.aging_days ?? '-',
{ key: 'vehicle', value: row.vehicle_number ?? '-' }, },
{ {
key: 'marketing_type', key: 'warehouse',
value: row.marketing_type ? ( 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 ? (
<View style={{ alignItems: 'center' }}> <View style={{ alignItems: 'center' }}>
<PdfStatusBadge <PdfStatusBadge
style={{ style={{
@@ -138,89 +163,70 @@ const getTableData = (rows: DailyMarketingReport): PdfTbodyCell[][] => {
) : ( ) : (
'-' '-'
), ),
}, },
{ key: 'qty', value: formatNumber(row.qty ?? 0), align: 'right' }, {
{ key: 'qty',
key: 'avg_weight', header: 'Quantity',
value: formatNumber(row.average_weight_kg ?? 0), flex: 0.7,
align: 'right', align: 'right',
}, cell: ({ row }) => formatNumber(row.qty ?? 0),
{ footer: summary ? formatNumber(summary.total_qty ?? 0) : '',
key: 'total_weight', footerAlign: 'right',
value: formatNumber(row.total_weight_kg ?? 0), },
align: 'right', {
}, key: 'avg_weight',
{ header: 'Rata-Rata (Kg)',
key: 'sales_price', flex: 0.8,
value: formatCurrency(row.sales_price_per_kg ?? 0), align: 'right',
align: 'right', cell: ({ row }) => formatNumber(row.average_weight_kg ?? 0),
}, footer: summary ? formatNumber(summary.total_weight_kg ?? 0) : '',
{ footerAlign: 'right',
key: 'hpp_price', },
value: formatCurrency(row.hpp_price_per_kg ?? 0), {
align: 'right', key: 'total_weight',
}, header: 'Total Berat (Kg)',
{ flex: 0.9,
key: 'sales_amount', align: 'right',
value: formatCurrency(row.sales_amount ?? 0), cell: ({ row }) => formatNumber(row.total_weight_kg ?? 0),
align: 'right', footer: summary ? formatNumber(summary.total_weight_kg ?? 0) : '',
}, footerAlign: 'right',
{ },
key: 'hpp_amount', {
value: formatCurrency(row.hpp_amount ?? 0), key: 'sales_price',
align: 'right', header: 'Harga Jual (Rp)',
}, flex: 0.9,
]); align: 'right',
}; cell: ({ row }) => formatCurrency(row.sales_price_per_kg ?? 0),
footer: '',
const getTableFooter = (summary?: SalesSummary): PdfTfootCell[] => { },
if (!summary) return []; {
key: 'hpp_price',
return [ header: 'HPP (Rp)',
{ key: 'no', value: 'TOTAL' }, flex: 1.3,
{ key: 'so_date', value: '' }, align: 'right',
{ key: 'do_date', value: '' }, cell: ({ row }) => formatCurrency(row.hpp_price_per_kg ?? 0),
{ key: 'aging', value: '' }, footer: summary ? formatCurrency(summary.total_hpp_price_per_kg ?? 0) : '',
{ key: 'warehouse', value: '' }, footerAlign: 'right',
{ key: 'customer', value: '' }, },
{ key: 'sales', value: '' }, {
{ key: 'product', value: '' }, key: 'sales_amount',
{ key: 'do_number', value: '' }, header: 'Total Jual (Rp)',
{ key: 'vehicle', value: '' }, flex: 1,
{ key: 'marketing_type', value: '' }, align: 'right',
{ cell: ({ row }) => formatCurrency(row.sales_amount ?? 0),
key: 'qty', footer: summary ? formatCurrency(summary.total_sales_amount ?? 0) : '',
value: formatNumber(summary.total_qty ?? 0), footerAlign: 'right',
align: 'right', },
}, {
{ key: 'hpp_amount',
key: 'avg_weight', header: 'Total HPP (Rp)',
value: formatNumber(summary.total_weight_kg ?? 0), flex: 1.3,
align: 'right', align: 'right',
}, cell: ({ row }) => formatCurrency(row.hpp_amount ?? 0),
{ footer: summary ? formatCurrency(summary.total_hpp_amount ?? 0) : '',
key: 'total_weight', footerAlign: 'right',
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',
},
];
};
const DailyMarketingReportPDF = ({ const DailyMarketingReportPDF = ({
data, data,
@@ -249,9 +255,9 @@ const DailyMarketingReportPDF = ({
{/* Table */} {/* Table */}
<PdfTable <PdfTable
columns={getTableColumns()} columns={getTableColumns(summary)}
data={getTableData(rows)} data={rows}
footer={getTableFooter(summary)} showFooter={!!summary}
footerLabel='TOTAL' footerLabel='TOTAL'
/> />
@@ -14,12 +14,7 @@ import {
HppPerKandangPerWeightRange, HppPerKandangPerWeightRange,
} from '@/types/api/report/hpp-per-kandang'; } from '@/types/api/report/hpp-per-kandang';
import { formatDate, formatNumber, formatCurrency } from '@/lib/helper'; import { formatDate, formatNumber, formatCurrency } from '@/lib/helper';
import { import { PdfTable, PdfColumn } from '@/components/helper/pdf/table';
PdfTable,
PdfColumn,
PdfTbodyCell,
PdfTfootCell,
} from '@/components/helper/pdf/table';
import { PdfParamBadge } from '@/components/helper/pdf/badge/PdfParamBadge'; import { PdfParamBadge } from '@/components/helper/pdf/badge/PdfParamBadge';
import { PdfTypography } from '@/components/helper/pdf/typography/PdfTypography'; import { PdfTypography } from '@/components/helper/pdf/typography/PdfTypography';
@@ -70,234 +65,185 @@ const formatSuppliers = (
}; };
// Helper functions for PdfTable - Rekapitulasi // Helper functions for PdfTable - Rekapitulasi
const getRekapitulasiColumns = (): PdfColumn[] => [ const getRekapitulasiColumns = (): PdfColumn<HppPerKandangPerWeightRange>[] => [
{ key: 'rentang_bw', header: 'Rentang BW', flex: 1.2, align: 'center' }, {
{ key: 'sisa_butir', header: 'Sisa Butir', flex: 1, align: 'right' }, key: 'rentang_bw',
{ key: 'sisa_kg', header: 'Sisa Kg', flex: 1, align: 'right' }, 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', key: 'rata_rata_bobot',
header: 'Rata-Rata Bobot (Kg)', header: 'Rata-Rata Bobot (Kg)',
flex: 1.2, flex: 1.2,
align: 'right', align: 'right',
cell: ({ row }) => formatNumber(row.avg_weight_kg || 0),
}, },
{ {
key: 'feed_supplier', key: 'feed_supplier',
header: 'Feed (Supplier)', header: 'Feed (Supplier)',
flex: 1.5, flex: 1.5,
align: 'left', align: 'left',
cell: ({ row }) => formatSuppliers(row.feed_suppliers),
}, },
{ {
key: 'doc_supplier', key: 'doc_supplier',
header: 'DOC (Supplier)', header: 'DOC (Supplier)',
flex: 1.2, flex: 1.2,
align: 'left', align: 'left',
cell: ({ row }) => formatSuppliers(row.doc_suppliers),
}, },
{ {
key: 'rata_harga_doc', key: 'rata_harga_doc',
header: 'Rata-Rata Harga DOC', header: 'Rata-Rata Harga DOC',
flex: 1.2, flex: 1.2,
align: 'right', align: 'right',
cell: ({ row }) => formatCurrency(row.average_doc_price_rp || 0),
}, },
{ {
key: 'hpp_telur', key: 'hpp_telur',
header: 'HPP Telur (Rp/Kg)', header: 'HPP Telur (Rp/Kg)',
flex: 1.2, flex: 1.2,
align: 'right', align: 'right',
cell: ({ row }) => formatCurrency(row.egg_hpp_rp_per_kg || 0),
}, },
{ {
key: 'nominal_sisa', key: 'nominal_sisa',
header: 'Nominal Sisa', header: 'Nominal Sisa',
flex: 1.2, flex: 1.2,
align: 'right', 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 // Helper functions for PdfTable - Detail Per Kandang
const getDetailColumns = (): PdfColumn[] => [ const getDetailColumns = (
{ key: 'no', header: 'No', flex: 0.5, align: 'center' }, summary?: HppPerKandangReport['summary'],
{ key: 'kandang', header: 'Kandang', flex: 1.5, align: 'left' }, allFeedSuppliers?: string,
{ key: 'rentang_bw', header: 'Rentang BW', flex: 1, align: 'left' }, allDocSuppliers?: string
): PdfColumn<HppPerKandangRow>[] => [
{
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', key: 'rata_rata_bobot',
header: 'Rata-Rata Bobot (Kg)', header: 'Rata-Rata Bobot (Kg)',
flex: 1, flex: 1,
align: 'right', 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', key: 'feed_supplier',
header: 'Feed (Supplier)', header: 'Feed (Supplier)',
flex: 1.2, flex: 1.2,
align: 'left', align: 'left',
cell: ({ row }) => formatSuppliers(row.feed_suppliers),
footer: allFeedSuppliers || '-',
}, },
{ {
key: 'doc_supplier', key: 'doc_supplier',
header: 'DOC (Supplier)', header: 'DOC (Supplier)',
flex: 1, flex: 1,
align: 'left', align: 'left',
cell: ({ row }) => formatSuppliers(row.doc_suppliers),
footer: allDocSuppliers || '-',
}, },
{ {
key: 'rata_harga_doc', key: 'rata_harga_doc',
header: 'Rata-Rata Harga DOC', header: 'Rata-Rata Harga DOC',
flex: 1.2, flex: 1.2,
align: 'right', 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', key: 'hpp_telur',
header: 'HPP Telur (Rp/Kg)', header: 'HPP Telur (Rp/Kg)',
flex: 1, flex: 1,
align: 'right', 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', key: 'nominal_sisa',
header: 'Nominal Sisa', header: 'Nominal Sisa',
flex: 1.2, flex: 1.2,
align: 'right', 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 = ( const createPDFDocument = (
params: HppPerKandangExportParams, params: HppPerKandangExportParams,
allFeedSuppliers: string, allFeedSuppliers: string,
@@ -355,7 +301,7 @@ const createPDFDocument = (
</PdfTypography> </PdfTypography>
<PdfTable <PdfTable
columns={getRekapitulasiColumns()} columns={getRekapitulasiColumns()}
data={getRekapitulasiData(rekapitulasiByWeightRange)} data={rekapitulasiByWeightRange}
/> />
</View> </View>
@@ -365,17 +311,13 @@ const createPDFDocument = (
Detail Per Kandang Detail Per Kandang
</PdfTypography> </PdfTypography>
<PdfTable <PdfTable
columns={getDetailColumns()} columns={getDetailColumns(
data={getDetailData(params.data.rows)} params.data.summary,
footer={ allFeedSuppliers,
params.data.summary allDocSuppliers
? getDetailFooter( )}
params.data.summary, data={params.data.rows}
allFeedSuppliers, showFooter={!!params.data.summary}
allDocSuppliers
)
: undefined
}
footerLabel='TOTAL' footerLabel='TOTAL'
/> />
</View> </View>
@@ -9,11 +9,7 @@ import { ProductionResult } from '@/types/api/report/production-result';
import { PdfTypography } from '@/components/helper/pdf/typography/PdfTypography'; import { PdfTypography } from '@/components/helper/pdf/typography/PdfTypography';
import { PdfParamBadge } from '@/components/helper/pdf/badge/PdfParamBadge'; import { PdfParamBadge } from '@/components/helper/pdf/badge/PdfParamBadge';
import { PdfPageNumber } from '@/components/helper/pdf/layout/PdfPageNumber'; import { PdfPageNumber } from '@/components/helper/pdf/layout/PdfPageNumber';
import { import { PdfTable, PdfColumn } from '@/components/helper/pdf/table';
PdfTable,
PdfColumn,
PdfTbodyCell,
} from '@/components/helper/pdf/table';
type MappedProductionResultsItem = { type MappedProductionResultsItem = {
projectFlockKandang: BaseProjectFlockKandang; projectFlockKandang: BaseProjectFlockKandang;
@@ -64,246 +60,339 @@ function valueText(v: unknown) {
// ======================================== // ========================================
// TABLE 1: WOA & BW // TABLE 1: WOA & BW
// ======================================== // ========================================
const getBwTableColumns = (): PdfColumn[] => [ const getBwTableColumns = (): PdfColumn<ProductionResult>[] => [
{ key: 'no', header: 'No', flex: 0.5, align: 'center' }, {
{ key: 'woa', header: 'Week of Age', flex: 0.8, align: 'center' }, key: 'no',
{ key: 'bw', header: 'Body Weight', flex: 1, align: 'right' }, header: 'No',
{ key: 'std_bw', header: 'Std Body Weight', flex: 1, align: 'right' }, flex: 0.5,
{ key: 'uniformity', header: 'Uniformity', flex: 1.2, align: 'right' }, 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', key: 'std_uniformity',
header: 'Std Uniformity', header: 'Std Uniformity',
flex: 1.3, flex: 1.3,
align: 'right', 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 // TABLE 2: DEPLESI
// ======================================== // ========================================
const getDepTableColumns = (): PdfColumn[] => [ const getDepTableColumns = (): PdfColumn<ProductionResult>[] => [
{ key: 'no', header: 'No', flex: 0.5, align: 'center' }, {
key: 'no',
header: 'No',
flex: 0.5,
align: 'center',
cell: ({ row, index }) => index + 1,
},
{ {
key: 'dep_kum', key: 'dep_kum',
header: 'Depletion Cummulative', header: 'Depletion Cummulative',
flex: 1.5, flex: 1.5,
align: 'right', 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 // TABLE 3: BUTIRAN
// ======================================== // ========================================
const getButiranTableColumns = (): PdfColumn[] => [ const getButiranTableColumns = (): PdfColumn<ProductionResult>[] => [
{ key: 'no', header: 'No', flex: 0.5, align: 'center' }, {
{ key: 'butiran_utuh', header: 'Utuh', flex: 1.2, align: 'right' }, key: 'no',
{ key: 'butiran_putih', header: 'Putih', flex: 1.2, align: 'right' }, header: 'No',
{ key: 'butiran_retak', header: 'Retak', flex: 1.2, align: 'right' }, flex: 0.5,
{ key: 'butiran_pecah', header: 'Pecah', flex: 1.2, align: 'right' }, align: 'center',
{ key: 'butiran_jumlah', header: 'Jumlah', flex: 1.2, align: 'right' }, cell: ({ row, index }) => index + 1,
{ key: 'total_butir', header: 'Total Butir', flex: 1.3, align: 'right' }, },
{
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) // TABLE 4: BERAT (KG)
// ======================================== // ========================================
const getKgTableColumns = (): PdfColumn[] => [ const getKgTableColumns = (): PdfColumn<ProductionResult>[] => [
{ key: 'no', header: 'No', flex: 0.5, align: 'center' }, {
{ key: 'kg_utuh', header: 'Utuh (Kg)', flex: 1.2, align: 'right' }, key: 'no',
{ key: 'kg_putih', header: 'Putih (Kg)', flex: 1.2, align: 'right' }, header: 'No',
{ key: 'kg_retak', header: 'Retak (Kg)', flex: 1.2, align: 'right' }, flex: 0.5,
{ key: 'kg_pecah', header: 'Pecah (Kg)', flex: 1.2, align: 'right' }, align: 'center',
{ key: 'kg_jumlah', header: 'Jumlah (Kg)', flex: 1.3, align: 'right' }, cell: ({ row, index }) => index + 1,
{ key: 'total_kg', header: 'Total (Kg)', flex: 1.3, align: 'right' }, },
{
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 // TABLE 5: PERSENTASE
// ======================================== // ========================================
const getPersenTableColumns = (): PdfColumn[] => [ const getPersenTableColumns = (): PdfColumn<ProductionResult>[] => [
{ key: 'no', header: 'No', flex: 0.5, align: 'center' }, {
{ key: 'persen_utuh', header: 'Utuh (%)', flex: 1.5, align: 'right' }, key: 'no',
{ key: 'persen_putih', header: 'Putih (%)', flex: 1.5, align: 'right' }, header: 'No',
{ key: 'persen_retak', header: '% Retak (%)', flex: 1.5, align: 'right' }, flex: 0.5,
{ key: 'persen_pecah', header: '% Pecah (%)', flex: 1.5, align: 'right' }, 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) // TABLE 6: PRODUKSI (HD, FI, EM, EW)
// ======================================== // ========================================
const getProduksi1TableColumns = (): PdfColumn[] => [ const getProduksi1TableColumns = (): PdfColumn<ProductionResult>[] => [
{ key: 'no', header: 'No', flex: 0.5, align: 'center' }, {
{ key: 'hd', header: 'Hen Day', flex: 0.8, align: 'right' }, key: 'no',
{ key: 'hd_std', header: 'Hen Day Std', flex: 1, align: 'right' }, header: 'No',
{ key: 'fi', header: 'Feed Intake', flex: 0.8, align: 'right' }, flex: 0.5,
{ key: 'fi_std', header: 'Feed Intake Std', flex: 1, align: 'right' }, align: 'center',
{ key: 'em', header: 'Egg Mass', flex: 0.8, align: 'right' }, cell: ({ row, index }) => index + 1,
{ 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' }, 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) // TABLE 7: PRODUKSI (FCR, HH)
// ======================================== // ========================================
const getProduksi2TableColumns = (): PdfColumn[] => [ const getProduksi2TableColumns = (): PdfColumn<ProductionResult>[] => [
{ key: 'no', header: 'No', flex: 0.5, align: 'center' }, {
{ key: 'fcr', header: 'FCR', flex: 1, align: 'right' }, key: 'no',
{ key: 'fcr_std', header: 'FCR Std', flex: 1.2, align: 'right' }, header: 'No',
{ key: 'hh', header: 'Hen House', flex: 1, align: 'right' }, flex: 0.5,
{ key: 'hh_std', header: 'Hen House Std', flex: 1.2, align: 'right' }, 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 * ✅ Main PDF Component
*/ */
@@ -383,7 +472,7 @@ const ProductionResultReportPDF = ({
<Text style={styles.tableTitle}>1. WOA & Body Weight</Text> <Text style={styles.tableTitle}>1. WOA & Body Weight</Text>
<PdfTable <PdfTable
columns={getBwTableColumns()} columns={getBwTableColumns()}
data={getBwTableData(item.productionResult!)} data={item.productionResult!}
/> />
</View> </View>
@@ -392,7 +481,7 @@ const ProductionResultReportPDF = ({
<Text style={styles.tableTitle}>2. Deplesi</Text> <Text style={styles.tableTitle}>2. Deplesi</Text>
<PdfTable <PdfTable
columns={getDepTableColumns()} columns={getDepTableColumns()}
data={getDepTableData(item.productionResult!)} data={item.productionResult!}
/> />
</View> </View>
@@ -401,7 +490,7 @@ const ProductionResultReportPDF = ({
<Text style={styles.tableTitle}>3. Butiran</Text> <Text style={styles.tableTitle}>3. Butiran</Text>
<PdfTable <PdfTable
columns={getButiranTableColumns()} columns={getButiranTableColumns()}
data={getButiranTableData(item.productionResult!)} data={item.productionResult!}
/> />
</View> </View>
@@ -410,7 +499,7 @@ const ProductionResultReportPDF = ({
<Text style={styles.tableTitle}>4. Berat (Kg)</Text> <Text style={styles.tableTitle}>4. Berat (Kg)</Text>
<PdfTable <PdfTable
columns={getKgTableColumns()} columns={getKgTableColumns()}
data={getKgTableData(item.productionResult!)} data={item.productionResult!}
/> />
</View> </View>
@@ -419,7 +508,7 @@ const ProductionResultReportPDF = ({
<Text style={styles.tableTitle}>5. Persentase</Text> <Text style={styles.tableTitle}>5. Persentase</Text>
<PdfTable <PdfTable
columns={getPersenTableColumns()} columns={getPersenTableColumns()}
data={getPersenTableData(item.productionResult!)} data={item.productionResult!}
/> />
</View> </View>
@@ -430,7 +519,7 @@ const ProductionResultReportPDF = ({
</Text> </Text>
<PdfTable <PdfTable
columns={getProduksi1TableColumns()} columns={getProduksi1TableColumns()}
data={getProduksi1TableData(item.productionResult!)} data={item.productionResult!}
/> />
</View> </View>
@@ -439,7 +528,7 @@ const ProductionResultReportPDF = ({
<Text style={styles.tableTitle}>7. Produksi (FCR, HH)</Text> <Text style={styles.tableTitle}>7. Produksi (FCR, HH)</Text>
<PdfTable <PdfTable
columns={getProduksi2TableColumns()} columns={getProduksi2TableColumns()}
data={getProduksi2TableData(item.productionResult!)} data={item.productionResult!}
/> />
</View> </View>
</> </>