"""Тесты sklad_metrics (общие расчёты склада).""" from __future__ import annotations import os import tempfile import unittest import uuid from datetime import datetime from app import create_app, db from app.models import Component, ComponentStock, LoadingReport, LoadingReportComponent from app.services.sklad_metrics import ( consumed_by_component_id, effective_consumed_kg, list_stock_balance_items, month_consumption_by_component, ) from config import TestingConfig class SkladMetricsTestConfig(TestingConfig): _TMP_DIR = tempfile.mkdtemp(prefix="wesp-sklad-metrics-") SQLALCHEMY_DATABASE_URI = f"sqlite:///{os.path.join(_TMP_DIR, 'recipes_test.db')}" SQLALCHEMY_BINDS = {"reports": f"sqlite:///{os.path.join(_TMP_DIR, 'reports_test.db')}"} class SkladMetricsTests(unittest.TestCase): def setUp(self) -> None: self.app = create_app(SkladMetricsTestConfig) self.ctx = self.app.app_context() self.ctx.push() db.create_all() self._seed() def tearDown(self) -> None: db.session.remove() db.drop_all() self.ctx.pop() def _seed(self) -> None: comp = Component(name="Сено", type="roughage") db.session.add(comp) db.session.flush() db.session.add( ComponentStock( component_id=comp.id, component_name=comp.name, total_kg=500.0, stocktake_at=datetime.now(), updated_at=datetime.now(), ) ) report = LoadingReport( id=str(uuid.uuid4()), recipe_id=str(uuid.uuid4()), recipe_name="R1", start_time=datetime.now(), dispenser_type="dispenser", ) db.session.add(report) db.session.flush() db.session.add( LoadingReportComponent( report_id=report.id, component_id=comp.id, component_name=comp.name, target_weight=10.0, actual_weight=7.5, loading_order=1, ) ) db.session.commit() self.comp_id = comp.id def test_consumed_by_component_id(self) -> None: with self.app.app_context(): data = consumed_by_component_id( db, LoadingReport, LoadingReportComponent ) self.assertAlmostEqual(data.get(self.comp_id, 0), 7.5) def test_list_stock_balance_items(self) -> None: today = datetime.now().date() start = today.replace(day=1).isoformat() end = today.isoformat() with self.app.app_context(): items = list_stock_balance_items( db, LoadingReport, LoadingReportComponent, None, None, date_from=start, date_to=end, ) self.assertEqual(len(items), 1) self.assertEqual(items[0]["name"], "Сено") self.assertAlmostEqual(items[0]["consumed_total_kg"], 7.5) def test_effective_consumed_kg_zero_actual_uses_target(self) -> None: self.assertAlmostEqual(effective_consumed_kg(0, 0, 12.5), 12.5) self.assertAlmostEqual(effective_consumed_kg(0, -2, 12.5), 12.5) self.assertAlmostEqual(effective_consumed_kg(8, 1.5, 10), 9.5) self.assertAlmostEqual(effective_consumed_kg(8, -1, 10), 8.0) def test_consumed_zero_actual_with_target(self) -> None: report = LoadingReport( id=str(uuid.uuid4()), recipe_id=str(uuid.uuid4()), recipe_name="R2", start_time=datetime.now(), dispenser_type="dispenser", ) db.session.add(report) db.session.flush() db.session.add( LoadingReportComponent( report_id=report.id, component_id=self.comp_id, component_name="Сено", target_weight=15.0, actual_weight=0.0, loading_order=1, ) ) db.session.commit() with self.app.app_context(): data = consumed_by_component_id(db, LoadingReport, LoadingReportComponent) self.assertAlmostEqual(data.get(self.comp_id, 0), 22.5) def test_consumed_matches_by_name_when_id_differs(self) -> None: report = LoadingReport( id=str(uuid.uuid4()), recipe_id=str(uuid.uuid4()), recipe_name="R-name", start_time=datetime.now(), dispenser_type="dispenser", ) db.session.add(report) db.session.flush() db.session.add( LoadingReportComponent( report_id=report.id, component_id="other-id-123", component_name="Сено", target_weight=10.0, actual_weight=4.0, loading_order=1, ) ) db.session.commit() today = datetime.now().date() start = today.replace(day=1).isoformat() end = today.isoformat() with self.app.app_context(): items = list_stock_balance_items( db, LoadingReport, LoadingReportComponent, None, None, date_from=start, date_to=end, ) stock_item = next(i for i in items if i.get("has_stock")) self.assertAlmostEqual(stock_item["consumed_total_kg"], 11.5) def test_month_consumption_without_stock_row(self) -> None: now = datetime.now() report = LoadingReport( id=str(uuid.uuid4()), recipe_id=str(uuid.uuid4()), recipe_name="R3", start_time=now, dispenser_type="dispenser", ) db.session.add(report) db.session.flush() db.session.add( LoadingReportComponent( report_id=report.id, component_id=None, component_name="Силос", target_weight=20.0, actual_weight=0.0, loading_order=1, ) ) db.session.commit() with self.app.app_context(): agg = month_consumption_by_component( db, LoadingReport, LoadingReportComponent, now.year, now.month ) self.assertTrue(any("Силос" in (m.get("name") or "") for m in agg.values())) if __name__ == "__main__": unittest.main()