refactor(FE-356): Refactor weight-range grouping and format currency

This commit is contained in:
rstubryan
2025-12-18 11:12:30 +07:00
parent 4edd4f1285
commit 83fc92d48b
@@ -10,7 +10,7 @@ import {
pdf, pdf,
} from '@react-pdf/renderer'; } from '@react-pdf/renderer';
import { HppPerKandangReport } from '@/types/api/report/hpp-per-kandang'; import { HppPerKandangReport } from '@/types/api/report/hpp-per-kandang';
import { formatDate, formatNumber } from '@/lib/helper'; import { formatDate, formatNumber, formatCurrency } from '@/lib/helper';
Font.register({ Font.register({
family: 'Helvetica', family: 'Helvetica',
@@ -148,88 +148,72 @@ interface HppPerKandangExportParams {
}; };
} }
interface WeightRangeGroup { const rekapitulasiData = (data: HppPerKandangReport['rows']) => {
weight_min: number; const groups: { [key: string]: HppPerKandangReport['rows'] } = {};
weight_max: number;
items: HppPerKandangReport['rows'];
totals: {
total_remaining_chicken_birds: number;
total_remaining_chicken_weight_kg: number;
average_weight_kg: number;
total_hpp_rp: number;
total_remaining_value_rp: number;
all_feed_suppliers: string[];
all_doc_suppliers: string[];
average_doc_price_rp: number;
};
}
const groupDataByWeightRange = (
data: HppPerKandangReport['rows'],
summary: HppPerKandangReport['summary']
): WeightRangeGroup[] => {
const groups: {
[key: string]: WeightRangeGroup;
} = {};
data.forEach((item) => { data.forEach((item) => {
const key = `${item.weight_range.weight_min}-${item.weight_range.weight_max}`; const key = `${item.weight_range.weight_min}-${item.weight_range.weight_max}`;
if (!groups[key]) { if (!groups[key]) {
groups[key] = { groups[key] = [];
weight_min: item.weight_range.weight_min,
weight_max: item.weight_range.weight_max,
items: [],
totals: {
total_remaining_chicken_birds: 0,
total_remaining_chicken_weight_kg: 0,
average_weight_kg: 0,
total_hpp_rp: 0,
total_remaining_value_rp: 0,
all_feed_suppliers: [],
all_doc_suppliers: [],
average_doc_price_rp: 0,
},
};
}
groups[key].items.push(item);
groups[key].totals.total_remaining_chicken_birds +=
item.remaining_chicken_birds;
groups[key].totals.total_remaining_chicken_weight_kg +=
item.remaining_chicken_weight_kg;
groups[key].totals.total_hpp_rp += item.hpp_rp;
groups[key].totals.total_remaining_value_rp += item.remaining_value_rp;
item.feed_suppliers?.forEach((supplier) => {
const alias = supplier.alias || supplier.name;
if (!groups[key].totals.all_feed_suppliers.includes(alias)) {
groups[key].totals.all_feed_suppliers.push(alias);
} }
groups[key].push(item);
}); });
item.doc_suppliers?.forEach((supplier) => { return Object.entries(groups)
const alias = supplier.alias || supplier.name; .map(([key, items]) => ({
if (!groups[key].totals.all_doc_suppliers.includes(alias)) { weight_min: items[0].weight_range.weight_min,
groups[key].totals.all_doc_suppliers.push(alias); weight_max: items[0].weight_range.weight_max,
} items,
}); total_remaining_chicken_birds: items.reduce(
}); (sum, item) => sum + item.remaining_chicken_birds,
Object.values(groups).forEach((group) => {
group.totals.average_weight_kg =
group.totals.total_remaining_chicken_weight_kg /
group.totals.total_remaining_chicken_birds;
group.totals.average_doc_price_rp =
group.items.length > 0
? group.items.reduce(
(sum, item) => sum + item.average_doc_price_rp,
0 0
) / group.items.length ),
: 0; total_remaining_chicken_weight_kg: items.reduce(
}); (sum, item) => sum + item.remaining_chicken_weight_kg,
0
return Object.values(groups).sort((a, b) => a.weight_min - b.weight_min); ),
average_weight_kg:
items.reduce((sum, item) => sum + item.remaining_chicken_weight_kg, 0) /
items.reduce((sum, item) => sum + item.remaining_chicken_birds, 0),
total_hpp_rp: items.reduce((sum, item) => sum + item.hpp_rp, 0),
total_remaining_value_rp: items.reduce(
(sum, item) => sum + item.remaining_value_rp,
0
),
total_egg_production_pieces: items.reduce(
(sum, item) => sum + (item.egg_production_pieces || 0),
0
),
total_egg_production_kg: items.reduce(
(sum, item) => sum + (item.egg_production_kg || 0),
0
),
total_egg_value_rp: items.reduce(
(sum, item) => sum + (item.egg_value_rp || 0),
0
),
average_egg_hpp_rp_per_kg:
items.reduce((sum, item) => sum + (item.egg_hpp_rp_per_kg || 0), 0) /
items.length,
average_doc_price_rp:
items.reduce((sum, item) => sum + item.average_doc_price_rp, 0) /
items.length,
all_feed_suppliers: [
...new Set(
items.flatMap(
(item) => item.feed_suppliers?.map((s) => s.alias || s.name) || []
)
),
],
all_doc_suppliers: [
...new Set(
items.flatMap(
(item) => item.doc_suppliers?.map((s) => s.alias || s.name) || []
)
),
],
}))
.sort((a, b) => a.weight_min - b.weight_min);
}; };
const getParameterText = (params: HppPerKandangExportParams['params']) => { const getParameterText = (params: HppPerKandangExportParams['params']) => {
@@ -246,27 +230,11 @@ const getParameterText = (params: HppPerKandangExportParams['params']) => {
return paramsText; return paramsText;
}; };
const getTotalsForExport = (data: HppPerKandangReport['rows']) => {
const totalHppRp = data.reduce((sum, item) => sum + (item.hpp_rp || 0), 0);
const avgDocPrice =
data.length > 0
? data.reduce((sum, item) => sum + (item.average_doc_price_rp || 0), 0) /
data.length
: 0;
return {
total_hpp_rp: totalHppRp,
total_average_doc_price_rp: avgDocPrice,
};
};
const createPDFDocument = ( const createPDFDocument = (
data: HppPerKandangExportParams['data'], data: HppPerKandangExportParams['data'],
params: HppPerKandangExportParams['params'] params: HppPerKandangExportParams['params']
) => { ) => {
const summary = data.summary; const rekapitulasiByWeightRange = rekapitulasiData(data.rows);
const exportTotals = getTotalsForExport(data.rows);
const groupedByWeightRange = groupDataByWeightRange(data.rows, summary);
return ( return (
<Document> <Document>
@@ -334,12 +302,12 @@ const createPDFDocument = (
</View> </View>
{/* Table Body - Rekapitulasi */} {/* Table Body - Rekapitulasi */}
{groupedByWeightRange.map((group, index) => ( {rekapitulasiByWeightRange.map((group, index) => (
<View <View
key={index} key={index}
style={[ style={[
pdfStyles.tableRow, pdfStyles.tableRow,
index < groupedByWeightRange.length - 1 index < rekapitulasiByWeightRange.length - 1
? pdfStyles.tableBorderBottom ? pdfStyles.tableBorderBottom
: {}, : {},
]} ]}
@@ -352,82 +320,50 @@ const createPDFDocument = (
</View> </View>
<View style={[pdfStyles.tableCellRight, { flex: 1 }]}> <View style={[pdfStyles.tableCellRight, { flex: 1 }]}>
<Text> <Text>
{formatNumber(group.totals.total_remaining_chicken_birds)} {formatNumber(group.total_remaining_chicken_birds)}
</Text> </Text>
</View> </View>
<View style={[pdfStyles.tableCellRight, { flex: 1 }]}> <View style={[pdfStyles.tableCellRight, { flex: 1 }]}>
<Text> <Text>
{formatNumber( {formatNumber(group.total_remaining_chicken_weight_kg)}
group.totals.total_remaining_chicken_weight_kg
)}
</Text> </Text>
</View> </View>
<View style={[pdfStyles.tableCellRight, { flex: 1.2 }]}> <View style={[pdfStyles.tableCellRight, { flex: 1.2 }]}>
<Text>{formatNumber(group.totals.average_weight_kg)}</Text> <Text>{formatNumber(group.average_weight_kg)}</Text>
</View> </View>
<View style={[pdfStyles.tableCellRight, { flex: 1 }]}> <View style={[pdfStyles.tableCellRight, { flex: 1 }]}>
<Text> <Text>{formatNumber(group.total_egg_production_pieces)}</Text>
{formatNumber(
group.items.reduce(
(sum, item) => sum + (item.egg_production_pieces || 0),
0
)
)}
</Text>
</View> </View>
<View style={[pdfStyles.tableCellRight, { flex: 1 }]}> <View style={[pdfStyles.tableCellRight, { flex: 1 }]}>
<Text> <Text>{formatNumber(group.total_egg_production_kg)}</Text>
{formatNumber(
group.items.reduce(
(sum, item) => sum + (item.egg_production_kg || 0),
0
)
)}
</Text>
</View> </View>
<View style={[pdfStyles.tableCell, { flex: 1.5 }]}> <View style={[pdfStyles.tableCell, { flex: 1.5 }]}>
<Text>{group.totals.all_feed_suppliers.join(' | ')}</Text> <Text>{group.all_feed_suppliers.join(' | ')}</Text>
</View> </View>
<View style={[pdfStyles.tableCell, { flex: 1.2 }]}> <View style={[pdfStyles.tableCell, { flex: 1.2 }]}>
<Text>{group.totals.all_doc_suppliers.join(' | ')}</Text> <Text>{group.all_doc_suppliers.join(' | ')}</Text>
</View> </View>
<View style={[pdfStyles.tableCellRight, { flex: 1.2 }]}> <View style={[pdfStyles.tableCellRight, { flex: 1.2 }]}>
<Text>{formatNumber(group.totals.average_doc_price_rp)}</Text> <Text>{formatCurrency(group.average_doc_price_rp)}</Text>
</View> </View>
<View style={[pdfStyles.tableCellRight, { flex: 1.2 }]}> <View style={[pdfStyles.tableCellRight, { flex: 1.2 }]}>
<Text> <Text>{formatCurrency(group.total_egg_value_rp)}</Text>
{formatNumber(
group.items.reduce(
(sum, item) => sum + (item.egg_value_rp || 0),
0
)
)}
</Text>
</View> </View>
<View style={[pdfStyles.tableCellRight, { flex: 1 }]}> <View style={[pdfStyles.tableCellRight, { flex: 1 }]}>
<Text> <Text>
{formatNumber( {formatCurrency(
group.totals.total_remaining_chicken_birds > 0 group.total_remaining_chicken_birds > 0
? group.totals.total_hpp_rp / ? group.total_hpp_rp /
group.totals.total_remaining_chicken_birds group.total_remaining_chicken_birds
: 0 : 0
)} )}
</Text> </Text>
</View> </View>
<View style={[pdfStyles.tableCellRight, { flex: 1.2 }]}> <View style={[pdfStyles.tableCellRight, { flex: 1.2 }]}>
<Text> <Text>{formatCurrency(group.average_egg_hpp_rp_per_kg)}</Text>
{formatNumber(
group.items.reduce(
(sum, item) => sum + (item.egg_hpp_rp_per_kg || 0),
0
) / group.items.length || 0
)}
</Text>
</View> </View>
<View style={[pdfStyles.tableCellRight, { flex: 1.2 }]}> <View style={[pdfStyles.tableCellRight, { flex: 1.2 }]}>
<Text> <Text>{formatCurrency(group.total_remaining_value_rp)}</Text>
{formatNumber(group.totals.total_remaining_value_rp)}
</Text>
</View> </View>
</View> </View>
))} ))}
@@ -541,19 +477,19 @@ const createPDFDocument = (
</Text> </Text>
</View> </View>
<View style={[pdfStyles.tableCellRight, { flex: 1.2 }]}> <View style={[pdfStyles.tableCellRight, { flex: 1.2 }]}>
<Text>{formatNumber(item.average_doc_price_rp)}</Text> <Text>{formatCurrency(item.average_doc_price_rp)}</Text>
</View> </View>
<View style={[pdfStyles.tableCellRight, { flex: 1.2 }]}> <View style={[pdfStyles.tableCellRight, { flex: 1.2 }]}>
<Text>{formatNumber(item.egg_value_rp)}</Text> <Text>{formatCurrency(item.egg_value_rp)}</Text>
</View> </View>
<View style={[pdfStyles.tableCellRight, { flex: 0.8 }]}> <View style={[pdfStyles.tableCellRight, { flex: 0.8 }]}>
<Text>{formatNumber(item.hpp_rp)}</Text> <Text>{formatCurrency(item.hpp_rp)}</Text>
</View> </View>
<View style={[pdfStyles.tableCellRight, { flex: 1 }]}> <View style={[pdfStyles.tableCellRight, { flex: 1 }]}>
<Text>{formatNumber(item.egg_hpp_rp_per_kg)}</Text> <Text>{formatCurrency(item.egg_hpp_rp_per_kg)}</Text>
</View> </View>
<View style={[pdfStyles.tableCellRight, { flex: 1.2 }]}> <View style={[pdfStyles.tableCellRight, { flex: 1.2 }]}>
<Text>{formatNumber(item.remaining_value_rp)}</Text> <Text>{formatCurrency(item.remaining_value_rp)}</Text>
</View> </View>
</View> </View>
))} ))}