Files
shanxia2.0/backend/tests/test_crm_tier_date_identity.py
T

134 lines
5.0 KiB
Python

"""Equivalent date formats preserve CRM tier identity; isolated SQLite only."""
from datetime import date, datetime
import pymysql
import pytest
from sqlalchemy import create_engine
from sqlalchemy.orm import Session
from app.crud.crm import contract_product as cp
from app.models.crm_models import CrmContractProduct, CrmContractProductPriceTier
from app.models.pl_models import PlPlan, PlPlanLoadProduct, PlPlanDeliveryProduct
from app.schemas.crm.contract import ContractUpdateBody
@pytest.fixture
def db(monkeypatch):
def forbidden(*args, **kwargs):
raise AssertionError('Business MySQL connections are forbidden')
monkeypatch.setattr(pymysql, 'connect', forbidden)
engine = create_engine('sqlite://')
for model in (CrmContractProduct, CrmContractProductPriceTier, PlPlan, PlPlanLoadProduct, PlPlanDeliveryProduct):
model.__table__.create(engine)
with Session(engine, autoflush=False) as session:
yield session
engine.dispose()
def seed(db):
parent = CrmContractProduct(id='line', tenant_no='t', contract_id='contract', product_id='sku',
product_name='Product', unit='吨', package_spec='桶', quantity=1, price=10,
create_time=datetime(2020, 1, 1), create_by='creator')
db.add(parent)
db.flush()
return parent
def tier(db, row_id='tier', **extra):
values = dict(id=row_id, tenant_no='t', contract_id='contract', contract_product_id='line',
start_date=date(2026, 1, 1), end_date=date(2026, 2, 1), price=10, remark='keep',
letter_oss_ids='["letter"]', create_time=datetime(2020, 1, 1), create_by='creator')
values.update(extra)
row = CrmContractProductPriceTier(**values)
db.add(row)
db.flush()
return row
def tier_body(**kwargs):
values = dict(startDate='2026-01-01', endDate='2026-02-01', price=20)
values.update(kwargs)
return values
def save(db, tiers):
request = ContractUpdateBody(id='contract', customerId='customer', contractType='sale',
products=[{'id': 'line', 'priceTiers': tiers}])
payload = request.model_dump(by_alias=True, exclude_unset=True)
cp.sync_contract_products(db, tenant_no='t', contract_id='contract', products=payload['products'], user_id='editor')
db.flush()
@pytest.mark.parametrize('start,end', [
('2026-01-01 00:00:00', '2026-02-01 17:05:09'),
(' 2026-01-01 ', ' 2026-02-01 '),
('2026-1-1', '2026-2-1'),
('2026-01-01', '2026-02-01'),
])
def test_missing_tier_id_uses_canonical_day_and_keeps_metadata(db, start, end):
parent = seed(db)
old = tier(db)
for price in (20, 30):
save(db, [tier_body(startDate=start, endDate=end, price=price)])
assert db.query(CrmContractProductPriceTier).count() == 1
assert old.del_flag == 0 and old.price == 30 and old.remark == 'keep'
assert old.letter_oss_ids == '["letter"]'
assert old.create_by == 'creator' and old.create_time == datetime(2020, 1, 1)
assert parent.id == 'line' and parent.del_flag == 0 and parent.unit == '吨'
@pytest.mark.parametrize('start', ['bad', '', None, '2026-02-30'])
def test_bad_anonymous_dates_rejected_before_any_changes(db, start):
seed(db)
old = tier(db)
with pytest.raises(ValueError):
save(db, [tier_body(id='tier', price=99), tier_body(startDate=start)])
assert not db.new and not db.dirty and old.price == 10
def test_canonical_duplicate_old_rows_are_ambiguous(db):
seed(db)
tier(db)
tier(db, 'duplicate')
with pytest.raises(ValueError):
save(db, [tier_body(startDate='2026-01-01 00:00:00')])
assert not db.new and not db.dirty
def test_two_anonymous_rows_cannot_claim_same_existing_interval(db):
seed(db)
tier(db)
with pytest.raises(ValueError):
save(db, [tier_body(), tier_body(startDate='2026-01-01 00:00:00')])
assert not db.new and not db.dirty
@pytest.mark.parametrize('scope', [{'tenant_no': 'other'}, {'contract_id': 'other'},
{'contract_product_id': 'other'}, {'del_flag': 1}])
def test_explicit_tier_id_must_be_active_and_in_scope(db, scope):
seed(db)
tier(db, **scope)
with pytest.raises(ValueError):
save(db, [tier_body(id='tier')])
assert not db.new and not db.dirty
def test_explicit_partial_tier_patch_and_collection_omission(db):
seed(db)
old = tier(db)
save(db, [{'id': 'tier', 'price': 25}])
cp.sync_contract_products(db, tenant_no='t', contract_id='contract', products=[{'id': 'line', 'quantity': 2}])
db.flush()
assert old.del_flag == 0 and old.price == 25 and old.start_date == date(2026, 1, 1)
save(db, [])
assert old.del_flag == 1
def test_genuinely_new_interval_is_added_and_removed_interval_soft_deleted(db):
seed(db)
old = tier(db)
save(db, [tier_body(startDate='2026-03-01 00:00:00', endDate='2026-04-01 00:00:00')])
active = db.query(CrmContractProductPriceTier).filter_by(del_flag=0).one()
assert old.del_flag == 1 and active.id != old.id
assert active.start_date == date(2026, 3, 1) and active.end_date == date(2026, 4, 1)