迁移合同费用通用附件
1、删除旧费用附件 API、CRUD、模型和专用前端面板,统一改用通用附件能力。 2、调整合同费用接口为 study_id 语义,并补充合同、凭证、发票等附件类型的权限映射。 3、将合同费用新建和编辑改为抽屉流程,详情页与列表页复用同一编辑组件。 4、补充数据库迁移、附件列表、合同费用抽屉和权限场景测试,覆盖旧权限移除与新流程。
This commit is contained in:
@@ -23,6 +23,16 @@ class _FaqItem:
|
||||
self.study_id = study_id
|
||||
|
||||
|
||||
class _ContractFee:
|
||||
def __init__(self, study_id: uuid.UUID | None):
|
||||
self.study_id = study_id
|
||||
|
||||
|
||||
class _ContractPayment:
|
||||
def __init__(self, contract_fee_id: uuid.UUID):
|
||||
self.contract_fee_id = contract_fee_id
|
||||
|
||||
|
||||
def _make_request(*, method: str = "POST", path: str = "/", query_string: bytes = b"", json_body: bytes = b"", path_params=None):
|
||||
async def receive():
|
||||
return {"type": "http.request", "body": json_body, "more_body": False}
|
||||
@@ -80,6 +90,64 @@ async def test_require_study_not_locked_resolves_study_id_from_category_path(mon
|
||||
assert study.id == study_id
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_require_study_not_locked_resolves_study_id_from_contract_path(monkeypatch):
|
||||
study_id = uuid.uuid4()
|
||||
contract_id = uuid.uuid4()
|
||||
|
||||
async def fake_get_contract(_db, requested_contract_id):
|
||||
assert requested_contract_id == contract_id
|
||||
return _ContractFee(study_id)
|
||||
|
||||
async def fake_get_study(_db, requested_study_id):
|
||||
return _Study(requested_study_id, is_locked=False)
|
||||
|
||||
monkeypatch.setattr("app.crud.contract_fee.get_contract_fee", fake_get_contract)
|
||||
monkeypatch.setattr("app.crud.study.get", fake_get_study)
|
||||
|
||||
dependency = deps.require_study_not_locked()
|
||||
request = _make_request(
|
||||
path=f"/api/v1/fees/contracts/{contract_id}",
|
||||
path_params={"contract_id": str(contract_id)},
|
||||
)
|
||||
|
||||
study = await dependency(request=request, db=object())
|
||||
|
||||
assert study.id == study_id
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_require_study_not_locked_resolves_study_id_from_contract_payment_path(monkeypatch):
|
||||
study_id = uuid.uuid4()
|
||||
contract_id = uuid.uuid4()
|
||||
payment_id = uuid.uuid4()
|
||||
|
||||
async def fake_get_payment(_db, requested_payment_id):
|
||||
assert requested_payment_id == payment_id
|
||||
return _ContractPayment(contract_id)
|
||||
|
||||
async def fake_get_contract(_db, requested_contract_id):
|
||||
assert requested_contract_id == contract_id
|
||||
return _ContractFee(study_id)
|
||||
|
||||
async def fake_get_study(_db, requested_study_id):
|
||||
return _Study(requested_study_id, is_locked=False)
|
||||
|
||||
monkeypatch.setattr("app.crud.contract_fee_payment.get_payment", fake_get_payment)
|
||||
monkeypatch.setattr("app.crud.contract_fee.get_contract_fee", fake_get_contract)
|
||||
monkeypatch.setattr("app.crud.study.get", fake_get_study)
|
||||
|
||||
dependency = deps.require_study_not_locked()
|
||||
request = _make_request(
|
||||
path=f"/api/v1/fees/payments/{payment_id}",
|
||||
path_params={"payment_id": str(payment_id)},
|
||||
)
|
||||
|
||||
study = await dependency(request=request, db=object())
|
||||
|
||||
assert study.id == study_id
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_require_study_not_locked_rejects_locked_study_resolved_from_item_path(monkeypatch):
|
||||
study_id = uuid.uuid4()
|
||||
|
||||
Reference in New Issue
Block a user