gmao/tests/integration/test_cleaning_module.py

95 lines
5.3 KiB
Python
Raw Permalink Normal View History

from datetime import date, timedelta
from decimal import Decimal
from uuid import uuid4
import pytest
from app_new.extensions import db
from app_new.core.models import (
ProductGeneric, CommercialProduct, ProductPackaging, StockLocation,
Staff, ReusableContainer, CleaningForecastConfig, CollegeClosure, ClosureWorkDay,
)
from app_new.cleaning.services.stock import (
StockError, receive_stock, issue_stock, transfer_stock, dilute,
)
from app_new.cleaning.services.forecast import forecast_product
def setup_stock():
product = ProductGeneric(name=f"Nettoyant test {uuid4().hex[:8]}", reference_unit="L", product_type="liquide",
forecast_daily_quantity=Decimal("1.2"), stock_security=Decimal("2"))
ref = CommercialProduct(generic_product=product, commercial_name="Référence test")
packaging = ProductPackaging(generic_product=product, commercial_product=ref, name="Bidon 5 L",
purchase_unit="bidon", units_per_package=1,
reference_quantity_per_package=Decimal("5"))
suffix = uuid4().hex[:8]
loc_a = StockLocation(name=f"Stock test A {suffix}")
loc_b = StockLocation(name=f"Stock test B {suffix}")
db.session.add_all([product, ref, packaging, loc_a, loc_b])
db.session.flush()
return product, ref, packaging, loc_a, loc_b
def test_reception_conversion_and_fefo(app):
with app.app_context():
product, ref, packaging, loc_a, loc_b = setup_stock()
receive_stock(generic_product_id=product.id, commercial_product_id=ref.id, packaging_id=packaging.id,
packages=2, lot_number="ANCIEN", received_at=date.today(),
expiry_date=date.today() + timedelta(days=3), location_id=loc_a.id)
receive_stock(generic_product_id=product.id, commercial_product_id=ref.id, packaging_id=packaging.id,
packages=2, lot_number="RECENT", received_at=date.today(),
expiry_date=date.today() + timedelta(days=90), location_id=loc_a.id)
db.session.commit()
issue_stock(generic_product_id=product.id, quantity=Decimal("6"), source_location_id=loc_a.id)
db.session.commit()
lots = {lot.lot_number: sum((b.quantity for b in lot.balances), Decimal("0"))
for lot in product.lots}
assert lots["ANCIEN"] == Decimal("4.000000")
assert lots["RECENT"] == Decimal("10.000000")
def test_expired_lot_rejected_and_no_negative_stock(app):
with app.app_context():
product, ref, packaging, loc_a, _ = setup_stock()
receive_stock(generic_product_id=product.id, commercial_product_id=ref.id, packaging_id=packaging.id,
packages=1, lot_number="EXPIRE", received_at=date.today(),
expiry_date=date.today() - timedelta(days=1), location_id=loc_a.id)
db.session.commit()
with pytest.raises(StockError):
issue_stock(generic_product_id=product.id, quantity=1, source_location_id=loc_a.id)
with pytest.raises(StockError):
issue_stock(generic_product_id=product.id, quantity=100, source_location_id=loc_a.id)
db.session.rollback()
def test_transfer_and_dilution(app):
with app.app_context():
product, ref, packaging, loc_a, loc_b = setup_stock()
receive_stock(generic_product_id=product.id, commercial_product_id=ref.id, packaging_id=packaging.id,
packages=1, lot_number="TRANS", received_at=date.today(), location_id=loc_a.id)
container = ReusableContainer(identifier=f"FLACON-TEST-{uuid4().hex[:8]}", container_type="750 ml", volume_liters=Decimal("0.750"))
db.session.add(container); db.session.flush()
transfer_stock(generic_product_id=product.id, quantity=Decimal("1"), source_location_id=loc_a.id, destination_location_id=loc_b.id)
db.session.commit()
assert sum((b.quantity for b in product.lots[0].balances if b.location_id == loc_b.id), Decimal("0")) == Decimal("1.000000")
dilute(generic_product_id=product.id, total_liters=Decimal("0.750"), ratio_percent=Decimal("2"),
source_location_id=loc_b.id, container_id=container.id)
db.session.commit()
assert container.current_product_id == product.id
assert sum((b.quantity for b in product.lots[0].balances if b.location_id == loc_b.id), Decimal("0")) == Decimal("0.985000")
def test_forecast_distinguishes_school_days_permanence_and_closure(app):
with app.app_context():
product, ref, packaging, loc_a, _ = setup_stock()
start = date(2026, 9, 7)
closure = CollegeClosure(name="Vacances test", start_date=start + timedelta(days=5),
end_date=start + timedelta(days=9), closure_type="vacances_scolaires")
db.session.add(closure); db.session.flush()
db.session.add(ClosureWorkDay(closure_id=closure.id, work_date=start + timedelta(days=6),
start_time=__import__("datetime").time(8), end_time=__import__("datetime").time(12)))
db.session.commit()
result = forecast_product(product, start + timedelta(days=9), start_date=start)
assert result["school_days_consumption"] > 0
assert result["permanence_consumption"] == Decimal("0.480000")
assert result["closed_consumption"] == 0