diff --git a/src/components/pages/report/logistic-stock/export/PurchasesPerSupplierExport.tsx b/src/components/pages/report/logistic-stock/export/PurchasesPerSupplierExport.tsx
index a7967159..bd6f301a 100644
--- a/src/components/pages/report/logistic-stock/export/PurchasesPerSupplierExport.tsx
+++ b/src/components/pages/report/logistic-stock/export/PurchasesPerSupplierExport.tsx
@@ -11,6 +11,11 @@ import {
} from '@react-pdf/renderer';
import { LogisticPurchasePerSupplierReport } from '@/types/api/report/logistic-stock';
import { formatCurrency, formatDate, formatNumber } from '@/lib/helper';
+import {
+ PdfTable,
+ PdfColumn,
+ PdfTbodyCell,
+} from '@/components/helper/pdf/table';
Font.register({
family: 'Helvetica',
@@ -39,117 +44,6 @@ const pdfStyles = StyleSheet.create({
marginBottom: 8,
color: '#1f74bf',
},
- table: {
- borderWidth: 1,
- borderColor: '#000000',
- marginBottom: 15,
- },
- tableRow: {
- flexDirection: 'row',
- },
- tableHeader: {
- backgroundColor: '#F5F5F5',
- },
- tableCell: {
- flex: 1,
- borderRightWidth: 1,
- borderRightColor: '#000000',
- borderRightStyle: 'solid',
- padding: 4,
- fontSize: 8,
- textAlign: 'left',
- },
- tableCellNo: {
- flex: 1,
- borderRightWidth: 1,
- borderRightColor: '#000000',
- borderRightStyle: 'solid',
- padding: 4,
- fontSize: 8,
- textAlign: 'center',
- },
- tableCellLast: {
- flex: 1,
- padding: 4,
- fontSize: 8,
- },
- tableCellHeader: {
- flex: 1,
- borderRightWidth: 1,
- borderRightColor: '#000000',
- borderRightStyle: 'solid',
- padding: 4,
- fontSize: 8,
- fontWeight: 'bold',
- backgroundColor: '#F5F5F5',
- borderBottomWidth: 1,
- borderBottomColor: '#000000',
- borderBottomStyle: 'solid',
- paddingVertical: 12,
- textAlign: 'center',
- },
- tableCellHeaderRight: {
- flex: 1,
- borderRightWidth: 1,
- borderRightColor: '#000000',
- borderRightStyle: 'solid',
- padding: 4,
- fontSize: 8,
- fontWeight: 'bold',
- backgroundColor: '#F5F5F5',
- textAlign: 'right',
- borderBottomWidth: 1,
- borderBottomColor: '#000000',
- borderBottomStyle: 'solid',
- paddingVertical: 12,
- },
- tableCellHeaderLast: {
- flex: 1,
- padding: 4,
- fontSize: 8,
- fontWeight: 'bold',
- backgroundColor: '#F5F5F5',
- borderBottomWidth: 1,
- borderBottomColor: '#000000',
- borderBottomStyle: 'solid',
- paddingVertical: 12,
- textAlign: 'center',
- },
- tableCellRight: {
- flex: 1,
- borderRightWidth: 1,
- borderRightColor: '#000000',
- borderRightStyle: 'solid',
- padding: 4,
- fontSize: 8,
- textAlign: 'right',
- },
- tableCellCenter: {
- flex: 1,
- borderRightWidth: 1,
- borderRightColor: '#000000',
- borderRightStyle: 'solid',
- padding: 4,
- fontSize: 8,
- textAlign: 'center',
- },
- tableCellCenterLast: {
- flex: 1,
- padding: 4,
- fontSize: 8,
- textAlign: 'center',
- },
- tableBorderBottom: {
- borderBottomWidth: 1,
- borderBottomColor: '#000000',
- borderBottomStyle: 'solid',
- },
- supplierSection: {
- marginBottom: 10,
- },
- supplierSectionBreak: {
- marginBottom: 15,
- },
badge: {
backgroundColor: '#1f74bf',
color: '#FFFFFF',
@@ -174,6 +68,12 @@ const pdfStyles = StyleSheet.create({
flexWrap: 'wrap',
marginBottom: 8,
},
+ supplierSection: {
+ marginBottom: 10,
+ },
+ supplierSectionBreak: {
+ marginBottom: 15,
+ },
});
interface PurchasesPerSupplierExportParams {
@@ -218,6 +118,85 @@ const getParameterText = (
return paramsText;
};
+// Helper functions for PdfTable
+const getTableColumns = (): PdfColumn[] => [
+ { key: 'no', header: 'No', flex: 0.5, align: 'center' },
+ { key: 'receive_date', header: 'Tanggal Terima', flex: 1, align: 'center' },
+ { key: 'po_date', header: 'Tanggal PO', flex: 1, align: 'center' },
+ { key: 'po_number', header: 'Referensi', flex: 1, align: 'left' },
+ { key: 'product', header: 'Produk', flex: 1, align: 'left' },
+ { key: 'warehouse', header: 'Tujuan', flex: 1, align: 'left' },
+ { key: 'qty', header: 'Qty', flex: 0.8, align: 'right' },
+ { key: 'unit_price', header: 'Harga Beli', flex: 1.2, align: 'right' },
+ {
+ key: 'purchase_value',
+ header: 'Nilai Pembelian',
+ flex: 1.5,
+ align: 'right',
+ },
+ {
+ key: 'transport_price',
+ header: 'Biaya Transport',
+ flex: 1.2,
+ align: 'right',
+ },
+ { key: 'total_amount', header: 'Total', flex: 1.5, align: 'right' },
+ { key: 'expedition', header: 'Armada', flex: 1.2, align: 'center' },
+ { key: 'delivery_number', header: 'Surat Jalan', flex: 1, align: 'left' },
+];
+
+const getTableData = (
+ rows: LogisticPurchasePerSupplierReport['rows']
+): PdfTbodyCell[][] => {
+ return rows.map((item, index) => [
+ { key: 'no', value: index + 1, align: 'center' },
+ {
+ key: 'receive_date',
+ value: formatDate(item.receive_date, 'DD-MMM-YYYY'),
+ align: 'center',
+ },
+ {
+ key: 'po_date',
+ value: formatDate(item.po_date, 'DD-MMM-YYYY'),
+ align: 'center',
+ },
+ { 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_price',
+ value: formatCurrency(item.transport_unit_price || 0),
+ align: 'right',
+ },
+ {
+ key: 'total_amount',
+ value: formatCurrency(item.total_amount || 0),
+ align: 'right',
+ },
+ {
+ key: 'expedition',
+ value: (
+
+ {item.expedition || '-'}
+
+ ),
+ align: 'center',
+ },
+ { key: 'delivery_number', value: item.delivery_number || '-' },
+ ]);
+};
+
const createPDFDocument = (
supplierReports: LogisticPurchasePerSupplierReport[],
params: PurchasesPerSupplierExportParams['params']
@@ -266,114 +245,10 @@ const createPDFDocument = (
{supplierReport.supplier.name}
-
- {/* Table Header */}
-
-
- No
-
-
- Tanggal Terima
-
-
- Tanggal PO
-
-
- Referensi
-
-
- Produk
-
-
- Tujuan
-
-
- Qty
-
-
- Harga Beli
-
-
- Nilai Pembelian
-
-
- Biaya Transport
-
-
- Total
-
-
- Armada
-
-
- Surat Jalan
-
-
-
- {/* Table Body */}
- {supplierReport.rows.map(
- (
- item: LogisticPurchasePerSupplierReport['rows'][number],
- index: number
- ) => (
-
-
- {index + 1}
-
-
-
- {formatDate(item.receive_date, 'DD-MMM-YYYY')}
-
-
-
- {formatDate(item.po_date, 'DD-MMM-YYYY')}
-
-
- {item.po_number || '-'}
-
-
- {item.product?.name || '-'}
-
-
- {item.warehouse?.name || '-'}
-
-
- {formatNumber(item.qty || 0)}
-
-
- {formatCurrency(item.unit_price || 0)}
-
-
- {formatCurrency(item.purchase_value || 0)}
-
-
-
- {formatCurrency(item.transport_unit_price || 0)}
-
-
-
- {formatCurrency(item.total_amount || 0)}
-
-
-
- {item.expedition || '-'}
-
-
-
- {item.delivery_number || '-'}
-
-
- )
- )}
-
+
);
}
diff --git a/src/components/pages/report/sale/export/HppPerkandangExport.tsx b/src/components/pages/report/sale/export/HppPerkandangExport.tsx
index 9b05a88d..3a76d8f4 100644
--- a/src/components/pages/report/sale/export/HppPerkandangExport.tsx
+++ b/src/components/pages/report/sale/export/HppPerkandangExport.tsx
@@ -15,6 +15,12 @@ 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';
Font.register({
family: 'Helvetica',
@@ -43,85 +49,6 @@ const pdfStyles = StyleSheet.create({
marginBottom: 8,
color: '#1f74bf',
},
- table: {
- borderWidth: 1,
- borderColor: '#000000',
- marginBottom: 15,
- },
- tableRow: {
- flexDirection: 'row',
- },
- tableHeader: {
- backgroundColor: '#F5F5F5',
- },
- tableCell: {
- flex: 1,
- borderRightWidth: 1,
- borderRightColor: '#000000',
- borderRightStyle: 'solid',
- padding: 4,
- fontSize: 8,
- textAlign: 'left',
- },
- tableCellHeader: {
- flex: 1,
- borderRightWidth: 1,
- borderRightColor: '#000000',
- borderRightStyle: 'solid',
- padding: 4,
- fontSize: 8,
- fontWeight: 'bold',
- backgroundColor: '#F5F5F5',
- borderBottomWidth: 1,
- borderBottomColor: '#000000',
- borderBottomStyle: 'solid',
- paddingVertical: 12,
- textAlign: 'center',
- },
- tableCellHeaderRight: {
- flex: 1,
- borderRightWidth: 1,
- borderRightColor: '#000000',
- borderRightStyle: 'solid',
- padding: 4,
- fontSize: 8,
- fontWeight: 'bold',
- backgroundColor: '#F5F5F5',
- textAlign: 'right',
- borderBottomWidth: 1,
- borderBottomColor: '#000000',
- borderBottomStyle: 'solid',
- paddingVertical: 12,
- },
- tableCellRight: {
- flex: 1,
- borderRightWidth: 1,
- borderRightColor: '#000000',
- borderRightStyle: 'solid',
- padding: 4,
- fontSize: 8,
- textAlign: 'right',
- },
- tableCellCenter: {
- flex: 1,
- borderRightWidth: 1,
- borderRightColor: '#000000',
- borderRightStyle: 'solid',
- padding: 4,
- fontSize: 8,
- textAlign: 'center',
- },
- tableBorderBottom: {
- borderBottomWidth: 1,
- borderBottomColor: '#000000',
- borderBottomStyle: 'solid',
- },
- supplierSection: {
- marginBottom: 10,
- },
- supplierSectionBreak: {
- marginBottom: 15,
- },
parameterBadge: {
backgroundColor: '#F5F5F5',
color: '#333333',
@@ -136,6 +63,9 @@ const pdfStyles = StyleSheet.create({
flexWrap: 'wrap',
marginBottom: 8,
},
+ section: {
+ marginBottom: 15,
+ },
});
interface HppPerKandangExportParams {
@@ -192,6 +122,215 @@ const getParameterText = (params: HppPerKandangExportParams['params']) => {
return paramsText;
};
+// 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' },
+ {
+ key: 'rata_rata_bobot',
+ header: 'Rata-Rata Bobot (Kg)',
+ flex: 1.2,
+ align: 'right',
+ },
+ { key: 'feed_supplier', header: 'Feed (Supplier)', flex: 1.5, align: 'left' },
+ { key: 'doc_supplier', header: 'DOC (Supplier)', flex: 1.2, align: 'left' },
+ {
+ key: 'rata_harga_doc',
+ header: 'Rata-Rata Harga DOC',
+ flex: 1.2,
+ align: 'right',
+ },
+ { key: 'hpp_telur', header: 'HPP Telur (RP/KG)', flex: 1.2, align: 'right' },
+ { key: 'nominal_sisa', header: 'Nominal Sisa', flex: 1.2, align: 'right' },
+];
+
+const getRekapitulasiData = (
+ perWeightRange: HppPerKandangPerWeightRange[]
+): PdfTbodyCell[][] => {
+ return perWeightRange.map((group) => [
+ { key: 'rentang_bw', value: group.label, align: 'center' },
+ {
+ key: 'sisa_butir',
+ value: formatNumber(group.egg_production_pieces),
+ align: 'right',
+ },
+ {
+ key: 'sisa_kg',
+ value: formatNumber(group.egg_production_kg),
+ align: 'right',
+ },
+ {
+ key: 'rata_rata_bobot',
+ value: formatNumber(group.avg_weight_kg),
+ align: 'right',
+ },
+ {
+ key: 'feed_supplier',
+ value:
+ group.feed_suppliers
+ ?.map((s: { alias?: string; name: string }) => s.alias || s.name)
+ .join(' | ') || '-',
+ },
+ {
+ key: 'doc_supplier',
+ value:
+ group.doc_suppliers
+ ?.map((s: { alias?: string; name: string }) => s.alias || s.name)
+ .join(' | ') || '-',
+ },
+ {
+ key: 'rata_harga_doc',
+ value: formatCurrency(group.average_doc_price_rp),
+ align: 'right',
+ },
+ {
+ key: 'hpp_telur',
+ value: formatCurrency(group.egg_hpp_rp_per_kg),
+ align: 'right',
+ },
+ {
+ key: 'nominal_sisa',
+ value: formatCurrency(group.egg_value_rp),
+ 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' },
+ {
+ key: 'rata_rata_bobot',
+ header: 'Rata-Rata Bobot (Kg)',
+ flex: 1,
+ align: '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' },
+ { key: 'doc_supplier', header: 'DOC (Supplier)', flex: 1, align: 'left' },
+ {
+ key: 'rata_harga_doc',
+ header: 'Rata-Rata Harga DOC',
+ flex: 1.2,
+ align: 'right',
+ },
+ { key: 'hpp_telur', header: 'HPP Telur (RP/KG)', flex: 1, align: 'right' },
+ { key: 'nominal_sisa', header: 'Nominal Sisa', flex: 1.2, align: 'right' },
+];
+
+const getDetailData = (rows: HppPerKandangRow[]): PdfTbodyCell[][] => {
+ return rows.map((item, index) => [
+ { key: 'no', value: index + 1, align: 'center' },
+ { 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),
+ align: 'right',
+ },
+ {
+ key: 'sisa_butir',
+ value: formatNumber(item.egg_production_pieces),
+ align: 'right',
+ },
+ {
+ key: 'sisa_kg',
+ value: formatNumber(item.egg_production_kg),
+ align: 'right',
+ },
+ {
+ key: 'feed_supplier',
+ value:
+ item.feed_suppliers
+ ?.map((s: { alias?: string; name: string }) => s.alias || s.name)
+ .join(' | ') || '-',
+ },
+ {
+ key: 'doc_supplier',
+ value:
+ item.doc_suppliers
+ ?.map((s: { alias?: string; name: string }) => s.alias || s.name)
+ .join(' | ') || '-',
+ },
+ {
+ key: 'rata_harga_doc',
+ value: formatCurrency(item.average_doc_price_rp),
+ align: 'right',
+ },
+ {
+ key: 'hpp_telur',
+ value: formatCurrency(item.egg_hpp_rp_per_kg),
+ align: 'right',
+ },
+ {
+ key: 'nominal_sisa',
+ value: formatCurrency(item.egg_value_rp),
+ align: 'right',
+ },
+ ]);
+};
+
+const getDetailFooter = (
+ summary: HppPerKandangReport['summary']
+): PdfTfootCell[] => {
+ if (!summary?.total) return [];
+
+ const allFeedSuppliers =
+ summary.total.feed_suppliers
+ ?.map((s: { alias?: string; name: string }) => s.alias || s.name)
+ .join(' | ') || '-';
+
+ const allDocSuppliers =
+ summary.total.doc_suppliers
+ ?.map((s: { alias?: string; name: string }) => s.alias || s.name)
+ .join(' | ') || '-';
+
+ return [
+ { key: 'no', value: 'TOTAL' },
+ { key: 'kandang', value: 'ALL' },
+ { key: 'rentang_bw', value: '-' },
+ {
+ key: 'rata_rata_bobot',
+ value: formatNumber(summary.total.average_weight_kg),
+ align: 'right',
+ },
+ {
+ key: 'sisa_butir',
+ value: formatNumber(summary.total.total_egg_production_pieces),
+ align: 'right',
+ },
+ {
+ key: 'sisa_kg',
+ value: formatNumber(summary.total.total_egg_production_kg),
+ 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),
+ align: 'right',
+ },
+ {
+ key: 'hpp_telur',
+ value: formatCurrency(summary.total.average_egg_hpp_rp_per_kg),
+ align: 'right',
+ },
+ {
+ key: 'nominal_sisa',
+ value: formatCurrency(summary.total.total_egg_value_rp),
+ align: 'right',
+ },
+ ];
+};
+
const createPDFDocument = (
data: HppPerKandangExportParams['data'],
params: HppPerKandangExportParams['params']
@@ -216,404 +355,23 @@ const createPDFDocument = (
{/* Rekapitulasi Section */}
-
+
Rekapitulasi
-
-
- {/* Table Header */}
-
-
- Rentang BW
-
-
- Sisa Butir
-
-
- Sisa Kg
-
-
- Rata-Rata Bobot (Kg)
-
-
- Feed (Supplier)
-
-
- DOC (Supplier)
-
-
- Rata-Rata Harga DOC
-
-
- HPP Telur (RP/KG)
-
-
- Nominal Sisa
-
-
-
- {/* Table Body - Rekapitulasi */}
- {rekapitulasiByWeightRange.map(
- (group: HppPerKandangPerWeightRange, index: number) => (
-
-
- {group.label}
-
-
- {formatNumber(group.egg_production_pieces)}
-
-
- {formatNumber(group.egg_production_kg)}
-
-
- {formatNumber(group.avg_weight_kg)}
-
-
-
- {group.feed_suppliers
- ?.map(
- (s: { alias?: string; name: string }) =>
- s.alias || s.name
- )
- .join(' | ') || '-'}
-
-
-
-
- {group.doc_suppliers
- ?.map(
- (s: { alias?: string; name: string }) =>
- s.alias || s.name
- )
- .join(' | ') || '-'}
-
-
-
- {formatCurrency(group.average_doc_price_rp)}
-
-
- {formatCurrency(group.egg_hpp_rp_per_kg)}
-
-
- {formatCurrency(group.egg_value_rp)}
-
-
- )
- )}
-
+
{/* Detail Per Kandang Section */}
-
+
Detail Per Kandang
-
-
- {/* Table Header */}
-
-
- No
-
-
- Kandang
-
-
- Rentang BW
-
-
- Rata-Rata Bobot (Kg)
-
-
- Sisa Butir
-
-
- Sisa Kg (Telur)
-
-
- Feed (Supplier)
-
-
- DOC (Supplier)
-
-
- Rata-Rata Harga DOC
-
-
- HPP Telur (RP/KG)
-
-
- Nominal Sisa
-
-
-
- {/* Table Body - Detail Per Kandang */}
- {data.rows.map((item: HppPerKandangRow, index: number) => (
-
-
- {index + 1}
-
-
- {item.kandang?.name || '-'}
-
-
-
- {item.weight_range.weight_min.toFixed(2)} -{' '}
- {item.weight_range.weight_max.toFixed(2)}
-
-
-
- {formatNumber(item.avg_weight_kg)}
-
-
- {formatNumber(item.egg_production_pieces)}
-
-
- {formatNumber(item.egg_production_kg)}
-
-
-
- {item.feed_suppliers
- ?.map(
- (s: { alias?: string; name: string }) =>
- s.alias || s.name
- )
- .join(' | ')}
-
-
-
-
- {item.doc_suppliers
- ?.map(
- (s: { alias?: string; name: string }) =>
- s.alias || s.name
- )
- .join(' | ')}
-
-
-
- {formatCurrency(item.average_doc_price_rp)}
-
-
- {formatCurrency(item.egg_hpp_rp_per_kg)}
-
-
- {formatCurrency(item.egg_value_rp)}
-
-
- ))}
-
- {/* TOTAL Row */}
- {data.summary?.total && (
-
-
- TOTAL
-
-
- ALL
-
-
- -
-
-
-
- {formatNumber(data.summary.total.average_weight_kg)}
-
-
-
-
- {formatNumber(
- data.summary.total.total_egg_production_pieces
- )}
-
-
-
-
- {formatNumber(data.summary.total.total_egg_production_kg)}
-
-
-
-
- {data.rows
- .flatMap((row: HppPerKandangRow) =>
- row.feed_suppliers?.map(
- (s: { alias?: string; name: string }) =>
- s.alias || s.name
- )
- )
- .filter(
- (v: string, i: number, a: string[]) =>
- a.indexOf(v) === i
- )
- .join(' | ') || '-'}
-
-
-
-
- {data.rows
- .flatMap((row: HppPerKandangRow) =>
- row.doc_suppliers?.map(
- (s: { alias?: string; name: string }) =>
- s.alias || s.name
- )
- )
- .filter(
- (v: string, i: number, a: string[]) =>
- a.indexOf(v) === i
- )
- .join(' | ') || '-'}
-
-
-
-
- {formatCurrency(
- data.summary.total.total_average_doc_price_rp
- )}
-
-
-
-
- {formatCurrency(
- data.summary.total.average_egg_hpp_rp_per_kg
- )}
-
-
-
-
- {formatCurrency(data.summary.total.total_egg_value_rp)}
-
-
-
- )}
-
+