from __future__ import annotations import unittest from datetime import UTC, datetime from decimal import Decimal from sqlalchemy import BigInteger, create_engine from sqlalchemy.ext.compiler import compiles from sqlalchemy.orm import Session, sessionmaker @compiles(BigInteger, "sqlite") def _compile_bigint_for_sqlite(type_, compiler, **kw) -> str: _ = type_, compiler, kw return "INTEGER" import app.models.master_data # noqa: E402,F401 from app.api.routes import master_data # noqa: E402 from app.models.base import Base # noqa: E402 from app.models.master_data import Item # noqa: E402 from app.schemas.database import MaterialCreate # noqa: E402 class MaterialCodeGenerationTest(unittest.TestCase): def setUp(self) -> None: engine = create_engine("sqlite+pysqlite:///:memory:", future=True) Base.metadata.create_all(engine) self.SessionLocal = sessionmaker(bind=engine, autoflush=False, autocommit=False, future=True) self.db: Session = self.SessionLocal() def tearDown(self) -> None: self.db.close() def _payload(self) -> MaterialCreate: return MaterialCreate( item_name="冷轧钢板", specification="1.0*1000", material_grade="SPCC", unit_weight_kg=1, safety_stock_weight_kg=0, scrap_sale_price=0, default_supplier_id=None, purchase_calc_mode="BY_WEIGHT", min_purchase_qty=0, purchase_multiple_qty=0, purchase_multiple_weight_kg=0, lead_time_days=0, quality_rule=None, status="ACTIVE", ) def _add_item(self, code: str, item_type: str = "RAW_MATERIAL") -> None: now = datetime.now(UTC) self.db.add( Item( item_code=code, item_name=f"测试物料{code}", item_type=item_type, specification="", material_grade="", unit_weight_kg=Decimal("0"), safety_stock_weight_kg=Decimal("0"), scrap_sale_price=Decimal("0"), status="ACTIVE", created_at=now, updated_at=now, ) ) self.db.commit() def test_material_code_starts_from_named_five_digit_sequence(self) -> None: self.assertEqual(master_data._build_material_code(self.db, self._payload()), "原材料00001") def test_material_code_increments_existing_named_material_codes(self) -> None: self._add_item("原材料00001") self._add_item("RM-SPCC-001") self.assertEqual(master_data._build_material_code(self.db, self._payload()), "原材料00002") def test_material_code_ignores_legacy_numeric_codes(self) -> None: self._add_item("00001") self._add_item("110801001050") self._add_item("110501000185") self.assertEqual(master_data._build_material_code(self.db, self._payload()), "原材料00001") def test_material_code_skips_codes_used_by_other_item_types(self) -> None: self._add_item("原材料00001") self._add_item("原材料00002", item_type="FINISHED_GOOD") self.assertEqual(master_data._build_material_code(self.db, self._payload()), "原材料00003") if __name__ == "__main__": unittest.main()