mirror of
https://gitlab.com/mbugroup/lti-web-client.git
synced 2026-05-20 13:32:00 +00:00
652 lines
20 KiB
TypeScript
652 lines
20 KiB
TypeScript
'use client';
|
|
|
|
import {
|
|
Document,
|
|
Image,
|
|
Link,
|
|
Page,
|
|
StyleSheet,
|
|
Text,
|
|
View,
|
|
} from '@react-pdf/renderer';
|
|
|
|
import { Expense } from '@/types/api/expense';
|
|
import { formatCurrency, formatDate, formatNumber } from '@/lib/helper';
|
|
|
|
interface ExpensePDFProps {
|
|
expense?: Expense;
|
|
}
|
|
|
|
const ExpensePDFStyle = StyleSheet.create({
|
|
page: {
|
|
paddingTop: 24,
|
|
paddingBottom: 64,
|
|
paddingHorizontal: 32,
|
|
},
|
|
|
|
companyInfoHeader: {
|
|
width: '100%',
|
|
display: 'flex',
|
|
flexDirection: 'row',
|
|
justifyContent: 'space-between',
|
|
alignItems: 'flex-start',
|
|
marginBottom: 8,
|
|
},
|
|
companyLogo: {
|
|
width: 64,
|
|
height: 'auto',
|
|
},
|
|
companyInfoHeaderDate: {
|
|
paddingTop: 8,
|
|
fontSize: 12,
|
|
},
|
|
companyName: {
|
|
fontSize: 12,
|
|
fontWeight: 'bold',
|
|
marginBottom: 4,
|
|
},
|
|
companyAddress: {
|
|
fontSize: 8,
|
|
maxWidth: 400,
|
|
marginBottom: 10,
|
|
},
|
|
|
|
title: {
|
|
marginTop: 16,
|
|
fontSize: 16,
|
|
lineHeight: '150%',
|
|
textAlign: 'center',
|
|
fontFamily: 'Times-Roman',
|
|
fontWeight: 'bold',
|
|
},
|
|
|
|
footer: {
|
|
width: '100%',
|
|
display: 'flex',
|
|
flexDirection: 'row',
|
|
justifyContent: 'space-between',
|
|
alignItems: 'center',
|
|
paddingHorizontal: 32,
|
|
|
|
position: 'absolute',
|
|
fontSize: 10,
|
|
bottom: 30,
|
|
left: 0,
|
|
right: 0,
|
|
textAlign: 'center',
|
|
color: 'grey',
|
|
},
|
|
|
|
// wrapper
|
|
generalInfoTable: {
|
|
width: '100%',
|
|
marginTop: 8,
|
|
borderWidth: 1,
|
|
borderColor: '#000000',
|
|
borderBottomWidth: 0,
|
|
fontSize: 12,
|
|
},
|
|
|
|
generalInfoTableRow: {
|
|
flexDirection: 'row',
|
|
borderBottomWidth: 1,
|
|
borderBottomColor: '#000000',
|
|
},
|
|
|
|
// columns
|
|
generalInfoTableColLabel: {
|
|
width: '30%',
|
|
paddingVertical: 6,
|
|
paddingHorizontal: 8,
|
|
},
|
|
generalInfoTableColSeparator: {
|
|
width: '3%',
|
|
justifyContent: 'center',
|
|
alignItems: 'center',
|
|
paddingVertical: 6,
|
|
},
|
|
generalInfoTableColValue: {
|
|
width: '67%',
|
|
paddingVertical: 6,
|
|
paddingHorizontal: 8,
|
|
},
|
|
|
|
generalInfoTableLabelText: {
|
|
fontWeight: 'bold',
|
|
},
|
|
generalInfoTableValueText: {},
|
|
|
|
// expense detail table
|
|
expenseDetailContainer: {
|
|
width: '100%',
|
|
marginTop: 12,
|
|
},
|
|
expenseDetailTitle: {
|
|
fontSize: 14,
|
|
lineHeight: '150%',
|
|
fontFamily: 'Times-Roman',
|
|
fontWeight: 'bold',
|
|
textAlign: 'center',
|
|
},
|
|
kandangExpenseContainer: {
|
|
width: '100%',
|
|
marginTop: 8,
|
|
},
|
|
kandangExpenseTitle: {
|
|
fontSize: 14,
|
|
lineHeight: '150%',
|
|
fontFamily: 'Times-Roman',
|
|
fontWeight: 'bold',
|
|
textAlign: 'center',
|
|
},
|
|
kandangExpenseTable: {
|
|
width: '100%',
|
|
marginTop: 8,
|
|
borderWidth: 1,
|
|
borderColor: '#000000',
|
|
borderBottomWidth: 0,
|
|
fontSize: 12,
|
|
},
|
|
kandangExpenseTableRow: {
|
|
flexDirection: 'row',
|
|
borderBottomWidth: 1,
|
|
borderBottomColor: '#000000',
|
|
},
|
|
kandangExpenseTableColLabel: {
|
|
width: '20%',
|
|
paddingVertical: 6,
|
|
paddingHorizontal: 8,
|
|
},
|
|
kandangExpenseTableColLabelBorderRight: {
|
|
borderRight: '1px solid #000000',
|
|
},
|
|
kandangExpenseTableColNonstock: {
|
|
width: '20%',
|
|
},
|
|
kandangExpenseTableColNote: {
|
|
width: '40%',
|
|
},
|
|
kandangExpenseHeaderLabelText: {
|
|
fontWeight: 'bold',
|
|
},
|
|
kandangExpenseLabelText: {
|
|
fontSize: 10,
|
|
},
|
|
kandangExpenseTableFooterColTotalExpenseCaption: {
|
|
width: '40%',
|
|
paddingVertical: 6,
|
|
paddingHorizontal: 8,
|
|
textAlign: 'right',
|
|
},
|
|
kandangExpenseTableFooterColTotalExpenseValue: {
|
|
width: '60%',
|
|
paddingVertical: 6,
|
|
paddingHorizontal: 8,
|
|
},
|
|
|
|
// utils
|
|
doubleDivider: {
|
|
width: '100%',
|
|
height: 6,
|
|
borderTop: '2px solid black',
|
|
borderBottom: '2px solid black',
|
|
},
|
|
});
|
|
|
|
const ExpensePDF = ({ expense }: ExpensePDFProps) => {
|
|
const isLatestApprovalRejected =
|
|
expense?.latest_approval?.action === 'REJECTED';
|
|
const isExpenseRealized =
|
|
expense?.latest_approval?.step_number &&
|
|
expense?.latest_approval.step_number >= 4;
|
|
|
|
const realizationStatus = isExpenseRealized
|
|
? 'Sudah Realisasi'
|
|
: 'Belum Realisasi';
|
|
|
|
const rows = [
|
|
{ label: 'Nomor PO', value: expense?.po_number },
|
|
{ label: 'Nomor Referensi', value: expense?.reference_number },
|
|
{
|
|
label: 'Kategori',
|
|
value:
|
|
expense?.category === 'BOP'
|
|
? 'Biaya Operasional'
|
|
: expense?.category === 'NON-BOP'
|
|
? 'Non Biaya Operasional'
|
|
: '',
|
|
},
|
|
{ label: 'Lokasi', value: expense?.location.name },
|
|
{
|
|
label: 'Kandang',
|
|
value: expense?.kandangs.map((item) => item.name).join(', '),
|
|
},
|
|
{ label: 'Vendor', value: expense?.supplier.name },
|
|
{
|
|
label: 'Tanggal Transaksi',
|
|
value: formatDate(expense?.transaction_date, 'DD MMMM YYYY'),
|
|
},
|
|
{
|
|
label: 'Tanggal Realisasi',
|
|
value: expense?.realization_date
|
|
? formatDate(expense?.realization_date, 'DD MMMM YYYY')
|
|
: '-',
|
|
},
|
|
{ label: 'Nama Pengaju', value: expense?.created_user.name },
|
|
{
|
|
label: 'Nominal Biaya',
|
|
value: formatCurrency(expense?.grand_total ?? 0),
|
|
},
|
|
{
|
|
label: 'Nominal Pengajuan',
|
|
value: formatCurrency(expense?.total_pengajuan ?? 0),
|
|
},
|
|
{
|
|
label: 'Nominal Realisasi',
|
|
value: expense?.total_realisasi
|
|
? formatCurrency(expense?.total_realisasi ?? 0)
|
|
: '-',
|
|
},
|
|
{ label: 'Status Pencairan', value: realizationStatus },
|
|
{
|
|
label: 'Status Biaya',
|
|
value: isLatestApprovalRejected
|
|
? 'Ditolak'
|
|
: expense?.latest_approval?.step_name,
|
|
},
|
|
];
|
|
|
|
return (
|
|
<Document>
|
|
<Page style={ExpensePDFStyle.page}>
|
|
<View>
|
|
<View style={ExpensePDFStyle.companyInfoHeader}>
|
|
<Image
|
|
style={ExpensePDFStyle.companyLogo}
|
|
src='/assets/img/lti-logo.png'
|
|
/>
|
|
|
|
<Text style={ExpensePDFStyle.companyInfoHeaderDate}>
|
|
{formatDate(Date.now(), 'DD MMMM YYYY')}
|
|
</Text>
|
|
</View>
|
|
|
|
<View>
|
|
<Text style={ExpensePDFStyle.companyName}>
|
|
PT LUMBUNG TELUR INDONESIA
|
|
</Text>
|
|
<Text style={ExpensePDFStyle.companyAddress}>
|
|
SOHO Building Lt.3 (Paris Van Java), Jalan Karang Tinggal, Kel.
|
|
Cipedes, Kec. Sukajadi, Kota Bandung 40162
|
|
</Text>
|
|
|
|
<View style={ExpensePDFStyle.doubleDivider} />
|
|
</View>
|
|
</View>
|
|
|
|
<Text style={ExpensePDFStyle.title}>
|
|
Laporan{' '}
|
|
{expense?.category === 'BOP'
|
|
? 'Biaya Operasional'
|
|
: 'Non-Biaya Operasional'}{' '}
|
|
{expense?.po_number}
|
|
</Text>
|
|
|
|
{/* General info table */}
|
|
<View style={ExpensePDFStyle.generalInfoTable}>
|
|
{rows.map((row) => (
|
|
<View style={ExpensePDFStyle.generalInfoTableRow} key={row.label}>
|
|
<View style={ExpensePDFStyle.generalInfoTableColLabel}>
|
|
<Text style={ExpensePDFStyle.generalInfoTableLabelText}>
|
|
{row.label}
|
|
</Text>
|
|
</View>
|
|
<View style={ExpensePDFStyle.generalInfoTableColSeparator}>
|
|
<Text>:</Text>
|
|
</View>
|
|
<View style={ExpensePDFStyle.generalInfoTableColValue}>
|
|
<Text style={ExpensePDFStyle.generalInfoTableValueText}>
|
|
{row.value}
|
|
</Text>
|
|
</View>
|
|
</View>
|
|
))}
|
|
</View>
|
|
|
|
{/* Detail expense request */}
|
|
<View
|
|
minPresenceAhead={80}
|
|
style={ExpensePDFStyle.expenseDetailContainer}
|
|
>
|
|
<Text style={ExpensePDFStyle.expenseDetailTitle}>
|
|
Rincian Pengajuan Biaya Operasional
|
|
</Text>
|
|
|
|
{expense?.kandangs.map((kandangExpense, kandangExpenseIdx) => {
|
|
let expenseRequestTotal = 0;
|
|
|
|
kandangExpense.pengajuans?.forEach(
|
|
(item) => (expenseRequestTotal += item.price)
|
|
);
|
|
|
|
return (
|
|
<View
|
|
key={kandangExpenseIdx}
|
|
style={ExpensePDFStyle.kandangExpenseContainer}
|
|
>
|
|
<Text style={ExpensePDFStyle.kandangExpenseTitle}>
|
|
{kandangExpense.name}
|
|
</Text>
|
|
|
|
<View style={ExpensePDFStyle.kandangExpenseTable}>
|
|
<View style={[ExpensePDFStyle.kandangExpenseTableRow]}>
|
|
<View
|
|
style={[
|
|
ExpensePDFStyle.kandangExpenseTableColLabel,
|
|
ExpensePDFStyle.kandangExpenseTableColLabelBorderRight,
|
|
ExpensePDFStyle.kandangExpenseTableColNonstock,
|
|
]}
|
|
>
|
|
<Text
|
|
style={ExpensePDFStyle.kandangExpenseHeaderLabelText}
|
|
>
|
|
Nonstock
|
|
</Text>
|
|
</View>
|
|
<View
|
|
style={[
|
|
ExpensePDFStyle.kandangExpenseTableColLabel,
|
|
ExpensePDFStyle.kandangExpenseTableColLabelBorderRight,
|
|
]}
|
|
>
|
|
<Text
|
|
style={ExpensePDFStyle.kandangExpenseHeaderLabelText}
|
|
>
|
|
Kuantitas
|
|
</Text>
|
|
</View>
|
|
<View
|
|
style={[
|
|
ExpensePDFStyle.kandangExpenseTableColLabel,
|
|
ExpensePDFStyle.kandangExpenseTableColLabelBorderRight,
|
|
]}
|
|
>
|
|
<Text
|
|
style={ExpensePDFStyle.kandangExpenseHeaderLabelText}
|
|
>
|
|
Harga Satuan
|
|
</Text>
|
|
</View>
|
|
<View
|
|
style={[
|
|
ExpensePDFStyle.kandangExpenseTableColLabel,
|
|
ExpensePDFStyle.kandangExpenseTableColNote,
|
|
]}
|
|
>
|
|
<Text
|
|
style={ExpensePDFStyle.kandangExpenseHeaderLabelText}
|
|
>
|
|
Catatan
|
|
</Text>
|
|
</View>
|
|
</View>
|
|
|
|
{kandangExpense.pengajuans?.map((pengajuan, pengajuanIdx) => (
|
|
<View
|
|
key={pengajuanIdx}
|
|
style={ExpensePDFStyle.kandangExpenseTableRow}
|
|
>
|
|
<View
|
|
style={[
|
|
ExpensePDFStyle.kandangExpenseTableColLabel,
|
|
ExpensePDFStyle.kandangExpenseTableColLabelBorderRight,
|
|
ExpensePDFStyle.kandangExpenseTableColNonstock,
|
|
]}
|
|
>
|
|
<Text style={ExpensePDFStyle.kandangExpenseLabelText}>
|
|
{pengajuan.nonstock.name}
|
|
</Text>
|
|
</View>
|
|
<View
|
|
style={[
|
|
ExpensePDFStyle.kandangExpenseTableColLabel,
|
|
ExpensePDFStyle.kandangExpenseTableColLabelBorderRight,
|
|
]}
|
|
>
|
|
<Text style={ExpensePDFStyle.kandangExpenseLabelText}>
|
|
{formatNumber(pengajuan.qty)}
|
|
</Text>
|
|
</View>
|
|
<View
|
|
style={[
|
|
ExpensePDFStyle.kandangExpenseTableColLabel,
|
|
ExpensePDFStyle.kandangExpenseTableColLabelBorderRight,
|
|
]}
|
|
>
|
|
<Text style={ExpensePDFStyle.kandangExpenseLabelText}>
|
|
{formatCurrency(pengajuan.price)}
|
|
</Text>
|
|
</View>
|
|
<View
|
|
style={[
|
|
ExpensePDFStyle.kandangExpenseTableColLabel,
|
|
ExpensePDFStyle.kandangExpenseTableColNote,
|
|
]}
|
|
>
|
|
<Text style={ExpensePDFStyle.kandangExpenseLabelText}>
|
|
{pengajuan.note}
|
|
</Text>
|
|
</View>
|
|
</View>
|
|
))}
|
|
|
|
<View style={[ExpensePDFStyle.kandangExpenseTableRow]}>
|
|
<View
|
|
style={[
|
|
ExpensePDFStyle.kandangExpenseTableFooterColTotalExpenseCaption,
|
|
ExpensePDFStyle.kandangExpenseTableColLabelBorderRight,
|
|
]}
|
|
>
|
|
<Text
|
|
style={ExpensePDFStyle.kandangExpenseHeaderLabelText}
|
|
>
|
|
Total Biaya Keseluruhan
|
|
</Text>
|
|
</View>
|
|
<View
|
|
style={[
|
|
ExpensePDFStyle.kandangExpenseTableFooterColTotalExpenseValue,
|
|
]}
|
|
>
|
|
<Text
|
|
style={ExpensePDFStyle.kandangExpenseHeaderLabelText}
|
|
>
|
|
{formatCurrency(expenseRequestTotal)}
|
|
</Text>
|
|
</View>
|
|
</View>
|
|
</View>
|
|
</View>
|
|
);
|
|
})}
|
|
</View>
|
|
|
|
{/* Detail expense realization */}
|
|
<View
|
|
minPresenceAhead={80}
|
|
style={ExpensePDFStyle.expenseDetailContainer}
|
|
>
|
|
<Text style={ExpensePDFStyle.expenseDetailTitle}>
|
|
Rincian Realisasi Biaya Operasional
|
|
</Text>
|
|
|
|
{expense?.kandangs.map((kandangExpense, kandangExpenseIdx) => {
|
|
let expenseRealizationTotal = 0;
|
|
|
|
kandangExpense.realisasi?.forEach(
|
|
(item) => (expenseRealizationTotal += item.price)
|
|
);
|
|
|
|
return (
|
|
<View
|
|
key={kandangExpenseIdx}
|
|
style={ExpensePDFStyle.kandangExpenseContainer}
|
|
>
|
|
<Text style={ExpensePDFStyle.kandangExpenseTitle}>
|
|
{kandangExpense.name}
|
|
</Text>
|
|
|
|
<View style={ExpensePDFStyle.kandangExpenseTable}>
|
|
<View style={[ExpensePDFStyle.kandangExpenseTableRow]}>
|
|
<View
|
|
style={[
|
|
ExpensePDFStyle.kandangExpenseTableColLabel,
|
|
ExpensePDFStyle.kandangExpenseTableColLabelBorderRight,
|
|
ExpensePDFStyle.kandangExpenseTableColNonstock,
|
|
]}
|
|
>
|
|
<Text
|
|
style={ExpensePDFStyle.kandangExpenseHeaderLabelText}
|
|
>
|
|
Nonstock
|
|
</Text>
|
|
</View>
|
|
<View
|
|
style={[
|
|
ExpensePDFStyle.kandangExpenseTableColLabel,
|
|
ExpensePDFStyle.kandangExpenseTableColLabelBorderRight,
|
|
]}
|
|
>
|
|
<Text
|
|
style={ExpensePDFStyle.kandangExpenseHeaderLabelText}
|
|
>
|
|
Kuantitas
|
|
</Text>
|
|
</View>
|
|
<View
|
|
style={[
|
|
ExpensePDFStyle.kandangExpenseTableColLabel,
|
|
ExpensePDFStyle.kandangExpenseTableColLabelBorderRight,
|
|
]}
|
|
>
|
|
<Text
|
|
style={ExpensePDFStyle.kandangExpenseHeaderLabelText}
|
|
>
|
|
Harga Satuan
|
|
</Text>
|
|
</View>
|
|
<View
|
|
style={[
|
|
ExpensePDFStyle.kandangExpenseTableColLabel,
|
|
ExpensePDFStyle.kandangExpenseTableColNote,
|
|
]}
|
|
>
|
|
<Text
|
|
style={ExpensePDFStyle.kandangExpenseHeaderLabelText}
|
|
>
|
|
Catatan
|
|
</Text>
|
|
</View>
|
|
</View>
|
|
|
|
{kandangExpense.realisasi?.map((realisasi, realisasiIdx) => (
|
|
<View
|
|
key={realisasiIdx}
|
|
style={ExpensePDFStyle.kandangExpenseTableRow}
|
|
>
|
|
<View
|
|
style={[
|
|
ExpensePDFStyle.kandangExpenseTableColLabel,
|
|
ExpensePDFStyle.kandangExpenseTableColLabelBorderRight,
|
|
ExpensePDFStyle.kandangExpenseTableColNonstock,
|
|
]}
|
|
>
|
|
<Text style={ExpensePDFStyle.kandangExpenseLabelText}>
|
|
{realisasi.nonstock.name}
|
|
</Text>
|
|
</View>
|
|
<View
|
|
style={[
|
|
ExpensePDFStyle.kandangExpenseTableColLabel,
|
|
ExpensePDFStyle.kandangExpenseTableColLabelBorderRight,
|
|
]}
|
|
>
|
|
<Text style={ExpensePDFStyle.kandangExpenseLabelText}>
|
|
{formatNumber(realisasi.qty)}
|
|
</Text>
|
|
</View>
|
|
<View
|
|
style={[
|
|
ExpensePDFStyle.kandangExpenseTableColLabel,
|
|
ExpensePDFStyle.kandangExpenseTableColLabelBorderRight,
|
|
]}
|
|
>
|
|
<Text style={ExpensePDFStyle.kandangExpenseLabelText}>
|
|
{formatCurrency(realisasi.price)}
|
|
</Text>
|
|
</View>
|
|
<View
|
|
style={[
|
|
ExpensePDFStyle.kandangExpenseTableColLabel,
|
|
ExpensePDFStyle.kandangExpenseTableColNote,
|
|
]}
|
|
>
|
|
<Text style={ExpensePDFStyle.kandangExpenseLabelText}>
|
|
{realisasi.note}
|
|
</Text>
|
|
</View>
|
|
</View>
|
|
))}
|
|
|
|
<View style={[ExpensePDFStyle.kandangExpenseTableRow]}>
|
|
<View
|
|
style={[
|
|
ExpensePDFStyle.kandangExpenseTableFooterColTotalExpenseCaption,
|
|
ExpensePDFStyle.kandangExpenseTableColLabelBorderRight,
|
|
]}
|
|
>
|
|
<Text
|
|
style={ExpensePDFStyle.kandangExpenseHeaderLabelText}
|
|
>
|
|
Total Biaya Keseluruhan
|
|
</Text>
|
|
</View>
|
|
<View
|
|
style={[
|
|
ExpensePDFStyle.kandangExpenseTableFooterColTotalExpenseValue,
|
|
]}
|
|
>
|
|
<Text
|
|
style={ExpensePDFStyle.kandangExpenseHeaderLabelText}
|
|
>
|
|
{formatCurrency(expenseRealizationTotal)}
|
|
</Text>
|
|
</View>
|
|
</View>
|
|
</View>
|
|
</View>
|
|
);
|
|
})}
|
|
</View>
|
|
|
|
<View style={ExpensePDFStyle.footer} fixed>
|
|
<Link
|
|
src={`${process.env.NEXT_PUBLIC_LTI_URL}expense/detail?expenseId=${expense?.id}`}
|
|
>
|
|
{expense?.po_number}
|
|
</Link>
|
|
|
|
<Text
|
|
render={({ pageNumber, totalPages }) =>
|
|
`${pageNumber} / ${totalPages}`
|
|
}
|
|
fixed
|
|
/>
|
|
</View>
|
|
</Page>
|
|
</Document>
|
|
);
|
|
};
|
|
|
|
export default ExpensePDF;
|