yumoqing 55b4943170 feat(discount): 折扣率语义扩展——允许>1加价卖给客户+解析改专属优先/*兜底
用户决策(2026-09-06):
1. 折扣率允许大于1:平台可加价卖产品给客户(如存储缺省1.5);
   也可小于1让利(如百炼模型0.9,供应商有折扣传导给客户)
2. 解析规则:确定客户→查客户专属折扣;查不到→'*'通配兜底;再无→1.0
   旧实现bug:专属与通配OR混查取全局min——专属0.95+通配0.8返回0.8,
   专属记录形同虚设

改动:
- init.py sor_get_min_product_discount: 两步查询(专属层min→通配层min)
- init.py add/update_discount_detail: 上限1→9.9999(DB double(5,4)约束)
- discount_setting/save_discount.dspy: 兜底记录识别'*'+NULL两种形态;
  「客户折扣率不得高于通用折扣率」校验保留(加价语义下=单客户价不贵于通用价)
- marketing_discount_setting/save_discount.dspy: >1上限放开,保留<基准约束
- generate_qr.dspy: 同上放开
- index.ui: placeholder 0~1→'>1加价 <1折扣',dec_len 2→4

本地mock测试5场景全过:专属优先/通配兜底/无记录1.0/exact_match不兜底/层内min
2026-09-06 15:43:01 +08:00

79 lines
3.2 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 营销方案产品折扣保存
# 规则: 0 < 新折扣 < 全部用户基准折扣
# 自动 upsert 到 discount_detail
result = {'widgettype': 'Message', 'options': {'title': 'Error', 'message': 'Invalid', 'type': 'error', 'timeout': 3}}
try:
discountid = params_kw.get('discountid')
productid = params_kw.get('productid')
discount_val = params_kw.get('discount')
base_discount = params_kw.get('base_discount')
detail_id = params_kw.get('detail_id')
if not discountid or not productid:
raise Exception('缺少必要参数')
# 过滤 NaN/空值
if discount_val is not None:
s = str(discount_val).strip().lower()
if s in ('', 'nan', 'none', 'null'):
discount_val = None
user_orgid = await get_userorgid()
dbname = get_module_dbname('discount')
# discount为0或空 → 删除明细
if not discount_val or float(discount_val) == 0:
if detail_id:
async with DBPools().sqlorContext(dbname) as sor:
await sor.D('discount_detail', {'id': detail_id})
result = {'widgettype': 'Message', 'options': {'title': 'Success', 'message': '已移出营销方案', 'type': 'success', 'timeout': 3}}
else:
result = {'widgettype': 'Message', 'options': {'title': 'Success', 'message': 'ok', 'type': 'success', 'timeout': 3}}
return result
new_val = float(discount_val)
# 验证: 必须大于0
if new_val <= 0:
result = {'widgettype': 'Error', 'options': {'title': '折扣无效', 'message': '折扣必须大于0', 'timeout': 5}}
return result
# 验证: 不能超过基准折扣
if base_discount:
base_val = float(base_discount)
if new_val >= base_val:
result = {'widgettype': 'Error', 'options': {'title': '折扣无效', 'message': f'营销折扣率({new_val})必须小于基准折扣率({base_val})', 'timeout': 5}}
return result
# 2026-09-06: 放开 >1 上限——折扣率>1=加价(基准若是1.5加价,营销可设1.2仍低于基准)。
# 约束只剩两条:>0(上面已验)、< 基准折扣率(上面已验)、≤9.9999(DB double(5,4))
if new_val > 9.9999:
result = {'widgettype': 'Error', 'options': {'title': '折扣无效', 'message': '折扣率不能大于9.9999', 'timeout': 5}}
return result
async with DBPools().sqlorContext(dbname) as sor:
if detail_id:
await sor.U('discount_detail', {
'id': detail_id,
'discount': new_val
})
else:
await sor.C('discount_detail', {
'id': getID(),
'discountid': discountid,
'resellerid': user_orgid,
'prodtypeid': None,
'productid': productid,
'discount': new_val
})
result = {'widgettype': 'Message', 'options': {'title': 'Success', 'message': f'折扣保存成功({new_val})', 'type': 'success', 'timeout': 3}}
except Exception as e:
debug(f'save_marketing_discount error: {format_exc()}')
result['options'] = {'title': 'Error', 'message': f'保存失败: {str(e)}', 'type': 'error', 'timeout': 5}
return result