fix(FE): fixing laporan ledger debt supplier

This commit is contained in:
randy-ar
2026-01-14 10:40:54 +07:00
parent d34413fa3a
commit ba679865c4
5 changed files with 172 additions and 108 deletions
@@ -8,16 +8,16 @@ const FinanceTabs = () => {
const tabs = [
{
id: '1',
label: 'Kontrol Pembayaran Customer',
content: <CustomerPaymentTab />,
},
{
id: '2',
label: 'Rekapitulasi Hutang Ke Supplier',
content: <DebtSupplierTab />,
},
{
id: '2',
label: 'Kontrol Pembayaran Customer',
content: <CustomerPaymentTab />,
},
];
return (
@@ -176,7 +176,7 @@ const createPDFDocument = (params: DebtSupplierExportPDFParams) => {
<Text>No. PO</Text>
</View>
<View style={[pdfStyles.tableCellHeader, { flex: 1 }]}>
<Text>Tgl Terima</Text>
<Text>Tgl Terima/Bayar</Text>
</View>
<View style={[pdfStyles.tableCellHeader, { flex: 1 }]}>
<Text>Tgl PO</Text>
@@ -191,19 +191,19 @@ const createPDFDocument = (params: DebtSupplierExportPDFParams) => {
<Text>Gudang</Text>
</View>
<View style={[pdfStyles.tableCellHeader, { flex: 1 }]}>
<Text>Tgl Jatuh Tempo</Text>
<Text>Jatuh Tempo</Text>
</View>
<View style={[pdfStyles.tableCellHeader, { flex: 1 }]}>
<Text>Status Jatuh Tempo</Text>
</View>
<View style={[pdfStyles.tableCellHeaderRight, { flex: 1.5 }]}>
<Text>Total Harga</Text>
<Text>Nominal Pembelian (Rp)</Text>
</View>
<View style={[pdfStyles.tableCellHeaderRight, { flex: 1.5 }]}>
<Text>Pembayaran</Text>
<Text>Pembayaran (Rp)</Text>
</View>
<View style={[pdfStyles.tableCellHeaderRight, { flex: 1.5 }]}>
<Text>Hutang</Text>
<Text>Sisa Saldo Hutang (Rp)</Text>
</View>
<View style={[pdfStyles.tableCellHeader, { flex: 1 }]}>
<Text>Status</Text>
@@ -213,6 +213,65 @@ const createPDFDocument = (params: DebtSupplierExportPDFParams) => {
</View>
</View>
{/* Initial Balance Row */}
<View style={[pdfStyles.tableRow, pdfStyles.tableBorderBottom]}>
<View style={[pdfStyles.tableCellNo, { flex: 0.5 }]}>
<Text></Text>
</View>
<View style={[pdfStyles.tableCell, { flex: 1.5 }]}>
<Text></Text>
</View>
<View style={[pdfStyles.tableCell, { flex: 1.5 }]}>
<Text></Text>
</View>
<View style={[pdfStyles.tableCellCenter, { flex: 1 }]}>
<Text></Text>
</View>
<View style={[pdfStyles.tableCellCenter, { flex: 1 }]}>
<Text></Text>
</View>
<View style={[pdfStyles.tableCellCenter, { flex: 0.6 }]}>
<Text></Text>
</View>
<View style={[pdfStyles.tableCell, { flex: 1 }]}>
<Text></Text>
</View>
<View style={[pdfStyles.tableCell, { flex: 1 }]}>
<Text></Text>
</View>
<View style={[pdfStyles.tableCellCenter, { flex: 1 }]}>
<Text></Text>
</View>
<View style={[pdfStyles.tableCell, { flex: 1 }]}>
<Text></Text>
</View>
<View style={[pdfStyles.tableCellRight, { flex: 1.5 }]}>
<Text></Text>
</View>
<View style={[pdfStyles.tableCellRight, { flex: 1.5 }]}>
<Text></Text>
</View>
<View
style={[
pdfStyles.tableCellRight,
{
flex: 1.5,
color: supplierReport.initial_balance < 0 ? 'red' : 'black',
},
]}
>
<Text>
{formatCurrency(supplierReport.initial_balance || 0)}
</Text>
</View>
<View style={[pdfStyles.tableCell, { flex: 1 }]}>
<Text></Text>
</View>
<View style={[pdfStyles.tableCell, { flex: 1 }]}>
<Text></Text>
</View>
</View>
{/* Table Body */}
{supplierReport.rows.map((item, index) => (
<View
@@ -297,10 +356,10 @@ const createPDFDocument = (params: DebtSupplierExportPDFParams) => {
<View
style={[
pdfStyles.tableCellRight,
{ flex: 1.5, color: item.debt_price < 0 ? 'red' : 'black' },
{ flex: 1.5, color: item.balance < 0 ? 'red' : 'black' },
]}
>
<Text>{formatCurrency(item.debt_price)}</Text>
<Text>{formatCurrency(item.balance)}</Text>
</View>
<View style={[pdfStyles.tableCell, { flex: 1 }]}>
<Text>{item.status || '-'}</Text>
@@ -2,7 +2,7 @@
import * as XLSX from 'xlsx';
import { formatDate } from '@/lib/helper';
import { DebtSupplier } from '@/types/api/report/debt-supplier';
import { DebtRow, DebtSupplier } from '@/types/api/report/debt-supplier';
interface DebtSupplierExportExcelParams {
data: DebtSupplier[];
@@ -21,12 +21,29 @@ export const generateDebtSupplierExcel = (
const supplierData = supplierReport.rows;
const supplierName = supplierReport.supplier.name || 'Unknown Supplier';
const excelData: { [key: string]: string | number }[] = supplierData.map(
(item, index) => ({
const excelData: { [key: string]: string | number }[] = [
{
No: '',
'Nomor PR': '',
'Nomor PO': '',
'Tanggal Terima/Bayar': '',
'Tanggal PO': '',
'Aging (Hari)': '',
Area: '',
Gudang: '',
'Jatuh Tempo': '',
'Status Jatuh Tempo': '',
'Nominal Pembelian (Rp)': '',
'Pembayaran (Rp)': '',
'Sisa Saldo Hutang (Rp)': supplierReport.initial_balance || 0,
Status: '',
'Nomor Perjalanan': '',
},
...supplierData.map((item, index) => ({
No: index + 1,
'Nomor PR': item.pr_number || '',
'Nomor PO': item.po_number || '',
'Tanggal Terima': item.received_date
'Tanggal Terima/Bayar': item.received_date
? item.received_date != '-'
? formatDate(item.received_date, 'MM/DD/YYYY')
: '-'
@@ -39,35 +56,35 @@ export const generateDebtSupplierExcel = (
'Aging (Hari)': item.aging || 0,
Area: item.area?.name || '',
Gudang: item.warehouse?.name || '',
'Tanggal Jatuh Tempo': item.due_date
'Jatuh Tempo': item.due_date
? item.due_date != '-'
? formatDate(item.due_date, 'MM/DD/YYYY')
: '-'
: '-',
'Status Jatuh Tempo': item.due_status || '',
'Total Harga': item.total_price || 0,
'Harga Pembayaran': item.payment_price || 0,
'Harga Hutang': item.debt_price || 0,
'Nominal Pembelian (Rp)': item.total_price || 0,
'Pembayaran (Rp)': item.payment_price || 0,
'Sisa Saldo Hutang (Rp)': item.debt_price || 0,
Status: item.status || '',
'Nomor Perjalanan': item.travel_number || '',
})
);
})),
];
if (supplierReport.total) {
excelData.push({
No: 'Total',
'Nomor PR': '',
'Nomor PO': '',
'Tanggal Terima': '',
'Tanggal Terima/Bayar': '',
'Tanggal PO': '',
'Aging (Hari)': supplierReport.total.aging || 0,
Area: '',
Gudang: '',
'Tanggal Jatuh Tempo': '',
'Jatuh Tempo': '',
'Status Jatuh Tempo': '',
'Total Harga': supplierReport.total.total_price || 0,
'Harga Pembayaran': supplierReport.total.payment_price || 0,
'Harga Hutang': supplierReport.total.debt_price || 0,
'Nominal Pembelian (Rp)': supplierReport.total.total_price || 0,
'Pembayaran (Rp)': supplierReport.total.payment_price || 0,
'Sisa Saldo Hutang (Rp)': supplierReport.total.debt_price || 0,
Status: '',
'Nomor Perjalanan': '',
});
@@ -79,16 +96,16 @@ export const generateDebtSupplierExcel = (
{ wch: 5 }, // No
{ wch: 15 }, // Nomor PR
{ wch: 15 }, // Nomor PO
{ wch: 15 }, // Tanggal PR
{ wch: 15 }, // Tanggal Terima/Bayar
{ wch: 15 }, // Tanggal PO
{ wch: 12 }, // Aging
{ wch: 15 }, // Area
{ wch: 15 }, // Gudang
{ wch: 18 }, // Tanggal Jatuh Tempo
{ wch: 18 }, // Jatuh Tempo
{ wch: 18 }, // Status Jatuh Tempo
{ wch: 15 }, // Total Harga
{ wch: 15 }, // Harga Pembayaran
{ wch: 15 }, // Harga Hutang
{ wch: 15 }, // Nominal Pembelian (Rp)
{ wch: 15 }, // Pembayaran (Rp)
{ wch: 15 }, // Sisa Saldo Hutang (Rp)
{ wch: 12 }, // Status
{ wch: 15 }, // Nomor Perjalanan
];
@@ -21,7 +21,6 @@ import { ColumnDef } from '@tanstack/react-table';
import { useCallback, useMemo, useState } from 'react';
import toast from 'react-hot-toast';
import useSWR from 'swr';
import Pagination from '@/components/Pagination';
import { DebtSupplierApi } from '@/services/api/report/debt-supplier';
const DebtSupplierTab = () => {
@@ -30,10 +29,6 @@ const DebtSupplierTab = () => {
const [isExcelExportLoading, setIsExcelExportLoading] = useState(false);
const isAnyExportLoading = isPdfExportLoading || isExcelExportLoading;
// ===== PAGINATION STATE =====
const [currentPage, setCurrentPage] = useState(1);
const [pageSize, setPageSize] = useState(10);
// ===== SUBMISSION STATE =====
const [isSubmitted, setIsSubmitted] = useState(false);
@@ -71,18 +66,10 @@ const DebtSupplierTab = () => {
const handleApplyFilters = useCallback(() => {
const errors: Record<string, string> = {};
if (!filterStartDate) {
errors.start_date = 'Tanggal mulai wajib diisi';
}
if (!filterEndDate) {
errors.end_date = 'Tanggal akhir wajib diisi';
}
setFilterErrors(errors);
if (Object.keys(errors).length === 0) {
setIsSubmitted(true);
setCurrentPage(1);
filterModal.closeModal();
}
}, [filterModal, filterStartDate, filterEndDate]);
@@ -96,11 +83,9 @@ const DebtSupplierTab = () => {
filterSupplier.length > 0
? filterSupplier.map((v) => String(v.value)).join(',')
: undefined,
filter_by: filterDateType?.value,
filter_by: filterDateType?.value?.toString() || undefined,
start_date: filterStartDate || undefined,
end_date: filterEndDate || undefined,
page: currentPage,
limit: pageSize,
};
return ['debt-supplier-report', params];
@@ -109,11 +94,9 @@ const DebtSupplierTab = () => {
([, params]) =>
DebtSupplierApi.getDebtSupplierReport(
params.supplier_ids,
params.filter_by?.toString(),
params.filter_by?.toString() || undefined,
params.start_date,
params.end_date,
params.page,
params.limit
params.end_date
)
);
@@ -138,7 +121,7 @@ const DebtSupplierTab = () => {
filterSupplier.length > 0
? filterSupplier.map((v) => String(v.value)).join(',')
: undefined,
filter_by: filterDateType?.value?.toString(),
filter_by: filterDateType?.value?.toString() || undefined,
start_date: filterStartDate || undefined,
end_date: filterEndDate || undefined,
date_type: filterDateType ? filterDateType.value : undefined,
@@ -150,9 +133,7 @@ const DebtSupplierTab = () => {
params.supplier_ids,
params.filter_by,
params.start_date,
params.end_date,
params.page,
params.limit
params.end_date
);
return isResponseSuccess(response)
@@ -207,37 +188,18 @@ const DebtSupplierTab = () => {
}
}, [debtSupplierExport]);
// ===== PAGINATION HANDLERS =====
const handlePageChange = (page: number) => {
setCurrentPage(page);
};
const handleRowChange = (pageSize: number) => {
setPageSize(pageSize);
};
const handleNextPage = () => {
if (meta && currentPage < meta.total_pages) {
setCurrentPage(currentPage + 1);
}
};
const handlePrevPage = () => {
if (currentPage > 1) {
setCurrentPage(currentPage - 1);
}
};
const getTableColumns = (supplier: DebtSupplier): ColumnDef<DebtRow>[] => [
{
id: 'no',
header: 'No',
cell: (props) => props.row.index + 1,
enableSorting: false,
cell: (props) => props.row.index,
},
{
id: 'pr_number',
header: 'Nomor PR',
accessorKey: 'pr_number',
enableSorting: false,
cell: (props) => {
const value = props.row.original.pr_number;
return value || '-';
@@ -247,6 +209,7 @@ const DebtSupplierTab = () => {
id: 'po_number',
header: 'Nomor PO',
accessorKey: 'po_number',
enableSorting: false,
cell: (props) => {
const value = props.row.original.po_number;
return value || '-';
@@ -254,8 +217,9 @@ const DebtSupplierTab = () => {
},
{
id: 'received_date',
header: 'Tanggal Terima',
header: 'Tanggal Terima/Bayar',
accessorKey: 'received_date',
enableSorting: false,
cell: (props) => {
const value = props.row.original.received_date;
return value
@@ -269,6 +233,7 @@ const DebtSupplierTab = () => {
id: 'po_date',
header: 'Tanggal PO',
accessorKey: 'po_date',
enableSorting: false,
cell: (props) => {
const value = props.row.original.po_date;
return value
@@ -282,6 +247,7 @@ const DebtSupplierTab = () => {
id: 'aging',
header: 'Aging',
accessorKey: 'aging',
enableSorting: false,
cell: (props) => {
const value = props.row.original.aging;
return <div className='text-center'>{formatNumber(value)} Hari</div>;
@@ -295,6 +261,7 @@ const DebtSupplierTab = () => {
id: 'area',
header: 'Area',
accessorKey: 'area',
enableSorting: false,
cell: (props) => {
const value = props.row.original.area?.name;
return value || '-';
@@ -304,6 +271,7 @@ const DebtSupplierTab = () => {
id: 'warehouse',
header: 'Gudang',
accessorKey: 'warehouse',
enableSorting: false,
cell: (props) => {
const value = props.row.original.warehouse?.name;
return value || '-';
@@ -311,8 +279,9 @@ const DebtSupplierTab = () => {
},
{
id: 'due_date',
header: 'Tanggal Jatuh Tempo',
header: 'Jatuh Tempo',
accessorKey: 'due_date',
enableSorting: false,
cell: (props) => {
const value = props.row.original.due_date;
return value
@@ -326,6 +295,7 @@ const DebtSupplierTab = () => {
id: 'due_status',
header: 'Status Jatuh Tempo',
accessorKey: 'due_status',
enableSorting: false,
cell: (props) => {
const value = props.row.original.due_status;
return value || '-';
@@ -333,8 +303,9 @@ const DebtSupplierTab = () => {
},
{
id: 'total_price',
header: 'Total Harga',
header: 'Nominal Pembelian',
accessorKey: 'total_price',
enableSorting: false,
cell: (props) => {
const value = props.row.original.total_price;
return (
@@ -354,8 +325,9 @@ const DebtSupplierTab = () => {
},
{
id: 'payment_price',
header: 'Harga Pembayaran',
header: 'Pembayaran',
accessorKey: 'payment_price',
enableSorting: false,
cell: (props) => {
const value = props.row.original.payment_price;
return (
@@ -374,11 +346,12 @@ const DebtSupplierTab = () => {
},
},
{
id: 'debt_price',
header: 'Harga Hutang',
accessorKey: 'debt_price',
id: 'balance',
header: 'Sisa Saldo Hutang',
accessorKey: 'balance',
enableSorting: false,
cell: (props) => {
const value = props.row.original.debt_price;
const value = props.row.original.balance;
return (
<div className={`text-right ${value < 0 ? 'text-red-500' : ''}`}>
{formatCurrency(value)}
@@ -398,6 +371,7 @@ const DebtSupplierTab = () => {
id: 'status',
header: 'Status',
accessorKey: 'status',
enableSorting: false,
cell: (props) => {
const value = props.row.original.status;
return value || '-';
@@ -407,6 +381,7 @@ const DebtSupplierTab = () => {
id: 'travel_number',
header: 'Nomor Perjalanan',
accessorKey: 'travel_number',
enableSorting: false,
cell: (props) => {
const value = props.row.original.travel_number;
return value || '-';
@@ -471,9 +446,14 @@ const DebtSupplierTab = () => {
collapsible={true}
>
<Table
data={supplierReport.rows}
data={[
{
balance: supplierReport.initial_balance,
} as DebtRow,
...supplierReport.rows,
]}
columns={getTableColumns(supplierReport)}
pageSize={supplierReport.rows.length}
pageSize={supplierReport.rows.length + 1}
renderFooter={supplierReport.rows.length > 0}
className={{
containerClassName: 'w-full',
@@ -493,26 +473,38 @@ const DebtSupplierTab = () => {
'px-4 py-3 text-xs text-gray-900 whitespace-nowrap',
paginationClassName: 'hidden',
}}
renderCustomRow={(row) => {
if (row.index == 0) {
return (
<tr
className='hover:bg-gray-50 transition-colors border-b border-l border-r border-b-gray-200 border-l-gray-200 border-r-gray-200'
key={row.index}
>
<td
className='px-4 py-3 text-xs text-gray-900 whitespace-nowrap'
colSpan={12}
></td>
<td className='px-4 py-3 text-xs text-gray-900 whitespace-nowrap'>
<div
className={`text-right ${row.original.balance < 0 ? 'text-red-500' : ''}`}
>
{formatCurrency(row.original.balance)}
</div>
</td>
<td
className='px-4 py-3 text-xs text-gray-900 whitespace-nowrap'
colSpan={2}
></td>
</tr>
);
}
}}
/>
</Card>
);
})
)}
</div>
{meta && data.length > 0 && (
<div className='mt-6'>
<Pagination
currentPage={meta.page}
totalItems={meta.total_results}
onPageChange={handlePageChange}
onRowChange={handleRowChange}
onNextPage={handleNextPage}
onPrevPage={handlePrevPage}
rowOptions={[10, 25, 50, 100]}
itemsPerPage={meta.limit}
/>
</div>
)}
{/* Filter Modal */}
<Modal
+1 -5
View File
@@ -15,9 +15,7 @@ export class DebtSupplierApiService extends BaseApiService<
supplier_ids?: string,
filter_by?: string,
start_date?: string,
end_date?: string,
page?: number,
limit?: number
end_date?: string
): Promise<BaseApiResponse<DebtSupplier[]> | undefined> {
return await this.customRequest<BaseApiResponse<DebtSupplier[]>>(
`debt-supplier`,
@@ -28,8 +26,6 @@ export class DebtSupplierApiService extends BaseApiService<
filter_by: filter_by,
start_date: start_date,
end_date: end_date,
page: page,
limit: limit,
},
}
);