补齐证书齐备、证件折算、移动用证和合同绑定,并带上报销批量发放、付款备注以及个人开发流程升级,便于继续讨论已安排计划的调整规则。 Co-authored-by: Cursor <cursoragent@cursor.com>
78 lines
4.4 KiB
Python
78 lines
4.4 KiB
Python
"""计划配置的指纹与逐证人工条件,不涉及业务数据库。"""
|
|
import pytest
|
|
from fastapi import HTTPException
|
|
from types import SimpleNamespace
|
|
|
|
|
|
def test_condition_confirmation_is_per_certificate_and_current_revision():
|
|
from app.services.pl_quota_planning import validate_conditions
|
|
from app.schemas.pl.quota_planning import PlanQuotaConfiguration
|
|
fingerprint='a'*64
|
|
cert=SimpleNamespace(revision=2)
|
|
config=PlanQuotaConfiguration(purchaseCertIds=['g'],transportCertIds=['y'])
|
|
certs={('purchase','g'):cert,('transport','y'):cert}
|
|
with pytest.raises(HTTPException): validate_conditions(config,certs,fingerprint)
|
|
conditions=[{'certType':kind,'certId':cid,'certRevision':2,'sourceFingerprint':fingerprint,
|
|
'partiesConfirmed':True,'routeConfirmed':kind=='transport','note':'核对实际参与方与证面范围'} for kind,cid in certs]
|
|
config=PlanQuotaConfiguration(**{**config.model_dump(by_alias=True),'conditions':conditions})
|
|
validate_conditions(config,certs,fingerprint)
|
|
with pytest.raises(HTTPException): validate_conditions(config,certs,'b'*64)
|
|
cert.revision=3
|
|
with pytest.raises(HTTPException): validate_conditions(config,certs,fingerprint)
|
|
|
|
|
|
def test_transport_route_confirmation_cannot_be_replaced_by_parties_confirmation():
|
|
from app.services.pl_quota_planning import validate_conditions
|
|
from app.schemas.pl.quota_planning import PlanQuotaConfiguration
|
|
config=PlanQuotaConfiguration(transportCertIds=['y'],conditions=[{
|
|
'certType':'transport','certId':'y','certRevision':1,'sourceFingerprint':'a'*64,
|
|
'partiesConfirmed':True,'routeConfirmed':False,'note':'只确认参与方'}])
|
|
with pytest.raises(HTTPException) as error:
|
|
validate_conditions(config,{('transport','y'):SimpleNamespace(revision=1)},'a'*64)
|
|
assert error.value.status_code==409
|
|
|
|
|
|
def test_insufficient_cert_balance_blocks_arrange_until_quantity_is_adjusted():
|
|
from app.services.pl_quota_diagnostics import insufficient_quantity_message
|
|
message = insufficient_quantity_message([
|
|
{'certType': 'purchase', 'reason': '余额不足,请补充该证种额度',
|
|
'unit': 'dun', 'demand': '30', 'availableUpperBound': '10', 'gapLowerBound': '20'},
|
|
{'certType': 'transport', 'reason': '余额不足,请补充该证种额度',
|
|
'unit': 'dun', 'demand': '30', 'availableUpperBound': '30', 'gapLowerBound': '0'},
|
|
])
|
|
assert message == '购买证剩余10.000吨,本次需要30.000吨。证件余量不足,不能安排运输,请先调整到有余量后再安排'
|
|
assert insufficient_quantity_message([
|
|
{'certType': 'purchase', 'reason': '总余额仅为上界;请核对每个合同产品范围、运输引用关系及人工分配约束',
|
|
'unit': '吨', 'demand': '1', 'availableUpperBound': '1', 'gapLowerBound': '0'},
|
|
]) == ''
|
|
|
|
|
|
def test_arrange_uses_active_contract_cert_bindings_and_names_the_gap():
|
|
from app.services.pl_quota_planning import classify_contract_cert_bindings
|
|
source = SimpleNamespace(contract_id='c1', contract_product_id='line-1')
|
|
purchase = {
|
|
'certType': 'purchase', 'certId': 'buy-1', 'linkStatus': 'active',
|
|
'products': [{'contractProductId': 'line-1', 'scopeStatus': 'active'}],
|
|
}
|
|
transport = {
|
|
'certType': 'transport', 'certId': 'move-1', 'linkStatus': 'active',
|
|
'products': [{'contractProductId': 'line-1', 'scopeStatus': 'active'}],
|
|
}
|
|
stopped = {**transport, 'certId': 'move-old', 'linkStatus': 'inactive'}
|
|
labels = {'c1': 'HT-1'}
|
|
with pytest.raises(HTTPException) as error:
|
|
classify_contract_cert_bindings([source], {'c1': [purchase, stopped]}, labels)
|
|
assert error.value.status_code == 409
|
|
assert error.value.detail == '合同HT-1尚未绑定购买证和运输证,请先到合同中绑定后再安排运输'
|
|
assert classify_contract_cert_bindings([source], {'c1': [purchase, transport, stopped]}, labels) == (
|
|
['buy-1'], ['move-1'],
|
|
)
|
|
|
|
|
|
def test_supplement_is_explicit_and_archive_public_schema_preserves_intent():
|
|
from app.schemas.pl.plan import PlanArchiveBody
|
|
from app.schemas.pl.quota_planning import PlanQuotaOperation
|
|
assert PlanArchiveBody().quota_allow_supplement is False
|
|
assert PlanArchiveBody(quotaAllowSupplement=True).quota_allow_supplement is True
|
|
assert PlanQuotaOperation(operationId='op', allowSupplement=True).allow_supplement is True
|