83 lines
3.6 KiB
Python
83 lines
3.6 KiB
Python
|
|
"""单据编号生成器的跨位宽正确性(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"
|