# accounting — 记账模块 ## 模块定位 Sage 平台的**核心记账引擎**:以复式记账(借/贷双腿)为基础,围绕 「科目(subject)→ 账户(account)→ 明细(acc_detail)/ 余额(acc_balance)/ 流水 (accounting_log)」的账务体系,向上提供开户、充值、消费、账单、总账、日结、信用额度、 多币种汇率等能力。 记账的定义 = **写分录明细 + 写账务日志 + 修改账户余额**,三步缺一不可,且余额更新与 透支/信用额度校验必须和明细写入在**同一数据库事务上下文**内完成(原子性铁律)。 模块形态:Python 包 `accounting`(业务逻辑)+ `models/`(表定义)+ `json/`(CRUD 定义) + `wwwroot/`(页面/dspy 端点)+ `init/data.json`(种子数据)+ `scripts/`(RBAC 注册)。 宿主应用(sage 主应用)通过 `load_accounting()` 把所有类与函数注入 `ServerEnv`, 供其他模块(product、supplychain、unipay 等)跨模块调用。 核心业务文件:`accounting_config.py`(Accounting 配置类)/ `openaccount.py`(开户)/ `recharge.py`(充值 RechargeBiz)/ `consume.py`(消费记账)/ `bill.py`(write_bill 账单)/ `ledger.py`(总账)/ `dayend_balance.py`(日结) / `settle.py`+`settledate.py`(结算)/ `creditlimit.py`(信用额度)/ `exchange.py`(多币种汇率换算)/ `stats.py`(统计)/ `order_to_bill.py`(订单转账单)。 ## 表清单(models/*.json) | 表名 | 说明 | 关键字段 | |---|---|---| | `subject` | 科目表 | name, balance_side(余额方向), subjecttype | | `account` | 机构账户表 | accounting_orgid, orgid, org1id, currency, subjectid, balance_at, max_detailno | | `acc_balance` | 账户余额表 | accountid, acc_date, balance | | `acc_detail` | 账户明细表(分录) | accountid, acc_no, acc_date, acc_timestamp, acc_dir(借/贷), summary, amount | | `accounting_log` | 账务流水表 | accountid, acc_date, acc_timestamp, acc_dir, summary, amount, billid | | `account_config` | 账户配置表(参与方类型→科目) | subjectid, partytype, party1type | | `accounting_config` | 记账配置表(驱动分录双腿) | action, accounting_orgtype, accounting_dir, orgtype, org1type, subjectid, amt_pattern | | `bill` | 账单 | customerid, resellerid, productid, resourceid, orderid, business_op, amount | | `bill_detail` | 账单明细 | accounting_orgid, billid, description, participantid, participanttype, subjectname, accounting_dir | | `biz_order` | 业务订单 | customerid, resellerid, order_date, order_status, business_op, amount, currency | | `biz_orderdetail` | 业务订单明细 | orderid, productid, product_cnt, prod_config, list_amount, trans_amount, currency | | `ledger` | 总账表 | accounting_orgid, subjectid, acc_date, d_balance(借方), c_balance(贷方), currency, exchange_rate | | `credit_limit` | 信用额度表 | accountid, orgid, grant_orgid(授信方,多租户), credit_limit, used_credit, available_credit, valid_from | | `currency` | 币种 | name, symbol, decimal_places, is_base, status | | `exchange_rate` | 汇率表 | from_currency, to_currency, buy_rate, sell_rate, mid_rate, effective_date | 种子数据(`init/data.json`):appcodes/appcodes_kv 码表 + subject 科目 + account_config + accounting_config 记账配置 + currency + exchange_rate 初始汇率。 信用额度建表 SQL 另见 `sql/credit_limit.sql`;多币种迁移见 `scripts/multi_currency_migration.sql`。 ## 对外 API / dspy 端点(wwwroot/) **api/(多币种管理端)**: - `currency_create/update/delete.dspy` — 币种维护 - `exchange_rate_create/update/delete.dspy` — 汇率维护 - `fetch_forex_rates.dspy` — 拉取外部外汇牌价 **credit_limit/(信用额度管理)**: - `add/update/delete/get_credit_limit.dspy` — 额度 CRUD - `credit_manage.ui` / `credit_overview.ui` / `hub.ui` — 管理/总览页面 - `api/credit_summary.dspy`、`api/set_customer_credit.dspy`(+`set_credit_form.ui`) — 额度汇总与客户授信设置 **账务页面端点(wwwroot/ 根)**: - `myaccounts.dspy/.ui`、`mybalance.dspy`、`accdetail.dspy/.ui` — 我的账户/余额/明细 - `billing.dspy/.ui`、`billing_download.dspy` — 账单查询与下载 - `get_user_balance.dspy`、`oca.dspy`、`usermenu.ui`、`error_accounting.ui` - 开户端点:`open_owner_accounts.dspy` / `open_provider_accounts.dspy` / `open_reseller_accounts.dspy` / `open_reseller_provider_accounts.dspy` / `open_customer_accounts.dspy` / `open_customer_accounts_with_orgid.dspy` - 代充值:`proxy_recharge.ui` + `proxy_recharge_submit.dspy` - 统计 widget:`stat_account_count.ui` / `stat_month_consumption.ui` / `stat_today_consumption.ui` / `stat_total_balance.ui` CRUD 定义在 `json/`(account/acc_balance/acc_detail/accounting_log/accounting_config/ account_config/subject/credit_limit/currency_list/exchange_rate_list),其中 `json/build.sh` 用 `xls2ui -m ../models -o ../wwwroot accounting *.json` 从模型生成 wwwroot 页面——**改 CRUD 走 json 定义重新生成,勿手改产物**。 ## load 注册函数 宿主在初始化时调用: ```python from accounting.init import load_accounting load_accounting() ``` `load_accounting()` 把以下能力注入 `ServerEnv`(跨模块可见): - **配置/业务类**:`Accounting`(记账配置类)、`RechargeBiz`(充值业务) - **记账函数**:`consume_accounting`、`recharge_accounting`、`write_bill` - **开户函数**:`openOwnerAccounts` / `openProviderAccounts` / `openResellerAccounts` / `openCustomerAccounts` / `openRetailRelationshipAccounts` - **查询函数**:`getAccountBalance`、`getCustomerBalance`、`getAccountByName`、 `get_account_total_amount`、`get_accdetail`、`all_my_accounts`、`get_accounting_stats` - **信用额度**:`get_credit_limit_for_account`、`update_used_credit`、`set_credit_limit`、 `get_credit_stats`、`get_my_credit_list`、`get_all_customer_credits` + 三个 web 包装 (`get_credit_stats_web` / `get_my_credits_web` / `get_all_credits_web`,供 Jinja2 .ui 用) - **多币种**:`get_exchange_rate`、`convert_currency`、`convert_to_base`、 `get_user_currency`、`BASE_CURRENCY` 数据库名通过宿主注册的 `get_module_dbname('accounting')` 解析(跨宿主复用时表前缀/库名 映射的关键钩子)。独立调试可跑 `app/acc.py`(自带 webapp + RegisterFunction 的最小宿主)。 ## 宿主集成(部署) 1. **安装**:`./py3/bin/pip install pkgs/accounting`(setup.py 打包,包名 accounting, 版本见 `accounting/version.py`)。宿主 `import_init.py` 的 INIT_MODULES 需包含本模块 以导入 `init/data.json` 种子数据(码表/科目/记账配置)。 2. **入口调用**:宿主初始化处 `import` 后显式调用 `load_accounting()`。 3. **RBAC 注册**:`./py3/bin/python pkgs/accounting/scripts/load_path.py [--add-only]` (自动定位宿主根:含 py3+wwwroot;把 wwwroot 全部 .ui/.dspy 路径注册进 permission 表并给角色授权)。注册后 redis db0 FLUSHDB 刷 RBAC 缓存即生效。 4. **建表**:models/*.json 经 json2ddl 生成 DDL 部署期执行;运行期禁止 schema 变更。 信用额度增量见 `sql/credit_limit.sql`,多币种迁移见 `scripts/multi_currency_migration.sql`。 5. **菜单**:新页面需在 sage 主仓库 `wwwroot/global_menu.ui` 加菜单入口。 6. **i18n**:词条在 `i18n/{zh,en,jp,ko}`,部署时合并进宿主。 新功能四件套核查(缺一即不可见/不可用):`init.py` ServerEnv 暴露 → `scripts/load_path.py` RBAC 路径 → 宿主 `global_menu.ui` 菜单 → `json/<表>.json` CRUD 定义。 ## 部署注意 1. **余额更新不是可选项**:只写 acc_detail 不改 account/acc_balance 的实现按定义就是 不完整记账;透支/信用额度校验必须与明细写入同一事务上下文。 2. **数据隔离**:查询类端点(如 get_accdetail)必须按 `orgid` 过滤,防止按 accountid 越权枚举他人账务——新增查询端点照抄该模式。 3. **信用额度多租户**:`grant_orgid` 区分授信方;管理端读全部、客户端只读本机构视图, 迁移按 `sql/credit_limit.sql` 执行。 4. **pip 非 editable 安装**:改码后必须重新 `pip install` 到宿主 py3 再重启, 只 git pull 仓库不会更新 site-packages(可用文件 md5 对比确认加载版本)。 5. **sage 核心记账引擎 vs sageapi 网关是两层**:本仓库是核心引擎(复式记账/双腿/科目), sageapi 只是轻量 API 网关;credit_limit 等逻辑两层都可能需要但实现方式不同,勿混淆。 6. **先读全模块再改**:PFBiz → Accounting → leg_accounting 是完整既有体系,加功能前先读 `accounting_config.py` / `creditlimit.py` / `consume.py`,勿发明平行实现。 7. RBAC 精确 path 匹配,父路径不覆盖子路径;新增 dspy 必须逐条注册,否则 403。 设计文档见 `docs/平台类系统记账子系统.docx`;测试脚本见 `test/` (open_account.py / recharge.py / run_test.py)。