diff --git a/.gitignore b/.gitignore
index 7d6264e6..e47b8ec3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -44,4 +44,4 @@ next-env.d.ts
.idea
# claude
-.claude
\ No newline at end of file
+.claude
diff --git a/package-lock.json b/package-lock.json
index 01bff9ef..f960d1c5 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1855,6 +1855,7 @@
"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"
}
@@ -1924,6 +1925,7 @@
"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",
@@ -2447,6 +2449,7 @@
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
"dev": true,
"license": "MIT",
+ "peer": true,
"bin": {
"acorn": "bin/acorn"
},
@@ -3060,7 +3063,8 @@
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
- "license": "MIT"
+ "license": "MIT",
+ "peer": true
},
"node_modules/daisyui": {
"version": "5.5.8",
@@ -3516,6 +3520,7 @@
"integrity": "sha512-t5aPOpmtJcZcz5UJyY2GbvpDlsK5E8JqRqoKtfiKE3cNh437KIqfJr3A3AKf5k64NPx6d0G3dno6XDY05PqPtw==",
"dev": true,
"license": "MIT",
+ "peer": true,
"dependencies": {
"@eslint-community/eslint-utils": "^4.8.0",
"@eslint-community/regexpp": "^4.12.1",
@@ -3689,6 +3694,7 @@
"integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==",
"dev": true,
"license": "MIT",
+ "peer": true,
"dependencies": {
"@rtsao/scc": "^1.1.0",
"array-includes": "^3.1.9",
@@ -6167,6 +6173,7 @@
"resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz",
"integrity": "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==",
"license": "MIT",
+ "peer": true,
"engines": {
"node": ">=0.10.0"
}
@@ -6197,6 +6204,7 @@
"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"
},
@@ -7083,6 +7091,7 @@
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
"dev": true,
"license": "MIT",
+ "peer": true,
"engines": {
"node": ">=12"
},
@@ -7250,6 +7259,7 @@
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
"dev": true,
"license": "Apache-2.0",
+ "peer": true,
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
diff --git a/src/app/closing/detail/page.tsx b/src/app/closing/detail/page.tsx
index 6225b8dd..487533be 100644
--- a/src/app/closing/detail/page.tsx
+++ b/src/app/closing/detail/page.tsx
@@ -4,6 +4,7 @@ import { useRouter, useSearchParams } from 'next/navigation';
import useSWR from 'swr';
import ClosingDetail from '@/components/pages/closing/ClosingDetail';
+import SalesReportTable from '@/components/pages/closing/sale/SalesReportTable';
import { ClosingApi } from '@/services/api/closing';
import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
@@ -19,6 +20,11 @@ const ClosingDetailPage = () => {
(id: number) => ClosingApi.getGeneralInfo(id)
);
+ const { data: salesReport, isLoading: isLoadingSalesReport } = useSWR(
+ closingId,
+ (id: number) => ClosingApi.getPenjualan(id)
+ );
+
if (!closingId) {
router.back();
@@ -43,6 +49,9 @@ const ClosingDetailPage = () => {
{!isLoadingClosing && isResponseSuccess(closing) && (
)}
+ {!isLoadingSalesReport && isResponseSuccess(salesReport) && (
+
+ )}
);
};
diff --git a/src/app/production/project-flock/layout.tsx b/src/app/production/project-flock/layout.tsx
index 698064cf..b74ef612 100644
--- a/src/app/production/project-flock/layout.tsx
+++ b/src/app/production/project-flock/layout.tsx
@@ -52,6 +52,7 @@ export default function ProjectFlockLayout({
closeOnBackdropClick={isDetail ? true : false}
onBackdropClick={handleBackdropClick}
variant='right'
+ zIndex='99999'
sidebarContent={isOpen &&
{children}
}
/>
>
diff --git a/src/components/FloatingActionsButton.tsx b/src/components/FloatingActionsButton.tsx
index c0033d72..c9ca3454 100644
--- a/src/components/FloatingActionsButton.tsx
+++ b/src/components/FloatingActionsButton.tsx
@@ -54,7 +54,7 @@ const FloatingActionsButton = ({
diff --git a/src/components/Navbar.tsx b/src/components/Navbar.tsx
index 973bf031..bee92a57 100644
--- a/src/components/Navbar.tsx
+++ b/src/components/Navbar.tsx
@@ -7,6 +7,7 @@ import { Icon } from '@iconify/react';
import Menu from '@/components/menu/Menu';
import MenuItem from '@/components/menu/MenuItem';
import Button from '@/components/Button';
+import Dropdown from '@/components/dropdown/Dropdown';
import { useAuth } from '@/services/hooks/useAuth';
import { AuthApi } from '@/services/api/auth';
@@ -52,21 +53,21 @@ const Navbar = ({ title, toggleSidebar }: NavbarProps) => {
-
-
-
-
+
);
diff --git a/src/components/Table.tsx b/src/components/Table.tsx
index 970c5bc1..9feb33e2 100644
--- a/src/components/Table.tsx
+++ b/src/components/Table.tsx
@@ -14,6 +14,7 @@ import {
SortingState,
OnChangeFn,
Row,
+ HeaderContext,
} from '@tanstack/react-table';
import { rankItem } from '@tanstack/match-sorter-utils';
import { Icon } from '@iconify/react';
@@ -31,6 +32,9 @@ interface TableClassNames {
tableBodyClassName?: string;
bodyRowClassName?: string;
bodyColumnClassName?: string;
+ tableFooterClassName?: string;
+ footerRowClassName?: string;
+ footerColumnClassName?: string;
paginationClassName?: string;
}
@@ -53,6 +57,7 @@ export interface TableProps {
rowSelection?: Record;
setRowSelection?: OnChangeFn>;
enableRowSelection?: boolean | ((row: Row) => boolean);
+ renderFooter?: boolean;
withCheckbox?: boolean;
rowOptions?: number[];
}
@@ -67,18 +72,22 @@ const emptyContentDefaultValue = (
);
-const TABLE_DEFAULT_STYLING = {
+export const TABLE_DEFAULT_STYLING = {
containerClassName: 'w-full mb-20',
tableWrapperClassName:
'overflow-x-auto border border-solid border-base-content/10 rounded-lg',
tableClassName: 'font-inter w-full table-auto text-sm font-medium',
tableHeaderClassName: '',
headerRowClassName: '',
- headerColumnClassName: 'px-4 py-3 text-base-content/50',
+ headerColumnClassName:
+ 'px-4 py-3 border-base-content/10 text-base-content/50',
tableBodyClassName: '',
- bodyRowClassName: 'border-t border-t-base-content/10',
+ bodyRowClassName: 'border-t border-base-content/10',
bodyColumnClassName: 'px-4 py-3 text-base-content',
paginationClassName: '',
+ tableFooterClassName: 'font-semibold border-base-content/10',
+ footerRowClassName: 'bg-base-200 border-t-2 border-base-content/10',
+ footerColumnClassName: 'p-4 text-base-content whitespace-nowrap',
};
const Table = ({
@@ -100,6 +109,7 @@ const Table = ({
rowSelection,
setRowSelection,
enableRowSelection,
+ renderFooter = false,
withCheckbox = false,
rowOptions = [10, 20, 50, 100],
}: TableProps) => {
@@ -214,58 +224,82 @@ const Table = ({
key={headerGroup.id}
className={tableClassNames.headerRowClassName}
>
- {headerGroup.headers.map((header) => (
-
-
- {flexRender(
- header.column.columnDef.header,
- header.getContext()
+ {headerGroup.headers.map((header) => {
+ const columnRelativeDepth =
+ header.depth - header.column.depth;
+ if (
+ !header.isPlaceholder &&
+ columnRelativeDepth > 1 &&
+ header.id === header.column.id
+ ) {
+ return null;
+ }
+ let rowSpan = 1;
+ if (header.isPlaceholder) {
+ const leafs = header.getLeafHeaders();
+ rowSpan = leafs[leafs.length - 1].depth - header.depth;
+ }
+ return (
+ 1,
+ },
+ tableClassNames.headerColumnClassName
)}
+ >
+ 1,
+ })}
+ >
+ {flexRender(
+ header.column.columnDef.header,
+ header.getContext()
+ )}
- {header.column.getCanSort() && (
-
-
-
-
- )}
-
- |
- ))}
+ {header.column.getCanSort() && (
+
+
+
+
+ )}
+
+ |
+ );
+ })}
))}
@@ -290,6 +324,28 @@ const Table = ({
))}
+
+ {renderFooter && (
+
+ {table.getAllLeafColumns().map((column) => (
+ |
+ {column.columnDef.footer &&
+ flexRender(column.columnDef.footer, {
+ column,
+ header: column.columnDef,
+ table,
+ } as HeaderContext)}
+ |
+ ))}
+
+ )}
+
diff --git a/src/components/dropdown/Dropdown.tsx b/src/components/dropdown/Dropdown.tsx
new file mode 100644
index 00000000..4489231d
--- /dev/null
+++ b/src/components/dropdown/Dropdown.tsx
@@ -0,0 +1,116 @@
+'use client';
+
+import { ReactNode, useRef, useEffect, useState } from 'react';
+import { cn } from '@/lib/helper';
+
+interface DropdownProps {
+ trigger: ReactNode;
+ children: ReactNode;
+ position?:
+ | 'top'
+ | 'bottom'
+ | 'left'
+ | 'right'
+ | 'top-start'
+ | 'top-end'
+ | 'bottom-start'
+ | 'bottom-end'
+ | 'left-start'
+ | 'left-end'
+ | 'right-start'
+ | 'right-end';
+ align?: 'start' | 'center' | 'end';
+ hover?: boolean;
+ className?: string;
+ contentClassName?: string;
+}
+
+const Dropdown = ({
+ trigger,
+ children,
+ position = 'bottom',
+ align = 'start',
+ hover = false,
+ className,
+ contentClassName,
+}: DropdownProps) => {
+ const [isOpen, setIsOpen] = useState(false);
+ const dropdownRef = useRef(null);
+
+ // Handle click outside to close dropdown
+ useEffect(() => {
+ const handleClickOutside = (event: MouseEvent) => {
+ if (
+ dropdownRef.current &&
+ !dropdownRef.current.contains(event.target as Node)
+ ) {
+ setIsOpen(false);
+ }
+ };
+
+ if (isOpen) {
+ document.addEventListener('mousedown', handleClickOutside);
+ }
+
+ return () => {
+ document.removeEventListener('mousedown', handleClickOutside);
+ };
+ }, [isOpen]);
+
+ // Build position classes
+ const getPositionClasses = () => {
+ const classes: string[] = [];
+
+ // Handle combined positions like 'top-start'
+ if (position.includes('-')) {
+ const [pos, al] = position.split('-');
+ classes.push(`dropdown-${pos}`);
+ classes.push(`dropdown-${al}`);
+ } else {
+ classes.push(`dropdown-${position}`);
+ if (align !== 'start') {
+ classes.push(`dropdown-${align}`);
+ }
+ }
+
+ return classes.join(' ');
+ };
+
+ const handleToggle = (e: React.MouseEvent) => {
+ e.preventDefault();
+ e.stopPropagation();
+ // alert('clicked');
+ setIsOpen(!isOpen);
+ };
+
+ return (
+
+ {/* Trigger Button */}
+
+ {trigger}
+
+
+ {/* Dropdown Content - Only render when open */}
+ {isOpen && (
+
setIsOpen(false)} // Close on item click
+ >
+ {children}
+
+ )}
+
+ );
+};
+
+export default Dropdown;
diff --git a/src/components/pages/closing/sale/SalesReportTable.tsx b/src/components/pages/closing/sale/SalesReportTable.tsx
new file mode 100644
index 00000000..e509eb7d
--- /dev/null
+++ b/src/components/pages/closing/sale/SalesReportTable.tsx
@@ -0,0 +1,285 @@
+'use client';
+
+import React, { useMemo } from 'react';
+import { ColumnDef } from '@tanstack/react-table';
+import Table from '@/components/Table';
+import Card from '@/components/Card';
+import Badge from '@/components/Badge';
+import { formatCurrency, formatNumber, formatDate } from '@/lib/helper';
+import { BaseClosingSales, BaseSales } from '@/types/api/closing';
+import { Product } from '@/types/api/master-data/product';
+import { Customer } from '@/types/api/master-data/customer';
+import { Kandang } from '@/types/api/master-data/kandang';
+
+interface SalesReportTableProps {
+ type?: 'detail';
+ initialValues?: BaseClosingSales;
+}
+
+const SalesReportTable = ({
+ type = 'detail',
+ initialValues,
+}: SalesReportTableProps) => {
+ const salesData: BaseSales[] = useMemo(() => {
+ return initialValues?.sales || [];
+ }, [initialValues]);
+
+ const totals = useMemo(() => {
+ if (salesData.length === 0) {
+ return {
+ totalQuantity: 0,
+ totalWeight: 0,
+ avgWeight: 0,
+ avgPricePartner: 0,
+ totalPartner: 0,
+ };
+ }
+
+ const totalQuantity = salesData.reduce(
+ (sum, item) => sum + (item.qty || 0),
+ 0
+ );
+ const totalWeight = salesData.reduce(
+ (sum, item) => sum + (item.weight || 0),
+ 0
+ );
+ const avgWeight = totalQuantity > 0 ? totalWeight / totalQuantity : 0;
+
+ const validPriceItems = salesData.filter(
+ (item) => item.price != null && item.price > 0
+ );
+ const avgPricePartner =
+ validPriceItems.length > 0
+ ? validPriceItems.reduce((sum, item) => sum + item.price, 0) /
+ validPriceItems.length
+ : 0;
+
+ const totalPartner = salesData.reduce(
+ (sum, item) => sum + (item.total_price || 0),
+ 0
+ );
+
+ return {
+ totalQuantity,
+ totalWeight,
+ avgWeight,
+ avgPricePartner,
+ totalPartner,
+ };
+ }, [salesData]);
+
+ const salesColumns: ColumnDef[] = useMemo(
+ () => [
+ {
+ id: 'realization_date',
+ accessorKey: 'realization_date',
+ header: 'Tanggal Realisasi',
+ cell: (props) => {
+ const date = props.row.original.realization_date;
+ return date ? formatDate(date, 'DD MMM YYYY') : '-';
+ },
+ footer: () => (
+ Total Penjualan
+ ),
+ },
+ {
+ id: 'age',
+ accessorKey: 'age',
+ header: 'Umur',
+ cell: (props) => props.getValue() || '-',
+ },
+ {
+ id: 'do_number',
+ accessorKey: 'do_number',
+ header: 'No. DO',
+ cell: (props) => props.getValue() || '-',
+ },
+ {
+ id: 'product',
+ accessorKey: 'product',
+ header: 'Produk',
+ cell: (props) => {
+ const product = props.getValue() as Product;
+ return product?.name || '-';
+ },
+ },
+ {
+ id: 'customer',
+ accessorKey: 'customer',
+ header: 'Customer',
+ cell: (props) => {
+ const customer = props.getValue() as Customer;
+ return customer?.name || '-';
+ },
+ },
+ {
+ id: 'jumlah',
+ header: 'Jumlah',
+ columns: [
+ {
+ id: 'qty',
+ accessorKey: 'qty',
+ header: 'Kuantitas',
+ cell: (props) => {
+ const value = props.getValue() as number;
+ return {formatNumber(value)}
;
+ },
+ footer: () => (
+
+ {formatNumber(totals.totalQuantity)}
+
+ ),
+ },
+ {
+ id: 'weight',
+ accessorKey: 'weight',
+ header: 'Kg',
+ cell: (props) => {
+ const value = props.getValue() as number;
+ return {formatNumber(value)}
;
+ },
+ footer: () => (
+
+ {formatNumber(totals.totalWeight)}
+
+ ),
+ },
+ ],
+ },
+ {
+ id: 'avg_weight',
+ accessorKey: 'avg_weight',
+ header: 'AVG (Kg)',
+ cell: (props) => {
+ const value = props.getValue() as number;
+ return {formatNumber(value)}
;
+ },
+ footer: () => (
+
+ {formatNumber(totals.avgWeight)}
+
+ ),
+ },
+ {
+ id: 'price_partner',
+ accessorKey: 'price',
+ header: 'Harga Mitra (Rp)',
+ cell: (props) => {
+ const value = props.getValue() as number;
+ return {formatCurrency(value)}
;
+ },
+ footer: () => (
+
+ {formatCurrency(totals.avgPricePartner)}
+
+ ),
+ },
+ {
+ id: 'total_mitra',
+ accessorKey: 'total_price',
+ header: 'Total Mitra (Rp)',
+ cell: (props) => {
+ const value = props.getValue() as number;
+ return {formatCurrency(value)}
;
+ },
+ footer: () => (
+
+ {formatCurrency(totals.totalPartner)}
+
+ ),
+ },
+ {
+ id: 'price_act',
+ accessorKey: 'price',
+ header: 'Harga Act (Rp)',
+ cell: (props) => {
+ const value = props.getValue() as number;
+ return {formatCurrency(value)}
;
+ },
+ },
+ {
+ id: 'total_act',
+ accessorKey: 'total_price',
+ header: 'Total Act (Rp)',
+ cell: (props) => {
+ const value = props.getValue() as number;
+ return {formatCurrency(value)}
;
+ },
+ },
+ {
+ id: 'kandang',
+ accessorKey: 'kandang',
+ header: 'Kandang',
+ cell: (props) => {
+ const kandang = props.getValue() as Kandang;
+ return kandang?.name || '-';
+ },
+ },
+ {
+ id: 'payment_status',
+ accessorKey: 'payment_status',
+ header: 'Status Pembayaran',
+ cell: (props) => {
+ const status = props.getValue() as string;
+ const getStatusColor = (status: string) => {
+ if (!status) return 'neutral';
+ switch (status.toLowerCase()) {
+ case 'paid':
+ return 'success';
+ case 'tempo':
+ return 'warning';
+ default:
+ return 'neutral';
+ }
+ };
+
+ return (
+
+ {status || '-'}
+
+ );
+ },
+ },
+ ],
+ []
+ );
+
+ return (
+ <>
+
+
+
Penjualan
+
+ 0}
+ className={{
+ tableWrapperClassName: 'overflow-x-auto',
+ tableClassName: 'w-full table-auto text-sm',
+ headerColumnClassName:
+ 'px-4 py-3 text-xs font-semibold text-gray-500 last:flex last:flex-row last:justify-end 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:
+ '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:
+ 'px-4 py-3 text-xs text-gray-900 whitespace-nowrap',
+ tableFooterClassName:
+ 'bg-gray-100 font-semibold border border-gray-200',
+ footerRowClassName: 'border-t-2 border-gray-300',
+ footerColumnClassName:
+ 'px-4 py-3 text-xs text-gray-900 whitespace-nowrap',
+ }}
+ />
+
+
+
+ >
+ );
+};
+
+export default SalesReportTable;
diff --git a/src/components/pages/production/recording/RecordingTable.tsx b/src/components/pages/production/recording/RecordingTable.tsx
index 6cf254e7..4a413bc4 100644
--- a/src/components/pages/production/recording/RecordingTable.tsx
+++ b/src/components/pages/production/recording/RecordingTable.tsx
@@ -370,7 +370,7 @@ const RecordingTable = () => {
const [isDeleteLoading, setIsDeleteLoading] = useState(false);
const [isApproveLoading, setIsApproveLoading] = useState(false);
const [isRejectLoading, setIsRejectLoading] = useState(false);
- const [approvalNotes, setApprovalNotes] = useState('');
+ const [, setApprovalNotes] = useState('');
const singleDeleteModal = useModal();
const approveModal = useModal();
diff --git a/src/services/api/closing.ts b/src/services/api/closing.ts
index 041108d0..fe2c2d50 100644
--- a/src/services/api/closing.ts
+++ b/src/services/api/closing.ts
@@ -9,12 +9,29 @@ import {
} from '@/types/api/closing';
import { httpClient, httpClientFetcher } from '@/services/http/client';
import { BaseApiResponse } from '@/types/api/api-general';
+import { ClosingSales } from '@/types/api/closing';
export class ClosingApiService extends BaseApiService {
constructor(basePath: string) {
super(basePath);
}
+ async getPenjualan(
+ id: number
+ ): Promise | undefined> {
+ try {
+ const getPenjualanPath = `${id}/penjualan`;
+ return await this.customRequest>(
+ getPenjualanPath
+ );
+ } catch (error) {
+ if (axios.isAxiosError>(error)) {
+ return error.response?.data;
+ }
+ return undefined;
+ }
+ }
+
async getAllIncomingSapronakFetcher(
endpoint: string
): Promise> {
@@ -51,4 +68,4 @@ export class ClosingApiService extends BaseApiService {
}
}
-export const ClosingApi = new ClosingApiService('/closing');
+export const ClosingApi = new ClosingApiService('/closings');
diff --git a/src/types/api/closing.d.ts b/src/types/api/closing.d.ts
index 3f7ba816..95b2f57f 100644
--- a/src/types/api/closing.d.ts
+++ b/src/types/api/closing.d.ts
@@ -1,9 +1,34 @@
import { Area } from '@/types/api/master-data/area';
import { Fcr } from '@/types/api/master-data/fcr';
import { Flock } from '@/types/api/master-data/flock';
-import { Kandang } from '@/types/api/master-data/kandang';
import { Location } from '@/types/api/master-data/location';
-import { BaseApproval, BaseMetadata } from '@/types/api/api-general';
+import { Kandang } from '@/types/api/master-data/kandang';
+import { Product } from '@type/api/master-data/product';
+import { Customer } from '@type/api/master-data/customer';
+import { BaseMetadata } from '@/types/api/api-general';
+
+export type BaseSales = {
+ id: number;
+ realization_date: string;
+ age: number;
+ do_number: string;
+ product: Product;
+ customer: Customer;
+ qty: number;
+ weight: number;
+ avg_weight: number;
+ price: number;
+ total_price: number;
+ kandang: Kandang;
+ payment_status: string;
+};
+
+export type BaseClosingSales = {
+ project_type: string;
+ flock_id: number;
+ period: number;
+ sales: BaseSales[];
+};
export type BaseClosing = {
id: number;
@@ -53,3 +78,4 @@ export type ClosingIncomingSapronak = {
};
export type ClosingOutgoingSapronak = ClosingIncomingSapronak;
+export type ClosingSales = BaseMetadata & BaseClosingSales;