Merge branch 'development' of gitlab.com:mbugroup/lti-web-client into fix/adjustment-submission-form-biaya

This commit is contained in:
rstubryan
2026-01-12 14:33:42 +07:00
10 changed files with 164 additions and 45 deletions
+27 -2
View File
@@ -7,18 +7,33 @@ import ClosingDetail from '@/components/pages/closing/ClosingDetail';
import { ClosingApi } from '@/services/api/closing'; import { ClosingApi } from '@/services/api/closing';
import { isResponseError, isResponseSuccess } from '@/lib/api-helper'; import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
import { FlockApi } from '@/services/api/master-data';
import { ProjectFlockApi } from '@/services/api/production/project-flock';
import { ProjectFlockKandangApi } from '@/services/api/production';
const ClosingDetailPage = () => { const ClosingDetailPage = () => {
const router = useRouter(); const router = useRouter();
const searchParams = useSearchParams(); const searchParams = useSearchParams();
const closingId = searchParams.get('closingId'); const closingId = searchParams.get('closingId');
const kandangId = searchParams.get('kandangId'); // project flock kandang ID
const { data: closing, isLoading: isLoadingClosing } = useSWR( const { data: closing, isLoading: isLoadingClosing } = useSWR(
closingId, closingId,
(id: number) => ClosingApi.getGeneralInfo(id) (id: number) => ClosingApi.getGeneralInfo(id)
); );
// WORKAROUND - get flock data from closing ID
const { data: projectData, isLoading: isLoadingProject } = useSWR(
`flock-${closingId}`,
() => ProjectFlockApi.getSingle(Number(closingId))
);
// WORKAROUND - get kandang data from closing ID
const { data: kandangData, isLoading: isLoadingKandang } = useSWR(
kandangId ? `kandang-${closingId}-${kandangId}` : null,
() => ProjectFlockKandangApi.getSingle(Number(kandangId))
);
const { data: salesData, isLoading: isLoadingSales } = useSWR( const { data: salesData, isLoading: isLoadingSales } = useSWR(
closingId ? `sales-${closingId}` : null, closingId ? `sales-${closingId}` : null,
() => ClosingApi.getPenjualan(Number(closingId)) () => ClosingApi.getPenjualan(Number(closingId))
@@ -44,8 +59,12 @@ const ClosingDetailPage = () => {
return; return;
} }
const isLoading = isLoadingClosing || isLoadingHppEkspedisi; const isLoading =
// const isLoading = isLoadingClosing || isLoadingSales || isLoadingHppEkspedisi; isLoadingClosing ||
isLoadingSales ||
isLoadingHppEkspedisi ||
isLoadingProject ||
isLoadingKandang;
return ( return (
<div className='w-full p-4 flex flex-row justify-center'> <div className='w-full p-4 flex flex-row justify-center'>
@@ -61,6 +80,12 @@ const ClosingDetailPage = () => {
? hppEkspedisiData.data ? hppEkspedisiData.data
: undefined : undefined
} }
projectData={
isResponseSuccess(projectData) ? projectData.data : undefined
}
kandangData={
isResponseSuccess(kandangData) ? kandangData.data : undefined
}
/> />
)} )}
</div> </div>
+23 -2
View File
@@ -19,12 +19,17 @@ import ClosingOverheadTabContent from '@/components/pages/closing/ClosingOverhea
import ClosingFinanceTabContent from '@/components/pages/closing/ClosingFinanceTabContent'; import ClosingFinanceTabContent from '@/components/pages/closing/ClosingFinanceTabContent';
import SalesReportTable from '@/components/pages/closing/sale/SalesReportTable'; import SalesReportTable from '@/components/pages/closing/sale/SalesReportTable';
import HppExpeditionReportTable from './hpp-ekspedisi/HppExpeditionReportTable'; import HppExpeditionReportTable from './hpp-ekspedisi/HppExpeditionReportTable';
import ClosingKandangList from '@/components/pages/closing/ClosingKandangList';
import { ProjectFlock } from '@/types/api/production/project-flock';
import { ProjectFlockKandang } from '@/types/api/production/project-flock-kandang';
interface ClosingDetailProps { interface ClosingDetailProps {
id: number; id: number;
initialValue?: ClosingGeneralInformation; initialValue?: ClosingGeneralInformation;
salesData?: BaseClosingSales; salesData?: BaseClosingSales;
hppExpeditionData?: ClosingHppExpedition; hppExpeditionData?: ClosingHppExpedition;
projectData?: ProjectFlock;
kandangData?: ProjectFlockKandang;
} }
const ClosingDetail: React.FC<ClosingDetailProps> = ({ const ClosingDetail: React.FC<ClosingDetailProps> = ({
@@ -32,6 +37,8 @@ const ClosingDetail: React.FC<ClosingDetailProps> = ({
initialValue, initialValue,
salesData, salesData,
hppExpeditionData, hppExpeditionData,
projectData,
kandangData,
}) => { }) => {
const [activeTab, setActiveTab] = useState<string>('sapronak'); const [activeTab, setActiveTab] = useState<string>('sapronak');
@@ -49,6 +56,7 @@ const ClosingDetail: React.FC<ClosingDetailProps> = ({
<ClosingSapronakCalculationTabContent <ClosingSapronakCalculationTabContent
closingGeneralInformation={initialValue} closingGeneralInformation={initialValue}
projectFlockId={id} projectFlockId={id}
projectKandangId={kandangData?.id}
/> />
), ),
}, },
@@ -87,7 +95,9 @@ const ClosingDetail: React.FC<ClosingDetailProps> = ({
<section className='w-full max-w-7xl pb-16'> <section className='w-full max-w-7xl pb-16'>
<header className='flex flex-col gap-4'> <header className='flex flex-col gap-4'>
<Button <Button
href='/closing' href={
!kandangData ? '/closing' : `/closing/detail/?closingId=${id}`
}
variant='link' variant='link'
className='w-fit p-0 text-primary' className='w-fit p-0 text-primary'
> >
@@ -98,7 +108,18 @@ const ClosingDetail: React.FC<ClosingDetailProps> = ({
<h1 className='text-2xl font-bold text-center'>Detail Closing</h1> <h1 className='text-2xl font-bold text-center'>Detail Closing</h1>
</header> </header>
<ClosingGeneralInformationTable initialValue={initialValue} /> <ClosingGeneralInformationTable
initialValue={initialValue}
projectData={projectData}
kandangData={kandangData}
/>
{!kandangData && (
<ClosingKandangList
initialValue={initialValue}
projectData={projectData}
/>
)}
<Tabs <Tabs
activeTabId={activeTab} activeTabId={activeTab}
@@ -1,12 +1,29 @@
import { ClosingGeneralInformation } from '@/types/api/closing'; import { ClosingGeneralInformation } from '@/types/api/closing';
import { ProjectFlock } from '@/types/api/production/project-flock';
import { ProjectFlockKandang } from '@/types/api/production/project-flock-kandang';
import { useMemo } from 'react';
interface ClosingGeneralInformationProps { interface ClosingGeneralInformationProps {
initialValue?: ClosingGeneralInformation; initialValue?: ClosingGeneralInformation;
projectData?: ProjectFlock;
kandangData?: ProjectFlockKandang;
} }
const ClosingGeneralInformationTable = ({ const ClosingGeneralInformationTable = ({
initialValue, initialValue,
projectData,
kandangData,
}: ClosingGeneralInformationProps) => { }: ClosingGeneralInformationProps) => {
const chickinPopulation = useMemo(() => {
if (kandangData) {
return kandangData?.chickins?.reduce(
(acc, chickin) => acc + chickin.usage_qty,
0
);
}
return 0;
}, [kandangData]);
return ( return (
<div className='w-full my-4 @container'> <div className='w-full my-4 @container'>
<div className='flex flex-col @sm:flex-row gap-4'> <div className='flex flex-col @sm:flex-row gap-4'>
@@ -17,7 +34,9 @@ const ClosingGeneralInformationTable = ({
<tr> <tr>
<td>Lokasi</td> <td>Lokasi</td>
<td>:</td> <td>:</td>
<td>{initialValue?.location_name}</td> <td>
{initialValue?.location_name ?? projectData?.location?.name}
</td>
</tr> </tr>
<tr> <tr>
<td>Periode</td> <td>Periode</td>
@@ -27,12 +46,20 @@ const ClosingGeneralInformationTable = ({
<tr> <tr>
<td>Project Flock</td> <td>Project Flock</td>
<td>:</td> <td>:</td>
<td>{initialValue?.project_flock?.name}</td> <td>
{initialValue?.project_flock?.name ??
projectData?.flock_name}
</td>
</tr> </tr>
<tr> <tr>
<td>Populasi</td> <td>Populasi</td>
<td>:</td> <td>:</td>
<td>{initialValue?.population} Ekor</td> <td>
{!kandangData
? (initialValue?.population ?? 0)
: (chickinPopulation ?? 0)}{' '}
Ekor
</td>
</tr> </tr>
<tr> <tr>
<td>Jenis Project</td> <td>Jenis Project</td>
@@ -40,9 +67,13 @@ const ClosingGeneralInformationTable = ({
<td>{initialValue?.project_type}</td> <td>{initialValue?.project_type}</td>
</tr> </tr>
<tr className='table-row @sm:hidden'> <tr className='table-row @sm:hidden'>
<td>Kandang Aktif</td> <td>Kandang {!kandangData && 'Aktif'}</td>
<td>:</td> <td>:</td>
<td>{initialValue?.active_house_count} Kandang</td> <td>
{!kandangData
? `${initialValue?.active_house_count} Kandang`
: kandangData?.kandang?.name}
</td>
</tr> </tr>
<tr className='table-row @sm:hidden'> <tr className='table-row @sm:hidden'>
<td>Status Pembayaran Penjualan</td> <td>Status Pembayaran Penjualan</td>
@@ -69,9 +100,13 @@ const ClosingGeneralInformationTable = ({
<table className='table table-zebra table-sm'> <table className='table table-zebra table-sm'>
<tbody> <tbody>
<tr> <tr>
<td>Kandang Aktif</td> <td>Kandang {!kandangData && 'Aktif'}</td>
<td>:</td> <td>:</td>
<td>{initialValue?.active_house_count} Kandang</td> <td>
{!kandangData
? `${initialValue?.active_house_count} Kandang`
: kandangData?.kandang?.name}
</td>
</tr> </tr>
<tr> <tr>
<td>Status Pembayaran Penjualan</td> <td>Status Pembayaran Penjualan</td>
@@ -0,0 +1,37 @@
import Button from '@/components/Button';
import { ClosingGeneralInformation } from '@/types/api/closing';
import { ProjectFlock } from '@/types/api/production/project-flock';
const ClosingKandangList = ({
initialValue,
projectData,
}: {
initialValue?: ClosingGeneralInformation;
projectData?: ProjectFlock;
}) => {
return (
<div className='w-full my-4 @container'>
<div className='flex flex-col @sm:flex-row gap-4'>
<div className='w-full'>
<div className='overflow-x-auto'>
<h1 className='font-bold my-4'>Kandang</h1>
<div className='flex flex-wrap gap-2 mb-4'>
{projectData?.kandangs?.map((kandang) => (
<Button
key={kandang.id}
variant='outline'
href={`/closing/detail/?closingId=${initialValue?.flock_id}&kandangId=${kandang.project_flock_kandang_id}`}
className='min-w-32'
>
{kandang.name}
</Button>
))}
</div>
</div>
</div>
</div>
</div>
);
};
export default ClosingKandangList;
@@ -5,11 +5,13 @@ import { ClosingGeneralInformation } from '@/types/api/closing';
interface ClosingSapronakCalculationTabContentProps { interface ClosingSapronakCalculationTabContentProps {
projectFlockId?: number; projectFlockId?: number;
projectKandangId?: number;
closingGeneralInformation?: ClosingGeneralInformation; closingGeneralInformation?: ClosingGeneralInformation;
} }
const ClosingSapronakCalculationTabContent = ({ const ClosingSapronakCalculationTabContent = ({
projectFlockId, projectFlockId,
projectKandangId,
closingGeneralInformation, closingGeneralInformation,
}: ClosingSapronakCalculationTabContentProps) => { }: ClosingSapronakCalculationTabContentProps) => {
return ( return (
@@ -19,6 +21,7 @@ const ClosingSapronakCalculationTabContent = ({
<ClosingSapronakCalculationTable <ClosingSapronakCalculationTable
closingGeneralInformation={closingGeneralInformation} closingGeneralInformation={closingGeneralInformation}
projectFlockId={projectFlockId} projectFlockId={projectFlockId}
projectKandangId={projectKandangId}
/> />
</> </>
)} )}
@@ -17,16 +17,18 @@ import { ClosingGeneralInformation } from '@/types/api/closing';
interface ClosingSapronakCalculationTableProps { interface ClosingSapronakCalculationTableProps {
projectFlockId: number; projectFlockId: number;
projectKandangId?: number;
closingGeneralInformation?: ClosingGeneralInformation; closingGeneralInformation?: ClosingGeneralInformation;
} }
const ClosingSapronakCalculationTable = ({ const ClosingSapronakCalculationTable = ({
projectFlockId, projectFlockId,
closingGeneralInformation, closingGeneralInformation,
projectKandangId,
}: ClosingSapronakCalculationTableProps) => { }: ClosingSapronakCalculationTableProps) => {
const { data: sapronakCalculation, isLoading } = useSWR( const { data: sapronakCalculation, isLoading } = useSWR(
`/closing/sapronak-calculation/${projectFlockId}`, `/closing/sapronak-calculation/${projectFlockId}${projectKandangId ? `/${projectKandangId}` : ''}`,
() => ClosingApi.getPerhitunganSapronak(projectFlockId), () => ClosingApi.getPerhitunganSapronak(projectFlockId, projectKandangId),
{ {
keepPreviousData: true, keepPreviousData: true,
} }
@@ -57,11 +59,11 @@ const ClosingSapronakCalculationTable = ({
cell: (props) => cell: (props) =>
props.row.original.qty_in props.row.original.qty_in
? formatNumber(props.row.original.qty_in as number) ? formatNumber(props.row.original.qty_in as number)
: '-', : '0',
footer: total footer: total
? () => ( ? () => (
<div className='font-semibold text-gray-900'> <div className='font-semibold text-gray-900'>
{total?.qty_in ? formatNumber(total?.qty_in) : '-'} {total?.qty_in ? formatNumber(total?.qty_in) : '0'}
</div> </div>
) )
: '', : '',
@@ -72,11 +74,11 @@ const ClosingSapronakCalculationTable = ({
cell: (props) => cell: (props) =>
props.row.original.qty_out props.row.original.qty_out
? formatNumber(props.row.original.qty_out as number) ? formatNumber(props.row.original.qty_out as number)
: '-', : '0',
footer: total footer: total
? () => ( ? () => (
<div className='font-semibold text-gray-900'> <div className='font-semibold text-gray-900'>
{total?.qty_out ? formatNumber(total?.qty_out) : '-'} {total?.qty_out ? formatNumber(total?.qty_out) : '0'}
</div> </div>
) )
: '', : '',
@@ -87,11 +89,11 @@ const ClosingSapronakCalculationTable = ({
cell: (props) => cell: (props) =>
props.row.original.qty_used props.row.original.qty_used
? formatNumber(props.row.original.qty_used as number) ? formatNumber(props.row.original.qty_used as number)
: '-', : '0',
footer: total footer: total
? () => ( ? () => (
<div className='font-semibold text-gray-900'> <div className='font-semibold text-gray-900'>
{total?.qty_used ? formatNumber(total?.qty_used) : '-'} {total?.qty_used ? formatNumber(total?.qty_used) : '0'}
</div> </div>
) )
: '', : '',
@@ -173,14 +175,6 @@ const ClosingSapronakCalculationTable = ({
[sapronakCalculation] [sapronakCalculation]
); );
const pulletColumns = useMemo(
() =>
isResponseSuccess(sapronakCalculation)
? createColumns(sapronakCalculation.data?.pullet?.total)
: createColumns(),
[sapronakCalculation]
);
return ( return (
<div className='flex flex-col gap-4'> <div className='flex flex-col gap-4'>
{/* Table DOC jika kategori Project Flock Growing */} {/* Table DOC jika kategori Project Flock Growing */}
@@ -200,20 +194,17 @@ const ClosingSapronakCalculationTable = ({
<Table<RowSapronakCalculation> <Table<RowSapronakCalculation>
data={ data={
isResponseSuccess(sapronakCalculation) isResponseSuccess(sapronakCalculation)
? ((closingGeneralInformation?.project_category === 'GROWING' ? (sapronakCalculation.data?.doc?.rows ?? [])
? sapronakCalculation.data?.doc?.rows
: sapronakCalculation.data?.pullet?.rows) ?? [])
: [] : []
} }
columns={ columns={docColumns}
closingGeneralInformation?.project_category === 'GROWING'
? docColumns
: pulletColumns
}
className={{ className={{
containerClassName: 'my-4', containerClassName: 'my-4',
}} }}
renderFooter={isResponseSuccess(sapronakCalculation)} renderFooter={
isResponseSuccess(sapronakCalculation) &&
sapronakCalculation.data?.doc?.rows.length > 0
}
/> />
</Card> </Card>
@@ -236,7 +227,10 @@ const ClosingSapronakCalculationTable = ({
className={{ className={{
containerClassName: 'my-4', containerClassName: 'my-4',
}} }}
renderFooter={isResponseSuccess(sapronakCalculation)} renderFooter={
isResponseSuccess(sapronakCalculation) &&
sapronakCalculation.data?.ovk?.rows.length > 0
}
/> />
</Card> </Card>
@@ -259,7 +253,10 @@ const ClosingSapronakCalculationTable = ({
className={{ className={{
containerClassName: 'my-4', containerClassName: 'my-4',
}} }}
renderFooter={isResponseSuccess(sapronakCalculation)} renderFooter={
isResponseSuccess(sapronakCalculation) &&
sapronakCalculation.data?.pakan?.rows.length > 0
}
/> />
</Card> </Card>
</div> </div>
@@ -96,7 +96,7 @@ const DebtSupplierTab = () => {
filterSupplier.length > 0 filterSupplier.length > 0
? filterSupplier.map((v) => String(v.value)).join(',') ? filterSupplier.map((v) => String(v.value)).join(',')
: undefined, : undefined,
filter_by: 'do_date' as const, filter_by: filterDataType?.value || 'do_date',
start_date: filterStartDate || undefined, start_date: filterStartDate || undefined,
end_date: filterEndDate || undefined, end_date: filterEndDate || undefined,
page: currentPage, page: currentPage,
@@ -109,7 +109,7 @@ const DebtSupplierTab = () => {
([, params]) => ([, params]) =>
FinanceApi.getDebtSupplierReport( FinanceApi.getDebtSupplierReport(
params.supplier_ids, params.supplier_ids,
params.filter_by, params.filter_by?.toString(),
params.start_date, params.start_date,
params.end_date, params.end_date,
params.page, params.page,
@@ -448,7 +448,7 @@ const DebtSupplierTab = () => {
<Table <Table
data={supplierReport.rows} data={supplierReport.rows}
columns={getTableColumns(supplierReport)} columns={getTableColumns(supplierReport)}
pageSize={10} pageSize={supplierReport.rows.length}
renderFooter={supplierReport.rows.length > 0} renderFooter={supplierReport.rows.length > 0}
className={{ className={{
containerClassName: 'w-full', containerClassName: 'w-full',
@@ -534,6 +534,7 @@ const DebtSupplierTab = () => {
<div className='mt-auto'> <div className='mt-auto'>
<DateInput <DateInput
label=' '
name='end_date' name='end_date'
value={filterEndDate} value={filterEndDate}
onChange={(e) => { onChange={(e) => {
+3 -2
View File
@@ -92,10 +92,11 @@ export class ClosingApiService extends BaseApiService<Closing, null, null> {
} }
async getPerhitunganSapronak( async getPerhitunganSapronak(
id: number id: number,
projectKandangId?: number
): Promise<BaseApiResponse<ClosingSapronakCalculation> | undefined> { ): Promise<BaseApiResponse<ClosingSapronakCalculation> | undefined> {
try { try {
const path = `${this.basePath}/${id}/perhitungan_sapronak`; const path = `${this.basePath}/${id}${projectKandangId ? `/${projectKandangId}` : ''}/perhitungan_sapronak`;
return await httpClient<BaseApiResponse<ClosingSapronakCalculation>>( return await httpClient<BaseApiResponse<ClosingSapronakCalculation>>(
path, path,
{ {
+1 -1
View File
@@ -40,7 +40,7 @@ export class FinanceApiService extends BaseApiService<
async getDebtSupplierReport( async getDebtSupplierReport(
supplier_ids?: string, supplier_ids?: string,
filter_by?: 'do_date', filter_by?: string,
start_date?: string, start_date?: string,
end_date?: string, end_date?: string,
page?: number, page?: number,
-1
View File
@@ -185,7 +185,6 @@ export type ClosingSapronakCalculation = {
doc: ClosingSapronakCalculationItem; doc: ClosingSapronakCalculationItem;
ovk: ClosingSapronakCalculationItem; ovk: ClosingSapronakCalculationItem;
pakan: ClosingSapronakCalculationItem; pakan: ClosingSapronakCalculationItem;
pullet: ClosingSapronakCalculationItem;
}; };
// ====== OVERHEAD ====== // ====== OVERHEAD ======