|
|
@@ -679,6 +679,82 @@ def list_contract_gaps(
|
|
|
return PlanContractGapListResponse(total=total, list=items)
|
|
|
|
|
|
|
|
|
+
|
|
|
+CONTRACT_GAP_EXPORT_COLUMNS = [
|
|
|
+ ExportColumn(title="计划编号", field="planNo"),
|
|
|
+ ExportColumn(title="计划日期", field="planDate"),
|
|
|
+ ExportColumn(title="单据类型", field="planType"),
|
|
|
+ ExportColumn(title="跟进状态", field="followStatus"),
|
|
|
+ ExportColumn(title="供方", field="supplierName"),
|
|
|
+ ExportColumn(title="需方", field="customerName"),
|
|
|
+ ExportColumn(title="缺口类型", field="gapType"),
|
|
|
+]
|
|
|
+
|
|
|
+
|
|
|
+class ContractGapExportBody(BaseModel):
|
|
|
+ plan_no: Optional[str] = Field(default=None, alias="planNo")
|
|
|
+ gap_type: Optional[str] = Field(default=None, alias="gapType")
|
|
|
+ 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")
|
|
|
+ q: Optional[str] = None
|
|
|
+ ids: Optional[List[str]] = None
|
|
|
+ columns: Optional[List[str]] = None
|
|
|
+ model_config = {"populate_by_name": True}
|
|
|
+
|
|
|
+
|
|
|
+@router.post("/contract-gaps/export")
|
|
|
+def export_contract_gaps(
|
|
|
+ body: ContractGapExportBody,
|
|
|
+ db: Session = Depends(get_db),
|
|
|
+ user: DbUserContext = Depends(get_db_current_user),
|
|
|
+):
|
|
|
+ try:
|
|
|
+ tenant_no = get_tenant_no(db, user.to_tenant_user_dict())
|
|
|
+ _, items = list_plan_contract_gaps(
|
|
|
+ db,
|
|
|
+ tenant_no=tenant_no,
|
|
|
+ user=user,
|
|
|
+ page=1,
|
|
|
+ page_size=EXPORT_MAX_ROWS,
|
|
|
+ plan_no=body.plan_no,
|
|
|
+ gap_type=body.gap_type,
|
|
|
+ customer_keyword=body.customer_keyword,
|
|
|
+ plan_date_from=body.plan_date_from,
|
|
|
+ plan_date_to=body.plan_date_to,
|
|
|
+ q=body.q,
|
|
|
+ ids=body.ids,
|
|
|
+ max_page_size=EXPORT_MAX_ROWS,
|
|
|
+ )
|
|
|
+ plan_type_labels = {"direct": "直销单", "inbound": "入库单", "outbound": "出库单"}
|
|
|
+ gap_labels = {"supplier": "仅缺供方合同", "demander": "仅缺需方合同", "both": "双侧缺口"}
|
|
|
+ rows = []
|
|
|
+ for item in items:
|
|
|
+ data = item.model_dump(by_alias=True)
|
|
|
+ data["planType"] = plan_type_labels.get(str(data.get("planType") or ""), data.get("planType"))
|
|
|
+ sides = set(data.get("gapSides") or [])
|
|
|
+ if "supplier" in sides and "demander" in sides:
|
|
|
+ data["gapType"] = "双侧缺口"
|
|
|
+ elif "supplier" in sides:
|
|
|
+ data["gapType"] = "仅缺供方合同"
|
|
|
+ elif "demander" in sides:
|
|
|
+ data["gapType"] = "仅缺需方合同"
|
|
|
+ else:
|
|
|
+ data["gapType"] = ""
|
|
|
+ for k, v in list(data.items()):
|
|
|
+ if hasattr(v, "isoformat"):
|
|
|
+ data[k] = str(v)[:19]
|
|
|
+ rows.append(data)
|
|
|
+ cols = filter_export_columns(CONTRACT_GAP_EXPORT_COLUMNS, body.columns)
|
|
|
+ date_str = datetime.now().strftime("%Y%m%d")
|
|
|
+ return export_rows_to_xlsx_response(
|
|
|
+ db, tenant_no=tenant_no, sheet_title="合同完善", columns=cols, rows=rows,
|
|
|
+ filename_stem=f"计划合同完善_{date_str}",
|
|
|
+ )
|
|
|
+ except ExportValidationError as e:
|
|
|
+ raise HTTPException(status_code=400, detail=str(e)) from e
|
|
|
+
|
|
|
+
|
|
|
@router.put("/{id}/contracts/{side}", response_model=PlanContractGapRead)
|
|
|
def put_plan_side_contracts(
|
|
|
id: str,
|