109 lines
4.5 KiB
Plaintext
109 lines
4.5 KiB
Plaintext
# recharge_reverse_submit.dspy — 充值错帐冲正提交(2026-09-08)
|
||
# 参数:bill_id(要冲正的充值账单)、reason(冲正原因,记入审计)
|
||
# 逻辑:校验归属→防重复冲正→取原单费率→recharge_accounting(RECHARGE_REVERSE)
|
||
# 引擎自动翻转借贷方向(accounting_config.py endswith('_REVERSE'))→审计留痕
|
||
# orderid='REV:<bill_id>' 既防重复冲正,又让列表能识别「已冲正」
|
||
|
||
dbname = get_module_dbname('accounting')
|
||
uid = await get_user()
|
||
if not uid:
|
||
return json.dumps({'success': False, 'error': '未登录'}, ensure_ascii=False)
|
||
userorgid = await get_userorgid() or ''
|
||
bill_id = (params_kw or {}).get('bill_id', '') or ''
|
||
reason = (params_kw or {}).get('reason', '') or ''
|
||
if not bill_id:
|
||
return json.dumps({'success': False, 'error': '缺少 bill_id'}, ensure_ascii=False)
|
||
|
||
|
||
async def _is_owner_role(sor, uid):
|
||
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", {})
|
||
return any((getattr(r, 'orgtypeid', '') or '') == 'owner' for r in (recs or []))
|
||
|
||
|
||
async def _customer_belongs(sor, customerid, resellerid):
|
||
if not customerid or not resellerid or resellerid == '0':
|
||
return False
|
||
recs = await sor.sqlExe(
|
||
"SELECT id FROM organization WHERE id=${c}$ AND parentid=${r}$",
|
||
{'c': customerid, 'r': resellerid})
|
||
await sor.sqlExe("COMMIT", {})
|
||
if recs:
|
||
return True
|
||
recs2 = await sor.sqlExe(
|
||
"SELECT id FROM discount_customer_bind WHERE customerid=${c}$ AND resellerid=${r}$ LIMIT 1",
|
||
{'c': customerid, 'r': resellerid})
|
||
await sor.sqlExe("COMMIT", {})
|
||
return bool(recs2)
|
||
|
||
|
||
db = DBPools()
|
||
async with db.sqlorContext(dbname) as sor:
|
||
# 1. 取原充值账单
|
||
recs = await sor.sqlExe(
|
||
"SELECT id, customerid, orderid, business_op, amount FROM bill WHERE id=${b}$",
|
||
{'b': bill_id})
|
||
await sor.sqlExe("COMMIT", {})
|
||
if not recs:
|
||
return json.dumps({'success': False, 'error': '账单不存在'}, ensure_ascii=False)
|
||
bill = recs[0]
|
||
if getattr(bill, 'business_op', '') != 'RECHARGE':
|
||
return json.dumps({'success': False, 'error': '只能冲正充值(RECHARGE)账单'}, ensure_ascii=False)
|
||
customerid = getattr(bill, 'customerid', '') or ''
|
||
|
||
# 2. 归属校验(reseller 财务只能冲正自己名下客户的充值)
|
||
is_owner = await _is_owner_role(sor, uid)
|
||
if not is_owner and not await _customer_belongs(sor, customerid, userorgid):
|
||
return json.dumps({'success': False, 'error': '无权冲正该账单(客户不归属当前组织)'}, ensure_ascii=False)
|
||
|
||
# 3. 防重复冲正:已存在 orderid='REV:<bill_id>' 的冲正单则拒绝
|
||
rev_orderid = 'REV:' + bill_id
|
||
exist = await sor.sqlExe(
|
||
"SELECT id FROM bill WHERE orderid=${o}$ AND business_op='RECHARGE_REVERSE' LIMIT 1",
|
||
{'o': rev_orderid})
|
||
await sor.sqlExe("COMMIT", {})
|
||
if exist:
|
||
return json.dumps({'success': False, 'error': '该充值账单已冲正过,不可重复冲正'}, ensure_ascii=False)
|
||
|
||
# 4. 取原单充值费率(payment_log.id = bill.orderid,代客充值/在线充值都写了 payment_log)
|
||
feerate = 0.0
|
||
orig_orderid = getattr(bill, 'orderid', '') or ''
|
||
if orig_orderid:
|
||
prec = await sor.sqlExe(
|
||
"SELECT pay_feerate FROM payment_log WHERE id=${o}$ LIMIT 1", {'o': orig_orderid})
|
||
await sor.sqlExe("COMMIT", {})
|
||
if prec:
|
||
try:
|
||
feerate = float(getattr(prec[0], 'pay_feerate', 0) or 0)
|
||
except (TypeError, ValueError):
|
||
feerate = 0.0
|
||
|
||
# 5. 冲正记账(引擎按 endswith('_REVERSE') 翻转借贷方向;transdate=当前营业日)
|
||
amount = float(getattr(bill, 'amount', 0) or 0)
|
||
biz_date = await get_business_date(sor)
|
||
try:
|
||
await recharge_accounting(sor, customerid, 'RECHARGE_REVERSE', rev_orderid,
|
||
biz_date, amount, feerate)
|
||
await sor.sqlExe("COMMIT", {})
|
||
except Exception as e:
|
||
return json.dumps({'success': False,
|
||
'error': '冲正记账失败:%s' % str(e)[:200]}, ensure_ascii=False)
|
||
|
||
# 6. 审计留痕(旁路,失败不阻断)
|
||
try:
|
||
from app_audit import audit_log
|
||
op_recs = await sor.sqlExe("SELECT username FROM users WHERE id=${u}$", {'u': uid})
|
||
op_name = (op_recs[0].username if op_recs else uid)
|
||
await audit_log(sor, uid, op_name, 'recharge_reverse', target=customerid,
|
||
detail='充值错帐冲正 bill=%s 金额=%s 原因=%s' % (bill_id, amount, reason or '(未填)'),
|
||
result='ok', client_ip='')
|
||
await sor.sqlExe("COMMIT", {})
|
||
except Exception:
|
||
pass
|
||
|
||
return json.dumps({'success': True,
|
||
'message': '冲正成功:¥%.2f(原账单 %s)' % (amount, bill_id)},
|
||
ensure_ascii=False)
|