feat: 结算处理+供应商充值后端dspy - get_reseller/provider_settlement: 查询未结算的supplychain_accounting记录 - add/update_settlement: 调用SettleAccounting发起结算 - get/add/update_provider_recharge: 供应商充值记录CRUD

This commit is contained in:
Hermes Agent 2026-06-18 13:51:45 +08:00
parent a175d79cc2
commit ae8e323f16
8 changed files with 533 additions and 0 deletions

View File

@ -0,0 +1,60 @@
ns = params_kw.copy()
supplier_id = ns.get('supplier_id', '')
amount = ns.get('amount', 0)
payment_method = ns.get('payment_method', '')
transaction_no = ns.get('transaction_no', '')
remark = ns.get('remark', '')
if not supplier_id:
return json.dumps({'success': False, 'error': '请选择供应商'}, ensure_ascii=False)
if not amount or float(amount) <= 0:
return json.dumps({'success': False, 'error': '充值金额必须大于0'}, ensure_ascii=False)
acc_dbname = get_module_dbname('accounting')
userorgid = await get_userorgid()
acc_id = getID()
bill_id = getID()
# 创建充值记录到accounting_log
log_data = {
'id': acc_id,
'accountid': supplier_id,
'acc_date': curDateString(),
'acc_dir': 'C',
'amount': float(amount),
'summary': f'供应商充值 - {payment_method} {transaction_no}',
'business_op': '供应商充值',
'billid': bill_id,
'created_at': timestampstr()
}
# 创建bill记录
bill_data = {
'id': bill_id,
'customerid': supplier_id,
'resellerid': userorgid,
'business_op': '供应商充值',
'amount': float(amount),
'bill_date': curDateString(),
'bill_timestamp': timestampstr(),
'bill_state': '1'
}
db = DBPools()
try:
async with db.sqlorContext(acc_dbname) as sor:
await sor.C('bill', bill_data)
await sor.C('accounting_log', log_data)
debug(f'provider_recharge: created {acc_id}, supplier={supplier_id}, amt={amount}')
return json.dumps({
'success': True,
'message': f'充值记录已创建,金额: {amount}',
'id': acc_id,
'bill_id': bill_id
}, ensure_ascii=False, default=str)
except Exception as e:
debug(f'provider_recharge error: {format_exc()}')
return json.dumps({'success': False, 'error': f'充值失败: {str(e)}'}, ensure_ascii=False)

View File

@ -0,0 +1,70 @@
ns = params_kw.copy()
record_id = ns.get('id', '')
remark = ns.get('remark', '')
payment_method = ns.get('payment_method', '')
if not record_id:
return json.dumps({'success': False, 'error': '缺少记录ID'}, ensure_ascii=False)
sc_dbname = get_module_dbname('supplychain')
acc_dbname = get_module_dbname('accounting')
userorgid = await get_userorgid()
# 查询supplychain_accounting记录
db = DBPools()
async with db.sqlorContext(sc_dbname) as sor:
recs = await sor.sqlExe("""
SELECT sa.*, sp.supplier_name
FROM supplychain_accounting sa
LEFT JOIN suppliers sp ON sa.supplier_id = sp.id COLLATE utf8mb4_unicode_ci
WHERE sa.id = ${record_id}$
""", {'record_id': record_id})
if not recs:
return json.dumps({'success': False, 'error': '记录不存在'}, ensure_ascii=False)
rec = recs[0]
# 供应商结算: supply_amount是我方应付给供应商的金额
from accounting.settle import SettleAccounting
settle_log = {
'accounting_orgid': userorgid,
'providerid': rec.supplier_id or '',
'sale_mode': '1',
'settle_date': str(rec.sale_date or curDateString()),
'settle_amt': float(rec.supply_amount or 0),
'business_op': '供应商结算',
'remark': remark
}
try:
async with db.sqlorContext(acc_dbname) as acc_sor:
sa = SettleAccounting(settle_log)
await sa.accounting(acc_sor)
# 更新sales_ledger
async with db.sqlorContext(sc_dbname) as sc_sor:
await sc_sor.sqlExe("""
UPDATE sales_ledger
SET settlement_status = '1', updated_at = NOW()
WHERE productid = ${productid}$
AND supplier_id = ${supplier_id}$
AND sale_date = ${sale_date}$
""", {
'productid': rec.productid,
'supplier_id': rec.supplier_id,
'sale_date': str(rec.sale_date)
})
debug(f'provider_settlement: settled {record_id}, amt={rec.supply_amount}')
return json.dumps({
'success': True,
'message': f'结算成功,金额: {rec.supply_amount}',
'settle_id': sa.settleid,
'bill_id': sa.billid
}, ensure_ascii=False, default=str)
except Exception as e:
debug(f'provider_settlement error: {format_exc()}')
return json.dumps({'success': False, 'error': f'结算失败: {str(e)}'}, ensure_ascii=False)

