200 lines
6.5 KiB
Python
200 lines
6.5 KiB
Python
"""Тесты 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()
|