@@ -0,0 +1,199 @@
|
||||
"""Тесты 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()
|
||||
Reference in New Issue
Block a user