from __future__ import annotations import unittest 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 import app.models.miniapp # noqa: E402,F401 import app.models.operations # noqa: E402,F401 import app.models.org # noqa: E402,F401 import app.models.planning # noqa: E402,F401 import app.models.sales # noqa: E402,F401 from app.models.base import Base # noqa: E402 from app.models.master_data import Item, Warehouse # noqa: E402 from app.models.operations import StockLot # noqa: E402 from app.services.system_config import RAW_MATERIAL_LOT_PREFIX_CONFIG_CODE, upsert_system_config # noqa: E402 from app.services.operations import build_inventory_lot_no # noqa: E402 class InventoryLotNumberingTest(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() self.material = Item( id=1, item_code="110501000185", item_name="左右后纵梁后段毛坯", item_type="RAW_MATERIAL", unit_weight_kg=Decimal("1"), status="ACTIVE", ) self.other_material = Item( id=2, item_code="110501000186", item_name="其他原材料", item_type="RAW_MATERIAL", unit_weight_kg=Decimal("1"), status="ACTIVE", ) self.warehouse = Warehouse( id=1, warehouse_code="WH-RAW", warehouse_name="原材料库", warehouse_type="RAW", status="ACTIVE", ) self.db.add_all([self.material, self.other_material, self.warehouse]) self.db.commit() def tearDown(self) -> None: self.db.close() def test_inventory_lot_no_starts_from_global_prefix_sequence(self) -> None: self.assertEqual(build_inventory_lot_no(self.db, self.material.id), "YL0001") def test_inventory_lot_no_increments_from_existing_lot_for_different_material(self) -> None: self.db.add( StockLot( lot_no="YL0001", lot_role="INBOUND_RAW", item_id=self.other_material.id, warehouse_id=self.warehouse.id, source_doc_type="TEST", source_doc_id=1, inbound_qty=Decimal("0"), inbound_weight_kg=Decimal("100"), remaining_qty=Decimal("0"), remaining_weight_kg=Decimal("100"), locked_qty=Decimal("0"), locked_weight_kg=Decimal("0"), unit_cost=Decimal("5"), quality_status="PASS", status="AVAILABLE", ) ) self.db.commit() self.assertEqual(build_inventory_lot_no(self.db, self.material.id), "YL0002") def test_inventory_lot_no_uses_natural_width_after_four_digits(self) -> None: self.db.add( StockLot( lot_no="YL9999", lot_role="INBOUND_RAW", item_id=self.material.id, warehouse_id=self.warehouse.id, source_doc_type="TEST", source_doc_id=1, inbound_qty=Decimal("0"), inbound_weight_kg=Decimal("100"), remaining_qty=Decimal("0"), remaining_weight_kg=Decimal("100"), locked_qty=Decimal("0"), locked_weight_kg=Decimal("0"), unit_cost=Decimal("5"), quality_status="PASS", status="AVAILABLE", ) ) self.db.commit() self.assertEqual(build_inventory_lot_no(self.db, self.material.id), "YL10000") def test_inventory_lot_no_uses_configured_prefix(self) -> None: upsert_system_config( self.db, config_code=RAW_MATERIAL_LOT_PREFIX_CONFIG_CODE, config_name="原材料库存批次号前缀", config_value="rm", ) self.db.commit() self.assertEqual(build_inventory_lot_no(self.db, self.material.id), "RM0001")