mirror of
https://gitlab.com/mbugroup/lti-web-client.git
synced 2026-05-20 13:32:00 +00:00
Merge branch 'feat/FE/US-284/TASK-324-325-slicing-and-integration-sapronak-calculation-closing-report' into 'feat/FE/US-284/sapronak-calculation-report'
[FEAT/FE][US#284/TASK#324-325] Add Feature Perhitungan Sapronak Closing Report See merge request mbugroup/lti-web-client!71
This commit is contained in:
Generated
+2
-12
@@ -15,7 +15,7 @@
|
||||
"clsx": "^2.1.1",
|
||||
"formik": "^2.4.6",
|
||||
"moment": "^2.30.1",
|
||||
"next": "^15.5.7",
|
||||
"next": "15.5.7",
|
||||
"react": "19.1.0",
|
||||
"react-day-picker": "^9.11.1",
|
||||
"react-dom": "19.1.0",
|
||||
@@ -1855,7 +1855,6 @@
|
||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.2.tgz",
|
||||
"integrity": "sha512-6mDvHUFSjyT2B2yeNx2nUgMxh9LtOWvkhIU3uePn2I2oyNymUAX1NIsdgviM4CH+JSrp2D2hsMvJOkxY+0wNRA==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"csstype": "^3.0.2"
|
||||
}
|
||||
@@ -1925,7 +1924,6 @@
|
||||
"integrity": "sha512-BnOroVl1SgrPLywqxyqdJ4l3S2MsKVLDVxZvjI1Eoe8ev2r3kGDo+PcMihNmDE+6/KjkTubSJnmqGZZjQSBq/g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@typescript-eslint/scope-manager": "8.46.2",
|
||||
"@typescript-eslint/types": "8.46.2",
|
||||
@@ -2449,7 +2447,6 @@
|
||||
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"acorn": "bin/acorn"
|
||||
},
|
||||
@@ -3063,8 +3060,7 @@
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
|
||||
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/daisyui": {
|
||||
"version": "5.5.8",
|
||||
@@ -3520,7 +3516,6 @@
|
||||
"integrity": "sha512-t5aPOpmtJcZcz5UJyY2GbvpDlsK5E8JqRqoKtfiKE3cNh437KIqfJr3A3AKf5k64NPx6d0G3dno6XDY05PqPtw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.8.0",
|
||||
"@eslint-community/regexpp": "^4.12.1",
|
||||
@@ -3694,7 +3689,6 @@
|
||||
"integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@rtsao/scc": "^1.1.0",
|
||||
"array-includes": "^3.1.9",
|
||||
@@ -6173,7 +6167,6 @@
|
||||
"resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz",
|
||||
"integrity": "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
@@ -6204,7 +6197,6 @@
|
||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.0.tgz",
|
||||
"integrity": "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"scheduler": "^0.26.0"
|
||||
},
|
||||
@@ -7091,7 +7083,6 @@
|
||||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
@@ -7259,7 +7250,6 @@
|
||||
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
|
||||
+1
-1
@@ -18,7 +18,7 @@
|
||||
"clsx": "^2.1.1",
|
||||
"formik": "^2.4.6",
|
||||
"moment": "^2.30.1",
|
||||
"next": "^15.5.7",
|
||||
"next": "15.5.7",
|
||||
"react": "19.1.0",
|
||||
"react-day-picker": "^9.11.1",
|
||||
"react-dom": "19.1.0",
|
||||
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
BaseClosingSales,
|
||||
} from '@/types/api/closing';
|
||||
import ClosingSapronakTabContent from './ClosingSapronakTabContent';
|
||||
import ClosingSapronakCalculationTabContent from '@/components/pages/closing/ClosingSapronakCalculationTabContent';
|
||||
import SalesReportTable from './sale/SalesReportTable';
|
||||
|
||||
interface ClosingDetailProps {
|
||||
@@ -37,7 +38,7 @@ const ClosingDetail: React.FC<ClosingDetailProps> = ({
|
||||
{
|
||||
id: 'perhitunganSapronak',
|
||||
label: 'Perhitungan Sapronak',
|
||||
content: 'Perhitungan Sapronak',
|
||||
content: <ClosingSapronakCalculationTabContent projectFlockId={id} />,
|
||||
},
|
||||
{
|
||||
id: 'penjualan',
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
'use client';
|
||||
|
||||
import ClosingIncomingSapronaksTable from '@/components/pages/closing/ClosingIncomingSapronaksTable';
|
||||
import ClosingOutgoingSapronaksTable from '@/components/pages/closing/ClosingOutgoingSapronaksTable';
|
||||
import ClosingSapronakCalculationTable from '@/components/pages/closing/ClosingSapronakCalculationTable';
|
||||
|
||||
interface ClosingSapronakCalculationTabContentProps {
|
||||
projectFlockId?: number;
|
||||
}
|
||||
|
||||
const ClosingSapronakCalculationTabContent = ({
|
||||
projectFlockId,
|
||||
}: ClosingSapronakCalculationTabContentProps) => {
|
||||
return (
|
||||
<div className='flex flex-col gap-4'>
|
||||
{projectFlockId && (
|
||||
<>
|
||||
<ClosingSapronakCalculationTable projectFlockId={projectFlockId} />
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ClosingSapronakCalculationTabContent;
|
||||
@@ -0,0 +1,218 @@
|
||||
'use client';
|
||||
|
||||
import Card from '@/components/Card';
|
||||
|
||||
import Table from '@/components/Table';
|
||||
import { cn, formatCurrency, formatNumber } from '@/lib/helper';
|
||||
import {
|
||||
RowSapronakCalculation,
|
||||
TotalSapronakCalculation,
|
||||
} from '@/types/api/closing';
|
||||
import { ColumnDef } from '@tanstack/react-table';
|
||||
import { useMemo } from 'react';
|
||||
import useSWR from 'swr';
|
||||
import { ClosingApi } from '@/services/api/closing';
|
||||
import { isResponseSuccess } from '@/lib/api-helper';
|
||||
|
||||
interface ClosingSapronakCalculationTableProps {
|
||||
type?: 'detail';
|
||||
projectFlockId: number;
|
||||
}
|
||||
|
||||
const ClosingSapronakCalculationTable = ({
|
||||
type,
|
||||
projectFlockId,
|
||||
}: ClosingSapronakCalculationTableProps) => {
|
||||
const { data: sapronakCalculation, isLoading } = useSWR(
|
||||
`/closing/sapronak-calculation/${projectFlockId}`,
|
||||
() => ClosingApi.getPerhitunganSapronak(projectFlockId)
|
||||
);
|
||||
|
||||
// Helper function to create columns with footer support
|
||||
const createColumns = (
|
||||
total?: TotalSapronakCalculation
|
||||
): ColumnDef<RowSapronakCalculation>[] => [
|
||||
{
|
||||
header: 'Tanggal',
|
||||
accessorKey: 'tanggal',
|
||||
cell: (props) => (props.getValue() as string) || '-',
|
||||
footer: 'Total',
|
||||
},
|
||||
{
|
||||
header: 'No. Referensi',
|
||||
accessorKey: 'no_referensi',
|
||||
cell: (props) => (props.getValue() as string) || '-',
|
||||
footer: '',
|
||||
},
|
||||
{
|
||||
header: 'QTY Masuk',
|
||||
accessorKey: 'qty_masuk',
|
||||
cell: (props) => formatNumber(props.getValue() as number),
|
||||
footer: total
|
||||
? () => (
|
||||
<div className='font-semibold text-gray-900'>
|
||||
{formatNumber(total.qty_masuk)}
|
||||
</div>
|
||||
)
|
||||
: '',
|
||||
},
|
||||
{
|
||||
header: 'QTY Keluar',
|
||||
accessorKey: 'qty_keluar',
|
||||
cell: (props) => formatNumber(props.getValue() as number),
|
||||
footer: total
|
||||
? () => (
|
||||
<div className='font-semibold text-gray-900'>
|
||||
{formatNumber(total.qty_keluar)}
|
||||
</div>
|
||||
)
|
||||
: '',
|
||||
},
|
||||
{
|
||||
header: 'QTY Pakai',
|
||||
accessorKey: 'qty_pakai',
|
||||
cell: (props) => formatNumber(props.getValue() as number),
|
||||
footer: total
|
||||
? () => (
|
||||
<div className='font-semibold text-gray-900'>
|
||||
{formatNumber(total.qty_pakai)}
|
||||
</div>
|
||||
)
|
||||
: '',
|
||||
},
|
||||
{
|
||||
header: 'Uraian',
|
||||
accessorKey: 'uraian',
|
||||
cell: (props) => (props.getValue() as string) || '-',
|
||||
footer: '',
|
||||
},
|
||||
{
|
||||
header: 'Kategori Produk',
|
||||
accessorKey: 'kategori_produk',
|
||||
cell: (props) => (props.getValue() as string) || '-',
|
||||
footer: '',
|
||||
},
|
||||
{
|
||||
header: 'Harga Beli/Qty (Rp)',
|
||||
accessorKey: 'harga_beli_per_qty',
|
||||
cell: (props) => formatCurrency(props.getValue() as number),
|
||||
footer: total
|
||||
? () => (
|
||||
<div className='font-semibold text-gray-900'>
|
||||
{formatCurrency(total.harga_beli_per_qty)}
|
||||
</div>
|
||||
)
|
||||
: '',
|
||||
},
|
||||
{
|
||||
header: 'Total Harga (Rp)',
|
||||
accessorKey: 'total_harga',
|
||||
cell: (props) => formatCurrency(props.getValue() as number),
|
||||
footer: total
|
||||
? () => (
|
||||
<div className='font-semibold text-gray-900'>
|
||||
{formatCurrency(total.total_harga)}
|
||||
</div>
|
||||
)
|
||||
: '',
|
||||
},
|
||||
{
|
||||
header: 'Keterangan',
|
||||
accessorKey: 'keterangan',
|
||||
cell: (props) => (props.getValue() as string) || '-',
|
||||
footer: '',
|
||||
},
|
||||
];
|
||||
|
||||
// Memoize columns untuk setiap kategori
|
||||
const docBroilerColumns = useMemo(
|
||||
() =>
|
||||
isResponseSuccess(sapronakCalculation)
|
||||
? createColumns(sapronakCalculation.data?.doc_broiler.total)
|
||||
: createColumns(),
|
||||
[sapronakCalculation]
|
||||
);
|
||||
|
||||
const ovkColumns = useMemo(
|
||||
() =>
|
||||
isResponseSuccess(sapronakCalculation)
|
||||
? createColumns(sapronakCalculation.data?.ovk.total)
|
||||
: createColumns(),
|
||||
[sapronakCalculation]
|
||||
);
|
||||
|
||||
const pakanColumns = useMemo(
|
||||
() =>
|
||||
isResponseSuccess(sapronakCalculation)
|
||||
? createColumns(sapronakCalculation.data?.pakan.total)
|
||||
: createColumns(),
|
||||
[sapronakCalculation]
|
||||
);
|
||||
|
||||
return (
|
||||
<div className='flex flex-col gap-4'>
|
||||
{isResponseSuccess(sapronakCalculation) && (
|
||||
<>
|
||||
<Card
|
||||
title='DOC Broiler'
|
||||
collapsible
|
||||
defaultCollapsed={false}
|
||||
className={{
|
||||
wrapper: 'w-full',
|
||||
body: 'p-4 shadow',
|
||||
}}
|
||||
>
|
||||
<Table<RowSapronakCalculation>
|
||||
data={sapronakCalculation.data?.doc_broiler.rows ?? []}
|
||||
columns={docBroilerColumns}
|
||||
className={{
|
||||
containerClassName: 'my-4',
|
||||
}}
|
||||
renderFooter
|
||||
/>
|
||||
</Card>
|
||||
|
||||
<Card
|
||||
title='OVK'
|
||||
variant='bordered'
|
||||
collapsible
|
||||
defaultCollapsed={true}
|
||||
className={{
|
||||
wrapper: 'w-full',
|
||||
}}
|
||||
>
|
||||
<Table<RowSapronakCalculation>
|
||||
data={sapronakCalculation.data?.ovk.rows ?? []}
|
||||
columns={ovkColumns}
|
||||
className={{
|
||||
containerClassName: 'my-4',
|
||||
}}
|
||||
renderFooter
|
||||
/>
|
||||
</Card>
|
||||
|
||||
<Card
|
||||
title='Pakan'
|
||||
variant='bordered'
|
||||
collapsible
|
||||
defaultCollapsed={true}
|
||||
className={{
|
||||
wrapper: 'w-full',
|
||||
}}
|
||||
>
|
||||
<Table<RowSapronakCalculation>
|
||||
data={sapronakCalculation.data?.pakan.rows ?? []}
|
||||
columns={pakanColumns}
|
||||
className={{
|
||||
containerClassName: 'my-4',
|
||||
}}
|
||||
renderFooter
|
||||
/>
|
||||
</Card>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ClosingSapronakCalculationTable;
|
||||
@@ -0,0 +1,984 @@
|
||||
/**
|
||||
* Dummy Data untuk Closing API
|
||||
*
|
||||
* File ini berisi dummy data untuk testing API Closing sebelum backend siap.
|
||||
*
|
||||
* Struktur data mengikuti tipe yang didefinisikan di @/types/api/closing.d.ts
|
||||
*
|
||||
* @example
|
||||
* // 1. Menggunakan getAllFetcher dengan SWR:
|
||||
* import useSWR from 'swr';
|
||||
* import { ClosingApi } from '@/services/api/closing';
|
||||
*
|
||||
* const { data, error, isLoading } = useSWR(
|
||||
* '/closings',
|
||||
* ClosingApi.getAllFetcher.bind(ClosingApi)
|
||||
* );
|
||||
*
|
||||
* if (data?.status === 'success') {
|
||||
* console.log(data.data); // Array of Closing objects
|
||||
* }
|
||||
*
|
||||
* @example
|
||||
* // 2. Menggunakan getSingle:
|
||||
* import { ClosingApi } from '@/services/api/closing';
|
||||
*
|
||||
* const response = await ClosingApi.getSingle(1);
|
||||
* if (response?.status === 'success') {
|
||||
* console.log(response.data); // Single Closing object
|
||||
* } else if (response?.status === 'error') {
|
||||
* console.error(response.message); // Error message
|
||||
* }
|
||||
*
|
||||
* @example
|
||||
* // 3. Menggunakan getGeneralInfo dengan SWR:
|
||||
* import useSWR from 'swr';
|
||||
* import { ClosingApi } from '@/services/api/closing';
|
||||
*
|
||||
* const closingId = 1;
|
||||
* const { data, error, isLoading } = useSWR(
|
||||
* closingId,
|
||||
* (id: number) => ClosingApi.getGeneralInfo(id)
|
||||
* );
|
||||
*
|
||||
* if (data?.status === 'success') {
|
||||
* console.log(data.data); // ClosingGeneralInformation object
|
||||
* }
|
||||
*
|
||||
* @example
|
||||
* // 4. Menggunakan getAllIncomingSapronakFetcher dengan SWR:
|
||||
* import useSWR from 'swr';
|
||||
* import { ClosingApi } from '@/services/api/closing';
|
||||
*
|
||||
* const { data, error, isLoading } = useSWR(
|
||||
* `${ClosingApi.basePath}/1/sapronak/incoming`,
|
||||
* ClosingApi.getAllIncomingSapronakFetcher.bind(ClosingApi)
|
||||
* );
|
||||
*
|
||||
* if (data?.status === 'success') {
|
||||
* console.log(data.data); // Array of ClosingIncomingSapronak
|
||||
* }
|
||||
*
|
||||
* @example
|
||||
* // 5. Menggunakan getAllOutgoingSapronakFetcher dengan SWR:
|
||||
* import useSWR from 'swr';
|
||||
* import { ClosingApi } from '@/services/api/closing';
|
||||
*
|
||||
* const { data, error, isLoading } = useSWR(
|
||||
* `${ClosingApi.basePath}/1/sapronak/outgoing`,
|
||||
* ClosingApi.getAllOutgoingSapronakFetcher.bind(ClosingApi)
|
||||
* );
|
||||
*
|
||||
* if (data?.status === 'success') {
|
||||
* console.log(data.data); // Array of ClosingOutgoingSapronak
|
||||
* }
|
||||
*
|
||||
* @see {@link /home/sweetpotet/Documents/projects/lti-web-client/src/types/api/closing.d.ts}
|
||||
*/
|
||||
|
||||
import { format } from 'date-fns';
|
||||
import {
|
||||
Closing,
|
||||
ClosingGeneralInformation,
|
||||
ClosingIncomingSapronak,
|
||||
ClosingOutgoingSapronak,
|
||||
ClosingSapronakCalculation,
|
||||
} from '@/types/api/closing';
|
||||
import { CreatedUser, BaseApiResponse } from '@/types/api/api-general';
|
||||
|
||||
// Waktu saat ini untuk created_at/updated_at
|
||||
const now = format(new Date(), 'yyyy-MM-dd HH:mm:ss');
|
||||
const today = format(new Date(), 'yyyy-MM-dd');
|
||||
const yesterday = format(
|
||||
new Date().setDate(new Date().getDate() - 1),
|
||||
'yyyy-MM-dd'
|
||||
);
|
||||
const lastWeek = format(
|
||||
new Date().setDate(new Date().getDate() - 7),
|
||||
'yyyy-MM-dd'
|
||||
);
|
||||
const lastMonth = format(
|
||||
new Date().setMonth(new Date().getMonth() - 1),
|
||||
'yyyy-MM-dd'
|
||||
);
|
||||
|
||||
// ======================
|
||||
// 👤 Created User
|
||||
// ======================
|
||||
export const createdUser: CreatedUser = {
|
||||
id: 1,
|
||||
id_user: 1,
|
||||
email: 'admin@example.com',
|
||||
name: 'Admin Utama',
|
||||
};
|
||||
|
||||
// ======================
|
||||
// 📊 Closing Dummy Data
|
||||
// ======================
|
||||
export const dummyClosings: Closing[] = [
|
||||
// 1. Closing dengan status Pengajuan - GROWING
|
||||
{
|
||||
id: 1,
|
||||
location_id: 1,
|
||||
location_name: 'Farm Sukajadi',
|
||||
project_category: 'GROWING',
|
||||
period: 1,
|
||||
closing_date: today,
|
||||
shed_label: 'Kandang A1, A2, A3',
|
||||
shed_count: 3,
|
||||
sales_paid_amount: 150000000,
|
||||
sales_remaining_amount: 50000000,
|
||||
sales_payment_status: 'Sebagian Lunas',
|
||||
project_status: 'Pengajuan',
|
||||
created_user: createdUser,
|
||||
created_at: now,
|
||||
updated_at: now,
|
||||
},
|
||||
|
||||
// 2. Closing dengan status Aktif - LAYING
|
||||
{
|
||||
id: 2,
|
||||
location_id: 2,
|
||||
location_name: 'Farm Cihampelas',
|
||||
project_category: 'LAYING',
|
||||
period: 2,
|
||||
closing_date: yesterday,
|
||||
shed_label: 'Kandang B1, B2',
|
||||
shed_count: 2,
|
||||
sales_paid_amount: 200000000,
|
||||
sales_remaining_amount: 0,
|
||||
sales_payment_status: 'Lunas',
|
||||
project_status: 'Aktif',
|
||||
created_user: createdUser,
|
||||
created_at: lastWeek,
|
||||
updated_at: yesterday,
|
||||
},
|
||||
|
||||
// 3. Closing dengan status Selesai - GROWING
|
||||
{
|
||||
id: 3,
|
||||
location_id: 3,
|
||||
location_name: 'Farm Pasteur',
|
||||
project_category: 'GROWING',
|
||||
period: 3,
|
||||
closing_date: lastWeek,
|
||||
shed_label: 'Kandang C1, C2, C3, C4',
|
||||
shed_count: 4,
|
||||
sales_paid_amount: 300000000,
|
||||
sales_remaining_amount: 25000000,
|
||||
sales_payment_status: 'Sebagian Lunas',
|
||||
project_status: 'Selesai',
|
||||
created_user: createdUser,
|
||||
created_at: lastMonth,
|
||||
updated_at: lastWeek,
|
||||
},
|
||||
|
||||
// 4. Closing dengan status Aktif - LAYING
|
||||
{
|
||||
id: 4,
|
||||
location_id: 4,
|
||||
location_name: 'Farm Setiabudi',
|
||||
project_category: 'LAYING',
|
||||
period: 1,
|
||||
closing_date: today,
|
||||
shed_label: 'Kandang D1',
|
||||
shed_count: 1,
|
||||
sales_paid_amount: 75000000,
|
||||
sales_remaining_amount: 75000000,
|
||||
sales_payment_status: 'Belum Lunas',
|
||||
project_status: 'Aktif',
|
||||
created_user: createdUser,
|
||||
created_at: yesterday,
|
||||
updated_at: now,
|
||||
},
|
||||
|
||||
// 5. Closing dengan status Selesai - GROWING
|
||||
{
|
||||
id: 5,
|
||||
location_id: 5,
|
||||
location_name: 'Farm Dago',
|
||||
project_category: 'GROWING',
|
||||
period: 4,
|
||||
closing_date: lastMonth,
|
||||
shed_label: 'Kandang E1, E2, E3, E4, E5',
|
||||
shed_count: 5,
|
||||
sales_paid_amount: 500000000,
|
||||
sales_remaining_amount: 0,
|
||||
sales_payment_status: 'Lunas',
|
||||
project_status: 'Selesai',
|
||||
created_user: createdUser,
|
||||
created_at: lastMonth,
|
||||
updated_at: lastMonth,
|
||||
},
|
||||
|
||||
// 6. Closing dengan status Pengajuan - LAYING
|
||||
{
|
||||
id: 6,
|
||||
location_id: 6,
|
||||
location_name: 'Farm Lembang',
|
||||
project_category: 'LAYING',
|
||||
period: 2,
|
||||
closing_date: undefined, // Belum ada tanggal closing
|
||||
shed_label: 'Kandang F1, F2',
|
||||
shed_count: 2,
|
||||
sales_paid_amount: 0,
|
||||
sales_remaining_amount: 180000000,
|
||||
sales_payment_status: 'Belum Lunas',
|
||||
project_status: 'Pengajuan',
|
||||
created_user: createdUser,
|
||||
created_at: now,
|
||||
updated_at: now,
|
||||
},
|
||||
|
||||
// 7. Closing dengan status Aktif - GROWING
|
||||
{
|
||||
id: 7,
|
||||
location_id: 7,
|
||||
location_name: 'Farm Ciwidey',
|
||||
project_category: 'GROWING',
|
||||
period: 1,
|
||||
closing_date: yesterday,
|
||||
shed_label: 'Kandang G1, G2, G3',
|
||||
shed_count: 3,
|
||||
sales_paid_amount: 120000000,
|
||||
sales_remaining_amount: 30000000,
|
||||
sales_payment_status: 'Sebagian Lunas',
|
||||
project_status: 'Aktif',
|
||||
created_user: createdUser,
|
||||
created_at: lastWeek,
|
||||
updated_at: yesterday,
|
||||
},
|
||||
|
||||
// 8. Closing dengan status Selesai - LAYING
|
||||
{
|
||||
id: 8,
|
||||
location_id: 8,
|
||||
location_name: 'Farm Bandung Timur',
|
||||
project_category: 'LAYING',
|
||||
period: 3,
|
||||
closing_date: lastMonth,
|
||||
shed_label: 'Kandang H1, H2, H3, H4, H5, H6',
|
||||
shed_count: 6,
|
||||
sales_paid_amount: 600000000,
|
||||
sales_remaining_amount: 0,
|
||||
sales_payment_status: 'Lunas',
|
||||
project_status: 'Selesai',
|
||||
created_user: createdUser,
|
||||
created_at: lastMonth,
|
||||
updated_at: lastMonth,
|
||||
},
|
||||
];
|
||||
|
||||
// ======================
|
||||
// 📊 Closing General Information Dummy Data
|
||||
// ======================
|
||||
export const dummyClosingGeneralInformations: ClosingGeneralInformation[] = [
|
||||
// 1. General Info - GROWING - Pengajuan
|
||||
{
|
||||
id: 1,
|
||||
location_id: 1,
|
||||
location_name: 'Farm Sukajadi',
|
||||
project_category: 'GROWING',
|
||||
period: 1,
|
||||
closing_date: today,
|
||||
shed_label: 'Kandang A1, A2, A3',
|
||||
shed_count: 3,
|
||||
sales_paid_amount: 150000000,
|
||||
sales_remaining_amount: 50000000,
|
||||
sales_payment_status: 'Sebagian Lunas',
|
||||
project_status: 'Pengajuan',
|
||||
flock_id: 101,
|
||||
project_type: 'GROWING',
|
||||
population: 15000,
|
||||
active_house_count: 3,
|
||||
closing_status: 'Draft',
|
||||
created_user: createdUser,
|
||||
created_at: now,
|
||||
updated_at: now,
|
||||
},
|
||||
|
||||
// 2. General Info - LAYING - Aktif
|
||||
{
|
||||
id: 2,
|
||||
location_id: 2,
|
||||
location_name: 'Farm Cihampelas',
|
||||
project_category: 'LAYING',
|
||||
period: 2,
|
||||
closing_date: yesterday,
|
||||
shed_label: 'Kandang B1, B2',
|
||||
shed_count: 2,
|
||||
sales_paid_amount: 200000000,
|
||||
sales_remaining_amount: 0,
|
||||
sales_payment_status: 'Lunas',
|
||||
project_status: 'Aktif',
|
||||
flock_id: 102,
|
||||
project_type: 'LAYING',
|
||||
population: 10000,
|
||||
active_house_count: 2,
|
||||
closing_status: 'In Progress',
|
||||
created_user: createdUser,
|
||||
created_at: lastWeek,
|
||||
updated_at: yesterday,
|
||||
},
|
||||
|
||||
// 3. General Info - GROWING - Selesai
|
||||
{
|
||||
id: 3,
|
||||
location_id: 3,
|
||||
location_name: 'Farm Pasteur',
|
||||
project_category: 'GROWING',
|
||||
period: 3,
|
||||
closing_date: lastWeek,
|
||||
shed_label: 'Kandang C1, C2, C3, C4',
|
||||
shed_count: 4,
|
||||
sales_paid_amount: 300000000,
|
||||
sales_remaining_amount: 25000000,
|
||||
sales_payment_status: 'Sebagian Lunas',
|
||||
project_status: 'Selesai',
|
||||
flock_id: 103,
|
||||
project_type: 'GROWING',
|
||||
population: 20000,
|
||||
active_house_count: 4,
|
||||
closing_status: 'Completed',
|
||||
created_user: createdUser,
|
||||
created_at: lastMonth,
|
||||
updated_at: lastWeek,
|
||||
},
|
||||
|
||||
// 4. General Info - LAYING - Aktif
|
||||
{
|
||||
id: 4,
|
||||
location_id: 4,
|
||||
location_name: 'Farm Setiabudi',
|
||||
project_category: 'LAYING',
|
||||
period: 1,
|
||||
closing_date: today,
|
||||
shed_label: 'Kandang D1',
|
||||
shed_count: 1,
|
||||
sales_paid_amount: 75000000,
|
||||
sales_remaining_amount: 75000000,
|
||||
sales_payment_status: 'Belum Lunas',
|
||||
project_status: 'Aktif',
|
||||
flock_id: 104,
|
||||
project_type: 'LAYING',
|
||||
population: 5000,
|
||||
active_house_count: 1,
|
||||
closing_status: 'In Progress',
|
||||
created_user: createdUser,
|
||||
created_at: yesterday,
|
||||
updated_at: now,
|
||||
},
|
||||
|
||||
// 5. General Info - GROWING - Selesai
|
||||
{
|
||||
id: 5,
|
||||
location_id: 5,
|
||||
location_name: 'Farm Dago',
|
||||
project_category: 'GROWING',
|
||||
period: 4,
|
||||
closing_date: lastMonth,
|
||||
shed_label: 'Kandang E1, E2, E3, E4, E5',
|
||||
shed_count: 5,
|
||||
sales_paid_amount: 500000000,
|
||||
sales_remaining_amount: 0,
|
||||
sales_payment_status: 'Lunas',
|
||||
project_status: 'Selesai',
|
||||
flock_id: 105,
|
||||
project_type: 'GROWING',
|
||||
population: 25000,
|
||||
active_house_count: 5,
|
||||
closing_status: 'Completed',
|
||||
created_user: createdUser,
|
||||
created_at: lastMonth,
|
||||
updated_at: lastMonth,
|
||||
},
|
||||
|
||||
// 6. General Info - LAYING - Pengajuan
|
||||
{
|
||||
id: 6,
|
||||
location_id: 6,
|
||||
location_name: 'Farm Lembang',
|
||||
project_category: 'LAYING',
|
||||
period: 2,
|
||||
closing_date: undefined,
|
||||
shed_label: 'Kandang F1, F2',
|
||||
shed_count: 2,
|
||||
sales_paid_amount: 0,
|
||||
sales_remaining_amount: 180000000,
|
||||
sales_payment_status: 'Belum Lunas',
|
||||
project_status: 'Pengajuan',
|
||||
flock_id: 106,
|
||||
project_type: 'LAYING',
|
||||
population: 12000,
|
||||
active_house_count: 2,
|
||||
closing_status: 'Draft',
|
||||
created_user: createdUser,
|
||||
created_at: now,
|
||||
updated_at: now,
|
||||
},
|
||||
|
||||
// 7. General Info - GROWING - Aktif
|
||||
{
|
||||
id: 7,
|
||||
location_id: 7,
|
||||
location_name: 'Farm Ciwidey',
|
||||
project_category: 'GROWING',
|
||||
period: 1,
|
||||
closing_date: yesterday,
|
||||
shed_label: 'Kandang G1, G2, G3',
|
||||
shed_count: 3,
|
||||
sales_paid_amount: 120000000,
|
||||
sales_remaining_amount: 30000000,
|
||||
sales_payment_status: 'Sebagian Lunas',
|
||||
project_status: 'Aktif',
|
||||
flock_id: 107,
|
||||
project_type: 'GROWING',
|
||||
population: 18000,
|
||||
active_house_count: 3,
|
||||
closing_status: 'In Progress',
|
||||
created_user: createdUser,
|
||||
created_at: lastWeek,
|
||||
updated_at: yesterday,
|
||||
},
|
||||
|
||||
// 8. General Info - LAYING - Selesai
|
||||
{
|
||||
id: 8,
|
||||
location_id: 8,
|
||||
location_name: 'Farm Bandung Timur',
|
||||
project_category: 'LAYING',
|
||||
period: 3,
|
||||
closing_date: lastMonth,
|
||||
shed_label: 'Kandang H1, H2, H3, H4, H5, H6',
|
||||
shed_count: 6,
|
||||
sales_paid_amount: 600000000,
|
||||
sales_remaining_amount: 0,
|
||||
sales_payment_status: 'Lunas',
|
||||
project_status: 'Selesai',
|
||||
flock_id: 108,
|
||||
project_type: 'LAYING',
|
||||
population: 30000,
|
||||
active_house_count: 6,
|
||||
closing_status: 'Completed',
|
||||
created_user: createdUser,
|
||||
created_at: lastMonth,
|
||||
updated_at: lastMonth,
|
||||
},
|
||||
];
|
||||
|
||||
// ======================
|
||||
// 📦 Incoming Sapronak Dummy Data
|
||||
// ======================
|
||||
export const dummyIncomingSapronaks: ClosingIncomingSapronak[] = [
|
||||
{
|
||||
id: 1,
|
||||
date: today,
|
||||
reference_number: 'IN-2025-001',
|
||||
transaction_type: 'Pembelian',
|
||||
product_name: 'DOC Broiler Cobb 500',
|
||||
product_category: 'DOC',
|
||||
product_sub_category: 'DOC Broiler',
|
||||
source_warehouse: 'Gudang Pusat',
|
||||
destination_warehouse: 'Kandang A1',
|
||||
quantity: 5000,
|
||||
unit: 'Ekor',
|
||||
formatted_quantity: '5,000 Ekor',
|
||||
notes: 'DOC berkualitas tinggi dari supplier terpercaya',
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
date: yesterday,
|
||||
reference_number: 'IN-2025-002',
|
||||
transaction_type: 'Transfer Masuk',
|
||||
product_name: 'Pakan Starter BR-1',
|
||||
product_category: 'Pakan',
|
||||
product_sub_category: 'Starter',
|
||||
source_warehouse: 'Gudang Area Bandung',
|
||||
destination_warehouse: 'Kandang B1',
|
||||
quantity: 100,
|
||||
unit: 'Sak',
|
||||
formatted_quantity: '100 Sak (5,000 Kg)',
|
||||
notes: 'Pakan starter untuk periode awal',
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
date: lastWeek,
|
||||
reference_number: 'IN-2025-003',
|
||||
transaction_type: 'Pembelian',
|
||||
product_name: 'Vitamin B Complex',
|
||||
product_category: 'OVK',
|
||||
product_sub_category: 'Vitamin',
|
||||
source_warehouse: 'Supplier Medion',
|
||||
destination_warehouse: 'Gudang Farmasi',
|
||||
quantity: 50,
|
||||
unit: 'Botol',
|
||||
formatted_quantity: '50 Botol',
|
||||
notes: 'Vitamin untuk meningkatkan daya tahan tubuh',
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
date: today,
|
||||
reference_number: 'IN-2025-004',
|
||||
transaction_type: 'Pembelian',
|
||||
product_name: 'Pakan Finisher BR-2',
|
||||
product_category: 'Pakan',
|
||||
product_sub_category: 'Finisher',
|
||||
source_warehouse: 'Gudang Pusat',
|
||||
destination_warehouse: 'Kandang C1',
|
||||
quantity: 200,
|
||||
unit: 'Sak',
|
||||
formatted_quantity: '200 Sak (10,000 Kg)',
|
||||
notes: 'Pakan finisher untuk periode akhir',
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
date: yesterday,
|
||||
reference_number: 'IN-2025-005',
|
||||
transaction_type: 'Transfer Masuk',
|
||||
product_name: 'Antibiotik Enrofloxacin',
|
||||
product_category: 'OVK',
|
||||
product_sub_category: 'Obat',
|
||||
source_warehouse: 'Gudang Area Jakarta',
|
||||
destination_warehouse: 'Gudang Farmasi',
|
||||
quantity: 30,
|
||||
unit: 'Box',
|
||||
formatted_quantity: '30 Box',
|
||||
notes: 'Antibiotik untuk pencegahan penyakit',
|
||||
},
|
||||
];
|
||||
|
||||
// ======================
|
||||
// 📤 Outgoing Sapronak Dummy Data
|
||||
// ======================
|
||||
export const dummyOutgoingSapronaks: ClosingOutgoingSapronak[] = [
|
||||
{
|
||||
id: 1,
|
||||
date: today,
|
||||
reference_number: 'OUT-2025-001',
|
||||
transaction_type: 'Pemakaian',
|
||||
product_name: 'Pakan Starter BR-1',
|
||||
product_category: 'Pakan',
|
||||
product_sub_category: 'Starter',
|
||||
source_warehouse: 'Kandang A1',
|
||||
destination_warehouse: 'Konsumsi Kandang A1',
|
||||
quantity: 50,
|
||||
unit: 'Sak',
|
||||
formatted_quantity: '50 Sak (2,500 Kg)',
|
||||
notes: 'Pemakaian pakan harian periode starter',
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
date: yesterday,
|
||||
reference_number: 'OUT-2025-002',
|
||||
transaction_type: 'Transfer Keluar',
|
||||
product_name: 'DOC Broiler Cobb 500',
|
||||
product_category: 'DOC',
|
||||
product_sub_category: 'DOC Broiler',
|
||||
source_warehouse: 'Kandang B1',
|
||||
destination_warehouse: 'Kandang B2',
|
||||
quantity: 1000,
|
||||
unit: 'Ekor',
|
||||
formatted_quantity: '1,000 Ekor',
|
||||
notes: 'Transfer DOC ke kandang baru',
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
date: lastWeek,
|
||||
reference_number: 'OUT-2025-003',
|
||||
transaction_type: 'Pemakaian',
|
||||
product_name: 'Vitamin B Complex',
|
||||
product_category: 'OVK',
|
||||
product_sub_category: 'Vitamin',
|
||||
source_warehouse: 'Gudang Farmasi',
|
||||
destination_warehouse: 'Konsumsi Kandang C1',
|
||||
quantity: 10,
|
||||
unit: 'Botol',
|
||||
formatted_quantity: '10 Botol',
|
||||
notes: 'Pemberian vitamin untuk meningkatkan kesehatan',
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
date: today,
|
||||
reference_number: 'OUT-2025-004',
|
||||
transaction_type: 'Pemakaian',
|
||||
product_name: 'Pakan Finisher BR-2',
|
||||
product_category: 'Pakan',
|
||||
product_sub_category: 'Finisher',
|
||||
source_warehouse: 'Kandang C1',
|
||||
destination_warehouse: 'Konsumsi Kandang C1',
|
||||
quantity: 80,
|
||||
unit: 'Sak',
|
||||
formatted_quantity: '80 Sak (4,000 Kg)',
|
||||
notes: 'Pemakaian pakan harian periode finisher',
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
date: yesterday,
|
||||
reference_number: 'OUT-2025-005',
|
||||
transaction_type: 'Pemakaian',
|
||||
product_name: 'Antibiotik Enrofloxacin',
|
||||
product_category: 'OVK',
|
||||
product_sub_category: 'Obat',
|
||||
source_warehouse: 'Gudang Farmasi',
|
||||
destination_warehouse: 'Konsumsi Kandang D1',
|
||||
quantity: 5,
|
||||
unit: 'Box',
|
||||
formatted_quantity: '5 Box',
|
||||
notes: 'Pengobatan untuk ayam yang sakit',
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
date: lastWeek,
|
||||
reference_number: 'OUT-2025-006',
|
||||
transaction_type: 'Transfer Keluar',
|
||||
product_name: 'Pakan Starter BR-1',
|
||||
product_category: 'Pakan',
|
||||
product_sub_category: 'Starter',
|
||||
source_warehouse: 'Kandang E1',
|
||||
destination_warehouse: 'Kandang E2',
|
||||
quantity: 30,
|
||||
unit: 'Sak',
|
||||
formatted_quantity: '30 Sak (1,500 Kg)',
|
||||
notes: 'Transfer pakan antar kandang',
|
||||
},
|
||||
];
|
||||
|
||||
// ======================
|
||||
// 📊 Perhitungan Sapronak Dummy Data
|
||||
// ======================
|
||||
export const dummySapronakCalculation: ClosingSapronakCalculation = {
|
||||
// DOC Broiler Calculation
|
||||
doc_broiler: {
|
||||
rows: [
|
||||
{
|
||||
id: 1,
|
||||
tanggal: today,
|
||||
no_referensi: 'IN-2025-001',
|
||||
qty_masuk: 5000,
|
||||
qty_keluar: 0,
|
||||
qty_pakai: 0,
|
||||
uraian: 'DOC Broiler Cobb 500',
|
||||
kategori_produk: 'DOC Broiler',
|
||||
harga_beli_per_qty: 8000,
|
||||
total_harga: 40000000,
|
||||
keterangan: 'Pembelian DOC dari supplier',
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
tanggal: yesterday,
|
||||
no_referensi: 'OUT-2025-002',
|
||||
qty_masuk: 0,
|
||||
qty_keluar: 1000,
|
||||
qty_pakai: 0,
|
||||
uraian: 'DOC Broiler Cobb 500',
|
||||
kategori_produk: 'DOC Broiler',
|
||||
harga_beli_per_qty: 8000,
|
||||
total_harga: 8000000,
|
||||
keterangan: 'Transfer DOC ke kandang lain',
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
tanggal: lastWeek,
|
||||
no_referensi: 'USE-2025-001',
|
||||
qty_masuk: 0,
|
||||
qty_keluar: 0,
|
||||
qty_pakai: 50,
|
||||
uraian: 'DOC Broiler Cobb 500',
|
||||
kategori_produk: 'DOC Broiler',
|
||||
harga_beli_per_qty: 8000,
|
||||
total_harga: 400000,
|
||||
keterangan: 'Mortalitas DOC',
|
||||
},
|
||||
],
|
||||
total: {
|
||||
label: 'Total DOC Broiler',
|
||||
qty_masuk: 5000,
|
||||
qty_keluar: 1000,
|
||||
qty_pakai: 50,
|
||||
harga_beli_per_qty: 8000,
|
||||
total_harga: 48400000,
|
||||
},
|
||||
},
|
||||
|
||||
// OVK Calculation
|
||||
ovk: {
|
||||
rows: [
|
||||
{
|
||||
id: 1,
|
||||
tanggal: today,
|
||||
no_referensi: 'IN-2025-003',
|
||||
qty_masuk: 50,
|
||||
qty_keluar: 0,
|
||||
qty_pakai: 0,
|
||||
uraian: 'Vitamin B Complex',
|
||||
kategori_produk: 'Vitamin',
|
||||
harga_beli_per_qty: 150000,
|
||||
total_harga: 7500000,
|
||||
keterangan: 'Pembelian vitamin',
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
tanggal: yesterday,
|
||||
no_referensi: 'IN-2025-005',
|
||||
qty_masuk: 30,
|
||||
qty_keluar: 0,
|
||||
qty_pakai: 0,
|
||||
uraian: 'Antibiotik Enrofloxacin',
|
||||
kategori_produk: 'Obat',
|
||||
harga_beli_per_qty: 250000,
|
||||
total_harga: 7500000,
|
||||
keterangan: 'Pembelian antibiotik',
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
tanggal: lastWeek,
|
||||
no_referensi: 'OUT-2025-003',
|
||||
qty_masuk: 0,
|
||||
qty_keluar: 0,
|
||||
qty_pakai: 10,
|
||||
uraian: 'Vitamin B Complex',
|
||||
kategori_produk: 'Vitamin',
|
||||
harga_beli_per_qty: 150000,
|
||||
total_harga: 1500000,
|
||||
keterangan: 'Pemakaian vitamin',
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
tanggal: yesterday,
|
||||
no_referensi: 'OUT-2025-005',
|
||||
qty_masuk: 0,
|
||||
qty_keluar: 0,
|
||||
qty_pakai: 5,
|
||||
uraian: 'Antibiotik Enrofloxacin',
|
||||
kategori_produk: 'Obat',
|
||||
harga_beli_per_qty: 250000,
|
||||
total_harga: 1250000,
|
||||
keterangan: 'Pemakaian antibiotik',
|
||||
},
|
||||
],
|
||||
total: {
|
||||
label: 'Total OVK',
|
||||
qty_masuk: 80,
|
||||
qty_keluar: 0,
|
||||
qty_pakai: 15,
|
||||
harga_beli_per_qty: 200000,
|
||||
total_harga: 17750000,
|
||||
},
|
||||
},
|
||||
|
||||
// Pakan Calculation
|
||||
pakan: {
|
||||
rows: [
|
||||
{
|
||||
id: 1,
|
||||
tanggal: yesterday,
|
||||
no_referensi: 'IN-2025-002',
|
||||
qty_masuk: 100,
|
||||
qty_keluar: 0,
|
||||
qty_pakai: 0,
|
||||
uraian: 'Pakan Starter BR-1',
|
||||
kategori_produk: 'Starter',
|
||||
harga_beli_per_qty: 450000,
|
||||
total_harga: 45000000,
|
||||
keterangan: 'Pembelian pakan starter',
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
tanggal: today,
|
||||
no_referensi: 'IN-2025-004',
|
||||
qty_masuk: 200,
|
||||
qty_keluar: 0,
|
||||
qty_pakai: 0,
|
||||
uraian: 'Pakan Finisher BR-2',
|
||||
kategori_produk: 'Finisher',
|
||||
harga_beli_per_qty: 480000,
|
||||
total_harga: 96000000,
|
||||
keterangan: 'Pembelian pakan finisher',
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
tanggal: today,
|
||||
no_referensi: 'OUT-2025-001',
|
||||
qty_masuk: 0,
|
||||
qty_keluar: 0,
|
||||
qty_pakai: 50,
|
||||
uraian: 'Pakan Starter BR-1',
|
||||
kategori_produk: 'Starter',
|
||||
harga_beli_per_qty: 450000,
|
||||
total_harga: 22500000,
|
||||
keterangan: 'Pemakaian pakan starter',
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
tanggal: today,
|
||||
no_referensi: 'OUT-2025-004',
|
||||
qty_masuk: 0,
|
||||
qty_keluar: 0,
|
||||
qty_pakai: 80,
|
||||
uraian: 'Pakan Finisher BR-2',
|
||||
kategori_produk: 'Finisher',
|
||||
harga_beli_per_qty: 480000,
|
||||
total_harga: 38400000,
|
||||
keterangan: 'Pemakaian pakan finisher',
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
tanggal: lastWeek,
|
||||
no_referensi: 'OUT-2025-006',
|
||||
qty_masuk: 0,
|
||||
qty_keluar: 30,
|
||||
qty_pakai: 0,
|
||||
uraian: 'Pakan Starter BR-1',
|
||||
kategori_produk: 'Starter',
|
||||
harga_beli_per_qty: 450000,
|
||||
total_harga: 13500000,
|
||||
keterangan: 'Transfer pakan ke kandang lain',
|
||||
},
|
||||
],
|
||||
total: {
|
||||
label: 'Total Pakan',
|
||||
qty_masuk: 300,
|
||||
qty_keluar: 30,
|
||||
qty_pakai: 130,
|
||||
harga_beli_per_qty: 465000,
|
||||
total_harga: 215400000,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
// ======================
|
||||
// 🔧 Dummy API Response Functions
|
||||
// ======================
|
||||
|
||||
/**
|
||||
* Dummy implementation for getAllFetcher
|
||||
* Returns all closing records
|
||||
*/
|
||||
export const dummyGetAllFetcher = async (): Promise<{
|
||||
code: number;
|
||||
status: 'success';
|
||||
message: string;
|
||||
data: Closing[];
|
||||
}> => {
|
||||
await new Promise((resolve) => setTimeout(resolve, 500));
|
||||
return {
|
||||
code: 200,
|
||||
status: 'success',
|
||||
message: 'Data closing berhasil diambil',
|
||||
data: dummyClosings,
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Dummy implementation for getSingle
|
||||
* Returns a single closing by ID
|
||||
*/
|
||||
export const dummyGetSingle = async (
|
||||
id: number
|
||||
): Promise<BaseApiResponse<Closing> | undefined> => {
|
||||
await new Promise((resolve) => setTimeout(resolve, 300));
|
||||
const closing = dummyClosings.find((c) => c.id === id);
|
||||
|
||||
if (!closing) {
|
||||
return {
|
||||
code: 404,
|
||||
status: 'error',
|
||||
message: `Closing dengan ID ${id} tidak ditemukan`,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
code: 200,
|
||||
status: 'success',
|
||||
message: 'Data closing berhasil diambil',
|
||||
data: closing,
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Dummy implementation for getAllIncomingSapronakFetcher
|
||||
* Returns all incoming sapronak records
|
||||
*/
|
||||
export const dummyGetAllIncomingSapronakFetcher = async (): Promise<{
|
||||
code: number;
|
||||
status: 'success';
|
||||
message: string;
|
||||
data: ClosingIncomingSapronak[];
|
||||
}> => {
|
||||
await new Promise((resolve) => setTimeout(resolve, 400));
|
||||
return {
|
||||
code: 200,
|
||||
status: 'success',
|
||||
message: 'Data sapronak masuk berhasil diambil',
|
||||
data: dummyIncomingSapronaks,
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Dummy implementation for getAllOutgoingSapronakFetcher
|
||||
* Returns all outgoing sapronak records
|
||||
*/
|
||||
export const dummyGetAllOutgoingSapronakFetcher = async (): Promise<{
|
||||
code: number;
|
||||
status: 'success';
|
||||
message: string;
|
||||
data: ClosingOutgoingSapronak[];
|
||||
}> => {
|
||||
await new Promise((resolve) => setTimeout(resolve, 400));
|
||||
return {
|
||||
code: 200,
|
||||
status: 'success',
|
||||
message: 'Data sapronak keluar berhasil diambil',
|
||||
data: dummyOutgoingSapronaks,
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Dummy implementation for getGeneralInfo
|
||||
* Returns closing general information by ID
|
||||
*/
|
||||
export const dummyGetGeneralInfo = async (
|
||||
id: number
|
||||
): Promise<BaseApiResponse<ClosingGeneralInformation> | undefined> => {
|
||||
await new Promise((resolve) => setTimeout(resolve, 300));
|
||||
const closingInfo = dummyClosingGeneralInformations.find((c) => c.id == id);
|
||||
|
||||
if (!closingInfo) {
|
||||
return {
|
||||
code: 404,
|
||||
status: 'error',
|
||||
message: `Closing general information dengan ID ${id} tidak ditemukan`,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
code: 200,
|
||||
status: 'success',
|
||||
message: 'Data closing general information berhasil diambil',
|
||||
data: closingInfo,
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Dummy implementation for getPerhitunganSapronak
|
||||
* Returns sapronak calculation data
|
||||
*/
|
||||
export const dummyGetPerhitunganSapronak = async (
|
||||
id: number
|
||||
): Promise<
|
||||
| {
|
||||
code: number;
|
||||
status: 'success';
|
||||
message: string;
|
||||
data: ClosingSapronakCalculation;
|
||||
}
|
||||
| undefined
|
||||
> => {
|
||||
await new Promise((resolve) => setTimeout(resolve, 400));
|
||||
return {
|
||||
code: 200,
|
||||
status: 'success',
|
||||
message: 'Data perhitungan sapronak berhasil diambil',
|
||||
data: dummySapronakCalculation,
|
||||
};
|
||||
};
|
||||
+101
-3
@@ -6,9 +6,18 @@ import {
|
||||
ClosingGeneralInformation,
|
||||
ClosingIncomingSapronak,
|
||||
ClosingOutgoingSapronak,
|
||||
ClosingSapronakCalculation,
|
||||
} from '@/types/api/closing';
|
||||
import { httpClient, httpClientFetcher } from '@/services/http/client';
|
||||
import { BaseApiResponse } from '@/types/api/api-general';
|
||||
import {
|
||||
dummyGetAllFetcher,
|
||||
dummyGetSingle,
|
||||
dummyGetAllIncomingSapronakFetcher,
|
||||
dummyGetAllOutgoingSapronakFetcher,
|
||||
dummyGetGeneralInfo,
|
||||
dummyGetPerhitunganSapronak,
|
||||
} from '@/dummy/closing.dummy';
|
||||
import { httpClient, httpClientFetcher } from '@/services/http/client';
|
||||
import { ClosingSales } from '@/types/api/closing';
|
||||
|
||||
export class ClosingApiService extends BaseApiService<Closing, null, null> {
|
||||
@@ -16,6 +25,39 @@ export class ClosingApiService extends BaseApiService<Closing, null, null> {
|
||||
super(basePath);
|
||||
}
|
||||
|
||||
async getAllFetcher(endpoint: string): Promise<BaseApiResponse<Closing[]>> {
|
||||
// TODO: Remove this block when backend is ready
|
||||
// return await dummyGetAllFetcher();
|
||||
|
||||
// Uncomment this when backend is ready
|
||||
return await httpClientFetcher<BaseApiResponse<Closing[]>>(endpoint);
|
||||
}
|
||||
|
||||
async getSingle(id: number): Promise<BaseApiResponse<Closing> | undefined> {
|
||||
// TODO: Remove this block when backend is ready
|
||||
// try {
|
||||
// return await dummyGetSingle(id);
|
||||
// } catch (error) {
|
||||
// if (axios.isAxiosError<BaseApiResponse<Closing>>(error)) {
|
||||
// return error.response?.data;
|
||||
// }
|
||||
// return undefined;
|
||||
// }
|
||||
|
||||
// Uncomment this when backend is ready
|
||||
try {
|
||||
const getSinglePath = `${this.basePath}/${id}`;
|
||||
const getSingleRes =
|
||||
await httpClient<BaseApiResponse<Closing>>(getSinglePath);
|
||||
return getSingleRes;
|
||||
} catch (error) {
|
||||
if (axios.isAxiosError<BaseApiResponse<Closing>>(error)) {
|
||||
return error.response?.data;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
async getPenjualan(
|
||||
id: number
|
||||
): Promise<BaseApiResponse<ClosingSales> | undefined> {
|
||||
@@ -36,6 +78,10 @@ export class ClosingApiService extends BaseApiService<Closing, null, null> {
|
||||
async getAllIncomingSapronakFetcher(
|
||||
endpoint: string
|
||||
): Promise<BaseApiResponse<ClosingIncomingSapronak[]>> {
|
||||
// TODO: Remove this block when backend is ready
|
||||
// return await dummyGetAllIncomingSapronakFetcher();
|
||||
|
||||
// Uncomment this when backend is ready
|
||||
return await httpClientFetcher<BaseApiResponse<ClosingIncomingSapronak[]>>(
|
||||
endpoint
|
||||
);
|
||||
@@ -44,19 +90,37 @@ export class ClosingApiService extends BaseApiService<Closing, null, null> {
|
||||
async getAllOutgoingSapronakFetcher(
|
||||
endpoint: string
|
||||
): Promise<BaseApiResponse<ClosingOutgoingSapronak[]>> {
|
||||
// TODO: Remove this block when backend is ready
|
||||
// return await dummyGetAllOutgoingSapronakFetcher();
|
||||
|
||||
// Uncomment this when backend is ready
|
||||
return await httpClientFetcher<BaseApiResponse<ClosingOutgoingSapronak[]>>(
|
||||
endpoint
|
||||
);
|
||||
}
|
||||
|
||||
async getGeneralInfo(id: number) {
|
||||
async getGeneralInfo(
|
||||
id: number
|
||||
): Promise<BaseApiResponse<ClosingGeneralInformation> | undefined> {
|
||||
// TODO: Remove this block when backend is ready
|
||||
// try {
|
||||
// return await dummyGetGeneralInfo(id);
|
||||
// } catch (error) {
|
||||
// if (
|
||||
// axios.isAxiosError<BaseApiResponse<ClosingGeneralInformation>>(error)
|
||||
// ) {
|
||||
// return error.response?.data;
|
||||
// }
|
||||
// return undefined;
|
||||
// }
|
||||
|
||||
// Uncomment this when backend is ready
|
||||
try {
|
||||
const getGeneralInfoPath = `${this.basePath}/${id}`;
|
||||
const getGeneralInfoRes =
|
||||
await httpClient<BaseApiResponse<ClosingGeneralInformation>>(
|
||||
getGeneralInfoPath
|
||||
);
|
||||
|
||||
return getGeneralInfoRes;
|
||||
} catch (error) {
|
||||
if (
|
||||
@@ -67,6 +131,40 @@ export class ClosingApiService extends BaseApiService<Closing, null, null> {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
async getPerhitunganSapronak(
|
||||
id: number
|
||||
): Promise<BaseApiResponse<ClosingSapronakCalculation> | undefined> {
|
||||
// TODO: Remove this block when backend is ready
|
||||
// try {
|
||||
// return await dummyGetPerhitunganSapronak(id);
|
||||
// } catch (error) {
|
||||
// if (
|
||||
// axios.isAxiosError<BaseApiResponse<ClosingSapronakCalculation>>(error)
|
||||
// ) {
|
||||
// return error.response?.data;
|
||||
// }
|
||||
// return undefined;
|
||||
// }
|
||||
|
||||
// Uncomment this when backend is ready
|
||||
try {
|
||||
const path = `${this.basePath}/${id}/perhitungan_sapronak`;
|
||||
return await httpClient<BaseApiResponse<ClosingSapronakCalculation>>(
|
||||
path,
|
||||
{
|
||||
method: 'GET',
|
||||
}
|
||||
);
|
||||
} catch (error) {
|
||||
if (
|
||||
axios.isAxiosError<BaseApiResponse<ClosingSapronakCalculation>>(error)
|
||||
) {
|
||||
return error.response?.data;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const ClosingApi = new ClosingApiService('/closings');
|
||||
|
||||
Vendored
+36
@@ -78,4 +78,40 @@ export type ClosingIncomingSapronak = {
|
||||
};
|
||||
|
||||
export type ClosingOutgoingSapronak = ClosingIncomingSapronak;
|
||||
|
||||
// ====== PERHITUNGAN SAPRONAK ======
|
||||
|
||||
export type RowSapronakCalculation = {
|
||||
id: number;
|
||||
tanggal: string;
|
||||
no_referensi: string;
|
||||
qty_masuk: number;
|
||||
qty_keluar: number;
|
||||
qty_pakai: number;
|
||||
uraian: string;
|
||||
kategori_produk: string;
|
||||
harga_beli_per_qty: number;
|
||||
total_harga: number;
|
||||
keterangan: string;
|
||||
};
|
||||
|
||||
export type TotalSapronakCalculation = {
|
||||
label: string;
|
||||
qty_masuk: number;
|
||||
qty_keluar: number;
|
||||
qty_pakai: number;
|
||||
harga_beli_per_qty: number;
|
||||
total_harga: number;
|
||||
};
|
||||
|
||||
export type ClosingSapronakCalculationItem = {
|
||||
rows: RowSapronakCalculation[];
|
||||
total: TotalSapronakCalculation;
|
||||
};
|
||||
|
||||
export type ClosingSapronakCalculation = {
|
||||
doc_broiler: ClosingSapronakCalculationItem;
|
||||
ovk: ClosingSapronakCalculationItem;
|
||||
pakan: ClosingSapronakCalculationItem;
|
||||
};
|
||||
export type ClosingSales = BaseMetadata & BaseClosingSales;
|
||||
|
||||
Reference in New Issue
Block a user