View File

@ -0,0 +1,86 @@
ns = params_kw.copy()
ns['page'] = ns.get('page') or 1
ns['sort'] = ns.get('sort') or 'acc_date'
date_start = ns.get('date_start', '')
date_end = ns.get('date_end', '')
supplier_id = ns.get('supplier_id', '')
recharge_status = ns.get('recharge_status', '')
acc_dbname = get_module_dbname('accounting')
sc_dbname = get_module_dbname('supplychain')
userorgid = await get_userorgid()
# 查询供应商充值记录: accounting_log中business_op='供应商充值'的记录
where = "WHERE al.business_op = '供应商充值'"
sql_params = {}
if date_start:
where += " AND al.acc_date >= ${date_start}$"
sql_params['date_start'] = date_start
if date_end:
where += " AND al.acc_date <= ${date_end}$"
sql_params['date_end'] = date_end
sql = f"""
SELECT
al.id,
al.accountid,
al.acc_date as recharge_date,
al.amount,
al.acc_dir,
al.summary,
al.business_op,
al.billid,
b.customerid as supplier_id,
sp.supplier_name as supplier_id_text,
CASE
WHEN al.billid IS NOT NULL AND al.billid != '' THEN '2'
WHEN al.amount > 0 THEN '1'
ELSE '0'
END as recharge_status,
al.created_at
FROM accounting_log al
LEFT JOIN bill b ON al.billid = b.id COLLATE utf8mb4_unicode_ci
LEFT JOIN (
SELECT id, supplier_name FROM suppliers
) sp ON b.customerid = sp.id COLLATE utf8mb4_unicode_ci
{where}
ORDER BY al.acc_date DESC, al.id
"""
if supplier_id:
sql = f"SELECT * FROM ({sql}) t WHERE t.supplier_id = ${supplier_id}$"
sql_params['supplier_id'] = supplier_id
if recharge_status:
sql = f"SELECT * FROM ({sql}) t WHERE t.recharge_status = ${recharge_status}$"
sql_params['recharge_status'] = recharge_status
db = DBPools()
async with db.sqlorContext(acc_dbname) as sor:
recs = await sor.sqlExe(sql, sql_params)
rows = []
for r in (recs or []):
rows.append({
'id': r.id,
'supplier_id': r.supplier_id or '',
'supplier_id_text': r.supplier_id_text or '',
'recharge_date': str(r.recharge_date or ''),
'amount': float(r.amount or 0),
'payment_method': r.summary or '',
'transaction_no': r.billid or '',
'recharge_status': r.recharge_status or '0',
'remark': r.summary or '',
'created_at': str(r.created_at or '')
})
total = len(rows)
debug(f'get_provider_recharge: {total} records')
return json.dumps({
'success': True,
'total': total,
'rows': rows
}, ensure_ascii=False, default=str)

View File

@ -0,0 +1,86 @@
ns = params_kw.copy()
ns['page'] = ns.get('page') or 1
ns['sort'] = ns.get('sort') or 'sale_date'
date_start = ns.get('date_start', '')
date_end = ns.get('date_end', '')
supplier_id = ns.get('supplier_id', '')
settlement_status = ns.get('settlement_status', '')
sc_dbname = get_module_dbname('supplychain')
userorgid = await get_userorgid()
where = "WHERE sa.resellerid = ${userorgid}$"
sql_params = {'userorgid': userorgid}
if date_start:
where += " AND sa.sale_date >= ${date_start}$"
sql_params['date_start'] = date_start
if date_end:
where += " AND sa.sale_date <= ${date_end}$"
sql_params['date_end'] = date_end
if supplier_id:
where += " AND sa.supplier_id = ${supplier_id}$"
sql_params['supplier_id'] = supplier_id
sql = f"""
SELECT
sa.id,
sa.supplier_id,
sp.supplier_name as supplier_id_text,
sa.sale_date,
sa.sale_date as settlement_period,
p.name as product_name,
sa.productid,
sa.quantity,
sa.supply_amount as amount,
sa.dist_amount,
COALESCE(sl.settlement_status, '0') as settlement_status,
sa.source_type,
sa.source_id
FROM supplychain_accounting sa
LEFT JOIN product p ON sa.productid = p.id COLLATE utf8mb4_unicode_ci
LEFT JOIN suppliers sp ON sa.supplier_id = sp.id COLLATE utf8mb4_unicode_ci
LEFT JOIN sales_ledger sl ON sl.productid = sa.productid COLLATE utf8mb4_unicode_ci
AND sl.supplier_id = sa.supplier_id COLLATE utf8mb4_unicode_ci
AND sl.sale_date = sa.sale_date
{where}
ORDER BY sa.sale_date DESC, sa.id
"""
if settlement_status:
sql = f"""
SELECT * FROM ({sql}) t WHERE t.settlement_status = ${settlement_status}$
"""
sql_params['settlement_status'] = settlement_status
db = DBPools()
async with db.sqlorContext(sc_dbname) as sor:
recs = await sor.sqlExe(sql, sql_params)
rows = []
for r in (recs or []):
rows.append({
'id': r.id,
'supplier_id': r.supplier_id or '',
'supplier_id_text': r.supplier_id_text or '',
'settlement_period': str(r.sale_date or ''),
'product_name': r.product_name or '',
'amount': float(r.amount or 0),
'settlement_status': r.settlement_status or '0',
'settlement_date': '',
'payment_method': '',
'reference_no': r.source_id or '',
'remark': r.source_type or '',
'created_at': str(r.sale_date or '')
})
total = len(rows)
debug(f'get_provider_settlement: {total} records')
return json.dumps({
'success': True,
'total': total,
'rows': rows
}, ensure_ascii=False, default=str)

