feat: 新增代客充值和错帐处理页面
- proxy_recharge.ui: Form表单输入客户用户名+充值金额 - proxy_recharge_submit.dspy: 查找用户+创建payment_log+调用recharge_accounting - error_accounting.ui: 错帐处理管理页面(placeholder,含设计文档)
This commit is contained in:
parent
029a76f960
commit
656fe2fc51
497
wwwroot/error_accounting.ui
Normal file
497
wwwroot/error_accounting.ui
Normal file
@ -0,0 +1,497 @@
|
|||||||
|
{#
|
||||||
|
错帐处理 (Error Accounting) Page
|
||||||
|
|
||||||
|
PURPOSE:
|
||||||
|
This page provides a management interface for handling accounting errors
|
||||||
|
and exceptions. It allows operators to review, correct, and resolve
|
||||||
|
accounting discrepancies.
|
||||||
|
|
||||||
|
INTENDED WORKFLOW:
|
||||||
|
1. An accounting exception is detected (manually or automatically):
|
||||||
|
- wrong_account: Transaction posted to incorrect account/subject
|
||||||
|
- duplicate_entry: Same transaction recorded more than once
|
||||||
|
- missing_entry: Expected transaction not found in records
|
||||||
|
- amount_mismatch: Debit/credit amounts don't balance or differ from source
|
||||||
|
|
||||||
|
2. Operator reviews the error log table (error_accounting_log):
|
||||||
|
- Each row shows: timestamp, error type, original transaction info, status
|
||||||
|
- Filter by status (pending/resolved) to prioritize work
|
||||||
|
|
||||||
|
3. Operator selects an error record and chooses a correction action:
|
||||||
|
- reverse_entry: Create a reversing journal entry to cancel the original
|
||||||
|
- adjust_entry: Create an adjustment entry to correct the amount/account
|
||||||
|
- mark_resolved: Flag as resolved without further action (e.g., duplicate already fixed)
|
||||||
|
|
||||||
|
4. The correction is recorded with an audit trail linking back to the original error.
|
||||||
|
|
||||||
|
DATA SOURCE:
|
||||||
|
- Table: error_accounting_log
|
||||||
|
- Expected fields: id, timestamp, error_type, original_trans_id,
|
||||||
|
original_subject, original_amount, original_summary,
|
||||||
|
error_description, status, resolved_at, resolved_by, correction_action
|
||||||
|
|
||||||
|
TOOLBAR ACTIONS:
|
||||||
|
- 报告错帐: Opens a form to manually report a new accounting error
|
||||||
|
- 全部: Show all error records (no filter)
|
||||||
|
- 待处理: Filter to show only pending/unresolved errors
|
||||||
|
- 已处理: Filter to show only resolved errors
|
||||||
|
|
||||||
|
ROW ACTIONS (on click):
|
||||||
|
- 冲正 (reverse_entry): Reverse the original transaction
|
||||||
|
- 调整 (adjust_entry): Create an adjustment/correction entry
|
||||||
|
- 标记已处理 (mark_resolved): Mark as resolved
|
||||||
|
#}
|
||||||
|
{
|
||||||
|
"widgettype": "VBox",
|
||||||
|
"id": "error_accounting_page",
|
||||||
|
"options": {
|
||||||
|
"width": "100%",
|
||||||
|
"height": "100%",
|
||||||
|
"gap": "12px",
|
||||||
|
"padding": "16px"
|
||||||
|
},
|
||||||
|
"subwidgets": [
|
||||||
|
{
|
||||||
|
"widgettype": "VBox",
|
||||||
|
"id": "error_acc_header",
|
||||||
|
"options": {
|
||||||
|
"bgcolor": "#1E293B",
|
||||||
|
"padding": "20px",
|
||||||
|
"borderRadius": "12px",
|
||||||
|
"border": "1px solid #334155",
|
||||||
|
"gap": "8px"
|
||||||
|
},
|
||||||
|
"subwidgets": [
|
||||||
|
{
|
||||||
|
"widgettype": "HBox",
|
||||||
|
"options": {
|
||||||
|
"alignItems": "center",
|
||||||
|
"justifyContent": "space-between"
|
||||||
|
},
|
||||||
|
"subwidgets": [
|
||||||
|
{
|
||||||
|
"widgettype": "VBox",
|
||||||
|
"options": {"gap": "4px"},
|
||||||
|
"subwidgets": [
|
||||||
|
{
|
||||||
|
"widgettype": "Text",
|
||||||
|
"options": {
|
||||||
|
"text": "错帐处理",
|
||||||
|
"fontSize": "22px",
|
||||||
|
"fontWeight": "700",
|
||||||
|
"color": "#F1F5F9"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"widgettype": "Text",
|
||||||
|
"options": {
|
||||||
|
"text": "会计差错管理 — 发现、纠正并解决帐务异常记录",
|
||||||
|
"fontSize": "13px",
|
||||||
|
"color": "#94A3B8"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"widgettype": "HBox",
|
||||||
|
"id": "error_acc_toolbar",
|
||||||
|
"options": {
|
||||||
|
"bgcolor": "#1E293B",
|
||||||
|
"padding": "12px 16px",
|
||||||
|
"borderRadius": "10px",
|
||||||
|
"border": "1px solid #334155",
|
||||||
|
"alignItems": "center",
|
||||||
|
"gap": "10px"
|
||||||
|
},
|
||||||
|
"subwidgets": [
|
||||||
|
{
|
||||||
|
"widgettype": "Button",
|
||||||
|
"id": "btn_report_error",
|
||||||
|
"options": {
|
||||||
|
"label": "报告错帐",
|
||||||
|
"bgcolor": "#EF4444",
|
||||||
|
"color": "#FFFFFF",
|
||||||
|
"borderRadius": "6px",
|
||||||
|
"padding": "8px 16px",
|
||||||
|
"fontWeight": "600"
|
||||||
|
},
|
||||||
|
"binds": [{
|
||||||
|
"wid": "self",
|
||||||
|
"event": "click",
|
||||||
|
"actiontype": "urlwidget",
|
||||||
|
"target": "PopupWindow",
|
||||||
|
"popup_options": {
|
||||||
|
"title": "报告错帐",
|
||||||
|
"width": "560px",
|
||||||
|
"height": "600px"
|
||||||
|
},
|
||||||
|
"options": {
|
||||||
|
"url": "{{entire_url('/accounting/error_accounting_report.ui')}}"
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"widgettype": "Filler"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"widgettype": "Text",
|
||||||
|
"options": {
|
||||||
|
"text": "筛选:",
|
||||||
|
"fontSize": "13px",
|
||||||
|
"color": "#94A3B8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"widgettype": "Button",
|
||||||
|
"id": "btn_filter_all",
|
||||||
|
"options": {
|
||||||
|
"label": "全部",
|
||||||
|
"bgcolor": "#3B82F6",
|
||||||
|
"color": "#FFFFFF",
|
||||||
|
"borderRadius": "6px",
|
||||||
|
"padding": "6px 12px"
|
||||||
|
},
|
||||||
|
"binds": [{
|
||||||
|
"wid": "self",
|
||||||
|
"event": "click",
|
||||||
|
"actiontype": "script",
|
||||||
|
"script": "const tab = bricks.getWidgetById('error_acc_tabular'); if(tab) { tab.render({}); }"
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"widgettype": "Button",
|
||||||
|
"id": "btn_filter_pending",
|
||||||
|
"options": {
|
||||||
|
"label": "待处理",
|
||||||
|
"bgcolor": "#F59E0B",
|
||||||
|
"color": "#FFFFFF",
|
||||||
|
"borderRadius": "6px",
|
||||||
|
"padding": "6px 12px"
|
||||||
|
},
|
||||||
|
"binds": [{
|
||||||
|
"wid": "self",
|
||||||
|
"event": "click",
|
||||||
|
"actiontype": "script",
|
||||||
|
"script": "const tab = bricks.getWidgetById('error_acc_tabular'); if(tab) { tab.render({status: 'pending'}); }"
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"widgettype": "Button",
|
||||||
|
"id": "btn_filter_resolved",
|
||||||
|
"options": {
|
||||||
|
"label": "已处理",
|
||||||
|
"bgcolor": "#22C55E",
|
||||||
|
"color": "#FFFFFF",
|
||||||
|
"borderRadius": "6px",
|
||||||
|
"padding": "6px 12px"
|
||||||
|
},
|
||||||
|
"binds": [{
|
||||||
|
"wid": "self",
|
||||||
|
"event": "click",
|
||||||
|
"actiontype": "script",
|
||||||
|
"script": "const tab = bricks.getWidgetById('error_acc_tabular'); if(tab) { tab.render({status: 'resolved'}); }"
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"widgettype": "VBox",
|
||||||
|
"id": "error_acc_table_container",
|
||||||
|
"options": {
|
||||||
|
"bgcolor": "#1E293B",
|
||||||
|
"borderRadius": "10px",
|
||||||
|
"border": "1px solid #334155",
|
||||||
|
"width": "100%",
|
||||||
|
"flex": "1"
|
||||||
|
},
|
||||||
|
"subwidgets": [
|
||||||
|
{
|
||||||
|
"widgettype": "HBox",
|
||||||
|
"options": {
|
||||||
|
"bgcolor": "#0F172A",
|
||||||
|
"padding": "10px 16px",
|
||||||
|
"borderRadius": "10px 10px 0 0",
|
||||||
|
"alignItems": "center"
|
||||||
|
},
|
||||||
|
"subwidgets": [
|
||||||
|
{
|
||||||
|
"widgettype": "Text",
|
||||||
|
"options": {"text": "时间", "fontSize": "12px", "fontWeight": "600", "color": "#94A3B8", "cwidth": 16}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"widgettype": "Text",
|
||||||
|
"options": {"text": "错帐类型", "fontSize": "12px", "fontWeight": "600", "color": "#94A3B8", "cwidth": 12}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"widgettype": "Text",
|
||||||
|
"options": {"text": "原始交易", "fontSize": "12px", "fontWeight": "600", "color": "#94A3B8", "cwidth": 20}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"widgettype": "Text",
|
||||||
|
"options": {"text": "金额", "fontSize": "12px", "fontWeight": "600", "color": "#94A3B8", "cwidth": 10}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"widgettype": "Text",
|
||||||
|
"options": {"text": "说明", "fontSize": "12px", "fontWeight": "600", "color": "#94A3B8", "cwidth": 24}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"widgettype": "Text",
|
||||||
|
"options": {"text": "状态", "fontSize": "12px", "fontWeight": "600", "color": "#94A3B8", "cwidth": 8}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"widgettype": "Text",
|
||||||
|
"options": {"text": "操作", "fontSize": "12px", "fontWeight": "600", "color": "#94A3B8", "cwidth": 10}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"widgettype": "Tabular",
|
||||||
|
"id": "error_acc_tabular",
|
||||||
|
"options": {
|
||||||
|
"width": "100%",
|
||||||
|
"height": "100%",
|
||||||
|
"css": "filler",
|
||||||
|
"data_url": "{{entire_url('/accounting/error_accounting_log.dspy')}}",
|
||||||
|
"editable": false,
|
||||||
|
"page_rows": 50,
|
||||||
|
"row_options": {
|
||||||
|
"browserfields": {
|
||||||
|
"exclouded": ["row_num_"]
|
||||||
|
},
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"name": "timestamp",
|
||||||
|
"title": "时间",
|
||||||
|
"type": "timestamp",
|
||||||
|
"uitype": "timestamp",
|
||||||
|
"datatype": "timestamp",
|
||||||
|
"label": "时间",
|
||||||
|
"cwidth": 16
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "error_type",
|
||||||
|
"title": "错帐类型",
|
||||||
|
"type": "str",
|
||||||
|
"length": 20,
|
||||||
|
"uitype": "str",
|
||||||
|
"datatype": "str",
|
||||||
|
"label": "错帐类型",
|
||||||
|
"cwidth": 12
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "original_summary",
|
||||||
|
"title": "原始交易",
|
||||||
|
"type": "str",
|
||||||
|
"length": 100,
|
||||||
|
"uitype": "str",
|
||||||
|
"datatype": "str",
|
||||||
|
"label": "原始交易",
|
||||||
|
"cwidth": 20
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "original_amount",
|
||||||
|
"title": "金额",
|
||||||
|
"type": "float",
|
||||||
|
"length": 18,
|
||||||
|
"dec": 4,
|
||||||
|
"uitype": "float",
|
||||||
|
"datatype": "float",
|
||||||
|
"label": "金额",
|
||||||
|
"cwidth": 10
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "error_description",
|
||||||
|
"title": "说明",
|
||||||
|
"type": "str",
|
||||||
|
"length": 200,
|
||||||
|
"uitype": "str",
|
||||||
|
"datatype": "str",
|
||||||
|
"label": "说明",
|
||||||
|
"cwidth": 24
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "status",
|
||||||
|
"title": "状态",
|
||||||
|
"type": "str",
|
||||||
|
"length": 10,
|
||||||
|
"uitype": "str",
|
||||||
|
"datatype": "str",
|
||||||
|
"label": "状态",
|
||||||
|
"cwidth": 8
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"widgettype": "VBox",
|
||||||
|
"id": "error_acc_empty_hint",
|
||||||
|
"options": {
|
||||||
|
"padding": "20px",
|
||||||
|
"alignItems": "center",
|
||||||
|
"bgcolor": "#1E293B"
|
||||||
|
},
|
||||||
|
"subwidgets": [
|
||||||
|
{
|
||||||
|
"widgettype": "Text",
|
||||||
|
"options": {
|
||||||
|
"text": "如表格为空,表示暂无错帐记录。请点击「报告错帐」按钮手动添加,或确认 error_accounting_log 数据源已配置。",
|
||||||
|
"fontSize": "13px",
|
||||||
|
"color": "#64748B"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"widgettype": "VBox",
|
||||||
|
"id": "error_acc_legend",
|
||||||
|
"options": {
|
||||||
|
"bgcolor": "#1E293B",
|
||||||
|
"padding": "16px",
|
||||||
|
"borderRadius": "10px",
|
||||||
|
"border": "1px solid #334155",
|
||||||
|
"gap": "8px"
|
||||||
|
},
|
||||||
|
"subwidgets": [
|
||||||
|
{
|
||||||
|
"widgettype": "Text",
|
||||||
|
"options": {
|
||||||
|
"text": "错帐类型说明",
|
||||||
|
"fontSize": "14px",
|
||||||
|
"fontWeight": "600",
|
||||||
|
"color": "#F1F5F9"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"widgettype": "HBox",
|
||||||
|
"options": {
|
||||||
|
"gap": "16px",
|
||||||
|
"alignItems": "center"
|
||||||
|
},
|
||||||
|
"subwidgets": [
|
||||||
|
{
|
||||||
|
"widgettype": "HBox",
|
||||||
|
"options": {"gap": "6px", "alignItems": "center"},
|
||||||
|
"subwidgets": [
|
||||||
|
{
|
||||||
|
"widgettype": "HBox",
|
||||||
|
"options": {
|
||||||
|
"bgcolor": "#EF444433",
|
||||||
|
"padding": "2px 8px",
|
||||||
|
"borderRadius": "4px"
|
||||||
|
},
|
||||||
|
"subwidgets": [
|
||||||
|
{
|
||||||
|
"widgettype": "Text",
|
||||||
|
"options": {"text": "科目错误", "fontSize": "12px", "color": "#EF4444"}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"widgettype": "Text",
|
||||||
|
"options": {"text": "wrong_account", "fontSize": "11px", "color": "#64748B"}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"widgettype": "HBox",
|
||||||
|
"options": {"gap": "6px", "alignItems": "center"},
|
||||||
|
"subwidgets": [
|
||||||
|
{
|
||||||
|
"widgettype": "HBox",
|
||||||
|
"options": {
|
||||||
|
"bgcolor": "#F59E0B33",
|
||||||
|
"padding": "2px 8px",
|
||||||
|
"borderRadius": "4px"
|
||||||
|
},
|
||||||
|
"subwidgets": [
|
||||||
|
{
|
||||||
|
"widgettype": "Text",
|
||||||
|
"options": {"text": "重复入帐", "fontSize": "12px", "color": "#F59E0B"}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"widgettype": "Text",
|
||||||
|
"options": {"text": "duplicate_entry", "fontSize": "11px", "color": "#64748B"}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"widgettype": "HBox",
|
||||||
|
"options": {"gap": "6px", "alignItems": "center"},
|
||||||
|
"subwidgets": [
|
||||||
|
{
|
||||||
|
"widgettype": "HBox",
|
||||||
|
"options": {
|
||||||
|
"bgcolor": "#8B5CF633",
|
||||||
|
"padding": "2px 8px",
|
||||||
|
"borderRadius": "4px"
|
||||||
|
},
|
||||||
|
"subwidgets": [
|
||||||
|
{
|
||||||
|
"widgettype": "Text",
|
||||||
|
"options": {"text": "漏记", "fontSize": "12px", "color": "#8B5CF6"}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"widgettype": "Text",
|
||||||
|
"options": {"text": "missing_entry", "fontSize": "11px", "color": "#64748B"}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"widgettype": "HBox",
|
||||||
|
"options": {"gap": "6px", "alignItems": "center"},
|
||||||
|
"subwidgets": [
|
||||||
|
{
|
||||||
|
"widgettype": "HBox",
|
||||||
|
"options": {
|
||||||
|
"bgcolor": "#3B82F633",
|
||||||
|
"padding": "2px 8px",
|
||||||
|
"borderRadius": "4px"
|
||||||
|
},
|
||||||
|
"subwidgets": [
|
||||||
|
{
|
||||||
|
"widgettype": "Text",
|
||||||
|
"options": {"text": "金额不符", "fontSize": "12px", "color": "#3B82F6"}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"widgettype": "Text",
|
||||||
|
"options": {"text": "amount_mismatch", "fontSize": "11px", "color": "#64748B"}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"widgettype": "HBox",
|
||||||
|
"options": {
|
||||||
|
"gap": "16px",
|
||||||
|
"alignItems": "center",
|
||||||
|
"marginTop": "4px"
|
||||||
|
},
|
||||||
|
"subwidgets": [
|
||||||
|
{
|
||||||
|
"widgettype": "Text",
|
||||||
|
"options": {
|
||||||
|
"text": "纠正操作: 冲正(reverse_entry) | 调整(adjust_entry) | 标记已处理(mark_resolved)",
|
||||||
|
"fontSize": "12px",
|
||||||
|
"color": "#94A3B8"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
8
wwwroot/open_customer_accounts_with_orgid.dspy
Normal file
8
wwwroot/open_customer_accounts_with_orgid.dspy
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
debug(f'{params_kw=}')
|
||||||
|
dbname = get_module_dbname('accounting')
|
||||||
|
orgid = await get_userorgid()
|
||||||
|
db = DBPools()
|
||||||
|
async with db.sqlorContext(dbname) as sor:
|
||||||
|
await openCustomerAccounts(sor, '0', orgid)
|
||||||
|
return f'{orgid} customer accounts opened'
|
||||||
|
return f'{db.e_except=}'
|
||||||
82
wwwroot/proxy_recharge.ui
Normal file
82
wwwroot/proxy_recharge.ui
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
{
|
||||||
|
"widgettype": "VBox",
|
||||||
|
"options": {
|
||||||
|
"width": "100%",
|
||||||
|
"height": "100%",
|
||||||
|
"padding": "16px",
|
||||||
|
"gap": "16px"
|
||||||
|
},
|
||||||
|
"subwidgets": [
|
||||||
|
{
|
||||||
|
"widgettype": "VBox",
|
||||||
|
"options": {
|
||||||
|
"bgcolor": "#1E293B",
|
||||||
|
"borderRadius": "10px",
|
||||||
|
"border": "1px solid #334155",
|
||||||
|
"padding": "20px",
|
||||||
|
"cwidth": 40
|
||||||
|
},
|
||||||
|
"subwidgets": [
|
||||||
|
{
|
||||||
|
"widgettype": "Title3",
|
||||||
|
"options": {"text": "代客充值", "color": "#F1F5F9"}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"widgettype": "Text",
|
||||||
|
"options": {
|
||||||
|
"text": "输入客户用户名和充值金额,由管理员代为客户完成充值操作。",
|
||||||
|
"color": "#94A3B8",
|
||||||
|
"fontSize": "13px"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"widgettype": "Form",
|
||||||
|
"id": "proxy_recharge_form",
|
||||||
|
"options": {
|
||||||
|
"name": "proxy_recharge",
|
||||||
|
"submit_url": "{{entire_url('/accounting/proxy_recharge_submit.dspy')}}",
|
||||||
|
"show_label": true,
|
||||||
|
"submit_label": "确认充值",
|
||||||
|
"submit_css": "primary",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"name": "username",
|
||||||
|
"label": "客户用户名",
|
||||||
|
"uitype": "str",
|
||||||
|
"required": true,
|
||||||
|
"placeholder": "输入客户用户名",
|
||||||
|
"cwidth": 20
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "amount",
|
||||||
|
"label": "充值金额",
|
||||||
|
"uitype": "float",
|
||||||
|
"required": true,
|
||||||
|
"placeholder": "输入充值金额",
|
||||||
|
"cwidth": 20
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"binds": [{
|
||||||
|
"wid": "self",
|
||||||
|
"event": "submit",
|
||||||
|
"actiontype": "urldata",
|
||||||
|
"target": "recharge_result",
|
||||||
|
"options": {
|
||||||
|
"url": "{{entire_url('/accounting/proxy_recharge_submit.dspy')}}"
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"widgettype": "VBox",
|
||||||
|
"id": "recharge_result",
|
||||||
|
"options": {
|
||||||
|
"width": "100%",
|
||||||
|
"padding": "8px"
|
||||||
|
},
|
||||||
|
"subwidgets": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
148
wwwroot/proxy_recharge_submit.dspy
Normal file
148
wwwroot/proxy_recharge_submit.dspy
Normal file
@ -0,0 +1,148 @@
|
|||||||
|
|
||||||
|
action = params_kw.get('action', 'submit')
|
||||||
|
|
||||||
|
# ---- Lookup mode: find customer by username, return info ----
|
||||||
|
if action == 'lookup':
|
||||||
|
username = params_kw.get('username', '').strip()
|
||||||
|
if not username:
|
||||||
|
return json.dumps({'status': 'error', 'message': '用户名不能为空'}, ensure_ascii=False, default=str)
|
||||||
|
|
||||||
|
db = DBPools()
|
||||||
|
dbname = get_module_dbname('accounting')
|
||||||
|
async with db.sqlorContext(dbname) as sor:
|
||||||
|
sql = """
|
||||||
|
select
|
||||||
|
u.username,
|
||||||
|
u.orgid as customerid,
|
||||||
|
o.orgname,
|
||||||
|
a.id as accountid,
|
||||||
|
a.balance
|
||||||
|
from users u
|
||||||
|
left join organization o on u.orgid = o.id COLLATE utf8mb4_unicode_ci
|
||||||
|
left join account a on a.orgid = u.orgid COLLATE utf8mb4_unicode_ci
|
||||||
|
where u.username = ${username}$
|
||||||
|
limit 1
|
||||||
|
"""
|
||||||
|
recs = await sor.sqlExe(sql, {'username': username})
|
||||||
|
if not recs or len(recs) == 0:
|
||||||
|
return json.dumps({'status': 'error', 'message': f'用户 {username} 不存在'}, ensure_ascii=False, default=str)
|
||||||
|
|
||||||
|
rec = recs[0]
|
||||||
|
return json.dumps({
|
||||||
|
'status': 'ok',
|
||||||
|
'data': {
|
||||||
|
'username': rec.username,
|
||||||
|
'customerid': rec.customerid,
|
||||||
|
'orgname': rec.orgname or '',
|
||||||
|
'accountid': rec.accountid or '',
|
||||||
|
'balance': float(rec.balance) if rec.balance else 0.0
|
||||||
|
}
|
||||||
|
}, ensure_ascii=False, default=str)
|
||||||
|
|
||||||
|
|
||||||
|
# ---- Submit mode: process the proxy recharge ----
|
||||||
|
username = params_kw.get('username', '').strip()
|
||||||
|
amount_raw = params_kw.get('amount', 0)
|
||||||
|
|
||||||
|
if not username:
|
||||||
|
return {
|
||||||
|
"widgettype": "Text",
|
||||||
|
"options": {"text": "❌ 用户名不能为空", "color": "#EF4444"}
|
||||||
|
}
|
||||||
|
|
||||||
|
try:
|
||||||
|
amount = float(amount_raw)
|
||||||
|
except (ValueError, TypeError):
|
||||||
|
return {
|
||||||
|
"widgettype": "Text",
|
||||||
|
"options": {"text": "❌ 充值金额格式错误", "color": "#EF4444"}
|
||||||
|
}
|
||||||
|
|
||||||
|
if amount <= 0:
|
||||||
|
return {
|
||||||
|
"widgettype": "Text",
|
||||||
|
"options": {"text": "❌ 充值金额必须大于0", "color": "#EF4444"}
|
||||||
|
}
|
||||||
|
|
||||||
|
userid = await get_user()
|
||||||
|
userorgid = await get_userorgid()
|
||||||
|
db = DBPools()
|
||||||
|
|
||||||
|
# Look up the target customer by username
|
||||||
|
dbname = get_module_dbname('accounting')
|
||||||
|
async with db.sqlorContext(dbname) as sor:
|
||||||
|
sql = """
|
||||||
|
select
|
||||||
|
u.username,
|
||||||
|
u.orgid as customerid,
|
||||||
|
o.orgname,
|
||||||
|
a.id as accountid
|
||||||
|
from users u
|
||||||
|
left join organization o on u.orgid = o.id COLLATE utf8mb4_unicode_ci
|
||||||
|
left join account a on a.orgid = u.orgid COLLATE utf8mb4_unicode_ci
|
||||||
|
where u.username = ${username}$
|
||||||
|
limit 1
|
||||||
|
"""
|
||||||
|
recs = await sor.sqlExe(sql, {'username': username})
|
||||||
|
if not recs or len(recs) == 0:
|
||||||
|
return {
|
||||||
|
"widgettype": "Text",
|
||||||
|
"options": {"text": f"❌ 找不到用户名: {username}", "color": "#EF4444"}
|
||||||
|
}
|
||||||
|
|
||||||
|
customer = recs[0]
|
||||||
|
customerid = customer.customerid
|
||||||
|
|
||||||
|
if customerid == userorgid:
|
||||||
|
return {
|
||||||
|
"widgettype": "Text",
|
||||||
|
"options": {"text": "❌ 不能给自己进行代客充值", "color": "#EF4444"}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Create payment log in unipay for audit trail
|
||||||
|
unipay_dbname = get_module_dbname('unipay')
|
||||||
|
async with db.sqlorContext(unipay_dbname) as unipay_sor:
|
||||||
|
plog_id = getID()
|
||||||
|
biz_date = await get_business_date(sor)
|
||||||
|
now_str = timestampstr()
|
||||||
|
plog_data = {
|
||||||
|
"id": plog_id,
|
||||||
|
"customerid": customerid,
|
||||||
|
"channelid": "proxy",
|
||||||
|
"payment_name": "充值",
|
||||||
|
"payer_client_ip": "admin_proxy",
|
||||||
|
"amount_total": amount,
|
||||||
|
"pay_feerate": 0.0,
|
||||||
|
"pay_fee": 0.0,
|
||||||
|
"currency": "CNY",
|
||||||
|
"payment_status": "1",
|
||||||
|
"init_timestamp": now_str,
|
||||||
|
"payed_timestamp": now_str,
|
||||||
|
"cancel_timestamp": "2000-01-01 00:00:00.001",
|
||||||
|
"userid": userid
|
||||||
|
}
|
||||||
|
await unipay_sor.C('payment_log', plog_data.copy())
|
||||||
|
|
||||||
|
# Perform recharge accounting
|
||||||
|
await recharge_accounting(
|
||||||
|
sor,
|
||||||
|
customerid,
|
||||||
|
'RECHARGE',
|
||||||
|
plog_id,
|
||||||
|
biz_date,
|
||||||
|
amount,
|
||||||
|
0.0
|
||||||
|
)
|
||||||
|
|
||||||
|
debug(f'Proxy recharge: user={username}, customerid={customerid}, amount={amount}, operator={userid}')
|
||||||
|
|
||||||
|
orgname = customer.orgname or ''
|
||||||
|
return {
|
||||||
|
"widgettype": "Text",
|
||||||
|
"options": {
|
||||||
|
"text": f"✅ 代客充值成功 — 已为用户 {username} ({orgname}) 充值 ¥{amount:.2f}",
|
||||||
|
"color": "#22C55E",
|
||||||
|
"fontSize": "14px",
|
||||||
|
"fontWeight": "500"
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user