|
|
@@ -10,7 +10,7 @@ from datetime import date, datetime
|
|
|
from decimal import Decimal, InvalidOperation
|
|
|
from typing import List, Optional, Union
|
|
|
|
|
|
-from sqlalchemy import and_, func, or_
|
|
|
+from sqlalchemy import and_, exists, func, or_, select
|
|
|
from sqlalchemy.orm import Session
|
|
|
|
|
|
from app.models.pl_models import (
|
|
|
@@ -23,11 +23,13 @@ from app.models.pl_models import (
|
|
|
PlPlanProduct,
|
|
|
PlTransport,
|
|
|
)
|
|
|
+from app.services.pl_internal_customer import INTERNAL_CUSTOMER_NAMES
|
|
|
from app.services.pl_plan_service import plan_has_incomplete_hazard_cert_exists
|
|
|
|
|
|
DateLike = Union[date, datetime, str, None]
|
|
|
|
|
|
_NUMBER_OPS = frozenset({"between", "eq", "gt", "gte", "lt", "lte"})
|
|
|
+_STOCK_ROLES = frozenset({"inbound", "outbound"})
|
|
|
|
|
|
|
|
|
def plan_product_name_exists(db: Session, tenant_no: str, keyword: str):
|
|
|
@@ -321,6 +323,30 @@ def plan_stop_kind_exists(db: Session, tenant_no: str, kind: str):
|
|
|
)
|
|
|
|
|
|
|
|
|
+def stock_role_exists_clause(*, tenant_no: str, stock_role: str):
|
|
|
+ """inbound=送货段客户为内部客户;outbound=装货段客户为内部客户。"""
|
|
|
+ role = (stock_role or "").strip().lower()
|
|
|
+ if role not in _STOCK_ROLES:
|
|
|
+ raise ValueError(f"invalid stockRole: {stock_role}")
|
|
|
+ model = PlPlanDelivery if role == "inbound" else PlPlanLoad
|
|
|
+ return exists(
|
|
|
+ select(model.id).where(
|
|
|
+ model.plan_id == PlPlan.id,
|
|
|
+ model.tenant_no == tenant_no,
|
|
|
+ model.del_flag == 0,
|
|
|
+ model.customer_name.in_(tuple(INTERNAL_CUSTOMER_NAMES)),
|
|
|
+ )
|
|
|
+ )
|
|
|
+
|
|
|
+
|
|
|
+def apply_stock_role_filter(query, *, stock_role: Optional[str], tenant_no: str):
|
|
|
+ if stock_role is None or str(stock_role).strip() == "":
|
|
|
+ return query
|
|
|
+ return query.filter(
|
|
|
+ stock_role_exists_clause(tenant_no=tenant_no, stock_role=str(stock_role))
|
|
|
+ )
|
|
|
+
|
|
|
+
|
|
|
def parse_stop_customer_keywords(raw: Optional[str]) -> List[str]:
|
|
|
"""客户列多值:JSON 数组,或单个纯文本关键字。"""
|
|
|
if raw is None:
|