Просмотр исходного кода

feat(pl): filter plans by delivery-stop date range

Add EXISTS on pl_plan_delivery.delivery_time for list column filters.

Co-authored-by: Cursor <cursoragent@cursor.com>
wuzilong 1 месяц назад
Родитель
Сommit
efe8aed409

+ 33 - 1
backend/app/services/pl/plan_list_filters.py

@@ -11,7 +11,7 @@ from typing import List, Optional, Union
 from sqlalchemy import func, or_
 from sqlalchemy.orm import Session
 
-from app.models.pl_models import PlPlan, PlPlanProduct
+from app.models.pl_models import PlPlan, PlPlanDelivery, PlPlanProduct
 from app.services.pl_plan_service import plan_has_incomplete_hazard_cert_exists
 
 DateLike = Union[date, datetime, str, None]
@@ -45,6 +45,27 @@ def plan_package_spec_exists(db: Session, tenant_no: str, keyword: str):
     )
 
 
+def plan_delivery_time_exists(
+    db: Session,
+    tenant_no: str,
+    *,
+    d_from: Optional[date] = None,
+    d_to: Optional[date] = None,
+):
+    """任一未删除送货段的送货日历日落在 [from, to] 内。"""
+    q = db.query(PlPlanDelivery.id).filter(
+        PlPlanDelivery.plan_id == PlPlan.id,
+        PlPlanDelivery.tenant_no == tenant_no,
+        PlPlanDelivery.del_flag == 0,
+        PlPlanDelivery.delivery_time.isnot(None),
+    )
+    if d_from is not None:
+        q = q.filter(func.date(PlPlanDelivery.delivery_time) >= d_from)
+    if d_to is not None:
+        q = q.filter(func.date(PlPlanDelivery.delivery_time) <= d_to)
+    return q.exists()
+
+
 def plan_plate_match(keyword: str):
     """车牌号 / 挂车号模糊匹配。"""
     kw = keyword.lower()
@@ -109,6 +130,8 @@ def build_plan_column_filters(
     license_plate: Optional[str] = None,
     plan_date_from: DateLike = None,
     plan_date_to: DateLike = None,
+    delivery_time_from: DateLike = None,
+    delivery_time_to: DateLike = None,
     cert_upload_status: Optional[str] = None,
     hazard_cert_ready: Optional[Union[int, str]] = None,
     hazard_cert_ready_in: Optional[str] = None,
@@ -169,6 +192,15 @@ def build_plan_column_filters(
     if d_to is not None:
         filters.append(PlPlan.plan_date <= d_to)
 
+    dt_from = _parse_date(delivery_time_from)
+    dt_to = _parse_date(delivery_time_to)
+    if (dt_from is not None or dt_to is not None) and db is not None:
+        filters.append(
+            plan_delivery_time_exists(
+                db, tenant_no, d_from=dt_from, d_to=dt_to
+            )
+        )
+
     if (cert_upload_status or "").strip().lower() == "incomplete":
         filters.append(plan_has_incomplete_hazard_cert_exists(tenant_no))
 

+ 30 - 0
backend/tests/test_pl_plan_list_filters.py

@@ -120,3 +120,33 @@ def test_hazard_cert_ready_single():
     )
     assert len(filters) == 1
     assert "hazard_cert_ready" in _compile(filters[0])
+
+
+def test_delivery_time_range_exists_requires_db():
+    filters = build_plan_column_filters(
+        db=None,
+        tenant_no="shanxia2.0",
+        delivery_time_from=date(2026, 9, 12),
+        delivery_time_to=date(2026, 9, 14),
+    )
+    assert filters == []
+
+
+def test_delivery_time_range_exists_sql():
+    from unittest.mock import MagicMock
+
+    db = MagicMock()
+    q = MagicMock()
+    db.query.return_value = q
+    q.filter.return_value = q
+    q.exists.return_value = "DELIVERY_TIME_EXISTS"
+
+    filters = build_plan_column_filters(
+        db=db,
+        tenant_no="shanxia2.0",
+        delivery_time_from=date(2026, 9, 12),
+        delivery_time_to="2026-09-14",
+    )
+    assert filters == ["DELIVERY_TIME_EXISTS"]
+    assert db.query.called
+    assert q.exists.called