feat(pl): wire deliveryTimeFrom/To through plan list and export

Contract-gap list/export share the same stop-date EXISTS filter.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
wuzilongandCursor committed 2026-09-12 21:23:31 +08:00
1 parent 0e3df1f39d
commit 204b5f48c8
2 files changed
+52 -2

No files matched your search

+37
View File
@@ -117,6 +117,7 @@ from app.services.pl_plan_print_service import get_delivery_slip_print_context
from app.services.pl_plan_stop_service import (
attach_plan_stops_to_item,
collect_stop_qty_warnings,
collect_unique_delivery_date_strings,
load_plan_stops_grouped,
replace_plan_stops,
sync_plan_legacy_from_stops,
@@ -383,6 +384,8 @@ def _filters_from_params(params: dict, *, db: Session, tenant_no: str) -> list:
license_plate=_param_get(params, "licensePlate", "license_plate"),
plan_date_from=_param_get(params, "planDateFrom", "plan_date_from"),
plan_date_to=_param_get(params, "planDateTo", "plan_date_to"),
delivery_time_from=_param_get(params, "deliveryTimeFrom", "delivery_time_from"),
delivery_time_to=_param_get(params, "deliveryTimeTo", "delivery_time_to"),
cert_upload_status=_param_get(params, "certUploadStatus", "cert_upload_status"),
hazard_cert_ready=_param_get(params, "hazardCertReady", "hazard_cert_ready"),
hazard_cert_ready_in=_param_get(
@@ -431,6 +434,12 @@ def list_plans(
license_plate: Optional[str] = Query(default=None, alias="licensePlate", description="车牌号"),
plan_date_from: Optional[date] = Query(default=None, alias="planDateFrom", description="计划日期开始"),
plan_date_to: Optional[date] = Query(default=None, alias="planDateTo", description="计划日期结束"),
delivery_time_from: Optional[date] = Query(
default=None, alias="deliveryTimeFrom", description="送货时间开始"
),
delivery_time_to: Optional[date] = Query(
default=None, alias="deliveryTimeTo", description="送货时间结束"
),
has_hazard_product: Optional[int] = Query(
default=None,
alias="hasHazardProduct",
@@ -505,6 +514,8 @@ def list_plans(
licensePlate=license_plate,
planDateFrom=plan_date_from,
planDateTo=plan_date_to,
deliveryTimeFrom=delivery_time_from,
deliveryTimeTo=delivery_time_to,
certUploadStatus=cert_upload_status,
hazardCertReady=hazard_cert_ready,
)
@@ -633,6 +644,7 @@ PLAN_EXPORT_COLUMNS = [
ExportColumn(title="计划编号", field="planNo"),
ExportColumn(title="跟进状态", field="followStatus"),
ExportColumn(title="计划日期", field="planDate"),
ExportColumn(title="送货时间", field="deliveryTimes"),
ExportColumn(title="单据类型", field="planType"),
ExportColumn(title="供方", field="supplierName"),
ExportColumn(title="需方", field="customerName"),
@@ -703,6 +715,8 @@ class PlanExportBody(BaseModel):
license_plate: Optional[str] = Field(default=None, alias="licensePlate")
plan_date_from: Optional[date] = Field(default=None, alias="planDateFrom")
plan_date_to: Optional[date] = Field(default=None, alias="planDateTo")
delivery_time_from: Optional[date] = Field(default=None, alias="deliveryTimeFrom")
delivery_time_to: Optional[date] = Field(default=None, alias="deliveryTimeTo")
has_hazard_product: Optional[int] = Field(default=None, alias="hasHazardProduct")
cert_upload_status: Optional[str] = Field(default=None, alias="certUploadStatus")
hazard_cert_ready: Optional[int] = Field(default=None, alias="hazardCertReady")
@@ -771,6 +785,8 @@ def export_plans(
sheet_title = "计划归档"
filename_stem = f"计划归档_{date_str}"
else:
plan_ids = [plan.id for plan in plans]
stops_by_plan = load_plan_stops_grouped(db, tenant_no, plan_ids)
items = []
for plan in plans:
data = PlanWithProducts.model_validate(plan).model_dump(by_alias=True)
@@ -778,6 +794,9 @@ def export_plans(
data["planDate"] = str(data["planDate"])[:10]
if data.get("erpEntered") is not None:
data["erpEntered"] = "是" if int(data["erpEntered"]) else "否"
_load_read, delivery_reads = stops_by_plan.get(plan.id, (None, []))
times = collect_unique_delivery_date_strings(delivery_reads)
data["deliveryTimes"] = " / ".join(times) if times else ""
items.append(data)
cols = filter_export_columns(PLAN_EXPORT_COLUMNS, body.columns)
sheet_title = "计划列表"
@@ -884,6 +903,8 @@ def list_contract_gaps(
),
plan_date_from: Optional[date] = Query(default=None, alias="planDateFrom"),
plan_date_to: Optional[date] = Query(default=None, alias="planDateTo"),
delivery_time_from: Optional[date] = Query(default=None, alias="deliveryTimeFrom"),
delivery_time_to: Optional[date] = Query(default=None, alias="deliveryTimeTo"),
plan_type: Optional[str] = Query(default=None, alias="planType"),
follow_status: Optional[str] = Query(default=None, alias="followStatus"),
q: Optional[str] = Query(
@@ -901,6 +922,8 @@ def list_contract_gaps(
customerKeyword=customer_keyword,
planDateFrom=plan_date_from,
planDateTo=plan_date_to,
deliveryTimeFrom=delivery_time_from,
deliveryTimeTo=delivery_time_to,
planType=plan_type,
followStatus=follow_status,
)
@@ -915,6 +938,8 @@ def list_contract_gaps(
customer_keyword=_param_get(params, "customerKeyword", "customer_keyword"),
plan_date_from=_param_get(params, "planDateFrom", "plan_date_from"),
plan_date_to=_param_get(params, "planDateTo", "plan_date_to"),
delivery_time_from=_param_get(params, "deliveryTimeFrom", "delivery_time_from"),
delivery_time_to=_param_get(params, "deliveryTimeTo", "delivery_time_to"),
plan_type=_param_get(params, "planType", "plan_type"),
plan_type_in=_param_get(params, "planTypeIn", "plan_type_in"),
follow_status=_param_get(params, "followStatus", "followStatusIn", "follow_status"),
@@ -929,6 +954,7 @@ def list_contract_gaps(
CONTRACT_GAP_EXPORT_COLUMNS = [
ExportColumn(title="计划编号", field="planNo"),
ExportColumn(title="计划日期", field="planDate"),
ExportColumn(title="送货时间", field="deliveryTimes"),
ExportColumn(title="单据类型", field="planType"),
ExportColumn(title="跟进状态", field="followStatus"),
ExportColumn(title="供方", field="supplierName"),
@@ -943,6 +969,8 @@ class ContractGapExportBody(BaseModel):
customer_keyword: Optional[str] = Field(default=None, alias="customerKeyword")
plan_date_from: Optional[date] = Field(default=None, alias="planDateFrom")
plan_date_to: Optional[date] = Field(default=None, alias="planDateTo")
delivery_time_from: Optional[date] = Field(default=None, alias="deliveryTimeFrom")
delivery_time_to: Optional[date] = Field(default=None, alias="deliveryTimeTo")
q: Optional[str] = None
ids: Optional[List[str]] = None
columns: Optional[List[str]] = None
@@ -969,6 +997,12 @@ def export_contract_gaps(
customer_keyword=_param_get(params, "customerKeyword", "customer_keyword"),
plan_date_from=_param_get(params, "planDateFrom", "plan_date_from"),
plan_date_to=_param_get(params, "planDateTo", "plan_date_to"),
delivery_time_from=_param_get(
params, "deliveryTimeFrom", "delivery_time_from"
),
delivery_time_to=_param_get(
params, "deliveryTimeTo", "delivery_time_to"
),
plan_type=_param_get(params, "planType", "plan_type"),
plan_type_in=_param_get(params, "planTypeIn", "plan_type_in"),
follow_status=_param_get(
@@ -995,6 +1029,9 @@ def export_contract_gaps(
data["gapType"] = "仅缺需方合同"
else:
data["gapType"] = ""
times = data.get("deliveryTimes") or []
if isinstance(times, list):
data["deliveryTimes"] = " / ".join(str(t) for t in times) if times else ""
for k, v in list(data.items()):
if hasattr(v, "isoformat"):
data[k] = str(v)[:19]
+15 -2
View File
@@ -22,8 +22,11 @@ from app.schemas.pl.plan import (
PlanLoadRead,
PlanProductRead,
)
from app.services.pl.plan_list_filters import build_plan_column_filters
from app.services.pl_plan_stop_service import collect_unique_delivery_date_strings
from app.services.pl.plan_list_filters import (
_parse_date,
build_plan_column_filters,
plan_delivery_time_exists,
)
from app.services.pl_data_scope import apply_pl_scope
from app.services.pl_internal_customer import INTERNAL_CUSTOMER_NAMES, is_internal_customer_name
from app.services.pl_plan_service import (
@@ -38,6 +41,7 @@ from app.services.pl_plan_stop_service import (
PLAN_CONTRACT_SIDE_DELIVERY,
PLAN_CONTRACT_SIDE_LOAD,
assert_at_most_one_contract,
collect_unique_delivery_date_strings,
load_plan_stops_grouped,
)
from app.services.pl_product_display import load_crm_product_alias_maps, resolve_product_display_names
@@ -330,6 +334,8 @@ def list_plan_contract_gaps(
customer_keyword: Optional[str] = None,
plan_date_from: Optional[date] = None,
plan_date_to: Optional[date] = None,
delivery_time_from: Optional[date] = None,
delivery_time_to: Optional[date] = None,
plan_type: Optional[str] = None,
plan_type_in: Optional[str] = None,
follow_status: Optional[str] = None,
@@ -419,6 +425,13 @@ def list_plan_contract_gaps(
if column_filters:
query = query.filter(*column_filters)
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:
query = query.filter(
plan_delivery_time_exists(db, tenant_no, d_from=dt_from, d_to=dt_to)
)
if q:
q_kw = q.strip()
if q_kw: