mirror of
https://gitlab.com/mbugroup/lti-web-client.git
synced 2026-05-20 13:32:00 +00:00
fix(FE): remove dummy data and integrate live API for closing finance and fixing closing UI when given data is null
This commit is contained in:
@@ -136,381 +136,370 @@ const ClosingFinanceTable = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='flex flex-col gap-4'>
|
<div className='flex flex-col gap-4'>
|
||||||
{isResponseSuccess(finance) && (
|
<>
|
||||||
<>
|
<Card
|
||||||
<Card
|
variant='bordered'
|
||||||
variant='bordered'
|
className={{
|
||||||
className={{
|
wrapper: 'w-full',
|
||||||
wrapper: 'w-full',
|
}}
|
||||||
}}
|
>
|
||||||
>
|
<div className='grid grid-cols-2 gap-6'>
|
||||||
<div className='grid grid-cols-2 gap-6'>
|
<div className='flex flex-col gap-1'>
|
||||||
<div className='flex flex-col gap-1'>
|
<div>
|
||||||
<div>
|
{isResponseSuccess(finance)
|
||||||
{formatTitleCase(
|
? formatTitleCase(
|
||||||
finance.data.profit_loss.data.summary.gross_profit.label ||
|
finance.data.profit_loss.data.summary.gross_profit
|
||||||
'-'
|
.label || '-'
|
||||||
)}
|
)
|
||||||
</div>
|
: 'Laba Rugi Brutto'}
|
||||||
<div className='text-lg font-bold'>
|
|
||||||
{formatCurrency(
|
|
||||||
finance.data.profit_loss.data.summary.gross_profit.amount
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div className='flex flex-col gap-1'>
|
<div className='text-lg font-bold'>
|
||||||
<div>
|
{isResponseSuccess(finance)
|
||||||
{formatTitleCase(
|
? formatCurrency(
|
||||||
finance.data.profit_loss.data.summary.net_profit.label ||
|
finance.data.profit_loss.data.summary.gross_profit.amount
|
||||||
'-'
|
)
|
||||||
)}
|
: '-'}
|
||||||
</div>
|
|
||||||
<div className='text-lg font-bold'>
|
|
||||||
{formatCurrency(
|
|
||||||
finance.data.profit_loss.data.summary.net_profit.amount
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
<div className='flex flex-col gap-1'>
|
||||||
<Card
|
<div>
|
||||||
title={finance.data.hpp_purchases.title}
|
{isResponseSuccess(finance)
|
||||||
variant='bordered'
|
? formatTitleCase(
|
||||||
collapsible
|
finance.data.profit_loss.data.summary.net_profit.label ||
|
||||||
className={{
|
'-'
|
||||||
wrapper: 'w-full',
|
)
|
||||||
}}
|
: 'Laba Rugi Netto'}
|
||||||
>
|
</div>
|
||||||
<div className='mt-6 p-0 mb-0'>
|
<div className='text-lg font-bold'>
|
||||||
<Table<HppTableRow>
|
{isResponseSuccess(finance)
|
||||||
data={hppTableData}
|
? formatCurrency(
|
||||||
columns={[
|
finance.data.profit_loss.data.summary.net_profit.amount
|
||||||
{
|
)
|
||||||
header: 'No.',
|
: '-'}
|
||||||
enableSorting: false,
|
</div>
|
||||||
accessorFn: (item, index) => {
|
</div>
|
||||||
if (item.isGroupHeader) return '-';
|
</div>
|
||||||
// Calculate row number excluding group headers
|
</Card>
|
||||||
const dataRowsBefore = hppTableData
|
<Card
|
||||||
.slice(0, index)
|
title={
|
||||||
.filter((row) => !row.isGroupHeader).length;
|
isResponseSuccess(finance)
|
||||||
return dataRowsBefore + 1;
|
? finance.data.hpp_purchases.title
|
||||||
|
: 'HPP Purchases'
|
||||||
|
}
|
||||||
|
variant='bordered'
|
||||||
|
collapsible
|
||||||
|
className={{
|
||||||
|
wrapper: 'w-full',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div className='mt-6 p-0 mb-0'>
|
||||||
|
<Table<HppTableRow>
|
||||||
|
data={hppTableData}
|
||||||
|
columns={[
|
||||||
|
{
|
||||||
|
header: 'No.',
|
||||||
|
enableSorting: false,
|
||||||
|
accessorFn: (item, index) => {
|
||||||
|
if (item.isGroupHeader) return '-';
|
||||||
|
const dataRowsBefore = hppTableData
|
||||||
|
.slice(0, index)
|
||||||
|
.filter((row) => !row.isGroupHeader).length;
|
||||||
|
return dataRowsBefore + 1;
|
||||||
|
},
|
||||||
|
footer: (props) => {
|
||||||
|
return 'HPP';
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
header: 'Type',
|
||||||
|
enableSorting: false,
|
||||||
|
accessorFn: (item) => formatTitleCase(item.type || '-'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
header: 'Budgeting',
|
||||||
|
enableSorting: false,
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
header: 'Rp/Ekor',
|
||||||
|
id: 'budgeting_rp_per_bird',
|
||||||
|
enableSorting: false,
|
||||||
|
accessorFn: (item) =>
|
||||||
|
formatCurrency(item.budgeting?.rp_per_bird || 0),
|
||||||
|
footer: (props) => {
|
||||||
|
return props.column.id === 'budgeting_rp_per_bird' &&
|
||||||
|
isResponseSuccess(finance)
|
||||||
|
? formatCurrency(
|
||||||
|
finance.data.hpp_purchases.summary_hpp.budgeting
|
||||||
|
.rp_per_bird || 0
|
||||||
|
)
|
||||||
|
: '-';
|
||||||
|
},
|
||||||
},
|
},
|
||||||
footer: (props) => {
|
{
|
||||||
return 'HPP';
|
header: 'Rp/Kg',
|
||||||
|
id: 'budgeting_rp_per_kg',
|
||||||
|
enableSorting: false,
|
||||||
|
accessorFn: (item) =>
|
||||||
|
formatCurrency(item.budgeting?.rp_per_kg || 0),
|
||||||
|
footer: (props) => {
|
||||||
|
return props.column.id === 'budgeting_rp_per_kg' &&
|
||||||
|
isResponseSuccess(finance)
|
||||||
|
? formatCurrency(
|
||||||
|
finance.data.hpp_purchases.summary_hpp.budgeting
|
||||||
|
.rp_per_kg || 0
|
||||||
|
)
|
||||||
|
: '-';
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
{
|
||||||
{
|
header: 'Jumlah (Rp)',
|
||||||
header: 'Type',
|
id: 'budgeting_amount',
|
||||||
enableSorting: false,
|
enableSorting: false,
|
||||||
accessorFn: (item) => formatTitleCase(item.type || '-'),
|
accessorFn: (item) =>
|
||||||
},
|
formatCurrency(item.budgeting?.amount || 0),
|
||||||
{
|
footer: (props) => {
|
||||||
header: 'Budgeting',
|
return props.column.id === 'budgeting_amount' &&
|
||||||
enableSorting: false,
|
isResponseSuccess(finance)
|
||||||
columns: [
|
? formatCurrency(
|
||||||
{
|
finance.data.hpp_purchases.summary_hpp.budgeting
|
||||||
header: 'Rp/Ekor',
|
.amount || 0
|
||||||
id: 'budgeting_rp_per_bird',
|
)
|
||||||
enableSorting: false,
|
: '-';
|
||||||
accessorFn: (item) =>
|
|
||||||
formatCurrency(item.budgeting?.rp_per_bird || 0),
|
|
||||||
footer: (props) => {
|
|
||||||
return props.column.id === 'budgeting_rp_per_bird'
|
|
||||||
? formatCurrency(
|
|
||||||
finance.data.hpp_purchases.hpp.reduce(
|
|
||||||
(total, hpp) =>
|
|
||||||
total +
|
|
||||||
(finance.data.hpp_purchases.summary_hpp
|
|
||||||
.budgeting.rp_per_bird || 0),
|
|
||||||
0
|
|
||||||
)
|
|
||||||
)
|
|
||||||
: '-';
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{
|
},
|
||||||
header: 'Rp/Kg',
|
],
|
||||||
id: 'budgeting_rp_per_kg',
|
},
|
||||||
enableSorting: false,
|
{
|
||||||
accessorFn: (item) =>
|
header: 'Realization',
|
||||||
formatCurrency(item.budgeting?.rp_per_kg || 0),
|
enableSorting: false,
|
||||||
footer: (props) => {
|
columns: [
|
||||||
return props.column.id === 'budgeting_rp_per_kg'
|
{
|
||||||
? formatCurrency(
|
header: 'Rp/Ekor',
|
||||||
finance.data.hpp_purchases.hpp.reduce(
|
id: 'realization_rp_per_bird',
|
||||||
(total, hpp) =>
|
enableSorting: false,
|
||||||
total +
|
accessorFn: (item) =>
|
||||||
(finance.data.hpp_purchases.summary_hpp
|
formatCurrency(item.realization?.rp_per_bird || 0),
|
||||||
.budgeting.rp_per_kg || 0),
|
footer: (props) => {
|
||||||
0
|
return props.column.id === 'realization_rp_per_bird' &&
|
||||||
)
|
isResponseSuccess(finance)
|
||||||
)
|
? formatCurrency(
|
||||||
: '-';
|
finance.data.hpp_purchases.summary_hpp.realization
|
||||||
},
|
.rp_per_bird || 0
|
||||||
|
)
|
||||||
|
: '-';
|
||||||
},
|
},
|
||||||
{
|
},
|
||||||
header: 'Jumlah (Rp)',
|
{
|
||||||
id: 'budgeting_amount',
|
header: 'Rp/Kg',
|
||||||
enableSorting: false,
|
id: 'realization_rp_per_kg',
|
||||||
accessorFn: (item) =>
|
enableSorting: false,
|
||||||
formatCurrency(item.budgeting?.amount || 0),
|
accessorFn: (item) =>
|
||||||
footer: (props) => {
|
formatCurrency(item.realization?.rp_per_kg || 0),
|
||||||
return props.column.id === 'budgeting_amount'
|
footer: (props) => {
|
||||||
? formatCurrency(
|
return props.column.id === 'realization_rp_per_kg' &&
|
||||||
finance.data.hpp_purchases.hpp.reduce(
|
isResponseSuccess(finance)
|
||||||
(total, hpp) =>
|
? formatCurrency(
|
||||||
total +
|
finance.data.hpp_purchases.summary_hpp.realization
|
||||||
(finance.data.hpp_purchases.summary_hpp
|
.rp_per_kg || 0
|
||||||
.budgeting.amount || 0),
|
)
|
||||||
0
|
: '-';
|
||||||
)
|
|
||||||
)
|
|
||||||
: '-';
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
],
|
},
|
||||||
},
|
{
|
||||||
{
|
header: 'Jumlah (Rp)',
|
||||||
header: 'Realization',
|
id: 'realization_amount',
|
||||||
enableSorting: false,
|
enableSorting: false,
|
||||||
columns: [
|
accessorFn: (item) =>
|
||||||
{
|
formatCurrency(item.realization?.amount || 0),
|
||||||
header: 'Rp/Ekor',
|
footer: (props) => {
|
||||||
id: 'realization_rp_per_bird',
|
return props.column.id === 'realization_amount' &&
|
||||||
enableSorting: false,
|
isResponseSuccess(finance)
|
||||||
accessorFn: (item) =>
|
? formatCurrency(
|
||||||
formatCurrency(item.realization?.rp_per_bird || 0),
|
finance.data.hpp_purchases.summary_hpp.realization
|
||||||
footer: (props) => {
|
.amount || 0
|
||||||
return props.column.id === 'realization_rp_per_bird'
|
)
|
||||||
? formatCurrency(
|
: '-';
|
||||||
finance.data.hpp_purchases.hpp.reduce(
|
|
||||||
(total, hpp) =>
|
|
||||||
total +
|
|
||||||
(finance.data.hpp_purchases.summary_hpp
|
|
||||||
.realization.rp_per_bird || 0),
|
|
||||||
0
|
|
||||||
)
|
|
||||||
)
|
|
||||||
: '-';
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{
|
},
|
||||||
header: 'Rp/Kg',
|
],
|
||||||
id: 'realization_rp_per_kg',
|
},
|
||||||
enableSorting: false,
|
]}
|
||||||
accessorFn: (item) =>
|
renderCustomRow={(row) => {
|
||||||
formatCurrency(item.realization?.rp_per_kg || 0),
|
const rowData = row.original;
|
||||||
footer: (props) => {
|
if (rowData.isGroupHeader) {
|
||||||
return props.column.id === 'realization_rp_per_kg'
|
return (
|
||||||
? formatCurrency(
|
<tr
|
||||||
finance.data.hpp_purchases.hpp.reduce(
|
key={row.id}
|
||||||
(total, hpp) =>
|
className={TABLE_DEFAULT_STYLING.bodyRowClassName}
|
||||||
total +
|
>
|
||||||
(finance.data.hpp_purchases.summary_hpp
|
<td
|
||||||
.realization.rp_per_kg || 0),
|
className={TABLE_DEFAULT_STYLING.bodyColumnClassName}
|
||||||
0
|
></td>
|
||||||
)
|
<td
|
||||||
)
|
colSpan={7}
|
||||||
: '-';
|
className={TABLE_DEFAULT_STYLING.bodyColumnClassName}
|
||||||
},
|
>
|
||||||
},
|
<div className='font-bold'>
|
||||||
{
|
{formatTitleCase(rowData.group_name ?? '-')}
|
||||||
header: 'Jumlah (Rp)',
|
</div>
|
||||||
id: 'realization_amount',
|
</td>
|
||||||
enableSorting: false,
|
</tr>
|
||||||
accessorFn: (item) =>
|
);
|
||||||
formatCurrency(item.realization?.amount || 0),
|
}
|
||||||
footer: (props) => {
|
return null;
|
||||||
return props.column.id === 'realization_amount'
|
}}
|
||||||
? formatCurrency(
|
renderFooter={isResponseSuccess(finance)}
|
||||||
finance.data.hpp_purchases.hpp.reduce(
|
/>
|
||||||
(total, hpp) =>
|
</div>
|
||||||
total +
|
</Card>
|
||||||
(finance.data.hpp_purchases.summary_hpp
|
<Card
|
||||||
.realization.amount || 0),
|
title={
|
||||||
0
|
isResponseSuccess(finance)
|
||||||
)
|
? finance.data.profit_loss.title
|
||||||
)
|
: 'Profit/Loss'
|
||||||
: '-';
|
}
|
||||||
},
|
variant='bordered'
|
||||||
},
|
collapsible
|
||||||
],
|
className={{
|
||||||
},
|
wrapper: 'w-full',
|
||||||
]}
|
}}
|
||||||
renderCustomRow={(row) => {
|
>
|
||||||
const rowData = row.original;
|
<div className='mt-6 p-0 mb-0'>
|
||||||
if (rowData.isGroupHeader) {
|
<Table<ProfitLossTableRow>
|
||||||
|
data={profitLossTableData}
|
||||||
|
columns={[
|
||||||
|
{
|
||||||
|
header: 'Type',
|
||||||
|
enableSorting: false,
|
||||||
|
accessorFn: (item) => item.type,
|
||||||
|
cell: (item) => (
|
||||||
|
<div className='ps-6'>
|
||||||
|
{formatTitleCase(item.row.original.type || '-')}
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
footer: (item) => (
|
||||||
|
<div className='font-bold'>
|
||||||
|
{isResponseSuccess(finance)
|
||||||
|
? formatTitleCase(
|
||||||
|
finance.data.profit_loss.data.summary.net_profit
|
||||||
|
.label || '-'
|
||||||
|
)
|
||||||
|
: '-'}
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
header: 'Rp/Ekor',
|
||||||
|
enableSorting: false,
|
||||||
|
accessorFn: (item) => formatCurrency(item.rp_per_bird || 0),
|
||||||
|
footer: (item) => (
|
||||||
|
<div className='font-bold'>
|
||||||
|
{isResponseSuccess(finance)
|
||||||
|
? formatCurrency(
|
||||||
|
finance.data.profit_loss.data.summary.net_profit
|
||||||
|
.rp_per_bird || 0
|
||||||
|
)
|
||||||
|
: formatCurrency(0)}
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
header: 'Rp/Kg',
|
||||||
|
enableSorting: false,
|
||||||
|
accessorFn: (item) => formatCurrency(item.rp_per_kg || 0),
|
||||||
|
footer: (item) => (
|
||||||
|
<div className='font-bold'>
|
||||||
|
{isResponseSuccess(finance)
|
||||||
|
? formatCurrency(
|
||||||
|
finance.data.profit_loss.data.summary.net_profit
|
||||||
|
.rp_per_kg || 0
|
||||||
|
)
|
||||||
|
: formatCurrency(0)}
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
header: 'Jumlah (Rp)',
|
||||||
|
enableSorting: false,
|
||||||
|
accessorFn: (item) => formatCurrency(item.amount || 0),
|
||||||
|
footer: (item) => (
|
||||||
|
<div className='font-bold'>
|
||||||
|
{isResponseSuccess(finance)
|
||||||
|
? formatCurrency(
|
||||||
|
finance.data.profit_loss.data.summary.net_profit
|
||||||
|
.amount || 0
|
||||||
|
)
|
||||||
|
: formatCurrency(0)}
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
renderCustomRow={(row) => {
|
||||||
|
const rowData = row.original;
|
||||||
|
if (rowData.isGroupHeader) {
|
||||||
|
if (rowData.amount) {
|
||||||
return (
|
return (
|
||||||
<tr
|
<tr
|
||||||
key={row.id}
|
key={row.id}
|
||||||
className={TABLE_DEFAULT_STYLING.bodyRowClassName}
|
className={TABLE_DEFAULT_STYLING.footerRowClassName}
|
||||||
>
|
>
|
||||||
<td
|
<td
|
||||||
className={TABLE_DEFAULT_STYLING.bodyColumnClassName}
|
className={TABLE_DEFAULT_STYLING.bodyColumnClassName}
|
||||||
></td>
|
>
|
||||||
|
<div className='font-bold'>
|
||||||
|
{formatTitleCase(rowData.label ?? '-')}
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
<td
|
<td
|
||||||
colSpan={7}
|
|
||||||
className={TABLE_DEFAULT_STYLING.bodyColumnClassName}
|
className={TABLE_DEFAULT_STYLING.bodyColumnClassName}
|
||||||
>
|
>
|
||||||
<div className='font-bold'>
|
<div className='font-bold'>
|
||||||
{formatTitleCase(rowData.group_name ?? '-')}
|
{formatCurrency(rowData.rp_per_bird ?? 0)}
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td
|
||||||
|
className={TABLE_DEFAULT_STYLING.bodyColumnClassName}
|
||||||
|
>
|
||||||
|
<div className='font-bold'>
|
||||||
|
{formatCurrency(rowData.rp_per_kg ?? 0)}
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td
|
||||||
|
className={TABLE_DEFAULT_STYLING.bodyColumnClassName}
|
||||||
|
>
|
||||||
|
<div className='font-bold'>
|
||||||
|
{formatCurrency(rowData.amount ?? 0)}
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return null;
|
return (
|
||||||
}}
|
<tr
|
||||||
renderFooter
|
key={row.id}
|
||||||
/>
|
className={TABLE_DEFAULT_STYLING.bodyRowClassName}
|
||||||
</div>
|
>
|
||||||
</Card>
|
<td
|
||||||
<Card
|
colSpan={4}
|
||||||
title={finance.data.profit_loss.title}
|
className={TABLE_DEFAULT_STYLING.bodyColumnClassName}
|
||||||
variant='bordered'
|
|
||||||
collapsible
|
|
||||||
className={{
|
|
||||||
wrapper: 'w-full',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div className='mt-6 p-0 mb-0'>
|
|
||||||
<Table<ProfitLossTableRow>
|
|
||||||
data={profitLossTableData}
|
|
||||||
columns={[
|
|
||||||
{
|
|
||||||
header: 'Type',
|
|
||||||
enableSorting: false,
|
|
||||||
accessorFn: (item) => item.type,
|
|
||||||
cell: (item) => (
|
|
||||||
<div className='ps-6'>
|
|
||||||
{formatTitleCase(item.row.original.type || '-')}
|
|
||||||
</div>
|
|
||||||
),
|
|
||||||
footer: (item) => (
|
|
||||||
<div className='font-bold'>
|
|
||||||
{formatTitleCase(
|
|
||||||
finance.data.profit_loss.data.summary.net_profit
|
|
||||||
.label || '-'
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
header: 'Rp/Ekor',
|
|
||||||
enableSorting: false,
|
|
||||||
accessorFn: (item) => formatCurrency(item.rp_per_bird || 0),
|
|
||||||
footer: (item) => (
|
|
||||||
<div className='font-bold'>
|
|
||||||
{formatCurrency(
|
|
||||||
finance.data.profit_loss.data.summary.net_profit
|
|
||||||
.rp_per_bird || 0
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
header: 'Rp/Kg',
|
|
||||||
enableSorting: false,
|
|
||||||
accessorFn: (item) => formatCurrency(item.rp_per_kg || 0),
|
|
||||||
footer: (item) => (
|
|
||||||
<div className='font-bold'>
|
|
||||||
{formatCurrency(
|
|
||||||
finance.data.profit_loss.data.summary.net_profit
|
|
||||||
.rp_per_kg || 0
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
header: 'Jumlah (Rp)',
|
|
||||||
enableSorting: false,
|
|
||||||
accessorFn: (item) => formatCurrency(item.amount || 0),
|
|
||||||
footer: (item) => (
|
|
||||||
<div className='font-bold'>
|
|
||||||
{formatCurrency(
|
|
||||||
finance.data.profit_loss.data.summary.net_profit
|
|
||||||
.amount || 0
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
renderCustomRow={(row) => {
|
|
||||||
const rowData = row.original;
|
|
||||||
if (rowData.isGroupHeader) {
|
|
||||||
if (rowData.amount) {
|
|
||||||
return (
|
|
||||||
<tr
|
|
||||||
key={row.id}
|
|
||||||
className={TABLE_DEFAULT_STYLING.footerRowClassName}
|
|
||||||
>
|
|
||||||
<td
|
|
||||||
className={
|
|
||||||
TABLE_DEFAULT_STYLING.bodyColumnClassName
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<div className='font-bold'>
|
|
||||||
{formatTitleCase(rowData.label ?? '-')}
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td
|
|
||||||
className={
|
|
||||||
TABLE_DEFAULT_STYLING.bodyColumnClassName
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<div className='font-bold'>
|
|
||||||
{formatCurrency(rowData.rp_per_bird ?? 0)}
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td
|
|
||||||
className={
|
|
||||||
TABLE_DEFAULT_STYLING.bodyColumnClassName
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<div className='font-bold'>
|
|
||||||
{formatCurrency(rowData.rp_per_kg ?? 0)}
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td
|
|
||||||
className={
|
|
||||||
TABLE_DEFAULT_STYLING.bodyColumnClassName
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<div className='font-bold'>
|
|
||||||
{formatCurrency(rowData.amount ?? 0)}
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<tr
|
|
||||||
key={row.id}
|
|
||||||
className={TABLE_DEFAULT_STYLING.bodyRowClassName}
|
|
||||||
>
|
>
|
||||||
<td
|
<div className='font-bold'>
|
||||||
colSpan={4}
|
{formatTitleCase(rowData.group_name ?? '-')}
|
||||||
className={TABLE_DEFAULT_STYLING.bodyColumnClassName}
|
</div>
|
||||||
>
|
</td>
|
||||||
<div className='font-bold'>
|
</tr>
|
||||||
{formatTitleCase(rowData.group_name ?? '-')}
|
);
|
||||||
</div>
|
}
|
||||||
</td>
|
return null;
|
||||||
</tr>
|
}}
|
||||||
);
|
className={{
|
||||||
}
|
paginationClassName: 'hidden',
|
||||||
return null;
|
}}
|
||||||
}}
|
renderFooter={isResponseSuccess(finance)}
|
||||||
className={{
|
/>
|
||||||
paginationClassName: 'hidden',
|
</div>
|
||||||
}}
|
</Card>
|
||||||
renderFooter
|
</>
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</Card>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -154,66 +154,74 @@ const ClosingSapronakCalculationTable = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='flex flex-col gap-4'>
|
<div className='flex flex-col gap-4'>
|
||||||
{isResponseSuccess(sapronakCalculation) && (
|
<Card
|
||||||
<>
|
title='DOC Broiler'
|
||||||
<Card
|
collapsible
|
||||||
title='DOC Broiler'
|
defaultCollapsed={false}
|
||||||
collapsible
|
className={{
|
||||||
defaultCollapsed={false}
|
wrapper: 'w-full',
|
||||||
className={{
|
body: 'p-4 shadow',
|
||||||
wrapper: 'w-full',
|
}}
|
||||||
body: 'p-4 shadow',
|
>
|
||||||
}}
|
<Table<RowSapronakCalculation>
|
||||||
>
|
data={
|
||||||
<Table<RowSapronakCalculation>
|
isResponseSuccess(sapronakCalculation)
|
||||||
data={sapronakCalculation.data?.doc_broiler.rows ?? []}
|
? (sapronakCalculation.data?.doc_broiler.rows ?? [])
|
||||||
columns={docBroilerColumns}
|
: []
|
||||||
className={{
|
}
|
||||||
containerClassName: 'my-4',
|
columns={docBroilerColumns}
|
||||||
}}
|
className={{
|
||||||
renderFooter
|
containerClassName: 'my-4',
|
||||||
/>
|
}}
|
||||||
</Card>
|
renderFooter={isResponseSuccess(sapronakCalculation)}
|
||||||
|
/>
|
||||||
|
</Card>
|
||||||
|
|
||||||
<Card
|
<Card
|
||||||
title='OVK'
|
title='OVK'
|
||||||
variant='bordered'
|
variant='bordered'
|
||||||
collapsible
|
collapsible
|
||||||
defaultCollapsed={true}
|
defaultCollapsed={true}
|
||||||
className={{
|
className={{
|
||||||
wrapper: 'w-full',
|
wrapper: 'w-full',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Table<RowSapronakCalculation>
|
<Table<RowSapronakCalculation>
|
||||||
data={sapronakCalculation.data?.ovk.rows ?? []}
|
data={
|
||||||
columns={ovkColumns}
|
isResponseSuccess(sapronakCalculation)
|
||||||
className={{
|
? (sapronakCalculation.data?.ovk.rows ?? [])
|
||||||
containerClassName: 'my-4',
|
: []
|
||||||
}}
|
}
|
||||||
renderFooter
|
columns={ovkColumns}
|
||||||
/>
|
className={{
|
||||||
</Card>
|
containerClassName: 'my-4',
|
||||||
|
}}
|
||||||
|
renderFooter={isResponseSuccess(sapronakCalculation)}
|
||||||
|
/>
|
||||||
|
</Card>
|
||||||
|
|
||||||
<Card
|
<Card
|
||||||
title='Pakan'
|
title='Pakan'
|
||||||
variant='bordered'
|
variant='bordered'
|
||||||
collapsible
|
collapsible
|
||||||
defaultCollapsed={true}
|
defaultCollapsed={true}
|
||||||
className={{
|
className={{
|
||||||
wrapper: 'w-full',
|
wrapper: 'w-full',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Table<RowSapronakCalculation>
|
<Table<RowSapronakCalculation>
|
||||||
data={sapronakCalculation.data?.pakan.rows ?? []}
|
data={
|
||||||
columns={pakanColumns}
|
isResponseSuccess(sapronakCalculation)
|
||||||
className={{
|
? (sapronakCalculation.data?.pakan.rows ?? [])
|
||||||
containerClassName: 'my-4',
|
: []
|
||||||
}}
|
}
|
||||||
renderFooter
|
columns={pakanColumns}
|
||||||
/>
|
className={{
|
||||||
</Card>
|
containerClassName: 'my-4',
|
||||||
</>
|
}}
|
||||||
)}
|
renderFooter={isResponseSuccess(sapronakCalculation)}
|
||||||
|
/>
|
||||||
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,185 +0,0 @@
|
|||||||
/**
|
|
||||||
* Dummy data for ClosingFinance
|
|
||||||
* Generated from: closing_keuangan.json
|
|
||||||
*
|
|
||||||
* This file is auto-generated. Do not edit manually.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { ClosingFinance } from '../../types/api/closing';
|
|
||||||
import { BaseApiResponse } from '@/types/api/api-general';
|
|
||||||
|
|
||||||
const DUMMY_DATA: ClosingFinance = {
|
|
||||||
project_flock_id: 1,
|
|
||||||
period: 1,
|
|
||||||
project_type: 'LAYING',
|
|
||||||
volume_base: {
|
|
||||||
total_birds: 254435,
|
|
||||||
total_weight_kg: 499961,
|
|
||||||
},
|
|
||||||
hpp_purchases: {
|
|
||||||
title: 'Pembelian HPP Budgeting dan HPP Realisasi',
|
|
||||||
hpp: [
|
|
||||||
{
|
|
||||||
group_name: 'hpp dan pengeluaran',
|
|
||||||
data: [
|
|
||||||
{
|
|
||||||
type: 'Pembelian PULLET LAYER',
|
|
||||||
budgeting: {
|
|
||||||
rp_per_bird: 7458.82,
|
|
||||||
rp_per_kg: 3795.866,
|
|
||||||
amount: 1897784868,
|
|
||||||
},
|
|
||||||
realization: {
|
|
||||||
rp_per_bird: 7292.414,
|
|
||||||
rp_per_kg: 3711.18,
|
|
||||||
amount: 1855445430,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'Pembelian OVK',
|
|
||||||
budgeting: {
|
|
||||||
rp_per_bird: 385.681,
|
|
||||||
rp_per_kg: 196.277,
|
|
||||||
amount: 98130789,
|
|
||||||
},
|
|
||||||
realization: {
|
|
||||||
rp_per_bird: 424.097,
|
|
||||||
rp_per_kg: 215.827,
|
|
||||||
amount: 107905006,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'Pembelian Pakan',
|
|
||||||
budgeting: {
|
|
||||||
rp_per_bird: 23002.545,
|
|
||||||
rp_per_kg: 11706.218,
|
|
||||||
amount: 5852652652,
|
|
||||||
},
|
|
||||||
realization: {
|
|
||||||
rp_per_bird: 25193.973,
|
|
||||||
rp_per_kg: 12821.457,
|
|
||||||
amount: 6410228456,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
group_name: 'hpp dan bahan baku',
|
|
||||||
data: [
|
|
||||||
{
|
|
||||||
type: 'Pengeluaran Overhead',
|
|
||||||
budgeting: {
|
|
||||||
rp_per_bird: 6165.894,
|
|
||||||
rp_per_kg: 3137.883,
|
|
||||||
amount: 1568819297,
|
|
||||||
},
|
|
||||||
realization: {
|
|
||||||
rp_per_bird: 5975.831,
|
|
||||||
rp_per_kg: 3041.158,
|
|
||||||
amount: 1520460611,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'Beban Ekspedisi',
|
|
||||||
budgeting: {
|
|
||||||
rp_per_bird: 304.218,
|
|
||||||
rp_per_kg: 154.819,
|
|
||||||
amount: 77403605,
|
|
||||||
},
|
|
||||||
realization: {
|
|
||||||
rp_per_bird: 237.466,
|
|
||||||
rp_per_kg: 120.849,
|
|
||||||
amount: 60419779,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
summary_hpp: {
|
|
||||||
label: 'HPP',
|
|
||||||
budgeting: {
|
|
||||||
rp_per_bird: 37317.158,
|
|
||||||
rp_per_kg: 18991.064,
|
|
||||||
amount: 9494791211,
|
|
||||||
},
|
|
||||||
realization: {
|
|
||||||
rp_per_bird: 39123.781,
|
|
||||||
rp_per_kg: 19910.472,
|
|
||||||
amount: 9954459282,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
profit_loss: {
|
|
||||||
title: 'Laba Rugi Perusahaan',
|
|
||||||
data: {
|
|
||||||
penjualan: [
|
|
||||||
{
|
|
||||||
type: 'Penjualan Telur dan Ayam Afkir',
|
|
||||||
rp_per_bird: 37551.535,
|
|
||||||
rp_per_kg: 19110.34,
|
|
||||||
amount: 9554424729,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
pembelian: [
|
|
||||||
{
|
|
||||||
type: 'Pembelian Sapronak Supplier',
|
|
||||||
rp_per_bird: 27629.158,
|
|
||||||
rp_per_kg: 14060.746,
|
|
||||||
amount: 7029824870,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'Pengeluaran Overhead',
|
|
||||||
rp_per_bird: 5975.831,
|
|
||||||
rp_per_kg: 3041.158,
|
|
||||||
amount: 1520460611,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'Beban Ekspedisi',
|
|
||||||
rp_per_bird: 237.466,
|
|
||||||
rp_per_kg: 120.849,
|
|
||||||
amount: 60419779,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
summary: {
|
|
||||||
gross_profit: {
|
|
||||||
label: 'LABA RUGI BRUTTO',
|
|
||||||
rp_per_bird: 9922.376,
|
|
||||||
rp_per_kg: 5049.594,
|
|
||||||
amount: 2524599859,
|
|
||||||
},
|
|
||||||
sub_total: {
|
|
||||||
label: 'SUB TOTAL',
|
|
||||||
rp_per_bird: 3709.079,
|
|
||||||
rp_per_kg: 1887.586,
|
|
||||||
amount: 943719469,
|
|
||||||
},
|
|
||||||
net_profit: {
|
|
||||||
label: 'LABA RUGI NETTO',
|
|
||||||
rp_per_bird: 3709.079,
|
|
||||||
rp_per_kg: 1887.586,
|
|
||||||
amount: 943719469,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get dummy ClosingFinance data
|
|
||||||
* @param id - Optional ID parameter
|
|
||||||
* @returns Promise with BaseApiResponse containing ClosingFinance
|
|
||||||
*/
|
|
||||||
export async function dummyGetOneClosingFinance(
|
|
||||||
id?: number
|
|
||||||
): Promise<BaseApiResponse<ClosingFinance>> {
|
|
||||||
return new Promise((resolve) => {
|
|
||||||
setTimeout(() => {
|
|
||||||
resolve({
|
|
||||||
code: 200,
|
|
||||||
status: 'success',
|
|
||||||
message: 'Data retrieved successfully',
|
|
||||||
data: DUMMY_DATA,
|
|
||||||
});
|
|
||||||
}, 500);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
@@ -1,388 +0,0 @@
|
|||||||
import { format } from 'date-fns';
|
|
||||||
import { Area } from '@/types/api/master-data/area';
|
|
||||||
import { Location } from '@/types/api/master-data/location';
|
|
||||||
import { Kandang } from '@/types/api/master-data/kandang';
|
|
||||||
import { Warehouse } from '@/types/api/master-data/warehouse';
|
|
||||||
import { ProductWarehouse } from '@/types/api/inventory/product-warehouse';
|
|
||||||
import {
|
|
||||||
BaseMarketing,
|
|
||||||
Marketing,
|
|
||||||
BaseSalesOrder,
|
|
||||||
BaseDeliveryOrder,
|
|
||||||
BaseDelivery,
|
|
||||||
} from '@/types/api/marketing/marketing';
|
|
||||||
import {
|
|
||||||
CreatedUser,
|
|
||||||
BaseApproval,
|
|
||||||
BaseMetadata,
|
|
||||||
} from '@/types/api/api-general';
|
|
||||||
import { Product } from '@/types/api/master-data/product';
|
|
||||||
import { Customer } from '@/types/api/master-data/customer';
|
|
||||||
import { Uom } from '@/types/api/master-data/uom';
|
|
||||||
import { ProductCategory } from '@/types/api/master-data/product-category';
|
|
||||||
import { Supplier } from '@/types/api/master-data/supplier';
|
|
||||||
|
|
||||||
// Waktu saat ini untuk created_at/updated_at
|
|
||||||
const now = format(new Date(), 'yyyy-MM-dd HH:mm:ss');
|
|
||||||
const today = format(new Date(), 'yyyy-MM-dd');
|
|
||||||
const tomorrow = format(
|
|
||||||
new Date().setDate(new Date().getDate() + 1),
|
|
||||||
'yyyy-MM-dd'
|
|
||||||
);
|
|
||||||
|
|
||||||
// ======================
|
|
||||||
// 👤 Created User & Helper Data
|
|
||||||
// ======================
|
|
||||||
export const createdUser: CreatedUser = {
|
|
||||||
id: 1,
|
|
||||||
id_user: 1,
|
|
||||||
email: 'admin@example.com',
|
|
||||||
name: 'Admin Utama',
|
|
||||||
};
|
|
||||||
|
|
||||||
const dummyProductBase: Product = {
|
|
||||||
id: 101,
|
|
||||||
name: 'Pakan Ayam Premium',
|
|
||||||
brand: 'Brand Hebat',
|
|
||||||
sku: 'PAK-001',
|
|
||||||
product_price: 15000,
|
|
||||||
selling_price: 18000,
|
|
||||||
tax: 0.1,
|
|
||||||
expiry_period: 365,
|
|
||||||
uom: { id: 1, name: 'Sak' } as Uom,
|
|
||||||
product_category: { id: 1, name: 'Pakan' } as ProductCategory,
|
|
||||||
suppliers: [{ id: 1, name: 'Supplier A' } as Supplier],
|
|
||||||
flags: ['PAKAN'],
|
|
||||||
created_user: createdUser,
|
|
||||||
created_at: now,
|
|
||||||
updated_at: now,
|
|
||||||
};
|
|
||||||
|
|
||||||
// ======================
|
|
||||||
// 📍 Area Dummy
|
|
||||||
// ======================
|
|
||||||
export const dummyAreas: Area[] = [
|
|
||||||
{
|
|
||||||
id: 1,
|
|
||||||
name: 'Bandung Barat',
|
|
||||||
created_user: createdUser,
|
|
||||||
created_at: now,
|
|
||||||
updated_at: now,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 2,
|
|
||||||
name: 'Cimahi Utara',
|
|
||||||
created_user: createdUser,
|
|
||||||
created_at: now,
|
|
||||||
updated_at: now,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
// ======================
|
|
||||||
// 🏢 Location Dummy
|
|
||||||
// ======================
|
|
||||||
export const dummyLocations: Location[] = [
|
|
||||||
{
|
|
||||||
id: 1,
|
|
||||||
name: 'Gudang A',
|
|
||||||
address: 'Jl. Sukajadi No. 12',
|
|
||||||
area: dummyAreas[0],
|
|
||||||
created_user: createdUser,
|
|
||||||
created_at: now,
|
|
||||||
updated_at: now,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 2,
|
|
||||||
name: 'Gudang B',
|
|
||||||
address: 'Jl. Setiabudi No. 45',
|
|
||||||
area: dummyAreas[1],
|
|
||||||
created_user: createdUser,
|
|
||||||
created_at: now,
|
|
||||||
updated_at: now,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
// ======================
|
|
||||||
// 🐔 Kandang Dummy
|
|
||||||
// ======================
|
|
||||||
export const dummyKandangs: Kandang[] = [
|
|
||||||
{
|
|
||||||
id: 1,
|
|
||||||
name: 'Kandang Ayam Layer 1',
|
|
||||||
status: 'AKTIF',
|
|
||||||
capacity: 500,
|
|
||||||
location: dummyLocations[0],
|
|
||||||
pic: createdUser,
|
|
||||||
created_user: createdUser,
|
|
||||||
created_at: now,
|
|
||||||
updated_at: now,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 2,
|
|
||||||
name: 'Kandang Ayam Broiler 2',
|
|
||||||
status: 'NONAKTIF',
|
|
||||||
capacity: 300,
|
|
||||||
location: dummyLocations[1],
|
|
||||||
pic: createdUser,
|
|
||||||
created_user: createdUser,
|
|
||||||
created_at: now,
|
|
||||||
updated_at: now,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
// ======================
|
|
||||||
// 🏭 Warehouse Dummy
|
|
||||||
// ======================
|
|
||||||
export const dummyWarehouses: Warehouse[] = [
|
|
||||||
{
|
|
||||||
id: 1,
|
|
||||||
type: 'AREA',
|
|
||||||
name: 'Gudang Wilayah Bandung Barat',
|
|
||||||
area: dummyAreas[0],
|
|
||||||
created_user: createdUser,
|
|
||||||
created_at: now,
|
|
||||||
updated_at: now,
|
|
||||||
} as Warehouse,
|
|
||||||
{
|
|
||||||
id: 2,
|
|
||||||
type: 'LOKASI',
|
|
||||||
name: 'Gudang Produksi Sukajadi',
|
|
||||||
area: dummyAreas[0],
|
|
||||||
location: { ...dummyLocations[0], area: dummyAreas[0] },
|
|
||||||
created_user: createdUser,
|
|
||||||
created_at: now,
|
|
||||||
updated_at: now,
|
|
||||||
} as Warehouse,
|
|
||||||
{
|
|
||||||
id: 3,
|
|
||||||
type: 'KANDANG',
|
|
||||||
name: 'Gudang Kandang Layer 1',
|
|
||||||
area: dummyAreas[0],
|
|
||||||
location: { ...dummyLocations[0], area: dummyAreas[0] },
|
|
||||||
kandang: {
|
|
||||||
...dummyKandangs[0],
|
|
||||||
location: dummyLocations[0],
|
|
||||||
pic: createdUser,
|
|
||||||
},
|
|
||||||
created_user: createdUser,
|
|
||||||
created_at: now,
|
|
||||||
updated_at: now,
|
|
||||||
} as Warehouse,
|
|
||||||
];
|
|
||||||
|
|
||||||
// ======================
|
|
||||||
// 📦 Product Warehouse Dummy
|
|
||||||
// ======================
|
|
||||||
export const dummyProductWarehouses: ProductWarehouse[] = [
|
|
||||||
{
|
|
||||||
id: 1,
|
|
||||||
product_id: 101,
|
|
||||||
warehouse_id: 1,
|
|
||||||
quantity: 1000,
|
|
||||||
product: dummyProductBase,
|
|
||||||
warehouse: dummyWarehouses[0],
|
|
||||||
created_user: createdUser,
|
|
||||||
created_at: now,
|
|
||||||
updated_at: now,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 2,
|
|
||||||
product_id: 102,
|
|
||||||
warehouse_id: 2,
|
|
||||||
quantity: 500,
|
|
||||||
product: {
|
|
||||||
...dummyProductBase,
|
|
||||||
id: 102,
|
|
||||||
name: 'Vitamin Ayam Super',
|
|
||||||
sku: 'VIT-002',
|
|
||||||
flags: ['VITAMIN'],
|
|
||||||
selling_price: 25000,
|
|
||||||
},
|
|
||||||
warehouse: dummyWarehouses[1],
|
|
||||||
created_user: createdUser,
|
|
||||||
created_at: now,
|
|
||||||
updated_at: now,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
// ======================
|
|
||||||
// 💼 Marketing Dummy
|
|
||||||
// ======================
|
|
||||||
|
|
||||||
// Helper untuk Sales Order (SO) Item
|
|
||||||
const soItem1: BaseSalesOrder = {
|
|
||||||
vehicle_number: 'B 1234 ABC',
|
|
||||||
id: 101,
|
|
||||||
marketing_id: 1,
|
|
||||||
product_warehouse_id: 1,
|
|
||||||
qty: 100,
|
|
||||||
unit_price: 18000, // Harga jual
|
|
||||||
avg_weight: 1.0,
|
|
||||||
total_weight: 100 * 1.0,
|
|
||||||
total_price: 100 * 18000,
|
|
||||||
product_warehouse: dummyProductWarehouses[0] as ProductWarehouse,
|
|
||||||
};
|
|
||||||
const soItem2: BaseSalesOrder = {
|
|
||||||
vehicle_number: 'D 5678 EFG',
|
|
||||||
id: 102,
|
|
||||||
marketing_id: 2,
|
|
||||||
product_warehouse_id: 2,
|
|
||||||
qty: 50,
|
|
||||||
unit_price: 25000,
|
|
||||||
avg_weight: 0.5,
|
|
||||||
total_weight: 50 * 0.5,
|
|
||||||
total_price: 50 * 25000,
|
|
||||||
product_warehouse: dummyProductWarehouses[1] as ProductWarehouse,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Helper untuk Delivery Item (DO) Detail
|
|
||||||
const doDelivery1: BaseDelivery[] = [
|
|
||||||
{
|
|
||||||
product_warehouse: dummyProductWarehouses[0] as ProductWarehouse,
|
|
||||||
qty: soItem1.qty,
|
|
||||||
unit_price: soItem1.unit_price,
|
|
||||||
total_weight: soItem1.total_weight,
|
|
||||||
avg_weight: soItem1.avg_weight,
|
|
||||||
total_price: soItem1.total_price,
|
|
||||||
vehicle_number: 'B 1234 ABC',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const doDelivery2: BaseDelivery[] = [
|
|
||||||
{
|
|
||||||
product_warehouse: dummyProductWarehouses[1] as ProductWarehouse,
|
|
||||||
qty: soItem2.qty,
|
|
||||||
unit_price: soItem2.unit_price,
|
|
||||||
total_weight: soItem2.total_weight,
|
|
||||||
avg_weight: soItem2.avg_weight,
|
|
||||||
total_price: soItem2.total_price,
|
|
||||||
vehicle_number: 'D 5678 EFG',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
// Helper untuk Delivery Order (DO) Header
|
|
||||||
const deliveryOrder1: BaseDeliveryOrder[] = [
|
|
||||||
{
|
|
||||||
id: 1,
|
|
||||||
marketing_id: 3,
|
|
||||||
do_number: 'DO-003-2025',
|
|
||||||
delivery_date: tomorrow,
|
|
||||||
warehouse: dummyWarehouses[0],
|
|
||||||
deliveries: doDelivery1,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
export const dummyMarketings: Marketing[] = [
|
|
||||||
// 1. Pengajuan Order (Langkah Pertama/Awal)
|
|
||||||
{
|
|
||||||
id: 1,
|
|
||||||
status: 'DRAFT',
|
|
||||||
// name: 'SO-001-2025', // `name` is not part of BaseMarketing
|
|
||||||
so_number: 'SO-001-2025',
|
|
||||||
so_date: today,
|
|
||||||
customer: {
|
|
||||||
id: 1,
|
|
||||||
name: 'PT Maju Jaya',
|
|
||||||
pic_id: 1,
|
|
||||||
pic: createdUser,
|
|
||||||
type: 'Distributor',
|
|
||||||
address: 'Jl. Merdeka No. 1',
|
|
||||||
phone: '081212121212',
|
|
||||||
email: 'contact@majujaya.com',
|
|
||||||
account_number: '1234567890',
|
|
||||||
created_user: createdUser,
|
|
||||||
created_at: now,
|
|
||||||
updated_at: now,
|
|
||||||
} as Customer,
|
|
||||||
sales_person: createdUser,
|
|
||||||
notes: 'Pengajuan Order Awal, menunggu persetujuan harga.',
|
|
||||||
latest_approval: {
|
|
||||||
step_number: 1,
|
|
||||||
step_name: 'Pengajuan Order',
|
|
||||||
action: 'CREATED',
|
|
||||||
action_by: createdUser,
|
|
||||||
action_at: now,
|
|
||||||
} as BaseApproval,
|
|
||||||
sales_order: [soItem1],
|
|
||||||
delivery_order: [],
|
|
||||||
created_user: createdUser,
|
|
||||||
created_at: now,
|
|
||||||
updated_at: now,
|
|
||||||
} as Marketing,
|
|
||||||
|
|
||||||
// 2. Sales Order (Disetujui dan Siap DO)
|
|
||||||
{
|
|
||||||
id: 2,
|
|
||||||
status: 'APPROVED',
|
|
||||||
// name: 'SO-002-2025', // `name` is not part of BaseMarketing
|
|
||||||
so_number: 'SO-002-2025',
|
|
||||||
so_date: today,
|
|
||||||
customer: {
|
|
||||||
id: 2,
|
|
||||||
name: 'CV Sumber Sehat',
|
|
||||||
pic_id: 2,
|
|
||||||
pic: createdUser,
|
|
||||||
type: 'Retail',
|
|
||||||
address: 'Jl. Cihampelas No. 5',
|
|
||||||
phone: '082222222222',
|
|
||||||
email: 'info@sumbersehat.com',
|
|
||||||
account_number: '9876543210',
|
|
||||||
created_user: createdUser,
|
|
||||||
created_at: now,
|
|
||||||
updated_at: now,
|
|
||||||
} as Customer,
|
|
||||||
sales_person: createdUser,
|
|
||||||
notes: 'Sales Order telah disetujui oleh Supervisor.',
|
|
||||||
latest_approval: {
|
|
||||||
id: 2,
|
|
||||||
step_number: 2,
|
|
||||||
step_name: 'Sales Order',
|
|
||||||
action: 'APPROVED',
|
|
||||||
action_by: createdUser,
|
|
||||||
action_at: now,
|
|
||||||
} as BaseApproval,
|
|
||||||
sales_order: [soItem2],
|
|
||||||
delivery_order: [], // Belum ada pengiriman (DO) yang dibuat
|
|
||||||
created_user: createdUser,
|
|
||||||
created_at: now,
|
|
||||||
updated_at: now,
|
|
||||||
} as Marketing,
|
|
||||||
|
|
||||||
// 3. Delivery Order (Proses Pengiriman telah dibuat)
|
|
||||||
{
|
|
||||||
id: 3,
|
|
||||||
status: 'DELIVERED', // Asumsi status DELIVERED berarti DO sudah selesai/terbuat
|
|
||||||
// name: 'SO-003-2025', // `name` is not part of BaseMarketing
|
|
||||||
so_number: 'SO-003-2025',
|
|
||||||
so_date: today,
|
|
||||||
customer: {
|
|
||||||
id: 3,
|
|
||||||
name: 'UD Ternak Sejahtera',
|
|
||||||
pic_id: 3,
|
|
||||||
pic: createdUser,
|
|
||||||
type: 'Reseller',
|
|
||||||
address: 'Jl. Pasteur No. 88',
|
|
||||||
phone: '083333333333',
|
|
||||||
email: 'halo@ternaksejahtera.com',
|
|
||||||
account_number: '1122334455',
|
|
||||||
created_user: createdUser,
|
|
||||||
created_at: now,
|
|
||||||
updated_at: now,
|
|
||||||
} as Customer,
|
|
||||||
sales_person: createdUser,
|
|
||||||
notes: 'Pengiriman barang telah berhasil dilakukan.',
|
|
||||||
latest_approval: {
|
|
||||||
id: 3,
|
|
||||||
step_number: 3,
|
|
||||||
step_name: 'Delivery Order',
|
|
||||||
action: 'COMPLETED',
|
|
||||||
action_by: createdUser,
|
|
||||||
action_at: now,
|
|
||||||
} as BaseApproval,
|
|
||||||
sales_order: [soItem1, soItem2],
|
|
||||||
delivery_order: deliveryOrder1, // DO sudah terbuat
|
|
||||||
created_user: createdUser,
|
|
||||||
created_at: now,
|
|
||||||
updated_at: now,
|
|
||||||
} as Marketing,
|
|
||||||
];
|
|
||||||
@@ -1,621 +0,0 @@
|
|||||||
/**
|
|
||||||
* Dummy Data untuk Report Expense API
|
|
||||||
*
|
|
||||||
* File ini berisi dummy data untuk testing Report Expense API sebelum backend siap.
|
|
||||||
*
|
|
||||||
* Struktur data mengikuti tipe yang didefinisikan di @/types/api/report/report-expense.d.ts
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
* // Menggunakan getAllFetcher dengan SWR:
|
|
||||||
* import useSWR from 'swr';
|
|
||||||
* import { ReportExpenseApi } from '@/services/api/report';
|
|
||||||
*
|
|
||||||
* const { data, error, isLoading } = useSWR(
|
|
||||||
* ReportExpenseApi.basePath,
|
|
||||||
* ReportExpenseApi.getAllFetcher
|
|
||||||
* );
|
|
||||||
*
|
|
||||||
* if (data?.status === 'success') {
|
|
||||||
* console.log(data.data); // Array of ReportExpense objects
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* @see {@link /home/sweetpotet/Documents/projects/lti-web-client/src/types/api/report/report-expense.d.ts}
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { format } from 'date-fns';
|
|
||||||
import {
|
|
||||||
Pengajuan,
|
|
||||||
Realisasi,
|
|
||||||
ReportExpense,
|
|
||||||
} from '@/types/api/report/report-expense';
|
|
||||||
import { BaseApiResponse, CreatedUser } from '@/types/api/api-general';
|
|
||||||
import { Supplier } from '@/types/api/master-data/supplier';
|
|
||||||
import { Location } from '@/types/api/master-data/location';
|
|
||||||
import { Nonstock } from '@/types/api/master-data/nonstock';
|
|
||||||
import { Kandang } from '@/types/api/master-data/kandang';
|
|
||||||
|
|
||||||
// Waktu saat ini untuk created_at/updated_at
|
|
||||||
const now = format(new Date(), 'yyyy-MM-dd HH:mm:ss');
|
|
||||||
const today = format(new Date(), 'yyyy-MM-dd');
|
|
||||||
const yesterday = format(
|
|
||||||
new Date(new Date().setDate(new Date().getDate() - 1)),
|
|
||||||
'yyyy-MM-dd'
|
|
||||||
);
|
|
||||||
const lastWeek = format(
|
|
||||||
new Date(new Date().setDate(new Date().getDate() - 7)),
|
|
||||||
'yyyy-MM-dd'
|
|
||||||
);
|
|
||||||
const lastMonth = format(
|
|
||||||
new Date(new Date().setMonth(new Date().getMonth() - 1)),
|
|
||||||
'yyyy-MM-dd'
|
|
||||||
);
|
|
||||||
|
|
||||||
// ======================
|
|
||||||
// 👤 Created User
|
|
||||||
// ======================
|
|
||||||
const createdUser: CreatedUser = {
|
|
||||||
id: 1,
|
|
||||||
id_user: 1,
|
|
||||||
email: 'admin@example.com',
|
|
||||||
name: 'Admin Utama',
|
|
||||||
};
|
|
||||||
|
|
||||||
// ======================
|
|
||||||
// 🏢 Supplier Dummy Data
|
|
||||||
// ======================
|
|
||||||
const dummySuppliers: Supplier[] = [
|
|
||||||
{
|
|
||||||
id: 1,
|
|
||||||
name: 'PT. Mitra Pakan Sejahtera',
|
|
||||||
alias: 'MPS',
|
|
||||||
pic: 'Budi Santoso',
|
|
||||||
type: 'Pakan',
|
|
||||||
category: 'Supplier Utama',
|
|
||||||
hatchery: '-',
|
|
||||||
phone: '022-1234567',
|
|
||||||
email: 'info@mitrapakan.com',
|
|
||||||
address: 'Jl. Raya Industri No. 123, Bandung',
|
|
||||||
npwp: '01.234.567.8-901.000',
|
|
||||||
account_number: '1234567890',
|
|
||||||
due_date: 30,
|
|
||||||
created_user: createdUser,
|
|
||||||
created_at: now,
|
|
||||||
updated_at: now,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 2,
|
|
||||||
name: 'CV. Sumber Ternak Jaya',
|
|
||||||
alias: 'STJ',
|
|
||||||
pic: 'Siti Rahayu',
|
|
||||||
type: 'DOC',
|
|
||||||
category: 'Supplier Utama',
|
|
||||||
hatchery: 'Hatchery Jaya',
|
|
||||||
phone: '021-9876543',
|
|
||||||
email: 'contact@sumberternak.com',
|
|
||||||
address: 'Jl. Peternakan No. 45, Jakarta',
|
|
||||||
npwp: '02.345.678.9-012.000',
|
|
||||||
account_number: '0987654321',
|
|
||||||
due_date: 45,
|
|
||||||
created_user: createdUser,
|
|
||||||
created_at: now,
|
|
||||||
updated_at: now,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 3,
|
|
||||||
name: 'PT. Agro Veteriner Indonesia',
|
|
||||||
alias: 'AVI',
|
|
||||||
pic: 'Dr. Ahmad Fauzi',
|
|
||||||
type: 'OVK',
|
|
||||||
category: 'Supplier Utama',
|
|
||||||
hatchery: '-',
|
|
||||||
phone: '031-5555666',
|
|
||||||
email: 'sales@agroveteriner.co.id',
|
|
||||||
address: 'Jl. Kesehatan Hewan No. 78, Surabaya',
|
|
||||||
npwp: '03.456.789.0-123.000',
|
|
||||||
account_number: '5678901234',
|
|
||||||
due_date: 60,
|
|
||||||
created_user: createdUser,
|
|
||||||
created_at: now,
|
|
||||||
updated_at: now,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
// ======================
|
|
||||||
// 📍 Location Dummy Data
|
|
||||||
// ======================
|
|
||||||
const dummyLocations: Location[] = [
|
|
||||||
{
|
|
||||||
id: 1,
|
|
||||||
name: 'Farm Sukajadi',
|
|
||||||
address: 'Jl. Sukajadi No. 100, Bandung',
|
|
||||||
area: {
|
|
||||||
id: 1,
|
|
||||||
name: 'Bandung Barat',
|
|
||||||
},
|
|
||||||
created_user: createdUser,
|
|
||||||
created_at: now,
|
|
||||||
updated_at: now,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 2,
|
|
||||||
name: 'Farm Cihampelas',
|
|
||||||
address: 'Jl. Cihampelas No. 200, Bandung',
|
|
||||||
area: {
|
|
||||||
id: 1,
|
|
||||||
name: 'Bandung Barat',
|
|
||||||
},
|
|
||||||
created_user: createdUser,
|
|
||||||
created_at: now,
|
|
||||||
updated_at: now,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 3,
|
|
||||||
name: 'Farm Pasteur',
|
|
||||||
address: 'Jl. Pasteur No. 300, Bandung',
|
|
||||||
area: {
|
|
||||||
id: 2,
|
|
||||||
name: 'Bandung Timur',
|
|
||||||
},
|
|
||||||
created_user: createdUser,
|
|
||||||
created_at: now,
|
|
||||||
updated_at: now,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
// ======================
|
|
||||||
// 📦 Nonstock Dummy Data
|
|
||||||
// ======================
|
|
||||||
const dummyNonstocks: Nonstock[] = [
|
|
||||||
{
|
|
||||||
id: 1,
|
|
||||||
name: 'Listrik',
|
|
||||||
uom_id: 1,
|
|
||||||
uom: { id: 1, name: 'kWh' },
|
|
||||||
suppliers: [],
|
|
||||||
flags: [],
|
|
||||||
created_user: createdUser,
|
|
||||||
created_at: now,
|
|
||||||
updated_at: now,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 2,
|
|
||||||
name: 'Air',
|
|
||||||
uom_id: 2,
|
|
||||||
uom: { id: 2, name: 'm³' },
|
|
||||||
suppliers: [],
|
|
||||||
flags: [],
|
|
||||||
created_user: createdUser,
|
|
||||||
created_at: now,
|
|
||||||
updated_at: now,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 3,
|
|
||||||
name: 'Bahan Bakar',
|
|
||||||
uom_id: 3,
|
|
||||||
uom: { id: 3, name: 'Liter' },
|
|
||||||
suppliers: [],
|
|
||||||
flags: [],
|
|
||||||
created_user: createdUser,
|
|
||||||
created_at: now,
|
|
||||||
updated_at: now,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 4,
|
|
||||||
name: 'Pemeliharaan Kandang',
|
|
||||||
uom_id: 4,
|
|
||||||
uom: { id: 4, name: 'Unit' },
|
|
||||||
suppliers: [],
|
|
||||||
flags: [],
|
|
||||||
created_user: createdUser,
|
|
||||||
created_at: now,
|
|
||||||
updated_at: now,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 5,
|
|
||||||
name: 'Transportasi',
|
|
||||||
uom_id: 5,
|
|
||||||
uom: { id: 5, name: 'Trip' },
|
|
||||||
suppliers: [],
|
|
||||||
flags: [],
|
|
||||||
created_user: createdUser,
|
|
||||||
created_at: now,
|
|
||||||
updated_at: now,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
// ======================
|
|
||||||
// 🏠 Kandang Dummy Data
|
|
||||||
// ======================
|
|
||||||
const dummyKandangs: Kandang[] = [
|
|
||||||
{
|
|
||||||
id: 1,
|
|
||||||
name: 'Kandang A1',
|
|
||||||
status: 'Aktif',
|
|
||||||
location: dummyLocations[0],
|
|
||||||
capacity: 5000,
|
|
||||||
pic: {
|
|
||||||
id_user: 1,
|
|
||||||
id: 1,
|
|
||||||
name: 'Budi Kandang',
|
|
||||||
email: 'budi@example.com',
|
|
||||||
},
|
|
||||||
created_user: createdUser,
|
|
||||||
created_at: now,
|
|
||||||
updated_at: now,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 2,
|
|
||||||
name: 'Kandang B1',
|
|
||||||
status: 'Aktif',
|
|
||||||
location: dummyLocations[1],
|
|
||||||
capacity: 4000,
|
|
||||||
pic: {
|
|
||||||
id_user: 2,
|
|
||||||
id: 2,
|
|
||||||
name: 'Siti Kandang',
|
|
||||||
email: 'siti@example.com',
|
|
||||||
},
|
|
||||||
created_user: createdUser,
|
|
||||||
created_at: now,
|
|
||||||
updated_at: now,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 3,
|
|
||||||
name: 'Kandang C1',
|
|
||||||
status: 'Aktif',
|
|
||||||
location: dummyLocations[2],
|
|
||||||
capacity: 6000,
|
|
||||||
pic: {
|
|
||||||
id_user: 3,
|
|
||||||
id: 3,
|
|
||||||
name: 'Ahmad Kandang',
|
|
||||||
email: 'ahmad@example.com',
|
|
||||||
},
|
|
||||||
created_user: createdUser,
|
|
||||||
created_at: now,
|
|
||||||
updated_at: now,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
// ======================
|
|
||||||
// 📋 Pengajuan Dummy Data
|
|
||||||
// ======================
|
|
||||||
const dummyPengajuans: Pengajuan[] = [
|
|
||||||
{
|
|
||||||
id: 1,
|
|
||||||
expense_id: 1,
|
|
||||||
project_flock_kandang_id: 1,
|
|
||||||
kandang_id: 1,
|
|
||||||
nonstock_id: 1,
|
|
||||||
qty: 1000,
|
|
||||||
price: 1500,
|
|
||||||
notes: 'Pengajuan biaya listrik bulan ini',
|
|
||||||
nonstock: dummyNonstocks[0],
|
|
||||||
created_at: now,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 2,
|
|
||||||
expense_id: 2,
|
|
||||||
project_flock_kandang_id: 2,
|
|
||||||
kandang_id: 2,
|
|
||||||
nonstock_id: 2,
|
|
||||||
qty: 500,
|
|
||||||
price: 5000,
|
|
||||||
notes: 'Pengajuan biaya air bulan ini',
|
|
||||||
nonstock: dummyNonstocks[1],
|
|
||||||
created_at: now,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 3,
|
|
||||||
expense_id: 3,
|
|
||||||
project_flock_kandang_id: 3,
|
|
||||||
kandang_id: 3,
|
|
||||||
nonstock_id: 3,
|
|
||||||
qty: 200,
|
|
||||||
price: 15000,
|
|
||||||
notes: 'Pengajuan biaya bahan bakar',
|
|
||||||
nonstock: dummyNonstocks[2],
|
|
||||||
created_at: now,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 4,
|
|
||||||
expense_id: 4,
|
|
||||||
project_flock_kandang_id: 1,
|
|
||||||
kandang_id: 1,
|
|
||||||
nonstock_id: 4,
|
|
||||||
qty: 1,
|
|
||||||
price: 5000000,
|
|
||||||
notes: 'Pengajuan biaya pemeliharaan kandang',
|
|
||||||
nonstock: dummyNonstocks[3],
|
|
||||||
created_at: now,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 5,
|
|
||||||
expense_id: 5,
|
|
||||||
project_flock_kandang_id: 2,
|
|
||||||
kandang_id: 2,
|
|
||||||
nonstock_id: 5,
|
|
||||||
qty: 10,
|
|
||||||
price: 500000,
|
|
||||||
notes: 'Pengajuan biaya transportasi',
|
|
||||||
nonstock: dummyNonstocks[4],
|
|
||||||
created_at: now,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
// ======================
|
|
||||||
// 💰 Realisasi Dummy Data
|
|
||||||
// ======================
|
|
||||||
const dummyRealisasis: Realisasi[] = [
|
|
||||||
{
|
|
||||||
id: 1,
|
|
||||||
expense_nonstock_id: 1,
|
|
||||||
qty: 950,
|
|
||||||
price: 1500,
|
|
||||||
notes: 'Realisasi biaya listrik aktual',
|
|
||||||
nonstock: dummyNonstocks[0],
|
|
||||||
created_at: now,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 2,
|
|
||||||
expense_nonstock_id: 2,
|
|
||||||
qty: 480,
|
|
||||||
price: 5000,
|
|
||||||
notes: 'Realisasi biaya air aktual',
|
|
||||||
nonstock: dummyNonstocks[1],
|
|
||||||
created_at: now,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 3,
|
|
||||||
expense_nonstock_id: 3,
|
|
||||||
qty: 195,
|
|
||||||
price: 15000,
|
|
||||||
notes: 'Realisasi biaya bahan bakar aktual',
|
|
||||||
nonstock: dummyNonstocks[2],
|
|
||||||
created_at: now,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 4,
|
|
||||||
expense_nonstock_id: 4,
|
|
||||||
qty: 1,
|
|
||||||
price: 4800000,
|
|
||||||
notes: 'Realisasi biaya pemeliharaan kandang',
|
|
||||||
nonstock: dummyNonstocks[3],
|
|
||||||
created_at: now,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 5,
|
|
||||||
expense_nonstock_id: 5,
|
|
||||||
qty: 9,
|
|
||||||
price: 500000,
|
|
||||||
notes: 'Realisasi biaya transportasi',
|
|
||||||
nonstock: dummyNonstocks[4],
|
|
||||||
created_at: now,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
// ======================
|
|
||||||
// 📊 Report Expense Dummy Data
|
|
||||||
// ======================
|
|
||||||
export const dummyReportExpenses: ReportExpense[] = [
|
|
||||||
{
|
|
||||||
id: 1,
|
|
||||||
reference_number: 'EXP-2025-001',
|
|
||||||
po_number: 'PO-2025-001',
|
|
||||||
category: 'Utilitas',
|
|
||||||
supplier: dummySuppliers[0],
|
|
||||||
realization_date: today,
|
|
||||||
transaction_date: yesterday,
|
|
||||||
pengajuan: dummyPengajuans[0],
|
|
||||||
realisasi: dummyRealisasis[0],
|
|
||||||
kandang: dummyKandangs[0],
|
|
||||||
created_at: now,
|
|
||||||
updated_at: now,
|
|
||||||
created_user: createdUser,
|
|
||||||
latest_approval: {
|
|
||||||
id: 1,
|
|
||||||
step_number: 1,
|
|
||||||
step_name: 'Manager Approval',
|
|
||||||
action: 'PENDING',
|
|
||||||
notes: '',
|
|
||||||
action_by: createdUser,
|
|
||||||
action_at: now,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 2,
|
|
||||||
reference_number: 'EXP-2025-002',
|
|
||||||
po_number: 'PO-2025-002',
|
|
||||||
category: 'Utilitas',
|
|
||||||
supplier: dummySuppliers[0],
|
|
||||||
realization_date: today,
|
|
||||||
transaction_date: yesterday,
|
|
||||||
pengajuan: dummyPengajuans[1],
|
|
||||||
realisasi: dummyRealisasis[1],
|
|
||||||
kandang: dummyKandangs[1],
|
|
||||||
created_at: now,
|
|
||||||
updated_at: now,
|
|
||||||
created_user: createdUser,
|
|
||||||
latest_approval: {
|
|
||||||
id: 2,
|
|
||||||
step_number: 2,
|
|
||||||
step_name: 'Finance Approval',
|
|
||||||
action: 'APPROVED',
|
|
||||||
notes: 'Disetujui oleh finance',
|
|
||||||
action_by: createdUser,
|
|
||||||
action_at: now,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 3,
|
|
||||||
reference_number: 'EXP-2025-003',
|
|
||||||
po_number: 'PO-2025-003',
|
|
||||||
category: 'Operasional',
|
|
||||||
supplier: dummySuppliers[1],
|
|
||||||
realization_date: lastWeek,
|
|
||||||
transaction_date: lastWeek,
|
|
||||||
pengajuan: dummyPengajuans[2],
|
|
||||||
realisasi: dummyRealisasis[2],
|
|
||||||
kandang: dummyKandangs[2],
|
|
||||||
created_at: lastWeek,
|
|
||||||
updated_at: lastWeek,
|
|
||||||
created_user: createdUser,
|
|
||||||
latest_approval: {
|
|
||||||
id: 3,
|
|
||||||
step_number: 3,
|
|
||||||
step_name: 'Director Approval',
|
|
||||||
action: 'APPROVED',
|
|
||||||
notes: 'Disetujui oleh direktur',
|
|
||||||
action_by: createdUser,
|
|
||||||
action_at: lastWeek,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 4,
|
|
||||||
reference_number: 'EXP-2025-004',
|
|
||||||
po_number: 'PO-2025-004',
|
|
||||||
category: 'Maintenance',
|
|
||||||
supplier: dummySuppliers[2],
|
|
||||||
realization_date: today,
|
|
||||||
transaction_date: yesterday,
|
|
||||||
pengajuan: dummyPengajuans[3],
|
|
||||||
realisasi: dummyRealisasis[3],
|
|
||||||
kandang: dummyKandangs[0],
|
|
||||||
created_at: now,
|
|
||||||
updated_at: now,
|
|
||||||
created_user: createdUser,
|
|
||||||
latest_approval: {
|
|
||||||
id: 4,
|
|
||||||
step_number: 1,
|
|
||||||
step_name: 'Manager Approval',
|
|
||||||
action: 'REJECTED',
|
|
||||||
notes: 'Biaya terlalu tinggi, perlu revisi',
|
|
||||||
action_by: createdUser,
|
|
||||||
action_at: now,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 5,
|
|
||||||
reference_number: 'EXP-2025-005',
|
|
||||||
po_number: 'PO-2025-005',
|
|
||||||
category: 'Operasional',
|
|
||||||
supplier: dummySuppliers[1],
|
|
||||||
realization_date: yesterday,
|
|
||||||
transaction_date: lastWeek,
|
|
||||||
pengajuan: dummyPengajuans[4],
|
|
||||||
realisasi: dummyRealisasis[4],
|
|
||||||
kandang: dummyKandangs[1],
|
|
||||||
created_at: lastWeek,
|
|
||||||
updated_at: yesterday,
|
|
||||||
created_user: createdUser,
|
|
||||||
latest_approval: {
|
|
||||||
id: 5,
|
|
||||||
step_number: 2,
|
|
||||||
step_name: 'Finance Approval',
|
|
||||||
action: 'PENDING',
|
|
||||||
notes: '',
|
|
||||||
action_by: createdUser,
|
|
||||||
action_at: yesterday,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 6,
|
|
||||||
reference_number: 'EXP-2025-006',
|
|
||||||
po_number: 'PO-2025-006',
|
|
||||||
category: 'Utilitas',
|
|
||||||
supplier: dummySuppliers[0],
|
|
||||||
realization_date: lastMonth,
|
|
||||||
transaction_date: lastMonth,
|
|
||||||
pengajuan: {
|
|
||||||
id: 6,
|
|
||||||
expense_id: 6,
|
|
||||||
project_flock_kandang_id: 3,
|
|
||||||
kandang_id: 3,
|
|
||||||
nonstock_id: 1,
|
|
||||||
qty: 1200,
|
|
||||||
price: 1500,
|
|
||||||
notes: 'Pengajuan biaya listrik bulan lalu',
|
|
||||||
nonstock: dummyNonstocks[0],
|
|
||||||
created_at: lastMonth,
|
|
||||||
},
|
|
||||||
realisasi: {
|
|
||||||
id: 6,
|
|
||||||
expense_nonstock_id: 6,
|
|
||||||
qty: 1150,
|
|
||||||
price: 1500,
|
|
||||||
notes: 'Realisasi biaya listrik bulan lalu',
|
|
||||||
nonstock: dummyNonstocks[0],
|
|
||||||
created_at: lastMonth,
|
|
||||||
},
|
|
||||||
kandang: dummyKandangs[2],
|
|
||||||
created_at: lastMonth,
|
|
||||||
updated_at: lastMonth,
|
|
||||||
created_user: createdUser,
|
|
||||||
latest_approval: {
|
|
||||||
id: 6,
|
|
||||||
step_number: 3,
|
|
||||||
step_name: 'Director Approval',
|
|
||||||
action: 'APPROVED',
|
|
||||||
notes: 'Selesai diproses',
|
|
||||||
action_by: createdUser,
|
|
||||||
action_at: lastMonth,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
// ======================
|
|
||||||
// 🔧 Fetcher Functions
|
|
||||||
// ======================
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Dummy fetcher untuk mendapatkan semua data report expense
|
|
||||||
* @returns Promise dengan BaseApiResponse berisi array ReportExpense
|
|
||||||
*/
|
|
||||||
export async function dummyGetAllFetcher(): Promise<
|
|
||||||
BaseApiResponse<ReportExpense[]>
|
|
||||||
> {
|
|
||||||
// Simulasi delay network
|
|
||||||
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
||||||
|
|
||||||
return {
|
|
||||||
code: 200,
|
|
||||||
status: 'success',
|
|
||||||
message: 'Data report expense berhasil diambil',
|
|
||||||
data: dummyReportExpenses,
|
|
||||||
meta: {
|
|
||||||
page: 1,
|
|
||||||
limit: 10,
|
|
||||||
total_results: dummyReportExpenses.length,
|
|
||||||
total_pages: 1,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Dummy fetcher untuk mendapatkan single data report expense berdasarkan ID
|
|
||||||
* @param id - ID dari report expense yang ingin diambil
|
|
||||||
* @returns Promise dengan BaseApiResponse berisi single ReportExpense
|
|
||||||
*/
|
|
||||||
export async function dummyGetSingle(
|
|
||||||
id: number
|
|
||||||
): Promise<BaseApiResponse<ReportExpense>> {
|
|
||||||
// Simulasi delay network
|
|
||||||
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
||||||
|
|
||||||
const reportExpense = dummyReportExpenses.find((item) => item.id === id);
|
|
||||||
|
|
||||||
if (!reportExpense) {
|
|
||||||
return {
|
|
||||||
code: 404,
|
|
||||||
status: 'error',
|
|
||||||
message: `Report expense dengan ID ${id} tidak ditemukan`,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
code: 200,
|
|
||||||
status: 'success',
|
|
||||||
message: 'Data report expense berhasil diambil',
|
|
||||||
data: reportExpense,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -11,56 +11,13 @@ import {
|
|||||||
ClosingSapronakCalculation,
|
ClosingSapronakCalculation,
|
||||||
} from '@/types/api/closing';
|
} from '@/types/api/closing';
|
||||||
import { BaseApiResponse } from '@/types/api/api-general';
|
import { BaseApiResponse } from '@/types/api/api-general';
|
||||||
import {
|
|
||||||
dummyGetAllFetcher,
|
|
||||||
dummyGetSingle,
|
|
||||||
dummyGetAllIncomingSapronakFetcher,
|
|
||||||
dummyGetAllOutgoingSapronakFetcher,
|
|
||||||
dummyGetGeneralInfo,
|
|
||||||
dummyGetPerhitunganSapronak,
|
|
||||||
dummyGetOverhead,
|
|
||||||
} from '@/dummy/closing.dummy';
|
|
||||||
import { httpClient, httpClientFetcher } from '@/services/http/client';
|
import { httpClient, httpClientFetcher } from '@/services/http/client';
|
||||||
import { ClosingSales } from '@/types/api/closing';
|
import { ClosingSales } from '@/types/api/closing';
|
||||||
import { dummyGetOneClosingFinance } from '@/dummy/json/closing-finance.dummy';
|
|
||||||
|
|
||||||
export class ClosingApiService extends BaseApiService<Closing, null, null> {
|
export class ClosingApiService extends BaseApiService<Closing, null, null> {
|
||||||
constructor(basePath: string) {
|
constructor(basePath: string) {
|
||||||
super(basePath);
|
super(basePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
async getAllFetcher(endpoint: string): Promise<BaseApiResponse<Closing[]>> {
|
|
||||||
// TODO: Remove this block when backend is ready
|
|
||||||
// return await dummyGetAllFetcher();
|
|
||||||
|
|
||||||
// Uncomment this when backend is ready
|
|
||||||
return await httpClientFetcher<BaseApiResponse<Closing[]>>(endpoint);
|
|
||||||
}
|
|
||||||
|
|
||||||
async getSingle(id: number): Promise<BaseApiResponse<Closing> | undefined> {
|
|
||||||
// TODO: Remove this block when backend is ready
|
|
||||||
// try {
|
|
||||||
// return await dummyGetSingle(id);
|
|
||||||
// } catch (error) {
|
|
||||||
// if (axios.isAxiosError<BaseApiResponse<Closing>>(error)) {
|
|
||||||
// return error.response?.data;
|
|
||||||
// }
|
|
||||||
// return undefined;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Uncomment this when backend is ready
|
|
||||||
try {
|
|
||||||
const getSinglePath = `${this.basePath}/${id}`;
|
|
||||||
const getSingleRes =
|
|
||||||
await httpClient<BaseApiResponse<Closing>>(getSinglePath);
|
|
||||||
return getSingleRes;
|
|
||||||
} catch (error) {
|
|
||||||
if (axios.isAxiosError<BaseApiResponse<Closing>>(error)) {
|
|
||||||
}
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async getPenjualan(
|
async getPenjualan(
|
||||||
id: number
|
id: number
|
||||||
): Promise<BaseApiResponse<ClosingSales> | undefined> {
|
): Promise<BaseApiResponse<ClosingSales> | undefined> {
|
||||||
@@ -81,10 +38,6 @@ export class ClosingApiService extends BaseApiService<Closing, null, null> {
|
|||||||
async getAllIncomingSapronakFetcher(
|
async getAllIncomingSapronakFetcher(
|
||||||
endpoint: string
|
endpoint: string
|
||||||
): Promise<BaseApiResponse<ClosingIncomingSapronak[]>> {
|
): Promise<BaseApiResponse<ClosingIncomingSapronak[]>> {
|
||||||
// TODO: Remove this block when backend is ready
|
|
||||||
// return await dummyGetAllIncomingSapronakFetcher();
|
|
||||||
|
|
||||||
// Uncomment this when backend is ready
|
|
||||||
return await httpClientFetcher<BaseApiResponse<ClosingIncomingSapronak[]>>(
|
return await httpClientFetcher<BaseApiResponse<ClosingIncomingSapronak[]>>(
|
||||||
endpoint
|
endpoint
|
||||||
);
|
);
|
||||||
@@ -93,31 +46,14 @@ export class ClosingApiService extends BaseApiService<Closing, null, null> {
|
|||||||
async getAllOutgoingSapronakFetcher(
|
async getAllOutgoingSapronakFetcher(
|
||||||
endpoint: string
|
endpoint: string
|
||||||
): Promise<BaseApiResponse<ClosingOutgoingSapronak[]>> {
|
): Promise<BaseApiResponse<ClosingOutgoingSapronak[]>> {
|
||||||
// TODO: Remove this block when backend is ready
|
return await httpClientFetcher<BaseApiResponse<ClosingOutgoingSapronak[]>>(
|
||||||
return await dummyGetAllOutgoingSapronakFetcher();
|
endpoint
|
||||||
|
);
|
||||||
// Uncomment this when backend is ready
|
|
||||||
// return await httpClientFetcher<BaseApiResponse<ClosingOutgoingSapronak[]>>(
|
|
||||||
// endpoint
|
|
||||||
// );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async getGeneralInfo(
|
async getGeneralInfo(
|
||||||
id: number
|
id: number
|
||||||
): Promise<BaseApiResponse<ClosingGeneralInformation> | undefined> {
|
): Promise<BaseApiResponse<ClosingGeneralInformation> | undefined> {
|
||||||
// TODO: Remove this block when backend is ready
|
|
||||||
// try {
|
|
||||||
// return await dummyGetGeneralInfo(id);
|
|
||||||
// } catch (error) {
|
|
||||||
// if (
|
|
||||||
// axios.isAxiosError<BaseApiResponse<ClosingGeneralInformation>>(error)
|
|
||||||
// ) {
|
|
||||||
// return error.response?.data;
|
|
||||||
// }
|
|
||||||
// return undefined;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Uncomment this when backend is ready
|
|
||||||
try {
|
try {
|
||||||
const getGeneralInfoPath = `${this.basePath}/${id}`;
|
const getGeneralInfoPath = `${this.basePath}/${id}`;
|
||||||
const getGeneralInfoRes =
|
const getGeneralInfoRes =
|
||||||
@@ -138,19 +74,6 @@ export class ClosingApiService extends BaseApiService<Closing, null, null> {
|
|||||||
async getPerhitunganSapronak(
|
async getPerhitunganSapronak(
|
||||||
id: number
|
id: number
|
||||||
): Promise<BaseApiResponse<ClosingSapronakCalculation> | undefined> {
|
): Promise<BaseApiResponse<ClosingSapronakCalculation> | undefined> {
|
||||||
// TODO: Remove this block when backend is ready
|
|
||||||
// try {
|
|
||||||
// return await dummyGetPerhitunganSapronak(id);
|
|
||||||
// } catch (error) {
|
|
||||||
// if (
|
|
||||||
// axios.isAxiosError<BaseApiResponse<ClosingSapronakCalculation>>(error)
|
|
||||||
// ) {
|
|
||||||
// return error.response?.data;
|
|
||||||
// }
|
|
||||||
// return undefined;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Uncomment this when backend is ready
|
|
||||||
try {
|
try {
|
||||||
const path = `${this.basePath}/${id}/perhitungan_sapronak`;
|
const path = `${this.basePath}/${id}/perhitungan_sapronak`;
|
||||||
return await httpClient<BaseApiResponse<ClosingSapronakCalculation>>(
|
return await httpClient<BaseApiResponse<ClosingSapronakCalculation>>(
|
||||||
@@ -172,17 +95,6 @@ export class ClosingApiService extends BaseApiService<Closing, null, null> {
|
|||||||
async getOverhead(
|
async getOverhead(
|
||||||
id: number
|
id: number
|
||||||
): Promise<BaseApiResponse<ClosingOverhead> | undefined> {
|
): Promise<BaseApiResponse<ClosingOverhead> | undefined> {
|
||||||
// TODO: Remove this block when backend is ready
|
|
||||||
// try {
|
|
||||||
// return await dummyGetOverhead(id);
|
|
||||||
// } catch (error) {
|
|
||||||
// if (axios.isAxiosError<BaseApiResponse<ClosingOverhead>>(error)) {
|
|
||||||
// return error.response?.data;
|
|
||||||
// }
|
|
||||||
// return undefined;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Uncomment this when backend is ready
|
|
||||||
try {
|
try {
|
||||||
const path = `${this.basePath}/${id}/overhead`;
|
const path = `${this.basePath}/${id}/overhead`;
|
||||||
return await httpClient<BaseApiResponse<ClosingOverhead>>(path, {
|
return await httpClient<BaseApiResponse<ClosingOverhead>>(path, {
|
||||||
@@ -199,12 +111,8 @@ export class ClosingApiService extends BaseApiService<Closing, null, null> {
|
|||||||
async getFinance(
|
async getFinance(
|
||||||
id: number
|
id: number
|
||||||
): Promise<BaseApiResponse<ClosingFinance> | undefined> {
|
): Promise<BaseApiResponse<ClosingFinance> | undefined> {
|
||||||
// TODO: Remove this block when backend is ready
|
|
||||||
// return dummyGetOneClosingFinance(id);
|
|
||||||
|
|
||||||
// Uncomment this when backend is ready
|
|
||||||
try {
|
try {
|
||||||
const path = `${this.basePath}/${id}/finance`;
|
const path = `${this.basePath}/${id}/keuangan`;
|
||||||
return await httpClient<BaseApiResponse<ClosingFinance>>(path, {
|
return await httpClient<BaseApiResponse<ClosingFinance>>(path, {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
import { dummyMarketings } from '@/dummy/marketing.dummy';
|
|
||||||
import { sleep } from '@/lib/helper';
|
|
||||||
import { BaseApiService } from '@/services/api/base';
|
import { BaseApiService } from '@/services/api/base';
|
||||||
import { httpClient } from '@/services/http/client';
|
import { httpClient } from '@/services/http/client';
|
||||||
import { BaseApiResponse } from '@/types/api/api-general';
|
import { BaseApiResponse } from '@/types/api/api-general';
|
||||||
@@ -31,41 +29,6 @@ export class SalesOrderService extends BaseApiService<
|
|||||||
super(basePath);
|
super(basePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
// /**
|
|
||||||
// * Override: Mengambil semua data Marketing dari dummyMarketings
|
|
||||||
// */
|
|
||||||
// async getAllFetcher(endpoint: string): Promise<BaseApiResponse<Marketing[]>> {
|
|
||||||
// // Simulasi delay jaringan
|
|
||||||
// await sleep(500);
|
|
||||||
|
|
||||||
// // Filter data marketing yang valid (jika menggunakan BaseMarketing[])
|
|
||||||
// const data = dummyMarketings as Marketing[];
|
|
||||||
|
|
||||||
// return createDummyResponse<Marketing[]>(data);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// /**
|
|
||||||
// * Override: Mengambil satu data Marketing berdasarkan ID dari dummyMarketings
|
|
||||||
// */
|
|
||||||
// async getSingle(id: number): Promise<BaseApiResponse<Marketing> | undefined> {
|
|
||||||
// // Simulasi delay jaringan
|
|
||||||
// await sleep(300);
|
|
||||||
|
|
||||||
// const foundData = dummyMarketings.find((m) => m.id == id);
|
|
||||||
|
|
||||||
// if (foundData) {
|
|
||||||
// // Data ditemukan, kembalikan respons sukses
|
|
||||||
// return createDummyResponse<Marketing>(foundData as Marketing);
|
|
||||||
// } else {
|
|
||||||
// // Data tidak ditemukan, simulasi respons error
|
|
||||||
// return {
|
|
||||||
// code: 404,
|
|
||||||
// status: 'error',
|
|
||||||
// message: 'Marketing data not found (MOCK)',
|
|
||||||
// };
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Approve single marketing data
|
* Approve single marketing data
|
||||||
*/
|
*/
|
||||||
|
|||||||
Reference in New Issue
Block a user