View File

@ -0,0 +1,87 @@
ns = params_kw.copy()
ns['page'] = ns.get('page') or 1
ns['sort'] = ns.get('sort') or 'sale_date'
date_start = ns.get('date_start', '')
date_end = ns.get('date_end', '')
sub_reseller_id = ns.get('sub_reseller_id', '')
settlement_status = ns.get('settlement_status', '')
sc_dbname = get_module_dbname('supplychain')
userorgid = await get_userorgid()
# 查询supplychain_accounting中该分销商的记录, 关联sales_ledger结算状态
where = "WHERE sa.resellerid = ${userorgid}$"
sql_params = {'userorgid': userorgid}
if date_start:
where += " AND sa.sale_date >= ${date_start}$"
sql_params['date_start'] = date_start
if date_end:
where += " AND sa.sale_date <= ${date_end}$"
sql_params['date_end'] = date_end
if sub_reseller_id:
where += " AND sa.sub_distributor_id = ${sub_reseller_id}$"
sql_params['sub_reseller_id'] = sub_reseller_id
sql = f"""
SELECT
sa.id,
sa.sub_distributor_id,
sr.sub_reseller_name as sub_reseller_id_text,
sa.sale_date,
sa.sale_date as settlement_period,
p.name as product_name,
sa.productid,
sa.quantity,
sa.dist_amount as amount,
sa.supply_amount,
COALESCE(sl.settlement_status, '0') as settlement_status,
sa.source_type,
sa.source_id
FROM supplychain_accounting sa
LEFT JOIN product p ON sa.productid = p.id COLLATE utf8mb4_unicode_ci
LEFT JOIN sub_resellers sr ON sa.sub_distributor_id = sr.id COLLATE utf8mb4_unicode_ci
LEFT JOIN sales_ledger sl ON sl.productid = sa.productid COLLATE utf8mb4_unicode_ci
AND sl.sub_reseller_id = sa.sub_distributor_id COLLATE utf8mb4_unicode_ci
AND sl.sale_date = sa.sale_date
{where}
ORDER BY sa.sale_date DESC, sa.id
"""
if settlement_status:
sql = f"""
SELECT * FROM ({sql}) t WHERE t.settlement_status = ${settlement_status}$
"""
sql_params['settlement_status'] = settlement_status
db = DBPools()
async with db.sqlorContext(sc_dbname) as sor:
recs = await sor.sqlExe(sql, sql_params)
rows = []
for r in (recs or []):
rows.append({
'id': r.id,
'sub_reseller_id': r.sub_distributor_id or '',
'sub_reseller_id_text': r.sub_reseller_id_text or '',
'settlement_period': str(r.sale_date or ''),
'product_name': r.product_name or '',
'amount': float(r.amount or 0),
'settlement_status': r.settlement_status or '0',
'settlement_date': '',
'payment_method': '',
'reference_no': r.source_id or '',
'remark': r.source_type or '',
'created_at': str(r.sale_date or '')
})
total = len(rows)
debug(f'get_reseller_settlement: {total} records')
return json.dumps({
'success': True,
'total': total,
'rows': rows
}, ensure_ascii=False, default=str)

View File

