accounting/wwwroot/recharge_reverse_list.dspy

122 lines
6.0 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.

# recharge_reverse_list.dspy — 充值错帐处理充值账单列表2026-09-08
# 归属过滤reseller 财务角色只看自己名下客户owner 财务角色看全部。
# 每条账单一个「冲正」按钮conform 确认),已冲正的显示标记禁用。
# 返回 widget 描述VBox 卡片列表),由 recharge_reverse.ui 的 rev_list 容器加载。
dbname = get_module_dbname('accounting')
uid = await get_user()
if not uid:
return {"widgettype": "Text", "options": {"text": "请先登录", "color": "#EF4444"}}
userorgid = await get_userorgid() or ''
db = DBPools()
async with db.sqlorContext(dbname) as sor:
# 操作者是否 owner 财务角色(不受归属限制)
roles_recs = await sor.sqlExe(
"SELECT r.orgtypeid FROM userrole ur JOIN role r ON ur.roleid=r.id WHERE ur.userid=${u}$",
{'u': uid})
await sor.sqlExe("COMMIT", {})
is_owner = any((getattr(r, 'orgtypeid', '') or '') == 'owner' for r in (roles_recs or []))
# 充值账单RECHARGE + 冲正 RECHARGE_REVERSE 一起查,冲正行用于判「已冲正」)
if is_owner:
bills = await sor.sqlExe(
"SELECT b.id, b.customerid, b.orderid, b.business_op, b.amount, b.bill_date, "
"COALESCE(o.orgname,'') AS orgname FROM bill b "
"LEFT JOIN organization o ON o.id=b.customerid "
"WHERE b.business_op IN ('RECHARGE','RECHARGE_REVERSE') "
"ORDER BY b.bill_date DESC, b.bill_timestamp DESC LIMIT 100", {})
else:
# reseller只看自己名下客户organization.parentid 或 discount_customer_bind
bills = await sor.sqlExe(
"SELECT b.id, b.customerid, b.orderid, b.business_op, b.amount, b.bill_date, "
"COALESCE(o.orgname,'') AS orgname FROM bill b "
"LEFT JOIN organization o ON o.id=b.customerid "
"WHERE b.business_op IN ('RECHARGE','RECHARGE_REVERSE') "
"AND (o.parentid=${r}$ OR EXISTS ("
" SELECT 1 FROM discount_customer_bind dcb "
" WHERE dcb.customerid=b.customerid AND dcb.resellerid=${r}$)) "
"ORDER BY b.bill_date DESC, b.bill_timestamp DESC LIMIT 100", {'r': userorgid})
await sor.sqlExe("COMMIT", {})
# 已冲正集合:冲正单的 orderid='REV:<原bill.id>'
reversed_ids = set()
reverses = [b for b in (bills or []) if getattr(b, 'business_op', '') == 'RECHARGE_REVERSE']
for rv in reverses:
oid = getattr(rv, 'orderid', '') or ''
if oid.startswith('REV:'):
reversed_ids.add(oid[4:])
submit_url = entire_url('/accounting/recharge_reverse_submit.dspy')
list_url = entire_url('/accounting/recharge_reverse_list.dspy')
cards = []
for b in (bills or []):
bop = getattr(b, 'business_op', '')
bid = getattr(b, 'id', '')
if bop == 'RECHARGE_REVERSE':
# 冲正单本身只展示不再冲正
cards.append({
"widgettype": "HBox",
"options": {"bgcolor": "#F0FDF4", "border": "1px solid #BBF7D0",
"borderRadius": "8px", "padding": "10px 14px", "gap": "12px",
"alignItems": "center"},
"subwidgets": [
{"widgettype": "Text", "options": {"text": "冲正单", "bgcolor": "#16A34A",
"color": "#FFFFFF", "borderRadius": "6px",
"padding": "2px 8px", "cfontsize": 0.85}},
{"widgettype": "Text", "options": {"text": str(getattr(b, 'orgname', '') or getattr(b, 'customerid', '')), "halign": "left", "css": "filler"}},
{"widgettype": "Text", "options": {"text": "¥" + str(getattr(b, 'amount', 0)), "halign": "left"}},
{"widgettype": "Text", "options": {"text": str(getattr(b, 'bill_date', '')), "color": "#94A3B8", "cfontsize": 0.85, "halign": "left"}},
]})
continue
is_rev = bid in reversed_ids
row = [
{"widgettype": "Text", "options": {"text": "充值", "bgcolor": "#2563EB" if not is_rev else "#94A3B8",
"color": "#FFFFFF", "borderRadius": "6px",
"padding": "2px 8px", "cfontsize": 0.85}},
{"widgettype": "Text", "options": {"text": str(getattr(b, 'orgname', '') or getattr(b, 'customerid', '')), "halign": "left", "css": "filler"}},
{"widgettype": "Text", "options": {"text": "¥" + str(getattr(b, 'amount', 0)), "halign": "left"}},
{"widgettype": "Text", "options": {"text": str(getattr(b, 'bill_date', '')), "color": "#94A3B8", "cfontsize": 0.85, "halign": "left"}},
]
if is_rev:
row.append({"widgettype": "Text", "options": {"text": "已冲正", "color": "#16A34A", "cfontsize": 0.85, "halign": "left"}})
else:
# 冲正按钮conform 确认 → script fetch POST → 成功后刷新列表
script = (
"var body=new URLSearchParams();"
"body.append('bill_id'," + json.dumps(bid) + ");"
"body.append('reason',(window.prompt&&window.prompt('冲正原因(记入审计)',''))||'');"
"var resp=await fetch(" + json.dumps(submit_url) + ",{method:'POST',body:body});"
"var rj=await resp.json();"
"if(rj.success){"
" var lc=bricks.getWidgetById('rev_list',bricks.app);"
" if(lc){lc.clear_widgets();var lr=await fetch(" + json.dumps(list_url) + ");var ld=await lr.json();"
" var lw=await bricks.widgetBuild(ld,lc);if(lw)lc.add_widget(lw);}"
" new bricks.Message({title:'冲正成功',message:rj.message||'已冲正'});"
"}else{new bricks.Message({title:'冲正失败',message:rj.error||'失败'});}")
row.append({
"widgettype": "Button",
"options": {"label": "冲正", "css": "small danger"},
"binds": [{
"wid": "self", "event": "click", "actiontype": "script", "target": "self",
"conform": {"title": "冲正确认",
"message": "确认冲正该笔充值 ¥" + str(getattr(b, 'amount', 0)) + "?冲正将翻转原记账方向(客户余额相应扣回),操作记入审计且不可自动撤销。",
"conform": {"label": "确认冲正"},
"discard": {"label": "取消"}},
"script": script}]})
cards.append({
"widgettype": "HBox",
"options": {"bgcolor": "#1E293B" if not is_rev else "#26313F",
"border": "1px solid #334155", "borderRadius": "8px",
"padding": "10px 14px", "gap": "12px", "alignItems": "center"},
"subwidgets": row})
if not cards:
cards = [{"widgettype": "Text", "options": {"text": "暂无充值账单", "color": "#94A3B8", "padding": "20px"}}]
return {
"widgettype": "VBox",
"options": {"width": "100%", "gap": "8px"},
"subwidgets": cards}