mirror of
https://gitlab.com/mbugroup/lti-web-client.git
synced 2026-05-20 05:22:02 +00:00
refactor(FE): Refactor PDF table components to simplify imports
This commit is contained in:
@@ -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<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',
|
||||
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 }) => (
|
||||
<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: '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 ? (
|
||||
<View style={{ alignItems: 'center' }}>
|
||||
<PdfStatusBadge
|
||||
style={{
|
||||
backgroundColor: getPDFBadgeStyle(item.status, 'payment').bg,
|
||||
color: getPDFBadgeStyle(item.status, 'payment').text,
|
||||
borderColor: getPDFBadgeStyle(item.status, 'payment').border,
|
||||
backgroundColor: getPDFBadgeStyle(row.status, 'payment').bg,
|
||||
color: getPDFBadgeStyle(row.status, 'payment').text,
|
||||
borderColor: getPDFBadgeStyle(row.status, 'payment').border,
|
||||
}}
|
||||
>
|
||||
{formatTitleCase(item.status)}
|
||||
{formatTitleCase(row.status)}
|
||||
</PdfStatusBadge>
|
||||
</View>
|
||||
) : (
|
||||
'-'
|
||||
),
|
||||
},
|
||||
{
|
||||
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 */}
|
||||
<PdfTable
|
||||
columns={getTableColumns()}
|
||||
data={getTableData(customerReport.rows)}
|
||||
footer={
|
||||
customerReport.summary
|
||||
? getTableFooter(customerReport.summary)
|
||||
: undefined
|
||||
}
|
||||
columns={getTableColumns(customerReport.summary)}
|
||||
data={customerReport.rows}
|
||||
showFooter={!!customerReport.summary}
|
||||
firstRow={
|
||||
typeof customerReport.initial_balance === 'number' &&
|
||||
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 { PdfTypography } from '@/components/helper/pdf/typography/PdfTypography';
|
||||
import { PdfStatusBadge } from '@/components/helper/pdf/badge/PdfStatusBadge';
|
||||
import {
|
||||
PdfTable,
|
||||
PdfColumn,
|
||||
PdfTbodyCell,
|
||||
PdfTfootCell,
|
||||
} from '@/components/helper/pdf/table';
|
||||
import { PdfTable, PdfColumn } from '@/components/helper/pdf/table';
|
||||
import { getPDFBadgeStyle } from '@/components/helper/pdf/utils/pdf-badge';
|
||||
import { Text } from '@react-pdf/renderer';
|
||||
|
||||
Font.register({
|
||||
family: 'Helvetica',
|
||||
@@ -44,160 +40,225 @@ const pdfStyles = StyleSheet.create({
|
||||
},
|
||||
});
|
||||
|
||||
const getTableColumns = (): PdfColumn[] => [
|
||||
{ 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 !== '-' ? (
|
||||
<View style={{ alignItems: 'center' }}>
|
||||
<PdfStatusBadge
|
||||
style={{
|
||||
backgroundColor: getPDFBadgeStyle(item.due_status, 'due').bg,
|
||||
color: getPDFBadgeStyle(item.due_status, 'due').text,
|
||||
borderColor: getPDFBadgeStyle(item.due_status, 'due').border,
|
||||
backgroundColor: getPDFBadgeStyle(
|
||||
(row as unknown as DebtRow).due_status,
|
||||
'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>
|
||||
</View>
|
||||
) : (
|
||||
'-'
|
||||
),
|
||||
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 }) => (
|
||||
<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',
|
||||
value: formatCurrency(item.payment_price),
|
||||
header: 'Pembayaran (Rp)',
|
||||
flex: 1.5,
|
||||
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',
|
||||
value: formatCurrency(item.balance),
|
||||
header: 'Sisa Saldo Hutang (Rp)',
|
||||
flex: 1.5,
|
||||
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',
|
||||
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 !== '-' ? (
|
||||
<View style={{ alignItems: 'center' }}>
|
||||
<PdfStatusBadge
|
||||
style={{
|
||||
backgroundColor: getPDFBadgeStyle(item.status, 'payment').bg,
|
||||
color: getPDFBadgeStyle(item.status, 'payment').text,
|
||||
borderColor: getPDFBadgeStyle(item.status, 'payment').border,
|
||||
backgroundColor: getPDFBadgeStyle(
|
||||
(row as unknown as DebtRow).status,
|
||||
'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>
|
||||
</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 {
|
||||
data: DebtSupplier[];
|
||||
params?: {
|
||||
@@ -263,13 +324,9 @@ const createPDFDocument = (params: DebtSupplierExportPDFParams) => {
|
||||
|
||||
{/* Table */}
|
||||
<PdfTable
|
||||
columns={getTableColumns()}
|
||||
data={getTableData(supplierReport.rows)}
|
||||
footer={
|
||||
supplierReport.total
|
||||
? getTableFooter(supplierReport.total)
|
||||
: undefined
|
||||
}
|
||||
columns={getTableColumns(supplierReport.total)}
|
||||
data={supplierReport.rows as unknown as Record<string, unknown>[]}
|
||||
showFooter={!!supplierReport.total}
|
||||
firstRow={
|
||||
typeof supplierReport.initial_balance === 'number' &&
|
||||
supplierReport.initial_balance !== 0
|
||||
|
||||
+100
-109
@@ -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<LogisticPurchasePerSupplierReport['rows'][number]>[] => [
|
||||
{
|
||||
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 ? (
|
||||
<View style={{ alignItems: 'center' }}>
|
||||
<PdfStatusBadge
|
||||
style={{
|
||||
@@ -150,50 +173,22 @@ const getTableData = (
|
||||
borderColor: '#60A5FA',
|
||||
}}
|
||||
>
|
||||
{item.expedition}
|
||||
{row.expedition}
|
||||
</PdfStatusBadge>
|
||||
</View>
|
||||
) : (
|
||||
'-'
|
||||
),
|
||||
},
|
||||
{ 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 */}
|
||||
<PdfTable
|
||||
columns={getTableColumns()}
|
||||
data={getTableData(supplierReport.rows)}
|
||||
footer={
|
||||
supplierReport.summary
|
||||
? getTableFooter(supplierReport.summary)
|
||||
: undefined
|
||||
}
|
||||
columns={getTableColumns(supplierReport.summary)}
|
||||
data={supplierReport.rows}
|
||||
showFooter={!!supplierReport.summary}
|
||||
/>
|
||||
</Page>
|
||||
))}
|
||||
|
||||
@@ -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<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',
|
||||
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 ? (
|
||||
<View style={{ alignItems: 'center' }}>
|
||||
<PdfStatusBadge
|
||||
style={{
|
||||
@@ -138,89 +163,70 @@ const getTableData = (rows: DailyMarketingReport): PdfTbodyCell[][] => {
|
||||
) : (
|
||||
'-'
|
||||
),
|
||||
},
|
||||
{ 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 */}
|
||||
<PdfTable
|
||||
columns={getTableColumns()}
|
||||
data={getTableData(rows)}
|
||||
footer={getTableFooter(summary)}
|
||||
columns={getTableColumns(summary)}
|
||||
data={rows}
|
||||
showFooter={!!summary}
|
||||
footerLabel='TOTAL'
|
||||
/>
|
||||
|
||||
|
||||
@@ -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<HppPerKandangPerWeightRange>[] => [
|
||||
{
|
||||
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<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',
|
||||
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 = (
|
||||
</PdfTypography>
|
||||
<PdfTable
|
||||
columns={getRekapitulasiColumns()}
|
||||
data={getRekapitulasiData(rekapitulasiByWeightRange)}
|
||||
data={rekapitulasiByWeightRange}
|
||||
/>
|
||||
</View>
|
||||
|
||||
@@ -365,17 +311,13 @@ const createPDFDocument = (
|
||||
Detail Per Kandang
|
||||
</PdfTypography>
|
||||
<PdfTable
|
||||
columns={getDetailColumns()}
|
||||
data={getDetailData(params.data.rows)}
|
||||
footer={
|
||||
params.data.summary
|
||||
? getDetailFooter(
|
||||
params.data.summary,
|
||||
allFeedSuppliers,
|
||||
allDocSuppliers
|
||||
)
|
||||
: undefined
|
||||
}
|
||||
columns={getDetailColumns(
|
||||
params.data.summary,
|
||||
allFeedSuppliers,
|
||||
allDocSuppliers
|
||||
)}
|
||||
data={params.data.rows}
|
||||
showFooter={!!params.data.summary}
|
||||
footerLabel='TOTAL'
|
||||
/>
|
||||
</View>
|
||||
|
||||
@@ -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<ProductionResult>[] => [
|
||||
{
|
||||
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<ProductionResult>[] => [
|
||||
{
|
||||
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<ProductionResult>[] => [
|
||||
{
|
||||
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<ProductionResult>[] => [
|
||||
{
|
||||
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<ProductionResult>[] => [
|
||||
{
|
||||
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<ProductionResult>[] => [
|
||||
{
|
||||
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<ProductionResult>[] => [
|
||||
{
|
||||
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 = ({
|
||||
<Text style={styles.tableTitle}>1. WOA & Body Weight</Text>
|
||||
<PdfTable
|
||||
columns={getBwTableColumns()}
|
||||
data={getBwTableData(item.productionResult!)}
|
||||
data={item.productionResult!}
|
||||
/>
|
||||
</View>
|
||||
|
||||
@@ -392,7 +481,7 @@ const ProductionResultReportPDF = ({
|
||||
<Text style={styles.tableTitle}>2. Deplesi</Text>
|
||||
<PdfTable
|
||||
columns={getDepTableColumns()}
|
||||
data={getDepTableData(item.productionResult!)}
|
||||
data={item.productionResult!}
|
||||
/>
|
||||
</View>
|
||||
|
||||
@@ -401,7 +490,7 @@ const ProductionResultReportPDF = ({
|
||||
<Text style={styles.tableTitle}>3. Butiran</Text>
|
||||
<PdfTable
|
||||
columns={getButiranTableColumns()}
|
||||
data={getButiranTableData(item.productionResult!)}
|
||||
data={item.productionResult!}
|
||||
/>
|
||||
</View>
|
||||
|
||||
@@ -410,7 +499,7 @@ const ProductionResultReportPDF = ({
|
||||
<Text style={styles.tableTitle}>4. Berat (Kg)</Text>
|
||||
<PdfTable
|
||||
columns={getKgTableColumns()}
|
||||
data={getKgTableData(item.productionResult!)}
|
||||
data={item.productionResult!}
|
||||
/>
|
||||
</View>
|
||||
|
||||
@@ -419,7 +508,7 @@ const ProductionResultReportPDF = ({
|
||||
<Text style={styles.tableTitle}>5. Persentase</Text>
|
||||
<PdfTable
|
||||
columns={getPersenTableColumns()}
|
||||
data={getPersenTableData(item.productionResult!)}
|
||||
data={item.productionResult!}
|
||||
/>
|
||||
</View>
|
||||
|
||||
@@ -430,7 +519,7 @@ const ProductionResultReportPDF = ({
|
||||
</Text>
|
||||
<PdfTable
|
||||
columns={getProduksi1TableColumns()}
|
||||
data={getProduksi1TableData(item.productionResult!)}
|
||||
data={item.productionResult!}
|
||||
/>
|
||||
</View>
|
||||
|
||||
@@ -439,7 +528,7 @@ const ProductionResultReportPDF = ({
|
||||
<Text style={styles.tableTitle}>7. Produksi (FCR, HH)</Text>
|
||||
<PdfTable
|
||||
columns={getProduksi2TableColumns()}
|
||||
data={getProduksi2TableData(item.productionResult!)}
|
||||
data={item.productionResult!}
|
||||
/>
|
||||
</View>
|
||||
</>
|
||||
|
||||
Reference in New Issue
Block a user