refactor(FE): Add sales/actual price fields and summary

This commit is contained in:
rstubryan
2026-01-21 10:01:08 +07:00
parent 9bd4df3f4c
commit 5053ce35df
2 changed files with 88 additions and 54 deletions
@@ -6,7 +6,11 @@ import Table from '@/components/Table';
import Card from '@/components/Card'; import Card from '@/components/Card';
import Badge from '@/components/Badge'; import Badge from '@/components/Badge';
import { formatCurrency, formatNumber, formatDate } from '@/lib/helper'; import { formatCurrency, formatNumber, formatDate } from '@/lib/helper';
import { BaseClosingSales, BaseSales } from '@/types/api/closing'; import {
BaseClosingSales,
BaseSales,
ClosingSalesSummary,
} from '@/types/api/closing';
import { Product } from '@/types/api/master-data/product'; import { Product } from '@/types/api/master-data/product';
import { Customer } from '@/types/api/master-data/customer'; import { Customer } from '@/types/api/master-data/customer';
import { Kandang } from '@/types/api/master-data/kandang'; import { Kandang } from '@/types/api/master-data/kandang';
@@ -24,14 +28,20 @@ const SalesReportTable = ({
return initialValues?.sales || []; return initialValues?.sales || [];
}, [initialValues]); }, [initialValues]);
const summary: ClosingSalesSummary | undefined = useMemo(() => {
return initialValues?.summary;
}, [initialValues]);
const totals = useMemo(() => { const totals = useMemo(() => {
if (salesData.length === 0) { if (salesData.length === 0) {
return { return {
totalQuantity: 0, totalQuantity: 0,
totalWeight: 0, totalWeight: 0,
avgWeight: 0, avgWeight: 0,
avgPricePartner: 0, avgSalesPrice: 0,
totalPartner: 0, totalSalesPrice: 0,
avgActualPrice: 0,
totalActualPrice: 0,
}; };
} }
@@ -45,26 +55,46 @@ const SalesReportTable = ({
); );
const avgWeight = totalQuantity > 0 ? totalWeight / totalQuantity : 0; const avgWeight = totalQuantity > 0 ? totalWeight / totalQuantity : 0;
const validPriceItems = salesData.filter( const totalSalesPrice = salesData.reduce(
(item) => item.price != null && item.price > 0 (sum, item) => sum + (item.total_sales_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 0
); );
const validSalesPriceItems = salesData.filter(
(item) => item.sales_price != null && item.sales_price > 0
);
const avgSalesPrice =
validSalesPriceItems.length > 0
? validSalesPriceItems.reduce(
(sum, item) => sum + item.sales_price,
0
) / validSalesPriceItems.length
: 0;
const totalActualPrice = salesData.reduce(
(sum, item) => sum + (item.total_actual_price || 0),
0
);
const validActualPriceItems = salesData.filter(
(item) => item.actual_price != null && item.actual_price > 0
);
const avgActualPrice =
validActualPriceItems.length > 0
? validActualPriceItems.reduce(
(sum, item) => sum + item.actual_price,
0
) / validActualPriceItems.length
: 0;
return { return {
totalQuantity, totalQuantity,
totalWeight, totalWeight,
avgWeight, avgWeight,
avgPricePartner, avgSalesPrice,
totalPartner, totalSalesPrice,
avgActualPrice,
totalActualPrice,
}; };
}, [salesData]); }, [salesData]);
@@ -161,50 +191,68 @@ const SalesReportTable = ({
), ),
}, },
{ {
id: 'price_partner', id: 'sales_price',
accessorKey: 'price', accessorKey: 'sales_price',
header: 'Harga Mitra (Rp)', header: 'Harga Sales (Rp)',
cell: (props) => { cell: (props) => {
const value = props.getValue() as number; const value = props.getValue() as number;
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(totals.avgPricePartner)} {summary
? formatCurrency(summary.avg_sales_price)
: formatCurrency(totals.avgSalesPrice)}
</div> </div>
), ),
}, },
{ {
id: 'total_mitra', id: 'total_sales_price',
accessorKey: 'total_price', accessorKey: 'total_sales_price',
header: 'Total Mitra (Rp)', header: 'Total Sales (Rp)',
cell: (props) => { cell: (props) => {
const value = props.getValue() as number; const value = props.getValue() as number;
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(totals.totalPartner)} {summary
? formatCurrency(summary.total_sales_price)
: formatCurrency(totals.totalSalesPrice)}
</div> </div>
), ),
}, },
{ {
id: 'price_act', id: 'actual_price',
accessorKey: 'price', accessorKey: 'actual_price',
header: 'Harga Act (Rp)', header: 'Harga Act (Rp)',
cell: (props) => { cell: (props) => {
const value = props.getValue() as number; const value = props.getValue() as number;
return <div className='text-right'>{formatCurrency(value)}</div>; return <div className='text-right'>{formatCurrency(value)}</div>;
}, },
footer: () => (
<div className='text-right font-semibold text-gray-900'>
{summary
? formatCurrency(summary.avg_actual_price)
: formatCurrency(totals.avgActualPrice)}
</div>
),
}, },
{ {
id: 'total_act', id: 'total_actual_price',
accessorKey: 'total_price', accessorKey: 'total_actual_price',
header: 'Total Act (Rp)', header: 'Total Act (Rp)',
cell: (props) => { cell: (props) => {
const value = props.getValue() as number; const value = props.getValue() as number;
return <div className='text-right'>{formatCurrency(value)}</div>; return <div className='text-right'>{formatCurrency(value)}</div>;
}, },
footer: () => (
<div className='text-right font-semibold text-gray-900'>
{summary
? formatCurrency(summary.total_actual_price)
: formatCurrency(totals.totalActualPrice)}
</div>
),
}, },
{ {
id: 'kandang', id: 'kandang',
+12 -26
View File
@@ -23,33 +23,18 @@ export type BaseSales = {
qty: number; qty: number;
weight: number; weight: number;
avg_weight: number; avg_weight: number;
price: number; sales_price: number;
total_price: number; total_sales_price: number;
actual_price: number;
total_actual_price: number;
kandang: Kandang; kandang: Kandang;
payment_status: string; };
};
export type ClosingSalesSummary = {
export type BaseClosingSales = { total_sales_price: number;
project_type: string; avg_sales_price: number;
flock_id: number; total_actual_price: number;
period: number; avg_actual_price: number;
sales: BaseSales[];
};
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 = { export type BaseClosingSales = {
@@ -57,6 +42,7 @@ export type BaseClosingSales = {
flock_id: number; flock_id: number;
period: number; period: number;
sales: BaseSales[]; sales: BaseSales[];
summary: ClosingSalesSummary;
}; };
export type BaseClosing = { export type BaseClosing = {