mirror of
https://gitlab.com/mbugroup/lti-web-client.git
synced 2026-05-25 07:45:47 +00:00
Merge branch 'feat/create-pdf-component' into 'development'
[FEAT/FE] Create PDF Table Component See merge request mbugroup/lti-web-client!270
This commit is contained in:
@@ -0,0 +1,45 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import { View, StyleSheet } from '@react-pdf/renderer';
|
||||||
|
import { PdfThead, PdfColumn } from './PdfThead';
|
||||||
|
import { PdfTbody, PdfTbodyCell } from './PdfTbody';
|
||||||
|
import { PdfTfoot, PdfTfootCell } from './PdfTfoot';
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
table: {
|
||||||
|
borderWidth: 1,
|
||||||
|
borderColor: '#000000',
|
||||||
|
marginBottom: 15,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
interface PdfTableProps {
|
||||||
|
columns: PdfColumn[];
|
||||||
|
data: PdfTbodyCell[][];
|
||||||
|
footer?: PdfTfootCell[];
|
||||||
|
footerLabel?: string;
|
||||||
|
firstRow?: {
|
||||||
|
valueKey: string;
|
||||||
|
value: number;
|
||||||
|
align?: 'right';
|
||||||
|
color?: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export const PdfTable = ({
|
||||||
|
columns,
|
||||||
|
data,
|
||||||
|
footer,
|
||||||
|
footerLabel = 'Total',
|
||||||
|
firstRow,
|
||||||
|
}: PdfTableProps) => {
|
||||||
|
return (
|
||||||
|
<View style={styles.table}>
|
||||||
|
<PdfThead columns={columns} />
|
||||||
|
<PdfTbody columns={columns} rows={data} firstRow={firstRow} />
|
||||||
|
{footer && footer.length > 0 && (
|
||||||
|
<PdfTfoot columns={columns} cells={footer} label={footerLabel} />
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -0,0 +1,219 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import { Text, View, StyleSheet } from '@react-pdf/renderer';
|
||||||
|
|
||||||
|
export interface PdfColumn {
|
||||||
|
key: string;
|
||||||
|
header: string;
|
||||||
|
flex: number;
|
||||||
|
align?: 'left' | 'center' | 'right';
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PdfTbodyCell {
|
||||||
|
key: string;
|
||||||
|
value: string | number | React.ReactNode;
|
||||||
|
align?: 'left' | 'center' | 'right';
|
||||||
|
color?: string;
|
||||||
|
formatAs?: 'text' | 'date' | 'currency' | 'number';
|
||||||
|
formatDate?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
tableRow: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
},
|
||||||
|
tableBorderBottom: {
|
||||||
|
borderBottomWidth: 1,
|
||||||
|
borderBottomColor: '#000000',
|
||||||
|
borderBottomStyle: 'solid',
|
||||||
|
},
|
||||||
|
tableCell: {
|
||||||
|
flex: 1,
|
||||||
|
borderRightWidth: 1,
|
||||||
|
borderRightColor: '#000000',
|
||||||
|
borderRightStyle: 'solid',
|
||||||
|
padding: 4,
|
||||||
|
fontSize: 7,
|
||||||
|
textAlign: 'left',
|
||||||
|
},
|
||||||
|
tableCellLast: {
|
||||||
|
flex: 1,
|
||||||
|
padding: 4,
|
||||||
|
fontSize: 7,
|
||||||
|
borderRightWidth: 0,
|
||||||
|
},
|
||||||
|
tableCellRight: {
|
||||||
|
flex: 1,
|
||||||
|
borderRightWidth: 1,
|
||||||
|
borderRightColor: '#000000',
|
||||||
|
borderRightStyle: 'solid',
|
||||||
|
padding: 4,
|
||||||
|
fontSize: 7,
|
||||||
|
textAlign: 'right',
|
||||||
|
},
|
||||||
|
tableCellCenter: {
|
||||||
|
flex: 1,
|
||||||
|
borderRightWidth: 1,
|
||||||
|
borderRightColor: '#000000',
|
||||||
|
borderRightStyle: 'solid',
|
||||||
|
padding: 4,
|
||||||
|
fontSize: 7,
|
||||||
|
textAlign: 'center',
|
||||||
|
},
|
||||||
|
tableCellNo: {
|
||||||
|
flex: 0.5,
|
||||||
|
borderRightWidth: 1,
|
||||||
|
borderRightColor: '#000000',
|
||||||
|
borderRightStyle: 'solid',
|
||||||
|
padding: 4,
|
||||||
|
fontSize: 7,
|
||||||
|
textAlign: 'center',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
interface PdfTbodyProps {
|
||||||
|
columns: PdfColumn[];
|
||||||
|
rows: PdfTbodyCell[][];
|
||||||
|
firstRow?: {
|
||||||
|
valueKey: string;
|
||||||
|
value: number;
|
||||||
|
align?: 'right';
|
||||||
|
color?: string;
|
||||||
|
};
|
||||||
|
formatDate?: (date: string, format: string) => string;
|
||||||
|
formatNumber?: (num: number) => string;
|
||||||
|
formatCurrency?: (num: number) => string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const PdfTbody = ({ columns, rows, firstRow }: PdfTbodyProps) => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{/* First Row */}
|
||||||
|
{firstRow && (
|
||||||
|
<View style={[styles.tableRow, styles.tableBorderBottom]}>
|
||||||
|
{columns.map((column, index) => {
|
||||||
|
const isLastColumn = index === columns.length - 1;
|
||||||
|
const isfirstRowColumn = column.key === firstRow.valueKey;
|
||||||
|
const align = column.align || 'center';
|
||||||
|
|
||||||
|
const cellStyle =
|
||||||
|
column.key === 'no'
|
||||||
|
? [styles.tableCellNo, { flex: column.flex }]
|
||||||
|
: isfirstRowColumn
|
||||||
|
? [
|
||||||
|
styles.tableCellRight,
|
||||||
|
{
|
||||||
|
flex: column.flex,
|
||||||
|
color: firstRow.color || 'black',
|
||||||
|
borderRightWidth: isLastColumn ? 0 : 1,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
: align === 'right'
|
||||||
|
? [
|
||||||
|
styles.tableCellRight,
|
||||||
|
{
|
||||||
|
flex: column.flex,
|
||||||
|
borderRightWidth: isLastColumn ? 0 : 1,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
: align === 'center'
|
||||||
|
? [
|
||||||
|
styles.tableCellCenter,
|
||||||
|
{
|
||||||
|
flex: column.flex,
|
||||||
|
borderRightWidth: isLastColumn ? 0 : 1,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
: isLastColumn
|
||||||
|
? [
|
||||||
|
styles.tableCellLast,
|
||||||
|
{
|
||||||
|
flex: column.flex,
|
||||||
|
borderRightWidth: 0,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
: [styles.tableCell, { flex: column.flex }];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View key={column.key} style={cellStyle}>
|
||||||
|
<Text>{isfirstRowColumn ? firstRow.value : ''}</Text>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Data Rows */}
|
||||||
|
{rows.map((row, rowIndex) => {
|
||||||
|
const isLastRow = rowIndex === rows.length - 1;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View
|
||||||
|
key={rowIndex}
|
||||||
|
style={[
|
||||||
|
styles.tableRow,
|
||||||
|
!isLastRow ? styles.tableBorderBottom : {},
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
{columns.map((column, colIndex) => {
|
||||||
|
const cell = row.find((c) => c.key === column.key);
|
||||||
|
const isLastColumn = colIndex === columns.length - 1;
|
||||||
|
const align = cell?.align || column.align || 'center';
|
||||||
|
|
||||||
|
const cellStyle =
|
||||||
|
column.key === 'no'
|
||||||
|
? [styles.tableCellNo, { flex: column.flex }]
|
||||||
|
: align === 'right'
|
||||||
|
? [
|
||||||
|
styles.tableCellRight,
|
||||||
|
{
|
||||||
|
flex: column.flex,
|
||||||
|
color: cell?.color || 'black',
|
||||||
|
borderRightWidth: isLastColumn ? 0 : 1,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
: align === 'center'
|
||||||
|
? [
|
||||||
|
styles.tableCellCenter,
|
||||||
|
{
|
||||||
|
flex: column.flex,
|
||||||
|
color: cell?.color || 'black',
|
||||||
|
borderRightWidth: isLastColumn ? 0 : 1,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
: isLastColumn
|
||||||
|
? [
|
||||||
|
styles.tableCellLast,
|
||||||
|
{ flex: column.flex, borderRightWidth: 0 },
|
||||||
|
]
|
||||||
|
: [
|
||||||
|
styles.tableCell,
|
||||||
|
{
|
||||||
|
flex: column.flex,
|
||||||
|
color: cell?.color || 'black',
|
||||||
|
borderRightWidth: isLastColumn ? 0 : 1,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View key={column.key} style={cellStyle}>
|
||||||
|
{cell?.value !== undefined &&
|
||||||
|
cell?.value !== null &&
|
||||||
|
cell?.value !== '' ? (
|
||||||
|
typeof cell.value === 'object' ? (
|
||||||
|
cell.value
|
||||||
|
) : (
|
||||||
|
<Text>{String(cell.value)}</Text>
|
||||||
|
)
|
||||||
|
) : (
|
||||||
|
<Text>-</Text>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -0,0 +1,131 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import { Text, View, StyleSheet } from '@react-pdf/renderer';
|
||||||
|
|
||||||
|
export interface PdfColumn {
|
||||||
|
key: string;
|
||||||
|
header: string;
|
||||||
|
flex: number;
|
||||||
|
align?: 'left' | 'center' | 'right';
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PdfTfootCell {
|
||||||
|
key: string;
|
||||||
|
value: string | number;
|
||||||
|
align?: 'left' | 'center' | 'right';
|
||||||
|
flex?: number;
|
||||||
|
color?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
tableRow: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
},
|
||||||
|
summaryRow: {
|
||||||
|
backgroundColor: '#F0F0F0',
|
||||||
|
fontWeight: 'bold',
|
||||||
|
},
|
||||||
|
tableCell: {
|
||||||
|
flex: 1,
|
||||||
|
borderRightWidth: 1,
|
||||||
|
borderRightColor: '#000000',
|
||||||
|
borderRightStyle: 'solid',
|
||||||
|
padding: 4,
|
||||||
|
fontSize: 7,
|
||||||
|
textAlign: 'left',
|
||||||
|
},
|
||||||
|
tableCellLast: {
|
||||||
|
flex: 1,
|
||||||
|
padding: 4,
|
||||||
|
fontSize: 7,
|
||||||
|
borderRightWidth: 0,
|
||||||
|
},
|
||||||
|
tableCellRight: {
|
||||||
|
flex: 1,
|
||||||
|
borderRightWidth: 1,
|
||||||
|
borderRightColor: '#000000',
|
||||||
|
borderRightStyle: 'solid',
|
||||||
|
padding: 4,
|
||||||
|
fontSize: 7,
|
||||||
|
textAlign: 'right',
|
||||||
|
},
|
||||||
|
tableCellCenter: {
|
||||||
|
flex: 1,
|
||||||
|
borderRightWidth: 1,
|
||||||
|
borderRightColor: '#000000',
|
||||||
|
borderRightStyle: 'solid',
|
||||||
|
padding: 4,
|
||||||
|
fontSize: 7,
|
||||||
|
textAlign: 'center',
|
||||||
|
},
|
||||||
|
tableCellNo: {
|
||||||
|
flex: 0.5,
|
||||||
|
borderRightWidth: 1,
|
||||||
|
borderRightColor: '#000000',
|
||||||
|
borderRightStyle: 'solid',
|
||||||
|
padding: 4,
|
||||||
|
fontSize: 7,
|
||||||
|
textAlign: 'center',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
interface PdfTfootProps {
|
||||||
|
columns: PdfColumn[];
|
||||||
|
cells: PdfTfootCell[];
|
||||||
|
label?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const PdfTfoot = ({
|
||||||
|
columns,
|
||||||
|
cells,
|
||||||
|
label = 'Total',
|
||||||
|
}: PdfTfootProps) => {
|
||||||
|
return (
|
||||||
|
<View style={[styles.tableRow, styles.summaryRow]}>
|
||||||
|
{columns.map((column, index) => {
|
||||||
|
const isLastColumn = index === columns.length - 1;
|
||||||
|
const cellData = cells.find((c) => c.key === column.key);
|
||||||
|
|
||||||
|
const cellStyle =
|
||||||
|
column.key === 'no'
|
||||||
|
? [
|
||||||
|
styles.tableCellNo,
|
||||||
|
{ flex: column.flex, borderRightWidth: isLastColumn ? 0 : 1 },
|
||||||
|
]
|
||||||
|
: cellData?.align === 'right'
|
||||||
|
? [
|
||||||
|
styles.tableCellRight,
|
||||||
|
{
|
||||||
|
flex: column.flex,
|
||||||
|
color: cellData?.color || 'black',
|
||||||
|
borderRightWidth: isLastColumn ? 0 : 1,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
: cellData?.align === 'center'
|
||||||
|
? [
|
||||||
|
styles.tableCellCenter,
|
||||||
|
{
|
||||||
|
flex: column.flex,
|
||||||
|
color: cellData?.color || 'black',
|
||||||
|
borderRightWidth: isLastColumn ? 0 : 1,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
: isLastColumn
|
||||||
|
? [styles.tableCellLast, { flex: column.flex }]
|
||||||
|
: [
|
||||||
|
styles.tableCell,
|
||||||
|
{
|
||||||
|
flex: column.flex,
|
||||||
|
color: cellData?.color || 'black',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View key={column.key} style={cellStyle}>
|
||||||
|
<Text>{column.key === 'no' ? label : cellData?.value || ''}</Text>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import { Text, View, StyleSheet } from '@react-pdf/renderer';
|
||||||
|
|
||||||
|
export interface PdfColumn {
|
||||||
|
key: string;
|
||||||
|
header: string;
|
||||||
|
flex: number;
|
||||||
|
align?: 'left' | 'center' | 'right';
|
||||||
|
}
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
tableRow: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
},
|
||||||
|
tableHeader: {
|
||||||
|
backgroundColor: '#F5F5F5',
|
||||||
|
},
|
||||||
|
tableCellHeader: {
|
||||||
|
flex: 1,
|
||||||
|
borderRightWidth: 1,
|
||||||
|
borderRightColor: '#000000',
|
||||||
|
borderRightStyle: 'solid',
|
||||||
|
padding: 4,
|
||||||
|
fontSize: 7,
|
||||||
|
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: 7,
|
||||||
|
fontWeight: 'bold',
|
||||||
|
backgroundColor: '#F5F5F5',
|
||||||
|
textAlign: 'right',
|
||||||
|
borderBottomWidth: 1,
|
||||||
|
borderBottomColor: '#000000',
|
||||||
|
borderBottomStyle: 'solid',
|
||||||
|
paddingVertical: 12,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
interface PdfTheadProps {
|
||||||
|
columns: PdfColumn[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export const PdfThead = ({ columns }: PdfTheadProps) => {
|
||||||
|
return (
|
||||||
|
<View style={[styles.tableRow, styles.tableHeader]}>
|
||||||
|
{columns.map((column, index) => {
|
||||||
|
const align = column.align || 'center';
|
||||||
|
const isLastColumn = index === columns.length - 1;
|
||||||
|
|
||||||
|
const cellStyle =
|
||||||
|
align === 'right'
|
||||||
|
? [
|
||||||
|
styles.tableCellHeaderRight,
|
||||||
|
{
|
||||||
|
flex: column.flex,
|
||||||
|
textAlign: 'right' as const,
|
||||||
|
borderRightWidth: isLastColumn ? 0 : 1,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
: [
|
||||||
|
styles.tableCellHeader,
|
||||||
|
{
|
||||||
|
flex: column.flex,
|
||||||
|
textAlign: align as 'left' | 'center' | 'right',
|
||||||
|
borderRightWidth: isLastColumn ? 0 : 1,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View key={column.key} style={cellStyle}>
|
||||||
|
<Text>{column.header}</Text>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
export { PdfTable } from './PdfTable';
|
||||||
|
export { PdfThead } from './PdfThead';
|
||||||
|
export { PdfTbody } from './PdfTbody';
|
||||||
|
export { PdfTfoot } from './PdfTfoot';
|
||||||
|
export type { PdfColumn } from './PdfThead';
|
||||||
|
export type { PdfTbodyCell } from './PdfTbody';
|
||||||
|
export type { PdfTfootCell } from './PdfTfoot';
|
||||||
@@ -12,6 +12,12 @@ import {
|
|||||||
|
|
||||||
import { formatDate, formatCurrency, formatNumber } from '@/lib/helper';
|
import { formatDate, formatCurrency, formatNumber } from '@/lib/helper';
|
||||||
import { CustomerPaymentReport } from '@/types/api/report/customer-payment';
|
import { CustomerPaymentReport } from '@/types/api/report/customer-payment';
|
||||||
|
import {
|
||||||
|
PdfTable,
|
||||||
|
PdfColumn,
|
||||||
|
PdfTbodyCell,
|
||||||
|
PdfTfootCell,
|
||||||
|
} from '@/components/helper/pdf/table';
|
||||||
|
|
||||||
Font.register({
|
Font.register({
|
||||||
family: 'Helvetica',
|
family: 'Helvetica',
|
||||||
@@ -45,97 +51,6 @@ const pdfStyles = StyleSheet.create({
|
|||||||
marginBottom: 5,
|
marginBottom: 5,
|
||||||
color: '#333333',
|
color: '#333333',
|
||||||
},
|
},
|
||||||
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: 7,
|
|
||||||
textAlign: 'left',
|
|
||||||
},
|
|
||||||
tableCellNo: {
|
|
||||||
flex: 0.5,
|
|
||||||
borderRightWidth: 1,
|
|
||||||
borderRightColor: '#000000',
|
|
||||||
borderRightStyle: 'solid',
|
|
||||||
padding: 4,
|
|
||||||
fontSize: 7,
|
|
||||||
textAlign: 'center',
|
|
||||||
},
|
|
||||||
tableCellLast: {
|
|
||||||
flex: 1,
|
|
||||||
padding: 4,
|
|
||||||
fontSize: 7,
|
|
||||||
},
|
|
||||||
tableCellHeader: {
|
|
||||||
flex: 1,
|
|
||||||
borderRightWidth: 1,
|
|
||||||
borderRightColor: '#000000',
|
|
||||||
borderRightStyle: 'solid',
|
|
||||||
padding: 4,
|
|
||||||
fontSize: 7,
|
|
||||||
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: 7,
|
|
||||||
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: 7,
|
|
||||||
textAlign: 'right',
|
|
||||||
},
|
|
||||||
tableCellCenter: {
|
|
||||||
flex: 1,
|
|
||||||
borderRightWidth: 1,
|
|
||||||
borderRightColor: '#000000',
|
|
||||||
borderRightStyle: 'solid',
|
|
||||||
padding: 4,
|
|
||||||
fontSize: 7,
|
|
||||||
textAlign: 'center',
|
|
||||||
},
|
|
||||||
tableBorderBottom: {
|
|
||||||
borderBottomWidth: 1,
|
|
||||||
borderBottomColor: '#000000',
|
|
||||||
borderBottomStyle: 'solid',
|
|
||||||
},
|
|
||||||
summaryRow: {
|
|
||||||
backgroundColor: '#F0F0F0',
|
|
||||||
fontWeight: 'bold',
|
|
||||||
},
|
|
||||||
badge: {
|
badge: {
|
||||||
backgroundColor: '#1f74bf',
|
backgroundColor: '#1f74bf',
|
||||||
color: '#FFFFFF',
|
color: '#FFFFFF',
|
||||||
@@ -217,6 +132,165 @@ const getParameterText = (
|
|||||||
return paramsText;
|
return paramsText;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Helper functions for PdfTable
|
||||||
|
const getTableColumns = (): PdfColumn[] => [
|
||||||
|
{ key: 'no', header: 'No', flex: 0.5, align: 'center' },
|
||||||
|
{ key: 'trans_date', header: 'Tanggal DO', flex: 1.2, align: 'center' },
|
||||||
|
{
|
||||||
|
key: 'delivery_date',
|
||||||
|
header: 'Tanggal Realisasi',
|
||||||
|
flex: 1.2,
|
||||||
|
align: 'center',
|
||||||
|
},
|
||||||
|
{ 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', flex: 1.2, align: 'right' },
|
||||||
|
{ key: 'final_price', header: 'Harga Akhir', flex: 1.2, align: 'right' },
|
||||||
|
{ key: 'total_price', header: 'Total', flex: 1.2, align: 'right' },
|
||||||
|
{ key: 'payment_amount', header: 'Pembayaran', flex: 1.2, align: 'right' },
|
||||||
|
{ key: 'accounts_receivable', header: 'Saldo', flex: 1.2, align: 'right' },
|
||||||
|
{ 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 ? '#DC2626' : undefined,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'status',
|
||||||
|
value: item.status ? (
|
||||||
|
<View
|
||||||
|
style={[
|
||||||
|
pdfStyles.badge,
|
||||||
|
item.status === 'LUNAS'
|
||||||
|
? pdfStyles.badgeLunas
|
||||||
|
: pdfStyles.badgeBelumLunas,
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<Text>{item.status === 'LUNAS' ? 'Lunas' : 'Belum Lunas'}</Text>
|
||||||
|
</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',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'total_price',
|
||||||
|
value: formatCurrency(summary?.total_grand_amount || 0),
|
||||||
|
align: 'right',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'payment_amount',
|
||||||
|
value: formatCurrency(summary?.total_payment || 0),
|
||||||
|
align: 'right',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'accounts_receivable',
|
||||||
|
value: formatCurrency(summary?.total_accounts_receivable || 0),
|
||||||
|
align: 'right',
|
||||||
|
color:
|
||||||
|
(summary?.total_accounts_receivable || 0) < 0 ? '#DC2626' : undefined,
|
||||||
|
},
|
||||||
|
{ key: 'status', value: '' },
|
||||||
|
{ key: 'pickup_info', value: '' },
|
||||||
|
{ key: 'sales_person', value: '' },
|
||||||
|
];
|
||||||
|
|
||||||
const createPDFDocument = (params: CustomerPaymentExportPDFParams) => {
|
const createPDFDocument = (params: CustomerPaymentExportPDFParams) => {
|
||||||
return (
|
return (
|
||||||
<Document>
|
<Document>
|
||||||
@@ -269,329 +343,27 @@ const createPDFDocument = (params: CustomerPaymentExportPDFParams) => {
|
|||||||
</View>
|
</View>
|
||||||
|
|
||||||
{/* Table */}
|
{/* Table */}
|
||||||
<View style={pdfStyles.table}>
|
<PdfTable
|
||||||
{/* Table Header */}
|
columns={getTableColumns()}
|
||||||
<View style={[pdfStyles.tableRow, pdfStyles.tableHeader]}>
|
data={getTableData(customerReport.rows)}
|
||||||
<View style={[pdfStyles.tableCellHeader, { flex: 0.5 }]}>
|
footer={
|
||||||
<Text>No</Text>
|
customerReport.summary
|
||||||
</View>
|
? getTableFooter(customerReport.summary)
|
||||||
<View style={[pdfStyles.tableCellHeader, { flex: 1.2 }]}>
|
: undefined
|
||||||
<Text>Tanggal DO</Text>
|
}
|
||||||
</View>
|
firstRow={
|
||||||
<View style={[pdfStyles.tableCellHeader, { flex: 1.2 }]}>
|
typeof customerReport.initial_balance === 'number' &&
|
||||||
<Text>Tanggal Realisasi</Text>
|
customerReport.initial_balance !== 0
|
||||||
</View>
|
? {
|
||||||
<View style={[pdfStyles.tableCellHeader, { flex: 0.8 }]}>
|
valueKey: 'accounts_receivable',
|
||||||
<Text>Aging</Text>
|
value: customerReport.initial_balance,
|
||||||
</View>
|
align: 'right',
|
||||||
<View style={[pdfStyles.tableCellHeader, { flex: 1.5 }]}>
|
color:
|
||||||
<Text>Referensi</Text>
|
customerReport.initial_balance < 0 ? '#DC2626' : 'black',
|
||||||
</View>
|
}
|
||||||
<View style={[pdfStyles.tableCellHeader, { flex: 1.2 }]}>
|
: undefined
|
||||||
<Text>No Polisi</Text>
|
}
|
||||||
</View>
|
/>
|
||||||
<View style={[pdfStyles.tableCellHeaderRight, { flex: 0.8 }]}>
|
|
||||||
<Text>Qty</Text>
|
|
||||||
</View>
|
|
||||||
<View style={[pdfStyles.tableCellHeaderRight, { flex: 1 }]}>
|
|
||||||
<Text>Berat</Text>
|
|
||||||
</View>
|
|
||||||
<View style={[pdfStyles.tableCellHeaderRight, { flex: 0.8 }]}>
|
|
||||||
<Text>Rata-Rata</Text>
|
|
||||||
</View>
|
|
||||||
<View style={[pdfStyles.tableCellHeaderRight, { flex: 1.2 }]}>
|
|
||||||
<Text>Harga/Unit</Text>
|
|
||||||
</View>
|
|
||||||
<View style={[pdfStyles.tableCellHeaderRight, { flex: 1.2 }]}>
|
|
||||||
<Text>Harga Akhir</Text>
|
|
||||||
</View>
|
|
||||||
<View style={[pdfStyles.tableCellHeaderRight, { flex: 1.2 }]}>
|
|
||||||
<Text>Total</Text>
|
|
||||||
</View>
|
|
||||||
<View style={[pdfStyles.tableCellHeaderRight, { flex: 1.2 }]}>
|
|
||||||
<Text>Pembayaran</Text>
|
|
||||||
</View>
|
|
||||||
<View style={[pdfStyles.tableCellHeaderRight, { flex: 1.2 }]}>
|
|
||||||
<Text>Saldo</Text>
|
|
||||||
</View>
|
|
||||||
<View style={[pdfStyles.tableCellHeader, { flex: 1.5 }]}>
|
|
||||||
<Text>Keterangan</Text>
|
|
||||||
</View>
|
|
||||||
<View style={[pdfStyles.tableCellHeader, { flex: 1 }]}>
|
|
||||||
<Text>Pengambilan</Text>
|
|
||||||
</View>
|
|
||||||
<View
|
|
||||||
style={[
|
|
||||||
pdfStyles.tableCellHeader,
|
|
||||||
{ flex: 1.5, borderRightWidth: 0 },
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<Text>Sales</Text>
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
|
|
||||||
{/* Table Body */}
|
|
||||||
<>
|
|
||||||
{/* Initial Balance Row */}
|
|
||||||
<View style={[pdfStyles.tableRow, pdfStyles.tableBorderBottom]}>
|
|
||||||
<View style={[pdfStyles.tableCellNo, { flex: 0.5 }]}>
|
|
||||||
<Text></Text>
|
|
||||||
</View>
|
|
||||||
<View style={[pdfStyles.tableCellCenter, { flex: 1.2 }]}>
|
|
||||||
<Text></Text>
|
|
||||||
</View>
|
|
||||||
<View style={[pdfStyles.tableCellCenter, { flex: 1.2 }]}>
|
|
||||||
<Text></Text>
|
|
||||||
</View>
|
|
||||||
<View style={[pdfStyles.tableCellCenter, { flex: 0.8 }]}>
|
|
||||||
<Text></Text>
|
|
||||||
</View>
|
|
||||||
<View style={[pdfStyles.tableCell, { flex: 1.5 }]}>
|
|
||||||
<Text></Text>
|
|
||||||
</View>
|
|
||||||
<View style={[pdfStyles.tableCell, { flex: 1.2 }]}>
|
|
||||||
<Text></Text>
|
|
||||||
</View>
|
|
||||||
<View style={[pdfStyles.tableCellRight, { flex: 0.8 }]}>
|
|
||||||
<Text></Text>
|
|
||||||
</View>
|
|
||||||
<View style={[pdfStyles.tableCellRight, { flex: 1 }]}>
|
|
||||||
<Text></Text>
|
|
||||||
</View>
|
|
||||||
<View style={[pdfStyles.tableCellRight, { flex: 0.8 }]}>
|
|
||||||
<Text></Text>
|
|
||||||
</View>
|
|
||||||
<View style={[pdfStyles.tableCellRight, { flex: 1.2 }]}>
|
|
||||||
<Text></Text>
|
|
||||||
</View>
|
|
||||||
<View style={[pdfStyles.tableCellRight, { flex: 1.2 }]}>
|
|
||||||
<Text></Text>
|
|
||||||
</View>
|
|
||||||
<View style={[pdfStyles.tableCellRight, { flex: 1.2 }]}>
|
|
||||||
<Text></Text>
|
|
||||||
</View>
|
|
||||||
<View style={[pdfStyles.tableCellRight, { flex: 1.2 }]}>
|
|
||||||
<Text></Text>
|
|
||||||
</View>
|
|
||||||
<View
|
|
||||||
style={[
|
|
||||||
pdfStyles.tableCellRight,
|
|
||||||
{
|
|
||||||
flex: 1.2,
|
|
||||||
color:
|
|
||||||
typeof customerReport.initial_balance === 'number' &&
|
|
||||||
customerReport.initial_balance < 0
|
|
||||||
? 'red'
|
|
||||||
: 'black',
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<Text>
|
|
||||||
{formatCurrency(customerReport.initial_balance || 0)}
|
|
||||||
</Text>
|
|
||||||
</View>
|
|
||||||
<View style={[pdfStyles.tableCell, { flex: 1.5 }]}>
|
|
||||||
<Text></Text>
|
|
||||||
</View>
|
|
||||||
<View style={[pdfStyles.tableCell, { flex: 1 }]}>
|
|
||||||
<Text></Text>
|
|
||||||
</View>
|
|
||||||
<View
|
|
||||||
style={[
|
|
||||||
pdfStyles.tableCell,
|
|
||||||
{ flex: 1.5, borderRightWidth: 0 },
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<Text></Text>
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
|
|
||||||
{/* Data Rows */}
|
|
||||||
{customerReport.rows.map((item, index) => (
|
|
||||||
<View
|
|
||||||
key={index}
|
|
||||||
style={[
|
|
||||||
pdfStyles.tableRow,
|
|
||||||
index < customerReport.rows.length - 1
|
|
||||||
? pdfStyles.tableBorderBottom
|
|
||||||
: {},
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<View style={[pdfStyles.tableCellNo, { flex: 0.5 }]}>
|
|
||||||
<Text>{index + 1}</Text>
|
|
||||||
</View>
|
|
||||||
<View style={[pdfStyles.tableCellCenter, { flex: 1.2 }]}>
|
|
||||||
<Text>
|
|
||||||
{item.trans_date
|
|
||||||
? formatDate(item.trans_date, 'DD MMM YY')
|
|
||||||
: '-'}
|
|
||||||
</Text>
|
|
||||||
</View>
|
|
||||||
<View style={[pdfStyles.tableCellCenter, { flex: 1.2 }]}>
|
|
||||||
<Text>
|
|
||||||
{item.delivery_date
|
|
||||||
? formatDate(item.delivery_date, 'DD MMM YY')
|
|
||||||
: '-'}
|
|
||||||
</Text>
|
|
||||||
</View>
|
|
||||||
<View style={[pdfStyles.tableCellCenter, { flex: 0.8 }]}>
|
|
||||||
<Text>
|
|
||||||
{item.aging_day != null
|
|
||||||
? `${formatNumber(item.aging_day)} hari`
|
|
||||||
: '-'}
|
|
||||||
</Text>
|
|
||||||
</View>
|
|
||||||
<View style={[pdfStyles.tableCell, { flex: 1.5 }]}>
|
|
||||||
<Text>{item.reference || '-'}</Text>
|
|
||||||
</View>
|
|
||||||
<View style={[pdfStyles.tableCell, { flex: 1.2 }]}>
|
|
||||||
<Text>
|
|
||||||
{Array.isArray(item.vehicle_numbers)
|
|
||||||
? item.vehicle_numbers.length > 0
|
|
||||||
? item.vehicle_numbers.join(', ')
|
|
||||||
: '-'
|
|
||||||
: '-'}
|
|
||||||
</Text>
|
|
||||||
</View>
|
|
||||||
<View style={[pdfStyles.tableCellRight, { flex: 0.8 }]}>
|
|
||||||
<Text>{formatNumber(item.qty)}</Text>
|
|
||||||
</View>
|
|
||||||
<View style={[pdfStyles.tableCellRight, { flex: 1 }]}>
|
|
||||||
<Text>{formatNumber(item.weight)}</Text>
|
|
||||||
</View>
|
|
||||||
<View style={[pdfStyles.tableCellRight, { flex: 0.8 }]}>
|
|
||||||
<Text>{formatNumber(item.average_weight)}</Text>
|
|
||||||
</View>
|
|
||||||
<View style={[pdfStyles.tableCellRight, { flex: 1.2 }]}>
|
|
||||||
<Text>{formatCurrency(item.unit_price)}</Text>
|
|
||||||
</View>
|
|
||||||
<View style={[pdfStyles.tableCellRight, { flex: 1.2 }]}>
|
|
||||||
<Text>{formatCurrency(item.final_price)}</Text>
|
|
||||||
</View>
|
|
||||||
<View style={[pdfStyles.tableCellRight, { flex: 1.2 }]}>
|
|
||||||
<Text>{formatCurrency(item.total_price)}</Text>
|
|
||||||
</View>
|
|
||||||
<View style={[pdfStyles.tableCellRight, { flex: 1.2 }]}>
|
|
||||||
<Text>{formatCurrency(item.payment_amount)}</Text>
|
|
||||||
</View>
|
|
||||||
<View style={[pdfStyles.tableCellRight, { flex: 1.2 }]}>
|
|
||||||
<Text style={pdfStyles.textError}>
|
|
||||||
{formatCurrency(item.accounts_receivable)}
|
|
||||||
</Text>
|
|
||||||
</View>
|
|
||||||
<View style={[pdfStyles.tableCellCenter, { flex: 1.5 }]}>
|
|
||||||
{item.status ? (
|
|
||||||
<View
|
|
||||||
style={[
|
|
||||||
pdfStyles.badge,
|
|
||||||
item.status === 'LUNAS'
|
|
||||||
? pdfStyles.badgeLunas
|
|
||||||
: pdfStyles.badgeBelumLunas,
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<Text>
|
|
||||||
{item.status === 'LUNAS' ? 'Lunas' : 'Belum Lunas'}
|
|
||||||
</Text>
|
|
||||||
</View>
|
|
||||||
) : (
|
|
||||||
<Text>-</Text>
|
|
||||||
)}
|
|
||||||
</View>
|
|
||||||
<View style={[pdfStyles.tableCell, { flex: 1 }]}>
|
|
||||||
<Text>
|
|
||||||
{Array.isArray(item.pickup_info)
|
|
||||||
? item.pickup_info.length > 0
|
|
||||||
? item.pickup_info.join(', ')
|
|
||||||
: '-'
|
|
||||||
: '-'}
|
|
||||||
</Text>
|
|
||||||
</View>
|
|
||||||
<View
|
|
||||||
style={[
|
|
||||||
pdfStyles.tableCell,
|
|
||||||
{ flex: 1.5, borderRightWidth: 0 },
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<Text>{item.sales_person || '-'}</Text>
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
))}
|
|
||||||
</>
|
|
||||||
|
|
||||||
{/* Summary Row */}
|
|
||||||
{customerReport.summary && (
|
|
||||||
<View style={[pdfStyles.tableRow, pdfStyles.summaryRow]}>
|
|
||||||
<View style={[pdfStyles.tableCellNo, { flex: 0.5 }]}>
|
|
||||||
<Text>Total</Text>
|
|
||||||
</View>
|
|
||||||
<View style={[pdfStyles.tableCell, { flex: 1.2 }]}>
|
|
||||||
<Text></Text>
|
|
||||||
</View>
|
|
||||||
<View style={[pdfStyles.tableCell, { flex: 1.2 }]}>
|
|
||||||
<Text></Text>
|
|
||||||
</View>
|
|
||||||
<View style={[pdfStyles.tableCell, { flex: 0.8 }]}>
|
|
||||||
<Text></Text>
|
|
||||||
</View>
|
|
||||||
<View style={[pdfStyles.tableCell, { flex: 1.5 }]}>
|
|
||||||
<Text></Text>
|
|
||||||
</View>
|
|
||||||
<View style={[pdfStyles.tableCell, { flex: 1.2 }]}>
|
|
||||||
<Text></Text>
|
|
||||||
</View>
|
|
||||||
<View style={[pdfStyles.tableCellRight, { flex: 0.8 }]}>
|
|
||||||
<Text>{formatNumber(customerReport.summary.total_qty)}</Text>
|
|
||||||
</View>
|
|
||||||
<View style={[pdfStyles.tableCellRight, { flex: 1 }]}>
|
|
||||||
<Text>
|
|
||||||
{formatNumber(customerReport.summary.total_weight)}
|
|
||||||
</Text>
|
|
||||||
</View>
|
|
||||||
<View style={[pdfStyles.tableCellRight, { flex: 0.8 }]}>
|
|
||||||
<Text></Text>
|
|
||||||
</View>
|
|
||||||
<View style={[pdfStyles.tableCellRight, { flex: 1.2 }]}>
|
|
||||||
<Text></Text>
|
|
||||||
</View>
|
|
||||||
<View style={[pdfStyles.tableCellRight, { flex: 1.2 }]}>
|
|
||||||
<Text>
|
|
||||||
{formatCurrency(customerReport.summary.total_final_amount)}
|
|
||||||
</Text>
|
|
||||||
</View>
|
|
||||||
<View style={[pdfStyles.tableCellRight, { flex: 1.2 }]}>
|
|
||||||
<Text>
|
|
||||||
{formatCurrency(customerReport.summary.total_grand_amount)}
|
|
||||||
</Text>
|
|
||||||
</View>
|
|
||||||
<View style={[pdfStyles.tableCellRight, { flex: 1.2 }]}>
|
|
||||||
<Text>
|
|
||||||
{formatCurrency(customerReport.summary.total_payment)}
|
|
||||||
</Text>
|
|
||||||
</View>
|
|
||||||
<View style={[pdfStyles.tableCellRight, { flex: 1.2 }]}>
|
|
||||||
<Text style={pdfStyles.textError}>
|
|
||||||
{formatCurrency(
|
|
||||||
customerReport.summary.total_accounts_receivable
|
|
||||||
)}
|
|
||||||
</Text>
|
|
||||||
</View>
|
|
||||||
<View style={[pdfStyles.tableCell, { flex: 1.5 }]}>
|
|
||||||
<Text></Text>
|
|
||||||
</View>
|
|
||||||
<View style={[pdfStyles.tableCell, { flex: 1 }]}>
|
|
||||||
<Text></Text>
|
|
||||||
</View>
|
|
||||||
<View
|
|
||||||
style={[
|
|
||||||
pdfStyles.tableCell,
|
|
||||||
{ flex: 1.5, borderRightWidth: 0 },
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<Text></Text>
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
)}
|
|
||||||
</View>
|
|
||||||
</Page>
|
</Page>
|
||||||
))}
|
))}
|
||||||
</Document>
|
</Document>
|
||||||
|
|||||||
@@ -11,6 +11,11 @@ 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 {
|
||||||
|
PdfTable,
|
||||||
|
PdfColumn,
|
||||||
|
PdfTbodyCell,
|
||||||
|
} from '@/components/helper/pdf/table';
|
||||||
|
|
||||||
Font.register({
|
Font.register({
|
||||||
family: 'Helvetica',
|
family: 'Helvetica',
|
||||||
@@ -39,117 +44,6 @@ const pdfStyles = StyleSheet.create({
|
|||||||
marginBottom: 8,
|
marginBottom: 8,
|
||||||
color: '#1f74bf',
|
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: {
|
badge: {
|
||||||
backgroundColor: '#1f74bf',
|
backgroundColor: '#1f74bf',
|
||||||
color: '#FFFFFF',
|
color: '#FFFFFF',
|
||||||
@@ -174,6 +68,12 @@ const pdfStyles = StyleSheet.create({
|
|||||||
flexWrap: 'wrap',
|
flexWrap: 'wrap',
|
||||||
marginBottom: 8,
|
marginBottom: 8,
|
||||||
},
|
},
|
||||||
|
supplierSection: {
|
||||||
|
marginBottom: 10,
|
||||||
|
},
|
||||||
|
supplierSectionBreak: {
|
||||||
|
marginBottom: 15,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
interface PurchasesPerSupplierExportParams {
|
interface PurchasesPerSupplierExportParams {
|
||||||
@@ -218,6 +118,85 @@ const getParameterText = (
|
|||||||
return paramsText;
|
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: (
|
||||||
|
<View style={pdfStyles.badge}>
|
||||||
|
<Text>{item.expedition || '-'}</Text>
|
||||||
|
</View>
|
||||||
|
),
|
||||||
|
align: 'center',
|
||||||
|
},
|
||||||
|
{ key: 'delivery_number', value: item.delivery_number || '-' },
|
||||||
|
]);
|
||||||
|
};
|
||||||
|
|
||||||
const createPDFDocument = (
|
const createPDFDocument = (
|
||||||
supplierReports: LogisticPurchasePerSupplierReport[],
|
supplierReports: LogisticPurchasePerSupplierReport[],
|
||||||
params: PurchasesPerSupplierExportParams['params']
|
params: PurchasesPerSupplierExportParams['params']
|
||||||
@@ -266,114 +245,10 @@ const createPDFDocument = (
|
|||||||
{supplierReport.supplier.name}
|
{supplierReport.supplier.name}
|
||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
<View style={pdfStyles.table}>
|
<PdfTable
|
||||||
{/* Table Header */}
|
columns={getTableColumns()}
|
||||||
<View style={[pdfStyles.tableRow, pdfStyles.tableHeader]}>
|
data={getTableData(supplierReport.rows)}
|
||||||
<View style={[pdfStyles.tableCellHeader, { flex: 0.5 }]}>
|
/>
|
||||||
<Text>No</Text>
|
|
||||||
</View>
|
|
||||||
<View style={pdfStyles.tableCellHeader}>
|
|
||||||
<Text>Tanggal Terima</Text>
|
|
||||||
</View>
|
|
||||||
<View style={pdfStyles.tableCellHeader}>
|
|
||||||
<Text>Tanggal PO</Text>
|
|
||||||
</View>
|
|
||||||
<View style={pdfStyles.tableCellHeader}>
|
|
||||||
<Text>Referensi</Text>
|
|
||||||
</View>
|
|
||||||
<View style={pdfStyles.tableCellHeader}>
|
|
||||||
<Text>Produk</Text>
|
|
||||||
</View>
|
|
||||||
<View style={pdfStyles.tableCellHeader}>
|
|
||||||
<Text>Tujuan</Text>
|
|
||||||
</View>
|
|
||||||
<View style={[pdfStyles.tableCellHeaderRight, { flex: 0.8 }]}>
|
|
||||||
<Text>Qty</Text>
|
|
||||||
</View>
|
|
||||||
<View style={[pdfStyles.tableCellHeaderRight, { flex: 1.2 }]}>
|
|
||||||
<Text>Harga Beli</Text>
|
|
||||||
</View>
|
|
||||||
<View style={[pdfStyles.tableCellHeaderRight, { flex: 1.5 }]}>
|
|
||||||
<Text>Nilai Pembelian</Text>
|
|
||||||
</View>
|
|
||||||
<View style={[pdfStyles.tableCellHeaderRight, { flex: 1.2 }]}>
|
|
||||||
<Text>Biaya Transport</Text>
|
|
||||||
</View>
|
|
||||||
<View style={[pdfStyles.tableCellHeaderRight, { flex: 1.5 }]}>
|
|
||||||
<Text>Total</Text>
|
|
||||||
</View>
|
|
||||||
<View style={[pdfStyles.tableCellHeader, { flex: 1.2 }]}>
|
|
||||||
<Text>Armada</Text>
|
|
||||||
</View>
|
|
||||||
<View style={pdfStyles.tableCellHeaderLast}>
|
|
||||||
<Text>Surat Jalan</Text>
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
|
|
||||||
{/* Table Body */}
|
|
||||||
{supplierReport.rows.map(
|
|
||||||
(
|
|
||||||
item: LogisticPurchasePerSupplierReport['rows'][number],
|
|
||||||
index: number
|
|
||||||
) => (
|
|
||||||
<View
|
|
||||||
key={index}
|
|
||||||
style={[
|
|
||||||
pdfStyles.tableRow,
|
|
||||||
index < supplierReport.rows.length - 1
|
|
||||||
? pdfStyles.tableBorderBottom
|
|
||||||
: {},
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<View style={[pdfStyles.tableCellNo, { flex: 0.5 }]}>
|
|
||||||
<Text>{index + 1}</Text>
|
|
||||||
</View>
|
|
||||||
<View style={pdfStyles.tableCell}>
|
|
||||||
<Text>
|
|
||||||
{formatDate(item.receive_date, 'DD-MMM-YYYY')}
|
|
||||||
</Text>
|
|
||||||
</View>
|
|
||||||
<View style={pdfStyles.tableCell}>
|
|
||||||
<Text>{formatDate(item.po_date, 'DD-MMM-YYYY')}</Text>
|
|
||||||
</View>
|
|
||||||
<View style={pdfStyles.tableCell}>
|
|
||||||
<Text>{item.po_number || '-'}</Text>
|
|
||||||
</View>
|
|
||||||
<View style={pdfStyles.tableCell}>
|
|
||||||
<Text>{item.product?.name || '-'}</Text>
|
|
||||||
</View>
|
|
||||||
<View style={pdfStyles.tableCell}>
|
|
||||||
<Text>{item.warehouse?.name || '-'}</Text>
|
|
||||||
</View>
|
|
||||||
<View style={[pdfStyles.tableCellRight, { flex: 0.8 }]}>
|
|
||||||
<Text>{formatNumber(item.qty || 0)}</Text>
|
|
||||||
</View>
|
|
||||||
<View style={[pdfStyles.tableCellRight, { flex: 1.2 }]}>
|
|
||||||
<Text>{formatCurrency(item.unit_price || 0)}</Text>
|
|
||||||
</View>
|
|
||||||
<View style={[pdfStyles.tableCellRight, { flex: 1.5 }]}>
|
|
||||||
<Text>{formatCurrency(item.purchase_value || 0)}</Text>
|
|
||||||
</View>
|
|
||||||
<View style={[pdfStyles.tableCellRight, { flex: 1.2 }]}>
|
|
||||||
<Text>
|
|
||||||
{formatCurrency(item.transport_unit_price || 0)}
|
|
||||||
</Text>
|
|
||||||
</View>
|
|
||||||
<View style={[pdfStyles.tableCellRight, { flex: 1.5 }]}>
|
|
||||||
<Text>{formatCurrency(item.total_amount || 0)}</Text>
|
|
||||||
</View>
|
|
||||||
<View style={[pdfStyles.tableCell, { flex: 1.2 }]}>
|
|
||||||
<View style={pdfStyles.badge}>
|
|
||||||
<Text>{item.expedition || '-'}</Text>
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
<View style={pdfStyles.tableCellLast}>
|
|
||||||
<Text>{item.delivery_number || '-'}</Text>
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
)
|
|
||||||
)}
|
|
||||||
</View>
|
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ import Menu from '@/components/menu/Menu';
|
|||||||
import { generatePurchasesPerSupplierPDF } from '@/components/pages/report/logistic-stock/export/PurchasesPerSupplierExport';
|
import { generatePurchasesPerSupplierPDF } from '@/components/pages/report/logistic-stock/export/PurchasesPerSupplierExport';
|
||||||
import toast from 'react-hot-toast';
|
import toast from 'react-hot-toast';
|
||||||
import * as XLSX from 'xlsx';
|
import * as XLSX from 'xlsx';
|
||||||
|
import { Icon } from '@iconify/react';
|
||||||
|
|
||||||
const PurchasesPerSupplierTab = () => {
|
const PurchasesPerSupplierTab = () => {
|
||||||
// ===== STATE MANAGEMENT =====
|
// ===== STATE MANAGEMENT =====
|
||||||
@@ -723,27 +724,6 @@ const PurchasesPerSupplierTab = () => {
|
|||||||
subtitle='Laporan > Rekapitulasi Pembelian Per Supplier'
|
subtitle='Laporan > Rekapitulasi Pembelian Per Supplier'
|
||||||
className={{ wrapper: 'w-full', body: 'p-1!' }}
|
className={{ wrapper: 'w-full', body: 'p-1!' }}
|
||||||
>
|
>
|
||||||
<div className='mb-4 flex justify-end gap-2 [&_button]:px-4'>
|
|
||||||
<Button color='primary' onClick={handleSubmit}>
|
|
||||||
Cari
|
|
||||||
</Button>
|
|
||||||
<Button color='warning' onClick={resetFilters}>
|
|
||||||
Reset
|
|
||||||
</Button>
|
|
||||||
<Dropdown
|
|
||||||
trigger={
|
|
||||||
<Button color='success' isLoading={isAnyExportLoading}>
|
|
||||||
Export
|
|
||||||
</Button>
|
|
||||||
}
|
|
||||||
align='end'
|
|
||||||
>
|
|
||||||
<Menu className='w-32'>
|
|
||||||
<MenuItem title='Excel' onClick={handleExportExcel} />
|
|
||||||
<MenuItem title='PDF' onClick={handleExportPdf} />
|
|
||||||
</Menu>
|
|
||||||
</Dropdown>
|
|
||||||
</div>
|
|
||||||
<div className='grid md:grid-cols-3 grid-cols-1 gap-4'>
|
<div className='grid md:grid-cols-3 grid-cols-1 gap-4'>
|
||||||
<SelectInput
|
<SelectInput
|
||||||
label='Area'
|
label='Area'
|
||||||
@@ -848,6 +828,34 @@ const PurchasesPerSupplierTab = () => {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div className='mt-4 flex justify-end gap-2 [&_button]:px-4'>
|
||||||
|
<Button color='primary' onClick={handleSubmit}>
|
||||||
|
<Icon icon='heroicons:magnifying-glass' width={20} height={20} />
|
||||||
|
Cari
|
||||||
|
</Button>
|
||||||
|
<Button color='warning' onClick={resetFilters}>
|
||||||
|
<Icon icon='heroicons-outline:refresh' width={20} height={20} />
|
||||||
|
Reset
|
||||||
|
</Button>
|
||||||
|
<Dropdown
|
||||||
|
trigger={
|
||||||
|
<Button color='success' isLoading={isAnyExportLoading}>
|
||||||
|
Export
|
||||||
|
<Icon
|
||||||
|
icon='heroicons-outline:download'
|
||||||
|
width={20}
|
||||||
|
height={20}
|
||||||
|
/>
|
||||||
|
</Button>
|
||||||
|
}
|
||||||
|
align='end'
|
||||||
|
>
|
||||||
|
<Menu className='w-32'>
|
||||||
|
<MenuItem title='Excel' onClick={handleExportExcel} />
|
||||||
|
<MenuItem title='PDF' onClick={handleExportPdf} />
|
||||||
|
</Menu>
|
||||||
|
</Dropdown>
|
||||||
|
</div>
|
||||||
|
|
||||||
{!isSubmitted ? (
|
{!isSubmitted ? (
|
||||||
<div className='mt-6 text-center text-gray-500'>
|
<div className='mt-6 text-center text-gray-500'>
|
||||||
@@ -880,18 +888,25 @@ const PurchasesPerSupplierTab = () => {
|
|||||||
key={supplierReport.supplier.id}
|
key={supplierReport.supplier.id}
|
||||||
title={supplierReport.supplier.name}
|
title={supplierReport.supplier.name}
|
||||||
subtitle={`Total Pembelian: ${formatCurrency(totalPurchase)}`}
|
subtitle={`Total Pembelian: ${formatCurrency(totalPurchase)}`}
|
||||||
className={{ wrapper: 'w-full' }}
|
className={{
|
||||||
|
wrapper: 'w-full rounded-2xl',
|
||||||
|
body: 'p-0',
|
||||||
|
title:
|
||||||
|
'py-1.5 px-3 bg-primary text-white text-lg font-normal',
|
||||||
|
subtitle:
|
||||||
|
'px-3 pb-1 bg-primary text-white text-sm font-normal',
|
||||||
|
}}
|
||||||
variant='bordered'
|
variant='bordered'
|
||||||
collapsible={true}
|
collapsible={true}
|
||||||
>
|
>
|
||||||
<Table
|
<Table
|
||||||
data={supplierReport.rows}
|
data={supplierReport.rows}
|
||||||
columns={tableColumns}
|
columns={tableColumns}
|
||||||
pageSize={10}
|
pageSize={supplierReport.rows.length}
|
||||||
renderFooter={supplierReport.rows.length > 0}
|
renderFooter={supplierReport.rows.length > 0}
|
||||||
className={{
|
className={{
|
||||||
containerClassName: 'w-full',
|
containerClassName: 'w-full mb-0!',
|
||||||
tableWrapperClassName: 'overflow-x-auto mt-4',
|
tableWrapperClassName: 'overflow-x-auto',
|
||||||
tableClassName: 'w-full table-auto text-sm',
|
tableClassName: 'w-full table-auto text-sm',
|
||||||
headerRowClassName: 'border-b border-b-gray-200 bg-gray-50',
|
headerRowClassName: 'border-b border-b-gray-200 bg-gray-50',
|
||||||
headerColumnClassName:
|
headerColumnClassName:
|
||||||
|
|||||||
@@ -15,6 +15,12 @@ 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 {
|
||||||
|
PdfTable,
|
||||||
|
PdfColumn,
|
||||||
|
PdfTbodyCell,
|
||||||
|
PdfTfootCell,
|
||||||
|
} from '@/components/helper/pdf/table';
|
||||||
|
|
||||||
Font.register({
|
Font.register({
|
||||||
family: 'Helvetica',
|
family: 'Helvetica',
|
||||||
@@ -43,85 +49,6 @@ const pdfStyles = StyleSheet.create({
|
|||||||
marginBottom: 8,
|
marginBottom: 8,
|
||||||
color: '#1f74bf',
|
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: {
|
parameterBadge: {
|
||||||
backgroundColor: '#F5F5F5',
|
backgroundColor: '#F5F5F5',
|
||||||
color: '#333333',
|
color: '#333333',
|
||||||
@@ -136,6 +63,9 @@ const pdfStyles = StyleSheet.create({
|
|||||||
flexWrap: 'wrap',
|
flexWrap: 'wrap',
|
||||||
marginBottom: 8,
|
marginBottom: 8,
|
||||||
},
|
},
|
||||||
|
section: {
|
||||||
|
marginBottom: 15,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
interface HppPerKandangExportParams {
|
interface HppPerKandangExportParams {
|
||||||
@@ -192,6 +122,215 @@ const getParameterText = (params: HppPerKandangExportParams['params']) => {
|
|||||||
return paramsText;
|
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 = (
|
const createPDFDocument = (
|
||||||
data: HppPerKandangExportParams['data'],
|
data: HppPerKandangExportParams['data'],
|
||||||
params: HppPerKandangExportParams['params']
|
params: HppPerKandangExportParams['params']
|
||||||
@@ -216,404 +355,23 @@ const createPDFDocument = (
|
|||||||
</View>
|
</View>
|
||||||
|
|
||||||
{/* Rekapitulasi Section */}
|
{/* Rekapitulasi Section */}
|
||||||
<View style={pdfStyles.supplierSection}>
|
<View style={pdfStyles.section}>
|
||||||
<Text style={pdfStyles.supplierTitle}>Rekapitulasi</Text>
|
<Text style={pdfStyles.supplierTitle}>Rekapitulasi</Text>
|
||||||
|
<PdfTable
|
||||||
<View style={pdfStyles.table}>
|
columns={getRekapitulasiColumns()}
|
||||||
{/* Table Header */}
|
data={getRekapitulasiData(rekapitulasiByWeightRange)}
|
||||||
<View style={[pdfStyles.tableRow, pdfStyles.tableHeader]}>
|
/>
|
||||||
<View style={[pdfStyles.tableCellHeader, { flex: 1.2 }]}>
|
|
||||||
<Text>Rentang BW</Text>
|
|
||||||
</View>
|
|
||||||
<View style={[pdfStyles.tableCellHeaderRight, { flex: 1 }]}>
|
|
||||||
<Text>Sisa Butir</Text>
|
|
||||||
</View>
|
|
||||||
<View style={[pdfStyles.tableCellHeaderRight, { flex: 1 }]}>
|
|
||||||
<Text>Sisa Kg</Text>
|
|
||||||
</View>
|
|
||||||
<View style={[pdfStyles.tableCellHeaderRight, { flex: 1.2 }]}>
|
|
||||||
<Text>Rata-Rata Bobot (Kg)</Text>
|
|
||||||
</View>
|
|
||||||
<View style={[pdfStyles.tableCellHeader, { flex: 1.5 }]}>
|
|
||||||
<Text>Feed (Supplier)</Text>
|
|
||||||
</View>
|
|
||||||
<View style={[pdfStyles.tableCellHeader, { flex: 1.2 }]}>
|
|
||||||
<Text>DOC (Supplier)</Text>
|
|
||||||
</View>
|
|
||||||
<View style={[pdfStyles.tableCellHeaderRight, { flex: 1.2 }]}>
|
|
||||||
<Text>Rata-Rata Harga DOC</Text>
|
|
||||||
</View>
|
|
||||||
<View style={[pdfStyles.tableCellHeaderRight, { flex: 1.2 }]}>
|
|
||||||
<Text>HPP Telur (RP/KG)</Text>
|
|
||||||
</View>
|
|
||||||
<View
|
|
||||||
style={[
|
|
||||||
pdfStyles.tableCellHeaderRight,
|
|
||||||
{ flex: 1.2, borderRightWidth: 0 },
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<Text>Nominal Sisa</Text>
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
|
|
||||||
{/* Table Body - Rekapitulasi */}
|
|
||||||
{rekapitulasiByWeightRange.map(
|
|
||||||
(group: HppPerKandangPerWeightRange, index: number) => (
|
|
||||||
<View
|
|
||||||
key={index}
|
|
||||||
style={[
|
|
||||||
pdfStyles.tableRow,
|
|
||||||
index < rekapitulasiByWeightRange.length - 1
|
|
||||||
? pdfStyles.tableBorderBottom
|
|
||||||
: {},
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<View style={[pdfStyles.tableCellCenter, { flex: 1.2 }]}>
|
|
||||||
<Text>{group.label}</Text>
|
|
||||||
</View>
|
|
||||||
<View style={[pdfStyles.tableCellRight, { flex: 1 }]}>
|
|
||||||
<Text>{formatNumber(group.egg_production_pieces)}</Text>
|
|
||||||
</View>
|
|
||||||
<View style={[pdfStyles.tableCellRight, { flex: 1 }]}>
|
|
||||||
<Text>{formatNumber(group.egg_production_kg)}</Text>
|
|
||||||
</View>
|
|
||||||
<View style={[pdfStyles.tableCellRight, { flex: 1.2 }]}>
|
|
||||||
<Text>{formatNumber(group.avg_weight_kg)}</Text>
|
|
||||||
</View>
|
|
||||||
<View style={[pdfStyles.tableCell, { flex: 1.5 }]}>
|
|
||||||
<Text>
|
|
||||||
{group.feed_suppliers
|
|
||||||
?.map(
|
|
||||||
(s: { alias?: string; name: string }) =>
|
|
||||||
s.alias || s.name
|
|
||||||
)
|
|
||||||
.join(' | ') || '-'}
|
|
||||||
</Text>
|
|
||||||
</View>
|
|
||||||
<View style={[pdfStyles.tableCell, { flex: 1.2 }]}>
|
|
||||||
<Text>
|
|
||||||
{group.doc_suppliers
|
|
||||||
?.map(
|
|
||||||
(s: { alias?: string; name: string }) =>
|
|
||||||
s.alias || s.name
|
|
||||||
)
|
|
||||||
.join(' | ') || '-'}
|
|
||||||
</Text>
|
|
||||||
</View>
|
|
||||||
<View style={[pdfStyles.tableCellRight, { flex: 1.2 }]}>
|
|
||||||
<Text>{formatCurrency(group.average_doc_price_rp)}</Text>
|
|
||||||
</View>
|
|
||||||
<View style={[pdfStyles.tableCellRight, { flex: 1.2 }]}>
|
|
||||||
<Text>{formatCurrency(group.egg_hpp_rp_per_kg)}</Text>
|
|
||||||
</View>
|
|
||||||
<View
|
|
||||||
style={[
|
|
||||||
pdfStyles.tableCellRight,
|
|
||||||
{ flex: 1.2, borderRightWidth: 0 },
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<Text>{formatCurrency(group.egg_value_rp)}</Text>
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
)
|
|
||||||
)}
|
|
||||||
</View>
|
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
{/* Detail Per Kandang Section */}
|
{/* Detail Per Kandang Section */}
|
||||||
<View style={pdfStyles.supplierSectionBreak}>
|
<View style={pdfStyles.section}>
|
||||||
<Text style={pdfStyles.supplierTitle}>Detail Per Kandang</Text>
|
<Text style={pdfStyles.supplierTitle}>Detail Per Kandang</Text>
|
||||||
|
<PdfTable
|
||||||
<View style={pdfStyles.table}>
|
columns={getDetailColumns()}
|
||||||
{/* Table Header */}
|
data={getDetailData(data.rows)}
|
||||||
<View style={[pdfStyles.tableRow, pdfStyles.tableHeader]}>
|
footer={data.summary ? getDetailFooter(data.summary) : undefined}
|
||||||
<View style={[pdfStyles.tableCellHeader, { flex: 0.5 }]}>
|
footerLabel='TOTAL'
|
||||||
<Text>No</Text>
|
/>
|
||||||
</View>
|
|
||||||
<View style={[pdfStyles.tableCellHeader, { flex: 1.5 }]}>
|
|
||||||
<Text>Kandang</Text>
|
|
||||||
</View>
|
|
||||||
<View style={[pdfStyles.tableCellHeader, { flex: 1 }]}>
|
|
||||||
<Text>Rentang BW</Text>
|
|
||||||
</View>
|
|
||||||
<View style={[pdfStyles.tableCellHeaderRight, { flex: 1 }]}>
|
|
||||||
<Text>Rata-Rata Bobot (Kg)</Text>
|
|
||||||
</View>
|
|
||||||
<View style={[pdfStyles.tableCellHeaderRight, { flex: 0.8 }]}>
|
|
||||||
<Text>Sisa Butir</Text>
|
|
||||||
</View>
|
|
||||||
<View style={[pdfStyles.tableCellHeaderRight, { flex: 0.8 }]}>
|
|
||||||
<Text>Sisa Kg (Telur)</Text>
|
|
||||||
</View>
|
|
||||||
<View style={[pdfStyles.tableCellHeader, { flex: 1.2 }]}>
|
|
||||||
<Text>Feed (Supplier)</Text>
|
|
||||||
</View>
|
|
||||||
<View style={[pdfStyles.tableCellHeader, { flex: 1 }]}>
|
|
||||||
<Text>DOC (Supplier)</Text>
|
|
||||||
</View>
|
|
||||||
<View style={[pdfStyles.tableCellHeaderRight, { flex: 1.2 }]}>
|
|
||||||
<Text>Rata-Rata Harga DOC</Text>
|
|
||||||
</View>
|
|
||||||
<View style={[pdfStyles.tableCellHeaderRight, { flex: 1 }]}>
|
|
||||||
<Text>HPP Telur (RP/KG)</Text>
|
|
||||||
</View>
|
|
||||||
<View
|
|
||||||
style={[
|
|
||||||
pdfStyles.tableCellHeaderRight,
|
|
||||||
{ flex: 1.2, borderRightWidth: 0 },
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<Text>Nominal Sisa</Text>
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
|
|
||||||
{/* Table Body - Detail Per Kandang */}
|
|
||||||
{data.rows.map((item: HppPerKandangRow, index: number) => (
|
|
||||||
<View
|
|
||||||
key={index}
|
|
||||||
style={[pdfStyles.tableRow, pdfStyles.tableBorderBottom]}
|
|
||||||
>
|
|
||||||
<View style={[pdfStyles.tableCellCenter, { flex: 0.5 }]}>
|
|
||||||
<Text>{index + 1}</Text>
|
|
||||||
</View>
|
|
||||||
<View style={[pdfStyles.tableCell, { flex: 1.5 }]}>
|
|
||||||
<Text>{item.kandang?.name || '-'}</Text>
|
|
||||||
</View>
|
|
||||||
<View style={[pdfStyles.tableCell, { flex: 1 }]}>
|
|
||||||
<Text>
|
|
||||||
{item.weight_range.weight_min.toFixed(2)} -{' '}
|
|
||||||
{item.weight_range.weight_max.toFixed(2)}
|
|
||||||
</Text>
|
|
||||||
</View>
|
|
||||||
<View style={[pdfStyles.tableCellRight, { flex: 1 }]}>
|
|
||||||
<Text>{formatNumber(item.avg_weight_kg)}</Text>
|
|
||||||
</View>
|
|
||||||
<View style={[pdfStyles.tableCellRight, { flex: 0.8 }]}>
|
|
||||||
<Text>{formatNumber(item.egg_production_pieces)}</Text>
|
|
||||||
</View>
|
|
||||||
<View style={[pdfStyles.tableCellRight, { flex: 0.8 }]}>
|
|
||||||
<Text>{formatNumber(item.egg_production_kg)}</Text>
|
|
||||||
</View>
|
|
||||||
<View style={[pdfStyles.tableCell, { flex: 1.2 }]}>
|
|
||||||
<Text>
|
|
||||||
{item.feed_suppliers
|
|
||||||
?.map(
|
|
||||||
(s: { alias?: string; name: string }) =>
|
|
||||||
s.alias || s.name
|
|
||||||
)
|
|
||||||
.join(' | ')}
|
|
||||||
</Text>
|
|
||||||
</View>
|
|
||||||
<View style={[pdfStyles.tableCell, { flex: 1 }]}>
|
|
||||||
<Text>
|
|
||||||
{item.doc_suppliers
|
|
||||||
?.map(
|
|
||||||
(s: { alias?: string; name: string }) =>
|
|
||||||
s.alias || s.name
|
|
||||||
)
|
|
||||||
.join(' | ')}
|
|
||||||
</Text>
|
|
||||||
</View>
|
|
||||||
<View style={[pdfStyles.tableCellRight, { flex: 1.2 }]}>
|
|
||||||
<Text>{formatCurrency(item.average_doc_price_rp)}</Text>
|
|
||||||
</View>
|
|
||||||
<View style={[pdfStyles.tableCellRight, { flex: 1 }]}>
|
|
||||||
<Text>{formatCurrency(item.egg_hpp_rp_per_kg)}</Text>
|
|
||||||
</View>
|
|
||||||
<View
|
|
||||||
style={[
|
|
||||||
pdfStyles.tableCellRight,
|
|
||||||
{ flex: 1.2, borderRightWidth: 0 },
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<Text>{formatCurrency(item.egg_value_rp)}</Text>
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
))}
|
|
||||||
|
|
||||||
{/* TOTAL Row */}
|
|
||||||
{data.summary?.total && (
|
|
||||||
<View style={pdfStyles.tableRow}>
|
|
||||||
<View
|
|
||||||
style={[
|
|
||||||
pdfStyles.tableCellHeader,
|
|
||||||
{
|
|
||||||
flex: 0.5,
|
|
||||||
backgroundColor: '#F5F5F5',
|
|
||||||
borderBottomWidth: 0,
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<Text>TOTAL</Text>
|
|
||||||
</View>
|
|
||||||
<View
|
|
||||||
style={[
|
|
||||||
pdfStyles.tableCellHeader,
|
|
||||||
{
|
|
||||||
flex: 1.5,
|
|
||||||
backgroundColor: '#F5F5F5',
|
|
||||||
borderBottomWidth: 0,
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<Text>ALL</Text>
|
|
||||||
</View>
|
|
||||||
<View
|
|
||||||
style={[
|
|
||||||
pdfStyles.tableCellHeader,
|
|
||||||
{
|
|
||||||
flex: 1,
|
|
||||||
backgroundColor: '#F5F5F5',
|
|
||||||
borderBottomWidth: 0,
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<Text>-</Text>
|
|
||||||
</View>
|
|
||||||
<View
|
|
||||||
style={[
|
|
||||||
pdfStyles.tableCellHeaderRight,
|
|
||||||
{
|
|
||||||
flex: 1,
|
|
||||||
backgroundColor: '#F5F5F5',
|
|
||||||
borderBottomWidth: 0,
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<Text>
|
|
||||||
{formatNumber(data.summary.total.average_weight_kg)}
|
|
||||||
</Text>
|
|
||||||
</View>
|
|
||||||
<View
|
|
||||||
style={[
|
|
||||||
pdfStyles.tableCellHeaderRight,
|
|
||||||
{
|
|
||||||
flex: 0.8,
|
|
||||||
backgroundColor: '#F5F5F5',
|
|
||||||
borderBottomWidth: 0,
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<Text>
|
|
||||||
{formatNumber(
|
|
||||||
data.summary.total.total_egg_production_pieces
|
|
||||||
)}
|
|
||||||
</Text>
|
|
||||||
</View>
|
|
||||||
<View
|
|
||||||
style={[
|
|
||||||
pdfStyles.tableCellHeaderRight,
|
|
||||||
{
|
|
||||||
flex: 0.8,
|
|
||||||
backgroundColor: '#F5F5F5',
|
|
||||||
borderBottomWidth: 0,
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<Text>
|
|
||||||
{formatNumber(data.summary.total.total_egg_production_kg)}
|
|
||||||
</Text>
|
|
||||||
</View>
|
|
||||||
<View
|
|
||||||
style={[
|
|
||||||
pdfStyles.tableCellHeader,
|
|
||||||
{
|
|
||||||
flex: 1.2,
|
|
||||||
backgroundColor: '#F5F5F5',
|
|
||||||
borderBottomWidth: 0,
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<Text>
|
|
||||||
{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(' | ') || '-'}
|
|
||||||
</Text>
|
|
||||||
</View>
|
|
||||||
<View
|
|
||||||
style={[
|
|
||||||
pdfStyles.tableCellHeader,
|
|
||||||
{
|
|
||||||
flex: 1,
|
|
||||||
backgroundColor: '#F5F5F5',
|
|
||||||
borderBottomWidth: 0,
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<Text>
|
|
||||||
{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(' | ') || '-'}
|
|
||||||
</Text>
|
|
||||||
</View>
|
|
||||||
<View
|
|
||||||
style={[
|
|
||||||
pdfStyles.tableCellHeaderRight,
|
|
||||||
{
|
|
||||||
flex: 1.2,
|
|
||||||
backgroundColor: '#F5F5F5',
|
|
||||||
borderBottomWidth: 0,
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<Text>
|
|
||||||
{formatCurrency(
|
|
||||||
data.summary.total.total_average_doc_price_rp
|
|
||||||
)}
|
|
||||||
</Text>
|
|
||||||
</View>
|
|
||||||
<View
|
|
||||||
style={[
|
|
||||||
pdfStyles.tableCellHeaderRight,
|
|
||||||
{
|
|
||||||
flex: 1,
|
|
||||||
backgroundColor: '#F5F5F5',
|
|
||||||
borderBottomWidth: 0,
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<Text>
|
|
||||||
{formatCurrency(
|
|
||||||
data.summary.total.average_egg_hpp_rp_per_kg
|
|
||||||
)}
|
|
||||||
</Text>
|
|
||||||
</View>
|
|
||||||
<View
|
|
||||||
style={[
|
|
||||||
pdfStyles.tableCellHeaderRight,
|
|
||||||
{
|
|
||||||
flex: 1.2,
|
|
||||||
backgroundColor: '#F5F5F5',
|
|
||||||
borderBottomWidth: 0,
|
|
||||||
borderRightWidth: 0,
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<Text>
|
|
||||||
{formatCurrency(data.summary.total.total_egg_value_rp)}
|
|
||||||
</Text>
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
)}
|
|
||||||
</View>
|
|
||||||
</View>
|
</View>
|
||||||
</Page>
|
</Page>
|
||||||
</Document>
|
</Document>
|
||||||
|
|||||||
Reference in New Issue
Block a user