@ -0,0 +1,25 @@
ns = params_kw.copy()
record_id = ns.get('id', '')
remark = ns.get('remark', '')
if not record_id:
return json.dumps({'success': False, 'error': '缺少记录ID'}, ensure_ascii=False)
acc_dbname = get_module_dbname('accounting')
db = DBPools()
try:
async with db.sqlorContext(acc_dbname) as sor:
# 更新accounting_log的备注
await sor.U('accounting_log', {
'id': record_id,
'summary': remark
})
debug(f'update_provider_recharge: updated {record_id}')
return json.dumps({'success': True, 'message': '充值记录已更新'}, ensure_ascii=False)
except Exception as e:
debug(f'update_provider_recharge error: {format_exc()}')
return json.dumps({'success': False, 'error': f'更新失败: {str(e)}'}, ensure_ascii=False)

View File

@ -0,0 +1,41 @@
# 供应商结算确认(同add_provider_settlement逻辑update用于确认操作)
ns = params_kw.copy()
record_id = ns.get('id', '')
remark = ns.get('remark', '')
if not record_id:
return json.dumps({'success': False, 'error': '缺少记录ID'}, ensure_ascii=False)
sc_dbname = get_module_dbname('supplychain')
userorgid = await get_userorgid()
db = DBPools()
async with db.sqlorContext(sc_dbname) as sor:
recs = await sor.sqlExe("""
SELECT sa.* FROM supplychain_accounting sa
WHERE sa.id = ${record_id}$
""", {'record_id': record_id})
if not recs:
return json.dumps({'success': False, 'error': '记录不存在'}, ensure_ascii=False)
rec = recs[0]
# 更新结算状态
async with db.sqlorContext(sc_dbname) as sc_sor:
await sc_sor.sqlExe("""
UPDATE sales_ledger
SET settlement_status = '1', updated_at = NOW(), remark = ${remark}$
WHERE productid = ${productid}$
AND supplier_id = ${supplier_id}$
AND sale_date = ${sale_date}$
""", {
'productid': rec.productid,
'supplier_id': rec.supplier_id,
'sale_date': str(rec.sale_date),
'remark': remark
})
debug(f'update_provider_settlement: confirmed {record_id}')
return json.dumps({'success': True, 'message': '结算已确认'}, ensure_ascii=False)

View File

@ -0,0 +1,78 @@
import sys
import os
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
from accounting.settle import SettleAccounting, get_subjectid
from accounting.accounting_config import AccountingOrgs
ns = params_kw.copy()
record_id = ns.get('id', '')
remark = ns.get('remark', '')
payment_method = ns.get('payment_method', '')
if not record_id:
return json.dumps({'success': False, 'error': '缺少记录ID'}, ensure_ascii=False)
sc_dbname = get_module_dbname('supplychain')
acc_dbname = get_module_dbname('accounting')
userorgid = await get_userorgid()
# 查询supplychain_accounting记录
db = DBPools()
async with db.sqlorContext(sc_dbname) as sor:
recs = await sor.sqlExe("""
SELECT sa.*, sp.supplier_name, sr.sub_reseller_name
FROM supplychain_accounting sa
LEFT JOIN suppliers sp ON sa.supplier_id = sp.id COLLATE utf8mb4_unicode_ci
LEFT JOIN sub_resellers sr ON sa.sub_distributor_id = sr.id COLLATE utf8mb4_unicode_ci
WHERE sa.id = ${record_id}$
""", {'record_id': record_id})
if not recs:
return json.dumps({'success': False, 'error': '记录不存在'}, ensure_ascii=False)
rec = recs[0]
# 构建settle_log
settle_log = {
'accounting_orgid': userorgid,
'providerid': rec.supplier_id or '',
'sub_distributor_id': rec.sub_distributor_id or '',
'sale_mode': '0',
'settle_date': str(rec.sale_date or curDateString()),
'settle_amt': float(rec.dist_amount or 0),
'business_op': '分销商结算',
'remark': remark
}
# 调用SettleAccounting
try:
async with db.sqlorContext(acc_dbname) as acc_sor:
sa = SettleAccounting(settle_log)
await sa.accounting(acc_sor)
# 更新sales_ledger结算状态
async with db.sqlorContext(sc_dbname) as sc_sor:
await sc_sor.sqlExe("""
UPDATE sales_ledger
SET settlement_status = '1', updated_at = NOW()
WHERE productid = ${productid}$
AND sub_reseller_id = ${sub_distributor_id}$
AND sale_date = ${sale_date}$
""", {
'productid': rec.productid,
'sub_distributor_id': rec.sub_distributor_id,
'sale_date': str(rec.sale_date)
})
debug(f'reseller_settlement: settled {record_id}, amt={rec.dist_amount}')
return json.dumps({
'success': True,
'message': f'结算成功,金额: {rec.dist_amount}',
'settle_id': sa.settleid,
'bill_id': sa.billid
}, ensure_ascii=False, default=str)
except Exception as e:
debug(f'reseller_settlement error: {format_exc()}')
return json.dumps({'success': False, 'error': f'结算失败: {str(e)}'}, ensure_ascii=False)