adjust kpi card not showing after manual input before cutoff time

This commit is contained in:
Alberto-Audrix committed 2026-10-05 16:02:28 +07:00
1 parent 3f5db48b16
commit c963bb778c
2 files changed
+44 -10

No files matched your search

+22 -10
View File
@@ -15,44 +15,56 @@ from apps.operations.services.kpi_formulas import (
compute_fcr,
compute_stock_akhir,
)
from apps.operations.services.visibility import visible_through_date
def refresh_kpi_source_fields(cycle: Cycle, *, through: date | None = None) -> int:
"""
Upsert one KPI row per day that has cc + cw + fs + manual.
If `through` is specified, rollup calculations are bounded up to that date.
Otherwise, rollups are calculated for all complete days of the cycle (up to `cycle.end_date` if set).
Dashboard cutoff visibility is enforced by the KPI API queryset layer (`VisibilityFilteredMixin`).
Returns the number of KPI rows written.
"""
cutoff = through if through is not None else visible_through_date()
cutoff = through if through is not None else cycle.end_date
start = cycle.start_date
if cycle.end_date and cycle.end_date < cutoff:
if through is not None and cycle.end_date and cycle.end_date < cutoff:
cutoff = cycle.end_date
manual_filter: dict = {"cycle": cycle, "date__gte": start}
cc_filter: dict = {"cycle": cycle, "date__gte": start}
cw_filter: dict = {"cycle": cycle, "date__gte": start}
fs_filter: dict = {"cycle": cycle, "date__gte": start}
if cutoff is not None:
manual_filter["date__lte"] = cutoff
cc_filter["date__lte"] = cutoff
cw_filter["date__lte"] = cutoff
fs_filter["date__lte"] = cutoff
manuals = {
row.date: row
for row in ManualInput.objects.filter(cycle=cycle, date__gte=start, date__lte=cutoff).order_by(
"date", "pk"
)
for row in ManualInput.objects.filter(**manual_filter).order_by("date", "pk")
}
countings = {
row.date: row
for row in ChickenCounting.objects.filter(cycle=cycle, date__gte=start, date__lte=cutoff)
for row in ChickenCounting.objects.filter(**cc_filter)
}
weights = {
row.date: row
for row in ChickenWeight.objects.filter(cycle=cycle, date__gte=start, date__lte=cutoff)
for row in ChickenWeight.objects.filter(**cw_filter)
}
feeds = {
row.date: row
for row in FeedSacks.objects.filter(cycle=cycle, date__gte=start, date__lte=cutoff)
for row in FeedSacks.objects.filter(**fs_filter)
}
written = 0
harvest_age_sum = 0.0
for day in sorted(set(manuals) | set(countings) | set(weights) | set(feeds)):
if day < start or day > cutoff:
if day < start or (cutoff is not None and day > cutoff):
continue
manual = manuals.get(day)
cc = countings.get(day)
@@ -121,3 +121,25 @@ class KpiRollupServiceTests(TestCase):
refresh_kpi_source_fields(self.cycle, through=day)
self.assertTrue(KPI.objects.filter(cycle=self.cycle, date=day).exists())
self.assertEqual(KPI.objects.get(cycle=self.cycle, date=day).mortality_total, 21)
def test_manual_save_generates_kpi_without_explicit_through(self):
day = self.start
ChickenCounting.objects.create(cycle=self.cycle, date=day, total_count=1, mortality_count=0)
ChickenWeight.objects.create(
cycle=self.cycle,
date=day,
age=0,
doc_weight=40,
average_weight=35.0,
chicken_count=1,
uniformity=80,
average_daily_gain=0,
)
FeedSacks.objects.create(cycle=self.cycle, date=day, feed_use_today=15, feed_use_total=15)
ManualInput.objects.create(cycle=self.cycle, date=day, mortality_manual=21)
recompute_manual_totals(self.cycle)
written = refresh_kpi_source_fields(self.cycle)
self.assertEqual(written, 1)
self.assertTrue(KPI.objects.filter(cycle=self.cycle, date=day).exists())
self.assertEqual(KPI.objects.get(cycle=self.cycle, date=day).mortality_total, 21)