feat(backend): add draft save helpers for plan follow status drafting
Introduce min-field validation and follow_status resolution for saveMode draft/formal. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
1 parent
16c657b75e
commit
f28e82c1b3
2 files changed
+89
No files matched your search
@@ -1263,6 +1263,37 @@ def load_plan_contracts_grouped(
|
||||
return {pid: (lists[0], lists[1]) for pid, lists in buckets.items()}
|
||||
|
||||
|
||||
DRAFTABLE_FOLLOW_STATUSES = frozenset({"drafting", "pending_arrange"})
|
||||
|
||||
|
||||
def validate_draft_min_fields(*, belongs_company: Optional[str], create_date) -> None:
|
||||
if not (belongs_company or "").strip():
|
||||
raise HTTPException(status_code=422, detail="请选择归属公司")
|
||||
# create_date 允许为空,由 create/update 用 _resolve_create_date 兜底当天
|
||||
|
||||
|
||||
def resolve_follow_status_on_save(
|
||||
*,
|
||||
save_mode: str,
|
||||
current: Optional[str],
|
||||
is_create: bool,
|
||||
) -> str:
|
||||
mode = (save_mode or "formal").strip() or "formal"
|
||||
if mode == "draft":
|
||||
return "drafting"
|
||||
if is_create:
|
||||
return "pending_arrange"
|
||||
if current == "drafting":
|
||||
return "pending_arrange"
|
||||
return (current or "pending_arrange").strip() or "pending_arrange"
|
||||
|
||||
|
||||
def assert_can_save_as_draft(current_status: Optional[str]) -> None:
|
||||
status = (current_status or "").strip()
|
||||
if status not in DRAFTABLE_FOLLOW_STATUSES:
|
||||
raise HTTPException(status_code=400, detail="当前状态不可暂存")
|
||||
|
||||
|
||||
def validate_create_full(payload: PlanCreateFull) -> None:
|
||||
if not (payload.supplier_id or "").strip() or not (payload.supplier_name or "").strip():
|
||||
raise HTTPException(status_code=422, detail="请选择供方客户")
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
import pytest
|
||||
from fastapi import HTTPException
|
||||
|
||||
from app.services.pl_plan_service import (
|
||||
DRAFTABLE_FOLLOW_STATUSES,
|
||||
assert_can_save_as_draft,
|
||||
resolve_follow_status_on_save,
|
||||
validate_draft_min_fields,
|
||||
)
|
||||
|
||||
|
||||
def test_validate_draft_min_fields_requires_belongs_company():
|
||||
with pytest.raises(HTTPException) as exc:
|
||||
validate_draft_min_fields(belongs_company="", create_date=None)
|
||||
assert exc.value.status_code == 422
|
||||
assert "归属公司" in str(exc.value.detail)
|
||||
|
||||
|
||||
def test_validate_draft_min_fields_allows_missing_create_date():
|
||||
validate_draft_min_fields(belongs_company="shanxia", create_date=None)
|
||||
|
||||
|
||||
def test_resolve_follow_status_draft_always_drafting():
|
||||
assert (
|
||||
resolve_follow_status_on_save(
|
||||
save_mode="draft", current="pending_arrange", is_create=False
|
||||
)
|
||||
== "drafting"
|
||||
)
|
||||
assert resolve_follow_status_on_save(save_mode="draft", current=None, is_create=True) == "drafting"
|
||||
|
||||
|
||||
def test_resolve_follow_status_formal_promotes_drafting():
|
||||
assert (
|
||||
resolve_follow_status_on_save(save_mode="formal", current=None, is_create=True)
|
||||
== "pending_arrange"
|
||||
)
|
||||
assert (
|
||||
resolve_follow_status_on_save(save_mode="formal", current="drafting", is_create=False)
|
||||
== "pending_arrange"
|
||||
)
|
||||
assert (
|
||||
resolve_follow_status_on_save(save_mode="formal", current="arranged", is_create=False)
|
||||
== "arranged"
|
||||
)
|
||||
assert (
|
||||
resolve_follow_status_on_save(save_mode="formal", current="wait_notice", is_create=False)
|
||||
== "wait_notice"
|
||||
)
|
||||
|
||||
|
||||
def test_assert_can_save_as_draft_rejects_arranged():
|
||||
assert "drafting" in DRAFTABLE_FOLLOW_STATUSES
|
||||
assert "pending_arrange" in DRAFTABLE_FOLLOW_STATUSES
|
||||
with pytest.raises(HTTPException) as exc:
|
||||
assert_can_save_as_draft("arranged")
|
||||
assert exc.value.status_code == 400
|
||||
assert "不可暂存" in str(exc.value.detail)
|
||||
Reference in new issue
Block a user