"""单据编号生成器的跨位宽正确性(no_generator)。 守:当日序号超过 4 位宽度(9999→10000)后,VARCHAR 字典序排序会失效 ("-10000" < "-9999",'1'<'9')——MAX 检索若按纯字典序,seq 会恒重算出 同一号码,每笔建单都撞唯一键。修复口径:长度优先、同级按字典序 (同前缀下长度大即数值大;同长时字典序与数值序一致)。 """ import pytest from sqlalchemy import select from app.core.constants import NO_PREFIX_BUSINESS from app.core.db import get_session_factory from app.models import Business, Customer from app.services.no_generator import next_no, today_prefix pytestmark = pytest.mark.integration def _insert_business_nos(nos: list[str]) -> None: """直插同日前缀的业务编号行(仅编号用于 MAX 检索;其余列取默认/可空)。 直插绕过服务层(服务层走完整创建链太重),业务行为挂在 t_ 客户名下, 由 integration/conftest 兜底清理(YW% 单据 + t\\_% 客户)。 """ factory = get_session_factory() with factory() as session: customer = Customer( company_name="t_no_generator_client", owner_name="t_no_generator_owner", customer_type="enterprise", ) session.add(customer) session.flush() session.add_all(Business(business_no=no, customer_id=customer.id) for no in nos) session.commit() class TestMaxCrossWidth: """MAX 检索在同日前缀下跨位宽取最大序号。""" def test_序号破四位宽后仍按数值取最大(self) -> None: """4 位与 5 位并存:"-9999" 字典序大于 "-10000",纯字典序会取错。""" prefix = today_prefix(NO_PREFIX_BUSINESS) _insert_business_nos([f"{prefix}9999", f"{prefix}10000"]) factory = get_session_factory() with factory() as session: no = next_no( session, model=Business, column_name="business_no", prefix=NO_PREFIX_BUSINESS ) assert no == f"{prefix}10001" def test_同位宽内字典序与数值序一致仍取最大(self) -> None: """同宽多行(修复的排序不破坏原行为):是长度优先而非只按长度取首行。""" prefix = today_prefix(NO_PREFIX_BUSINESS) _insert_business_nos([f"{prefix}0099", f"{prefix}8499", f"{prefix}8500"]) factory = get_session_factory() with factory() as session: no = next_no( session, model=Business, column_name="business_no", prefix=NO_PREFIX_BUSINESS ) assert no == f"{prefix}8501" def test_无当日编号时从1起编(self) -> None: """当日无同前缀行(昨日编号不被 like 命中):从 0001 起,不受历史天数影响。 当日若已有其他用例留下的同前缀编号(用例间无隔离库),跳过—— 空表场景由每次 `down -v` 重建后的首个用例覆盖。 """ prefix = today_prefix(NO_PREFIX_BUSINESS) factory = get_session_factory() with factory() as session: existing = session.execute( select(Business.id).where(Business.business_no.like(f"{prefix}%")).limit(1) ).scalar() if existing is not None: pytest.skip(f"当日已有同前缀编号 {existing},空表场景跳过") with factory() as session: no = next_no( session, model=Business, column_name="business_no", prefix=NO_PREFIX_BUSINESS ) assert no == f"{prefix}0001"