mirror of
https://gitlab.com/mbugroup/lti-web-client.git
synced 2026-05-25 15:55:48 +00:00
Merge branch 'development' into 'staging'
refactor(FE): Improve vehicle number validation message and set See merge request mbugroup/lti-web-client!196
This commit is contained in:
@@ -24,8 +24,6 @@ const FinanceDetailPage = () => {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(finance);
|
|
||||||
|
|
||||||
// if (!finance || isResponseError(finance)) {
|
// if (!finance || isResponseError(finance)) {
|
||||||
// router.replace('/404');
|
// router.replace('/404');
|
||||||
// return;
|
// return;
|
||||||
|
|||||||
@@ -3,54 +3,11 @@ import Table, { TABLE_DEFAULT_STYLING } from '@/components/Table';
|
|||||||
import { isResponseSuccess } from '@/lib/api-helper';
|
import { isResponseSuccess } from '@/lib/api-helper';
|
||||||
import { formatCurrency, formatTitleCase } from '@/lib/helper';
|
import { formatCurrency, formatTitleCase } from '@/lib/helper';
|
||||||
import { ClosingApi } from '@/services/api/closing';
|
import { ClosingApi } from '@/services/api/closing';
|
||||||
import {
|
import { HppItem, ProfitLossItem } from '@/types/api/closing';
|
||||||
DataSummarySubTotal,
|
|
||||||
HppPurchaseData,
|
|
||||||
ProfitLossDataAmount,
|
|
||||||
} from '@/types/api/closing';
|
|
||||||
import { useSearchParams } from 'next/navigation';
|
import { useSearchParams } from 'next/navigation';
|
||||||
|
import { useMemo } from 'react';
|
||||||
import useSWR from 'swr';
|
import useSWR from 'swr';
|
||||||
|
|
||||||
type HppTableRow =
|
|
||||||
| (HppPurchaseData & {
|
|
||||||
group_name: string;
|
|
||||||
group_index: number;
|
|
||||||
isGroupHeader?: boolean;
|
|
||||||
})
|
|
||||||
| {
|
|
||||||
group_name: string;
|
|
||||||
group_index: number;
|
|
||||||
isGroupHeader: true;
|
|
||||||
type?: never;
|
|
||||||
budgeting?: never;
|
|
||||||
realization?: never;
|
|
||||||
}
|
|
||||||
| {
|
|
||||||
type: string;
|
|
||||||
group_name: string;
|
|
||||||
group_index: number;
|
|
||||||
isGroupHeader: false;
|
|
||||||
budgeting?: { rp_per_bird: number; rp_per_kg: number; amount: number };
|
|
||||||
realization?: { rp_per_bird: number; rp_per_kg: number; amount: number };
|
|
||||||
};
|
|
||||||
|
|
||||||
type ProfitLossTableRow =
|
|
||||||
| (DataSummarySubTotal & {
|
|
||||||
type: string;
|
|
||||||
group_name: string;
|
|
||||||
group_index: number;
|
|
||||||
isGroupHeader?: boolean;
|
|
||||||
})
|
|
||||||
| {
|
|
||||||
group_name: string;
|
|
||||||
group_index: number;
|
|
||||||
isGroupHeader: true;
|
|
||||||
type?: never;
|
|
||||||
rp_per_bird?: never;
|
|
||||||
rp_per_kg?: never;
|
|
||||||
amount?: never;
|
|
||||||
};
|
|
||||||
|
|
||||||
const ClosingFinanceTable = ({
|
const ClosingFinanceTable = ({
|
||||||
projectFlockId,
|
projectFlockId,
|
||||||
}: {
|
}: {
|
||||||
@@ -68,167 +25,60 @@ const ClosingFinanceTable = ({
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
const staticHppRows: Array<{
|
const hppTableData: HppItem[] = useMemo(() => {
|
||||||
group_name: string;
|
if (isResponseSuccess(finance)) {
|
||||||
type: string;
|
const customItems = {
|
||||||
group_index: number;
|
label: 'HPP dan Pengeluaran',
|
||||||
}> = [
|
code: 'custom_row',
|
||||||
{
|
} as HppItem;
|
||||||
group_name: 'HPP dan Pengeluaran',
|
const purchases = finance.data.hpp.items.filter(
|
||||||
type: 'Pembelian PAKAN',
|
(item) => item.category === 'purchase'
|
||||||
group_index: 0,
|
);
|
||||||
},
|
const totalBudgeting = {
|
||||||
{
|
label: 'HPP dan Bahan Baku',
|
||||||
group_name: 'HPP dan Pengeluaran',
|
code: 'custom_row',
|
||||||
type: 'Pembelian STARTER',
|
} as HppItem;
|
||||||
group_index: 0,
|
const overheads = finance.data.hpp.items.filter(
|
||||||
},
|
(item) => item.category === 'overhead'
|
||||||
{
|
);
|
||||||
group_name: 'HPP dan Pengeluaran',
|
return [customItems, ...purchases, totalBudgeting, ...overheads];
|
||||||
type: 'Pembelian DOC',
|
}
|
||||||
group_index: 0,
|
return [];
|
||||||
},
|
}, [finance]);
|
||||||
{
|
|
||||||
group_name: 'HPP dan Pengeluaran',
|
|
||||||
type: 'Pembelian PULLET',
|
|
||||||
group_index: 0,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
group_name: 'HPP dan Pengeluaran',
|
|
||||||
type: 'Pembelian LAYER',
|
|
||||||
group_index: 0,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
group_name: 'HPP dan Bahan Baku',
|
|
||||||
type: 'Pengeluaran Overhead',
|
|
||||||
group_index: 1,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
group_name: 'HPP dan Bahan Baku',
|
|
||||||
type: 'Beban Ekspedisi',
|
|
||||||
group_index: 1,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const hppTableData: HppTableRow[] = [
|
const profitLossTableData: ProfitLossItem[] = useMemo(() => {
|
||||||
{
|
if (isResponseSuccess(finance)) {
|
||||||
group_name: 'HPP dan Pengeluaran',
|
const incomes = finance.data.profit_loss.items.filter(
|
||||||
group_index: 0,
|
(item) => item.type === 'income'
|
||||||
isGroupHeader: true as const,
|
);
|
||||||
},
|
const purchases = finance.data.profit_loss.items.filter(
|
||||||
...staticHppRows
|
(item) => item.type === 'purchase'
|
||||||
.filter((row) => row.group_index === 0)
|
);
|
||||||
.map((staticRow) => {
|
const overheads = finance.data.profit_loss.items.filter(
|
||||||
const apiData = isResponseSuccess(finance)
|
(item) => item.type === 'overhead'
|
||||||
? finance.data.hpp_purchases.hpp
|
);
|
||||||
.find((g) => g.group_name === staticRow.group_name)
|
const grossProfit = {
|
||||||
?.data.find((d) => d.type === staticRow.type)
|
label: 'LABA RUGI BRUTO',
|
||||||
: null;
|
code: 'custom_row',
|
||||||
|
type: 'gross_profit',
|
||||||
return {
|
|
||||||
group_name: staticRow.group_name,
|
|
||||||
group_index: staticRow.group_index,
|
|
||||||
type: staticRow.type,
|
|
||||||
budgeting: apiData?.budgeting || {
|
|
||||||
rp_per_bird: 0,
|
|
||||||
rp_per_kg: 0,
|
|
||||||
amount: 0,
|
|
||||||
},
|
|
||||||
realization: apiData?.realization || {
|
|
||||||
rp_per_bird: 0,
|
|
||||||
rp_per_kg: 0,
|
|
||||||
amount: 0,
|
|
||||||
},
|
|
||||||
isGroupHeader: false as const,
|
|
||||||
};
|
|
||||||
}),
|
|
||||||
{
|
|
||||||
group_name: 'HPP dan Bahan Baku',
|
|
||||||
group_index: 1,
|
|
||||||
isGroupHeader: true as const,
|
|
||||||
},
|
|
||||||
...staticHppRows
|
|
||||||
.filter((row) => row.group_index === 1)
|
|
||||||
.map((staticRow) => {
|
|
||||||
const apiData = isResponseSuccess(finance)
|
|
||||||
? finance.data.hpp_purchases.hpp
|
|
||||||
.find((g) => g.group_name === staticRow.group_name)
|
|
||||||
?.data.find((d) => d.type === staticRow.type)
|
|
||||||
: null;
|
|
||||||
|
|
||||||
return {
|
|
||||||
group_name: staticRow.group_name,
|
|
||||||
group_index: staticRow.group_index,
|
|
||||||
type: staticRow.type,
|
|
||||||
budgeting: apiData?.budgeting || {
|
|
||||||
rp_per_bird: 0,
|
|
||||||
rp_per_kg: 0,
|
|
||||||
amount: 0,
|
|
||||||
},
|
|
||||||
realization: apiData?.realization || {
|
|
||||||
rp_per_bird: 0,
|
|
||||||
rp_per_kg: 0,
|
|
||||||
amount: 0,
|
|
||||||
},
|
|
||||||
isGroupHeader: false as const,
|
|
||||||
};
|
|
||||||
}),
|
|
||||||
{
|
|
||||||
group_name: 'HPP',
|
|
||||||
group_index: 2,
|
|
||||||
isGroupHeader: true as const,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const profitLossTableData: ProfitLossTableRow[] = isResponseSuccess(finance)
|
|
||||||
? [
|
|
||||||
// Pembelian group
|
|
||||||
...finance.data.profit_loss.data.pembelian.map((item) => ({
|
|
||||||
label: 'Pembelian',
|
|
||||||
group_name: 'Pembelian',
|
|
||||||
group_index: 1,
|
|
||||||
type: item.type,
|
|
||||||
rp_per_bird: item.rp_per_bird,
|
|
||||||
rp_per_kg: item.rp_per_kg,
|
|
||||||
amount: item.amount,
|
|
||||||
isGroupHeader: false as const,
|
|
||||||
})),
|
|
||||||
{
|
|
||||||
label: finance.data.profit_loss.data.summary.gross_profit.label,
|
|
||||||
group_name: 'Penjualan',
|
|
||||||
group_index: 0,
|
|
||||||
isGroupHeader: true as const,
|
|
||||||
type: finance.data.profit_loss.data.summary.gross_profit.label,
|
|
||||||
rp_per_bird:
|
rp_per_bird:
|
||||||
finance.data.profit_loss.data.summary.gross_profit.rp_per_bird,
|
finance.data.profit_loss.summary.gross_profit.rp_per_bird ?? 0,
|
||||||
rp_per_kg:
|
rp_per_kg: finance.data.profit_loss.summary.gross_profit.rp_per_kg ?? 0,
|
||||||
finance.data.profit_loss.data.summary.gross_profit.rp_per_kg,
|
amount: finance.data.profit_loss.summary.gross_profit.amount ?? 0,
|
||||||
amount: finance.data.profit_loss.data.summary.gross_profit.amount,
|
} as ProfitLossItem;
|
||||||
},
|
const subtotal = {
|
||||||
// Penjualan group
|
label: 'Subtotal',
|
||||||
...finance.data.profit_loss.data.penjualan.map((item) => ({
|
code: 'custom_row',
|
||||||
label: 'Penjualan',
|
type: 'subtotal',
|
||||||
group_name: 'Penjualan',
|
|
||||||
group_index: 0,
|
|
||||||
type: item.type,
|
|
||||||
rp_per_bird: item.rp_per_bird,
|
|
||||||
rp_per_kg: item.rp_per_kg,
|
|
||||||
amount: item.amount,
|
|
||||||
isGroupHeader: false as const,
|
|
||||||
})),
|
|
||||||
{
|
|
||||||
label: finance.data.profit_loss.data.summary.sub_total.label,
|
|
||||||
group_name: 'Pembelian',
|
|
||||||
group_index: 1,
|
|
||||||
isGroupHeader: true as const,
|
|
||||||
type: finance.data.profit_loss.data.summary.sub_total.label,
|
|
||||||
rp_per_bird:
|
rp_per_bird:
|
||||||
finance.data.profit_loss.data.summary.sub_total.rp_per_bird,
|
finance.data.profit_loss.summary.sub_total.rp_per_bird ?? 0,
|
||||||
rp_per_kg: finance.data.profit_loss.data.summary.sub_total.rp_per_kg,
|
rp_per_kg: finance.data.profit_loss.summary.sub_total.rp_per_kg ?? 0,
|
||||||
amount: finance.data.profit_loss.data.summary.sub_total.amount,
|
amount: finance.data.profit_loss.summary.sub_total.amount ?? 0,
|
||||||
},
|
} as ProfitLossItem;
|
||||||
]
|
return [...incomes, ...purchases, grossProfit, ...overheads, subtotal];
|
||||||
: [];
|
}
|
||||||
|
return [];
|
||||||
|
}, [finance]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='flex flex-col gap-4'>
|
<div className='flex flex-col gap-4'>
|
||||||
@@ -241,35 +91,21 @@ const ClosingFinanceTable = ({
|
|||||||
>
|
>
|
||||||
<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>Laba Rugi Brutto</div>
|
||||||
{isResponseSuccess(finance)
|
|
||||||
? formatTitleCase(
|
|
||||||
finance.data.profit_loss.data.summary.gross_profit
|
|
||||||
.label || '-'
|
|
||||||
)
|
|
||||||
: 'Laba Rugi Brutto'}
|
|
||||||
</div>
|
|
||||||
<div className='text-lg font-bold'>
|
<div className='text-lg font-bold'>
|
||||||
{isResponseSuccess(finance)
|
{isResponseSuccess(finance)
|
||||||
? formatCurrency(
|
? formatCurrency(
|
||||||
finance.data.profit_loss.data.summary.gross_profit.amount
|
finance.data.profit_loss.summary.gross_profit.amount
|
||||||
)
|
)
|
||||||
: '-'}
|
: '-'}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className='flex flex-col gap-1'>
|
<div className='flex flex-col gap-1'>
|
||||||
<div>
|
<div>Laba Rugi Netto</div>
|
||||||
{isResponseSuccess(finance)
|
|
||||||
? formatTitleCase(
|
|
||||||
finance.data.profit_loss.data.summary.net_profit.label ||
|
|
||||||
'-'
|
|
||||||
)
|
|
||||||
: 'Laba Rugi Netto'}
|
|
||||||
</div>
|
|
||||||
<div className='text-lg font-bold'>
|
<div className='text-lg font-bold'>
|
||||||
{isResponseSuccess(finance)
|
{isResponseSuccess(finance)
|
||||||
? formatCurrency(
|
? formatCurrency(
|
||||||
finance.data.profit_loss.data.summary.net_profit.amount
|
finance.data.profit_loss.summary.net_profit.amount
|
||||||
)
|
)
|
||||||
: '-'}
|
: '-'}
|
||||||
</div>
|
</div>
|
||||||
@@ -277,11 +113,7 @@ const ClosingFinanceTable = ({
|
|||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
<Card
|
<Card
|
||||||
title={
|
title='HPP Purchases'
|
||||||
isResponseSuccess(finance)
|
|
||||||
? finance.data.hpp_purchases.title
|
|
||||||
: 'HPP Purchases'
|
|
||||||
}
|
|
||||||
variant='bordered'
|
variant='bordered'
|
||||||
collapsible
|
collapsible
|
||||||
className={{
|
className={{
|
||||||
@@ -289,7 +121,7 @@ const ClosingFinanceTable = ({
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div className='mt-6 p-0 mb-0'>
|
<div className='mt-6 p-0 mb-0'>
|
||||||
<Table<HppTableRow>
|
<Table<HppItem>
|
||||||
data={hppTableData}
|
data={hppTableData}
|
||||||
isLoading={isLoading}
|
isLoading={isLoading}
|
||||||
columns={[
|
columns={[
|
||||||
@@ -297,10 +129,10 @@ const ClosingFinanceTable = ({
|
|||||||
header: 'No.',
|
header: 'No.',
|
||||||
enableSorting: false,
|
enableSorting: false,
|
||||||
accessorFn: (item, index) => {
|
accessorFn: (item, index) => {
|
||||||
if (item.isGroupHeader) return '-';
|
if (item.code === 'custom_row') return '-';
|
||||||
const dataRowsBefore = hppTableData
|
const dataRowsBefore = hppTableData
|
||||||
.slice(0, index)
|
.slice(0, index)
|
||||||
.filter((row) => !row.isGroupHeader).length;
|
.filter((row) => row.code !== 'custom_row').length;
|
||||||
return dataRowsBefore + 1;
|
return dataRowsBefore + 1;
|
||||||
},
|
},
|
||||||
footer: (props) => {
|
footer: (props) => {
|
||||||
@@ -310,7 +142,7 @@ const ClosingFinanceTable = ({
|
|||||||
{
|
{
|
||||||
header: 'Jenis',
|
header: 'Jenis',
|
||||||
enableSorting: false,
|
enableSorting: false,
|
||||||
accessorFn: (item) => formatTitleCase(item.type || '-'),
|
accessorFn: (item) => formatTitleCase(item.label || '-'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
header: 'Budgeting',
|
header: 'Budgeting',
|
||||||
@@ -326,7 +158,7 @@ const ClosingFinanceTable = ({
|
|||||||
return props.column.id === 'budgeting_rp_per_bird' &&
|
return props.column.id === 'budgeting_rp_per_bird' &&
|
||||||
isResponseSuccess(finance)
|
isResponseSuccess(finance)
|
||||||
? formatCurrency(
|
? formatCurrency(
|
||||||
finance.data.hpp_purchases.summary_hpp?.budgeting
|
finance.data.hpp.summary?.budgeting
|
||||||
?.rp_per_bird || 0
|
?.rp_per_bird || 0
|
||||||
)
|
)
|
||||||
: '-';
|
: '-';
|
||||||
@@ -342,8 +174,8 @@ const ClosingFinanceTable = ({
|
|||||||
return props.column.id === 'budgeting_rp_per_kg' &&
|
return props.column.id === 'budgeting_rp_per_kg' &&
|
||||||
isResponseSuccess(finance)
|
isResponseSuccess(finance)
|
||||||
? formatCurrency(
|
? formatCurrency(
|
||||||
finance.data.hpp_purchases.summary_hpp?.budgeting
|
finance.data.hpp.summary?.budgeting?.rp_per_kg ||
|
||||||
?.rp_per_kg || 0
|
0
|
||||||
)
|
)
|
||||||
: '-';
|
: '-';
|
||||||
},
|
},
|
||||||
@@ -358,8 +190,7 @@ const ClosingFinanceTable = ({
|
|||||||
return props.column.id === 'budgeting_amount' &&
|
return props.column.id === 'budgeting_amount' &&
|
||||||
isResponseSuccess(finance)
|
isResponseSuccess(finance)
|
||||||
? formatCurrency(
|
? formatCurrency(
|
||||||
finance.data.hpp_purchases.summary_hpp?.budgeting
|
finance.data.hpp.summary?.budgeting?.amount || 0
|
||||||
?.amount || 0
|
|
||||||
)
|
)
|
||||||
: '-';
|
: '-';
|
||||||
},
|
},
|
||||||
@@ -380,8 +211,8 @@ const ClosingFinanceTable = ({
|
|||||||
return props.column.id === 'realization_rp_per_bird' &&
|
return props.column.id === 'realization_rp_per_bird' &&
|
||||||
isResponseSuccess(finance)
|
isResponseSuccess(finance)
|
||||||
? formatCurrency(
|
? formatCurrency(
|
||||||
finance.data.hpp_purchases.summary_hpp
|
finance.data.hpp.summary?.realization
|
||||||
?.realization?.rp_per_bird || 0
|
?.rp_per_bird || 0
|
||||||
)
|
)
|
||||||
: '-';
|
: '-';
|
||||||
},
|
},
|
||||||
@@ -396,8 +227,8 @@ const ClosingFinanceTable = ({
|
|||||||
return props.column.id === 'realization_rp_per_kg' &&
|
return props.column.id === 'realization_rp_per_kg' &&
|
||||||
isResponseSuccess(finance)
|
isResponseSuccess(finance)
|
||||||
? formatCurrency(
|
? formatCurrency(
|
||||||
finance.data.hpp_purchases.summary_hpp
|
finance.data.hpp.summary?.realization
|
||||||
?.realization?.rp_per_kg || 0
|
?.rp_per_kg || 0
|
||||||
)
|
)
|
||||||
: '-';
|
: '-';
|
||||||
},
|
},
|
||||||
@@ -412,8 +243,7 @@ const ClosingFinanceTable = ({
|
|||||||
return props.column.id === 'realization_amount' &&
|
return props.column.id === 'realization_amount' &&
|
||||||
isResponseSuccess(finance)
|
isResponseSuccess(finance)
|
||||||
? formatCurrency(
|
? formatCurrency(
|
||||||
finance.data.hpp_purchases.summary_hpp
|
finance.data.hpp.summary?.realization?.amount || 0
|
||||||
?.realization?.amount || 0
|
|
||||||
)
|
)
|
||||||
: '-';
|
: '-';
|
||||||
},
|
},
|
||||||
@@ -423,7 +253,7 @@ const ClosingFinanceTable = ({
|
|||||||
]}
|
]}
|
||||||
renderCustomRow={(row) => {
|
renderCustomRow={(row) => {
|
||||||
const rowData = row.original;
|
const rowData = row.original;
|
||||||
if (rowData.isGroupHeader) {
|
if (rowData.code === 'custom_row') {
|
||||||
return (
|
return (
|
||||||
<tr
|
<tr
|
||||||
key={row.id}
|
key={row.id}
|
||||||
@@ -437,7 +267,7 @@ const ClosingFinanceTable = ({
|
|||||||
className={TABLE_DEFAULT_STYLING.bodyColumnClassName}
|
className={TABLE_DEFAULT_STYLING.bodyColumnClassName}
|
||||||
>
|
>
|
||||||
<div className='font-bold'>
|
<div className='font-bold'>
|
||||||
{formatTitleCase(rowData.group_name ?? '-')}
|
{formatTitleCase(rowData.label ?? '-')}
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
@@ -450,11 +280,7 @@ const ClosingFinanceTable = ({
|
|||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
<Card
|
<Card
|
||||||
title={
|
title='Profit/Loss'
|
||||||
isResponseSuccess(finance)
|
|
||||||
? finance.data.profit_loss.title
|
|
||||||
: 'Profit/Loss'
|
|
||||||
}
|
|
||||||
variant='bordered'
|
variant='bordered'
|
||||||
collapsible
|
collapsible
|
||||||
className={{
|
className={{
|
||||||
@@ -462,39 +288,32 @@ const ClosingFinanceTable = ({
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div className='mt-6 p-0 mb-0'>
|
<div className='mt-6 p-0 mb-0'>
|
||||||
<Table<ProfitLossTableRow>
|
<Table<ProfitLossItem>
|
||||||
data={profitLossTableData}
|
data={profitLossTableData}
|
||||||
isLoading={isLoading}
|
isLoading={isLoading}
|
||||||
columns={[
|
columns={[
|
||||||
{
|
{
|
||||||
header: 'Jenis',
|
header: 'Jenis',
|
||||||
enableSorting: false,
|
enableSorting: false,
|
||||||
accessorFn: (item) => item.type,
|
accessorFn: (item) => item.label,
|
||||||
cell: (item) => (
|
cell: (item) => (
|
||||||
<div className=''>
|
<div className=''>
|
||||||
{formatTitleCase(item.row.original.type || '-')}
|
{formatTitleCase(item.row.original.label || '-')}
|
||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
footer: (item) => (
|
footer: () => (
|
||||||
<div className='font-bold uppercase'>
|
<div className='font-bold uppercase'>LABA RUGI NETTO</div>
|
||||||
{isResponseSuccess(finance)
|
|
||||||
? formatTitleCase(
|
|
||||||
finance.data.profit_loss.data.summary.net_profit
|
|
||||||
.label || '-'
|
|
||||||
)
|
|
||||||
: '-'}
|
|
||||||
</div>
|
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
header: 'Rp/Ekor',
|
header: 'Rp/Ekor',
|
||||||
enableSorting: false,
|
enableSorting: false,
|
||||||
accessorFn: (item) => formatCurrency(item.rp_per_bird || 0),
|
accessorFn: (item) => formatCurrency(item.rp_per_bird || 0),
|
||||||
footer: (item) => (
|
footer: () => (
|
||||||
<div className='font-bold'>
|
<div className='font-bold'>
|
||||||
{isResponseSuccess(finance)
|
{isResponseSuccess(finance)
|
||||||
? formatCurrency(
|
? formatCurrency(
|
||||||
finance.data.profit_loss.data.summary.net_profit
|
finance.data.profit_loss.summary.net_profit
|
||||||
.rp_per_bird || 0
|
.rp_per_bird || 0
|
||||||
)
|
)
|
||||||
: formatCurrency(0)}
|
: formatCurrency(0)}
|
||||||
@@ -505,11 +324,11 @@ const ClosingFinanceTable = ({
|
|||||||
header: 'Rp/Kg',
|
header: 'Rp/Kg',
|
||||||
enableSorting: false,
|
enableSorting: false,
|
||||||
accessorFn: (item) => formatCurrency(item.rp_per_kg || 0),
|
accessorFn: (item) => formatCurrency(item.rp_per_kg || 0),
|
||||||
footer: (item) => (
|
footer: () => (
|
||||||
<div className='font-bold'>
|
<div className='font-bold'>
|
||||||
{isResponseSuccess(finance)
|
{isResponseSuccess(finance)
|
||||||
? formatCurrency(
|
? formatCurrency(
|
||||||
finance.data.profit_loss.data.summary.net_profit
|
finance.data.profit_loss.summary.net_profit
|
||||||
.rp_per_kg || 0
|
.rp_per_kg || 0
|
||||||
)
|
)
|
||||||
: formatCurrency(0)}
|
: formatCurrency(0)}
|
||||||
@@ -520,11 +339,11 @@ const ClosingFinanceTable = ({
|
|||||||
header: 'Jumlah (Rp)',
|
header: 'Jumlah (Rp)',
|
||||||
enableSorting: false,
|
enableSorting: false,
|
||||||
accessorFn: (item) => formatCurrency(item.amount || 0),
|
accessorFn: (item) => formatCurrency(item.amount || 0),
|
||||||
footer: (item) => (
|
footer: () => (
|
||||||
<div className='font-bold'>
|
<div className='font-bold'>
|
||||||
{isResponseSuccess(finance)
|
{isResponseSuccess(finance)
|
||||||
? formatCurrency(
|
? formatCurrency(
|
||||||
finance.data.profit_loss.data.summary.net_profit
|
finance.data.profit_loss.summary.net_profit
|
||||||
.amount || 0
|
.amount || 0
|
||||||
)
|
)
|
||||||
: formatCurrency(0)}
|
: formatCurrency(0)}
|
||||||
@@ -534,37 +353,28 @@ const ClosingFinanceTable = ({
|
|||||||
]}
|
]}
|
||||||
renderCustomRow={(row) => {
|
renderCustomRow={(row) => {
|
||||||
const rowData = row.original;
|
const rowData = row.original;
|
||||||
if (rowData.isGroupHeader) {
|
if (rowData.code === 'custom_row') {
|
||||||
if (rowData.amount) {
|
|
||||||
return (
|
return (
|
||||||
<tr
|
<tr
|
||||||
key={row.id}
|
key={row.id}
|
||||||
className={TABLE_DEFAULT_STYLING.footerRowClassName}
|
className={TABLE_DEFAULT_STYLING.footerRowClassName}
|
||||||
>
|
>
|
||||||
<td
|
<td className={TABLE_DEFAULT_STYLING.bodyColumnClassName}>
|
||||||
className={TABLE_DEFAULT_STYLING.bodyColumnClassName}
|
|
||||||
>
|
|
||||||
<div className='font-bold ps-6 uppercase'>
|
<div className='font-bold ps-6 uppercase'>
|
||||||
{formatTitleCase(rowData.label ?? '-')}
|
{formatTitleCase(rowData.label ?? '-')}
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td
|
<td className={TABLE_DEFAULT_STYLING.bodyColumnClassName}>
|
||||||
className={TABLE_DEFAULT_STYLING.bodyColumnClassName}
|
|
||||||
>
|
|
||||||
<div className='font-bold'>
|
<div className='font-bold'>
|
||||||
{formatCurrency(rowData.rp_per_bird ?? 0)}
|
{formatCurrency(rowData.rp_per_bird ?? 0)}
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td
|
<td className={TABLE_DEFAULT_STYLING.bodyColumnClassName}>
|
||||||
className={TABLE_DEFAULT_STYLING.bodyColumnClassName}
|
|
||||||
>
|
|
||||||
<div className='font-bold'>
|
<div className='font-bold'>
|
||||||
{formatCurrency(rowData.rp_per_kg ?? 0)}
|
{formatCurrency(rowData.rp_per_kg ?? 0)}
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td
|
<td className={TABLE_DEFAULT_STYLING.bodyColumnClassName}>
|
||||||
className={TABLE_DEFAULT_STYLING.bodyColumnClassName}
|
|
||||||
>
|
|
||||||
<div className='font-bold'>
|
<div className='font-bold'>
|
||||||
{formatCurrency(rowData.amount ?? 0)}
|
{formatCurrency(rowData.amount ?? 0)}
|
||||||
</div>
|
</div>
|
||||||
@@ -572,22 +382,6 @@ const ClosingFinanceTable = ({
|
|||||||
</tr>
|
</tr>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return (
|
|
||||||
<tr
|
|
||||||
key={row.id}
|
|
||||||
className={TABLE_DEFAULT_STYLING.bodyRowClassName}
|
|
||||||
>
|
|
||||||
<td
|
|
||||||
colSpan={4}
|
|
||||||
className={TABLE_DEFAULT_STYLING.bodyColumnClassName}
|
|
||||||
>
|
|
||||||
<div className='font-bold'>
|
|
||||||
{formatTitleCase(rowData.group_name ?? '-')}
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return null;
|
return null;
|
||||||
}}
|
}}
|
||||||
className={{
|
className={{
|
||||||
|
|||||||
@@ -32,17 +32,11 @@ const ClosingOverheadTable = ({
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Helper function to create columns with footer support
|
// Helper function to create columns with footer support
|
||||||
const createColumns = (total?: OverheadTotal): ColumnDef<Overhead>[] => [
|
const createColumns = (
|
||||||
// Group untuk kolom tanpa footer
|
total?: OverheadTotal,
|
||||||
{
|
kandangId?: number
|
||||||
header: 'Nama Item',
|
): ColumnDef<Overhead>[] => {
|
||||||
accessorFn: (props) => props.item_name,
|
const flockColumn: ColumnDef<Overhead>[] = [
|
||||||
footer: 'Total Pengeluaran Overhead',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
header: 'Satuan',
|
|
||||||
accessorFn: (props) => props.uom_name,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
header: 'Budget Pengajuan',
|
header: 'Budget Pengajuan',
|
||||||
footer: '',
|
footer: '',
|
||||||
@@ -70,7 +64,9 @@ const ClosingOverheadTable = ({
|
|||||||
props.budget_total_amount
|
props.budget_total_amount
|
||||||
? formatCurrency(props.budget_total_amount)
|
? formatCurrency(props.budget_total_amount)
|
||||||
: '-',
|
: '-',
|
||||||
footer: total ? () => formatCurrency(total.budget_total_amount) : '',
|
footer: total
|
||||||
|
? () => formatCurrency(total.budget_total_amount)
|
||||||
|
: '',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@@ -110,10 +106,68 @@ const ClosingOverheadTable = ({
|
|||||||
props.actual_total_amount
|
props.actual_total_amount
|
||||||
? formatCurrency(props.actual_total_amount)
|
? formatCurrency(props.actual_total_amount)
|
||||||
: '-',
|
: '-',
|
||||||
footer: total ? () => formatCurrency(total.actual_total_amount) : '',
|
footer: total
|
||||||
|
? () => formatCurrency(total.actual_total_amount)
|
||||||
|
: '',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const kandangColumn: ColumnDef<Overhead>[] = [
|
||||||
|
{
|
||||||
|
id: 'actual_date',
|
||||||
|
header: 'Tanggal',
|
||||||
|
accessorFn: (props) =>
|
||||||
|
props.actual_date
|
||||||
|
? formatDate(props.actual_date, 'DD MMM, YYYY')
|
||||||
|
: '-',
|
||||||
|
footer: '',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'actual_quantity',
|
||||||
|
header: 'Jumlah',
|
||||||
|
accessorFn: (props) =>
|
||||||
|
props.actual_quantity ? formatNumber(props.actual_quantity) : '-',
|
||||||
|
footer: total ? () => formatNumber(total.actual_quantity) : '',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'actual_unit_price',
|
||||||
|
header: 'Harga Satuan',
|
||||||
|
accessorFn: (props) =>
|
||||||
|
props.actual_unit_price
|
||||||
|
? formatCurrency(props.actual_unit_price)
|
||||||
|
: '-',
|
||||||
|
footer: '',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'actual_total_amount',
|
||||||
|
header: 'Total',
|
||||||
|
accessorFn: (props) =>
|
||||||
|
props.actual_total_amount
|
||||||
|
? formatCurrency(props.actual_total_amount)
|
||||||
|
: '-',
|
||||||
|
footer: total ? () => formatCurrency(total.actual_total_amount) : '',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const finalColumns: ColumnDef<Overhead>[] = [
|
||||||
|
// Group untuk kolom tanpa footer
|
||||||
|
{
|
||||||
|
header: 'No',
|
||||||
|
accessorFn: (_, index) => index,
|
||||||
|
cell: (props) => props.row.index + 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
header: 'Nama Item',
|
||||||
|
accessorFn: (props) => props.item_name,
|
||||||
|
footer: 'Total Pengeluaran Overhead',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
header: 'Satuan',
|
||||||
|
accessorFn: (props) => props.uom_name,
|
||||||
|
},
|
||||||
|
...(kandangId ? kandangColumn : flockColumn),
|
||||||
{
|
{
|
||||||
id: 'cost_per_bird',
|
id: 'cost_per_bird',
|
||||||
header: 'Rp/Ekor',
|
header: 'Rp/Ekor',
|
||||||
@@ -122,11 +176,16 @@ const ClosingOverheadTable = ({
|
|||||||
footer: total ? () => formatCurrency(total.cost_per_bird) : '',
|
footer: total ? () => formatCurrency(total.cost_per_bird) : '',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
return finalColumns;
|
||||||
|
};
|
||||||
|
|
||||||
const columns = useMemo(
|
const columns = useMemo(
|
||||||
() =>
|
() =>
|
||||||
isResponseSuccess(overhead)
|
isResponseSuccess(overhead)
|
||||||
? createColumns(overhead.data?.total)
|
? createColumns(
|
||||||
|
overhead.data?.total,
|
||||||
|
kandangId ? Number(kandangId) : undefined
|
||||||
|
)
|
||||||
: createColumns(),
|
: createColumns(),
|
||||||
[overhead]
|
[overhead]
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -118,8 +118,6 @@ const DashboardProduction = () => {
|
|||||||
} as DashboardFilterType,
|
} as DashboardFilterType,
|
||||||
validationSchema: getDashboardFilterSchema(analysisMode),
|
validationSchema: getDashboardFilterSchema(analysisMode),
|
||||||
onSubmit: (values) => {
|
onSubmit: (values) => {
|
||||||
console.log(values);
|
|
||||||
|
|
||||||
handleApplyFilter({
|
handleApplyFilter({
|
||||||
start_date: values.startDate || '',
|
start_date: values.startDate || '',
|
||||||
end_date: values.endDate || '',
|
end_date: values.endDate || '',
|
||||||
@@ -139,8 +137,6 @@ const DashboardProduction = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleApplyFilter = (values: DashboardFilter) => {
|
const handleApplyFilter = (values: DashboardFilter) => {
|
||||||
console.log(values);
|
|
||||||
|
|
||||||
// Build query params object, only include non-empty values
|
// Build query params object, only include non-empty values
|
||||||
const params: Record<string, string> = {};
|
const params: Record<string, string> = {};
|
||||||
|
|
||||||
@@ -156,7 +152,6 @@ const DashboardProduction = () => {
|
|||||||
if (values.comparison_type) params.comparison_type = values.comparison_type;
|
if (values.comparison_type) params.comparison_type = values.comparison_type;
|
||||||
|
|
||||||
setEndpointUrl(`/dashboards?${new URLSearchParams(params).toString()}`);
|
setEndpointUrl(`/dashboards?${new URLSearchParams(params).toString()}`);
|
||||||
console.log(endpointUrl);
|
|
||||||
filterModal.closeModal();
|
filterModal.closeModal();
|
||||||
refreshDashboardProductionData();
|
refreshDashboardProductionData();
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,21 +1,17 @@
|
|||||||
import { ChangeEventHandler, useMemo, useState } from 'react';
|
import { ChangeEventHandler, useMemo, useState } from 'react';
|
||||||
import { CellContext, Row } from '@tanstack/react-table';
|
import { CellContext } from '@tanstack/react-table';
|
||||||
import { useSearchParams } from 'next/navigation';
|
import { useSearchParams } from 'next/navigation';
|
||||||
import useSWR from 'swr';
|
import useSWR from 'swr';
|
||||||
|
|
||||||
import Button from '@/components/Button';
|
import Button from '@/components/Button';
|
||||||
import Card from '@/components/Card';
|
import Card from '@/components/Card';
|
||||||
import Dropdown from '@/components/dropdown/Dropdown';
|
|
||||||
import DateInput from '@/components/input/DateInput';
|
import DateInput from '@/components/input/DateInput';
|
||||||
import DebouncedTextInput from '@/components/input/DebouncedTextInput';
|
import DebouncedTextInput from '@/components/input/DebouncedTextInput';
|
||||||
import SelectInput, {
|
import SelectInput, {
|
||||||
OptionType,
|
OptionType,
|
||||||
useSelect,
|
useSelect,
|
||||||
} from '@/components/input/SelectInput';
|
} from '@/components/input/SelectInput';
|
||||||
import Menu from '@/components/menu/Menu';
|
|
||||||
import MenuItem from '@/components/menu/MenuItem';
|
|
||||||
import Table from '@/components/Table';
|
import Table from '@/components/Table';
|
||||||
import Tooltip from '@/components/Tooltip';
|
|
||||||
import { formatCurrency, formatDate, formatTitleCase } from '@/lib/helper';
|
import { formatCurrency, formatDate, formatTitleCase } from '@/lib/helper';
|
||||||
import { useTableFilter } from '@/services/hooks/useTableFilter';
|
import { useTableFilter } from '@/services/hooks/useTableFilter';
|
||||||
import { Finance } from '@/types/api/finance/finance';
|
import { Finance } from '@/types/api/finance/finance';
|
||||||
@@ -23,11 +19,10 @@ import {
|
|||||||
FINANCE_INITIAL_BALANCE_STATUS,
|
FINANCE_INITIAL_BALANCE_STATUS,
|
||||||
FINANCE_INJECTION_STATUS,
|
FINANCE_INJECTION_STATUS,
|
||||||
FINANCE_TRANSACTION_STATUS,
|
FINANCE_TRANSACTION_STATUS,
|
||||||
ROWS_OPTIONS,
|
|
||||||
} from '@/config/constant';
|
} from '@/config/constant';
|
||||||
import { FinanceApi } from '@/services/api/finance';
|
import { FinanceApi } from '@/services/api/finance';
|
||||||
import { isResponseSuccess } from '@/lib/api-helper';
|
import { isResponseSuccess } from '@/lib/api-helper';
|
||||||
import { BankApi, CustomerApi, SupplierApi } from '@/services/api/master-data';
|
import { BankApi } from '@/services/api/master-data';
|
||||||
import { Bank } from '@/types/api/master-data/bank';
|
import { Bank } from '@/types/api/master-data/bank';
|
||||||
import { useModal } from '@/components/Modal';
|
import { useModal } from '@/components/Modal';
|
||||||
import ConfirmationModal from '@/components/modal/ConfirmationModal';
|
import ConfirmationModal from '@/components/modal/ConfirmationModal';
|
||||||
@@ -219,15 +214,12 @@ const FinanceTable = () => {
|
|||||||
{ label: 'Tanggal Dibuat', value: 'created_at' },
|
{ label: 'Tanggal Dibuat', value: 'created_at' },
|
||||||
];
|
];
|
||||||
}, []);
|
}, []);
|
||||||
const { options: bankOptions, rawData: bankRawData } = useSelect<Bank>(
|
const {
|
||||||
BankApi.basePath,
|
options: bankOptions,
|
||||||
'id',
|
rawData: bankRawData,
|
||||||
'alias',
|
setInputValue: bankInputValue,
|
||||||
'',
|
loadMore: bankLoadMore,
|
||||||
{
|
} = useSelect<Bank>(BankApi.basePath, 'id', 'alias');
|
||||||
limit: 'limit',
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
// ===== Handler =====
|
// ===== Handler =====
|
||||||
const searchChangeHandler: ChangeEventHandler<HTMLInputElement> = (e) => {
|
const searchChangeHandler: ChangeEventHandler<HTMLInputElement> = (e) => {
|
||||||
@@ -501,6 +493,8 @@ const FinanceTable = () => {
|
|||||||
label='Bank'
|
label='Bank'
|
||||||
value={selectedBank}
|
value={selectedBank}
|
||||||
onChange={bankChangeHandler}
|
onChange={bankChangeHandler}
|
||||||
|
onInputChange={bankInputValue}
|
||||||
|
onMenuScrollToBottom={bankLoadMore}
|
||||||
isClearable
|
isClearable
|
||||||
/>
|
/>
|
||||||
<SelectInput
|
<SelectInput
|
||||||
|
|||||||
@@ -113,20 +113,22 @@ const FormFinanceAdd = ({
|
|||||||
options: partyOptions,
|
options: partyOptions,
|
||||||
isLoadingOptions: isLoadingPartyOptions,
|
isLoadingOptions: isLoadingPartyOptions,
|
||||||
rawData: partyRawData,
|
rawData: partyRawData,
|
||||||
|
setInputValue: setPartyInputValue,
|
||||||
|
loadMore: loadMorePartyOptions,
|
||||||
} = useSelect<PartyCommonProps>(
|
} = useSelect<PartyCommonProps>(
|
||||||
formik.values.party_type_option?.value === 'CUSTOMER'
|
formik.values.party_type_option?.value === 'CUSTOMER'
|
||||||
? CustomerApi.basePath
|
? CustomerApi.basePath
|
||||||
: SupplierApi.basePath,
|
: SupplierApi.basePath,
|
||||||
'id',
|
'id',
|
||||||
'name',
|
'name'
|
||||||
'',
|
|
||||||
{ limit: 'limit' }
|
|
||||||
);
|
);
|
||||||
const {
|
const {
|
||||||
options: bankOptions,
|
options: bankOptions,
|
||||||
rawData: bankRawData,
|
rawData: bankRawData,
|
||||||
isLoadingOptions: isLoadingBankOptions,
|
isLoadingOptions: isLoadingBankOptions,
|
||||||
} = useSelect<Bank>(BankApi.basePath, 'id', 'name', '', { limit: 'limit' });
|
setInputValue: setBankInputValue,
|
||||||
|
loadMore: loadMoreBankOptions,
|
||||||
|
} = useSelect<Bank>(BankApi.basePath, 'id', 'name');
|
||||||
|
|
||||||
// ===== Helper Functions =====
|
// ===== Helper Functions =====
|
||||||
const transformFormValuesToPayload = (
|
const transformFormValuesToPayload = (
|
||||||
@@ -219,6 +221,8 @@ const FormFinanceAdd = ({
|
|||||||
placeholder={`Pilih ${formik.values.party_type_option?.value ? formatTitleCase(formik.values.party_type_option.value as string) : 'jenis transaksi dahulu'}`}
|
placeholder={`Pilih ${formik.values.party_type_option?.value ? formatTitleCase(formik.values.party_type_option.value as string) : 'jenis transaksi dahulu'}`}
|
||||||
options={partyOptions}
|
options={partyOptions}
|
||||||
value={formik.values.party_id_option}
|
value={formik.values.party_id_option}
|
||||||
|
onInputChange={setPartyInputValue}
|
||||||
|
onMenuScrollToBottom={loadMorePartyOptions}
|
||||||
onChange={(value) => {
|
onChange={(value) => {
|
||||||
formik.setFieldValue('party_id_option', value);
|
formik.setFieldValue('party_id_option', value);
|
||||||
if (isResponseSuccess(partyRawData) && value) {
|
if (isResponseSuccess(partyRawData) && value) {
|
||||||
@@ -304,6 +308,8 @@ const FormFinanceAdd = ({
|
|||||||
: []
|
: []
|
||||||
}
|
}
|
||||||
value={formik.values.bank_id_option}
|
value={formik.values.bank_id_option}
|
||||||
|
onInputChange={setBankInputValue}
|
||||||
|
onMenuScrollToBottom={loadMoreBankOptions}
|
||||||
onChange={(value) => {
|
onChange={(value) => {
|
||||||
formik.setFieldValue('bank_id_option', value);
|
formik.setFieldValue('bank_id_option', value);
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -104,21 +104,25 @@ const FormFinanceAddInitialBalance = ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
// ===== Options =====
|
// ===== Options =====
|
||||||
const { options: partyOptions, isLoadingOptions: isLoadingPartyOptions } =
|
const {
|
||||||
useSelect(
|
options: partyOptions,
|
||||||
|
isLoadingOptions: isLoadingPartyOptions,
|
||||||
|
setInputValue: setPartyInputValue,
|
||||||
|
loadMore: loadMorePartyOptions,
|
||||||
|
} = useSelect(
|
||||||
formik.values.party_type_option?.value === 'CUSTOMER'
|
formik.values.party_type_option?.value === 'CUSTOMER'
|
||||||
? CustomerApi.basePath
|
? CustomerApi.basePath
|
||||||
: SupplierApi.basePath,
|
: SupplierApi.basePath,
|
||||||
'id',
|
'id',
|
||||||
'name',
|
'name'
|
||||||
'',
|
|
||||||
{ limit: 'limit' }
|
|
||||||
);
|
);
|
||||||
const {
|
const {
|
||||||
options: bankOptions,
|
options: bankOptions,
|
||||||
rawData: bankRawData,
|
rawData: bankRawData,
|
||||||
isLoadingOptions: isLoadingBankOptions,
|
isLoadingOptions: isLoadingBankOptions,
|
||||||
} = useSelect<Bank>(BankApi.basePath, 'id', 'name', '', { limit: 'limit' });
|
setInputValue: setBankInputValue,
|
||||||
|
loadMore: loadMoreBankOptions,
|
||||||
|
} = useSelect<Bank>(BankApi.basePath, 'id', 'name');
|
||||||
|
|
||||||
// ===== Helper Functions =====
|
// ===== Helper Functions =====
|
||||||
const transformFormValuesToPayload = (
|
const transformFormValuesToPayload = (
|
||||||
@@ -189,6 +193,8 @@ const FormFinanceAddInitialBalance = ({
|
|||||||
placeholder='Pilih jenis pihak'
|
placeholder='Pilih jenis pihak'
|
||||||
options={FINANCE_PARTY_TYPE_OPTIONS}
|
options={FINANCE_PARTY_TYPE_OPTIONS}
|
||||||
value={formik.values.party_type_option}
|
value={formik.values.party_type_option}
|
||||||
|
onInputChange={setPartyInputValue}
|
||||||
|
onMenuScrollToBottom={loadMorePartyOptions}
|
||||||
onChange={(value) => {
|
onChange={(value) => {
|
||||||
formik.setFieldValue('party_type_option', value);
|
formik.setFieldValue('party_type_option', value);
|
||||||
formik.setFieldValue('party_id_option', null);
|
formik.setFieldValue('party_id_option', null);
|
||||||
@@ -218,6 +224,8 @@ const FormFinanceAddInitialBalance = ({
|
|||||||
placeholder={`Pilih ${formik.values.party_type_option?.value ? formatTitleCase(formik.values.party_type_option.value as string) : 'jenis pihak dahulu'}`}
|
placeholder={`Pilih ${formik.values.party_type_option?.value ? formatTitleCase(formik.values.party_type_option.value as string) : 'jenis pihak dahulu'}`}
|
||||||
options={partyOptions}
|
options={partyOptions}
|
||||||
value={formik.values.party_id_option}
|
value={formik.values.party_id_option}
|
||||||
|
onInputChange={setPartyInputValue}
|
||||||
|
onMenuScrollToBottom={loadMorePartyOptions}
|
||||||
onChange={(value) => {
|
onChange={(value) => {
|
||||||
formik.setFieldValue('party_id_option', value);
|
formik.setFieldValue('party_id_option', value);
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -80,7 +80,9 @@ const FormFinanceInjection = ({
|
|||||||
options: bankOptions,
|
options: bankOptions,
|
||||||
rawData: bankRawData,
|
rawData: bankRawData,
|
||||||
isLoadingOptions: isLoadingBankOptions,
|
isLoadingOptions: isLoadingBankOptions,
|
||||||
} = useSelect<Bank>(BankApi.basePath, 'id', 'name', '', { limit: 'limit' });
|
setInputValue: setBankInputValue,
|
||||||
|
loadMore: loadMoreBankOptions,
|
||||||
|
} = useSelect<Bank>(BankApi.basePath, 'id', 'name');
|
||||||
|
|
||||||
// ===== Helper Functions =====
|
// ===== Helper Functions =====
|
||||||
const transformFormValuesToPayload = (
|
const transformFormValuesToPayload = (
|
||||||
@@ -162,6 +164,8 @@ const FormFinanceInjection = ({
|
|||||||
: []
|
: []
|
||||||
}
|
}
|
||||||
value={formik.values.bank_id_option}
|
value={formik.values.bank_id_option}
|
||||||
|
onInputChange={setBankInputValue}
|
||||||
|
onMenuScrollToBottom={loadMoreBankOptions}
|
||||||
onChange={(value) => {
|
onChange={(value) => {
|
||||||
formik.setFieldValue('bank_id_option', value);
|
formik.setFieldValue('bank_id_option', value);
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -184,12 +184,16 @@ const MarketingTable = () => {
|
|||||||
const {
|
const {
|
||||||
options: productsOptions,
|
options: productsOptions,
|
||||||
isLoadingOptions: isLoadingProductsOptions,
|
isLoadingOptions: isLoadingProductsOptions,
|
||||||
|
setInputValue: setProductsInputValue,
|
||||||
|
loadMore: loadMoreProducts,
|
||||||
} = useSelect(ProductApi.basePath, 'id', 'name', '', {
|
} = useSelect(ProductApi.basePath, 'id', 'name', '', {
|
||||||
limit: 'limit',
|
limit: 'limit',
|
||||||
});
|
});
|
||||||
const {
|
const {
|
||||||
options: customersOptions,
|
options: customersOptions,
|
||||||
isLoadingOptions: isLoadingCustomersOptions,
|
isLoadingOptions: isLoadingCustomersOptions,
|
||||||
|
setInputValue: setCustomersInputValue,
|
||||||
|
loadMore: loadMoreCustomers,
|
||||||
} = useSelect(CustomerApi.basePath, 'id', 'name', '', {
|
} = useSelect(CustomerApi.basePath, 'id', 'name', '', {
|
||||||
limit: 'limit',
|
limit: 'limit',
|
||||||
});
|
});
|
||||||
@@ -400,6 +404,8 @@ const MarketingTable = () => {
|
|||||||
.join(',') || ''
|
.join(',') || ''
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
onInputChange={setProductsInputValue}
|
||||||
|
onMenuScrollToBottom={loadMoreProducts}
|
||||||
isMulti
|
isMulti
|
||||||
/>
|
/>
|
||||||
{/* select status */}
|
{/* select status */}
|
||||||
@@ -444,6 +450,8 @@ const MarketingTable = () => {
|
|||||||
(value as OptionType)?.value.toString() || ''
|
(value as OptionType)?.value.toString() || ''
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
onInputChange={setCustomersInputValue}
|
||||||
|
onMenuScrollToBottom={loadMoreCustomers}
|
||||||
/>
|
/>
|
||||||
</TableRowSizeSelector>
|
</TableRowSizeSelector>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ import RequirePermission from '@/components/helper/RequirePermission';
|
|||||||
import { Area } from '@/types/api/master-data/area';
|
import { Area } from '@/types/api/master-data/area';
|
||||||
import { AreaApi } from '@/services/api/master-data';
|
import { AreaApi } from '@/services/api/master-data';
|
||||||
import { cn } from '@/lib/helper';
|
import { cn } from '@/lib/helper';
|
||||||
import { isResponseSuccess } from '@/lib/api-helper';
|
import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
|
||||||
import { useTableFilter } from '@/services/hooks/useTableFilter';
|
import { useTableFilter } from '@/services/hooks/useTableFilter';
|
||||||
import { ROWS_OPTIONS } from '@/config/constant';
|
import { ROWS_OPTIONS } from '@/config/constant';
|
||||||
|
|
||||||
@@ -164,7 +164,14 @@ const AreasTable = () => {
|
|||||||
const confirmationModalDeleteClickHandler = async () => {
|
const confirmationModalDeleteClickHandler = async () => {
|
||||||
setIsDeleteLoading(true);
|
setIsDeleteLoading(true);
|
||||||
|
|
||||||
await AreaApi.delete(selectedArea?.id as number);
|
const deleteResponse = await AreaApi.delete(selectedArea?.id as number);
|
||||||
|
|
||||||
|
if (isResponseError(deleteResponse)) {
|
||||||
|
toast.error(deleteResponse.message);
|
||||||
|
setIsDeleteLoading(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
refreshAreas();
|
refreshAreas();
|
||||||
|
|
||||||
deleteModal.closeModal();
|
deleteModal.closeModal();
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ import RequirePermission from '@/components/helper/RequirePermission';
|
|||||||
import { Bank } from '@/types/api/master-data/bank';
|
import { Bank } from '@/types/api/master-data/bank';
|
||||||
import { BankApi } from '@/services/api/master-data';
|
import { BankApi } from '@/services/api/master-data';
|
||||||
import { cn } from '@/lib/helper';
|
import { cn } from '@/lib/helper';
|
||||||
import { isResponseSuccess } from '@/lib/api-helper';
|
import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
|
||||||
import { useTableFilter } from '@/services/hooks/useTableFilter';
|
import { useTableFilter } from '@/services/hooks/useTableFilter';
|
||||||
import { ROWS_OPTIONS } from '@/config/constant';
|
import { ROWS_OPTIONS } from '@/config/constant';
|
||||||
|
|
||||||
@@ -177,7 +177,14 @@ const BanksTable = () => {
|
|||||||
const confirmationModalDeleteClickHandler = async () => {
|
const confirmationModalDeleteClickHandler = async () => {
|
||||||
setIsDeleteLoading(true);
|
setIsDeleteLoading(true);
|
||||||
|
|
||||||
await BankApi.delete(selectedBank?.id as number);
|
const deleteResponse = await BankApi.delete(selectedBank?.id as number);
|
||||||
|
|
||||||
|
if (isResponseError(deleteResponse)) {
|
||||||
|
toast.error(deleteResponse.message);
|
||||||
|
setIsDeleteLoading(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
refreshBanks();
|
refreshBanks();
|
||||||
|
|
||||||
deleteModal.closeModal();
|
deleteModal.closeModal();
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import RowDropdownOptions from '@/components/table/RowDropdownOptions';
|
|||||||
import RowOptionsMenuWrapper from '@/components/table/RowOptionsMenuWrapper';
|
import RowOptionsMenuWrapper from '@/components/table/RowOptionsMenuWrapper';
|
||||||
import RequirePermission from '@/components/helper/RequirePermission';
|
import RequirePermission from '@/components/helper/RequirePermission';
|
||||||
import { ROWS_OPTIONS } from '@/config/constant';
|
import { ROWS_OPTIONS } from '@/config/constant';
|
||||||
import { isResponseSuccess } from '@/lib/api-helper';
|
import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
|
||||||
import { cn } from '@/lib/helper';
|
import { cn } from '@/lib/helper';
|
||||||
import { CustomerApi } from '@/services/api/master-data';
|
import { CustomerApi } from '@/services/api/master-data';
|
||||||
import { useTableFilter } from '@/services/hooks/useTableFilter';
|
import { useTableFilter } from '@/services/hooks/useTableFilter';
|
||||||
@@ -186,7 +186,16 @@ const CustomersTable = () => {
|
|||||||
const confirmationModalDeleteClickHandler = async () => {
|
const confirmationModalDeleteClickHandler = async () => {
|
||||||
setIsDeleteLoading(true);
|
setIsDeleteLoading(true);
|
||||||
|
|
||||||
await CustomerApi.delete(selectedCustomer?.id as number);
|
const deleteResponse = await CustomerApi.delete(
|
||||||
|
selectedCustomer?.id as number
|
||||||
|
);
|
||||||
|
|
||||||
|
if (isResponseError(deleteResponse)) {
|
||||||
|
toast.error(deleteResponse.message);
|
||||||
|
setIsDeleteLoading(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
refreshCustomers();
|
refreshCustomers();
|
||||||
|
|
||||||
deleteModal.closeModal();
|
deleteModal.closeModal();
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ import RequirePermission from '@/components/helper/RequirePermission';
|
|||||||
import { Fcr } from '@/types/api/master-data/fcr';
|
import { Fcr } from '@/types/api/master-data/fcr';
|
||||||
import { FcrApi } from '@/services/api/master-data';
|
import { FcrApi } from '@/services/api/master-data';
|
||||||
import { cn } from '@/lib/helper';
|
import { cn } from '@/lib/helper';
|
||||||
import { isResponseSuccess } from '@/lib/api-helper';
|
import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
|
||||||
import { useTableFilter } from '@/services/hooks/useTableFilter';
|
import { useTableFilter } from '@/services/hooks/useTableFilter';
|
||||||
import { ROWS_OPTIONS } from '@/config/constant';
|
import { ROWS_OPTIONS } from '@/config/constant';
|
||||||
|
|
||||||
@@ -164,7 +164,14 @@ const FcrsTable = () => {
|
|||||||
const confirmationModalDeleteClickHandler = async () => {
|
const confirmationModalDeleteClickHandler = async () => {
|
||||||
setIsDeleteLoading(true);
|
setIsDeleteLoading(true);
|
||||||
|
|
||||||
await FcrApi.delete(selectedFcr?.id as number);
|
const deleteResponse = await FcrApi.delete(selectedFcr?.id as number);
|
||||||
|
|
||||||
|
if (isResponseError(deleteResponse)) {
|
||||||
|
toast.error(deleteResponse.message);
|
||||||
|
setIsDeleteLoading(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
refreshFcrs();
|
refreshFcrs();
|
||||||
|
|
||||||
deleteModal.closeModal();
|
deleteModal.closeModal();
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ import DebouncedTextInput from '@/components/input/DebouncedTextInput';
|
|||||||
import SelectInput, { OptionType } from '@/components/input/SelectInput';
|
import SelectInput, { OptionType } from '@/components/input/SelectInput';
|
||||||
import { ROWS_OPTIONS } from '@/config/constant';
|
import { ROWS_OPTIONS } from '@/config/constant';
|
||||||
import Table from '@/components/Table';
|
import Table from '@/components/Table';
|
||||||
import { isResponseSuccess } from '@/lib/api-helper';
|
import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
|
||||||
import ConfirmationModal from '@/components/modal/ConfirmationModal';
|
import ConfirmationModal from '@/components/modal/ConfirmationModal';
|
||||||
|
|
||||||
const RowsOptions = ({
|
const RowsOptions = ({
|
||||||
@@ -182,7 +182,14 @@ const FlockTable = () => {
|
|||||||
const confirmationModalDeleteClickHandler = async () => {
|
const confirmationModalDeleteClickHandler = async () => {
|
||||||
setIsDeleteLoading(true);
|
setIsDeleteLoading(true);
|
||||||
|
|
||||||
await FlockApi.delete(selectedFlock?.id as number);
|
const deleteResponse = await FlockApi.delete(selectedFlock?.id as number);
|
||||||
|
|
||||||
|
if (isResponseError(deleteResponse)) {
|
||||||
|
toast.error(deleteResponse.message);
|
||||||
|
setIsDeleteLoading(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
refreshFlocks();
|
refreshFlocks();
|
||||||
|
|
||||||
deleteModal.closeModal();
|
deleteModal.closeModal();
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ import RequirePermission from '@/components/helper/RequirePermission';
|
|||||||
import { Kandang } from '@/types/api/master-data/kandang';
|
import { Kandang } from '@/types/api/master-data/kandang';
|
||||||
import { KandangApi } from '@/services/api/master-data';
|
import { KandangApi } from '@/services/api/master-data';
|
||||||
import { cn, formatNumber } from '@/lib/helper';
|
import { cn, formatNumber } from '@/lib/helper';
|
||||||
import { isResponseSuccess } from '@/lib/api-helper';
|
import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
|
||||||
import { useTableFilter } from '@/services/hooks/useTableFilter';
|
import { useTableFilter } from '@/services/hooks/useTableFilter';
|
||||||
import { ROWS_OPTIONS } from '@/config/constant';
|
import { ROWS_OPTIONS } from '@/config/constant';
|
||||||
|
|
||||||
@@ -199,7 +199,16 @@ const KandangsTable = () => {
|
|||||||
const confirmationModalDeleteClickHandler = async () => {
|
const confirmationModalDeleteClickHandler = async () => {
|
||||||
setIsDeleteLoading(true);
|
setIsDeleteLoading(true);
|
||||||
|
|
||||||
await KandangApi.delete(selectedKandang?.id as number);
|
const deleteResponse = await KandangApi.delete(
|
||||||
|
selectedKandang?.id as number
|
||||||
|
);
|
||||||
|
|
||||||
|
if (isResponseError(deleteResponse)) {
|
||||||
|
toast.error(deleteResponse.message);
|
||||||
|
setIsDeleteLoading(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
refreshKandangs();
|
refreshKandangs();
|
||||||
|
|
||||||
deleteModal.closeModal();
|
deleteModal.closeModal();
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ import RequirePermission from '@/components/helper/RequirePermission';
|
|||||||
import { Location } from '@/types/api/master-data/location';
|
import { Location } from '@/types/api/master-data/location';
|
||||||
import { LocationApi } from '@/services/api/master-data';
|
import { LocationApi } from '@/services/api/master-data';
|
||||||
import { cn } from '@/lib/helper';
|
import { cn } from '@/lib/helper';
|
||||||
import { isResponseSuccess } from '@/lib/api-helper';
|
import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
|
||||||
import { useTableFilter } from '@/services/hooks/useTableFilter';
|
import { useTableFilter } from '@/services/hooks/useTableFilter';
|
||||||
import { ROWS_OPTIONS } from '@/config/constant';
|
import { ROWS_OPTIONS } from '@/config/constant';
|
||||||
|
|
||||||
@@ -186,7 +186,16 @@ const LocationsTable = () => {
|
|||||||
const confirmationModalDeleteClickHandler = async () => {
|
const confirmationModalDeleteClickHandler = async () => {
|
||||||
setIsDeleteLoading(true);
|
setIsDeleteLoading(true);
|
||||||
|
|
||||||
await LocationApi.delete(selectedLocation?.id as number);
|
const deleteResponse = await LocationApi.delete(
|
||||||
|
selectedLocation?.id as number
|
||||||
|
);
|
||||||
|
|
||||||
|
if (isResponseError(deleteResponse)) {
|
||||||
|
toast.error(deleteResponse.message);
|
||||||
|
setIsDeleteLoading(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
refreshLocations();
|
refreshLocations();
|
||||||
|
|
||||||
deleteModal.closeModal();
|
deleteModal.closeModal();
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ import RequirePermission from '@/components/helper/RequirePermission';
|
|||||||
import { Nonstock } from '@/types/api/master-data/nonstock';
|
import { Nonstock } from '@/types/api/master-data/nonstock';
|
||||||
import { NonstockApi } from '@/services/api/master-data';
|
import { NonstockApi } from '@/services/api/master-data';
|
||||||
import { cn } from '@/lib/helper';
|
import { cn } from '@/lib/helper';
|
||||||
import { isResponseSuccess } from '@/lib/api-helper';
|
import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
|
||||||
import { useTableFilter } from '@/services/hooks/useTableFilter';
|
import { useTableFilter } from '@/services/hooks/useTableFilter';
|
||||||
import { ROWS_OPTIONS } from '@/config/constant';
|
import { ROWS_OPTIONS } from '@/config/constant';
|
||||||
|
|
||||||
@@ -198,7 +198,16 @@ const NonstocksTable = () => {
|
|||||||
const confirmationModalDeleteClickHandler = async () => {
|
const confirmationModalDeleteClickHandler = async () => {
|
||||||
setIsDeleteLoading(true);
|
setIsDeleteLoading(true);
|
||||||
|
|
||||||
await NonstockApi.delete(selectedNonstock?.id as number);
|
const deleteResponse = await NonstockApi.delete(
|
||||||
|
selectedNonstock?.id as number
|
||||||
|
);
|
||||||
|
|
||||||
|
if (isResponseError(deleteResponse)) {
|
||||||
|
toast.error(deleteResponse.message);
|
||||||
|
setIsDeleteLoading(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
refreshNonstocks();
|
refreshNonstocks();
|
||||||
|
|
||||||
deleteModal.closeModal();
|
deleteModal.closeModal();
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ import RequirePermission from '@/components/helper/RequirePermission';
|
|||||||
import { ProductCategory } from '@/types/api/master-data/product-category';
|
import { ProductCategory } from '@/types/api/master-data/product-category';
|
||||||
import { ProductCategoryApi } from '@/services/api/master-data';
|
import { ProductCategoryApi } from '@/services/api/master-data';
|
||||||
import { cn } from '@/lib/helper';
|
import { cn } from '@/lib/helper';
|
||||||
import { isResponseSuccess } from '@/lib/api-helper';
|
import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
|
||||||
import { useTableFilter } from '@/services/hooks/useTableFilter';
|
import { useTableFilter } from '@/services/hooks/useTableFilter';
|
||||||
import { ROWS_OPTIONS } from '@/config/constant';
|
import { ROWS_OPTIONS } from '@/config/constant';
|
||||||
|
|
||||||
@@ -170,7 +170,16 @@ const ProductCategoryTable = () => {
|
|||||||
const confirmationModalDeleteClickHandler = async () => {
|
const confirmationModalDeleteClickHandler = async () => {
|
||||||
setIsDeleteLoading(true);
|
setIsDeleteLoading(true);
|
||||||
|
|
||||||
await ProductCategoryApi.delete(selectedProductCategory?.id as number);
|
const deleteResponse = await ProductCategoryApi.delete(
|
||||||
|
selectedProductCategory?.id as number
|
||||||
|
);
|
||||||
|
|
||||||
|
if (isResponseError(deleteResponse)) {
|
||||||
|
toast.error(deleteResponse.message);
|
||||||
|
setIsDeleteLoading(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
refreshProductCategories();
|
refreshProductCategories();
|
||||||
|
|
||||||
deleteModal.closeModal();
|
deleteModal.closeModal();
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ import RequirePermission from '@/components/helper/RequirePermission';
|
|||||||
import { Product } from '@/types/api/master-data/product';
|
import { Product } from '@/types/api/master-data/product';
|
||||||
import { ProductApi } from '@/services/api/master-data';
|
import { ProductApi } from '@/services/api/master-data';
|
||||||
import { cn } from '@/lib/helper';
|
import { cn } from '@/lib/helper';
|
||||||
import { isResponseSuccess } from '@/lib/api-helper';
|
import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
|
||||||
import { useTableFilter } from '@/services/hooks/useTableFilter';
|
import { useTableFilter } from '@/services/hooks/useTableFilter';
|
||||||
import { ROWS_OPTIONS } from '@/config/constant';
|
import { ROWS_OPTIONS } from '@/config/constant';
|
||||||
|
|
||||||
@@ -230,8 +230,19 @@ const ProductsTable = () => {
|
|||||||
|
|
||||||
const confirmationModalDeleteClickHandler = async () => {
|
const confirmationModalDeleteClickHandler = async () => {
|
||||||
setIsDeleteLoading(true);
|
setIsDeleteLoading(true);
|
||||||
await ProductApi.delete(selectedProduct?.id as number);
|
|
||||||
|
const deleteResponse = await ProductApi.delete(
|
||||||
|
selectedProduct?.id as number
|
||||||
|
);
|
||||||
|
|
||||||
|
if (isResponseError(deleteResponse)) {
|
||||||
|
toast.error(deleteResponse.message);
|
||||||
|
setIsDeleteLoading(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
refreshProducts();
|
refreshProducts();
|
||||||
|
|
||||||
deleteModal.closeModal();
|
deleteModal.closeModal();
|
||||||
toast.success('Successfully delete Product!');
|
toast.success('Successfully delete Product!');
|
||||||
setIsDeleteLoading(false);
|
setIsDeleteLoading(false);
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import { ProductionStandard } from '@/types/api/master-data/production-standard'
|
|||||||
import { Icon } from '@iconify/react';
|
import { Icon } from '@iconify/react';
|
||||||
import useSWR from 'swr';
|
import useSWR from 'swr';
|
||||||
import { ProductionStandardApi } from '@/services/api/master-data';
|
import { ProductionStandardApi } from '@/services/api/master-data';
|
||||||
import { isResponseSuccess } from '@/lib/api-helper';
|
import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
|
||||||
import RowOptionsMenuWrapper from '@/components/table/RowOptionsMenuWrapper';
|
import RowOptionsMenuWrapper from '@/components/table/RowOptionsMenuWrapper';
|
||||||
import { CellContext } from '@tanstack/react-table';
|
import { CellContext } from '@tanstack/react-table';
|
||||||
import { useModal } from '@/components/Modal';
|
import { useModal } from '@/components/Modal';
|
||||||
@@ -94,9 +94,16 @@ const ProductionStandardTable = () => {
|
|||||||
const confirmationModalDeleteClickHandler = async () => {
|
const confirmationModalDeleteClickHandler = async () => {
|
||||||
setIsDeleteLoading(true);
|
setIsDeleteLoading(true);
|
||||||
|
|
||||||
await ProductionStandardApi.delete(
|
const deleteResponse = await ProductionStandardApi.delete(
|
||||||
selectedProductionStandard?.id as number
|
selectedProductionStandard?.id as number
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (isResponseError(deleteResponse)) {
|
||||||
|
toast.error(deleteResponse.message);
|
||||||
|
setIsDeleteLoading(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
refreshProductionStandards();
|
refreshProductionStandards();
|
||||||
|
|
||||||
deleteModal.closeModal();
|
deleteModal.closeModal();
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import RowDropdownOptions from '@/components/table/RowDropdownOptions';
|
|||||||
import RowOptionsMenuWrapper from '@/components/table/RowOptionsMenuWrapper';
|
import RowOptionsMenuWrapper from '@/components/table/RowOptionsMenuWrapper';
|
||||||
import RequirePermission from '@/components/helper/RequirePermission';
|
import RequirePermission from '@/components/helper/RequirePermission';
|
||||||
import { ROWS_OPTIONS } from '@/config/constant';
|
import { ROWS_OPTIONS } from '@/config/constant';
|
||||||
import { isResponseSuccess } from '@/lib/api-helper';
|
import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
|
||||||
import { cn } from '@/lib/helper';
|
import { cn } from '@/lib/helper';
|
||||||
import { SupplierApi } from '@/services/api/master-data';
|
import { SupplierApi } from '@/services/api/master-data';
|
||||||
import { useTableFilter } from '@/services/hooks/useTableFilter';
|
import { useTableFilter } from '@/services/hooks/useTableFilter';
|
||||||
@@ -205,7 +205,16 @@ const SuppliersTable = () => {
|
|||||||
const confirmationModalDeleteClickHandler = async () => {
|
const confirmationModalDeleteClickHandler = async () => {
|
||||||
setIsDeleteLoading(true);
|
setIsDeleteLoading(true);
|
||||||
|
|
||||||
await SupplierApi.delete(selectedSupplier?.id as number);
|
const deleteResponse = await SupplierApi.delete(
|
||||||
|
selectedSupplier?.id as number
|
||||||
|
);
|
||||||
|
|
||||||
|
if (isResponseError(deleteResponse)) {
|
||||||
|
toast.error(deleteResponse.message);
|
||||||
|
setIsDeleteLoading(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
refreshSuppliers();
|
refreshSuppliers();
|
||||||
|
|
||||||
deleteModal.closeModal();
|
deleteModal.closeModal();
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ import RequirePermission from '@/components/helper/RequirePermission';
|
|||||||
import { Uom } from '@/types/api/master-data/uom';
|
import { Uom } from '@/types/api/master-data/uom';
|
||||||
import { UomApi } from '@/services/api/master-data';
|
import { UomApi } from '@/services/api/master-data';
|
||||||
import { cn } from '@/lib/helper';
|
import { cn } from '@/lib/helper';
|
||||||
import { isResponseSuccess } from '@/lib/api-helper';
|
import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
|
||||||
import { useTableFilter } from '@/services/hooks/useTableFilter';
|
import { useTableFilter } from '@/services/hooks/useTableFilter';
|
||||||
import { ROWS_OPTIONS } from '@/config/constant';
|
import { ROWS_OPTIONS } from '@/config/constant';
|
||||||
|
|
||||||
@@ -164,7 +164,14 @@ const UomsTable = () => {
|
|||||||
const confirmationModalDeleteClickHandler = async () => {
|
const confirmationModalDeleteClickHandler = async () => {
|
||||||
setIsDeleteLoading(true);
|
setIsDeleteLoading(true);
|
||||||
|
|
||||||
await UomApi.delete(selectedUom?.id as number);
|
const deleteResponse = await UomApi.delete(selectedUom?.id as number);
|
||||||
|
|
||||||
|
if (isResponseError(deleteResponse)) {
|
||||||
|
toast.error(deleteResponse.message);
|
||||||
|
setIsDeleteLoading(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
refreshUoms();
|
refreshUoms();
|
||||||
|
|
||||||
deleteModal.closeModal();
|
deleteModal.closeModal();
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ import RequirePermission from '@/components/helper/RequirePermission';
|
|||||||
import { Warehouse } from '@/types/api/master-data/warehouse';
|
import { Warehouse } from '@/types/api/master-data/warehouse';
|
||||||
import { WarehouseApi } from '@/services/api/master-data';
|
import { WarehouseApi } from '@/services/api/master-data';
|
||||||
import { cn } from '@/lib/helper';
|
import { cn } from '@/lib/helper';
|
||||||
import { isResponseSuccess } from '@/lib/api-helper';
|
import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
|
||||||
import { useTableFilter } from '@/services/hooks/useTableFilter';
|
import { useTableFilter } from '@/services/hooks/useTableFilter';
|
||||||
import { ROWS_OPTIONS } from '@/config/constant';
|
import { ROWS_OPTIONS } from '@/config/constant';
|
||||||
|
|
||||||
@@ -220,7 +220,16 @@ const WarehousesTable = () => {
|
|||||||
const confirmationModalDeleteClickHandler = async () => {
|
const confirmationModalDeleteClickHandler = async () => {
|
||||||
setIsDeleteLoading(true);
|
setIsDeleteLoading(true);
|
||||||
|
|
||||||
await WarehouseApi.delete(selectedWarehouse?.id as number);
|
const deleteResponse = await WarehouseApi.delete(
|
||||||
|
selectedWarehouse?.id as number
|
||||||
|
);
|
||||||
|
|
||||||
|
if (isResponseError(deleteResponse)) {
|
||||||
|
toast.error(deleteResponse.message);
|
||||||
|
setIsDeleteLoading(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
refreshWarehouses();
|
refreshWarehouses();
|
||||||
|
|
||||||
deleteModal.closeModal();
|
deleteModal.closeModal();
|
||||||
|
|||||||
@@ -5,7 +5,10 @@ import Button from '@/components/Button';
|
|||||||
import FloatingActionsButton from '@/components/FloatingActionsButton';
|
import FloatingActionsButton from '@/components/FloatingActionsButton';
|
||||||
import CheckboxInput from '@/components/input/CheckboxInput';
|
import CheckboxInput from '@/components/input/CheckboxInput';
|
||||||
import DebouncedTextInput from '@/components/input/DebouncedTextInput';
|
import DebouncedTextInput from '@/components/input/DebouncedTextInput';
|
||||||
import SelectInput, { OptionType } from '@/components/input/SelectInput';
|
import SelectInput, {
|
||||||
|
OptionType,
|
||||||
|
useSelect,
|
||||||
|
} from '@/components/input/SelectInput';
|
||||||
import { useModal } from '@/components/Modal';
|
import { useModal } from '@/components/Modal';
|
||||||
import ConfirmationModal from '@/components/modal/ConfirmationModal';
|
import ConfirmationModal from '@/components/modal/ConfirmationModal';
|
||||||
import ConfirmationModalWithNotes from '@/components/modal/ConfirmationModalWithNotes';
|
import ConfirmationModalWithNotes from '@/components/modal/ConfirmationModalWithNotes';
|
||||||
@@ -59,9 +62,6 @@ const ProjectFlockTable = ({ refresh }: { refresh?: () => void }) => {
|
|||||||
const selectedRowIds = Object.keys(rowSelection)
|
const selectedRowIds = Object.keys(rowSelection)
|
||||||
.filter((id) => rowSelection[id])
|
.filter((id) => rowSelection[id])
|
||||||
.map((id) => parseInt(id));
|
.map((id) => parseInt(id));
|
||||||
const [locationSelectInputValue, setLocationSelectInputValue] = useState('');
|
|
||||||
const [areaSelectInputValue, setAreaSelectInputValue] = useState('');
|
|
||||||
const [kandangSelectInputValue, setKandangSelectInputValue] = useState('');
|
|
||||||
const [selectedArea, setSelectedArea] = useState<OptionType | null>(null);
|
const [selectedArea, setSelectedArea] = useState<OptionType | null>(null);
|
||||||
const [selectedLocation, setSelectedLocation] = useState<OptionType | null>(
|
const [selectedLocation, setSelectedLocation] = useState<OptionType | null>(
|
||||||
null
|
null
|
||||||
@@ -90,55 +90,25 @@ const ProjectFlockTable = ({ refresh }: { refresh?: () => void }) => {
|
|||||||
{ revalidateOnMount: true }
|
{ revalidateOnMount: true }
|
||||||
);
|
);
|
||||||
|
|
||||||
const areaUrl = `${AreaApi.basePath}?${new URLSearchParams({
|
// ===== Fetch Data Select =====
|
||||||
search: areaSelectInputValue,
|
const {
|
||||||
limit: '100',
|
options: optionsArea,
|
||||||
}).toString()}`;
|
isLoadingOptions: isLoadingArea,
|
||||||
const { data: areas, isLoading: isLoadingAreas } = useSWR(
|
setInputValue: setAreaSelectInputValue,
|
||||||
areaUrl,
|
loadMore: loadMoreArea,
|
||||||
AreaApi.getAllFetcher
|
} = useSelect(AreaApi.basePath, 'id', 'name');
|
||||||
);
|
const {
|
||||||
|
options: optionsLocation,
|
||||||
const locationUrl = `${LocationApi.basePath}?${new URLSearchParams({
|
isLoadingOptions: isLoadingLocation,
|
||||||
search: locationSelectInputValue,
|
setInputValue: setLocationSelectInputValue,
|
||||||
area_id: selectedArea != null ? selectedArea.value.toString() : '',
|
loadMore: loadMoreLocation,
|
||||||
limit: '100',
|
} = useSelect(LocationApi.basePath, 'id', 'name');
|
||||||
}).toString()}`;
|
const {
|
||||||
const { data: locations, isLoading: isLoadingLocations } = useSWR(
|
options: optionsKandang,
|
||||||
locationUrl,
|
isLoadingOptions: isLoadingKandang,
|
||||||
LocationApi.getAllFetcher
|
setInputValue: setKandangSelectInputValue,
|
||||||
);
|
loadMore: loadMoreKandang,
|
||||||
|
} = useSelect(KandangApi.basePath, 'id', 'name');
|
||||||
const kandangUrl = `${KandangApi.basePath}?${new URLSearchParams({
|
|
||||||
search: kandangSelectInputValue,
|
|
||||||
location_id:
|
|
||||||
selectedLocation != null ? selectedLocation.value.toString() : '',
|
|
||||||
limit: '100',
|
|
||||||
}).toString()}`;
|
|
||||||
const { data: kandangs, isLoading: isLoadingKandang } = useSWR(
|
|
||||||
kandangUrl,
|
|
||||||
KandangApi.getAllFetcher
|
|
||||||
);
|
|
||||||
|
|
||||||
// ===== Data to Options Mapping ======
|
|
||||||
const optionsArea = isResponseSuccess(areas)
|
|
||||||
? areas?.data.map((area) => ({
|
|
||||||
value: area.id,
|
|
||||||
label: area.name,
|
|
||||||
}))
|
|
||||||
: [];
|
|
||||||
const optionsKandang = isResponseSuccess(kandangs)
|
|
||||||
? kandangs?.data.map((kandang) => ({
|
|
||||||
value: kandang.id,
|
|
||||||
label: kandang.name,
|
|
||||||
}))
|
|
||||||
: [];
|
|
||||||
const optionsLocation = isResponseSuccess(locations)
|
|
||||||
? locations?.data.map((location) => ({
|
|
||||||
value: location.id,
|
|
||||||
label: location.name,
|
|
||||||
}))
|
|
||||||
: [];
|
|
||||||
|
|
||||||
// ====== HANDLER ======
|
// ====== HANDLER ======
|
||||||
const confirmationModalDeleteClickHandler = async () => {
|
const confirmationModalDeleteClickHandler = async () => {
|
||||||
@@ -385,7 +355,7 @@ const ProjectFlockTable = ({ refresh }: { refresh?: () => void }) => {
|
|||||||
<SelectInput
|
<SelectInput
|
||||||
label='Area'
|
label='Area'
|
||||||
options={optionsArea}
|
options={optionsArea}
|
||||||
isLoading={isLoadingAreas}
|
isLoading={isLoadingArea}
|
||||||
value={selectedArea}
|
value={selectedArea}
|
||||||
onChange={(val) => {
|
onChange={(val) => {
|
||||||
setSelectedArea(val as OptionType);
|
setSelectedArea(val as OptionType);
|
||||||
@@ -395,12 +365,13 @@ const ProjectFlockTable = ({ refresh }: { refresh?: () => void }) => {
|
|||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
onInputChange={setAreaSelectInputValue}
|
onInputChange={setAreaSelectInputValue}
|
||||||
|
onMenuScrollToBottom={loadMoreArea}
|
||||||
isClearable
|
isClearable
|
||||||
/>
|
/>
|
||||||
<SelectInput
|
<SelectInput
|
||||||
label='Lokasi'
|
label='Lokasi'
|
||||||
options={optionsLocation}
|
options={optionsLocation}
|
||||||
isLoading={isLoadingLocations}
|
isLoading={isLoadingLocation}
|
||||||
value={selectedLocation}
|
value={selectedLocation}
|
||||||
onChange={(val) => {
|
onChange={(val) => {
|
||||||
setSelectedLocation(val as OptionType);
|
setSelectedLocation(val as OptionType);
|
||||||
@@ -410,6 +381,7 @@ const ProjectFlockTable = ({ refresh }: { refresh?: () => void }) => {
|
|||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
onInputChange={setLocationSelectInputValue}
|
onInputChange={setLocationSelectInputValue}
|
||||||
|
onMenuScrollToBottom={loadMoreLocation}
|
||||||
isClearable
|
isClearable
|
||||||
/>
|
/>
|
||||||
<SelectInput
|
<SelectInput
|
||||||
@@ -425,6 +397,7 @@ const ProjectFlockTable = ({ refresh }: { refresh?: () => void }) => {
|
|||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
onInputChange={setKandangSelectInputValue}
|
onInputChange={setKandangSelectInputValue}
|
||||||
|
onMenuScrollToBottom={loadMoreKandang}
|
||||||
isClearable
|
isClearable
|
||||||
/>
|
/>
|
||||||
<DebouncedTextInput
|
<DebouncedTextInput
|
||||||
|
|||||||
@@ -557,15 +557,12 @@ const ProjectFlockForm = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const onDeleteBudgetRowHandler = (nonstock_id: number, index?: number) => {
|
const onDeleteBudgetRowHandler = (nonstock_id: number, index?: number) => {
|
||||||
console.log(`nonstock_id: ${nonstock_id}, index: ${index}`);
|
|
||||||
if (!nonstock_id) {
|
if (!nonstock_id) {
|
||||||
const updatedBudgets = formik.values.project_budgets
|
const updatedBudgets = formik.values.project_budgets
|
||||||
.map((budget, i) => {
|
.map((budget, i) => {
|
||||||
if (i == index) {
|
if (i == index) {
|
||||||
console.log(`buget: ${null}, index: ${index}, i: ${i}`);
|
|
||||||
return null;
|
return null;
|
||||||
} else {
|
} else {
|
||||||
console.log(`buget: ${budget}, index: ${index}, i: ${i}`);
|
|
||||||
return budget;
|
return budget;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { RefObject } from 'react';
|
|||||||
import useSWR from 'swr';
|
import useSWR from 'swr';
|
||||||
import { Icon } from '@iconify/react';
|
import { Icon } from '@iconify/react';
|
||||||
import { SortingState, CellContext } from '@tanstack/react-table';
|
import { SortingState, CellContext } from '@tanstack/react-table';
|
||||||
import { cn, formatDate } from '@/lib/helper';
|
import { cn, formatDate, formatNumber } from '@/lib/helper';
|
||||||
import RequirePermission from '@/components/helper/RequirePermission';
|
import RequirePermission from '@/components/helper/RequirePermission';
|
||||||
import { useModal } from '@/components/Modal';
|
import { useModal } from '@/components/Modal';
|
||||||
import Modal from '@/components/Modal';
|
import Modal from '@/components/Modal';
|
||||||
@@ -656,20 +656,30 @@ const RecordingTable = () => {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
cell: ({ row }) => {
|
cell: ({ row }) => {
|
||||||
|
const recording = row.original;
|
||||||
|
const isDisabled = isRecordingApproved(recording);
|
||||||
|
|
||||||
|
const handleToggleSelection = (e: unknown) => {
|
||||||
|
if (!isDisabled) {
|
||||||
|
row.getToggleSelectedHandler()(e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div className={cn({ 'opacity-50': isDisabled })}>
|
||||||
<CheckboxInput
|
<CheckboxInput
|
||||||
name='row'
|
name='row'
|
||||||
checked={row.getIsSelected()}
|
checked={row.getIsSelected()}
|
||||||
indeterminate={row.getIsSomeSelected()}
|
indeterminate={row.getIsSomeSelected()}
|
||||||
onChange={row.getToggleSelectedHandler()}
|
onChange={handleToggleSelection}
|
||||||
|
disabled={isDisabled}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
header: '#',
|
header: 'No',
|
||||||
cell: (props) =>
|
cell: (props) =>
|
||||||
tableFilterState.pageSize * (tableFilterState.page - 1) +
|
tableFilterState.pageSize * (tableFilterState.page - 1) +
|
||||||
props.row.index +
|
props.row.index +
|
||||||
@@ -680,6 +690,10 @@ const RecordingTable = () => {
|
|||||||
cell: (props) =>
|
cell: (props) =>
|
||||||
props.row.original.project_flock?.flock_name || '-',
|
props.row.original.project_flock?.flock_name || '-',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
header: 'Periode',
|
||||||
|
cell: (props) => props.row.original.project_flock?.period || '-',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
header: 'Kategori',
|
header: 'Kategori',
|
||||||
cell: (props) => {
|
cell: (props) => {
|
||||||
@@ -696,7 +710,21 @@ const RecordingTable = () => {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
header: 'Umur (hari)',
|
header: 'Umur (hari)',
|
||||||
cell: (props) => props.row.original.day,
|
cell: (props) => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<span>
|
||||||
|
{props.row.original.day} (Minggu ke-
|
||||||
|
{props.row.original.project_flock.production_standart.week})
|
||||||
|
</span>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: 'warehouse.name',
|
||||||
|
header: 'Gudang',
|
||||||
|
cell: (props) => props.row.original.warehouse?.name,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
accessorKey: 'record_date',
|
accessorKey: 'record_date',
|
||||||
@@ -705,10 +733,263 @@ const RecordingTable = () => {
|
|||||||
formatDate(props.row.original.record_datetime, 'DD MMMM YYYY'),
|
formatDate(props.row.original.record_datetime, 'DD MMMM YYYY'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
header: 'Populasi Awal',
|
header: 'Populasi Akhir',
|
||||||
cell: (props) =>
|
cell: (props) =>
|
||||||
props.row.original.project_flock?.total_chick_qty?.toLocaleString() ||
|
props.row.original.project_flock?.total_chick_qty != null
|
||||||
'-',
|
? formatNumber(props.row.original.project_flock.total_chick_qty)
|
||||||
|
: '-',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'fcr',
|
||||||
|
header: 'FCR',
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
id: 'fcr_actual',
|
||||||
|
header: 'Actual',
|
||||||
|
cell: (props) => {
|
||||||
|
const value = props.row.original.fcr_value;
|
||||||
|
return (
|
||||||
|
<div className='text-center'>
|
||||||
|
{value !== null && value !== undefined
|
||||||
|
? formatNumber(value)
|
||||||
|
: '-'}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'fcr_standard',
|
||||||
|
header: 'Standard',
|
||||||
|
cell: (props) => {
|
||||||
|
const value = props.row.original.project_flock?.fcr?.fcr_std;
|
||||||
|
return (
|
||||||
|
<div className='text-center text-gray-600'>
|
||||||
|
{value !== null && value !== undefined
|
||||||
|
? formatNumber(value)
|
||||||
|
: '-'}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'feed_intake',
|
||||||
|
header: 'Feed Intake (KG)',
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
id: 'feed_intake_actual',
|
||||||
|
header: 'Actual',
|
||||||
|
cell: (props) => {
|
||||||
|
const value = props.row.original.feed_intake;
|
||||||
|
return (
|
||||||
|
<div className='text-center'>
|
||||||
|
{value !== null && value !== undefined
|
||||||
|
? formatNumber(value)
|
||||||
|
: '-'}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'feed_intake_standard',
|
||||||
|
header: 'Standard',
|
||||||
|
cell: (props) => {
|
||||||
|
const value =
|
||||||
|
props.row.original.project_flock?.production_standart
|
||||||
|
?.feed_intake_std;
|
||||||
|
return (
|
||||||
|
<div className='text-center text-gray-600'>
|
||||||
|
{value !== null && value !== undefined
|
||||||
|
? formatNumber(value)
|
||||||
|
: '-'}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'mortality',
|
||||||
|
header: 'Mortality',
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
id: 'cum_depletion_rate_actual',
|
||||||
|
header: 'Cum Depletion Rate',
|
||||||
|
cell: (props) => {
|
||||||
|
const value = props.row.original.cum_depletion_rate;
|
||||||
|
return (
|
||||||
|
<div className='text-center'>
|
||||||
|
{value !== null && value !== undefined
|
||||||
|
? `${value.toFixed(2)}%`
|
||||||
|
: '-'}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'max_depletion_std',
|
||||||
|
header: 'Max Depletion Std',
|
||||||
|
cell: (props) => {
|
||||||
|
const value =
|
||||||
|
props.row.original.project_flock?.production_standart
|
||||||
|
?.max_depletion_std;
|
||||||
|
return (
|
||||||
|
<div className='text-center text-gray-600'>
|
||||||
|
{value !== null && value !== undefined
|
||||||
|
? `${value.toFixed(2)}%`
|
||||||
|
: '-'}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'total_depletion',
|
||||||
|
header: 'Total Depletion',
|
||||||
|
cell: (props) => {
|
||||||
|
const value = props.row.original.total_depletion_qty;
|
||||||
|
return (
|
||||||
|
<div className='text-center'>
|
||||||
|
{value !== null && value !== undefined
|
||||||
|
? formatNumber(value)
|
||||||
|
: '-'}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'egg_production',
|
||||||
|
header: 'Egg Production',
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
id: 'egg_mass_actual',
|
||||||
|
header: 'Egg Mass Actual',
|
||||||
|
cell: (props) => {
|
||||||
|
const value = props.row.original.egg_mass;
|
||||||
|
return (
|
||||||
|
<div className='text-center'>
|
||||||
|
{value !== null && value !== undefined
|
||||||
|
? formatNumber(value)
|
||||||
|
: '-'}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'egg_mass_standard',
|
||||||
|
header: 'Egg Mass Standar',
|
||||||
|
cell: (props) => {
|
||||||
|
const value =
|
||||||
|
props.row.original.project_flock?.production_standart
|
||||||
|
?.egg_mass_std;
|
||||||
|
return (
|
||||||
|
<div className='text-center text-gray-600'>
|
||||||
|
{value !== null && value !== undefined
|
||||||
|
? formatNumber(value)
|
||||||
|
: '-'}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'egg_weight_actual',
|
||||||
|
header: 'Egg Weight Actual',
|
||||||
|
cell: (props) => {
|
||||||
|
const value = props.row.original.egg_weight;
|
||||||
|
return (
|
||||||
|
<div className='text-center'>
|
||||||
|
{value !== null && value !== undefined
|
||||||
|
? formatNumber(value)
|
||||||
|
: '-'}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'egg_weight_standard',
|
||||||
|
header: 'Egg Weight Standar',
|
||||||
|
cell: (props) => {
|
||||||
|
const value =
|
||||||
|
props.row.original.project_flock?.production_standart
|
||||||
|
?.egg_weight_std;
|
||||||
|
return (
|
||||||
|
<div className='text-center text-gray-600'>
|
||||||
|
{value !== null && value !== undefined
|
||||||
|
? formatNumber(value)
|
||||||
|
: '-'}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'hen_performance',
|
||||||
|
header: 'Hen Performance',
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
id: 'hen_day_actual',
|
||||||
|
header: 'Hen Day Actual',
|
||||||
|
cell: (props) => {
|
||||||
|
const value = props.row.original.hen_day;
|
||||||
|
return (
|
||||||
|
<div className='text-center'>
|
||||||
|
{value !== null && value !== undefined
|
||||||
|
? `${value.toFixed(2)}%`
|
||||||
|
: '-'}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'hen_day_standard',
|
||||||
|
header: 'Hen Day Standar',
|
||||||
|
cell: (props) => {
|
||||||
|
const value =
|
||||||
|
props.row.original.project_flock?.production_standart
|
||||||
|
?.hen_day_std;
|
||||||
|
return (
|
||||||
|
<div className='text-center text-gray-600'>
|
||||||
|
{value !== null && value !== undefined
|
||||||
|
? `${value.toFixed(2)}%`
|
||||||
|
: '-'}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'hen_house_actual',
|
||||||
|
header: 'Hen House Actual',
|
||||||
|
cell: (props) => {
|
||||||
|
const value = props.row.original.hen_house;
|
||||||
|
return (
|
||||||
|
<div className='text-center'>
|
||||||
|
{value !== null && value !== undefined
|
||||||
|
? `${value.toFixed(2)}%`
|
||||||
|
: '-'}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'hen_house_standard',
|
||||||
|
header: 'Hen House Standar',
|
||||||
|
cell: (props) => {
|
||||||
|
const value =
|
||||||
|
props.row.original.project_flock?.production_standart
|
||||||
|
?.hen_house_std;
|
||||||
|
return (
|
||||||
|
<div className='text-center text-gray-600'>
|
||||||
|
{value !== null && value !== undefined
|
||||||
|
? `${value.toFixed(2)}%`
|
||||||
|
: '-'}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
header: 'Status Approval',
|
header: 'Status Approval',
|
||||||
@@ -874,14 +1155,15 @@ const RecordingTable = () => {
|
|||||||
'mb-20':
|
'mb-20':
|
||||||
isResponseSuccess(recordings) && recordings?.data?.length === 0,
|
isResponseSuccess(recordings) && recordings?.data?.length === 0,
|
||||||
}),
|
}),
|
||||||
tableWrapperClassName: 'overflow-x-auto min-h-full!',
|
tableWrapperClassName: 'overflow-x-auto',
|
||||||
tableClassName: 'font-inter w-full table-auto min-h-full!',
|
tableClassName: 'w-full table-auto text-sm',
|
||||||
headerRowClassName: 'border-b border-b-gray-200',
|
headerRowClassName: 'border-b border-b-gray-200',
|
||||||
headerColumnClassName:
|
headerColumnClassName:
|
||||||
'px-6 py-3 text-xs font-semibold text-gray-500 last:flex last:flex-row last:justify-end',
|
'px-4 py-3 text-xs font-semibold text-gray-500 whitespace-nowrap border-l border-l-gray-200 border-r border-r-gray-200 border-t border-t-gray-200 border-gray-200 border-b-0',
|
||||||
bodyRowClassName: 'border-b border-b-gray-200',
|
bodyRowClassName:
|
||||||
|
'hover:bg-gray-50 transition-colors border-b border-gray-200 first:border-t first:border-t-gray-200 border-l border-l-gray-200 border-r border-r-gray-200',
|
||||||
bodyColumnClassName:
|
bodyColumnClassName:
|
||||||
'px-6 py-3 last:flex last:flex-row last:justify-end',
|
'px-4 py-3 text-xs text-gray-900 whitespace-nowrap',
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import {
|
|||||||
} from '@/types/api/production/recording';
|
} from '@/types/api/production/recording';
|
||||||
|
|
||||||
type RecordingGrowingFormSchemaType = {
|
type RecordingGrowingFormSchemaType = {
|
||||||
|
record_date: string;
|
||||||
project_flock_kandang: {
|
project_flock_kandang: {
|
||||||
value: number;
|
value: number;
|
||||||
label: string;
|
label: string;
|
||||||
@@ -85,6 +86,9 @@ const EggObjectSchema: Yup.ObjectSchema<EggSchema> = Yup.object({
|
|||||||
|
|
||||||
export const RecordingGrowingFormSchema: Yup.ObjectSchema<RecordingGrowingFormSchemaType> =
|
export const RecordingGrowingFormSchema: Yup.ObjectSchema<RecordingGrowingFormSchemaType> =
|
||||||
Yup.object({
|
Yup.object({
|
||||||
|
record_date: Yup.string()
|
||||||
|
.required('Tanggal recording wajib diisi!')
|
||||||
|
.typeError('Tanggal recording wajib diisi!'),
|
||||||
project_flock_kandang: Yup.object({
|
project_flock_kandang: Yup.object({
|
||||||
value: Yup.number().min(1).required(),
|
value: Yup.number().min(1).required(),
|
||||||
label: Yup.string().required(),
|
label: Yup.string().required(),
|
||||||
@@ -179,6 +183,9 @@ type RecordingFormData = Partial<Recording> & {
|
|||||||
export const getRecordingGrowingFormInitialValues = (
|
export const getRecordingGrowingFormInitialValues = (
|
||||||
initialValues?: RecordingFormData
|
initialValues?: RecordingFormData
|
||||||
): RecordingGrowingFormValues => ({
|
): RecordingGrowingFormValues => ({
|
||||||
|
record_date: initialValues?.record_datetime
|
||||||
|
? new Date(initialValues.record_datetime).toISOString().split('T')[0]
|
||||||
|
: new Date().toISOString().split('T')[0],
|
||||||
project_flock_kandang: initialValues?.project_flock_kandang_id
|
project_flock_kandang: initialValues?.project_flock_kandang_id
|
||||||
? {
|
? {
|
||||||
value: initialValues.project_flock_kandang_id,
|
value: initialValues.project_flock_kandang_id,
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -179,12 +179,16 @@ const TransferToLayingsTable = () => {
|
|||||||
setInputValue: setFlockSourceInputValue,
|
setInputValue: setFlockSourceInputValue,
|
||||||
options: flockSourceOptions,
|
options: flockSourceOptions,
|
||||||
isLoadingOptions: isLoadingFlockSourceOptions,
|
isLoadingOptions: isLoadingFlockSourceOptions,
|
||||||
|
loadMore: loadMoreFlockSource,
|
||||||
|
hasMore: hasMoreFlockSource,
|
||||||
} = useSelect<Flock>(FlockApi.basePath, 'id', 'name');
|
} = useSelect<Flock>(FlockApi.basePath, 'id', 'name');
|
||||||
|
|
||||||
const {
|
const {
|
||||||
setInputValue: setFlockDestinationInputValue,
|
setInputValue: setFlockDestinationInputValue,
|
||||||
options: flockDestinationOptions,
|
options: flockDestinationOptions,
|
||||||
isLoadingOptions: isLoadingFlockDestinationOptions,
|
isLoadingOptions: isLoadingFlockDestinationOptions,
|
||||||
|
loadMore: loadMoreFlockDestination,
|
||||||
|
hasMore: hasMoreFlockDestination,
|
||||||
} = useSelect<Flock>(FlockApi.basePath, 'id', 'name');
|
} = useSelect<Flock>(FlockApi.basePath, 'id', 'name');
|
||||||
|
|
||||||
// Flocks value
|
// Flocks value
|
||||||
@@ -595,6 +599,7 @@ const TransferToLayingsTable = () => {
|
|||||||
value={selectedFlockSource}
|
value={selectedFlockSource}
|
||||||
onChange={flockSourceChangeHandler}
|
onChange={flockSourceChangeHandler}
|
||||||
onInputChange={setFlockSourceInputValue}
|
onInputChange={setFlockSourceInputValue}
|
||||||
|
onMenuScrollToBottom={loadMoreFlockSource}
|
||||||
isClearable
|
isClearable
|
||||||
className={{
|
className={{
|
||||||
wrapper: 'col-span-12 sm:col-span-3',
|
wrapper: 'col-span-12 sm:col-span-3',
|
||||||
@@ -608,6 +613,7 @@ const TransferToLayingsTable = () => {
|
|||||||
value={selectedFlockDestination}
|
value={selectedFlockDestination}
|
||||||
onChange={flockDestinationChangeHandler}
|
onChange={flockDestinationChangeHandler}
|
||||||
onInputChange={setFlockDestinationInputValue}
|
onInputChange={setFlockDestinationInputValue}
|
||||||
|
onMenuScrollToBottom={loadMoreFlockDestination}
|
||||||
isClearable
|
isClearable
|
||||||
className={{
|
className={{
|
||||||
wrapper: 'col-span-12 sm:col-span-3',
|
wrapper: 'col-span-12 sm:col-span-3',
|
||||||
|
|||||||
@@ -270,6 +270,8 @@ const TransferToLayingForm = ({
|
|||||||
options: flockSourceOptions,
|
options: flockSourceOptions,
|
||||||
isLoadingOptions: isLoadingFlockSourceOptions,
|
isLoadingOptions: isLoadingFlockSourceOptions,
|
||||||
rawData: flockSources,
|
rawData: flockSources,
|
||||||
|
loadMore: loadMoreFlockSource,
|
||||||
|
hasMore: hasMoreFlockSource,
|
||||||
} = useSelect<ProjectFlock>(
|
} = useSelect<ProjectFlock>(
|
||||||
'/production/project-flocks',
|
'/production/project-flocks',
|
||||||
'id',
|
'id',
|
||||||
@@ -360,6 +362,8 @@ const TransferToLayingForm = ({
|
|||||||
options: flockDestinationOptions,
|
options: flockDestinationOptions,
|
||||||
isLoadingOptions: isLoadingFlockDestinationOptions,
|
isLoadingOptions: isLoadingFlockDestinationOptions,
|
||||||
rawData: flockDestinations,
|
rawData: flockDestinations,
|
||||||
|
loadMore: loadMoreFlockDestination,
|
||||||
|
hasMore: hasMoreFlockDestination,
|
||||||
} = useSelect<ProjectFlock>(
|
} = useSelect<ProjectFlock>(
|
||||||
'/production/project-flocks',
|
'/production/project-flocks',
|
||||||
'id',
|
'id',
|
||||||
@@ -573,6 +577,7 @@ const TransferToLayingForm = ({
|
|||||||
onChange={flockSourceChangeHandler}
|
onChange={flockSourceChangeHandler}
|
||||||
isLoading={isLoadingFlockSourceOptions}
|
isLoading={isLoadingFlockSourceOptions}
|
||||||
onInputChange={setFlockSourceInputValue}
|
onInputChange={setFlockSourceInputValue}
|
||||||
|
onMenuScrollToBottom={loadMoreFlockSource}
|
||||||
isError={
|
isError={
|
||||||
formik.touched.flockSource &&
|
formik.touched.flockSource &&
|
||||||
Boolean(typeof formik.errors.flockSource === 'string')
|
Boolean(typeof formik.errors.flockSource === 'string')
|
||||||
@@ -591,6 +596,7 @@ const TransferToLayingForm = ({
|
|||||||
onChange={flockDestinationChangeHandler}
|
onChange={flockDestinationChangeHandler}
|
||||||
isLoading={isLoadingFlockDestinationOptions}
|
isLoading={isLoadingFlockDestinationOptions}
|
||||||
onInputChange={setFlockDestinationInputValue}
|
onInputChange={setFlockDestinationInputValue}
|
||||||
|
onMenuScrollToBottom={loadMoreFlockDestination}
|
||||||
isError={
|
isError={
|
||||||
formik.touched.flockDestination &&
|
formik.touched.flockDestination &&
|
||||||
Boolean(typeof formik.errors.flockDestination === 'string')
|
Boolean(typeof formik.errors.flockDestination === 'string')
|
||||||
|
|||||||
@@ -37,7 +37,10 @@ import DateInput from '@/components/input/DateInput';
|
|||||||
import { LocationApi } from '@/services/api/master-data';
|
import { LocationApi } from '@/services/api/master-data';
|
||||||
import { ProjectFlockApi } from '@/services/api/production';
|
import { ProjectFlockApi } from '@/services/api/production';
|
||||||
import { Kandang } from '@/types/api/master-data/kandang';
|
import { Kandang } from '@/types/api/master-data/kandang';
|
||||||
import { ProjectFlockKandangLookup } from '@/types/api/production/project-flock';
|
import {
|
||||||
|
ProjectFlockKandangLookup,
|
||||||
|
ProjectFlock,
|
||||||
|
} from '@/types/api/production/project-flock';
|
||||||
import {
|
import {
|
||||||
getStatusColor,
|
getStatusColor,
|
||||||
getStatusIndicatorColor,
|
getStatusIndicatorColor,
|
||||||
@@ -229,63 +232,37 @@ const UniformityTable = () => {
|
|||||||
useState<number | undefined>(undefined);
|
useState<number | undefined>(undefined);
|
||||||
const [filterStartDate, setFilterStartDate] = useState('');
|
const [filterStartDate, setFilterStartDate] = useState('');
|
||||||
const [filterEndDate, setFilterEndDate] = useState('');
|
const [filterEndDate, setFilterEndDate] = useState('');
|
||||||
const [projectFlockSearchValue, setProjectFlockSearchValue] = useState('');
|
const [filterProjectFlockLocationId, setFilterProjectFlockLocationId] =
|
||||||
|
useState<string>('');
|
||||||
const [filterErrors, setFilterErrors] = useState<Record<string, string>>({});
|
const [filterErrors, setFilterErrors] = useState<Record<string, string>>({});
|
||||||
|
|
||||||
const {
|
const {
|
||||||
setInputValue: setFilterLocationInputValue,
|
setInputValue: setFilterLocationInputValue,
|
||||||
options: filterLocationOptions,
|
options: filterLocationOptions,
|
||||||
isLoadingOptions: isLoadingFilterLocations,
|
isLoadingOptions: isLoadingFilterLocations,
|
||||||
} = useSelect(LocationApi.basePath, 'id', 'name', 'search', {
|
loadMore: loadMoreFilterLocations,
|
||||||
limit: '100',
|
hasMore: hasMoreFilterLocations,
|
||||||
});
|
} = useSelect(LocationApi.basePath, 'id', 'name', 'search');
|
||||||
|
|
||||||
// ===== FETCH PROJECT FLOCKS DATA FOR FILTER =====
|
// ===== FETCH PROJECT FLOCKS DATA FOR FILTER =====
|
||||||
const filterProjectFlocksUrl = useMemo(() => {
|
|
||||||
const params = new URLSearchParams({
|
|
||||||
search: projectFlockSearchValue || '',
|
|
||||||
limit: '100',
|
|
||||||
});
|
|
||||||
if (filterLocation) {
|
|
||||||
params.append('location_id', filterLocation.value.toString());
|
|
||||||
}
|
|
||||||
return `${ProjectFlockApi.basePath}?${params.toString()}`;
|
|
||||||
}, [projectFlockSearchValue, filterLocation]);
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
data: filterProjectFlocksData,
|
setInputValue: setFilterProjectFlockSearchValue,
|
||||||
isLoading: isLoadingFilterProjectFlocks,
|
options: filterProjectFlockOptions,
|
||||||
} = useSWR(filterProjectFlocksUrl, ProjectFlockApi.getAllFetcher);
|
rawData: filterProjectFlocksRawData,
|
||||||
|
isLoadingOptions: isLoadingFilterProjectFlocks,
|
||||||
const filterProjectFlocksDataList = useMemo(
|
loadMore: loadMoreFilterProjectFlocks,
|
||||||
() =>
|
hasMore: hasMoreFilterProjectFlocks,
|
||||||
isResponseSuccess(filterProjectFlocksData)
|
} = useSelect(ProjectFlockApi.basePath, 'id', 'flock_name', 'search', {
|
||||||
? filterProjectFlocksData.data
|
location_id: filterProjectFlockLocationId,
|
||||||
: undefined,
|
});
|
||||||
[filterProjectFlocksData]
|
|
||||||
);
|
|
||||||
|
|
||||||
const filterProjectFlockOptions = useMemo(() => {
|
|
||||||
let options: OptionType[] = [];
|
|
||||||
|
|
||||||
if (isResponseSuccess(filterProjectFlocksData)) {
|
|
||||||
const flockOptions =
|
|
||||||
filterProjectFlocksData?.data.map((projectFlock) => ({
|
|
||||||
value: projectFlock.id,
|
|
||||||
label: projectFlock.flock_name || '',
|
|
||||||
})) || [];
|
|
||||||
options = options.concat(flockOptions);
|
|
||||||
}
|
|
||||||
|
|
||||||
return options;
|
|
||||||
}, [filterProjectFlocksData]);
|
|
||||||
|
|
||||||
// ===== KANDANG OPTIONS FOR FILTER =====
|
// ===== KANDANG OPTIONS FOR FILTER =====
|
||||||
const filterKandangOptions = useMemo(() => {
|
const filterKandangOptions = useMemo(() => {
|
||||||
let options: OptionType[] = [];
|
let options: OptionType[] = [];
|
||||||
|
|
||||||
if (filterProjectFlock && filterProjectFlocksDataList) {
|
if (filterProjectFlock && isResponseSuccess(filterProjectFlocksRawData)) {
|
||||||
const selectedProjectFlockData = filterProjectFlocksDataList.find(
|
const data = filterProjectFlocksRawData.data as unknown as ProjectFlock[];
|
||||||
|
const selectedProjectFlockData = data.find(
|
||||||
(pf) => pf.id === filterProjectFlock.value
|
(pf) => pf.id === filterProjectFlock.value
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -301,7 +278,7 @@ const UniformityTable = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return options;
|
return options;
|
||||||
}, [filterProjectFlock, filterProjectFlocksDataList]);
|
}, [filterProjectFlock, filterProjectFlocksRawData]);
|
||||||
|
|
||||||
// ===== PROJECT FLOCK KANDANG LOOKUP =====
|
// ===== PROJECT FLOCK KANDANG LOOKUP =====
|
||||||
const projectFlockKandangLookupUrl = useMemo(() => {
|
const projectFlockKandangLookupUrl = useMemo(() => {
|
||||||
@@ -394,9 +371,13 @@ const UniformityTable = () => {
|
|||||||
// ===== FILTER HANDLERS =====
|
// ===== FILTER HANDLERS =====
|
||||||
const handleFilterLocationChange = useCallback(
|
const handleFilterLocationChange = useCallback(
|
||||||
(val: OptionType | OptionType[] | null) => {
|
(val: OptionType | OptionType[] | null) => {
|
||||||
setFilterLocation(val as OptionType | null);
|
const location = val as OptionType | null;
|
||||||
|
setFilterLocation(location);
|
||||||
setFilterProjectFlock(null);
|
setFilterProjectFlock(null);
|
||||||
setFilterKandang(null);
|
setFilterKandang(null);
|
||||||
|
setFilterProjectFlockLocationId(
|
||||||
|
location ? location.value.toString() : ''
|
||||||
|
);
|
||||||
},
|
},
|
||||||
[]
|
[]
|
||||||
);
|
);
|
||||||
@@ -1206,6 +1187,7 @@ const UniformityTable = () => {
|
|||||||
options={filterLocationOptions}
|
options={filterLocationOptions}
|
||||||
onInputChange={setFilterLocationInputValue}
|
onInputChange={setFilterLocationInputValue}
|
||||||
isLoading={isLoadingFilterLocations}
|
isLoading={isLoadingFilterLocations}
|
||||||
|
onMenuScrollToBottom={loadMoreFilterLocations}
|
||||||
className={{ wrapper: 'w-full' }}
|
className={{ wrapper: 'w-full' }}
|
||||||
/>
|
/>
|
||||||
{filterErrors.location && (
|
{filterErrors.location && (
|
||||||
@@ -1225,8 +1207,9 @@ const UniformityTable = () => {
|
|||||||
setFilterErrors((prev) => ({ ...prev, project_flock: '' }));
|
setFilterErrors((prev) => ({ ...prev, project_flock: '' }));
|
||||||
}}
|
}}
|
||||||
options={filterProjectFlockOptions}
|
options={filterProjectFlockOptions}
|
||||||
onInputChange={setProjectFlockSearchValue}
|
onInputChange={setFilterProjectFlockSearchValue}
|
||||||
isLoading={isLoadingFilterProjectFlocks}
|
isLoading={isLoadingFilterProjectFlocks}
|
||||||
|
onMenuScrollToBottom={loadMoreFilterProjectFlocks}
|
||||||
isDisabled={!filterLocation}
|
isDisabled={!filterLocation}
|
||||||
className={{ wrapper: 'w-full' }}
|
className={{ wrapper: 'w-full' }}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import Badge from '../../../../Badge';
|
import Badge from '@/components/Badge';
|
||||||
import Card from '@/components/Card';
|
import Card from '@/components/Card';
|
||||||
import { Icon } from '@iconify/react';
|
import { Icon } from '@iconify/react';
|
||||||
import { formatNumber } from '@/lib/helper';
|
import { formatNumber } from '@/lib/helper';
|
||||||
|
|||||||
@@ -36,7 +36,10 @@ import {
|
|||||||
VerifyUniformityPayload,
|
VerifyUniformityPayload,
|
||||||
} from '@/types/api/production/uniformity';
|
} from '@/types/api/production/uniformity';
|
||||||
import { type BaseApiResponse } from '@/types/api/api-general';
|
import { type BaseApiResponse } from '@/types/api/api-general';
|
||||||
import { ProjectFlockKandangLookup } from '@/types/api/production/project-flock';
|
import {
|
||||||
|
ProjectFlockKandangLookup,
|
||||||
|
ProjectFlock,
|
||||||
|
} from '@/types/api/production/project-flock';
|
||||||
import { Kandang } from '@/types/api/master-data/kandang';
|
import { Kandang } from '@/types/api/master-data/kandang';
|
||||||
import UniformityPreviewForm from '@/components/pages/production/uniformity/form/UniformityPreviewForm';
|
import UniformityPreviewForm from '@/components/pages/production/uniformity/form/UniformityPreviewForm';
|
||||||
import UniformityResultForm from '@/components/pages/production/uniformity/form/UniformityResultForm';
|
import UniformityResultForm from '@/components/pages/production/uniformity/form/UniformityResultForm';
|
||||||
@@ -88,7 +91,9 @@ const UniformityForm = ({
|
|||||||
null
|
null
|
||||||
);
|
);
|
||||||
|
|
||||||
const [projectFlockSearchValue, setProjectFlockSearchValue] = useState('');
|
const [selectedProjectFlockLocationId, setSelectedProjectFlockLocationId] =
|
||||||
|
useState<string>('');
|
||||||
|
|
||||||
const [selectedProjectFlock, setSelectedProjectFlock] =
|
const [selectedProjectFlock, setSelectedProjectFlock] =
|
||||||
useState<OptionType | null>(null);
|
useState<OptionType | null>(null);
|
||||||
|
|
||||||
@@ -100,50 +105,21 @@ const UniformityForm = ({
|
|||||||
setInputValue: setLocationSelectInputValue,
|
setInputValue: setLocationSelectInputValue,
|
||||||
options: locationOptions,
|
options: locationOptions,
|
||||||
isLoadingOptions: isLoadingLocations,
|
isLoadingOptions: isLoadingLocations,
|
||||||
} = useSelect(LocationApi.basePath, 'id', 'name', 'search', {
|
loadMore: loadMoreLocations,
|
||||||
page: '1',
|
hasMore: hasMoreLocations,
|
||||||
limit: '100',
|
} = useSelect(LocationApi.basePath, 'id', 'name', 'search');
|
||||||
|
|
||||||
|
const {
|
||||||
|
setInputValue: setProjectFlockSearchValue,
|
||||||
|
options: projectFlockOptions,
|
||||||
|
rawData: projectFlocksRawData,
|
||||||
|
isLoadingOptions: isLoadingProjectFlocks,
|
||||||
|
loadMore: loadMoreProjectFlocks,
|
||||||
|
hasMore: hasMoreProjectFlocks,
|
||||||
|
} = useSelect(ProjectFlockApi.basePath, 'id', 'flock_name', 'search', {
|
||||||
|
location_id: selectedProjectFlockLocationId,
|
||||||
});
|
});
|
||||||
|
|
||||||
// ===== FETCH PROJECT FLOCKS DATA =====
|
|
||||||
const projectFlocksUrl = useMemo(() => {
|
|
||||||
const params = new URLSearchParams({
|
|
||||||
search: projectFlockSearchValue || '',
|
|
||||||
page: '1',
|
|
||||||
limit: '100',
|
|
||||||
});
|
|
||||||
if (selectedLocation) {
|
|
||||||
params.append('location_id', selectedLocation.value.toString());
|
|
||||||
}
|
|
||||||
return `${ProjectFlockApi.basePath}?${params.toString()}`;
|
|
||||||
}, [projectFlockSearchValue, selectedLocation]);
|
|
||||||
|
|
||||||
const { data: projectFlocksData, isLoading: isLoadingProjectFlocks } = useSWR(
|
|
||||||
projectFlocksUrl,
|
|
||||||
ProjectFlockApi.getAllFetcher
|
|
||||||
);
|
|
||||||
|
|
||||||
const projectFlocksDataList =
|
|
||||||
projectFlocksData?.status === 'success'
|
|
||||||
? projectFlocksData.data
|
|
||||||
: undefined;
|
|
||||||
|
|
||||||
// ===== PROJECT FLOCK OPTIONS =====
|
|
||||||
const projectFlockOptions = useMemo(() => {
|
|
||||||
let options: OptionType[] = [];
|
|
||||||
|
|
||||||
if (isResponseSuccess(projectFlocksData)) {
|
|
||||||
const flockOptions =
|
|
||||||
projectFlocksData?.data.map((projectFlock) => ({
|
|
||||||
value: projectFlock.id,
|
|
||||||
label: projectFlock.flock_name || '',
|
|
||||||
})) || [];
|
|
||||||
options = options.concat(flockOptions);
|
|
||||||
}
|
|
||||||
|
|
||||||
return options;
|
|
||||||
}, [projectFlocksData]);
|
|
||||||
|
|
||||||
// ===== APPROVED PROJECT FLOCK KANDANGS =====
|
// ===== APPROVED PROJECT FLOCK KANDANGS =====
|
||||||
const approvedProjectFlockKandangsUrl = useMemo(() => {
|
const approvedProjectFlockKandangsUrl = useMemo(() => {
|
||||||
const params = new URLSearchParams({
|
const params = new URLSearchParams({
|
||||||
@@ -168,8 +144,9 @@ const UniformityForm = ({
|
|||||||
const kandangOptions = useMemo(() => {
|
const kandangOptions = useMemo(() => {
|
||||||
let options: OptionType[] = [];
|
let options: OptionType[] = [];
|
||||||
|
|
||||||
if (selectedProjectFlock && projectFlocksDataList) {
|
if (selectedProjectFlock && isResponseSuccess(projectFlocksRawData)) {
|
||||||
const selectedProjectFlockData = projectFlocksDataList.find(
|
const data = projectFlocksRawData.data as unknown as ProjectFlock[];
|
||||||
|
const selectedProjectFlockData = data.find(
|
||||||
(pf) => pf.id === selectedProjectFlock.value
|
(pf) => pf.id === selectedProjectFlock.value
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -196,7 +173,7 @@ const UniformityForm = ({
|
|||||||
return options;
|
return options;
|
||||||
}, [
|
}, [
|
||||||
selectedProjectFlock,
|
selectedProjectFlock,
|
||||||
projectFlocksDataList,
|
projectFlocksRawData,
|
||||||
approvedProjectFlockKandangs,
|
approvedProjectFlockKandangs,
|
||||||
formType,
|
formType,
|
||||||
]);
|
]);
|
||||||
@@ -313,6 +290,10 @@ const UniformityForm = ({
|
|||||||
formik.setFieldValue('location_id', locationId);
|
formik.setFieldValue('location_id', locationId);
|
||||||
|
|
||||||
setSelectedLocation(location);
|
setSelectedLocation(location);
|
||||||
|
setSelectedProjectFlock(null);
|
||||||
|
setSelectedProjectFlockLocationId(
|
||||||
|
location ? location.value.toString() : ''
|
||||||
|
);
|
||||||
},
|
},
|
||||||
[]
|
[]
|
||||||
);
|
);
|
||||||
@@ -513,6 +494,7 @@ const UniformityForm = ({
|
|||||||
options={locationOptions}
|
options={locationOptions}
|
||||||
onInputChange={setLocationSelectInputValue}
|
onInputChange={setLocationSelectInputValue}
|
||||||
isLoading={isLoadingLocations}
|
isLoading={isLoadingLocations}
|
||||||
|
onMenuScrollToBottom={loadMoreLocations}
|
||||||
isError={
|
isError={
|
||||||
formik.touched.location_id && Boolean(formik.errors.location_id)
|
formik.touched.location_id && Boolean(formik.errors.location_id)
|
||||||
}
|
}
|
||||||
@@ -530,6 +512,7 @@ const UniformityForm = ({
|
|||||||
options={projectFlockOptions}
|
options={projectFlockOptions}
|
||||||
onInputChange={setProjectFlockSearchValue}
|
onInputChange={setProjectFlockSearchValue}
|
||||||
isLoading={isLoadingProjectFlocks}
|
isLoading={isLoadingProjectFlocks}
|
||||||
|
onMenuScrollToBottom={loadMoreProjectFlocks}
|
||||||
isDisabled={!formik.values.location_id}
|
isDisabled={!formik.values.location_id}
|
||||||
isError={
|
isError={
|
||||||
formik.touched.project_flock_id &&
|
formik.touched.project_flock_id &&
|
||||||
|
|||||||
@@ -156,8 +156,11 @@ const PurchaseOrderAcceptApprovalForm = ({
|
|||||||
setInputValue: setExpeditionsSelectInputValue,
|
setInputValue: setExpeditionsSelectInputValue,
|
||||||
options: expeditionVendors,
|
options: expeditionVendors,
|
||||||
isLoadingOptions: isLoadingExpeditions,
|
isLoadingOptions: isLoadingExpeditions,
|
||||||
|
loadMore: loadMoreExpeditions,
|
||||||
|
hasMore: hasMoreExpeditions,
|
||||||
} = useSelect<Supplier>(SupplierApi.basePath, 'id', 'name', 'search', {
|
} = useSelect<Supplier>(SupplierApi.basePath, 'id', 'name', 'search', {
|
||||||
category: 'BOP',
|
category: 'BOP',
|
||||||
|
flag: 'EKSPEDISI',
|
||||||
});
|
});
|
||||||
|
|
||||||
// ===== FORM CONFIGURATION =====
|
// ===== FORM CONFIGURATION =====
|
||||||
@@ -183,8 +186,8 @@ const PurchaseOrderAcceptApprovalForm = ({
|
|||||||
purchase_item_id: formItem.purchase_item_id || 0,
|
purchase_item_id: formItem.purchase_item_id || 0,
|
||||||
received_date: formItem.received_date || '',
|
received_date: formItem.received_date || '',
|
||||||
travel_number: formItem.travel_number || '',
|
travel_number: formItem.travel_number || '',
|
||||||
vehicle_number: formItem.vehicle_number || '',
|
vehicle_number: formItem.vehicle_number || null,
|
||||||
expedition_vendor_id: formItem.expedition_vendor_id || 0,
|
expedition_vendor_id: formItem.expedition_vendor_id || null,
|
||||||
received_qty:
|
received_qty:
|
||||||
typeof formItem.received_qty === 'string'
|
typeof formItem.received_qty === 'string'
|
||||||
? parseFloat(formItem.received_qty) || 0
|
? parseFloat(formItem.received_qty) || 0
|
||||||
@@ -192,10 +195,13 @@ const PurchaseOrderAcceptApprovalForm = ({
|
|||||||
transport_per_item:
|
transport_per_item:
|
||||||
typeof formItem.transport_per_item === 'string'
|
typeof formItem.transport_per_item === 'string'
|
||||||
? parseFloat(formItem.transport_per_item) || 0
|
? parseFloat(formItem.transport_per_item) || 0
|
||||||
: formItem.transport_per_item || 0,
|
: formItem.transport_per_item || null,
|
||||||
};
|
};
|
||||||
}) || [],
|
}) || [],
|
||||||
travel_documents: values.travel_documents || [],
|
travel_documents:
|
||||||
|
values.travel_documents
|
||||||
|
?.filter((file): file is File => file instanceof File)
|
||||||
|
.filter(Boolean) || undefined,
|
||||||
};
|
};
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
@@ -403,22 +409,13 @@ const PurchaseOrderAcceptApprovalForm = ({
|
|||||||
Dokumen Surat Jalan
|
Dokumen Surat Jalan
|
||||||
<span className='text-error'>*</span>
|
<span className='text-error'>*</span>
|
||||||
</th>
|
</th>
|
||||||
<th>
|
<th>Nomor Kendaraan</th>
|
||||||
Nomor Kendaraan
|
<th>Vendor Ekspedisi</th>
|
||||||
<span className='text-error'>*</span>
|
|
||||||
</th>
|
|
||||||
<th>
|
|
||||||
Vendor Ekspedisi
|
|
||||||
<span className='text-error'>*</span>
|
|
||||||
</th>
|
|
||||||
<th>
|
<th>
|
||||||
Jumlah Diterima
|
Jumlah Diterima
|
||||||
<span className='text-error'>*</span>
|
<span className='text-error'>*</span>
|
||||||
</th>
|
</th>
|
||||||
<th>
|
<th>Transport/Item</th>
|
||||||
Transport/Item
|
|
||||||
<span className='text-error'>*</span>
|
|
||||||
</th>
|
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
@@ -536,7 +533,6 @@ const PurchaseOrderAcceptApprovalForm = ({
|
|||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<TextInput
|
<TextInput
|
||||||
required
|
|
||||||
name={`items.${idx}.vehicle_number`}
|
name={`items.${idx}.vehicle_number`}
|
||||||
type='text'
|
type='text'
|
||||||
value={formItem?.vehicle_number || ''}
|
value={formItem?.vehicle_number || ''}
|
||||||
@@ -562,7 +558,6 @@ const PurchaseOrderAcceptApprovalForm = ({
|
|||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<SelectInput
|
<SelectInput
|
||||||
required
|
|
||||||
isClearable={true}
|
isClearable={true}
|
||||||
value={formItem?.expedition_vendor}
|
value={formItem?.expedition_vendor}
|
||||||
key={`expedition-vendor-${idx}`}
|
key={`expedition-vendor-${idx}`}
|
||||||
@@ -570,6 +565,8 @@ const PurchaseOrderAcceptApprovalForm = ({
|
|||||||
expeditionVendorChangeHandler(idx, val)
|
expeditionVendorChangeHandler(idx, val)
|
||||||
}
|
}
|
||||||
options={getExpeditionVendorOptions()}
|
options={getExpeditionVendorOptions()}
|
||||||
|
isLoading={isLoadingExpeditions}
|
||||||
|
onMenuScrollToBottom={loadMoreExpeditions}
|
||||||
isError={
|
isError={
|
||||||
isRepeaterInputError(idx, 'expedition_vendor_id')
|
isRepeaterInputError(idx, 'expedition_vendor_id')
|
||||||
.isError
|
.isError
|
||||||
@@ -629,7 +626,6 @@ const PurchaseOrderAcceptApprovalForm = ({
|
|||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<NumberInput
|
<NumberInput
|
||||||
required
|
|
||||||
name={`items.${idx}.transport_per_item`}
|
name={`items.${idx}.transport_per_item`}
|
||||||
value={formItem?.transport_per_item || ''}
|
value={formItem?.transport_per_item || ''}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
@@ -680,7 +676,6 @@ const PurchaseOrderAcceptApprovalForm = ({
|
|||||||
|
|
||||||
<div className={'col-span-2 my-2'}>
|
<div className={'col-span-2 my-2'}>
|
||||||
<FileInput
|
<FileInput
|
||||||
required
|
|
||||||
name='travel_documents'
|
name='travel_documents'
|
||||||
label='Dokumen Surat Jalan'
|
label='Dokumen Surat Jalan'
|
||||||
accept='.pdf,.jpg,.jpeg,.png'
|
accept='.pdf,.jpg,.jpeg,.png'
|
||||||
|
|||||||
@@ -38,16 +38,16 @@ type PurchaseRequestAcceptApprovalFormSchemaType = {
|
|||||||
purchase_item_id: number;
|
purchase_item_id: number;
|
||||||
received_date: string;
|
received_date: string;
|
||||||
travel_number: string;
|
travel_number: string;
|
||||||
vehicle_number: string;
|
vehicle_number?: string | null;
|
||||||
expedition_vendor?: {
|
expedition_vendor?: {
|
||||||
value: number;
|
value: number;
|
||||||
label: string;
|
label: string;
|
||||||
} | null;
|
} | null;
|
||||||
expedition_vendor_id: number;
|
expedition_vendor_id?: number | null;
|
||||||
received_qty: number | string;
|
received_qty: number | string;
|
||||||
transport_per_item: number | string;
|
transport_per_item?: number | string | null;
|
||||||
}[];
|
}[];
|
||||||
travel_documents: File[];
|
travel_documents?: (File | null | undefined)[] | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type PurchaseStaffApprovalItemSchema = {
|
export type PurchaseStaffApprovalItemSchema = {
|
||||||
@@ -75,14 +75,14 @@ export type PurchaseAcceptApprovalItemSchema = {
|
|||||||
purchase_item_id: number;
|
purchase_item_id: number;
|
||||||
received_date: string;
|
received_date: string;
|
||||||
travel_number: string;
|
travel_number: string;
|
||||||
vehicle_number: string;
|
vehicle_number?: string | null;
|
||||||
expedition_vendor?: {
|
expedition_vendor?: {
|
||||||
value: number;
|
value: number;
|
||||||
label: string;
|
label: string;
|
||||||
} | null;
|
} | null;
|
||||||
expedition_vendor_id: number;
|
expedition_vendor_id?: number | null;
|
||||||
received_qty: number | string;
|
received_qty: number | string;
|
||||||
transport_per_item: number | string;
|
transport_per_item?: number | string | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type PurchaseDeleteItemsSchema = {
|
export type PurchaseDeleteItemsSchema = {
|
||||||
@@ -184,24 +184,19 @@ const PurchaseAcceptApprovalItemObjectSchema: Yup.ObjectSchema<PurchaseAcceptApp
|
|||||||
.required('No. Surat jalan wajib diisi!')
|
.required('No. Surat jalan wajib diisi!')
|
||||||
.typeError('No. Surat jalan wajib diisi!'),
|
.typeError('No. Surat jalan wajib diisi!'),
|
||||||
vehicle_number: Yup.string()
|
vehicle_number: Yup.string()
|
||||||
.required('Nomor kendaraan wajib diisi!')
|
.nullable()
|
||||||
.typeError('Nomor kendaraan wajib diisi!'),
|
.optional()
|
||||||
|
.typeError('Nomor kendaraan harus berupa plat nomor!'),
|
||||||
expedition_vendor: Yup.object({
|
expedition_vendor: Yup.object({
|
||||||
value: Yup.number().min(1).required(),
|
value: Yup.number().min(1).required(),
|
||||||
label: Yup.string().required(),
|
label: Yup.string().required(),
|
||||||
}).nullable(),
|
})
|
||||||
|
.nullable()
|
||||||
|
.optional(),
|
||||||
expedition_vendor_id: Yup.number()
|
expedition_vendor_id: Yup.number()
|
||||||
.min(1, 'Vendor ekspedisi wajib diisi!')
|
.nullable()
|
||||||
.required('Vendor ekspedisi wajib diisi!')
|
.optional()
|
||||||
.test(
|
.typeError('Vendor ekspedisi harus berupa angka!'),
|
||||||
'is-valid-expedition-vendor',
|
|
||||||
'Vendor ekspedisi harus dipilih!',
|
|
||||||
function (value) {
|
|
||||||
if (!this.parent.expedition_vendor) return true;
|
|
||||||
return Boolean(value && value > 0);
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.typeError('Vendor ekspedisi harus dipilih!'),
|
|
||||||
received_qty: Yup.mixed<string | number>()
|
received_qty: Yup.mixed<string | number>()
|
||||||
.required('Jumlah diterima wajib diisi!')
|
.required('Jumlah diterima wajib diisi!')
|
||||||
.test(
|
.test(
|
||||||
@@ -217,13 +212,14 @@ const PurchaseAcceptApprovalItemObjectSchema: Yup.ObjectSchema<PurchaseAcceptApp
|
|||||||
)
|
)
|
||||||
.typeError('Jumlah diterima harus berupa angka!'),
|
.typeError('Jumlah diterima harus berupa angka!'),
|
||||||
transport_per_item: Yup.mixed<string | number>()
|
transport_per_item: Yup.mixed<string | number>()
|
||||||
.required('Biaya transport per item wajib diisi!')
|
.nullable()
|
||||||
|
.optional()
|
||||||
.test(
|
.test(
|
||||||
'is-valid-transport-per-item',
|
'is-valid-transport-per-item',
|
||||||
'Biaya transport per item harus berupa angka lebih dari atau sama dengan 0!',
|
'Biaya transport per item harus berupa angka lebih dari atau sama dengan 0!',
|
||||||
function (value) {
|
function (value) {
|
||||||
if (value === '' || value === null || value === undefined)
|
if (value === '' || value === null || value === undefined)
|
||||||
return false;
|
return true;
|
||||||
const numValue =
|
const numValue =
|
||||||
typeof value === 'string' ? parseFloat(value) : value;
|
typeof value === 'string' ? parseFloat(value) : value;
|
||||||
return !isNaN(numValue) && numValue >= 0;
|
return !isNaN(numValue) && numValue >= 0;
|
||||||
@@ -389,16 +385,17 @@ export const PurchaseRequestAcceptApprovalFormSchema: Yup.ObjectSchema<PurchaseR
|
|||||||
travel_documents: Yup.array()
|
travel_documents: Yup.array()
|
||||||
.of(
|
.of(
|
||||||
Yup.mixed<File>()
|
Yup.mixed<File>()
|
||||||
.required('Dokumen surat jalan wajib diupload!')
|
.nullable()
|
||||||
|
.optional()
|
||||||
.test('fileSize', 'Ukuran dokumen maksimal 5 MB', (value) => {
|
.test('fileSize', 'Ukuran dokumen maksimal 5 MB', (value) => {
|
||||||
if (!value) return true;
|
if (!value) return true;
|
||||||
if (value instanceof File) return value.size <= 5 * 1024 * 1024;
|
if (value instanceof File) return value.size <= 5 * 1024 * 1024;
|
||||||
return true;
|
return true;
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
.required('Dokumen surat jalan wajib diupload!')
|
.nullable()
|
||||||
.min(1, 'Minimal upload 1 dokumen surat jalan!')
|
.optional()
|
||||||
.typeError('Dokumen surat jalan wajib diupload!'),
|
.typeError('Dokumen surat jalan harus berupa array!'),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const PurchaseRequestAcceptApprovalFormInitialValues: PurchaseRequestAcceptApprovalFormSchemaType =
|
export const PurchaseRequestAcceptApprovalFormInitialValues: PurchaseRequestAcceptApprovalFormSchemaType =
|
||||||
|
|||||||
@@ -63,11 +63,9 @@ const PurchaseRequestForm = ({
|
|||||||
useState('');
|
useState('');
|
||||||
const [formErrorList, setFormErrorList] = useState<string[]>([]);
|
const [formErrorList, setFormErrorList] = useState<string[]>([]);
|
||||||
|
|
||||||
// ===== TYPE DEFINITIONS =====
|
const [selectedArea, setSelectedArea] = useState('');
|
||||||
interface ProductOptionType {
|
const [selectedLocation, setSelectedLocation] = useState('');
|
||||||
value: number;
|
const [disabledLocation, setDisabledLocation] = useState(true);
|
||||||
label: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ===== UTILITY FUNCTIONS =====
|
// ===== UTILITY FUNCTIONS =====
|
||||||
const isRepeaterInputError = (
|
const isRepeaterInputError = (
|
||||||
@@ -160,11 +158,35 @@ const PurchaseRequestForm = ({
|
|||||||
isLoadingOptions: isLoadingAreas,
|
isLoadingOptions: isLoadingAreas,
|
||||||
} = useSelect(AreaApi.basePath, 'id', 'name', 'search');
|
} = useSelect(AreaApi.basePath, 'id', 'name', 'search');
|
||||||
|
|
||||||
|
const {
|
||||||
|
options: locationOptions,
|
||||||
|
isLoadingOptions: isLoadingLocations,
|
||||||
|
loadMore: loadMoreLocations,
|
||||||
|
hasMore: hasMoreLocations,
|
||||||
|
} = useSelect(LocationApi.basePath, 'id', 'name', '', {
|
||||||
|
area_id:
|
||||||
|
selectedArea != ''
|
||||||
|
? selectedArea
|
||||||
|
: ((initialValues?.area?.id ?? '') as string),
|
||||||
|
});
|
||||||
|
|
||||||
const {
|
const {
|
||||||
inputValue: warehouseSelectInputValue,
|
inputValue: warehouseSelectInputValue,
|
||||||
setInputValue: setWarehouseSelectInputValue,
|
setInputValue: setWarehouseSelectInputValue,
|
||||||
|
options: warehouseOptions,
|
||||||
isLoadingOptions: isLoadingWarehouses,
|
isLoadingOptions: isLoadingWarehouses,
|
||||||
} = useSelect(WarehouseApi.basePath, 'id', 'name', 'search');
|
loadMore: loadMoreWarehouses,
|
||||||
|
hasMore: hasMoreWarehouses,
|
||||||
|
} = useSelect(WarehouseApi.basePath, 'id', 'name', 'search', {
|
||||||
|
area_id:
|
||||||
|
selectedArea != ''
|
||||||
|
? selectedArea
|
||||||
|
: ((initialValues?.area?.id ?? '') as string),
|
||||||
|
location_id:
|
||||||
|
selectedLocation != ''
|
||||||
|
? selectedLocation
|
||||||
|
: ((initialValues?.location?.id ?? '') as string),
|
||||||
|
});
|
||||||
|
|
||||||
// ===== FORM CONFIGURATION =====
|
// ===== FORM CONFIGURATION =====
|
||||||
const formikInitialValues = useMemo<PurchaseRequestFormValues>(
|
const formikInitialValues = useMemo<PurchaseRequestFormValues>(
|
||||||
@@ -267,70 +289,6 @@ const PurchaseRequestForm = ({
|
|||||||
return data;
|
return data;
|
||||||
}, [supplierData]);
|
}, [supplierData]);
|
||||||
|
|
||||||
const locationsUrl = useMemo(() => {
|
|
||||||
const params = new URLSearchParams({
|
|
||||||
search: locationSelectInputValue,
|
|
||||||
...(formik.values.area_id && formik.values.area_id > 0
|
|
||||||
? { area_id: formik.values.area_id.toString() }
|
|
||||||
: {}),
|
|
||||||
});
|
|
||||||
return `${LocationApi.basePath}?${params.toString()}`;
|
|
||||||
}, [locationSelectInputValue, formik.values.area_id]);
|
|
||||||
|
|
||||||
const { data: locations, isLoading: isLoadingLocations } = useSWR(
|
|
||||||
locationsUrl,
|
|
||||||
LocationApi.getAllFetcher
|
|
||||||
);
|
|
||||||
|
|
||||||
const locationOptions = useMemo(() => {
|
|
||||||
if (!isResponseSuccess(locations)) return [];
|
|
||||||
return (
|
|
||||||
locations?.data.map((location) => ({
|
|
||||||
value: location.id,
|
|
||||||
label: location.name,
|
|
||||||
})) || []
|
|
||||||
);
|
|
||||||
}, [locations]);
|
|
||||||
|
|
||||||
const warehousesUrl = useMemo(() => {
|
|
||||||
const params = new URLSearchParams({ search: warehouseSelectInputValue });
|
|
||||||
|
|
||||||
if (formik.values.area_id && formik.values.area_id > 0) {
|
|
||||||
params.append('area_id', formik.values.area_id.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (formik.values.location_id && formik.values.location_id > 0) {
|
|
||||||
params.append('location_id', formik.values.location_id.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
return `${WarehouseApi.basePath}?${params.toString()}`;
|
|
||||||
}, [
|
|
||||||
warehouseSelectInputValue,
|
|
||||||
formik.values.area_id,
|
|
||||||
formik.values.location_id,
|
|
||||||
]);
|
|
||||||
|
|
||||||
const { data: warehouses } = useSWR(
|
|
||||||
warehousesUrl,
|
|
||||||
WarehouseApi.getAllFetcher
|
|
||||||
);
|
|
||||||
|
|
||||||
const warehouseOptions = useMemo(() => {
|
|
||||||
if (!isResponseSuccess(warehouses)) return [];
|
|
||||||
|
|
||||||
return (
|
|
||||||
warehouses?.data.map((w) => ({
|
|
||||||
value: w.id,
|
|
||||||
label: w.name,
|
|
||||||
area: w.area?.name,
|
|
||||||
location:
|
|
||||||
'type' in w && (w.type === 'LOKASI' || w.type === 'KANDANG')
|
|
||||||
? w.location?.name
|
|
||||||
: undefined,
|
|
||||||
})) || []
|
|
||||||
);
|
|
||||||
}, [warehouses]);
|
|
||||||
|
|
||||||
const addPurchaseItem = () => {
|
const addPurchaseItem = () => {
|
||||||
const newItems = [
|
const newItems = [
|
||||||
...(formik.values.items || []),
|
...(formik.values.items || []),
|
||||||
@@ -407,6 +365,18 @@ const PurchaseRequestForm = ({
|
|||||||
}
|
}
|
||||||
}, [formik.values.supplier_id]);
|
}, [formik.values.supplier_id]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (type !== 'add' && initialValues) {
|
||||||
|
if (initialValues.area?.id) {
|
||||||
|
setSelectedArea(initialValues.area.id.toString());
|
||||||
|
setDisabledLocation(false);
|
||||||
|
}
|
||||||
|
if (initialValues.location?.id) {
|
||||||
|
setSelectedLocation(initialValues.location.id.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [type, initialValues]);
|
||||||
|
|
||||||
// ===== FORM HANDLERS =====
|
// ===== FORM HANDLERS =====
|
||||||
const handleSupplierChange = useCallback(
|
const handleSupplierChange = useCallback(
|
||||||
(val: OptionType | OptionType[] | null) => {
|
(val: OptionType | OptionType[] | null) => {
|
||||||
@@ -445,6 +415,16 @@ const PurchaseRequestForm = ({
|
|||||||
formik.setFieldValue('area_id', (area as OptionType)?.value || 0);
|
formik.setFieldValue('area_id', (area as OptionType)?.value || 0);
|
||||||
formik.setFieldTouched('area', true);
|
formik.setFieldTouched('area', true);
|
||||||
formik.setFieldValue('area', area);
|
formik.setFieldValue('area', area);
|
||||||
|
|
||||||
|
setSelectedArea((area as OptionType)?.value as string);
|
||||||
|
setSelectedLocation('');
|
||||||
|
const disabled = (area as OptionType)?.value == null;
|
||||||
|
setDisabledLocation(disabled);
|
||||||
|
|
||||||
|
formik.setFieldTouched('location_id', false);
|
||||||
|
formik.setFieldValue('location_id', 0);
|
||||||
|
formik.setFieldTouched('location', false);
|
||||||
|
formik.setFieldValue('location', null);
|
||||||
},
|
},
|
||||||
[]
|
[]
|
||||||
);
|
);
|
||||||
@@ -456,6 +436,8 @@ const PurchaseRequestForm = ({
|
|||||||
formik.setFieldValue('location_id', (location as OptionType)?.value || 0);
|
formik.setFieldValue('location_id', (location as OptionType)?.value || 0);
|
||||||
formik.setFieldTouched('location', true);
|
formik.setFieldTouched('location', true);
|
||||||
formik.setFieldValue('location', location);
|
formik.setFieldValue('location', location);
|
||||||
|
|
||||||
|
setSelectedLocation((location as OptionType)?.value as string);
|
||||||
},
|
},
|
||||||
[]
|
[]
|
||||||
);
|
);
|
||||||
@@ -596,10 +578,15 @@ const PurchaseRequestForm = ({
|
|||||||
placeholder='Pilih Lokasi...'
|
placeholder='Pilih Lokasi...'
|
||||||
value={formik.values.location}
|
value={formik.values.location}
|
||||||
onChange={handleLocationChange}
|
onChange={handleLocationChange}
|
||||||
options={locationOptions}
|
options={
|
||||||
|
selectedArea != '' || initialValues?.area?.id
|
||||||
|
? locationOptions
|
||||||
|
: []
|
||||||
|
}
|
||||||
onInputChange={setLocationSelectInputValue}
|
onInputChange={setLocationSelectInputValue}
|
||||||
isLoading={isLoadingLocations}
|
isLoading={isLoadingLocations}
|
||||||
isDisabled={type === 'detail'}
|
onMenuScrollToBottom={loadMoreLocations}
|
||||||
|
isDisabled={type === 'detail' || disabledLocation}
|
||||||
isClearable={type !== 'detail'}
|
isClearable={type !== 'detail'}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
@@ -713,6 +700,7 @@ const PurchaseRequestForm = ({
|
|||||||
options={warehouseOptions}
|
options={warehouseOptions}
|
||||||
onInputChange={setWarehouseSelectInputValue}
|
onInputChange={setWarehouseSelectInputValue}
|
||||||
isLoading={isLoadingWarehouses}
|
isLoading={isLoadingWarehouses}
|
||||||
|
onMenuScrollToBottom={loadMoreWarehouses}
|
||||||
isError={
|
isError={
|
||||||
isRepeaterInputError(idx, 'warehouse_id').isError
|
isRepeaterInputError(idx, 'warehouse_id').isError
|
||||||
}
|
}
|
||||||
@@ -732,9 +720,9 @@ const PurchaseRequestForm = ({
|
|||||||
required
|
required
|
||||||
value={item.product ?? undefined}
|
value={item.product ?? undefined}
|
||||||
onChange={(val) => {
|
onChange={(val) => {
|
||||||
const product = val as ProductOptionType | null;
|
const product = val as OptionType | null;
|
||||||
const productId =
|
const productId =
|
||||||
(product as ProductOptionType)?.value || 0;
|
(product as OptionType)?.value || 0;
|
||||||
|
|
||||||
formik.setFieldTouched(
|
formik.setFieldTouched(
|
||||||
`items.${idx}.product`,
|
`items.${idx}.product`,
|
||||||
|
|||||||
@@ -168,7 +168,7 @@ const DailyMarketingsTable = ({
|
|||||||
];
|
];
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
console.log({ sorting });
|
// console.log({ sorting });
|
||||||
|
|
||||||
if (sorting.length === 1) {
|
if (sorting.length === 1) {
|
||||||
onFilterByChange(sorting[0].id);
|
onFilterByChange(sorting[0].id);
|
||||||
|
|||||||
@@ -177,10 +177,12 @@ interface CustomerPaymentExportPDFParams {
|
|||||||
data: CustomerPaymentReport[];
|
data: CustomerPaymentReport[];
|
||||||
params?: {
|
params?: {
|
||||||
customer_name?: string;
|
customer_name?: string;
|
||||||
sales?: string;
|
// TODO: Uncomment when BE is ready
|
||||||
|
// sales?: string;
|
||||||
start_date?: string;
|
start_date?: string;
|
||||||
end_date?: string;
|
end_date?: string;
|
||||||
filter_by?: string;
|
// TODO: Uncomment when BE is ready
|
||||||
|
// filter_by?: string;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -195,9 +197,10 @@ const getParameterText = (
|
|||||||
paramsText.push('Semua Customer');
|
paramsText.push('Semua Customer');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (params?.sales) {
|
// TODO: Uncomment when BE is ready
|
||||||
paramsText.push(`Sales: ${params.sales}`);
|
// if (params?.sales) {
|
||||||
}
|
// paramsText.push(`Sales: ${params.sales}`);
|
||||||
|
// }
|
||||||
|
|
||||||
if (params?.start_date && params?.end_date) {
|
if (params?.start_date && params?.end_date) {
|
||||||
const startDate = formatDate(params.start_date, 'DD MMM YYYY');
|
const startDate = formatDate(params.start_date, 'DD MMM YYYY');
|
||||||
@@ -242,9 +245,10 @@ const createPDFDocument = (params: CustomerPaymentExportPDFParams) => {
|
|||||||
: '-'}
|
: '-'}
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
<View style={pdfStyles.parameterBadge}>
|
{/* TODO: Uncomment when BE is ready */}
|
||||||
|
{/* <View style={pdfStyles.parameterBadge}>
|
||||||
<Text>Filter Tanggal: Tanggal DO</Text>
|
<Text>Filter Tanggal: Tanggal DO</Text>
|
||||||
</View>
|
</View> */}
|
||||||
<View style={pdfStyles.parameterBadge}>
|
<View style={pdfStyles.parameterBadge}>
|
||||||
<Text>
|
<Text>
|
||||||
Customer: {params.params?.customer_name || 'Semua Customer'}
|
Customer: {params.params?.customer_name || 'Semua Customer'}
|
||||||
@@ -280,7 +284,7 @@ const createPDFDocument = (params: CustomerPaymentExportPDFParams) => {
|
|||||||
<View style={[pdfStyles.tableCellHeader, { flex: 0.8 }]}>
|
<View style={[pdfStyles.tableCellHeader, { flex: 0.8 }]}>
|
||||||
<Text>Aging</Text>
|
<Text>Aging</Text>
|
||||||
</View>
|
</View>
|
||||||
<View style={[pdfStyles.tableCellHeader, { flex: 1 }]}>
|
<View style={[pdfStyles.tableCellHeader, { flex: 1.5 }]}>
|
||||||
<Text>Referensi</Text>
|
<Text>Referensi</Text>
|
||||||
</View>
|
</View>
|
||||||
<View style={[pdfStyles.tableCellHeader, { flex: 1.2 }]}>
|
<View style={[pdfStyles.tableCellHeader, { flex: 1.2 }]}>
|
||||||
@@ -298,15 +302,9 @@ const createPDFDocument = (params: CustomerPaymentExportPDFParams) => {
|
|||||||
<View style={[pdfStyles.tableCellHeaderRight, { flex: 1.2 }]}>
|
<View style={[pdfStyles.tableCellHeaderRight, { flex: 1.2 }]}>
|
||||||
<Text>Harga Awal</Text>
|
<Text>Harga Awal</Text>
|
||||||
</View>
|
</View>
|
||||||
<View style={[pdfStyles.tableCellHeaderRight, { flex: 1 }]}>
|
|
||||||
<Text>CN</Text>
|
|
||||||
</View>
|
|
||||||
<View style={[pdfStyles.tableCellHeaderRight, { flex: 1.2 }]}>
|
<View style={[pdfStyles.tableCellHeaderRight, { flex: 1.2 }]}>
|
||||||
<Text>Harga Akhir</Text>
|
<Text>Harga Akhir</Text>
|
||||||
</View>
|
</View>
|
||||||
<View style={[pdfStyles.tableCellHeaderRight, { flex: 0.8 }]}>
|
|
||||||
<Text>Pajak</Text>
|
|
||||||
</View>
|
|
||||||
<View style={[pdfStyles.tableCellHeaderRight, { flex: 1.2 }]}>
|
<View style={[pdfStyles.tableCellHeaderRight, { flex: 1.2 }]}>
|
||||||
<Text>Total</Text>
|
<Text>Total</Text>
|
||||||
</View>
|
</View>
|
||||||
@@ -343,13 +341,15 @@ const createPDFDocument = (params: CustomerPaymentExportPDFParams) => {
|
|||||||
</View>
|
</View>
|
||||||
<View style={[pdfStyles.tableCellCenter, { flex: 1.2 }]}>
|
<View style={[pdfStyles.tableCellCenter, { flex: 1.2 }]}>
|
||||||
<Text>
|
<Text>
|
||||||
{item.do_date ? formatDate(item.do_date, 'DD MMM YY') : '-'}
|
{item.trans_date
|
||||||
|
? formatDate(item.trans_date, 'DD MMM YY')
|
||||||
|
: '-'}
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
<View style={[pdfStyles.tableCellCenter, { flex: 1.2 }]}>
|
<View style={[pdfStyles.tableCellCenter, { flex: 1.2 }]}>
|
||||||
<Text>
|
<Text>
|
||||||
{item.realization_date
|
{item.delivery_date
|
||||||
? formatDate(item.realization_date, 'DD MMM YY')
|
? formatDate(item.delivery_date, 'DD MMM YY')
|
||||||
: '-'}
|
: '-'}
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
@@ -358,11 +358,15 @@ const createPDFDocument = (params: CustomerPaymentExportPDFParams) => {
|
|||||||
{item.aging_day ? formatNumber(item.aging_day) : '-'} hari
|
{item.aging_day ? formatNumber(item.aging_day) : '-'} hari
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
<View style={[pdfStyles.tableCell, { flex: 1 }]}>
|
<View style={[pdfStyles.tableCell, { flex: 1.5 }]}>
|
||||||
<Text>{item.reference || '-'}</Text>
|
<Text>{item.reference || '-'}</Text>
|
||||||
</View>
|
</View>
|
||||||
<View style={[pdfStyles.tableCell, { flex: 1.2 }]}>
|
<View style={[pdfStyles.tableCell, { flex: 1.2 }]}>
|
||||||
<Text>{item.vehicle_plate || '-'}</Text>
|
<Text>
|
||||||
|
{Array.isArray(item.vehicle_numbers)
|
||||||
|
? item.vehicle_numbers.join(', ')
|
||||||
|
: item.vehicle_numbers || '-'}
|
||||||
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
<View style={[pdfStyles.tableCellRight, { flex: 0.8 }]}>
|
<View style={[pdfStyles.tableCellRight, { flex: 0.8 }]}>
|
||||||
<Text>{formatNumber(item.qty)}</Text>
|
<Text>{formatNumber(item.qty)}</Text>
|
||||||
@@ -376,20 +380,14 @@ const createPDFDocument = (params: CustomerPaymentExportPDFParams) => {
|
|||||||
<View style={[pdfStyles.tableCellRight, { flex: 1.2 }]}>
|
<View style={[pdfStyles.tableCellRight, { flex: 1.2 }]}>
|
||||||
<Text>{formatCurrency(item.price)}</Text>
|
<Text>{formatCurrency(item.price)}</Text>
|
||||||
</View>
|
</View>
|
||||||
<View style={[pdfStyles.tableCellRight, { flex: 1 }]}>
|
|
||||||
<Text>{formatCurrency(item.credit_note)}</Text>
|
|
||||||
</View>
|
|
||||||
<View style={[pdfStyles.tableCellRight, { flex: 1.2 }]}>
|
<View style={[pdfStyles.tableCellRight, { flex: 1.2 }]}>
|
||||||
<Text>{formatCurrency(item.final_price)}</Text>
|
<Text>{formatCurrency(item.final_price)}</Text>
|
||||||
</View>
|
</View>
|
||||||
<View style={[pdfStyles.tableCellRight, { flex: 0.8 }]}>
|
<View style={[pdfStyles.tableCellRight, { flex: 1.2 }]}>
|
||||||
<Text>{formatNumber(item.ppn)}%</Text>
|
<Text>{formatCurrency(item.total_price)}</Text>
|
||||||
</View>
|
</View>
|
||||||
<View style={[pdfStyles.tableCellRight, { flex: 1.2 }]}>
|
<View style={[pdfStyles.tableCellRight, { flex: 1.2 }]}>
|
||||||
<Text>{formatCurrency(item.total)}</Text>
|
<Text>{formatCurrency(item.payment_amount)}</Text>
|
||||||
</View>
|
|
||||||
<View style={[pdfStyles.tableCellRight, { flex: 1.2 }]}>
|
|
||||||
<Text>{formatCurrency(item.payment)}</Text>
|
|
||||||
</View>
|
</View>
|
||||||
<View style={[pdfStyles.tableCellRight, { flex: 1.2 }]}>
|
<View style={[pdfStyles.tableCellRight, { flex: 1.2 }]}>
|
||||||
<Text style={pdfStyles.textError}>
|
<Text style={pdfStyles.textError}>
|
||||||
@@ -397,30 +395,32 @@ const createPDFDocument = (params: CustomerPaymentExportPDFParams) => {
|
|||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
<View style={[pdfStyles.tableCell, { flex: 1.5 }]}>
|
<View style={[pdfStyles.tableCell, { flex: 1.5 }]}>
|
||||||
{item.notes ? (
|
{item.status ? (
|
||||||
<Text>{item.notes}</Text>
|
|
||||||
) : (
|
|
||||||
<View
|
<View
|
||||||
style={[
|
style={[
|
||||||
pdfStyles.badge,
|
pdfStyles.badge,
|
||||||
item.accounts_receivable === 0
|
item.status === 'LUNAS'
|
||||||
? pdfStyles.badgeLunas
|
? pdfStyles.badgeLunas
|
||||||
: pdfStyles.badgeBelumLunas,
|
: pdfStyles.badgeBelumLunas,
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
<Text>
|
<Text>
|
||||||
{item.accounts_receivable === 0
|
{item.status === 'LUNAS' ? 'Lunas' : 'Belum Lunas'}
|
||||||
? 'Lunas'
|
|
||||||
: 'Belum Lunas'}
|
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
|
) : (
|
||||||
|
<Text>-</Text>
|
||||||
)}
|
)}
|
||||||
</View>
|
</View>
|
||||||
<View style={[pdfStyles.tableCell, { flex: 1 }]}>
|
<View style={[pdfStyles.tableCell, { flex: 1 }]}>
|
||||||
<Text>{item.pickup_info || '-'}</Text>
|
<Text>
|
||||||
|
{Array.isArray(item.pickup_info)
|
||||||
|
? item.pickup_info.join(', ')
|
||||||
|
: item.pickup_info || '-'}
|
||||||
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
<View style={[pdfStyles.tableCell, { flex: 1.5 }]}>
|
<View style={[pdfStyles.tableCell, { flex: 1.5 }]}>
|
||||||
<Text>{item.sales_marketing || '-'}</Text>
|
<Text>{item.sales_person || '-'}</Text>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
))}
|
))}
|
||||||
@@ -440,7 +440,7 @@ const createPDFDocument = (params: CustomerPaymentExportPDFParams) => {
|
|||||||
<View style={[pdfStyles.tableCell, { flex: 0.8 }]}>
|
<View style={[pdfStyles.tableCell, { flex: 0.8 }]}>
|
||||||
<Text></Text>
|
<Text></Text>
|
||||||
</View>
|
</View>
|
||||||
<View style={[pdfStyles.tableCell, { flex: 1 }]}>
|
<View style={[pdfStyles.tableCell, { flex: 1.5 }]}>
|
||||||
<Text></Text>
|
<Text></Text>
|
||||||
</View>
|
</View>
|
||||||
<View style={[pdfStyles.tableCell, { flex: 1.2 }]}>
|
<View style={[pdfStyles.tableCell, { flex: 1.2 }]}>
|
||||||
@@ -458,25 +458,13 @@ const createPDFDocument = (params: CustomerPaymentExportPDFParams) => {
|
|||||||
<Text></Text>
|
<Text></Text>
|
||||||
</View>
|
</View>
|
||||||
<View style={[pdfStyles.tableCellRight, { flex: 1.2 }]}>
|
<View style={[pdfStyles.tableCellRight, { flex: 1.2 }]}>
|
||||||
<Text>
|
<Text></Text>
|
||||||
{formatCurrency(
|
|
||||||
customerReport.summary.total_initial_amount
|
|
||||||
)}
|
|
||||||
</Text>
|
|
||||||
</View>
|
|
||||||
<View style={[pdfStyles.tableCellRight, { flex: 1 }]}>
|
|
||||||
<Text>
|
|
||||||
{formatCurrency(customerReport.summary.total_credit_note)}
|
|
||||||
</Text>
|
|
||||||
</View>
|
</View>
|
||||||
<View style={[pdfStyles.tableCellRight, { flex: 1.2 }]}>
|
<View style={[pdfStyles.tableCellRight, { flex: 1.2 }]}>
|
||||||
<Text>
|
<Text>
|
||||||
{formatCurrency(customerReport.summary.total_final_amount)}
|
{formatCurrency(customerReport.summary.total_final_amount)}
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
<View style={[pdfStyles.tableCellRight, { flex: 0.8 }]}>
|
|
||||||
<Text></Text>
|
|
||||||
</View>
|
|
||||||
<View style={[pdfStyles.tableCellRight, { flex: 1.2 }]}>
|
<View style={[pdfStyles.tableCellRight, { flex: 1.2 }]}>
|
||||||
<Text>
|
<Text>
|
||||||
{formatCurrency(customerReport.summary.total_grand_amount)}
|
{formatCurrency(customerReport.summary.total_grand_amount)}
|
||||||
|
|||||||
@@ -24,30 +24,30 @@ export const generateCustomerPaymentExcel = (
|
|||||||
const excelData: { [key: string]: string | number }[] = customerData.map(
|
const excelData: { [key: string]: string | number }[] = customerData.map(
|
||||||
(item, index) => ({
|
(item, index) => ({
|
||||||
No: index + 1,
|
No: index + 1,
|
||||||
'Tanggal DO/Bayar': item.do_date
|
'Tanggal DO/Bayar': item.trans_date
|
||||||
? formatDate(item.do_date, 'DD MMM YYYY')
|
? formatDate(item.trans_date, 'DD MMM YYYY')
|
||||||
: '',
|
: '',
|
||||||
'Tanggal Realisasi': item.realization_date
|
'Tanggal Realisasi': item.delivery_date
|
||||||
? formatDate(item.realization_date, 'DD MMM YYYY')
|
? formatDate(item.delivery_date, 'DD MMM YYYY')
|
||||||
: '',
|
: '',
|
||||||
Aging: formatNumber(item.aging_day || 0),
|
Aging: formatNumber(item.aging_day || 0),
|
||||||
Referensi: item.reference || '',
|
Referensi: item.reference || '',
|
||||||
'Nomor Polisi': Array.isArray(item.vehicle_plate)
|
'Nomor Polisi': Array.isArray(item.vehicle_numbers)
|
||||||
? item.vehicle_plate.join(', ')
|
? item.vehicle_numbers.join(', ')
|
||||||
: '',
|
: '',
|
||||||
'Ekor/Qty': formatNumber(item.qty || 0),
|
'Ekor/Qty': formatNumber(item.qty || 0),
|
||||||
'Berat (Kg)': formatNumber(item.weight || 0),
|
'Berat (Kg)': formatNumber(item.weight || 0),
|
||||||
AVG: formatNumber(item.average_weight || 0),
|
AVG: formatNumber(item.average_weight || 0),
|
||||||
'Harga Awal': formatCurrency(item.price || 0),
|
'Harga Awal': formatCurrency(item.price || 0),
|
||||||
CN: formatCurrency(item.credit_note || 0),
|
|
||||||
'Harga Akhir': formatCurrency(item.final_price || 0),
|
'Harga Akhir': formatCurrency(item.final_price || 0),
|
||||||
'PPN (%)': formatNumber(item.ppn || 0),
|
Total: formatCurrency(item.total_price || 0),
|
||||||
Total: formatCurrency(item.total || 0),
|
Pembayaran: formatCurrency(item.payment_amount || 0),
|
||||||
Pembayaran: formatCurrency(item.payment || 0),
|
|
||||||
'Saldo Piutang': formatCurrency(item.accounts_receivable || 0),
|
'Saldo Piutang': formatCurrency(item.accounts_receivable || 0),
|
||||||
Keterangan: item.notes || '',
|
Keterangan: item.status || '',
|
||||||
Pengambilan: item.pickup_info || '',
|
Pengambilan: Array.isArray(item.pickup_info)
|
||||||
'Sales/Marketing': item.sales_marketing || '',
|
? item.pickup_info.join(', ')
|
||||||
|
: '',
|
||||||
|
'Sales/Marketing': item.sales_person || '',
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -62,14 +62,10 @@ export const generateCustomerPaymentExcel = (
|
|||||||
'Ekor/Qty': formatNumber(customerReport.summary.total_qty || 0),
|
'Ekor/Qty': formatNumber(customerReport.summary.total_qty || 0),
|
||||||
'Berat (Kg)': formatNumber(customerReport.summary.total_weight || 0),
|
'Berat (Kg)': formatNumber(customerReport.summary.total_weight || 0),
|
||||||
AVG: '',
|
AVG: '',
|
||||||
'Harga Awal': formatCurrency(
|
'Harga Awal': '',
|
||||||
customerReport.summary.total_initial_amount || 0
|
|
||||||
),
|
|
||||||
CN: formatCurrency(customerReport.summary.total_credit_note || 0),
|
|
||||||
'Harga Akhir': formatCurrency(
|
'Harga Akhir': formatCurrency(
|
||||||
customerReport.summary.total_final_amount || 0
|
customerReport.summary.total_final_amount || 0
|
||||||
),
|
),
|
||||||
'PPN (%)': '',
|
|
||||||
Total: formatCurrency(customerReport.summary.total_grand_amount || 0),
|
Total: formatCurrency(customerReport.summary.total_grand_amount || 0),
|
||||||
Pembayaran: formatCurrency(customerReport.summary.total_payment || 0),
|
Pembayaran: formatCurrency(customerReport.summary.total_payment || 0),
|
||||||
'Saldo Piutang': formatCurrency(
|
'Saldo Piutang': formatCurrency(
|
||||||
@@ -94,9 +90,7 @@ export const generateCustomerPaymentExcel = (
|
|||||||
{ wch: 12 }, // Berat
|
{ wch: 12 }, // Berat
|
||||||
{ wch: 10 }, // AVG
|
{ wch: 10 }, // AVG
|
||||||
{ wch: 15 }, // Harga Awal
|
{ wch: 15 }, // Harga Awal
|
||||||
{ wch: 10 }, // CN
|
|
||||||
{ wch: 15 }, // Harga Akhir
|
{ wch: 15 }, // Harga Akhir
|
||||||
{ wch: 10 }, // PPN
|
|
||||||
{ wch: 15 }, // Total
|
{ wch: 15 }, // Total
|
||||||
{ wch: 15 }, // Pembayaran
|
{ wch: 15 }, // Pembayaran
|
||||||
{ wch: 15 }, // Saldo Piutang
|
{ wch: 15 }, // Saldo Piutang
|
||||||
|
|||||||
@@ -47,6 +47,8 @@ const CustomerPaymentTab = () => {
|
|||||||
const [filterCustomer, setFilterCustomer] = useState<typeof customerOptions>(
|
const [filterCustomer, setFilterCustomer] = useState<typeof customerOptions>(
|
||||||
[]
|
[]
|
||||||
);
|
);
|
||||||
|
// TODO: Uncomment when BE is ready
|
||||||
|
// const [filterSales, setFilterSales] = useState<typeof salesOptions>([]);
|
||||||
const [filterSales, setFilterSales] = useState<typeof salesOptions>([]);
|
const [filterSales, setFilterSales] = useState<typeof salesOptions>([]);
|
||||||
const [filterStartDate, setFilterStartDate] = useState('');
|
const [filterStartDate, setFilterStartDate] = useState('');
|
||||||
const [filterEndDate, setFilterEndDate] = useState('');
|
const [filterEndDate, setFilterEndDate] = useState('');
|
||||||
@@ -61,6 +63,7 @@ const CustomerPaymentTab = () => {
|
|||||||
hasMore: hasMoreCustomers,
|
hasMore: hasMoreCustomers,
|
||||||
} = useSelect(CustomerApi.basePath, 'id', 'name', 'search');
|
} = useSelect(CustomerApi.basePath, 'id', 'name', 'search');
|
||||||
|
|
||||||
|
// TODO: Uncomment when BE is ready
|
||||||
const {
|
const {
|
||||||
options: salesOptions,
|
options: salesOptions,
|
||||||
setInputValue: setSalesInputValue,
|
setInputValue: setSalesInputValue,
|
||||||
@@ -135,23 +138,18 @@ const CustomerPaymentTab = () => {
|
|||||||
count += 1;
|
count += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sales filter
|
// TODO: Uncomment when BE is ready
|
||||||
if (filterSales.length > 0) {
|
// // Sales filter
|
||||||
count += 1;
|
// if (filterSales.length > 0) {
|
||||||
}
|
// count += 1;
|
||||||
|
// }
|
||||||
// Filter by (always count if submitted)
|
|
||||||
if (isSubmitted) {
|
|
||||||
count += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return count;
|
return count;
|
||||||
}, [
|
}, [
|
||||||
filterStartDate,
|
filterStartDate,
|
||||||
filterEndDate,
|
filterEndDate,
|
||||||
filterCustomer,
|
filterCustomer,
|
||||||
filterSales,
|
// filterSales,
|
||||||
isSubmitted,
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const hasFilters = activeFiltersCount > 0;
|
const hasFilters = activeFiltersCount > 0;
|
||||||
@@ -165,11 +163,12 @@ const CustomerPaymentTab = () => {
|
|||||||
filterCustomer.length > 0
|
filterCustomer.length > 0
|
||||||
? filterCustomer.map((v) => String(v.value)).join(',')
|
? filterCustomer.map((v) => String(v.value)).join(',')
|
||||||
: undefined,
|
: undefined,
|
||||||
sales_id:
|
// TODO: Uncomment when BE is ready
|
||||||
filterSales.length > 0
|
// sales_id:
|
||||||
? filterSales.map((v) => String(v.value)).join(',')
|
// filterSales.length > 0
|
||||||
: undefined,
|
// ? filterSales.map((v) => String(v.value)).join(',')
|
||||||
filter_by: 'do_date' as const,
|
// : undefined,
|
||||||
|
// filter_by: 'do_date' as const,
|
||||||
start_date: filterStartDate || undefined,
|
start_date: filterStartDate || undefined,
|
||||||
end_date: filterEndDate || undefined,
|
end_date: filterEndDate || undefined,
|
||||||
page: currentPage,
|
page: currentPage,
|
||||||
@@ -182,8 +181,8 @@ const CustomerPaymentTab = () => {
|
|||||||
([, params]) =>
|
([, params]) =>
|
||||||
FinanceApi.getCustomerPaymentReport(
|
FinanceApi.getCustomerPaymentReport(
|
||||||
params.customer_id,
|
params.customer_id,
|
||||||
params.sales_id,
|
undefined, // TODO: Change to params.sales_id when BE is ready
|
||||||
params.filter_by,
|
undefined, // TODO: Change to params.filter_by when BE is ready
|
||||||
params.start_date,
|
params.start_date,
|
||||||
params.end_date,
|
params.end_date,
|
||||||
params.page,
|
params.page,
|
||||||
@@ -208,11 +207,11 @@ const CustomerPaymentTab = () => {
|
|||||||
filterCustomer.length > 0
|
filterCustomer.length > 0
|
||||||
? filterCustomer.map((v) => String(v.value)).join(',')
|
? filterCustomer.map((v) => String(v.value)).join(',')
|
||||||
: undefined,
|
: undefined,
|
||||||
sales_id:
|
// TODO: Uncomment when BE is ready
|
||||||
filterSales.length > 0
|
// sales_id:
|
||||||
? filterSales.map((v) => String(v.value)).join(',')
|
// filterSales.length > 0
|
||||||
: undefined,
|
// ? filterSales.map((v) => String(v.value)).join(',')
|
||||||
filter_by: 'do_date' as const,
|
// : undefined,
|
||||||
start_date: filterStartDate || undefined,
|
start_date: filterStartDate || undefined,
|
||||||
end_date: filterEndDate || undefined,
|
end_date: filterEndDate || undefined,
|
||||||
limit: 100,
|
limit: 100,
|
||||||
@@ -221,8 +220,8 @@ const CustomerPaymentTab = () => {
|
|||||||
|
|
||||||
const response = await FinanceApi.getCustomerPaymentReport(
|
const response = await FinanceApi.getCustomerPaymentReport(
|
||||||
params.customer_id,
|
params.customer_id,
|
||||||
params.sales_id,
|
undefined, // TODO: Change to params.sales_id when BE is ready
|
||||||
params.filter_by,
|
undefined, // TODO: Change to params.filter_by when BE is ready
|
||||||
params.start_date,
|
params.start_date,
|
||||||
params.end_date,
|
params.end_date,
|
||||||
params.page,
|
params.page,
|
||||||
@@ -279,13 +278,15 @@ const CustomerPaymentTab = () => {
|
|||||||
filterCustomer.length > 0
|
filterCustomer.length > 0
|
||||||
? filterCustomer.map((c) => c.label).join(', ')
|
? filterCustomer.map((c) => c.label).join(', ')
|
||||||
: undefined,
|
: undefined,
|
||||||
sales:
|
// TODO: Uncomment when BE is ready
|
||||||
filterSales.length > 0
|
// sales:
|
||||||
? filterSales.map((s) => s.label).join(', ')
|
// filterSales.length > 0
|
||||||
: undefined,
|
// ? filterSales.map((s) => s.label).join(', ')
|
||||||
|
// : undefined,
|
||||||
start_date: filterStartDate || undefined,
|
start_date: filterStartDate || undefined,
|
||||||
end_date: filterEndDate || undefined,
|
end_date: filterEndDate || undefined,
|
||||||
filter_by: 'do_date',
|
// TODO: Uncomment when BE is ready
|
||||||
|
// filter_by: 'do_date' as const,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
toast.success('PDF berhasil dibuat dan diunduh.');
|
toast.success('PDF berhasil dibuat dan diunduh.');
|
||||||
@@ -303,36 +304,39 @@ const CustomerPaymentTab = () => {
|
|||||||
{
|
{
|
||||||
id: 'no',
|
id: 'no',
|
||||||
header: 'No',
|
header: 'No',
|
||||||
cell: (props) => props.row.index + 1,
|
cell: (props) => props.row.index,
|
||||||
footer: () => <div className='font-semibold text-gray-900'>Total</div>,
|
footer: () => <div className='font-semibold text-gray-900'>Total</div>,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'do_date_or_payment_date',
|
id: 'do_date_or_payment_date',
|
||||||
header: 'Tanggal DO/Bayar',
|
header: 'Tanggal Jual/Bayar',
|
||||||
accessorKey: 'do_date',
|
accessorKey: 'trans_date',
|
||||||
|
enableSorting: false,
|
||||||
cell: (props) => {
|
cell: (props) => {
|
||||||
const value = props.row.original.do_date;
|
const value = props.row.original.trans_date;
|
||||||
return formatDate(value, 'DD MMM YYYY');
|
return value ? formatDate(value, 'DD MMM YYYY') : '-';
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'realization_date',
|
id: 'realization_date',
|
||||||
header: 'Tanggal Realisasi',
|
header: 'Tanggal Realisasi',
|
||||||
accessorKey: 'realization_date',
|
accessorKey: 'delivery_date',
|
||||||
|
enableSorting: false,
|
||||||
cell: (props) => {
|
cell: (props) => {
|
||||||
const value = props.row.original.realization_date;
|
const value = props.row.original.delivery_date;
|
||||||
return formatDate(value, 'DD MMM YYYY');
|
return value ? formatDate(value, 'DD MMM YYYY') : '-';
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'aging',
|
id: 'aging',
|
||||||
header: 'Aging',
|
header: 'Aging',
|
||||||
accessorKey: 'aging_day',
|
accessorKey: 'aging_day',
|
||||||
|
enableSorting: false,
|
||||||
cell: (props) => {
|
cell: (props) => {
|
||||||
const value = props.row.original.aging_day;
|
const value = props.row.original.aging_day;
|
||||||
return (
|
return (
|
||||||
<div className='text-center'>
|
<div className='text-center'>
|
||||||
{value ? formatNumber(value) : '-'} hari
|
{value && value > 0 ? `${formatNumber(value)} hari` : '-'}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@@ -341,6 +345,7 @@ const CustomerPaymentTab = () => {
|
|||||||
id: 'reference',
|
id: 'reference',
|
||||||
header: 'Referensi',
|
header: 'Referensi',
|
||||||
accessorKey: 'reference',
|
accessorKey: 'reference',
|
||||||
|
enableSorting: false,
|
||||||
cell: (props) => {
|
cell: (props) => {
|
||||||
const value = props.row.original.reference;
|
const value = props.row.original.reference;
|
||||||
return value || '-';
|
return value || '-';
|
||||||
@@ -349,16 +354,18 @@ const CustomerPaymentTab = () => {
|
|||||||
{
|
{
|
||||||
id: 'vehicle_plate',
|
id: 'vehicle_plate',
|
||||||
header: 'Nomor Polisi',
|
header: 'Nomor Polisi',
|
||||||
accessorKey: 'vehicle_plate',
|
accessorKey: 'vehicle_numbers',
|
||||||
|
enableSorting: false,
|
||||||
cell: (props) => {
|
cell: (props) => {
|
||||||
const value = props.row.original.vehicle_plate;
|
const value = props.row.original.vehicle_numbers;
|
||||||
return value || '-';
|
return Array.isArray(value) ? value.join(', ') : value || '-';
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'qty',
|
id: 'qty',
|
||||||
header: 'Ekor/Qty',
|
header: 'Qty',
|
||||||
accessorKey: 'qty',
|
accessorKey: 'qty',
|
||||||
|
enableSorting: false,
|
||||||
cell: (props) => {
|
cell: (props) => {
|
||||||
const value = props.row.original.qty;
|
const value = props.row.original.qty;
|
||||||
return <div className='text-right'>{formatNumber(value)}</div>;
|
return <div className='text-right'>{formatNumber(value)}</div>;
|
||||||
@@ -373,6 +380,7 @@ const CustomerPaymentTab = () => {
|
|||||||
id: 'weight',
|
id: 'weight',
|
||||||
header: 'Berat (Kg)',
|
header: 'Berat (Kg)',
|
||||||
accessorKey: 'weight',
|
accessorKey: 'weight',
|
||||||
|
enableSorting: false,
|
||||||
cell: (props) => {
|
cell: (props) => {
|
||||||
const value = props.row.original.weight;
|
const value = props.row.original.weight;
|
||||||
return <div className='text-right'>{formatNumber(value)}</div>;
|
return <div className='text-right'>{formatNumber(value)}</div>;
|
||||||
@@ -387,6 +395,7 @@ const CustomerPaymentTab = () => {
|
|||||||
id: 'average_weight',
|
id: 'average_weight',
|
||||||
header: 'AVG',
|
header: 'AVG',
|
||||||
accessorKey: 'average_weight',
|
accessorKey: 'average_weight',
|
||||||
|
enableSorting: false,
|
||||||
cell: (props) => {
|
cell: (props) => {
|
||||||
const value = props.row.original.average_weight;
|
const value = props.row.original.average_weight;
|
||||||
return <div className='text-right'>{formatNumber(value)}</div>;
|
return <div className='text-right'>{formatNumber(value)}</div>;
|
||||||
@@ -399,34 +408,20 @@ const CustomerPaymentTab = () => {
|
|||||||
id: 'price',
|
id: 'price',
|
||||||
header: 'Harga Awal',
|
header: 'Harga Awal',
|
||||||
accessorKey: 'price',
|
accessorKey: 'price',
|
||||||
|
enableSorting: false,
|
||||||
cell: (props) => {
|
cell: (props) => {
|
||||||
const value = props.row.original.price;
|
const value = props.row.original.price;
|
||||||
return <div className='text-right'>{formatCurrency(value)}</div>;
|
return <div className='text-right'>{formatCurrency(value)}</div>;
|
||||||
},
|
},
|
||||||
footer: () => (
|
footer: () => (
|
||||||
<div className='text-right font-semibold text-gray-900'>
|
<div className='text-right font-semibold text-gray-900'>-</div>
|
||||||
{formatCurrency(summary.total_initial_amount) || '-'}
|
|
||||||
</div>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'credit_note',
|
|
||||||
header: 'CN',
|
|
||||||
accessorKey: 'credit_note',
|
|
||||||
cell: (props) => {
|
|
||||||
const value = props.row.original.credit_note;
|
|
||||||
return <div className='text-right'>{formatCurrency(value)}</div>;
|
|
||||||
},
|
|
||||||
footer: () => (
|
|
||||||
<div className='text-right font-semibold text-gray-900'>
|
|
||||||
{formatCurrency(summary.total_credit_note) || '-'}
|
|
||||||
</div>
|
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'final_price',
|
id: 'final_price',
|
||||||
header: 'Harga Akhir',
|
header: 'Harga Akhir',
|
||||||
accessorKey: 'final_price',
|
accessorKey: 'final_price',
|
||||||
|
enableSorting: false,
|
||||||
cell: (props) => {
|
cell: (props) => {
|
||||||
const value = props.row.original.final_price;
|
const value = props.row.original.final_price;
|
||||||
return <div className='text-right'>{formatCurrency(value)}</div>;
|
return <div className='text-right'>{formatCurrency(value)}</div>;
|
||||||
@@ -437,24 +432,13 @@ const CustomerPaymentTab = () => {
|
|||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
|
||||||
id: 'ppn',
|
|
||||||
header: 'PPN (%)',
|
|
||||||
accessorKey: 'ppn',
|
|
||||||
cell: (props) => {
|
|
||||||
const value = props.row.original.ppn;
|
|
||||||
return <div className='text-right'>{formatNumber(value)}%</div>;
|
|
||||||
},
|
|
||||||
footer: () => (
|
|
||||||
<div className='text-right font-semibold text-gray-900'>-</div>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
id: 'total',
|
id: 'total',
|
||||||
header: 'Total',
|
header: 'Total',
|
||||||
accessorKey: 'total',
|
accessorKey: 'total_price',
|
||||||
|
enableSorting: false,
|
||||||
cell: (props) => {
|
cell: (props) => {
|
||||||
const value = props.row.original.total;
|
const value = props.row.original.total_price;
|
||||||
return <div className='text-right'>{formatCurrency(value)}</div>;
|
return <div className='text-right'>{formatCurrency(value)}</div>;
|
||||||
},
|
},
|
||||||
footer: () => (
|
footer: () => (
|
||||||
@@ -466,9 +450,10 @@ const CustomerPaymentTab = () => {
|
|||||||
{
|
{
|
||||||
id: 'payment',
|
id: 'payment',
|
||||||
header: 'Pembayaran',
|
header: 'Pembayaran',
|
||||||
accessorKey: 'payment',
|
accessorKey: 'payment_amount',
|
||||||
|
enableSorting: false,
|
||||||
cell: (props) => {
|
cell: (props) => {
|
||||||
const value = props.row.original.payment;
|
const value = props.row.original.payment_amount;
|
||||||
return <div className='text-right'>{formatCurrency(value)}</div>;
|
return <div className='text-right'>{formatCurrency(value)}</div>;
|
||||||
},
|
},
|
||||||
footer: () => (
|
footer: () => (
|
||||||
@@ -481,14 +466,25 @@ const CustomerPaymentTab = () => {
|
|||||||
id: 'accounts_receivable',
|
id: 'accounts_receivable',
|
||||||
header: 'Saldo Piutang',
|
header: 'Saldo Piutang',
|
||||||
accessorKey: 'accounts_receivable',
|
accessorKey: 'accounts_receivable',
|
||||||
|
enableSorting: false,
|
||||||
cell: (props) => {
|
cell: (props) => {
|
||||||
const value = props.row.original.accounts_receivable;
|
const value = props.row.original.accounts_receivable;
|
||||||
return (
|
return (
|
||||||
<div className='text-right text-error'>{formatCurrency(value)}</div>
|
<div
|
||||||
|
className={`text-right font-semibold ${
|
||||||
|
value < 0 ? 'text-error' : ''
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{formatCurrency(value)}
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
footer: () => (
|
footer: () => (
|
||||||
<div className='text-right font-semibold text-gray-900'>
|
<div
|
||||||
|
className={`text-right font-semibold ${
|
||||||
|
summary.total_accounts_receivable < 0 ? 'text-error' : ''
|
||||||
|
}`}
|
||||||
|
>
|
||||||
{formatCurrency(summary.total_accounts_receivable) || '-'}
|
{formatCurrency(summary.total_accounts_receivable) || '-'}
|
||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
@@ -496,9 +492,10 @@ const CustomerPaymentTab = () => {
|
|||||||
{
|
{
|
||||||
id: 'notes',
|
id: 'notes',
|
||||||
header: 'Keterangan',
|
header: 'Keterangan',
|
||||||
accessorKey: 'notes',
|
accessorKey: 'status',
|
||||||
|
enableSorting: false,
|
||||||
cell: (props) => {
|
cell: (props) => {
|
||||||
const value = props.row.original.notes;
|
const value = props.row.original.status;
|
||||||
|
|
||||||
if (!value) {
|
if (!value) {
|
||||||
return '-';
|
return '-';
|
||||||
@@ -513,7 +510,7 @@ const CustomerPaymentTab = () => {
|
|||||||
status: getPaymentStatusIndicatorColor(value),
|
status: getPaymentStatusIndicatorColor(value),
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{getPaymentStatusText(value)}
|
<span>{getPaymentStatusText(value)}</span>
|
||||||
</Badge>
|
</Badge>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@@ -522,17 +519,19 @@ const CustomerPaymentTab = () => {
|
|||||||
id: 'pickup_info',
|
id: 'pickup_info',
|
||||||
header: 'Pengambilan',
|
header: 'Pengambilan',
|
||||||
accessorKey: 'pickup_info',
|
accessorKey: 'pickup_info',
|
||||||
|
enableSorting: false,
|
||||||
cell: (props) => {
|
cell: (props) => {
|
||||||
const value = props.row.original.pickup_info;
|
const value = props.row.original.pickup_info;
|
||||||
return value || '-';
|
return Array.isArray(value) ? value.join(', ') : value || '-';
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'sales_marketing',
|
id: 'sales_marketing',
|
||||||
header: 'Sales/Marketing',
|
header: 'Sales/Marketing',
|
||||||
accessorKey: 'sales_marketing',
|
accessorKey: 'sales_person',
|
||||||
|
enableSorting: false,
|
||||||
cell: (props) => {
|
cell: (props) => {
|
||||||
const value = props.row.original.sales_marketing;
|
const value = props.row.original.sales_person;
|
||||||
return value || '-';
|
return value || '-';
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -664,7 +663,8 @@ const CustomerPaymentTab = () => {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
{/* TODO: Uncomment when BE is ready */}
|
||||||
|
{/* <div>
|
||||||
<SelectInputCheckbox
|
<SelectInputCheckbox
|
||||||
label='Sales'
|
label='Sales'
|
||||||
placeholder='Pilih Sales'
|
placeholder='Pilih Sales'
|
||||||
@@ -679,9 +679,10 @@ const CustomerPaymentTab = () => {
|
|||||||
onMenuScrollToBottom={loadMoreSales}
|
onMenuScrollToBottom={loadMoreSales}
|
||||||
className={{ wrapper: 'w-full' }}
|
className={{ wrapper: 'w-full' }}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div> */}
|
||||||
|
|
||||||
<div>
|
{/* TODO: Uncomment when BE is ready */}
|
||||||
|
{/* <div>
|
||||||
<SelectInput
|
<SelectInput
|
||||||
label='Filter Berdasarkan'
|
label='Filter Berdasarkan'
|
||||||
placeholder='Pilih Filter Berdasarkan'
|
placeholder='Pilih Filter Berdasarkan'
|
||||||
@@ -690,7 +691,7 @@ const CustomerPaymentTab = () => {
|
|||||||
isDisabled={true}
|
isDisabled={true}
|
||||||
className={{ wrapper: 'w-full' }}
|
className={{ wrapper: 'w-full' }}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div> */}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Action Buttons */}
|
{/* Action Buttons */}
|
||||||
@@ -730,10 +731,7 @@ const CustomerPaymentTab = () => {
|
|||||||
const summary = customerReport.summary || {
|
const summary = customerReport.summary || {
|
||||||
total_qty: 0,
|
total_qty: 0,
|
||||||
total_weight: 0,
|
total_weight: 0,
|
||||||
total_initial_amount: 0,
|
|
||||||
total_credit_note: 0,
|
|
||||||
total_final_amount: 0,
|
total_final_amount: 0,
|
||||||
total_ppn: 0,
|
|
||||||
total_grand_amount: 0,
|
total_grand_amount: 0,
|
||||||
total_payment: 0,
|
total_payment: 0,
|
||||||
total_accounts_receivable: 0,
|
total_accounts_receivable: 0,
|
||||||
@@ -745,19 +743,27 @@ const CustomerPaymentTab = () => {
|
|||||||
<Card
|
<Card
|
||||||
key={customerReport.customer.id}
|
key={customerReport.customer.id}
|
||||||
title={customerReport.customer.name}
|
title={customerReport.customer.name}
|
||||||
|
subtitle={`(${customerReport.customer.address})`}
|
||||||
className={{
|
className={{
|
||||||
wrapper: 'w-full rounded-2xl',
|
wrapper: 'w-full rounded-2xl',
|
||||||
body: 'p-0',
|
body: 'p-0',
|
||||||
title:
|
title:
|
||||||
'py-1.5 px-3 bg-[#0069E0] text-white text-lg font-normal',
|
'py-1.5 px-3 bg-[#0069E0] text-white text-lg font-normal',
|
||||||
|
subtitle:
|
||||||
|
'px-3 pb-1 bg-[#0069E0] text-white text-sm font-normal',
|
||||||
}}
|
}}
|
||||||
variant='bordered'
|
variant='bordered'
|
||||||
collapsible={true}
|
collapsible={true}
|
||||||
>
|
>
|
||||||
<Table
|
<Table
|
||||||
data={customerReport.rows}
|
data={[
|
||||||
|
{
|
||||||
|
accounts_receivable: customerReport.initial_balance,
|
||||||
|
} as CustomerPaymentReport['rows'][0],
|
||||||
|
...customerReport.rows,
|
||||||
|
]}
|
||||||
columns={tableColumns}
|
columns={tableColumns}
|
||||||
pageSize={10}
|
pageSize={customerReport.rows.length + 1}
|
||||||
renderFooter={customerReport.rows.length > 0}
|
renderFooter={customerReport.rows.length > 0}
|
||||||
className={{
|
className={{
|
||||||
containerClassName: 'w-full',
|
containerClassName: 'w-full',
|
||||||
@@ -777,6 +783,36 @@ const CustomerPaymentTab = () => {
|
|||||||
'px-4 py-3 text-xs text-gray-900 whitespace-nowrap',
|
'px-4 py-3 text-xs text-gray-900 whitespace-nowrap',
|
||||||
paginationClassName: 'hidden',
|
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={13}
|
||||||
|
></td>
|
||||||
|
<td className='px-4 py-3 text-xs whitespace-nowrap'>
|
||||||
|
<div
|
||||||
|
className={`text-right ${
|
||||||
|
row.original.accounts_receivable < 0
|
||||||
|
? 'text-error'
|
||||||
|
: ''
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{formatCurrency(row.original.accounts_receivable)}
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td
|
||||||
|
className='px-4 py-3 text-xs text-gray-900 whitespace-nowrap'
|
||||||
|
colSpan={4}
|
||||||
|
></td>
|
||||||
|
</tr>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -226,7 +226,7 @@ const createPDFDocument = (
|
|||||||
<Text>Rentang BW</Text>
|
<Text>Rentang BW</Text>
|
||||||
</View>
|
</View>
|
||||||
<View style={[pdfStyles.tableCellHeaderRight, { flex: 1 }]}>
|
<View style={[pdfStyles.tableCellHeaderRight, { flex: 1 }]}>
|
||||||
<Text>Sisa Ekor</Text>
|
<Text>Sisa Butir</Text>
|
||||||
</View>
|
</View>
|
||||||
<View style={[pdfStyles.tableCellHeaderRight, { flex: 1 }]}>
|
<View style={[pdfStyles.tableCellHeaderRight, { flex: 1 }]}>
|
||||||
<Text>Sisa Kg</Text>
|
<Text>Sisa Kg</Text>
|
||||||
@@ -234,12 +234,6 @@ const createPDFDocument = (
|
|||||||
<View style={[pdfStyles.tableCellHeaderRight, { flex: 1.2 }]}>
|
<View style={[pdfStyles.tableCellHeaderRight, { flex: 1.2 }]}>
|
||||||
<Text>Rata-Rata Bobot (Kg)</Text>
|
<Text>Rata-Rata Bobot (Kg)</Text>
|
||||||
</View>
|
</View>
|
||||||
<View style={[pdfStyles.tableCellHeaderRight, { flex: 1 }]}>
|
|
||||||
<Text>Produksi Telur (Butir)</Text>
|
|
||||||
</View>
|
|
||||||
<View style={[pdfStyles.tableCellHeaderRight, { flex: 1 }]}>
|
|
||||||
<Text>Produksi Telur (Kg)</Text>
|
|
||||||
</View>
|
|
||||||
<View style={[pdfStyles.tableCellHeader, { flex: 1.5 }]}>
|
<View style={[pdfStyles.tableCellHeader, { flex: 1.5 }]}>
|
||||||
<Text>Feed (Supplier)</Text>
|
<Text>Feed (Supplier)</Text>
|
||||||
</View>
|
</View>
|
||||||
@@ -249,12 +243,6 @@ const createPDFDocument = (
|
|||||||
<View style={[pdfStyles.tableCellHeaderRight, { flex: 1.2 }]}>
|
<View style={[pdfStyles.tableCellHeaderRight, { flex: 1.2 }]}>
|
||||||
<Text>Rata-Rata Harga DOC</Text>
|
<Text>Rata-Rata Harga DOC</Text>
|
||||||
</View>
|
</View>
|
||||||
<View style={[pdfStyles.tableCellHeaderRight, { flex: 1.2 }]}>
|
|
||||||
<Text>Nilai Nominal Telur</Text>
|
|
||||||
</View>
|
|
||||||
<View style={[pdfStyles.tableCellHeaderRight, { flex: 1 }]}>
|
|
||||||
<Text>HPP Ayam</Text>
|
|
||||||
</View>
|
|
||||||
<View style={[pdfStyles.tableCellHeaderRight, { flex: 1.2 }]}>
|
<View style={[pdfStyles.tableCellHeaderRight, { flex: 1.2 }]}>
|
||||||
<Text>HPP Telur (RP/KG)</Text>
|
<Text>HPP Telur (RP/KG)</Text>
|
||||||
</View>
|
</View>
|
||||||
@@ -278,23 +266,15 @@ const createPDFDocument = (
|
|||||||
<View style={[pdfStyles.tableCellCenter, { flex: 1.2 }]}>
|
<View style={[pdfStyles.tableCellCenter, { flex: 1.2 }]}>
|
||||||
<Text>{group.label}</Text>
|
<Text>{group.label}</Text>
|
||||||
</View>
|
</View>
|
||||||
<View style={[pdfStyles.tableCellRight, { flex: 1 }]}>
|
|
||||||
<Text>{formatNumber(group.remaining_chicken_birds)}</Text>
|
|
||||||
</View>
|
|
||||||
<View style={[pdfStyles.tableCellRight, { flex: 1 }]}>
|
|
||||||
<Text>
|
|
||||||
{formatNumber(group.remaining_chicken_weight_kg)}
|
|
||||||
</Text>
|
|
||||||
</View>
|
|
||||||
<View style={[pdfStyles.tableCellRight, { flex: 1.2 }]}>
|
|
||||||
<Text>{formatNumber(group.avg_weight_kg)}</Text>
|
|
||||||
</View>
|
|
||||||
<View style={[pdfStyles.tableCellRight, { flex: 1 }]}>
|
<View style={[pdfStyles.tableCellRight, { flex: 1 }]}>
|
||||||
<Text>{formatNumber(group.egg_production_pieces)}</Text>
|
<Text>{formatNumber(group.egg_production_pieces)}</Text>
|
||||||
</View>
|
</View>
|
||||||
<View style={[pdfStyles.tableCellRight, { flex: 1 }]}>
|
<View style={[pdfStyles.tableCellRight, { flex: 1 }]}>
|
||||||
<Text>{formatNumber(group.egg_production_kg)}</Text>
|
<Text>{formatNumber(group.egg_production_kg)}</Text>
|
||||||
</View>
|
</View>
|
||||||
|
<View style={[pdfStyles.tableCellRight, { flex: 1.2 }]}>
|
||||||
|
<Text>{formatNumber(group.avg_weight_kg)}</Text>
|
||||||
|
</View>
|
||||||
<View style={[pdfStyles.tableCell, { flex: 1.5 }]}>
|
<View style={[pdfStyles.tableCell, { flex: 1.5 }]}>
|
||||||
<Text>
|
<Text>
|
||||||
{group.feed_suppliers
|
{group.feed_suppliers
|
||||||
@@ -318,17 +298,11 @@ const createPDFDocument = (
|
|||||||
<View style={[pdfStyles.tableCellRight, { flex: 1.2 }]}>
|
<View style={[pdfStyles.tableCellRight, { flex: 1.2 }]}>
|
||||||
<Text>{formatCurrency(group.average_doc_price_rp)}</Text>
|
<Text>{formatCurrency(group.average_doc_price_rp)}</Text>
|
||||||
</View>
|
</View>
|
||||||
<View style={[pdfStyles.tableCellRight, { flex: 1.2 }]}>
|
|
||||||
<Text>{formatCurrency(group.egg_value_rp)}</Text>
|
|
||||||
</View>
|
|
||||||
<View style={[pdfStyles.tableCellRight, { flex: 1 }]}>
|
|
||||||
<Text>{formatCurrency(group.hpp_rp)}</Text>
|
|
||||||
</View>
|
|
||||||
<View style={[pdfStyles.tableCellRight, { flex: 1.2 }]}>
|
<View style={[pdfStyles.tableCellRight, { flex: 1.2 }]}>
|
||||||
<Text>{formatCurrency(group.egg_hpp_rp_per_kg)}</Text>
|
<Text>{formatCurrency(group.egg_hpp_rp_per_kg)}</Text>
|
||||||
</View>
|
</View>
|
||||||
<View style={[pdfStyles.tableCellRight, { flex: 1.2 }]}>
|
<View style={[pdfStyles.tableCellRight, { flex: 1.2 }]}>
|
||||||
<Text>{formatCurrency(group.remaining_value_rp)}</Text>
|
<Text>{formatCurrency(group.egg_value_rp)}</Text>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
@@ -356,16 +330,10 @@ const createPDFDocument = (
|
|||||||
<Text>Rata-Rata Bobot (Kg)</Text>
|
<Text>Rata-Rata Bobot (Kg)</Text>
|
||||||
</View>
|
</View>
|
||||||
<View style={[pdfStyles.tableCellHeaderRight, { flex: 0.8 }]}>
|
<View style={[pdfStyles.tableCellHeaderRight, { flex: 0.8 }]}>
|
||||||
<Text>Sisa Ekor</Text>
|
<Text>Sisa Butir</Text>
|
||||||
</View>
|
</View>
|
||||||
<View style={[pdfStyles.tableCellHeaderRight, { flex: 0.8 }]}>
|
<View style={[pdfStyles.tableCellHeaderRight, { flex: 0.8 }]}>
|
||||||
<Text>Sisa Kg (Ayam)</Text>
|
<Text>Sisa Kg (Telur)</Text>
|
||||||
</View>
|
|
||||||
<View style={[pdfStyles.tableCellHeaderRight, { flex: 0.8 }]}>
|
|
||||||
<Text>Produksi Telur (Butir)</Text>
|
|
||||||
</View>
|
|
||||||
<View style={[pdfStyles.tableCellHeaderRight, { flex: 0.8 }]}>
|
|
||||||
<Text>Produksi Telur (Kg)</Text>
|
|
||||||
</View>
|
</View>
|
||||||
<View style={[pdfStyles.tableCellHeader, { flex: 1.2 }]}>
|
<View style={[pdfStyles.tableCellHeader, { flex: 1.2 }]}>
|
||||||
<Text>Feed (Supplier)</Text>
|
<Text>Feed (Supplier)</Text>
|
||||||
@@ -376,12 +344,6 @@ const createPDFDocument = (
|
|||||||
<View style={[pdfStyles.tableCellHeaderRight, { flex: 1.2 }]}>
|
<View style={[pdfStyles.tableCellHeaderRight, { flex: 1.2 }]}>
|
||||||
<Text>Rata-Rata Harga DOC</Text>
|
<Text>Rata-Rata Harga DOC</Text>
|
||||||
</View>
|
</View>
|
||||||
<View style={[pdfStyles.tableCellHeaderRight, { flex: 1.2 }]}>
|
|
||||||
<Text>Nilai Nominal Telur</Text>
|
|
||||||
</View>
|
|
||||||
<View style={[pdfStyles.tableCellHeaderRight, { flex: 0.8 }]}>
|
|
||||||
<Text>HPP Ayam</Text>
|
|
||||||
</View>
|
|
||||||
<View style={[pdfStyles.tableCellHeaderRight, { flex: 1 }]}>
|
<View style={[pdfStyles.tableCellHeaderRight, { flex: 1 }]}>
|
||||||
<Text>HPP Telur (RP/KG)</Text>
|
<Text>HPP Telur (RP/KG)</Text>
|
||||||
</View>
|
</View>
|
||||||
@@ -416,12 +378,6 @@ const createPDFDocument = (
|
|||||||
<View style={[pdfStyles.tableCellRight, { flex: 1 }]}>
|
<View style={[pdfStyles.tableCellRight, { flex: 1 }]}>
|
||||||
<Text>{formatNumber(item.avg_weight_kg)}</Text>
|
<Text>{formatNumber(item.avg_weight_kg)}</Text>
|
||||||
</View>
|
</View>
|
||||||
<View style={[pdfStyles.tableCellRight, { flex: 0.8 }]}>
|
|
||||||
<Text>{formatNumber(item.remaining_chicken_birds)}</Text>
|
|
||||||
</View>
|
|
||||||
<View style={[pdfStyles.tableCellRight, { flex: 0.8 }]}>
|
|
||||||
<Text>{formatNumber(item.remaining_chicken_weight_kg)}</Text>
|
|
||||||
</View>
|
|
||||||
<View style={[pdfStyles.tableCellRight, { flex: 0.8 }]}>
|
<View style={[pdfStyles.tableCellRight, { flex: 0.8 }]}>
|
||||||
<Text>{formatNumber(item.egg_production_pieces)}</Text>
|
<Text>{formatNumber(item.egg_production_pieces)}</Text>
|
||||||
</View>
|
</View>
|
||||||
@@ -451,17 +407,11 @@ const createPDFDocument = (
|
|||||||
<View style={[pdfStyles.tableCellRight, { flex: 1.2 }]}>
|
<View style={[pdfStyles.tableCellRight, { flex: 1.2 }]}>
|
||||||
<Text>{formatCurrency(item.average_doc_price_rp)}</Text>
|
<Text>{formatCurrency(item.average_doc_price_rp)}</Text>
|
||||||
</View>
|
</View>
|
||||||
<View style={[pdfStyles.tableCellRight, { flex: 1.2 }]}>
|
|
||||||
<Text>{formatCurrency(item.egg_value_rp)}</Text>
|
|
||||||
</View>
|
|
||||||
<View style={[pdfStyles.tableCellRight, { flex: 0.8 }]}>
|
|
||||||
<Text>{formatCurrency(item.hpp_rp)}</Text>
|
|
||||||
</View>
|
|
||||||
<View style={[pdfStyles.tableCellRight, { flex: 1 }]}>
|
<View style={[pdfStyles.tableCellRight, { flex: 1 }]}>
|
||||||
<Text>{formatCurrency(item.egg_hpp_rp_per_kg)}</Text>
|
<Text>{formatCurrency(item.egg_hpp_rp_per_kg)}</Text>
|
||||||
</View>
|
</View>
|
||||||
<View style={[pdfStyles.tableCellRight, { flex: 1.2 }]}>
|
<View style={[pdfStyles.tableCellRight, { flex: 1.2 }]}>
|
||||||
<Text>{formatCurrency(item.remaining_value_rp)}</Text>
|
<Text>{formatCurrency(item.egg_value_rp)}</Text>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@@ -335,10 +335,8 @@ const HppPerKandangTab = () => {
|
|||||||
? `${formatNumber(item.weight_range.weight_min)} - ${formatNumber(item.weight_range.weight_max)}`
|
? `${formatNumber(item.weight_range.weight_min)} - ${formatNumber(item.weight_range.weight_max)}`
|
||||||
: '',
|
: '',
|
||||||
'Rata-Rata Bobot (KG)': item.avg_weight_kg || 0,
|
'Rata-Rata Bobot (KG)': item.avg_weight_kg || 0,
|
||||||
'Sisa Ayam (Ekor)': item.remaining_chicken_birds || 0,
|
'Sisa Telur (Butir)': item.egg_production_pieces || 0,
|
||||||
'Sisa Ayam (KG)': item.remaining_chicken_weight_kg || 0,
|
'Sisa Telur (KG)': item.egg_production_kg || 0,
|
||||||
'Produksi Telur (Butir)': item.egg_production_pieces || 0,
|
|
||||||
'Produksi Telur (KG)': item.egg_production_kg || 0,
|
|
||||||
'Feed (Supplier)':
|
'Feed (Supplier)':
|
||||||
item.feed_suppliers
|
item.feed_suppliers
|
||||||
?.map((s: { alias?: string; name: string }) => s.alias || s.name)
|
?.map((s: { alias?: string; name: string }) => s.alias || s.name)
|
||||||
@@ -348,10 +346,8 @@ const HppPerKandangTab = () => {
|
|||||||
?.map((s: { alias?: string; name: string }) => s.alias || s.name)
|
?.map((s: { alias?: string; name: string }) => s.alias || s.name)
|
||||||
.join(' | ') || '',
|
.join(' | ') || '',
|
||||||
'Rata-Rata Harga DOC (RP)': item.average_doc_price_rp || 0,
|
'Rata-Rata Harga DOC (RP)': item.average_doc_price_rp || 0,
|
||||||
'Nilai Nominal Telur (RP)': item.egg_value_rp || 0,
|
|
||||||
'HPP Ayam (RP)': item.hpp_rp || 0,
|
|
||||||
'HPP Telur (RP/KG)': item.egg_hpp_rp_per_kg || 0,
|
'HPP Telur (RP/KG)': item.egg_hpp_rp_per_kg || 0,
|
||||||
'Nilai Nominal Sisa Ayam (RP)': item.remaining_value_rp || 0,
|
'Nilai Nominal Sisa Telur (RP)': item.egg_value_rp || 0,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -360,20 +356,14 @@ const HppPerKandangTab = () => {
|
|||||||
Kandang: 'ALL',
|
Kandang: 'ALL',
|
||||||
'Rentang Bobot': '-',
|
'Rentang Bobot': '-',
|
||||||
'Rata-Rata Bobot (KG)': summaryTotal?.average_weight_kg || 0,
|
'Rata-Rata Bobot (KG)': summaryTotal?.average_weight_kg || 0,
|
||||||
'Sisa Ayam (Ekor)': summaryTotal?.total_remaining_chicken_birds || 0,
|
'Sisa Telur (Butir)': summaryTotal?.total_egg_production_pieces || 0,
|
||||||
'Sisa Ayam (KG)': summaryTotal?.total_remaining_chicken_weight_kg || 0,
|
'Sisa Telur (KG)': summaryTotal?.total_egg_production_kg || 0,
|
||||||
'Produksi Telur (Butir)':
|
|
||||||
summaryTotal?.total_egg_production_pieces || 0,
|
|
||||||
'Produksi Telur (KG)': summaryTotal?.total_egg_production_kg || 0,
|
|
||||||
'Feed (Supplier)': allFeedSuppliers,
|
'Feed (Supplier)': allFeedSuppliers,
|
||||||
'DOC (Supplier)': allDocSuppliers,
|
'DOC (Supplier)': allDocSuppliers,
|
||||||
'Rata-Rata Harga DOC (RP)':
|
'Rata-Rata Harga DOC (RP)':
|
||||||
summaryTotal?.total_average_doc_price_rp || 0,
|
summaryTotal?.total_average_doc_price_rp || 0,
|
||||||
'Nilai Nominal Telur (RP)': summaryTotal?.total_egg_value_rp || 0,
|
|
||||||
'HPP Ayam (RP)': summaryTotal?.total_hpp_rp || 0,
|
|
||||||
'HPP Telur (RP/KG)': summaryTotal?.average_egg_hpp_rp_per_kg || 0,
|
'HPP Telur (RP/KG)': summaryTotal?.average_egg_hpp_rp_per_kg || 0,
|
||||||
'Nilai Nominal Sisa Ayam (RP)':
|
'Nilai Nominal Sisa Telur (RP)': summaryTotal?.total_egg_value_rp || 0,
|
||||||
summaryTotal?.total_remaining_value_rp || 0,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const worksheet = XLSX.utils.json_to_sheet(excelData);
|
const worksheet = XLSX.utils.json_to_sheet(excelData);
|
||||||
@@ -383,17 +373,13 @@ const HppPerKandangTab = () => {
|
|||||||
{ wch: 30 }, // Kandang
|
{ wch: 30 }, // Kandang
|
||||||
{ wch: 15 }, // Rentang Bobot
|
{ wch: 15 }, // Rentang Bobot
|
||||||
{ wch: 18 }, // Rata-Rata Bobot (KG)
|
{ wch: 18 }, // Rata-Rata Bobot (KG)
|
||||||
{ wch: 15 }, // Sisa Ayam (Ekor)
|
{ wch: 15 }, // Sisa Telur (Butir)
|
||||||
{ wch: 15 }, // Sisa Ayam (KG)
|
{ wch: 15 }, // Sisa Telur (KG)
|
||||||
{ wch: 18 }, // Produksi Telur (Butir)
|
|
||||||
{ wch: 18 }, // Produksi Telur (KG)
|
|
||||||
{ wch: 20 }, // Feed (Supplier)
|
{ wch: 20 }, // Feed (Supplier)
|
||||||
{ wch: 20 }, // DOC (Supplier)
|
{ wch: 20 }, // DOC (Supplier)
|
||||||
{ wch: 20 }, // Rata-Rata Harga DOC (RP)
|
{ wch: 20 }, // Rata-Rata Harga DOC (RP)
|
||||||
{ wch: 20 }, // Nilai Nominal Telur (RP)
|
|
||||||
{ wch: 15 }, // HPP Ayam (RP)
|
|
||||||
{ wch: 18 }, // HPP Telur (RP/KG)
|
{ wch: 18 }, // HPP Telur (RP/KG)
|
||||||
{ wch: 25 }, // Nilai Nominal Sisa Ayam (RP)
|
{ wch: 25 }, // Nilai Nominal Sisa Telur (RP)
|
||||||
];
|
];
|
||||||
worksheet['!cols'] = colWidths;
|
worksheet['!cols'] = colWidths;
|
||||||
|
|
||||||
@@ -533,37 +519,9 @@ const HppPerKandangTab = () => {
|
|||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
|
||||||
id: 'remaining_chicken_birds',
|
|
||||||
header: 'Sisa Ayam (Ekor)',
|
|
||||||
accessorKey: 'remaining_chicken_birds',
|
|
||||||
cell: (props) => {
|
|
||||||
const value = props.row.original.remaining_chicken_birds;
|
|
||||||
return <div className='text-right'>{formatNumber(value)}</div>;
|
|
||||||
},
|
|
||||||
footer: () => (
|
|
||||||
<div className='text-right font-semibold text-gray-900'>
|
|
||||||
{formatNumber(summaryTotal?.total_remaining_chicken_birds || 0)}
|
|
||||||
</div>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'remaining_chicken_weight_kg',
|
|
||||||
header: 'Sisa Ayam (KG)',
|
|
||||||
accessorKey: 'remaining_chicken_weight_kg',
|
|
||||||
cell: (props) => {
|
|
||||||
const value = props.row.original.remaining_chicken_weight_kg;
|
|
||||||
return <div className='text-right'>{formatNumber(value)}</div>;
|
|
||||||
},
|
|
||||||
footer: () => (
|
|
||||||
<div className='text-right font-semibold text-gray-900'>
|
|
||||||
{formatNumber(summaryTotal?.total_remaining_chicken_weight_kg || 0)}
|
|
||||||
</div>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
id: 'egg_production_pieces',
|
id: 'egg_production_pieces',
|
||||||
header: 'Produksi Telur (Butir)',
|
header: 'Sisa Telur (Butir)',
|
||||||
accessorKey: 'egg_production_pieces',
|
accessorKey: 'egg_production_pieces',
|
||||||
cell: (props) => {
|
cell: (props) => {
|
||||||
const value = props.row.original.egg_production_pieces;
|
const value = props.row.original.egg_production_pieces;
|
||||||
@@ -577,7 +535,7 @@ const HppPerKandangTab = () => {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'egg_production_kg',
|
id: 'egg_production_kg',
|
||||||
header: 'Produksi Telur (KG)',
|
header: 'Sisa Telur (KG)',
|
||||||
accessorKey: 'egg_production_kg',
|
accessorKey: 'egg_production_kg',
|
||||||
cell: (props) => {
|
cell: (props) => {
|
||||||
const value = props.row.original.egg_production_kg;
|
const value = props.row.original.egg_production_kg;
|
||||||
@@ -585,7 +543,7 @@ const HppPerKandangTab = () => {
|
|||||||
},
|
},
|
||||||
footer: () => (
|
footer: () => (
|
||||||
<div className='text-right font-semibold text-gray-900'>
|
<div className='text-right font-semibold text-gray-900'>
|
||||||
{formatNumber(summaryTotal?.total_remaining_chicken_weight_kg || 0)}
|
{formatNumber(summaryTotal?.total_egg_production_kg || 0)}
|
||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
@@ -639,34 +597,6 @@ const HppPerKandangTab = () => {
|
|||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
|
||||||
id: 'egg_value_rp',
|
|
||||||
header: 'Nilai Nominal Telur (RP)',
|
|
||||||
accessorKey: 'egg_value_rp',
|
|
||||||
cell: (props) => {
|
|
||||||
const value = props.row.original.egg_value_rp;
|
|
||||||
return <div className='text-right'>{formatCurrency(value)}</div>;
|
|
||||||
},
|
|
||||||
footer: () => (
|
|
||||||
<div className='text-right font-semibold text-gray-900'>
|
|
||||||
{formatCurrency(summaryTotal?.total_egg_value_rp || 0)}
|
|
||||||
</div>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'hpp_rp',
|
|
||||||
header: 'HPP Ayam (RP)',
|
|
||||||
accessorKey: 'hpp_rp',
|
|
||||||
cell: (props) => {
|
|
||||||
const value = props.row.original.hpp_rp;
|
|
||||||
return <div className='text-right'>{formatCurrency(value)}</div>;
|
|
||||||
},
|
|
||||||
footer: () => (
|
|
||||||
<div className='text-right font-semibold text-gray-900'>
|
|
||||||
{formatCurrency(summaryTotal?.total_hpp_rp || 0)}
|
|
||||||
</div>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
id: 'egg_hpp_rp_per_kg',
|
id: 'egg_hpp_rp_per_kg',
|
||||||
header: 'HPP Telur (RP/KG)',
|
header: 'HPP Telur (RP/KG)',
|
||||||
@@ -682,16 +612,16 @@ const HppPerKandangTab = () => {
|
|||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'remaining_value_rp',
|
id: 'egg_value_rp',
|
||||||
header: 'Nilai Nominal Sisa Ayam (RP)',
|
header: 'Nilai Nominal Sisa Telur (RP)',
|
||||||
accessorKey: 'remaining_value_rp',
|
accessorKey: 'egg_value_rp',
|
||||||
cell: (props) => {
|
cell: (props) => {
|
||||||
const value = props.row.original.remaining_value_rp;
|
const value = props.row.original.egg_value_rp;
|
||||||
return <div className='text-right'>{formatCurrency(value)}</div>;
|
return <div className='text-right'>{formatCurrency(value)}</div>;
|
||||||
},
|
},
|
||||||
footer: () => (
|
footer: () => (
|
||||||
<div className='text-right font-semibold text-gray-900'>
|
<div className='text-right font-semibold text-gray-900'>
|
||||||
{formatCurrency(summaryTotal?.total_remaining_value_rp || 0)}
|
{formatCurrency(summaryTotal?.total_egg_value_rp || 0)}
|
||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
@@ -725,7 +655,7 @@ const HppPerKandangTab = () => {
|
|||||||
key={'rekapitulasi-row'}
|
key={'rekapitulasi-row'}
|
||||||
>
|
>
|
||||||
<td
|
<td
|
||||||
colSpan={15}
|
colSpan={11}
|
||||||
className='px-4 py-3 text-gray-900 text-center font-semibold'
|
className='px-4 py-3 text-gray-900 text-center font-semibold'
|
||||||
>
|
>
|
||||||
Rekapitulasi per rentang bobot
|
Rekapitulasi per rentang bobot
|
||||||
@@ -747,12 +677,6 @@ const HppPerKandangTab = () => {
|
|||||||
<td className='text-right'>
|
<td className='text-right'>
|
||||||
{formatNumber(item.avg_weight_kg)}
|
{formatNumber(item.avg_weight_kg)}
|
||||||
</td>
|
</td>
|
||||||
<td className='text-right'>
|
|
||||||
{formatNumber(item.remaining_chicken_birds)}
|
|
||||||
</td>
|
|
||||||
<td className='text-right'>
|
|
||||||
{formatNumber(item.remaining_chicken_weight_kg)}
|
|
||||||
</td>
|
|
||||||
<td className='text-right'>
|
<td className='text-right'>
|
||||||
{formatNumber(item.egg_production_pieces)}
|
{formatNumber(item.egg_production_pieces)}
|
||||||
</td>
|
</td>
|
||||||
@@ -772,15 +696,11 @@ const HppPerKandangTab = () => {
|
|||||||
<td className='text-right'>
|
<td className='text-right'>
|
||||||
{formatCurrency(item.average_doc_price_rp)}
|
{formatCurrency(item.average_doc_price_rp)}
|
||||||
</td>
|
</td>
|
||||||
<td className='text-right'>
|
|
||||||
{formatCurrency(item.egg_value_rp)}
|
|
||||||
</td>
|
|
||||||
<td className='text-right'>{formatCurrency(item.hpp_rp)}</td>
|
|
||||||
<td className='text-right'>
|
<td className='text-right'>
|
||||||
{formatCurrency(item.egg_hpp_rp_per_kg)}
|
{formatCurrency(item.egg_hpp_rp_per_kg)}
|
||||||
</td>
|
</td>
|
||||||
<td className='text-right'>
|
<td className='text-right'>
|
||||||
{formatCurrency(item.remaining_value_rp)}
|
{formatCurrency(item.egg_value_rp)}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -121,6 +121,7 @@ export const ROUTE_PERMISSIONS: Record<string, string[]> = {
|
|||||||
'/report/finance/': [
|
'/report/finance/': [
|
||||||
'lti.repport.finance.list',
|
'lti.repport.finance.list',
|
||||||
'lti.repport.debtsupplier.list',
|
'lti.repport.debtsupplier.list',
|
||||||
|
'lti.repport.customerpayment.list',
|
||||||
],
|
],
|
||||||
|
|
||||||
// Inventory
|
// Inventory
|
||||||
|
|||||||
@@ -601,15 +601,15 @@ export function DailyChecklistContent() {
|
|||||||
) => {
|
) => {
|
||||||
const taskId = taskIdsByPhaseActivityId[activityId];
|
const taskId = taskIdsByPhaseActivityId[activityId];
|
||||||
|
|
||||||
console.log('[CHECKBOX] Click detected:', {
|
// console.log('[CHECKBOX] Click detected:', {
|
||||||
activityId,
|
// activityId,
|
||||||
employeeId,
|
// employeeId,
|
||||||
checked,
|
// checked,
|
||||||
taskId,
|
// taskId,
|
||||||
hasTaskId: !!taskId,
|
// hasTaskId: !!taskId,
|
||||||
checklistStatus,
|
// checklistStatus,
|
||||||
isEditable,
|
// isEditable,
|
||||||
});
|
// });
|
||||||
|
|
||||||
if (!taskId) {
|
if (!taskId) {
|
||||||
console.error('[CHECKBOX] No taskId found for activityId:', activityId);
|
console.error('[CHECKBOX] No taskId found for activityId:', activityId);
|
||||||
@@ -638,10 +638,10 @@ export function DailyChecklistContent() {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
console.log(
|
// console.log(
|
||||||
'[CHECKBOX] State updated optimistically:',
|
// '[CHECKBOX] State updated optimistically:',
|
||||||
updated[taskId]?.[employeeId]
|
// updated[taskId]?.[employeeId]
|
||||||
);
|
// );
|
||||||
return updated;
|
return updated;
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -653,7 +653,7 @@ export function DailyChecklistContent() {
|
|||||||
note: assignments[taskId]?.[employeeId]?.note || null,
|
note: assignments[taskId]?.[employeeId]?.note || null,
|
||||||
};
|
};
|
||||||
|
|
||||||
console.log('[CHECKBOX] Saving to database:', payload);
|
// console.log('[CHECKBOX] Saving to database:', payload);
|
||||||
|
|
||||||
const checkOrUncheckAssignmentRes =
|
const checkOrUncheckAssignmentRes =
|
||||||
await DailyChecklistApi.checkOrUncheckAssignment(payload);
|
await DailyChecklistApi.checkOrUncheckAssignment(payload);
|
||||||
@@ -679,7 +679,7 @@ export function DailyChecklistContent() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('[CHECKBOX] Saved successfully');
|
// console.log('[CHECKBOX] Saved successfully');
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleNoteChange = async (
|
const handleNoteChange = async (
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { BaseApiService } from '@/services/api/base';
|
import { BaseApiService } from '@/services/api/base';
|
||||||
import { BaseApiResponse } from '@/types/api/api-general';
|
import { BaseApiResponse } from '@/types/api/api-general';
|
||||||
import { CustomerPaymentReport } from '@/types/api/report/customer-payment';
|
import { CustomerPaymentReport } from '@/types/api/report/customer-payment';
|
||||||
import { DebtSupplier } from '@/types/api/report/debt-supplier';
|
|
||||||
|
|
||||||
export class FinanceApiService extends BaseApiService<
|
export class FinanceApiService extends BaseApiService<
|
||||||
CustomerPaymentReport,
|
CustomerPaymentReport,
|
||||||
@@ -14,8 +13,11 @@ export class FinanceApiService extends BaseApiService<
|
|||||||
|
|
||||||
async getCustomerPaymentReport(
|
async getCustomerPaymentReport(
|
||||||
customer_id?: string,
|
customer_id?: string,
|
||||||
|
// TODO: Uncomment when BE is ready
|
||||||
|
// sales_id?: string,
|
||||||
|
// filter_by?: 'do_date',
|
||||||
sales_id?: string,
|
sales_id?: string,
|
||||||
filter_by?: 'do_date',
|
filter_by?: 'do_date' | undefined,
|
||||||
start_date?: string,
|
start_date?: string,
|
||||||
end_date?: string,
|
end_date?: string,
|
||||||
page?: number,
|
page?: number,
|
||||||
@@ -27,8 +29,9 @@ export class FinanceApiService extends BaseApiService<
|
|||||||
method: 'GET',
|
method: 'GET',
|
||||||
params: {
|
params: {
|
||||||
customer_id: customer_id,
|
customer_id: customer_id,
|
||||||
sales_id: sales_id,
|
// TODO: Uncomment when BE is ready
|
||||||
filter_by: filter_by,
|
// sales_id: sales_id,
|
||||||
|
// filter_by: filter_by,
|
||||||
start_date: start_date,
|
start_date: start_date,
|
||||||
end_date: end_date,
|
end_date: end_date,
|
||||||
page: page,
|
page: page,
|
||||||
@@ -39,8 +42,8 @@ export class FinanceApiService extends BaseApiService<
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// export const FinanceApi = new FinanceApiService('reports');
|
export const FinanceApi = new FinanceApiService('reports');
|
||||||
|
|
||||||
export const FinanceApi = new FinanceApiService(
|
// export const FinanceApi = new FinanceApiService(
|
||||||
'http://localhost:4010/api/reports/finance'
|
// 'http://localhost:4010/api/reports/finance'
|
||||||
);
|
// );
|
||||||
|
|||||||
Vendored
+32
-47
@@ -219,64 +219,30 @@ export type ClosingSales = BaseMetadata & BaseClosingSales;
|
|||||||
|
|
||||||
// ====== FINANCE ======
|
// ====== FINANCE ======
|
||||||
export interface ClosingFinance {
|
export interface ClosingFinance {
|
||||||
project_flock_id: number;
|
hpp: ClosingFinanceHpp;
|
||||||
period: number;
|
|
||||||
project_type: string;
|
|
||||||
volume_base: ClosingFinanceVolumeBase;
|
|
||||||
hpp_purchases: ClosingFinanceHppPurchases;
|
|
||||||
profit_loss: ClosingFinanceProfitLoss;
|
profit_loss: ClosingFinanceProfitLoss;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ClosingFinanceProfitLoss {
|
export interface ClosingFinanceHpp {
|
||||||
title: string;
|
items: HppItem[];
|
||||||
data: ProfitLossData;
|
summary: HppSummary;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ClosingFinanceHppPurchases {
|
export interface HppItem {
|
||||||
title: string;
|
id: number;
|
||||||
hpp: GroupHppPurchase[];
|
category: string;
|
||||||
summary_hpp: HppPurchasesSummary;
|
code: string;
|
||||||
}
|
|
||||||
|
|
||||||
export interface ClosingFinanceVolumeBase {
|
|
||||||
total_birds: number;
|
|
||||||
total_weight_kg: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ProfitLossData {
|
|
||||||
penjualan: ProfitLossDataAmount[];
|
|
||||||
pembelian: ProfitLossDataAmount[];
|
|
||||||
summary: ProfitLossDataSummary;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface GroupHppPurchase {
|
|
||||||
group_name: string;
|
|
||||||
data: HppPurchaseData[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ProfitLossDataSummary {
|
|
||||||
gross_profit: DataSummarySubTotal;
|
|
||||||
sub_total: DataSummarySubTotal;
|
|
||||||
net_profit: DataSummarySubTotal;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ProfitLossDataAmount {
|
|
||||||
type: string;
|
|
||||||
rp_per_bird: number;
|
|
||||||
rp_per_kg: number;
|
|
||||||
amount: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface HppPurchasesSummary {
|
|
||||||
label: string;
|
label: string;
|
||||||
budgeting: HppPurchaseDataAmount;
|
budgeting: HppPurchaseDataAmount;
|
||||||
realization: HppPurchaseDataAmount;
|
realization: HppPurchaseDataAmount;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface HppPurchaseData {
|
export interface HppSummary {
|
||||||
type: string;
|
label: string;
|
||||||
budgeting: HppPurchaseDataAmount;
|
budgeting: HppPurchaseDataAmount;
|
||||||
realization: HppPurchaseDataAmount;
|
realization: HppPurchaseDataAmount;
|
||||||
|
egg_budgeting: HppPurchaseDataAmount;
|
||||||
|
egg_realization: HppPurchaseDataAmount;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface HppPurchaseDataAmount {
|
export interface HppPurchaseDataAmount {
|
||||||
@@ -285,8 +251,27 @@ export interface HppPurchaseDataAmount {
|
|||||||
amount: number;
|
amount: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DataSummarySubTotal {
|
export interface ClosingFinanceProfitLoss {
|
||||||
|
items: ProfitLossItem[];
|
||||||
|
summary: ProfitLossSummary;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ProfitLossItem {
|
||||||
|
code: string;
|
||||||
label: string;
|
label: string;
|
||||||
|
type: string;
|
||||||
|
rp_per_bird: number;
|
||||||
|
rp_per_kg: number;
|
||||||
|
amount: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ProfitLossSummary {
|
||||||
|
gross_profit: ProfitLossAmount;
|
||||||
|
sub_total: ProfitLossAmount;
|
||||||
|
net_profit: ProfitLossAmount;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ProfitLossAmount {
|
||||||
rp_per_bird: number;
|
rp_per_bird: number;
|
||||||
rp_per_kg: number;
|
rp_per_kg: number;
|
||||||
amount: number;
|
amount: number;
|
||||||
|
|||||||
+2
@@ -6,6 +6,7 @@ import { Location } from '@/types/api/master-data/location';
|
|||||||
import { BaseApproval, BaseMetadata } from '@/types/api/api-general';
|
import { BaseApproval, BaseMetadata } from '@/types/api/api-general';
|
||||||
import { Nonstock } from '@/types/api/master-data/nonstock';
|
import { Nonstock } from '@/types/api/master-data/nonstock';
|
||||||
import { ProductionStandard } from '@/types/api/master-data/production-standard';
|
import { ProductionStandard } from '@/types/api/master-data/production-standard';
|
||||||
|
import { Warehouse } from '@/types/api/master-data/warehouse';
|
||||||
|
|
||||||
export type BaseProjectFlock = {
|
export type BaseProjectFlock = {
|
||||||
id: number;
|
id: number;
|
||||||
@@ -71,6 +72,7 @@ export type ProjectFlockKandangLookup = {
|
|||||||
kandang_id: number;
|
kandang_id: number;
|
||||||
kandang: Kandang;
|
kandang: Kandang;
|
||||||
project_flock: ProjectFlock;
|
project_flock: ProjectFlock;
|
||||||
|
warehouse: Warehouse;
|
||||||
quantity: number;
|
quantity: number;
|
||||||
available_quantity?: number;
|
available_quantity?: number;
|
||||||
population: number;
|
population: number;
|
||||||
|
|||||||
Vendored
+4
-4
@@ -120,12 +120,12 @@ export type CreateAcceptApprovalRequestPayload = {
|
|||||||
purchase_item_id: number;
|
purchase_item_id: number;
|
||||||
received_date: string;
|
received_date: string;
|
||||||
travel_number: string;
|
travel_number: string;
|
||||||
vehicle_number: string;
|
vehicle_number?: string | null;
|
||||||
expedition_vendor_id: number;
|
expedition_vendor_id?: number | null;
|
||||||
received_qty: number;
|
received_qty: number;
|
||||||
transport_per_item: number;
|
transport_per_item?: number | null;
|
||||||
}[];
|
}[];
|
||||||
travel_documents?: File[];
|
travel_documents?: File[] | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type DeletePurchaseRequestItemPayload = {
|
export type DeletePurchaseRequestItemPayload = {
|
||||||
|
|||||||
+12
-15
@@ -2,34 +2,30 @@ import { BaseCustomer } from '@/types/api/master-data/customer';
|
|||||||
import { BaseMetadata } from '@/types/api/api-general';
|
import { BaseMetadata } from '@/types/api/api-general';
|
||||||
|
|
||||||
export type CustomerPaymentRow = {
|
export type CustomerPaymentRow = {
|
||||||
id: number;
|
transaction_type: string;
|
||||||
do_date: string;
|
transaction_id: number;
|
||||||
realization_date: string;
|
trans_date: string;
|
||||||
aging_day: number | null;
|
delivery_date: string | null;
|
||||||
reference: string;
|
reference: string;
|
||||||
vehicle_plate: string[];
|
vehicle_numbers: string[];
|
||||||
qty: number;
|
qty: number;
|
||||||
weight: number;
|
weight: number;
|
||||||
average_weight: number;
|
average_weight: number;
|
||||||
price: number;
|
price: number;
|
||||||
credit_note: number;
|
|
||||||
final_price: number;
|
final_price: number;
|
||||||
ppn: number;
|
total_price: number;
|
||||||
total: number;
|
payment_amount: number;
|
||||||
payment: number;
|
|
||||||
accounts_receivable: number;
|
accounts_receivable: number;
|
||||||
notes: string;
|
aging_day: number | null;
|
||||||
pickup_info: string;
|
status: string;
|
||||||
sales_marketing: string;
|
pickup_info: string[];
|
||||||
|
sales_person: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type CustomerPaymentSummary = {
|
export type CustomerPaymentSummary = {
|
||||||
total_qty: number;
|
total_qty: number;
|
||||||
total_weight: number;
|
total_weight: number;
|
||||||
total_initial_amount: number;
|
|
||||||
total_credit_note: number;
|
|
||||||
total_final_amount: number;
|
total_final_amount: number;
|
||||||
total_ppn: number;
|
|
||||||
total_grand_amount: number;
|
total_grand_amount: number;
|
||||||
total_payment: number;
|
total_payment: number;
|
||||||
total_accounts_receivable: number;
|
total_accounts_receivable: number;
|
||||||
@@ -37,6 +33,7 @@ export type CustomerPaymentSummary = {
|
|||||||
|
|
||||||
export type CustomerPaymentReport = BaseMetadata & {
|
export type CustomerPaymentReport = BaseMetadata & {
|
||||||
customer: BaseCustomer;
|
customer: BaseCustomer;
|
||||||
|
initial_balance: number;
|
||||||
rows: CustomerPaymentRow[];
|
rows: CustomerPaymentRow[];
|
||||||
summary: CustomerPaymentSummary;
|
summary: CustomerPaymentSummary;
|
||||||
};
|
};
|
||||||
|
|||||||
+2
-14
@@ -9,30 +9,22 @@ export type HppPerKandangRow = {
|
|||||||
weight_min: number;
|
weight_min: number;
|
||||||
weight_max: number;
|
weight_max: number;
|
||||||
};
|
};
|
||||||
remaining_chicken_birds: number;
|
|
||||||
remaining_chicken_weight_kg: number;
|
|
||||||
avg_weight_kg: number;
|
avg_weight_kg: number;
|
||||||
egg_production_pieces: number;
|
egg_production_pieces: number;
|
||||||
egg_production_kg: number;
|
egg_production_kg: number;
|
||||||
egg_hpp_rp_per_kg: number;
|
egg_hpp_rp_per_kg: number;
|
||||||
egg_value_rp: number;
|
egg_value_rp: number;
|
||||||
feed_suppliers: Supplier[];
|
feed_suppliers: Supplier[] | null;
|
||||||
doc_suppliers: Supplier[];
|
doc_suppliers: Supplier[] | null;
|
||||||
average_doc_price_rp: number;
|
average_doc_price_rp: number;
|
||||||
hpp_rp: number;
|
|
||||||
remaining_value_rp: number;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export type HppPerKandangSummaryTotal = {
|
export type HppPerKandangSummaryTotal = {
|
||||||
total_remaining_chicken_birds: number;
|
|
||||||
total_remaining_chicken_weight_kg: number;
|
|
||||||
average_weight_kg: number;
|
average_weight_kg: number;
|
||||||
total_remaining_value_rp: number;
|
|
||||||
total_egg_production_pieces: number;
|
total_egg_production_pieces: number;
|
||||||
total_egg_production_kg: number;
|
total_egg_production_kg: number;
|
||||||
average_egg_hpp_rp_per_kg: number;
|
average_egg_hpp_rp_per_kg: number;
|
||||||
total_egg_value_rp: number;
|
total_egg_value_rp: number;
|
||||||
total_hpp_rp: number;
|
|
||||||
total_average_doc_price_rp: number;
|
total_average_doc_price_rp: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -43,8 +35,6 @@ export type HppPerKandangPerWeightRange = {
|
|||||||
weight_max: number;
|
weight_max: number;
|
||||||
};
|
};
|
||||||
label: string;
|
label: string;
|
||||||
remaining_chicken_birds: number;
|
|
||||||
remaining_chicken_weight_kg: number;
|
|
||||||
avg_weight_kg: number;
|
avg_weight_kg: number;
|
||||||
egg_production_pieces: number;
|
egg_production_pieces: number;
|
||||||
egg_production_kg: number;
|
egg_production_kg: number;
|
||||||
@@ -53,8 +43,6 @@ export type HppPerKandangPerWeightRange = {
|
|||||||
feed_suppliers: Supplier[];
|
feed_suppliers: Supplier[];
|
||||||
doc_suppliers: Supplier[];
|
doc_suppliers: Supplier[];
|
||||||
average_doc_price_rp: number;
|
average_doc_price_rp: number;
|
||||||
hpp_rp: number;
|
|
||||||
remaining_value_rp: number;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export type HppPerKandangSummary = {
|
export type HppPerKandangSummary = {
|
||||||
|
|||||||
Reference in New Issue
Block a user