Commit c738186f authored by wushanjing's avatar wushanjing

更新订单状态流转E2E链路

parent 98fbb8ed
...@@ -197,6 +197,10 @@ class WanyoujiMemberPage(TenantAuthPage): ...@@ -197,6 +197,10 @@ class WanyoujiMemberPage(TenantAuthPage):
self.page = dual_browser_manager().page_for_account(member.phone, seed_page=self.page) self.page = dual_browser_manager().page_for_account(member.phone, seed_page=self.page)
if self.bug_reporter: if self.bug_reporter:
self.bug_reporter.bind_page(self.page) self.bug_reporter.bind_page(self.page)
if self.can_reuse_member_login(member):
if self.bug_reporter:
self.bug_reporter.clear()
return
assert self.code_provider is not None, "验证码登录校验需要配置 VerificationCodeProvider" assert self.code_provider is not None, "验证码登录校验需要配置 VerificationCodeProvider"
self.clear_desktop_login_state() self.clear_desktop_login_state()
self.goto(self.tenant_home_url.replace("/home", "/login")) self.goto(self.tenant_home_url.replace("/home", "/login"))
...@@ -219,12 +223,46 @@ class WanyoujiMemberPage(TenantAuthPage): ...@@ -219,12 +223,46 @@ class WanyoujiMemberPage(TenantAuthPage):
self.select_sub_account(member) self.select_sub_account(member)
self.verify_sub_account_entered(member) self.verify_sub_account_entered(member)
self.verify_wanyouji_role_menu_permissions(member) self.verify_wanyouji_role_menu_permissions(member)
setattr(self.page, "_e2e_current_member_account", member.phone)
setattr(self.page, "_e2e_current_player_account", member.phone) setattr(self.page, "_e2e_current_player_account", member.phone)
setattr(self.page, "_e2e_current_main_account", "") setattr(self.page, "_e2e_current_main_account", "")
if dual_browser_enabled(): if dual_browser_enabled():
dual_browser_manager().set_active_page(self.page) dual_browser_manager().set_active_page(self.page)
assert_account_page(self.page, member.phone) assert_account_page(self.page, member.phone)
def can_reuse_member_login(self, member: WanyoujiMemberData) -> bool:
reuse_flag = os.getenv("E2E_REUSE_MEMBER_LOGIN", "1").strip().lower()
if reuse_flag in {"0", "false", "no", "off"}:
return False
player_reuse_flag = os.getenv("E2E_REUSE_PLAYER_LOGIN", "1").strip().lower()
if member.role == "打手" and player_reuse_flag in {"0", "false", "no", "off"}:
return False
current_member = getattr(self.page, "_e2e_current_member_account", "")
current_player = getattr(self.page, "_e2e_current_player_account", "")
if member.phone not in {current_member, current_player}:
return False
try:
self.goto(WANYOUJI_CONSOLE_URL)
self.page.wait_for_load_state("domcontentloaded")
self.page.wait_for_timeout(1500)
text = self.page.locator("body").inner_text(timeout=5000)
if "登录" in text and ("验证码登录" in text or "账号登录" in text or "登录/注册" in text):
return False
if "选择账号" in text and "进入后台" in text:
self.select_sub_account(member)
self.goto(WANYOUJI_CONSOLE_URL)
self.page.wait_for_load_state("domcontentloaded")
self.page.wait_for_timeout(1500)
text = self.page.locator("body").inner_text(timeout=5000)
role_markers = [member.name, member.role, "丸友集"]
if member.role == "打手":
role_markers.extend(["打手应用", "我的订单", "我的收入"])
elif member.role == "客服":
role_markers.extend(["收单应用", "客户自助填单", "客服"])
return any(marker and marker in text for marker in role_markers)
except Exception:
return False
def select_sub_account(self, member: WanyoujiMemberData) -> None: def select_sub_account(self, member: WanyoujiMemberData) -> None:
body_text = self.page.locator("body").inner_text(timeout=5000) body_text = self.page.locator("body").inner_text(timeout=5000)
if "/account/select" not in self.page.url and "选择账号" not in body_text: if "/account/select" not in self.page.url and "选择账号" not in body_text:
......
This diff is collapsed.
from __future__ import annotations from __future__ import annotations
import os import os
import random
from datetime import datetime from datetime import datetime
import pytest import pytest
...@@ -9,6 +8,7 @@ from playwright.sync_api import Page ...@@ -9,6 +8,7 @@ from playwright.sync_api import Page
from e2e.pages.wanzhanggui_order_page import ManualOrderData, WanyoujiFulfillmentPage, WanzhangguiOrderPage from e2e.pages.wanzhanggui_order_page import ManualOrderData, WanyoujiFulfillmentPage, WanzhangguiOrderPage
from e2e.utils.account_store import E2EAccount, account_store from e2e.utils.account_store import E2EAccount, account_store
from e2e.utils.data_factory import manual_order_receive_price
@pytest.mark.e2e_order_001 @pytest.mark.e2e_order_001
...@@ -136,11 +136,3 @@ def latest_strategy_name_for_type(wanzhanggui_phone: str, strategy_type: str) -> ...@@ -136,11 +136,3 @@ def latest_strategy_name_for_type(wanzhanggui_phone: str, strategy_type: str) ->
raise AssertionError(f"台账中未找到 {strategy_type} 的策略名称") raise AssertionError(f"台账中未找到 {strategy_type} 的策略名称")
return sorted(candidates, key=lambda item: item[0], reverse=True)[0][1] return sorted(candidates, key=lambda item: item[0], reverse=True)[0][1]
def manual_order_receive_price() -> str:
explicit = os.getenv("E2E_MANUAL_ORDER_RECEIVE_PRICE", "").strip()
if explicit:
value = int(explicit)
assert 50 <= value <= 300, f"E2E_MANUAL_ORDER_RECEIVE_PRICE must be between 50 and 300, got {value}"
return str(value)
return str(random.randint(50, 300))
...@@ -213,7 +213,8 @@ def login_player_sub_account_by_sms(page: Page, settings, bug_reporter, player: ...@@ -213,7 +213,8 @@ def login_player_sub_account_by_sms(page: Page, settings, bug_reporter, player:
def can_reuse_player_login(page: Page, player: E2EAccount) -> bool: def can_reuse_player_login(page: Page, player: E2EAccount) -> bool:
if os.getenv("E2E_REUSE_PLAYER_LOGIN", "").lower() not in {"1", "true", "yes"}: reuse_flag = os.getenv("E2E_REUSE_PLAYER_LOGIN", "1").strip().lower()
if reuse_flag in {"0", "false", "no", "off"}:
return False return False
if getattr(page, "_e2e_current_player_account", "") != player.phone: if getattr(page, "_e2e_current_player_account", "") != player.phone:
return False return False
......
from __future__ import annotations from __future__ import annotations
import os import os
import random
from datetime import datetime from datetime import datetime
import pytest import pytest
...@@ -9,6 +8,7 @@ from playwright.sync_api import Page ...@@ -9,6 +8,7 @@ from playwright.sync_api import Page
from e2e.pages.wanzhanggui_order_page import ManualOrderData, WanzhangguiOrderPage from e2e.pages.wanzhanggui_order_page import ManualOrderData, WanzhangguiOrderPage
from e2e.utils.account_store import account_store from e2e.utils.account_store import account_store
from e2e.utils.data_factory import manual_order_receive_price
@pytest.mark.s_flow_001 @pytest.mark.s_flow_001
...@@ -145,11 +145,3 @@ def should_create_without_dispatch_order(order: ManualOrderData, wanzhanggui_pho ...@@ -145,11 +145,3 @@ def should_create_without_dispatch_order(order: ManualOrderData, wanzhanggui_pho
def has_reusable_order_identity(order: ManualOrderData) -> bool: def has_reusable_order_identity(order: ManualOrderData) -> bool:
return bool(order.main_order_no or order.source_order_no or os.getenv("E2E_S_FLOW_SOURCE_ORDER_NO", "").strip()) return bool(order.main_order_no or order.source_order_no or os.getenv("E2E_S_FLOW_SOURCE_ORDER_NO", "").strip())
def manual_order_receive_price() -> str:
explicit = os.getenv("E2E_MANUAL_ORDER_RECEIVE_PRICE", "").strip()
if explicit:
value = int(explicit)
assert 50 <= value <= 300, f"E2E_MANUAL_ORDER_RECEIVE_PRICE must be between 50 and 300, got {value}"
return str(value)
return str(random.randint(50, 300))
from __future__ import annotations from __future__ import annotations
import os import os
import random
from datetime import datetime from datetime import datetime
import pytest import pytest
...@@ -13,6 +12,7 @@ from e2e.tests.订单状态流转.上游发单.test_s_flow_006_dispatch_after_ca ...@@ -13,6 +12,7 @@ from e2e.tests.订单状态流转.上游发单.test_s_flow_006_dispatch_after_ca
latest_strategy_name_for_type, latest_strategy_name_for_type,
) )
from e2e.utils.account_store import account_store from e2e.utils.account_store import account_store
from e2e.utils.data_factory import manual_order_receive_price
@pytest.mark.s_flow_007 @pytest.mark.s_flow_007
...@@ -189,11 +189,3 @@ def should_create_order(order: ManualOrderData) -> bool: ...@@ -189,11 +189,3 @@ def should_create_order(order: ManualOrderData) -> bool:
mode = os.getenv("E2E_S_FLOW_CREATE_MODE", "create").strip().lower() mode = os.getenv("E2E_S_FLOW_CREATE_MODE", "create").strip().lower()
return mode not in {"reuse", "existing"} return mode not in {"reuse", "existing"}
def manual_order_receive_price() -> str:
explicit = os.getenv("E2E_MANUAL_ORDER_RECEIVE_PRICE", "").strip()
if explicit:
value = int(explicit)
assert 50 <= value <= 300, f"E2E_MANUAL_ORDER_RECEIVE_PRICE must be between 50 and 300, got {value}"
return str(value)
return str(random.randint(50, 300))
from __future__ import annotations
from importlib import import_module
import pytest
from playwright.sync_api import Page
from e2e.utils.data_factory import configure_random_percent_dispatch
def _case(module_path: str, function_name: str):
return getattr(import_module(module_path), function_name)
_test_s_flow_007_create_order_with_dispatch = _case(
"e2e.tests.订单状态流转.上游发单.test_s_flow_007_create_order_with_dispatch",
"test_s_flow_007_create_order_with_dispatch",
)
_test_s_flow_014_accept_upstream_order_from_hall = _case(
"e2e.tests.订单状态流转.上游发单.test_s_flow_014_accept_upstream_order_from_hall",
"test_s_flow_014_accept_upstream_order_from_hall",
)
_test_s_flow_021_dispatch_accepted_order_to_player = _case(
"e2e.tests.订单状态流转.上游发单.test_s_flow_021_dispatch_accepted_order_to_player",
"test_s_flow_021_dispatch_accepted_order_to_player",
)
_test_s_flow_026_player_accept_dispatched_order = _case(
"e2e.tests.订单状态流转.上游发单.test_s_flow_026_player_accept_dispatched_order",
"test_s_flow_026_player_accept_dispatched_order",
)
_test_s_flow_040_player_submit_acceptance = _case(
"e2e.tests.订单状态流转.订单验收.test_s_flow_040_player_submit_acceptance",
"test_s_flow_040_player_submit_acceptance",
)
_test_s_flow_043_accept_player_sub_order = _case(
"e2e.tests.订单状态流转.订单验收.test_s_flow_043_accept_player_sub_order",
"test_s_flow_043_accept_player_sub_order",
)
_test_s_flow_045_apply_upstream_acceptance = _case(
"e2e.tests.订单状态流转.订单验收.test_s_flow_045_apply_upstream_acceptance",
"test_s_flow_045_apply_upstream_acceptance",
)
_test_s_flow_046_start_pending_acceptance_aftersale = _case(
"e2e.tests.订单状态流转.丸掌柜发起售后.test_s_flow_046_start_pending_acceptance_aftersale",
"test_s_flow_046_start_pending_acceptance_aftersale",
)
_test_s_flow_048_cancel_pending_acceptance_aftersale = _case(
"e2e.tests.订单状态流转.丸掌柜发起售后.test_s_flow_048_cancel_pending_acceptance_aftersale",
"test_s_flow_048_cancel_pending_acceptance_aftersale",
)
_test_s_flow_034_start_abnormal_sub_order_aftersale = _case(
"e2e.tests.订单状态流转.丸掌柜发起售后.test_s_flow_034_start_abnormal_sub_order_aftersale",
"test_s_flow_034_start_abnormal_sub_order_aftersale",
)
_test_s_flow_083_cancel_abnormal_sub_order_aftersale = _case(
"e2e.tests.订单状态流转.丸掌柜发起售后.test_s_flow_083_cancel_abnormal_sub_order_aftersale",
"test_s_flow_083_cancel_abnormal_sub_order_aftersale",
)
def _configure_new_percent_dispatch(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setenv("E2E_S_FLOW_CREATE_MODE", "create")
monkeypatch.delenv("E2E_S_FLOW_MAIN_ORDER_NO", raising=False)
monkeypatch.delenv("E2E_S_FLOW_SOURCE_ORDER_NO", raising=False)
monkeypatch.delenv("E2E_S_FLOW_ORDER_TITLE", raising=False)
configure_random_percent_dispatch(monkeypatch)
monkeypatch.setenv("E2E_TASK_ASSIGNMENT_METHOD", "抢单大厅")
@pytest.mark.flow_030
@pytest.mark.e2e_ledger
@pytest.mark.e2e_chain
def test_flow_030_pending_acceptance_aftersale_cancel_then_abnormal_sub_order_aftersale_chain(
page: Page,
settings,
bug_reporter,
monkeypatch,
) -> None:
_configure_new_percent_dispatch(monkeypatch)
_test_s_flow_007_create_order_with_dispatch(page, settings, bug_reporter)
_test_s_flow_014_accept_upstream_order_from_hall(page, settings, bug_reporter)
_test_s_flow_021_dispatch_accepted_order_to_player(page, settings, bug_reporter)
_test_s_flow_026_player_accept_dispatched_order(page, settings, bug_reporter)
_test_s_flow_040_player_submit_acceptance(page, settings, bug_reporter)
_test_s_flow_043_accept_player_sub_order(page, settings, bug_reporter)
_test_s_flow_045_apply_upstream_acceptance(page, settings, bug_reporter)
_test_s_flow_046_start_pending_acceptance_aftersale(page, settings, bug_reporter)
_test_s_flow_048_cancel_pending_acceptance_aftersale(page, settings, bug_reporter)
_test_s_flow_034_start_abnormal_sub_order_aftersale(page, settings, bug_reporter)
_test_s_flow_083_cancel_abnormal_sub_order_aftersale(page, settings, bug_reporter)
...@@ -88,7 +88,7 @@ def test_s_flow_034_start_abnormal_sub_order_aftersale(page: Page, settings, bug ...@@ -88,7 +88,7 @@ def test_s_flow_034_start_abnormal_sub_order_aftersale(page: Page, settings, bug
wanyouji_abnormal_sub_order_aftersale_text=wanyouji_text[:1500], wanyouji_abnormal_sub_order_aftersale_text=wanyouji_text[:1500],
wanyouji_parent_status="售后中", wanyouji_parent_status="售后中",
wanyouji_visibility="workshop_order_list_visible", wanyouji_visibility="workshop_order_list_visible",
available_for_cases=["S-FLOW-036", "S-FLOW-035"], available_for_cases=["S-FLOW-083", "S-FLOW-036", "S-FLOW-035"],
) )
...@@ -119,6 +119,13 @@ def resolve_s_flow_034_order(wanzhanggui_phone: str) -> dict | None: ...@@ -119,6 +119,13 @@ def resolve_s_flow_034_order(wanzhanggui_phone: str) -> dict | None:
wanzhanggui_phone=wanzhanggui_phone, wanzhanggui_phone=wanzhanggui_phone,
current_flow_state="wanyouji_upstream_revoke_pending_in_progress", current_flow_state="wanyouji_upstream_revoke_pending_in_progress",
) )
if candidate:
return candidate
candidate = account_store().find_order_for_flow_case(
case_id="S-FLOW-034",
wanzhanggui_phone=wanzhanggui_phone,
current_flow_state="wanzhanggui_abnormal_sub_order_after_pending_acceptance_aftersale_cancelled",
)
if candidate: if candidate:
return candidate return candidate
...@@ -130,6 +137,7 @@ def resolve_s_flow_034_order(wanzhanggui_phone: str) -> dict | None: ...@@ -130,6 +137,7 @@ def resolve_s_flow_034_order(wanzhanggui_phone: str) -> dict | None:
"wanyouji_revoke_rejected_continue_service", "wanyouji_revoke_rejected_continue_service",
"wanzhanggui_abnormal_sub_order_after_revoke_rejected", "wanzhanggui_abnormal_sub_order_after_revoke_rejected",
"wanyouji_upstream_revoke_pending_in_progress", "wanyouji_upstream_revoke_pending_in_progress",
"wanzhanggui_abnormal_sub_order_after_pending_acceptance_aftersale_cancelled",
} }
and "S-FLOW-034" in item.get("available_for_cases", []) and "S-FLOW-034" in item.get("available_for_cases", [])
and "S-FLOW-034" not in item.get("consumed_by_cases", []) and "S-FLOW-034" not in item.get("consumed_by_cases", [])
......
from __future__ import annotations
import os
import pytest
from playwright.sync_api import Page
from e2e.pages.wanzhanggui_order_page import WanyoujiFulfillmentPage, WanzhangguiOrderPage
from e2e.tests.订单状态流转.penalty_recovery_flow_helpers import resolve_order_for_case, resolve_wanyouji_account
from e2e.utils.account_store import account_store
@pytest.mark.s_flow_046
@pytest.mark.e2e_ledger
@pytest.mark.e2e_chain
def test_s_flow_046_start_pending_acceptance_aftersale(page: Page, settings, bug_reporter) -> None:
wanzhanggui_phone = os.getenv("E2E_ORDER_WANZHANGGUI_PHONE", settings.wanzhanggui_tenant_phone)
wanzhanggui_password = os.getenv("E2E_ORDER_WANZHANGGUI_PASSWORD", settings.wanzhanggui_tenant_password)
order = resolve_order_for_case(
case_id="S-FLOW-046",
wanzhanggui_phone=wanzhanggui_phone,
current_flow_states=["wanzhanggui_waiting_upstream_acceptance"],
)
if not order:
pytest.skip("台账中未找到可供 S-FLOW-046 使用的待验收订单;请先执行 S-FLOW-045。")
source_order_no = order["source_order_no"]
main_order_no = order.get("main_order_no", "")
order_title = order.get("order_title", source_order_no)
amount = os.getenv("E2E_S_FLOW_AFTERSALE_AMOUNT", "1")
reason = f"S-FLOW-046 待验收阶段主单发起售后 {main_order_no or source_order_no}"
order_page = WanzhangguiOrderPage(page, settings.tenant_home_url, bug_reporter)
order_page.login_main_account(wanzhanggui_phone, wanzhanggui_password)
bug_reporter.record_if_errors(account=wanzhanggui_phone, location="S-FLOW-046 丸掌柜账号登录")
before_detail_text = order_page.assert_order_detail_statuses(
main_order_no=main_order_no,
source_order_no=source_order_no,
order_title=order_title,
expected_main_status=["待验收", "服务中", "代练中"],
expected_sub_status=["待验收", "已完成", "代练中"],
expected_aftersale_absent=True,
)
aftersale_detail_text = order_page.start_pending_acceptance_main_order_aftersale(
main_order_no=main_order_no,
source_order_no=source_order_no,
order_title=order_title,
reason=reason,
amount=amount,
)
bug_reporter.record_if_errors(
account=wanzhanggui_phone,
location=f"S-FLOW-046 丸掌柜待验收阶段主单发起售后:{main_order_no or source_order_no}",
)
wanyouji_account = resolve_wanyouji_account(order)
fulfillment_page = WanyoujiFulfillmentPage(page, settings.tenant_home_url, bug_reporter)
fulfillment_page.login_main_account(wanyouji_account.phone, wanyouji_account.password)
wanyouji_text = fulfillment_page.search_workshop_order_list(
fulfillment_page.open_workshop_order_list(),
source_order_no,
order_title,
)
assert source_order_no in wanyouji_text or order_title in wanyouji_text, (
f"S-FLOW-046 发起待验收主单售后后丸友集订单列表未找到订单;source_order_no={source_order_no}; "
f"text={wanyouji_text[:1500]}"
)
assert "售后" in wanyouji_text, (
f"S-FLOW-046 发起待验收主单售后后丸友集订单未进入售后相关状态;source_order_no={source_order_no}; "
f"text={wanyouji_text[:1500]}"
)
bug_reporter.record_if_errors(
account=wanyouji_account.phone,
location=f"S-FLOW-046 丸友集订单列表售后中状态校验:{main_order_no or source_order_no}",
)
account_store().mark_order_consumed_by_flow_case(
source_order_no=source_order_no,
case_id="S-FLOW-046",
status="pending_acceptance_aftersale_pending",
current_flow_state="wanzhanggui_pending_acceptance_aftersale_pending",
wanzhanggui_main_status="售后中",
wanzhanggui_sub_order_status="售后中",
aftersale_started_by=wanzhanggui_phone,
aftersale_reason=reason,
aftersale_amount=amount,
pending_acceptance_aftersale_before_detail_text=before_detail_text[:1500],
pending_acceptance_aftersale_started_detail_text=aftersale_detail_text[:1500],
wanyouji_pending_acceptance_aftersale_text=wanyouji_text[:1500],
available_for_cases=["S-FLOW-047", "S-FLOW-048"],
)
from __future__ import annotations
import os
import pytest
from playwright.sync_api import Page
from e2e.pages.wanzhanggui_order_page import WanzhangguiOrderPage
from e2e.tests.订单状态流转.penalty_recovery_flow_helpers import resolve_order_for_case
from e2e.utils.account_store import account_store
@pytest.mark.s_flow_047
@pytest.mark.e2e_ledger
@pytest.mark.e2e_chain
def test_s_flow_047_process_pending_acceptance_aftersale(page: Page, settings, bug_reporter) -> None:
wanzhanggui_phone = os.getenv("E2E_ORDER_WANZHANGGUI_PHONE", settings.wanzhanggui_tenant_phone)
wanzhanggui_password = os.getenv("E2E_ORDER_WANZHANGGUI_PASSWORD", settings.wanzhanggui_tenant_password)
store = account_store()
order = resolve_order_for_case(
case_id="S-FLOW-047",
wanzhanggui_phone=wanzhanggui_phone,
current_flow_states=["wanzhanggui_pending_acceptance_aftersale_pending"],
)
if not order:
pytest.skip("台账中未找到可供 S-FLOW-047 使用的待验收阶段售后中订单;请先执行 S-FLOW-046。")
source_order_no = order["source_order_no"]
main_order_no = order.get("main_order_no", "")
order_title = order.get("order_title", source_order_no)
process_amount = os.getenv("E2E_S_FLOW_AFTERSALE_PROCESS_AMOUNT", order.get("aftersale_amount", "1") or "1")
process_remark = f"S-FLOW-047 待验收阶段主单确认售后 {main_order_no or source_order_no}"
order_page = WanzhangguiOrderPage(page, settings.tenant_home_url, bug_reporter)
order_page.login_main_account(wanzhanggui_phone, wanzhanggui_password)
bug_reporter.record_if_errors(account=wanzhanggui_phone, location="S-FLOW-047 丸掌柜账号登录")
final_text = order_page.process_aftersale_for_order(
source_order_no,
order_title,
main_order_no=main_order_no,
remark=process_remark,
amount=process_amount,
)
bug_reporter.record_if_errors(
account=wanzhanggui_phone,
location=f"S-FLOW-047 丸掌柜待验收阶段确认主单售后:{main_order_no or source_order_no}/{order_title}",
)
detail_status_text = order_page.assert_order_detail_statuses(
main_order_no=main_order_no,
source_order_no=source_order_no,
order_title=order_title,
expected_main_status="已售后",
expected_sub_status_parts=["已售后"],
expected_aftersale_status="已完成",
)
bug_reporter.record_if_errors(
account=wanzhanggui_phone,
location=f"S-FLOW-047 丸掌柜待验收阶段售后完成状态校验:{main_order_no or source_order_no}",
)
store.mark_order_consumed_by_flow_case(
source_order_no=source_order_no,
case_id="S-FLOW-047",
status="aftersale_completed",
current_flow_state="wanzhanggui_aftersale_completed",
wanzhanggui_main_status="已售后",
wanzhanggui_sub_order_status="已售后",
aftersale_processed_by=wanzhanggui_phone,
aftersale_process_remark=process_remark,
aftersale_process_amount=process_amount,
pending_acceptance_aftersale_processed_text=final_text[:1500],
pending_acceptance_aftersale_processed_detail_status_text=detail_status_text[:1500],
available_for_cases=["S-FLOW-051"],
)
from __future__ import annotations
import os
import pytest
from playwright.sync_api import Page
from e2e.pages.wanzhanggui_order_page import WanyoujiFulfillmentPage, WanzhangguiOrderPage
from e2e.tests.订单状态流转.penalty_recovery_flow_helpers import resolve_order_for_case, resolve_wanyouji_account
from e2e.utils.account_store import account_store
@pytest.mark.s_flow_048
@pytest.mark.e2e_ledger
@pytest.mark.e2e_chain
def test_s_flow_048_cancel_pending_acceptance_aftersale(page: Page, settings, bug_reporter) -> None:
wanzhanggui_phone = os.getenv("E2E_ORDER_WANZHANGGUI_PHONE", settings.wanzhanggui_tenant_phone)
wanzhanggui_password = os.getenv("E2E_ORDER_WANZHANGGUI_PASSWORD", settings.wanzhanggui_tenant_password)
order = resolve_order_for_case(
case_id="S-FLOW-048",
wanzhanggui_phone=wanzhanggui_phone,
current_flow_states=["wanzhanggui_pending_acceptance_aftersale_pending"],
)
if not order:
pytest.skip("台账中未找到可供 S-FLOW-048 使用的待验收阶段售后中订单;请先执行 S-FLOW-046。")
source_order_no = order["source_order_no"]
main_order_no = order.get("main_order_no", "")
order_title = order.get("order_title", source_order_no)
reason = f"S-FLOW-048 待验收阶段取消主单售后 {main_order_no or source_order_no}"
order_page = WanzhangguiOrderPage(page, settings.tenant_home_url, bug_reporter)
order_page.login_main_account(wanzhanggui_phone, wanzhanggui_password)
bug_reporter.record_if_errors(account=wanzhanggui_phone, location="S-FLOW-048 丸掌柜账号登录")
final_text = order_page.cancel_pending_acceptance_main_order_aftersale(
main_order_no=main_order_no,
source_order_no=source_order_no,
order_title=order_title,
reason=reason,
)
detail_status_text = order_page.assert_order_detail_statuses(
main_order_no=main_order_no,
source_order_no=source_order_no,
order_title=order_title,
expected_main_status=["待验收", "服务中", "代练中", "异常中"],
expected_sub_status=["待验收", "已完成", "代练中", "异常中", "代练中异常"],
expected_sub_order_list_status=["待验收", "已完成", "代练中", "异常中", "代练中异常"],
expected_aftersale_absent=True,
)
bug_reporter.record_if_errors(
account=wanzhanggui_phone,
location=f"S-FLOW-048 丸掌柜待验收阶段取消主单售后:{main_order_no or source_order_no}",
)
wanyouji_account = resolve_wanyouji_account(order)
fulfillment_page = WanyoujiFulfillmentPage(page, settings.tenant_home_url, bug_reporter)
fulfillment_page.login_main_account(wanyouji_account.phone, wanyouji_account.password)
wanyouji_text = fulfillment_page.search_workshop_order_list(
fulfillment_page.open_workshop_order_list(),
source_order_no,
order_title,
)
wanyouji_row_text = WanyoujiFulfillmentPage.extract_order_row_text(wanyouji_text, source_order_no)
assert source_order_no in wanyouji_text or order_title in wanyouji_text, (
f"S-FLOW-048 取消待验收主单售后后丸友集订单列表未找到订单;source_order_no={source_order_no}; "
f"text={wanyouji_text[:1500]}"
)
assert "售后中" not in wanyouji_row_text, (
f"S-FLOW-048 取消待验收主单售后后丸友集目标订单仍为售后中;source_order_no={source_order_no}; "
f"row_text={wanyouji_row_text[:1500]}"
)
bug_reporter.record_if_errors(
account=wanyouji_account.phone,
location=f"S-FLOW-048 丸友集订单列表取消售后后状态校验:{main_order_no or source_order_no}",
)
account_store().mark_order_consumed_by_flow_case(
source_order_no=source_order_no,
case_id="S-FLOW-048",
status="pending_acceptance_aftersale_cancelled",
current_flow_state="wanzhanggui_abnormal_sub_order_after_pending_acceptance_aftersale_cancelled",
wanzhanggui_main_status="待验收",
wanzhanggui_sub_order_status="异常中",
wanzhanggui_sub_order_list_status="异常中",
aftersale_cancelled_by=wanzhanggui_phone,
aftersale_cancel_reason=reason,
pending_acceptance_aftersale_cancelled_text=final_text[:1500],
pending_acceptance_aftersale_cancelled_detail_text=detail_status_text[:1500],
wanyouji_pending_acceptance_aftersale_cancelled_text=wanyouji_text[:1500],
wanyouji_pending_acceptance_aftersale_cancelled_row_text=wanyouji_row_text[:1500],
available_for_cases=["S-FLOW-034"],
)
from __future__ import annotations
import os
import pytest
from playwright.sync_api import Page
from e2e.pages.wanzhanggui_order_page import WanyoujiFulfillmentPage, WanzhangguiOrderPage
from e2e.utils.account_store import account_store
@pytest.mark.s_flow_083
@pytest.mark.e2e_ledger
@pytest.mark.e2e_chain
def test_s_flow_083_cancel_abnormal_sub_order_aftersale(page: Page, settings, bug_reporter) -> None:
wanzhanggui_phone = os.getenv("E2E_ORDER_WANZHANGGUI_PHONE", settings.wanzhanggui_tenant_phone)
wanzhanggui_password = os.getenv("E2E_ORDER_WANZHANGGUI_PASSWORD", settings.wanzhanggui_tenant_password)
store = account_store()
order = resolve_s_flow_083_order(wanzhanggui_phone)
if not order:
pytest.skip("台账中未找到可供 S-FLOW-083 使用的异常中子单售后中订单,请先执行 S-FLOW-034。")
source_order_no = order["source_order_no"]
main_order_no = order.get("main_order_no", "")
order_title = order.get("order_title", source_order_no)
cancel_reason = f"S-FLOW-083 丸掌柜取消异常中子单售后 {main_order_no or source_order_no}"
order_page = WanzhangguiOrderPage(page, settings.tenant_home_url, bug_reporter)
order_page.login_main_account(wanzhanggui_phone, wanzhanggui_password)
bug_reporter.record_if_errors(account=wanzhanggui_phone, location="S-FLOW-083 丸掌柜账号登录")
final_detail_text = order_page.cancel_abnormal_sub_order_aftersale(
main_order_no=main_order_no,
source_order_no=source_order_no,
order_title=order_title,
reason=cancel_reason,
)
bug_reporter.record_if_errors(
account=wanzhanggui_phone,
location=f"S-FLOW-083 丸掌柜取消异常中子单售后:{main_order_no or source_order_no}",
)
wanyouji_phone = resolve_wanyouji_phone(order)
wanyouji_account = store.find(phone=wanyouji_phone)
assert wanyouji_account is not None, f"S-FLOW-083 台账中未找到丸友集账号:{wanyouji_phone}"
fulfillment_page = WanyoujiFulfillmentPage(page, settings.tenant_home_url, bug_reporter)
fulfillment_page.login_main_account(wanyouji_account.phone, wanyouji_account.password)
frame = fulfillment_page.open_workshop_order_list()
wanyouji_text = fulfillment_page.search_workshop_order_list(frame, source_order_no, order_title)
wanyouji_row_text = WanyoujiFulfillmentPage.extract_order_row_text(wanyouji_text, source_order_no)
assert source_order_no in wanyouji_text or order_title in wanyouji_text, (
f"S-FLOW-083 取消异常中子单售后后丸友集订单列表未找到订单;"
f"source_order_no={source_order_no}; text={wanyouji_text[:1500]}"
)
assert "售后中" not in wanyouji_row_text, (
f"S-FLOW-083 取消异常中子单售后后丸友集目标订单仍显示售后中;"
f"source_order_no={source_order_no}; row_text={wanyouji_row_text[:1500]}"
)
bug_reporter.record_if_errors(
account=wanyouji_account.phone,
location=f"S-FLOW-083 丸友集取消异常中子单售后后回查:{main_order_no or source_order_no}",
)
store.mark_order_consumed_by_flow_case(
source_order_no=source_order_no,
case_id="S-FLOW-083",
status="abnormal_sub_order_aftersale_cancelled",
current_flow_state="wanzhanggui_abnormal_sub_order_aftersale_cancelled",
expected_result=(
"丸掌柜侧异常中子单售后取消成功;目标子单不再显示售后中,恢复为异常中或页面对应可继续处理状态;"
"取消原因可追溯;丸友集侧目标订单不再停留售后中;后续可重新接 S-FLOW-034 或按异常中后续链路处理。"
),
wanzhanggui_expected_result="订单详情子单列表目标子单不再售后中,显示异常中/发单方取消撤销等可继续处理状态,售后信息消失或记录为已取消。",
wanyouji_expected_result="订单列表目标订单不再显示售后中,来源单号与主单号仍匹配同一订单。",
aftersale_cancelled_by=wanzhanggui_phone,
aftersale_cancel_reason=cancel_reason,
abnormal_sub_order_aftersale_cancelled_detail_text=final_detail_text[:1500],
wanyouji_abnormal_sub_order_aftersale_cancelled_text=wanyouji_text[:1500],
wanyouji_abnormal_sub_order_aftersale_cancelled_row_text=wanyouji_row_text[:1500],
wanzhanggui_sub_order_list_status="异常中",
available_for_cases=["S-FLOW-034"],
)
def resolve_s_flow_083_order(wanzhanggui_phone: str) -> dict | None:
explicit_main_order_no = os.getenv("E2E_S_FLOW_MAIN_ORDER_NO", "").strip()
explicit_source_order_no = os.getenv("E2E_S_FLOW_SOURCE_ORDER_NO", "").strip()
payload = account_store().load_payload()
if explicit_main_order_no or explicit_source_order_no:
for item in payload.get("dispatched_orders", []):
if wanzhanggui_phone and item.get("wanzhanggui_phone") != wanzhanggui_phone:
continue
if explicit_main_order_no and item.get("main_order_no") != explicit_main_order_no:
continue
if explicit_source_order_no and item.get("source_order_no") != explicit_source_order_no:
continue
return item
return None
candidate = account_store().find_order_for_flow_case(
case_id="S-FLOW-083",
wanzhanggui_phone=wanzhanggui_phone,
current_flow_state="wanzhanggui_abnormal_sub_order_aftersale_pending",
)
if candidate:
return candidate
candidates = [
item
for item in payload.get("dispatched_orders", [])
if (not wanzhanggui_phone or item.get("wanzhanggui_phone") == wanzhanggui_phone)
and item.get("current_flow_state") == "wanzhanggui_abnormal_sub_order_aftersale_pending"
and "S-FLOW-083" not in item.get("consumed_by_cases", [])
]
return sorted(candidates, key=lambda item: item.get("updated_at", item.get("created_at", "")), reverse=True)[0] if candidates else None
def resolve_wanyouji_phone(order: dict) -> str:
explicit = os.getenv("E2E_ORDER_WANYOUJI_PHONE", "").strip()
if explicit:
return explicit
phones = order.get("wanyouji_phones") or []
assert phones, f"S-FLOW-083 订单台账缺少 wanyouji_phones;order={order}"
return phones[0]
from __future__ import annotations
import pytest
from playwright.sync_api import Page
from e2e.utils.data_factory import configure_random_percent_dispatch
from e2e.tests.订单状态流转.上游发单.test_s_flow_007_create_order_with_dispatch import (
test_s_flow_007_create_order_with_dispatch as _test_s_flow_007_create_order_with_dispatch,
)
from e2e.tests.订单状态流转.上游发单.test_s_flow_014_accept_upstream_order_from_hall import (
test_s_flow_014_accept_upstream_order_from_hall as _test_s_flow_014_accept_upstream_order_from_hall,
)
from e2e.tests.订单状态流转.上游发单.test_s_flow_021_dispatch_accepted_order_to_player import (
test_s_flow_021_dispatch_accepted_order_to_player as _test_s_flow_021_dispatch_accepted_order_to_player,
)
from e2e.tests.订单状态流转.上游发单.test_s_flow_026_player_accept_dispatched_order import (
test_s_flow_026_player_accept_dispatched_order as _test_s_flow_026_player_accept_dispatched_order,
)
from e2e.tests.订单状态流转.订单验收.test_s_flow_040_player_submit_acceptance import (
test_s_flow_040_player_submit_acceptance as _test_s_flow_040_player_submit_acceptance,
)
from e2e.tests.订单状态流转.订单验收.test_s_flow_043_accept_player_sub_order import (
test_s_flow_043_accept_player_sub_order as _test_s_flow_043_accept_player_sub_order,
)
from e2e.tests.订单状态流转.订单验收.test_s_flow_045_apply_upstream_acceptance import (
test_s_flow_045_apply_upstream_acceptance as _test_s_flow_045_apply_upstream_acceptance,
)
from e2e.tests.订单状态流转.订单验收.test_s_flow_049_complete_upstream_acceptance import (
test_s_flow_049_complete_upstream_acceptance as _test_s_flow_049_complete_upstream_acceptance,
)
from e2e.tests.订单状态流转.主单申诉.test_s_flow_050_start_appeal_from_completed_order import (
test_s_flow_050_start_appeal_from_completed_order as _test_s_flow_050_start_appeal_from_completed_order,
)
from e2e.tests.订单状态流转.主单申诉.test_s_flow_052_approve_main_order_appeal import (
test_s_flow_052_approve_main_order_appeal as _test_s_flow_052_approve_main_order_appeal,
)
@pytest.mark.flow_026
@pytest.mark.e2e_ledger
@pytest.mark.e2e_chain
def test_flow_026_completed_order_appeal_approve_chain(page: Page, settings, bug_reporter, monkeypatch) -> None:
monkeypatch.setenv("E2E_S_FLOW_CREATE_MODE", "create")
monkeypatch.delenv("E2E_S_FLOW_MAIN_ORDER_NO", raising=False)
monkeypatch.delenv("E2E_S_FLOW_SOURCE_ORDER_NO", raising=False)
monkeypatch.delenv("E2E_S_FLOW_ORDER_TITLE", raising=False)
configure_random_percent_dispatch(monkeypatch)
monkeypatch.setenv("E2E_TASK_ASSIGNMENT_METHOD", "抢单大厅")
_test_s_flow_007_create_order_with_dispatch(page, settings, bug_reporter)
_test_s_flow_014_accept_upstream_order_from_hall(page, settings, bug_reporter)
_test_s_flow_021_dispatch_accepted_order_to_player(page, settings, bug_reporter)
_test_s_flow_026_player_accept_dispatched_order(page, settings, bug_reporter)
_test_s_flow_040_player_submit_acceptance(page, settings, bug_reporter)
_test_s_flow_043_accept_player_sub_order(page, settings, bug_reporter)
_test_s_flow_045_apply_upstream_acceptance(page, settings, bug_reporter)
_test_s_flow_049_complete_upstream_acceptance(page, settings, bug_reporter)
_test_s_flow_050_start_appeal_from_completed_order(page, settings, bug_reporter)
_test_s_flow_052_approve_main_order_appeal(page, settings, bug_reporter)
from __future__ import annotations
import pytest
from playwright.sync_api import Page
from e2e.utils.data_factory import configure_random_percent_dispatch
from e2e.tests.订单状态流转.上游发单.test_s_flow_007_create_order_with_dispatch import (
test_s_flow_007_create_order_with_dispatch as _test_s_flow_007_create_order_with_dispatch,
)
from e2e.tests.订单状态流转.上游发单.test_s_flow_014_accept_upstream_order_from_hall import (
test_s_flow_014_accept_upstream_order_from_hall as _test_s_flow_014_accept_upstream_order_from_hall,
)
from e2e.tests.订单状态流转.上游发单.test_s_flow_021_dispatch_accepted_order_to_player import (
test_s_flow_021_dispatch_accepted_order_to_player as _test_s_flow_021_dispatch_accepted_order_to_player,
)
from e2e.tests.订单状态流转.上游发单.test_s_flow_026_player_accept_dispatched_order import (
test_s_flow_026_player_accept_dispatched_order as _test_s_flow_026_player_accept_dispatched_order,
)
from e2e.tests.订单状态流转.订单验收.test_s_flow_040_player_submit_acceptance import (
test_s_flow_040_player_submit_acceptance as _test_s_flow_040_player_submit_acceptance,
)
from e2e.tests.订单状态流转.订单验收.test_s_flow_043_accept_player_sub_order import (
test_s_flow_043_accept_player_sub_order as _test_s_flow_043_accept_player_sub_order,
)
from e2e.tests.订单状态流转.订单验收.test_s_flow_045_apply_upstream_acceptance import (
test_s_flow_045_apply_upstream_acceptance as _test_s_flow_045_apply_upstream_acceptance,
)
from e2e.tests.订单状态流转.订单验收.test_s_flow_049_complete_upstream_acceptance import (
test_s_flow_049_complete_upstream_acceptance as _test_s_flow_049_complete_upstream_acceptance,
)
from e2e.tests.订单状态流转.主单申诉.test_s_flow_050_start_appeal_from_completed_order import (
test_s_flow_050_start_appeal_from_completed_order as _test_s_flow_050_start_appeal_from_completed_order,
)
from e2e.tests.订单状态流转.主单申诉.test_s_flow_053_reject_main_order_appeal import (
test_s_flow_053_reject_main_order_appeal as _test_s_flow_053_reject_main_order_appeal,
)
@pytest.mark.flow_027
@pytest.mark.e2e_ledger
@pytest.mark.e2e_chain
def test_flow_027_completed_order_appeal_reject_chain(page: Page, settings, bug_reporter, monkeypatch) -> None:
monkeypatch.setenv("E2E_S_FLOW_CREATE_MODE", "create")
monkeypatch.delenv("E2E_S_FLOW_MAIN_ORDER_NO", raising=False)
monkeypatch.delenv("E2E_S_FLOW_SOURCE_ORDER_NO", raising=False)
monkeypatch.delenv("E2E_S_FLOW_ORDER_TITLE", raising=False)
configure_random_percent_dispatch(monkeypatch)
monkeypatch.setenv("E2E_TASK_ASSIGNMENT_METHOD", "抢单大厅")
_test_s_flow_007_create_order_with_dispatch(page, settings, bug_reporter)
_test_s_flow_014_accept_upstream_order_from_hall(page, settings, bug_reporter)
_test_s_flow_021_dispatch_accepted_order_to_player(page, settings, bug_reporter)
_test_s_flow_026_player_accept_dispatched_order(page, settings, bug_reporter)
_test_s_flow_040_player_submit_acceptance(page, settings, bug_reporter)
_test_s_flow_043_accept_player_sub_order(page, settings, bug_reporter)
_test_s_flow_045_apply_upstream_acceptance(page, settings, bug_reporter)
_test_s_flow_049_complete_upstream_acceptance(page, settings, bug_reporter)
_test_s_flow_050_start_appeal_from_completed_order(page, settings, bug_reporter)
_test_s_flow_053_reject_main_order_appeal(page, settings, bug_reporter)
from __future__ import annotations
from importlib import import_module
import pytest
from playwright.sync_api import Page
from e2e.utils.data_factory import configure_random_percent_dispatch
def _case(module_path: str, function_name: str):
return getattr(import_module(module_path), function_name)
_test_s_flow_007_create_order_with_dispatch = _case(
"e2e.tests.订单状态流转.上游发单.test_s_flow_007_create_order_with_dispatch",
"test_s_flow_007_create_order_with_dispatch",
)
_test_s_flow_014_accept_upstream_order_from_hall = _case(
"e2e.tests.订单状态流转.上游发单.test_s_flow_014_accept_upstream_order_from_hall",
"test_s_flow_014_accept_upstream_order_from_hall",
)
_test_s_flow_021_dispatch_accepted_order_to_player = _case(
"e2e.tests.订单状态流转.上游发单.test_s_flow_021_dispatch_accepted_order_to_player",
"test_s_flow_021_dispatch_accepted_order_to_player",
)
_test_s_flow_026_player_accept_dispatched_order = _case(
"e2e.tests.订单状态流转.上游发单.test_s_flow_026_player_accept_dispatched_order",
"test_s_flow_026_player_accept_dispatched_order",
)
_test_s_flow_040_player_submit_acceptance = _case(
"e2e.tests.订单状态流转.订单验收.test_s_flow_040_player_submit_acceptance",
"test_s_flow_040_player_submit_acceptance",
)
_test_s_flow_043_accept_player_sub_order = _case(
"e2e.tests.订单状态流转.订单验收.test_s_flow_043_accept_player_sub_order",
"test_s_flow_043_accept_player_sub_order",
)
_test_s_flow_045_apply_upstream_acceptance = _case(
"e2e.tests.订单状态流转.订单验收.test_s_flow_045_apply_upstream_acceptance",
"test_s_flow_045_apply_upstream_acceptance",
)
_test_s_flow_046_start_pending_acceptance_aftersale = _case(
"e2e.tests.订单状态流转.丸掌柜发起售后.test_s_flow_046_start_pending_acceptance_aftersale",
"test_s_flow_046_start_pending_acceptance_aftersale",
)
_test_s_flow_047_process_pending_acceptance_aftersale = _case(
"e2e.tests.订单状态流转.丸掌柜发起售后.test_s_flow_047_process_pending_acceptance_aftersale",
"test_s_flow_047_process_pending_acceptance_aftersale",
)
_test_s_flow_051_start_appeal_from_aftersale_completed = _case(
"e2e.tests.订单状态流转.主单申诉.test_s_flow_051_start_appeal_from_aftersale_completed",
"test_s_flow_051_start_appeal_from_aftersale_completed",
)
_test_s_flow_052_approve_main_order_appeal = _case(
"e2e.tests.订单状态流转.主单申诉.test_s_flow_052_approve_main_order_appeal",
"test_s_flow_052_approve_main_order_appeal",
)
@pytest.mark.flow_028
@pytest.mark.e2e_ledger
@pytest.mark.e2e_chain
def test_flow_028_pending_acceptance_aftersale_appeal_approve_chain(
page: Page,
settings,
bug_reporter,
monkeypatch,
) -> None:
monkeypatch.setenv("E2E_S_FLOW_CREATE_MODE", "create")
monkeypatch.delenv("E2E_S_FLOW_MAIN_ORDER_NO", raising=False)
monkeypatch.delenv("E2E_S_FLOW_SOURCE_ORDER_NO", raising=False)
monkeypatch.delenv("E2E_S_FLOW_ORDER_TITLE", raising=False)
configure_random_percent_dispatch(monkeypatch)
monkeypatch.setenv("E2E_TASK_ASSIGNMENT_METHOD", "抢单大厅")
_test_s_flow_007_create_order_with_dispatch(page, settings, bug_reporter)
_test_s_flow_014_accept_upstream_order_from_hall(page, settings, bug_reporter)
_test_s_flow_021_dispatch_accepted_order_to_player(page, settings, bug_reporter)
_test_s_flow_026_player_accept_dispatched_order(page, settings, bug_reporter)
_test_s_flow_040_player_submit_acceptance(page, settings, bug_reporter)
_test_s_flow_043_accept_player_sub_order(page, settings, bug_reporter)
_test_s_flow_045_apply_upstream_acceptance(page, settings, bug_reporter)
_test_s_flow_046_start_pending_acceptance_aftersale(page, settings, bug_reporter)
_test_s_flow_047_process_pending_acceptance_aftersale(page, settings, bug_reporter)
_test_s_flow_051_start_appeal_from_aftersale_completed(page, settings, bug_reporter)
_test_s_flow_052_approve_main_order_appeal(page, settings, bug_reporter)
from __future__ import annotations
from importlib import import_module
import pytest
from playwright.sync_api import Page
from e2e.utils.data_factory import configure_random_percent_dispatch
def _case(module_path: str, function_name: str):
return getattr(import_module(module_path), function_name)
_test_s_flow_007_create_order_with_dispatch = _case(
"e2e.tests.订单状态流转.上游发单.test_s_flow_007_create_order_with_dispatch",
"test_s_flow_007_create_order_with_dispatch",
)
_test_s_flow_014_accept_upstream_order_from_hall = _case(
"e2e.tests.订单状态流转.上游发单.test_s_flow_014_accept_upstream_order_from_hall",
"test_s_flow_014_accept_upstream_order_from_hall",
)
_test_s_flow_021_dispatch_accepted_order_to_player = _case(
"e2e.tests.订单状态流转.上游发单.test_s_flow_021_dispatch_accepted_order_to_player",
"test_s_flow_021_dispatch_accepted_order_to_player",
)
_test_s_flow_026_player_accept_dispatched_order = _case(
"e2e.tests.订单状态流转.上游发单.test_s_flow_026_player_accept_dispatched_order",
"test_s_flow_026_player_accept_dispatched_order",
)
_test_s_flow_040_player_submit_acceptance = _case(
"e2e.tests.订单状态流转.订单验收.test_s_flow_040_player_submit_acceptance",
"test_s_flow_040_player_submit_acceptance",
)
_test_s_flow_043_accept_player_sub_order = _case(
"e2e.tests.订单状态流转.订单验收.test_s_flow_043_accept_player_sub_order",
"test_s_flow_043_accept_player_sub_order",
)
_test_s_flow_045_apply_upstream_acceptance = _case(
"e2e.tests.订单状态流转.订单验收.test_s_flow_045_apply_upstream_acceptance",
"test_s_flow_045_apply_upstream_acceptance",
)
_test_s_flow_046_start_pending_acceptance_aftersale = _case(
"e2e.tests.订单状态流转.丸掌柜发起售后.test_s_flow_046_start_pending_acceptance_aftersale",
"test_s_flow_046_start_pending_acceptance_aftersale",
)
_test_s_flow_047_process_pending_acceptance_aftersale = _case(
"e2e.tests.订单状态流转.丸掌柜发起售后.test_s_flow_047_process_pending_acceptance_aftersale",
"test_s_flow_047_process_pending_acceptance_aftersale",
)
_test_s_flow_051_start_appeal_from_aftersale_completed = _case(
"e2e.tests.订单状态流转.主单申诉.test_s_flow_051_start_appeal_from_aftersale_completed",
"test_s_flow_051_start_appeal_from_aftersale_completed",
)
_test_s_flow_053_reject_main_order_appeal = _case(
"e2e.tests.订单状态流转.主单申诉.test_s_flow_053_reject_main_order_appeal",
"test_s_flow_053_reject_main_order_appeal",
)
@pytest.mark.flow_029
@pytest.mark.e2e_ledger
@pytest.mark.e2e_chain
def test_flow_029_pending_acceptance_aftersale_appeal_reject_chain(
page: Page,
settings,
bug_reporter,
monkeypatch,
) -> None:
monkeypatch.setenv("E2E_S_FLOW_CREATE_MODE", "create")
monkeypatch.delenv("E2E_S_FLOW_MAIN_ORDER_NO", raising=False)
monkeypatch.delenv("E2E_S_FLOW_SOURCE_ORDER_NO", raising=False)
monkeypatch.delenv("E2E_S_FLOW_ORDER_TITLE", raising=False)
configure_random_percent_dispatch(monkeypatch)
monkeypatch.setenv("E2E_TASK_ASSIGNMENT_METHOD", "鎶㈠崟澶у巺")
_test_s_flow_007_create_order_with_dispatch(page, settings, bug_reporter)
_test_s_flow_014_accept_upstream_order_from_hall(page, settings, bug_reporter)
_test_s_flow_021_dispatch_accepted_order_to_player(page, settings, bug_reporter)
_test_s_flow_026_player_accept_dispatched_order(page, settings, bug_reporter)
_test_s_flow_040_player_submit_acceptance(page, settings, bug_reporter)
_test_s_flow_043_accept_player_sub_order(page, settings, bug_reporter)
_test_s_flow_045_apply_upstream_acceptance(page, settings, bug_reporter)
_test_s_flow_046_start_pending_acceptance_aftersale(page, settings, bug_reporter)
_test_s_flow_047_process_pending_acceptance_aftersale(page, settings, bug_reporter)
_test_s_flow_051_start_appeal_from_aftersale_completed(page, settings, bug_reporter)
_test_s_flow_053_reject_main_order_appeal(page, settings, bug_reporter)
from __future__ import annotations
import os
import re
import pytest
from playwright.sync_api import Page
from e2e.pages.wanzhanggui_order_page import WanzhangguiOrderPage
from e2e.utils.account_store import account_store
@pytest.mark.s_flow_050
@pytest.mark.e2e_ledger
@pytest.mark.e2e_chain
def test_s_flow_050_start_appeal_from_completed_order(page: Page, settings, bug_reporter) -> None:
wanzhanggui_phone = os.getenv("E2E_ORDER_WANZHANGGUI_PHONE", settings.wanzhanggui_tenant_phone)
wanzhanggui_password = os.getenv("E2E_ORDER_WANZHANGGUI_PASSWORD", settings.wanzhanggui_tenant_password)
store = account_store()
order = resolve_s_flow_050_order(wanzhanggui_phone)
if not order:
pytest.skip("台账中未找到可供 S-FLOW-050 使用的已完成订单;请先执行 S-FLOW-049。")
source_order_no = order["source_order_no"]
main_order_no = resolve_main_order_no(order)
order_title = order.get("order_title", source_order_no)
appeal_amount = os.getenv("E2E_S_FLOW_APPEAL_AMOUNT", "1")
appeal_reason = f"S-FLOW-050 已完成主单发起申诉 {main_order_no or source_order_no}"
assert order.get("status") == "upstream_completed", f"S-FLOW-050 前置业务状态不满足已完成;order={order}"
assert order.get("current_flow_state") == "wanzhanggui_upstream_acceptance_completed", (
f"S-FLOW-050 前置台账状态不满足上游验收完成;order={order}"
)
assert "S-FLOW-050" in order.get("available_for_cases", []), f"该订单未声明可用于 S-FLOW-050;order={order}"
order_page = WanzhangguiOrderPage(page, settings.tenant_home_url, bug_reporter)
order_page.login_main_account(wanzhanggui_phone, wanzhanggui_password)
bug_reporter.record_if_errors(account=wanzhanggui_phone, location="S-FLOW-050 丸掌柜账号登录")
if not main_order_no:
main_order_no = order_page.find_main_order_no_from_order_list(source_order_no=source_order_no, order_title=order_title)
appeal_reason = f"S-FLOW-050 已完成主单发起申诉 {main_order_no}"
final_text = order_page.start_appeal_for_order(
source_order_no,
order_title,
main_order_no=main_order_no,
reason=appeal_reason,
amount=appeal_amount,
expected_main_status="已完成",
case_label="S-FLOW-050",
)
bug_reporter.record_if_errors(
account=wanzhanggui_phone,
location=f"S-FLOW-050 丸掌柜已完成订单发起主单申诉:{main_order_no or source_order_no}/{order_title}",
)
appeal_record_text = order_page.assert_main_order_appeal_record_visible(
main_order_no=main_order_no,
source_order_no=source_order_no,
order_title=order_title,
reason=appeal_reason,
amount=appeal_amount,
)
bug_reporter.record_if_errors(
account=wanzhanggui_phone,
location=f"S-FLOW-050 丸掌柜主单申诉单记录校验:{main_order_no or source_order_no}/{order_title}",
)
detail_status_text = order_page.assert_order_detail_statuses(
main_order_no=main_order_no,
source_order_no=source_order_no,
order_title=order_title,
expected_main_status="已完成",
expected_sub_status_parts=["已完成", "申诉中"],
expected_appeal_status=["处理中", "申诉中", "待处理"],
)
bug_reporter.record_if_errors(
account=wanzhanggui_phone,
location=f"S-FLOW-050 丸掌柜订单详情申诉中状态校验:{main_order_no or source_order_no}",
)
store.mark_order_consumed_by_flow_case(
source_order_no=source_order_no,
case_id="S-FLOW-050",
main_order_no=main_order_no,
status="appeal_pending_from_aftersale",
current_flow_state="wanzhanggui_appeal_pending_from_aftersale_completed",
wanzhanggui_main_status="已完成",
wanzhanggui_sub_order_status="已完成→申诉中",
appeal_started_by=wanzhanggui_phone,
appeal_reason=appeal_reason,
appeal_amount=appeal_amount,
appeal_started_text=final_text[:1500],
appeal_record_text=appeal_record_text[:1500],
appeal_started_detail_status_text=detail_status_text[:1500],
available_for_cases=["S-FLOW-052", "S-FLOW-053"],
)
def resolve_s_flow_050_order(wanzhanggui_phone: str) -> dict | None:
explicit_main_order_no = os.getenv("E2E_S_FLOW_MAIN_ORDER_NO", "").strip()
explicit_source_order_no = os.getenv("E2E_S_FLOW_SOURCE_ORDER_NO", "").strip()
payload = account_store().load_payload()
if explicit_main_order_no or explicit_source_order_no:
for item in payload.get("dispatched_orders", []):
if wanzhanggui_phone and item.get("wanzhanggui_phone") != wanzhanggui_phone:
continue
if explicit_source_order_no and item.get("source_order_no") != explicit_source_order_no:
continue
if explicit_main_order_no and resolve_main_order_no(item) != explicit_main_order_no:
continue
if item.get("status") == "upstream_completed" and "S-FLOW-050" in item.get("available_for_cases", []):
return item
return None
reusable_orders = [
item
for item in payload.get("dispatched_orders", [])
if (not wanzhanggui_phone or item.get("wanzhanggui_phone") == wanzhanggui_phone)
and item.get("status") == "upstream_completed"
and item.get("current_flow_state") == "wanzhanggui_upstream_acceptance_completed"
and "S-FLOW-050" in item.get("available_for_cases", [])
and "S-FLOW-050" not in item.get("consumed_by_cases", [])
]
if reusable_orders:
return sorted(reusable_orders, key=lambda item: item.get("updated_at", item.get("created_at", "")), reverse=True)[0]
return None
def resolve_main_order_no(order: dict) -> str:
if order.get("main_order_no", "").startswith("BO"):
return order["main_order_no"]
for key in ["wanzhanggui_after_upstream_complete_text", "wanzhanggui_after_upstream_complete_detail_text"]:
value = order.get(key) or ""
match = re.search(r"BO\d{20}", value)
if match:
return match.group(0)
return ""
from __future__ import annotations
from importlib import import_module
import pytest
from playwright.sync_api import Page
from e2e.utils.data_factory import configure_random_percent_dispatch
def _case(module_path: str, function_name: str):
return getattr(import_module(module_path), function_name)
_test_s_flow_007_create_order_with_dispatch = _case(
"e2e.tests.订单状态流转.上游发单.test_s_flow_007_create_order_with_dispatch",
"test_s_flow_007_create_order_with_dispatch",
)
_test_s_flow_014_accept_upstream_order_from_hall = _case(
"e2e.tests.订单状态流转.上游发单.test_s_flow_014_accept_upstream_order_from_hall",
"test_s_flow_014_accept_upstream_order_from_hall",
)
_test_s_flow_021_dispatch_accepted_order_to_player = _case(
"e2e.tests.订单状态流转.上游发单.test_s_flow_021_dispatch_accepted_order_to_player",
"test_s_flow_021_dispatch_accepted_order_to_player",
)
_test_s_flow_026_player_accept_dispatched_order = _case(
"e2e.tests.订单状态流转.上游发单.test_s_flow_026_player_accept_dispatched_order",
"test_s_flow_026_player_accept_dispatched_order",
)
_test_s_flow_040_player_submit_acceptance = _case(
"e2e.tests.订单状态流转.订单验收.test_s_flow_040_player_submit_acceptance",
"test_s_flow_040_player_submit_acceptance",
)
_test_s_flow_043_accept_player_sub_order = _case(
"e2e.tests.订单状态流转.订单验收.test_s_flow_043_accept_player_sub_order",
"test_s_flow_043_accept_player_sub_order",
)
_test_s_flow_045_apply_upstream_acceptance = _case(
"e2e.tests.订单状态流转.订单验收.test_s_flow_045_apply_upstream_acceptance",
"test_s_flow_045_apply_upstream_acceptance",
)
_test_s_flow_049_complete_upstream_acceptance = _case(
"e2e.tests.订单状态流转.订单验收.test_s_flow_049_complete_upstream_acceptance",
"test_s_flow_049_complete_upstream_acceptance",
)
_test_s_flow_054_start_penalty_from_accepted_sub_order = _case(
"e2e.tests.订单状态流转.处罚追缴.test_s_flow_054_start_penalty_from_accepted_sub_order",
"test_s_flow_054_start_penalty_from_accepted_sub_order",
)
_test_s_flow_055_approve_penalty_from_accepted_sub_order = _case(
"e2e.tests.订单状态流转.处罚追缴.test_s_flow_055_approve_penalty_from_accepted_sub_order",
"test_s_flow_055_approve_penalty_from_accepted_sub_order",
)
def _configure_new_percent_dispatch(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setenv("E2E_S_FLOW_CREATE_MODE", "create")
monkeypatch.delenv("E2E_S_FLOW_MAIN_ORDER_NO", raising=False)
monkeypatch.delenv("E2E_S_FLOW_SOURCE_ORDER_NO", raising=False)
monkeypatch.delenv("E2E_S_FLOW_ORDER_TITLE", raising=False)
configure_random_percent_dispatch(monkeypatch)
monkeypatch.setenv("E2E_TASK_ASSIGNMENT_METHOD", "抢单大厅")
@pytest.mark.flow_045
@pytest.mark.e2e_ledger
@pytest.mark.e2e_chain
def test_flow_045_accepted_sub_order_penalty_approve_chain(page: Page, settings, bug_reporter, monkeypatch) -> None:
_configure_new_percent_dispatch(monkeypatch)
_test_s_flow_007_create_order_with_dispatch(page, settings, bug_reporter)
_test_s_flow_014_accept_upstream_order_from_hall(page, settings, bug_reporter)
_test_s_flow_021_dispatch_accepted_order_to_player(page, settings, bug_reporter)
_test_s_flow_026_player_accept_dispatched_order(page, settings, bug_reporter)
_test_s_flow_040_player_submit_acceptance(page, settings, bug_reporter)
_test_s_flow_043_accept_player_sub_order(page, settings, bug_reporter)
_test_s_flow_045_apply_upstream_acceptance(page, settings, bug_reporter)
_test_s_flow_049_complete_upstream_acceptance(page, settings, bug_reporter)
_test_s_flow_054_start_penalty_from_accepted_sub_order(page, settings, bug_reporter)
_test_s_flow_055_approve_penalty_from_accepted_sub_order(page, settings, bug_reporter)
from __future__ import annotations
from importlib import import_module
import pytest
from playwright.sync_api import Page
from e2e.utils.data_factory import configure_random_percent_dispatch
def _case(module_path: str, function_name: str):
return getattr(import_module(module_path), function_name)
_test_s_flow_007_create_order_with_dispatch = _case(
"e2e.tests.订单状态流转.上游发单.test_s_flow_007_create_order_with_dispatch",
"test_s_flow_007_create_order_with_dispatch",
)
_test_s_flow_014_accept_upstream_order_from_hall = _case(
"e2e.tests.订单状态流转.上游发单.test_s_flow_014_accept_upstream_order_from_hall",
"test_s_flow_014_accept_upstream_order_from_hall",
)
_test_s_flow_021_dispatch_accepted_order_to_player = _case(
"e2e.tests.订单状态流转.上游发单.test_s_flow_021_dispatch_accepted_order_to_player",
"test_s_flow_021_dispatch_accepted_order_to_player",
)
_test_s_flow_026_player_accept_dispatched_order = _case(
"e2e.tests.订单状态流转.上游发单.test_s_flow_026_player_accept_dispatched_order",
"test_s_flow_026_player_accept_dispatched_order",
)
_test_s_flow_040_player_submit_acceptance = _case(
"e2e.tests.订单状态流转.订单验收.test_s_flow_040_player_submit_acceptance",
"test_s_flow_040_player_submit_acceptance",
)
_test_s_flow_043_accept_player_sub_order = _case(
"e2e.tests.订单状态流转.订单验收.test_s_flow_043_accept_player_sub_order",
"test_s_flow_043_accept_player_sub_order",
)
_test_s_flow_045_apply_upstream_acceptance = _case(
"e2e.tests.订单状态流转.订单验收.test_s_flow_045_apply_upstream_acceptance",
"test_s_flow_045_apply_upstream_acceptance",
)
_test_s_flow_049_complete_upstream_acceptance = _case(
"e2e.tests.订单状态流转.订单验收.test_s_flow_049_complete_upstream_acceptance",
"test_s_flow_049_complete_upstream_acceptance",
)
_test_s_flow_054_start_penalty_from_accepted_sub_order = _case(
"e2e.tests.订单状态流转.处罚追缴.test_s_flow_054_start_penalty_from_accepted_sub_order",
"test_s_flow_054_start_penalty_from_accepted_sub_order",
)
_test_s_flow_056_reject_penalty_from_accepted_sub_order = _case(
"e2e.tests.订单状态流转.处罚追缴.test_s_flow_056_reject_penalty_from_accepted_sub_order",
"test_s_flow_056_reject_penalty_from_accepted_sub_order",
)
def _configure_new_percent_dispatch(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setenv("E2E_S_FLOW_CREATE_MODE", "create")
monkeypatch.delenv("E2E_S_FLOW_MAIN_ORDER_NO", raising=False)
monkeypatch.delenv("E2E_S_FLOW_SOURCE_ORDER_NO", raising=False)
monkeypatch.delenv("E2E_S_FLOW_ORDER_TITLE", raising=False)
configure_random_percent_dispatch(monkeypatch)
monkeypatch.setenv("E2E_TASK_ASSIGNMENT_METHOD", "抢单大厅")
@pytest.mark.flow_047
@pytest.mark.e2e_ledger
@pytest.mark.e2e_chain
def test_flow_047_accepted_sub_order_penalty_reject_chain(page: Page, settings, bug_reporter, monkeypatch) -> None:
_configure_new_percent_dispatch(monkeypatch)
_test_s_flow_007_create_order_with_dispatch(page, settings, bug_reporter)
_test_s_flow_014_accept_upstream_order_from_hall(page, settings, bug_reporter)
_test_s_flow_021_dispatch_accepted_order_to_player(page, settings, bug_reporter)
_test_s_flow_026_player_accept_dispatched_order(page, settings, bug_reporter)
_test_s_flow_040_player_submit_acceptance(page, settings, bug_reporter)
_test_s_flow_043_accept_player_sub_order(page, settings, bug_reporter)
_test_s_flow_045_apply_upstream_acceptance(page, settings, bug_reporter)
_test_s_flow_049_complete_upstream_acceptance(page, settings, bug_reporter)
_test_s_flow_054_start_penalty_from_accepted_sub_order(page, settings, bug_reporter)
_test_s_flow_056_reject_penalty_from_accepted_sub_order(page, settings, bug_reporter)
from __future__ import annotations
import os
import pytest
from playwright.sync_api import Page
from e2e.pages.wanzhanggui_order_page import WanzhangguiOrderPage
from e2e.utils.account_store import account_store
@pytest.mark.s_flow_054
@pytest.mark.e2e_ledger
@pytest.mark.e2e_chain
def test_s_flow_054_start_penalty_from_accepted_sub_order(page: Page, settings, bug_reporter) -> None:
wanzhanggui_phone = os.getenv("E2E_ORDER_WANZHANGGUI_PHONE", settings.wanzhanggui_tenant_phone)
wanzhanggui_password = os.getenv("E2E_ORDER_WANZHANGGUI_PASSWORD", settings.wanzhanggui_tenant_password)
store = account_store()
order = resolve_s_flow_054_order(wanzhanggui_phone)
if not order:
pytest.skip("台账中未找到可供 S-FLOW-054 使用的上游验收完成订单;请先执行 S-FLOW-049。")
source_order_no = order["source_order_no"]
main_order_no = order.get("main_order_no", "")
order_title = order.get("order_title", source_order_no)
amount = os.getenv("E2E_S_FLOW_PENALTY_AMOUNT", "1")
reason = f"S-FLOW-054 对验收成功子单发起处罚 {main_order_no or source_order_no}"
assert order.get("status") == "upstream_completed", f"S-FLOW-054 前置业务状态不满足已完成;order={order}"
assert order.get("current_flow_state") == "wanzhanggui_upstream_acceptance_completed", (
f"S-FLOW-054 前置台账状态不满足上游验收完成;order={order}"
)
order_page = WanzhangguiOrderPage(page, settings.tenant_home_url, bug_reporter)
order_page.login_main_account(wanzhanggui_phone, wanzhanggui_password)
bug_reporter.record_if_errors(account=wanzhanggui_phone, location="S-FLOW-054 丸掌柜账号登录")
before_detail_status_text = order_page.assert_order_detail_statuses(
main_order_no=main_order_no,
source_order_no=source_order_no,
order_title=order_title,
expected_main_status=["已完成", "待验收", "完成"],
expected_sub_status=["已完成", "验收成功", "服务中", "代练中"],
expected_sub_order_list_status=["验收成功", "已完成"],
expected_aftersale_absent=True,
)
assert "处罚" in before_detail_status_text, (
f"S-FLOW-054 前置验收成功子单未展示处罚入口;main_order_no={main_order_no}; "
f"source_order_no={source_order_no}; detail={before_detail_status_text[:1500]}"
)
bug_reporter.record_if_errors(
account=wanzhanggui_phone,
location=f"S-FLOW-054 丸掌柜发起处罚前验收成功状态校验:{main_order_no or source_order_no}",
)
order_page.start_penalty_for_accepted_sub_order(
main_order_no=main_order_no,
source_order_no=source_order_no,
order_title=order_title,
reason=reason,
amount=amount,
)
penalty_record_text = order_page.assert_sub_order_penalty_record_visible(
main_order_no=main_order_no,
source_order_no=source_order_no,
order_title=order_title,
expected_status_keywords=["处理中", "待处理", "申诉中", "待赔偿", "待审核", "去处理"],
)
bug_reporter.record_if_errors(
account=wanzhanggui_phone,
location=f"S-FLOW-054 丸掌柜子单处罚单回读处罚记录:{main_order_no or source_order_no}",
)
store.mark_order_consumed_by_flow_case(
source_order_no=source_order_no,
case_id="S-FLOW-054",
status="penalty_pending_from_accepted_sub_order",
current_flow_state="wanzhanggui_penalty_pending_from_accepted_sub_order",
penalty_started_by=wanzhanggui_phone,
penalty_reason=reason,
penalty_amount=amount,
accepted_sub_order_penalty_before_detail_text=before_detail_status_text[:1500],
accepted_sub_order_penalty_started_text=penalty_record_text[:1500],
sub_order_penalty_record_text=penalty_record_text[:1500],
available_for_cases=["S-FLOW-055", "S-FLOW-056"],
)
def resolve_s_flow_054_order(wanzhanggui_phone: str) -> dict | None:
explicit_main_order_no = os.getenv("E2E_S_FLOW_MAIN_ORDER_NO", "").strip()
explicit_source_order_no = os.getenv("E2E_S_FLOW_SOURCE_ORDER_NO", "").strip()
payload = account_store().load_payload()
if explicit_main_order_no or explicit_source_order_no:
for item in payload.get("dispatched_orders", []):
if wanzhanggui_phone and item.get("wanzhanggui_phone") != wanzhanggui_phone:
continue
if explicit_main_order_no and item.get("main_order_no") != explicit_main_order_no:
continue
if explicit_source_order_no and item.get("source_order_no") != explicit_source_order_no:
continue
return item
return None
candidate = account_store().find_order_for_flow_case(
case_id="S-FLOW-054",
wanzhanggui_phone=wanzhanggui_phone,
current_flow_state="wanzhanggui_upstream_acceptance_completed",
)
if candidate:
return candidate
candidates = [
item
for item in payload.get("dispatched_orders", [])
if (not wanzhanggui_phone or item.get("wanzhanggui_phone") == wanzhanggui_phone)
and item.get("status") == "upstream_completed"
and "S-FLOW-054" in item.get("available_for_cases", [])
and "S-FLOW-054" not in item.get("consumed_by_cases", [])
]
return sorted(candidates, key=lambda item: item.get("updated_at", item.get("created_at", "")), reverse=True)[0] if candidates else None
from __future__ import annotations
import pytest
from playwright.sync_api import Page
from e2e.tests.订单状态流转.处罚追缴.penalty_case_helpers import run_penalty_approve_case
@pytest.mark.s_flow_055
@pytest.mark.e2e_ledger
@pytest.mark.e2e_chain
def test_s_flow_055_approve_penalty_from_accepted_sub_order(page: Page, settings, bug_reporter) -> None:
run_penalty_approve_case(
page,
settings,
bug_reporter,
case_id="S-FLOW-055",
prerequisite_case_id="S-FLOW-054",
pending_flow_state="wanzhanggui_penalty_pending_from_accepted_sub_order",
final_status="penalty_approved_from_accepted_sub_order",
final_flow_state="wanyouji_penalty_approved_from_accepted_sub_order",
remark_prefix="丸友集同意验收成功子单处罚赔偿",
approved_text_field="accepted_sub_order_penalty_approved_text",
approved_record_field="accepted_sub_order_penalty_approved_record_text",
)
from __future__ import annotations
import pytest
from playwright.sync_api import Page
from e2e.tests.订单状态流转.处罚追缴.penalty_case_helpers import run_penalty_reject_case
@pytest.mark.s_flow_056
@pytest.mark.e2e_ledger
@pytest.mark.e2e_chain
def test_s_flow_056_reject_penalty_from_accepted_sub_order(page: Page, settings, bug_reporter) -> None:
run_penalty_reject_case(
page,
settings,
bug_reporter,
case_id="S-FLOW-056",
prerequisite_case_id="S-FLOW-054",
pending_flow_state="wanzhanggui_penalty_pending_from_accepted_sub_order",
final_status="penalty_rejected_from_accepted_sub_order",
final_flow_state="wanyouji_penalty_rejected_from_accepted_sub_order",
remark_prefix="丸友集驳回验收成功子单处罚",
rejected_text_field="accepted_sub_order_penalty_rejected_text",
rejected_record_field="accepted_sub_order_penalty_rejected_record_text",
)
...@@ -3,6 +3,8 @@ from __future__ import annotations ...@@ -3,6 +3,8 @@ from __future__ import annotations
import pytest import pytest
from playwright.sync_api import Page from playwright.sync_api import Page
from e2e.utils.data_factory import configure_random_percent_dispatch
from e2e.tests.订单状态流转.上游发单.test_s_flow_007_create_order_with_dispatch import ( from e2e.tests.订单状态流转.上游发单.test_s_flow_007_create_order_with_dispatch import (
test_s_flow_007_create_order_with_dispatch as _test_s_flow_007_create_order_with_dispatch, test_s_flow_007_create_order_with_dispatch as _test_s_flow_007_create_order_with_dispatch,
) )
...@@ -30,10 +32,7 @@ def test_flow_022_assigned_player_cancel_approve_chain(page: Page, settings, bug ...@@ -30,10 +32,7 @@ def test_flow_022_assigned_player_cancel_approve_chain(page: Page, settings, bug
monkeypatch.setenv("E2E_S_FLOW_CREATE_MODE", "create") monkeypatch.setenv("E2E_S_FLOW_CREATE_MODE", "create")
monkeypatch.delenv("E2E_S_FLOW_MAIN_ORDER_NO", raising=False) monkeypatch.delenv("E2E_S_FLOW_MAIN_ORDER_NO", raising=False)
monkeypatch.delenv("E2E_S_FLOW_SOURCE_ORDER_NO", raising=False) monkeypatch.delenv("E2E_S_FLOW_SOURCE_ORDER_NO", raising=False)
monkeypatch.setenv("E2E_S_FLOW_DISPATCH_PRICE_MODE", "percent") configure_random_percent_dispatch(monkeypatch)
monkeypatch.setenv("E2E_S_FLOW_DISPATCH_PRICE_PERCENT", "90")
monkeypatch.setenv("E2E_TASK_DISPATCH_PRICE_MODE", "percent")
monkeypatch.setenv("E2E_TASK_DISPATCH_PRICE_PERCENT", "90")
monkeypatch.setenv("E2E_TASK_ASSIGNMENT_METHOD", "抢单大厅") monkeypatch.setenv("E2E_TASK_ASSIGNMENT_METHOD", "抢单大厅")
_test_s_flow_007_create_order_with_dispatch(page, settings, bug_reporter) _test_s_flow_007_create_order_with_dispatch(page, settings, bug_reporter)
......
...@@ -3,6 +3,8 @@ from __future__ import annotations ...@@ -3,6 +3,8 @@ from __future__ import annotations
import pytest import pytest
from playwright.sync_api import Page from playwright.sync_api import Page
from e2e.utils.data_factory import configure_random_percent_dispatch
from e2e.tests.订单状态流转.上游发单.test_s_flow_007_create_order_with_dispatch import ( from e2e.tests.订单状态流转.上游发单.test_s_flow_007_create_order_with_dispatch import (
test_s_flow_007_create_order_with_dispatch as _test_s_flow_007_create_order_with_dispatch, test_s_flow_007_create_order_with_dispatch as _test_s_flow_007_create_order_with_dispatch,
) )
...@@ -37,10 +39,7 @@ def test_flow_044_cancel_upstream_acceptance_chain(page: Page, settings, bug_rep ...@@ -37,10 +39,7 @@ def test_flow_044_cancel_upstream_acceptance_chain(page: Page, settings, bug_rep
monkeypatch.delenv("E2E_S_FLOW_MAIN_ORDER_NO", raising=False) monkeypatch.delenv("E2E_S_FLOW_MAIN_ORDER_NO", raising=False)
monkeypatch.delenv("E2E_S_FLOW_SOURCE_ORDER_NO", raising=False) monkeypatch.delenv("E2E_S_FLOW_SOURCE_ORDER_NO", raising=False)
monkeypatch.delenv("E2E_S_FLOW_ORDER_TITLE", raising=False) monkeypatch.delenv("E2E_S_FLOW_ORDER_TITLE", raising=False)
monkeypatch.setenv("E2E_S_FLOW_DISPATCH_PRICE_MODE", "percent") configure_random_percent_dispatch(monkeypatch)
monkeypatch.setenv("E2E_S_FLOW_DISPATCH_PRICE_PERCENT", "90")
monkeypatch.setenv("E2E_TASK_DISPATCH_PRICE_MODE", "percent")
monkeypatch.setenv("E2E_TASK_DISPATCH_PRICE_PERCENT", "90")
monkeypatch.setenv("E2E_TASK_ASSIGNMENT_METHOD", "抢单大厅") monkeypatch.setenv("E2E_TASK_ASSIGNMENT_METHOD", "抢单大厅")
_test_s_flow_007_create_order_with_dispatch(page, settings, bug_reporter) _test_s_flow_007_create_order_with_dispatch(page, settings, bug_reporter)
......
...@@ -46,15 +46,19 @@ def test_s_flow_043_accept_player_sub_order(page: Page, settings, bug_reporter) ...@@ -46,15 +46,19 @@ def test_s_flow_043_accept_player_sub_order(page: Page, settings, bug_reporter)
source_order_no, source_order_no,
order_title, order_title,
) )
assert any(key in before_text for key in ["待验收", "验: 1", "验:1"]), ( already_accepted = any(key in before_text for key in ["已完成", "完成", "待结算", "结算", "完:"])
assert already_accepted or any(key in before_text for key in ["待验收", "验: 1", "验:1"]), (
f"S-FLOW-043 前置不满足打手子单待验收;source_order_no={source_order_no}; text={before_text[:1500]}" f"S-FLOW-043 前置不满足打手子单待验收;source_order_no={source_order_no}; text={before_text[:1500]}"
) )
accepted_text = fulfillment_page.accept_sub_order_and_verify_ready_for_upstream( if already_accepted:
source_order_no, accepted_text = before_text
order_title, else:
frame=wanyouji_frame, accepted_text = fulfillment_page.accept_sub_order_and_verify_ready_for_upstream(
current_text=before_text, source_order_no,
) order_title,
frame=wanyouji_frame,
current_text=before_text,
)
bug_reporter.record_if_errors( bug_reporter.record_if_errors(
account=wanyouji_account.phone, account=wanyouji_account.phone,
location=f"S-FLOW-043 丸友集同意验收打手子单:{main_order_no or source_order_no}", location=f"S-FLOW-043 丸友集同意验收打手子单:{main_order_no or source_order_no}",
...@@ -75,9 +79,9 @@ def test_s_flow_043_accept_player_sub_order(page: Page, settings, bug_reporter) ...@@ -75,9 +79,9 @@ def test_s_flow_043_accept_player_sub_order(page: Page, settings, bug_reporter)
main_order_no=main_order_no, main_order_no=main_order_no,
source_order_no=source_order_no, source_order_no=source_order_no,
order_title=order_title, order_title=order_title,
expected_main_status=["服务中", "代练中", "待验收"], expected_main_status=["服务中", "代练中", "待验收", "已完成"],
expected_sub_status=["服务中", "代练中"], expected_sub_status=["服务中", "代练中", "已完成", "申诉完成", "申诉中"],
expected_sub_order_list_status=["已完成", "服务中", "代练中"], expected_sub_order_list_status=["已完成", "验收成功", "服务中", "代练中"],
expected_aftersale_absent=True, expected_aftersale_absent=True,
) )
bug_reporter.record_if_errors( bug_reporter.record_if_errors(
...@@ -85,23 +89,33 @@ def test_s_flow_043_accept_player_sub_order(page: Page, settings, bug_reporter) ...@@ -85,23 +89,33 @@ def test_s_flow_043_accept_player_sub_order(page: Page, settings, bug_reporter)
location=f"S-FLOW-043 丸掌柜订单详情仍服务中校验:{main_order_no or source_order_no}", location=f"S-FLOW-043 丸掌柜订单详情仍服务中校验:{main_order_no or source_order_no}",
) )
consumed_cases = set(order.get("consumed_by_cases", []))
downstream_already_consumed = bool(consumed_cases.intersection({"S-FLOW-045", "S-FLOW-049", "S-FLOW-050", "S-FLOW-052", "S-FLOW-053"}))
updates = {
"task_player_phone": player_account.phone,
"task_wanyouji_phone": wanyouji_account.phone,
"sub_order_accepted_by": wanyouji_account.phone,
"settlement_verified_by": player_account.phone,
"expected_player_amount": expected_amount,
"wanyouji_before_sub_order_accept_text": before_text[:1500],
"wanyouji_after_sub_order_accept_text": accepted_text[:1500],
"player_income_after_sub_order_accept_text": income_text[:1500] if isinstance(income_text, str) else "",
"wanzhanggui_after_sub_order_accept_detail_text": detail_text[:1500],
"player_order_status": "已完成",
"wanyouji_sub_order_status": "已完成",
}
if not downstream_already_consumed:
updates.update(
{
"status": "sub_order_income_verified",
"current_flow_state": "player_income_ready_for_settlement",
"wanzhanggui_main_status": "服务中",
"wanzhanggui_sub_order_status": "代练中",
"available_for_cases": ["S-FLOW-080", "S-FLOW-045", "S-FLOW-054"],
}
)
store.mark_order_consumed_by_flow_case( store.mark_order_consumed_by_flow_case(
source_order_no=source_order_no, source_order_no=source_order_no,
case_id="S-FLOW-043", case_id="S-FLOW-043",
status="sub_order_income_verified", **updates,
current_flow_state="player_income_ready_for_settlement",
task_player_phone=player_account.phone,
task_wanyouji_phone=wanyouji_account.phone,
sub_order_accepted_by=wanyouji_account.phone,
settlement_verified_by=player_account.phone,
expected_player_amount=expected_amount,
wanyouji_before_sub_order_accept_text=before_text[:1500],
wanyouji_after_sub_order_accept_text=accepted_text[:1500],
player_income_after_sub_order_accept_text=income_text[:1500] if isinstance(income_text, str) else "",
wanzhanggui_after_sub_order_accept_detail_text=detail_text[:1500],
player_order_status="已完成",
wanyouji_sub_order_status="已完成",
wanzhanggui_main_status="服务中",
wanzhanggui_sub_order_status="代练中",
available_for_cases=["S-FLOW-080", "S-FLOW-045", "S-FLOW-054"],
) )
...@@ -3,6 +3,13 @@ from __future__ import annotations ...@@ -3,6 +3,13 @@ from __future__ import annotations
import random import random
from dataclasses import dataclass from dataclasses import dataclass
from datetime import datetime from datetime import datetime
import os
MANUAL_ORDER_RECEIVE_PRICE_MIN = 10
MANUAL_ORDER_RECEIVE_PRICE_MAX = 30
DISPATCH_PRICE_PERCENT_MIN = 60
DISPATCH_PRICE_PERCENT_MAX = 90
@dataclass(frozen=True) @dataclass(frozen=True)
...@@ -20,6 +27,48 @@ def random_mainland_phone(prefix: str = "19") -> str: ...@@ -20,6 +27,48 @@ def random_mainland_phone(prefix: str = "19") -> str:
return clean_prefix + "".join(str(random.randint(0, 9)) for _ in range(remaining)) return clean_prefix + "".join(str(random.randint(0, 9)) for _ in range(remaining))
def manual_order_receive_price() -> str:
explicit = os.getenv("E2E_MANUAL_ORDER_RECEIVE_PRICE", "").strip()
if explicit:
value = int(explicit)
assert MANUAL_ORDER_RECEIVE_PRICE_MIN <= value <= MANUAL_ORDER_RECEIVE_PRICE_MAX, (
"E2E_MANUAL_ORDER_RECEIVE_PRICE must be between "
f"{MANUAL_ORDER_RECEIVE_PRICE_MIN} and {MANUAL_ORDER_RECEIVE_PRICE_MAX}, got {value}"
)
return str(value)
return str(random.randint(MANUAL_ORDER_RECEIVE_PRICE_MIN, MANUAL_ORDER_RECEIVE_PRICE_MAX))
def random_dispatch_price_percent() -> str:
explicit = os.getenv("E2E_DISPATCH_PRICE_PERCENT", "").strip()
if explicit:
value = int(explicit)
assert DISPATCH_PRICE_PERCENT_MIN <= value <= DISPATCH_PRICE_PERCENT_MAX, (
"E2E_DISPATCH_PRICE_PERCENT must be between "
f"{DISPATCH_PRICE_PERCENT_MIN} and {DISPATCH_PRICE_PERCENT_MAX}, got {value}"
)
return str(value)
return str(random.randint(DISPATCH_PRICE_PERCENT_MIN, DISPATCH_PRICE_PERCENT_MAX))
def default_dispatch_price_percent() -> str:
existing = os.getenv("E2E_GENERATED_DISPATCH_PRICE_PERCENT", "").strip()
if existing:
return existing
percent = random_dispatch_price_percent()
os.environ["E2E_GENERATED_DISPATCH_PRICE_PERCENT"] = percent
return percent
def configure_random_percent_dispatch(monkeypatch) -> str:
percent = random_dispatch_price_percent()
monkeypatch.setenv("E2E_S_FLOW_DISPATCH_PRICE_MODE", "percent")
monkeypatch.setenv("E2E_S_FLOW_DISPATCH_PRICE_PERCENT", percent)
monkeypatch.setenv("E2E_TASK_DISPATCH_PRICE_MODE", "percent")
monkeypatch.setenv("E2E_TASK_DISPATCH_PRICE_PERCENT", percent)
return percent
def enterprise_data(password: str, prefix: str, enterprise_prefix: str) -> EnterpriseRegistrationData: def enterprise_data(password: str, prefix: str, enterprise_prefix: str) -> EnterpriseRegistrationData:
suffix = datetime.now().strftime("%m%d%H%M%S") + str(random.randint(100, 999)) suffix = datetime.now().strftime("%m%d%H%M%S") + str(random.randint(100, 999))
return EnterpriseRegistrationData( return EnterpriseRegistrationData(
......
...@@ -70,10 +70,25 @@ markers = ...@@ -70,10 +70,25 @@ markers =
s_flow_081: order status flow S-FLOW-081 Wanyouji completes player settlement before continuing dispatch s_flow_081: order status flow S-FLOW-081 Wanyouji completes player settlement before continuing dispatch
s_flow_044: order status flow S-FLOW-044 Wanyouji continues dispatch after completed child s_flow_044: order status flow S-FLOW-044 Wanyouji continues dispatch after completed child
s_flow_045: order status flow S-FLOW-045 Wanyouji applies upstream acceptance s_flow_045: order status flow S-FLOW-045 Wanyouji applies upstream acceptance
s_flow_046: order status flow S-FLOW-046 Wanzhanggui starts main-order aftersale during pending acceptance
s_flow_047: order status flow S-FLOW-047 Wanzhanggui confirms pending-acceptance main-order aftersale
s_flow_048: order status flow S-FLOW-048 Wanzhanggui cancels pending-acceptance aftersale
s_flow_082: order status flow S-FLOW-082 Wanyouji cancels upstream acceptance and both sides verify status s_flow_082: order status flow S-FLOW-082 Wanyouji cancels upstream acceptance and both sides verify status
s_flow_049: order status flow S-FLOW-049 Wanzhanggui completes upstream acceptance s_flow_049: order status flow S-FLOW-049 Wanzhanggui completes upstream acceptance
flow_044: order status flow FLOW-044 Wanyouji applies upstream acceptance then cancels it flow_044: order status flow FLOW-044 Wanyouji applies upstream acceptance then cancels it
s_flow_050: order status flow S-FLOW-050 Wanzhanggui starts appeal from completed order
s_flow_054: order status flow S-FLOW-054 Wanzhanggui starts penalty from accepted sub-order
s_flow_055: order status flow S-FLOW-055 Wanyouji approves penalty from accepted sub-order
s_flow_056: order status flow S-FLOW-056 Wanyouji rejects penalty from accepted sub-order
flow_026: order status flow FLOW-026 completed order appeal approved chain
flow_027: order status flow FLOW-027 completed order appeal rejected chain
flow_028: order status flow FLOW-028 pending-acceptance aftersale completed then appeal approved chain
flow_029: order status flow FLOW-029 pending-acceptance aftersale completed then appeal rejected chain
flow_030: order status flow FLOW-030 pending-acceptance aftersale canceled then abnormal sub-order aftersale started
flow_045: order status flow FLOW-045 accepted sub-order penalty approved
flow_047: order status flow FLOW-047 accepted sub-order penalty rejected
s_flow_034: order status flow S-FLOW-034 Wanzhanggui starts row-level aftersale for abnormal sub-order s_flow_034: order status flow S-FLOW-034 Wanzhanggui starts row-level aftersale for abnormal sub-order
s_flow_083: order status flow S-FLOW-083 Wanzhanggui cancels abnormal sub-order aftersale
s_flow_035: order status flow S-FLOW-035 Wanyouji approves abnormal sub-order aftersale s_flow_035: order status flow S-FLOW-035 Wanyouji approves abnormal sub-order aftersale
s_flow_036: order status flow S-FLOW-036 Wanyouji rejects abnormal sub-order aftersale s_flow_036: order status flow S-FLOW-036 Wanyouji rejects abnormal sub-order aftersale
s_flow_057: order status flow S-FLOW-057 Wanzhanggui continues dispatch after rejected sub-order s_flow_057: order status flow S-FLOW-057 Wanzhanggui continues dispatch after rejected sub-order
......
This diff is collapsed.
...@@ -10,8 +10,8 @@ from openpyxl.utils import get_column_letter ...@@ -10,8 +10,8 @@ from openpyxl.utils import get_column_letter
ROOT = Path(__file__).resolve().parents[1] ROOT = Path(__file__).resolve().parents[1]
SOURCE = ROOT / "SaaS_E2E测试用例_单Sheet用例测试列表.xlsx" SOURCE = ROOT / "测试用例" / "SaaS_E2E测试用例_单Sheet用例测试列表.xlsx"
OUTPUT = ROOT / "订单状态流转_链路流程线整理.xlsx" OUTPUT = ROOT / "测试用例" / "订单状态流转_链路流程线整理.xlsx"
CASE_SHEET = "订单状态流转" CASE_SHEET = "订单状态流转"
...@@ -390,13 +390,23 @@ def build_flows() -> list[dict[str, object]]: ...@@ -390,13 +390,23 @@ def build_flows() -> list[dict[str, object]]:
), ),
flow( flow(
"FLOW-030", "FLOW-030",
"待验收阶段-发起售后-取消售后-丸掌柜同意验收-已完成申诉", "待验收阶段-发起售后-取消售后-丸掌柜同意验收-验收成功子单处罚同意赔偿",
["S-FLOW-007", "S-FLOW-014", "S-FLOW-021", "S-FLOW-026", "S-FLOW-040", "S-FLOW-043", "S-FLOW-045", "S-FLOW-046", "S-FLOW-048", "S-FLOW-049", "S-FLOW-050"], ["S-FLOW-007", "S-FLOW-014", "S-FLOW-021", "S-FLOW-026", "S-FLOW-040", "S-FLOW-043", "S-FLOW-045", "S-FLOW-046", "S-FLOW-048", "S-FLOW-049", "S-FLOW-054", "S-FLOW-055"],
"主单待验收;子单待验收", "主单待验收;子单待验收",
"已完成;可发起已完成申诉", "已完成;验收成功子单处罚同意赔偿",
"待验收售后取消后回归验收", "待验收售后取消后回归验收 + 处罚追缴正向",
"验证待验收阶段售后取消后恢复验收前状态,再走丸掌柜同意验收。", "验证待验收阶段售后取消后恢复验收前状态,再走丸掌柜同意验收,并对子单验收成功状态发起处罚追缴。",
"取消售后后同一订单可继续普通验收。", "取消售后后同一订单可继续普通验收;丸掌柜同意上游验收后,子单列表验收成功状态可衔接 S-FLOW-054,再由 S-FLOW-055 覆盖同意赔偿分支。",
),
flow(
"FLOW-045",
"待验收阶段-发起售后-取消售后-丸掌柜同意验收-验收成功子单处罚驳回",
["S-FLOW-007", "S-FLOW-014", "S-FLOW-021", "S-FLOW-026", "S-FLOW-040", "S-FLOW-043", "S-FLOW-045", "S-FLOW-046", "S-FLOW-048", "S-FLOW-049", "S-FLOW-054", "S-FLOW-056"],
"主单待验收;子单待验收",
"已完成;验收成功子单处罚驳回",
"待验收售后取消后回归验收 + 处罚追缴逆向",
"验证待验收阶段售后取消后恢复验收前状态,再走丸掌柜同意验收,并对子单验收成功状态发起处罚追缴后由丸友集驳回。",
"与 FLOW-030 使用不同订单;S-FLOW-049 后同样先接 S-FLOW-054,再由 S-FLOW-056 覆盖驳回处罚分支。",
), ),
] ]
......
This diff is collapsed.
This diff is collapsed.
from __future__ import annotations
from copy import copy
from datetime import datetime
from pathlib import Path
import sys
import openpyxl
ROOT = Path(__file__).resolve().parents[1]
if str(ROOT) not in sys.path:
sys.path.insert(0, str(ROOT))
CASE_BOOK = ROOT / "测试用例" / "SaaS_E2E测试用例_单Sheet用例测试列表.xlsx"
FLOW_BOOK = ROOT / "测试用例" / "订单状态流转_链路流程线整理.xlsx"
CASE_UPDATES = {
"S-FLOW-054": {
"跨端/状态断言": "1. 处罚记录的主单号、来源单号、子单号、责任方、金额在丸掌柜侧一致。\n"
"2. 处罚只作用于目标子单,不误影响同主单其他子单。\n"
"3. 后续由 S-FLOW-055 或 S-FLOW-056 验证丸友集处理结果。",
"可组合/被引用关系": "承接 S-FLOW-049 后的已验收/验收成功子单;后续分支为 S-FLOW-055/S-FLOW-056;被 FLOW-030、FLOW-045 引用。",
"自动化状态": "已自动化",
"备注": "2026-07-02 修正:S-FLOW-049 后接验收成功子单处罚,后续分支为 S-FLOW-055/S-FLOW-056。",
},
"S-FLOW-055": {
"用例标题": "【丸友集】验收成功子单处罚-同意赔偿",
"预期结果": "1. 丸友集侧目标处罚/申诉记录处理完成或显示已完成/已通过。\n"
"2. 处罚金额等于申请处罚金额。\n"
"3. 丸掌柜侧子单处罚单同步为已完成或同意赔偿结果。",
"跨端/状态断言": "丸友集申诉工单、丸掌柜子单处罚单按丸掌柜主单号/来源单号可定位同一记录;处理状态和金额一致。",
"可组合/被引用关系": "承接 S-FLOW-054;FLOW-030 处罚同意赔偿分支引用。",
"自动化状态": "已自动化",
"备注": "2026-07-02 修正:作为 S-FLOW-054 后续同意赔偿分支,不再仅作为 S-FLOW-063 别名。",
},
"S-FLOW-056": {
"用例标题": "【丸友集】验收成功子单处罚-驳回处罚",
"预期结果": "1. 丸友集侧目标处罚/申诉记录处理完成或显示已拒绝/已驳回。\n"
"2. 处罚金额为0或页面对应未赔偿结果。\n"
"3. 丸掌柜侧子单处罚单同步为已拒绝或驳回结果。",
"跨端/状态断言": "丸友集申诉工单、丸掌柜子单处罚单按丸掌柜主单号/来源单号可定位同一记录;驳回状态和金额结果一致。",
"可组合/被引用关系": "承接 S-FLOW-054;FLOW-045 处罚驳回分支引用。",
"自动化状态": "已自动化",
"备注": "2026-07-02 修正:作为 S-FLOW-054 后续驳回处罚分支,不再仅作为 S-FLOW-064 别名。",
},
}
def headers(ws) -> dict[str, int]:
return {str(ws.cell(1, col).value or ""): col for col in range(1, ws.max_column + 1)}
def copy_row_style(ws, src_row: int, dst_row: int) -> None:
for col in range(1, ws.max_column + 1):
src = ws.cell(src_row, col)
dst = ws.cell(dst_row, col)
if src.has_style:
dst._style = copy(src._style)
dst.font = copy(src.font)
dst.fill = copy(src.fill)
dst.border = copy(src.border)
dst.alignment = copy(src.alignment)
dst.number_format = src.number_format
def delete_rows_by_first_col(ws, keys: set[str]) -> None:
for row in range(ws.max_row, 1, -1):
if str(ws.cell(row, 1).value or "") in keys:
ws.delete_rows(row)
def update_case_book() -> None:
wb = openpyxl.load_workbook(CASE_BOOK)
ws = wb["订单状态流转"]
h = headers(ws)
for row in range(2, ws.max_row + 1):
case_id = str(ws.cell(row, h["用例编号"]).value or "")
updates = CASE_UPDATES.get(case_id)
if not updates:
continue
for key, value in updates.items():
if key in h:
ws.cell(row, h[key]).value = value
wb.save(CASE_BOOK)
def sync_flow_book_from_generator() -> None:
from scripts.generate_order_status_flow_lines_excel import main as generate_flow_lines
generate_flow_lines()
wb = openpyxl.load_workbook(FLOW_BOOK)
if "说明" not in wb.sheetnames:
ws = wb.create_sheet("说明")
ws.append(["类型", "内容"])
wb["说明"].append(["更新记录", f"{datetime.now():%Y-%m-%d %H:%M:%S} FLOW-030 改为 S-FLOW-049 -> S-FLOW-054 -> S-FLOW-055;新增 FLOW-045 处罚驳回分支。"])
wb.save(FLOW_BOOK)
def main() -> None:
update_case_book()
sync_flow_book_from_generator()
print("FLOW-030/FLOW-045 penalty branches synced")
if __name__ == "__main__":
main()
from __future__ import annotations
from copy import copy
from datetime import datetime
from pathlib import Path
import shutil
from openpyxl import load_workbook
ROOT = Path(__file__).resolve().parents[1]
CASE_BOOK = ROOT / "测试用例" / "SaaS_E2E测试用例_单Sheet用例测试列表.xlsx"
FLOW_BOOK = ROOT / "测试用例" / "订单状态流转_链路流程线整理.xlsx"
CASE_083 = {
"用例编号": "S-FLOW-083",
"来源/组合依据": "FLOW-030 实测:S-FLOW-034 后丸掌柜子单列表售后中目标行展示【取消售后】入口。",
"所属模块": "丸掌柜发起售后",
"用例标题": "【丸掌柜】异常中子单售后中状态-取消子单售后",
"前置条件": "已执行 S-FLOW-034,丸掌柜已对异常中子单发起子单售后;丸掌柜订单详情子单列表目标子单状态为售后中,操作列存在【取消售后】入口。",
"执行步骤": "1. 登录丸掌柜主账号。\n2. 进入商家管理-订单管理。\n3. 按主单号或来源单号查询目标订单。\n4. 打开订单详情并定位子单列表中售后中的目标子单。\n5. 点击目标子单行内【取消售后】。\n6. 在取消售后弹窗中填写取消原因。\n7. 点击【确认取消】。\n8. 回到订单详情,回读主单状态、子单汇总状态、目标子单列表状态和售后信息。\n9. 登录丸友集账号,进入订单列表,按来源单号回查目标订单状态。",
"预期结果": "1. 丸掌柜侧异常中子单售后取消提交成功。\n2. 目标子单不再显示售后中,恢复为异常中或页面对应可继续处理状态。\n3. 售后取消原因可在订单详情/日志中追溯。\n4. 丸友集侧目标订单不再停留在售后中。\n5. 后续可按页面规则重新对异常中子单发起售后或选择其他异常中后续处理。",
"跨端/状态断言": "1. 本用例处理的是丸掌柜侧子单行内【取消售后】,不是丸友集侧处理/拒绝子单售后。\n2. 与 S-FLOW-036 区分:S-FLOW-036 为丸友集侧拒绝/取消子单售后,S-FLOW-083 为丸掌柜侧主动取消已发起的子单售后。\n3. 查询字段使用来源单号或主单号,不混用来源单号到订单号字段。",
"可组合/被引用关系": "承接 S-FLOW-034;被 FLOW-030 引用;取消后可重新接 S-FLOW-034 或按异常中子单后续链路处理。",
"自动化状态": "已自动化",
"备注": "2026-07-02 新增:覆盖丸掌柜侧异常中子单售后中行内【取消售后】入口。",
}
FLOW_030_SEQ = "S-FLOW-007 -> S-FLOW-014 -> S-FLOW-021 -> S-FLOW-026 -> S-FLOW-040 -> S-FLOW-043 -> S-FLOW-045 -> S-FLOW-046 -> S-FLOW-048 -> S-FLOW-034 -> S-FLOW-083"
FLOW_030_UPDATE = {
"链路编号": "FLOW-030",
"链路名称": "待验收阶段-发起售后-取消售后-异常中子单发起售后-取消子单售后",
"链路类型": "待验收售后取消后接异常中子单售后取消",
"终点状态": "异常中子单售后已取消;目标子单恢复异常中或页面对应可继续处理状态",
"用例编号序列": FLOW_030_SEQ,
"适用场景": "验证待验收阶段售后取消后,丸掌柜子单列表回到异常中;对异常中子单发起售后后,再由丸掌柜侧取消该子单售后。",
"数据复用建议": "同一订单在 S-FLOW-034 后继续接 S-FLOW-083;该分支与丸友集侧 S-FLOW-035/S-FLOW-036 处理分支互斥。",
"注意事项/缺口": "S-FLOW-083 是丸掌柜侧行内取消售后,不是丸友集侧处理售后;取消后可按页面实际状态决定是否重新接 S-FLOW-034。",
}
def header_map(ws) -> dict[str, int]:
return {str(ws.cell(1, col).value or "").strip(): col for col in range(1, ws.max_column + 1) if ws.cell(1, col).value}
def copy_cell_style(src, dst) -> None:
if src.has_style:
dst._style = copy(src._style)
dst.font = copy(src.font)
dst.fill = copy(src.fill)
dst.border = copy(src.border)
dst.alignment = copy(src.alignment)
dst.number_format = src.number_format
dst.protection = copy(src.protection)
def copy_row_style(ws, src_row: int, dst_row: int) -> None:
ws.row_dimensions[dst_row].height = ws.row_dimensions[src_row].height
for col in range(1, ws.max_column + 1):
copy_cell_style(ws.cell(src_row, col), ws.cell(dst_row, col))
def row_by_value(ws, col: int) -> dict[str, int]:
rows: dict[str, int] = {}
for row in range(2, ws.max_row + 1):
value = str(ws.cell(row, col).value or "").strip()
if value and value not in rows:
rows[value] = row
return rows
def set_row_values(ws, row: int, values: dict[str, str]) -> None:
h = header_map(ws)
for key, value in values.items():
if key in h:
ws.cell(row, h[key]).value = value
def update_case_book() -> None:
backup = CASE_BOOK.with_name(f"{CASE_BOOK.stem}.s_flow_083前备份_{datetime.now():%Y%m%d%H%M%S}{CASE_BOOK.suffix}")
shutil.copyfile(CASE_BOOK, backup)
wb = load_workbook(CASE_BOOK)
ws = wb["订单状态流转"]
h = header_map(ws)
rows = row_by_value(ws, h["用例编号"])
if "S-FLOW-083" in rows:
row = rows["S-FLOW-083"]
else:
anchor = rows.get("S-FLOW-082") or max(rows.values())
ws.insert_rows(anchor + 1)
copy_row_style(ws, anchor, anchor + 1)
row = anchor + 1
set_row_values(ws, row, CASE_083)
wb.save(CASE_BOOK)
def load_case_rows() -> dict[str, dict[str, str]]:
wb = load_workbook(CASE_BOOK, data_only=True)
ws = wb["订单状态流转"]
h = header_map(ws)
rows: dict[str, dict[str, str]] = {}
for row in range(2, ws.max_row + 1):
case_id = str(ws.cell(row, h["用例编号"]).value or "").strip()
if case_id:
rows[case_id] = {key: str(ws.cell(row, col).value or "") for key, col in h.items()}
return rows
def enrich_flow(values: dict[str, str], case_rows: dict[str, dict[str, str]]) -> dict[str, str]:
case_ids = [item.strip() for item in values["用例编号序列"].split("->") if item.strip()]
refs = []
modules = []
for case_id in case_ids:
case = case_rows.get(case_id, {})
refs.append(f"{case_id}({case.get('用例标题', '')})")
module = case.get("所属模块", "")
if module and module not in modules:
modules.append(module)
enriched = dict(values)
enriched["用例链路(编号+标题)"] = " -> ".join(refs)
enriched["涉及模块"] = " -> ".join(modules)
return enriched
def delete_detail_rows(ws, flow_id: str) -> int:
insert_at = ws.max_row + 1
for row in range(ws.max_row, 1, -1):
if str(ws.cell(row, 1).value or "").strip() == flow_id:
insert_at = min(insert_at, row)
ws.delete_rows(row)
return insert_at if insert_at <= ws.max_row + 1 else ws.max_row + 1
def update_flow_book() -> None:
backup = FLOW_BOOK.with_name(f"{FLOW_BOOK.stem}.s_flow_083前备份_{datetime.now():%Y%m%d%H%M%S}{FLOW_BOOK.suffix}")
shutil.copyfile(FLOW_BOOK, backup)
case_rows = load_case_rows()
wb = load_workbook(FLOW_BOOK)
summary = wb["链路流程线"]
hs = header_map(summary)
flow_rows = row_by_value(summary, hs["链路编号"])
set_row_values(summary, flow_rows["FLOW-030"], enrich_flow(FLOW_030_UPDATE, case_rows))
details = wb["链路步骤明细"]
insert_at = delete_detail_rows(details, "FLOW-030")
style_row = max(2, min(insert_at - 1, details.max_row))
for offset, case_id in enumerate([item.strip() for item in FLOW_030_SEQ.split("->") if item.strip()]):
row = insert_at + offset
details.insert_rows(row)
copy_row_style(details, style_row, row)
case = case_rows.get(case_id, {})
values = [
"FLOW-030",
str(offset + 1),
case_id,
case.get("所属模块", ""),
case.get("用例标题", ""),
case.get("前置条件", ""),
case.get("预期结果", ""),
]
for col, value in enumerate(values, start=1):
details.cell(row, col).value = value
coverage = wb["用例覆盖索引"]
hc = header_map(coverage)
coverage_rows = row_by_value(coverage, hc["用例编号"])
if "S-FLOW-083" not in coverage_rows:
template = coverage_rows.get("S-FLOW-082") or max(coverage_rows.values())
row = coverage.max_row + 1
coverage.insert_rows(row)
copy_row_style(coverage, template, row)
else:
row = coverage_rows["S-FLOW-083"]
set_row_values(
coverage,
row,
{
"用例编号": "S-FLOW-083",
"所属模块": CASE_083["所属模块"],
"用例标题": CASE_083["用例标题"],
"覆盖次数": "1",
"出现链路": "FLOW-030",
"是否已覆盖": "是",
"备注": "新增丸掌柜侧取消异常中子单售后分支",
},
)
if "说明" in wb.sheetnames:
wb["说明"].append(["更新记录", f"{datetime.now():%Y-%m-%d %H:%M:%S} 新增 S-FLOW-083,并将 FLOW-030 接到 S-FLOW-034 -> S-FLOW-083。"])
wb.save(FLOW_BOOK)
def main() -> None:
update_case_book()
update_flow_book()
print("Synced S-FLOW-083 and FLOW-030.")
if __name__ == "__main__":
main()
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment