|
|
@@ -2,7 +2,11 @@ from __future__ import annotations
|
|
|
|
|
|
from typing import List, Optional
|
|
|
|
|
|
-from app.services.pl_internal_customer import is_internal_customer_name
|
|
|
+from sqlalchemy import and_, exists, or_, select
|
|
|
+
|
|
|
+from app.models.pl_models import PlPlan, PlPlanContract
|
|
|
+from app.services.pl_internal_customer import INTERNAL_CUSTOMER_NAMES, is_internal_customer_name
|
|
|
+from app.services.pl_plan_service import PLAN_CONTRACT_SIDE_DEMANDER, PLAN_CONTRACT_SIDE_SUPPLIER
|
|
|
|
|
|
|
|
|
def compute_contract_gap_sides(
|
|
|
@@ -22,3 +26,38 @@ def compute_contract_gap_sides(
|
|
|
|
|
|
def side_is_exempt(name: Optional[str]) -> bool:
|
|
|
return is_internal_customer_name(name)
|
|
|
+
|
|
|
+
|
|
|
+def _side_contract_exists(tenant_no: str, side: str):
|
|
|
+ return exists(
|
|
|
+ select(PlPlanContract.id).where(
|
|
|
+ PlPlanContract.plan_id == PlPlan.id,
|
|
|
+ PlPlanContract.tenant_no == tenant_no,
|
|
|
+ PlPlanContract.del_flag == 0,
|
|
|
+ PlPlanContract.side == side,
|
|
|
+ )
|
|
|
+ )
|
|
|
+
|
|
|
+
|
|
|
+def plan_has_contract_gap_exists(tenant_no: str):
|
|
|
+ """列表筛选:计划存在合同缺口(非内置侧缺少有效 pl_plan_contract 关联)。"""
|
|
|
+ internal_names = tuple(INTERNAL_CUSTOMER_NAMES)
|
|
|
+ supplier_not_internal = or_(
|
|
|
+ PlPlan.supplier_name.is_(None),
|
|
|
+ PlPlan.supplier_name == "",
|
|
|
+ ~PlPlan.supplier_name.in_(internal_names),
|
|
|
+ )
|
|
|
+ customer_not_internal = or_(
|
|
|
+ PlPlan.customer_name.is_(None),
|
|
|
+ PlPlan.customer_name == "",
|
|
|
+ ~PlPlan.customer_name.in_(internal_names),
|
|
|
+ )
|
|
|
+ supplier_gap = and_(
|
|
|
+ supplier_not_internal,
|
|
|
+ ~_side_contract_exists(tenant_no, PLAN_CONTRACT_SIDE_SUPPLIER),
|
|
|
+ )
|
|
|
+ demander_gap = and_(
|
|
|
+ customer_not_internal,
|
|
|
+ ~_side_contract_exists(tenant_no, PLAN_CONTRACT_SIDE_DEMANDER),
|
|
|
+ )
|
|
|
+ return or_(supplier_gap, demander_gap)
|