|
@@ -537,7 +537,7 @@ async def simulate_calc(
|
|
|
}
|
|
}
|
|
|
)
|
|
)
|
|
|
|
|
|
|
|
-@router.post("/simulate", response_class=HTMLResponse)
|
|
|
|
|
|
|
+@router.post("/simulate7", response_class=HTMLResponse)
|
|
|
async def simulate_calc(
|
|
async def simulate_calc(
|
|
|
request: Request,
|
|
request: Request,
|
|
|
machine_id: int = Form(...),
|
|
machine_id: int = Form(...),
|
|
@@ -731,7 +731,224 @@ async def simulate_calc(
|
|
|
}
|
|
}
|
|
|
)
|
|
)
|
|
|
|
|
|
|
|
|
|
+@router.post("/simulate", response_class=HTMLResponse)
|
|
|
|
|
+async def simulate_calc(
|
|
|
|
|
+ request: Request,
|
|
|
|
|
+ machine_id: int = Form(...),
|
|
|
|
|
+ db: Session = Depends(get_db)
|
|
|
|
|
+):
|
|
|
|
|
+ form = await request.form()
|
|
|
|
|
+
|
|
|
|
|
+ option_ids = []
|
|
|
|
|
+ for k, v in form.multi_items():
|
|
|
|
|
+ if k.startswith("option_"):
|
|
|
|
|
+ option_ids.append(int(v))
|
|
|
|
|
+
|
|
|
|
|
+ # ---------------- 基准价 ----------------
|
|
|
|
|
+
|
|
|
|
|
+ base_row = db.execute(text("""
|
|
|
|
|
+ SELECT base_price
|
|
|
|
|
+ FROM machine_base_price
|
|
|
|
|
+ WHERE machine_id=:mid
|
|
|
|
|
+ LIMIT 1
|
|
|
|
|
+ """), {"mid": machine_id}).fetchone()
|
|
|
|
|
+
|
|
|
|
|
+ base_price = float(base_row.base_price) if base_row else 1000.0
|
|
|
|
|
+
|
|
|
|
|
+ # ---------------- 读取选项扣减规则 ----------------
|
|
|
|
|
+
|
|
|
|
|
+ rows = []
|
|
|
|
|
+ if option_ids:
|
|
|
|
|
+ rows = db.execute(text("""
|
|
|
|
|
+ SELECT
|
|
|
|
|
+ p.option_id,
|
|
|
|
|
+ p.factor,
|
|
|
|
|
+ p.absolute_deduct,
|
|
|
|
|
+ p.group_code,
|
|
|
|
|
+ p.severity_level,
|
|
|
|
|
+ p.sub_weight,
|
|
|
|
|
+ p.is_special,
|
|
|
|
|
+ p.repair_level,
|
|
|
|
|
+ g.cap_ratio,
|
|
|
|
|
+ g.group_weight
|
|
|
|
|
+ FROM price_option_factor p
|
|
|
|
|
+ LEFT JOIN price_damage_group g
|
|
|
|
|
+ ON p.group_code = g.group_code
|
|
|
|
|
+ WHERE p.option_id IN :ids
|
|
|
|
|
+ """), {"ids": tuple(option_ids)}).fetchall()
|
|
|
|
|
+
|
|
|
|
|
+ # ---------------- 覆盖关系 ----------------
|
|
|
|
|
+
|
|
|
|
|
+ overrides = db.execute(text("""
|
|
|
|
|
+ SELECT
|
|
|
|
|
+ trigger_group_code,
|
|
|
|
|
+ target_group_code,
|
|
|
|
|
+ override_type,
|
|
|
|
|
+ override_value
|
|
|
|
|
+ FROM price_group_override
|
|
|
|
|
+ """)).fetchall()
|
|
|
|
|
+
|
|
|
|
|
+ override_map = defaultdict(list)
|
|
|
|
|
+ for o in overrides:
|
|
|
|
|
+ override_map[o.trigger_group_code].append(o)
|
|
|
|
|
+
|
|
|
|
|
+ # ---------------- 分组 ----------------
|
|
|
|
|
+
|
|
|
|
|
+ groups = defaultdict(list)
|
|
|
|
|
+ specials = []
|
|
|
|
|
+ repairs = []
|
|
|
|
|
+
|
|
|
|
|
+ for r in rows:
|
|
|
|
|
+ if r.repair_level and r.repair_level > 0:
|
|
|
|
|
+ repairs.append(r)
|
|
|
|
|
+ continue
|
|
|
|
|
+
|
|
|
|
|
+ if r.is_special:
|
|
|
|
|
+ specials.append(r)
|
|
|
|
|
+ continue
|
|
|
|
|
+
|
|
|
|
|
+ groups[r.group_code].append(r)
|
|
|
|
|
+
|
|
|
|
|
+ # ---------------- 覆盖处理 ----------------
|
|
|
|
|
+
|
|
|
|
|
+ hit_groups = set(groups.keys())
|
|
|
|
|
+
|
|
|
|
|
+ skip_groups = set()
|
|
|
|
|
+ weight_override = {}
|
|
|
|
|
+
|
|
|
|
|
+ for g in hit_groups:
|
|
|
|
|
+ for o in override_map.get(g, []):
|
|
|
|
|
+ if o.override_type == "skip":
|
|
|
|
|
+ skip_groups.add(o.target_group_code)
|
|
|
|
|
+ elif o.override_type == "weight":
|
|
|
|
|
+ weight_override[o.target_group_code] = float(o.override_value)
|
|
|
|
|
+
|
|
|
|
|
+ # ---------------- 分组扣减 ----------------
|
|
|
|
|
+
|
|
|
|
|
+ total_ratio = 0.0
|
|
|
|
|
+ detail_rows = []
|
|
|
|
|
+
|
|
|
|
|
+ for group_code, items in groups.items():
|
|
|
|
|
+
|
|
|
|
|
+ if group_code in skip_groups:
|
|
|
|
|
+ continue
|
|
|
|
|
+
|
|
|
|
|
+ items = sorted(
|
|
|
|
|
+ items,
|
|
|
|
|
+ key=lambda x: (
|
|
|
|
|
+ float(x.factor or 0),
|
|
|
|
|
+ x.severity_level or 0
|
|
|
|
|
+ ),
|
|
|
|
|
+ reverse=True
|
|
|
|
|
+ )
|
|
|
|
|
+
|
|
|
|
|
+ main = items[0]
|
|
|
|
|
+
|
|
|
|
|
+ group_deduct = float(main.factor or 0)
|
|
|
|
|
+
|
|
|
|
|
+ for sub in items[1:]:
|
|
|
|
|
+ group_deduct += float(sub.factor or 0) * float(sub.sub_weight or 0.3)
|
|
|
|
|
|
|
|
|
|
+ cap_ratio = float(main.cap_ratio or 1.0)
|
|
|
|
|
+
|
|
|
|
|
+ cap = float(main.factor or 0) * cap_ratio
|
|
|
|
|
+
|
|
|
|
|
+ group_deduct = min(group_deduct, cap)
|
|
|
|
|
+
|
|
|
|
|
+ group_weight = float(main.group_weight or 1.0)
|
|
|
|
|
+ if group_code in weight_override:
|
|
|
|
|
+ group_weight = weight_override[group_code]
|
|
|
|
|
+
|
|
|
|
|
+ final_group_deduct = group_deduct * group_weight
|
|
|
|
|
+
|
|
|
|
|
+ total_ratio += final_group_deduct
|
|
|
|
|
+
|
|
|
|
|
+ detail_rows.append({
|
|
|
|
|
+ "option_name": f"--{group_code} 组扣减",
|
|
|
|
|
+ "factor": round(final_group_deduct, 4)
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+ for it in items:
|
|
|
|
|
+ detail_rows.append({
|
|
|
|
|
+ "option_name": it.option_id,
|
|
|
|
|
+ "factor": it.factor
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+ # ---------------- special 处理 ----------------
|
|
|
|
|
+
|
|
|
|
|
+ special_ratio = 0.0
|
|
|
|
|
+ for s in specials:
|
|
|
|
|
+ special_ratio += float(s.factor or 0)
|
|
|
|
|
+
|
|
|
|
|
+ detail_rows.append({
|
|
|
|
|
+ "option_name": f"特殊项:{s.option_id}",
|
|
|
|
|
+ "factor": s.factor
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+ # 特殊项封顶(示例:30%)
|
|
|
|
|
+ special_ratio = min(special_ratio, 0.30)
|
|
|
|
|
+ total_ratio += special_ratio
|
|
|
|
|
+
|
|
|
|
|
+ # ---------------- 维修分级 ----------------
|
|
|
|
|
+
|
|
|
|
|
+ repair_ratio = 0.0
|
|
|
|
|
+ max_repair_level = 0
|
|
|
|
|
+
|
|
|
|
|
+ for r in repairs:
|
|
|
|
|
+ repair_ratio += float(r.factor or 0)
|
|
|
|
|
+ max_repair_level = max(max_repair_level, r.repair_level or 0)
|
|
|
|
|
+
|
|
|
|
|
+ detail_rows.append({
|
|
|
|
|
+ "option_name": f"维修项:{r.option_id}",
|
|
|
|
|
+ "factor": r.factor
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+ # 维修封顶(示例)
|
|
|
|
|
+ if max_repair_level >= 3:
|
|
|
|
|
+ repair_ratio = max(repair_ratio, 0.30)
|
|
|
|
|
+ elif max_repair_level == 2:
|
|
|
|
|
+ repair_ratio = min(repair_ratio, 0.20)
|
|
|
|
|
+ else:
|
|
|
|
|
+ repair_ratio = min(repair_ratio, 0.10)
|
|
|
|
|
+
|
|
|
|
|
+ total_ratio += repair_ratio
|
|
|
|
|
+
|
|
|
|
|
+ # ---------------- 最终价格 ----------------
|
|
|
|
|
+
|
|
|
|
|
+ total_ratio = min(total_ratio, 0.90)
|
|
|
|
|
+
|
|
|
|
|
+ final_price = base_price * (1 - total_ratio)
|
|
|
|
|
+
|
|
|
|
|
+ # ---------------- 返回 ----------------
|
|
|
|
|
+
|
|
|
|
|
+ row = db.execute(text("""
|
|
|
|
|
+ SELECT m.name, t.estimate_packet
|
|
|
|
|
+ FROM machine_temp t
|
|
|
|
|
+ JOIN t_machine m ON t.machine_id = m.machine_id
|
|
|
|
|
+ WHERE t.machine_id = :mid
|
|
|
|
|
+ """), {"mid": machine_id}).fetchone()
|
|
|
|
|
+
|
|
|
|
|
+ import json
|
|
|
|
|
+ tpl = json.loads(row.estimate_packet)
|
|
|
|
|
+
|
|
|
|
|
+ return templates.TemplateResponse(
|
|
|
|
|
+ "simulate_one.html",
|
|
|
|
|
+ {
|
|
|
|
|
+ "request": request,
|
|
|
|
|
+ "machine_id": machine_id,
|
|
|
|
|
+ "machine_name": row.name,
|
|
|
|
|
+ "tpl": tpl,
|
|
|
|
|
+ "result": {
|
|
|
|
|
+ "base_price": round(base_price, 2),
|
|
|
|
|
+ "final_price": round(final_price, 2),
|
|
|
|
|
+ "total_ratio": round(total_ratio, 4),
|
|
|
|
|
+ "details": detail_rows,
|
|
|
|
|
+ "selected": [str(i) for i in option_ids]
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ )
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
@router.post("/estimate2", response_class=HTMLResponse)
|
|
@router.post("/estimate2", response_class=HTMLResponse)
|
|
|
def estimate_submit(
|
|
def estimate_submit(
|
|
|
request: Request,
|
|
request: Request,
|