from datetime import datetime from decimal import Decimal from types import SimpleNamespace from unittest.mock import MagicMock import pytest from app.services.pl_io_order_service import apply_site_feedback def _header(**kwargs): defaults = dict( id="order-1", tenant_no="t1", status="in_progress", site_feedback_remark=None, site_feedback_images=None, site_feedback_time=None, site_first_submitted_at=None, update_by=None, ) defaults.update(kwargs) return SimpleNamespace(**defaults) def _item(item_id="item-1"): return SimpleNamespace( id=item_id, plan_product_id="pp-1", actual_qty=None, qty_source=None, update_by=None, ) def test_apply_site_feedback_sets_status_completed(): header = _header() item = _item() db = MagicMock() apply_site_feedback( db, kind="inbound", header=header, items=[item], item_qty={"item-1": Decimal("12.5")}, remark="ok", images=["a.jpg"], status="completed", ) assert header.status == "completed" assert item.actual_qty == Decimal("12.5") assert header.site_first_submitted_at is not None assert header.site_feedback_remark == "ok" db.flush.assert_called_once() def test_apply_site_feedback_sets_status_voided(): header = _header() apply_site_feedback( MagicMock(), kind="inbound", header=header, items=[_item()], item_qty={"item-1": Decimal("1")}, remark=None, images=[], status="voided", ) assert header.status == "voided" def test_apply_site_feedback_keeps_in_progress_when_requested(): """首次提交选进行中时,不得隐式改成已完成。""" header = _header(status="in_progress") apply_site_feedback( MagicMock(), kind="inbound", header=header, items=[_item()], item_qty={"item-1": Decimal("1")}, remark=None, images=[], status="in_progress", ) assert header.status == "in_progress" assert header.site_first_submitted_at is not None def test_apply_site_feedback_rejects_invalid_status(): header = _header() with pytest.raises(ValueError, match="无效状态"): apply_site_feedback( MagicMock(), kind="inbound", header=header, items=[_item()], item_qty={}, remark=None, images=[], status="done", ) def test_apply_site_feedback_rejects_already_voided(): header = _header(status="voided") with pytest.raises(ValueError, match="单据已作废"): apply_site_feedback( MagicMock(), kind="inbound", header=header, items=[_item()], item_qty={}, remark=None, images=[], status="completed", ) def test_apply_site_feedback_does_not_reset_first_submitted_at(): first = datetime(2026, 1, 1, 12, 0, 0) header = _header( status="completed", site_first_submitted_at=first, ) apply_site_feedback( MagicMock(), kind="inbound", header=header, items=[_item()], item_qty={"item-1": Decimal("2")}, remark="二改", images=[], status="in_progress", ) assert header.status == "in_progress" assert header.site_first_submitted_at == first