结算初始化

This commit is contained in:
ping 2026-06-18 17:48:01 +08:00
parent acd37d0e19
commit 890846057f
12 changed files with 1720 additions and 0 deletions

60
b/account_config.sql Normal file
View File

@ -0,0 +1,60 @@
/*
Navicat Premium Data Transfer
Source Server : kbossdev
Source Server Type : MariaDB
Source Server Version : 100622
Source Host : db:3306
Source Schema : kboss_dev
Target Server Type : MariaDB
Target Server Version : 100622
File Encoding : 65001
Date: 18/06/2026 17:11:12
*/
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for account_config
-- ----------------------------
DROP TABLE IF EXISTS `account_config`;
CREATE TABLE `account_config` (
`id` varchar(32) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL COMMENT 'id',
`partytype` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL COMMENT '参与方类型',
`subjectname` varchar(32) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL COMMENT '科目名称',
`del_flg` varchar(1) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT '0' COMMENT '删除标志',
`create_at` timestamp NOT NULL DEFAULT current_timestamp() COMMENT '创建时间戳',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb3 COLLATE = utf8mb3_general_ci COMMENT = '账户配置表' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of account_config
-- ----------------------------
INSERT INTO `account_config` VALUES ('acc001', '本机构', '资金账号', '0', '2023-08-03 17:40:34');
INSERT INTO `account_config` VALUES ('acc002', '本机构', '折扣收入', '0', '2023-08-03 17:40:34');
INSERT INTO `account_config` VALUES ('acc003', '本机构', '底价收入', '0', '2023-08-03 17:40:34');
INSERT INTO `account_config` VALUES ('acc004', '本机构', '客户折扣支出', '0', '2023-08-03 17:40:34');
INSERT INTO `account_config` VALUES ('acc005', '本机构', '存放供应商资金', '0', '2023-08-03 17:40:34');
INSERT INTO `account_config` VALUES ('acc006', '本机构', '分销商代付费销售', '0', '2023-08-03 17:40:34');
INSERT INTO `account_config` VALUES ('acc007', '本机构', '支付宝手续费', '0', '2023-08-03 17:40:34');
INSERT INTO `account_config` VALUES ('acc008', '客户', '业务账', '0', '2023-08-03 17:40:34');
INSERT INTO `account_config` VALUES ('acc009', '供应商', '待结转折扣销售收入', '0', '2023-08-03 17:40:34');
INSERT INTO `account_config` VALUES ('acc010', '供应商', '待结转代付费销售收入', '0', '2023-08-03 17:40:34');
INSERT INTO `account_config` VALUES ('acc011', '供应商', '待结转底价销售收入', '0', '2023-08-03 17:40:34');
INSERT INTO `account_config` VALUES ('acc012', '分销商', '分销商存放资金', '0', '2023-08-03 17:40:34');
INSERT INTO `account_config` VALUES ('acc013', '分销商', '待结转折扣支出', '0', '2023-08-03 17:40:34');
INSERT INTO `account_config` VALUES ('acc014', '分销商', '待结转代付费支出', '0', '2023-08-03 17:40:34');
INSERT INTO `account_config` VALUES ('acc015', '分销商', '待结转底价支出', '0', '2023-08-03 17:40:34');
INSERT INTO `account_config` VALUES ('acc016', '分销商', '代付费销售', '0', '2023-08-03 17:40:34');
INSERT INTO `account_config` VALUES ('acc017', '客户', '算力券', '0', '2025-03-31 11:36:11');
INSERT INTO `account_config` VALUES ('acc019', '算力券发行方', '算力券发放', '0', '2025-04-08 11:21:33');
INSERT INTO `account_config` VALUES ('acc020', '算力券发行方', '算力券消费', '0', '2025-04-08 11:21:43');
INSERT INTO `account_config` VALUES ('acc021', '算力券承销方', '待结算算力券', '0', '2025-04-08 11:29:56');
INSERT INTO `account_config` VALUES ('acc022', '本机构', '算力劵临时账户', '0', '2025-04-10 16:34:46');
INSERT INTO `account_config` VALUES ('acc023', '分销商', '算力劵临时账户', '0', '2025-04-10 16:34:55');
INSERT INTO `account_config` VALUES ('acc024', '供应商', '算力劵临时账户', '0', '2025-04-10 16:35:03');
SET FOREIGN_KEY_CHECKS = 1;

131
b/accounting_config.sql Normal file
View File

@ -0,0 +1,131 @@
/*
Navicat Premium Data Transfer
Source Server : kbossdev
Source Server Type : MariaDB
Source Server Version : 100622
Source Host : db:3306
Source Schema : kboss_dev
Target Server Type : MariaDB
Target Server Version : 100622
File Encoding : 65001
Date: 18/06/2026 17:11:43
*/
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for accounting_config
-- ----------------------------
DROP TABLE IF EXISTS `accounting_config`;
CREATE TABLE `accounting_config` (
`id` varchar(32) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL COMMENT 'id',
`action` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL COMMENT '交易',
`specstr` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL COMMENT '特性',
`accounting_orgtype` varchar(32) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL COMMENT '账务机构',
`accounting_dir` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL COMMENT '记账方向',
`orgtype` varchar(32) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL COMMENT '机构类型',
`subjectname` varchar(21) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL COMMENT '科目名',
`amt_pattern` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL COMMENT '金额模板',
`del_flg` varchar(1) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT '0' COMMENT '删除标志',
`create_at` timestamp NOT NULL DEFAULT current_timestamp() COMMENT '创建时间戳',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb3 COLLATE = utf8mb3_general_ci COMMENT = '记账配置表' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of accounting_config
-- ----------------------------
INSERT INTO `accounting_config` VALUES ('accc-01', '充值', '充值', '客户所在机构', '', '本机构', '资金账号', '${交易金额}$', '0', '2023-08-03 17:40:34');
INSERT INTO `accounting_config` VALUES ('accc-02', '充值', '充值', '客户所在机构', '', '客户', '业务账', '${交易金额}$', '0', '2023-08-03 17:40:34');
INSERT INTO `accounting_config` VALUES ('accc-03', '充值', '充值', '分销商机构', '', '本机构', '资金账号', '${交易金额}$', '0', '2023-08-03 17:40:34');
INSERT INTO `accounting_config` VALUES ('accc-04', '充值', '充值', '分销商机构', '', '分销商', '分销商存放资金', '${交易金额}$', '0', '2023-08-03 17:40:34');
INSERT INTO `accounting_config` VALUES ('accc-05', '交易', '付费-折扣', '客户所在机构', '', '客户', '业务账', '${交易金额}$ * ${客户折扣}$', '0', '2023-08-03 17:40:34');
INSERT INTO `accounting_config` VALUES ('accc-06', '交易', '付费-折扣', '客户所在机构', '', '本机构', '折扣收入', '${交易金额}$ * (${客户折扣}$ - ${本方折扣}$)', '0', '2023-08-03 17:40:34');
INSERT INTO `accounting_config` VALUES ('accc-07', '交易', '付费-折扣', '客户所在机构', '', '供应商', '待结转折扣销售收入', '${交易金额}$ * ${本方折扣}$', '0', '2023-08-03 17:40:34');
INSERT INTO `accounting_config` VALUES ('accc-08', '交易', '付费-折扣-折扣', '分销商机构', '', '分销商', '分销商存放资金', '${交易金额}$ * ${分销商折扣}$', '0', '2023-08-03 17:40:34');
INSERT INTO `accounting_config` VALUES ('accc-09', '交易', '付费-折扣-折扣', '分销商机构', '', '本机构', '折扣收入', '${交易金额}$ * (${分销商折扣}$ - ${本方折扣}$)', '0', '2023-08-03 17:40:34');
INSERT INTO `accounting_config` VALUES ('accc-10', '交易', '付费-折扣-折扣', '分销商机构', '', '供应商', '待结转折扣销售收入', '${交易金额}$ * ${本方折扣}$', '0', '2023-08-03 17:40:34');
INSERT INTO `accounting_config` VALUES ('accc-11', '交易', '付费-折扣-代付费', '分销商机构', '', '分销商', '分销商存放资金', '${交易金额}$ * ${客户折扣}$', '0', '2023-08-03 17:40:34');
INSERT INTO `accounting_config` VALUES ('accc-12', '交易', '付费-折扣-代付费', '分销商机构', '', '本机构', '折扣收入', '${交易金额}$ * (${客户折扣}$ - ${本方折扣}$)', '0', '2023-08-03 17:40:34');
INSERT INTO `accounting_config` VALUES ('accc-13', '交易', '付费-折扣-代付费', '分销商机构', '', '供应商', '待结转折扣销售收入', '${交易金额}$ * ${本方折扣}$', '0', '2023-08-03 17:40:34');
INSERT INTO `accounting_config` VALUES ('accc-14', '交易', '付费-折扣-代付费', '分销商机构', '', '本机构', '分销商代付费销售', '${交易金额}$ * ${客户折扣}$', '0', '2023-08-03 17:40:34');
INSERT INTO `accounting_config` VALUES ('accc-15', '交易', '付费-折扣-代付费', '分销商机构', '', '分销商', '代付费销售', '${交易金额}$ * ${客户折扣}$', '0', '2023-08-03 17:40:34');
INSERT INTO `accounting_config` VALUES ('accc-16', '交易', '付费-代付费', '客户所在机构', '', '客户', '业务账', '${交易金额}$ * ${客户折扣}$', '0', '2023-08-03 17:40:34');
INSERT INTO `accounting_config` VALUES ('accc-17', '交易', '付费-代付费', '客户所在机构', '', '本机构', '客户折扣支出', '${交易金额}$ * (1 - ${客户折扣}$)', '0', '2023-08-03 17:40:34');
INSERT INTO `accounting_config` VALUES ('accc-18', '交易', '付费-代付费', '客户所在机构', '', '供应商', '待结转代付费销售收入', '${交易金额}$', '0', '2023-08-03 17:40:34');
INSERT INTO `accounting_config` VALUES ('accc-19', '交易', '付费-代付费-代付费', '分销商机构', '', '分销商', '分销商存放资金', '${交易金额}$', '0', '2023-08-03 17:40:34');
INSERT INTO `accounting_config` VALUES ('accc-20', '交易', '付费-代付费-代付费', '分销商机构', '', '供应商', '待结转代付费销售收入', '${交易金额}$', '0', '2023-08-03 17:40:34');
INSERT INTO `accounting_config` VALUES ('accc-21', '交易', '付费-代付费-代付费', '分销商机构', '', '本机构', '分销商代付费销售', '${交易金额}$', '0', '2023-08-03 17:40:34');
INSERT INTO `accounting_config` VALUES ('accc-22', '交易', '付费-代付费-代付费', '分销商机构', '', '分销商', '代付费销售', '${交易金额}$', '0', '2023-08-03 17:40:34');
INSERT INTO `accounting_config` VALUES ('accc-23', '交易', '付费-底价', '客户所在机构', '', '客户', '业务账', '${客户售价}$', '0', '2023-08-03 17:40:34');
INSERT INTO `accounting_config` VALUES ('accc-24', '交易', '付费-底价', '客户所在机构', '', '本机构', '底价收入', '${客户售价}$ - ${进价}$', '0', '2023-08-03 17:40:34');
INSERT INTO `accounting_config` VALUES ('accc-25', '交易', '付费-底价', '客户所在机构', '', '供应商', '待结转底价销售收入', '${进价}$', '0', '2023-08-03 17:40:34');
INSERT INTO `accounting_config` VALUES ('accc-26', '交易', '付费-底价-底价', '分销商机构', '', '分销商', '分销商存放资金', '${分销商售价}$', '0', '2023-08-03 17:40:34');
INSERT INTO `accounting_config` VALUES ('accc-27', '交易', '付费-底价-底价', '分销商机构', '', '本机构', '底价收入', '${分销商售价}$ - ${进价}$', '0', '2023-08-03 17:40:34');
INSERT INTO `accounting_config` VALUES ('accc-28', '交易', '付费-底价-底价', '分销商机构', '', '供应商', '待结转底价销售收入', '${进价}$', '0', '2023-08-03 17:40:34');
INSERT INTO `accounting_config` VALUES ('accc-29', '交易', '付费-底价-底价', '分销商机构', '', '分销商', '分销商存放资金', '${客户售价}$ * ${分销商售价}$', '0', '2023-08-03 17:40:34');
INSERT INTO `accounting_config` VALUES ('accc-30', '交易', '付费-底价-底价', '分销商机构', '', '本机构', '底价收入', '${客户售价}$ * ${分销商售价}$ - ${进价}$', '0', '2023-08-03 17:40:34');
INSERT INTO `accounting_config` VALUES ('accc-31', '交易', '付费-底价-底价', '分销商机构', '', '供应商', '待结转底价销售收入', '${进价}$', '0', '2023-08-03 17:40:34');
INSERT INTO `accounting_config` VALUES ('accc-32', '交易', '付费-底价-代付费', '分销商机构', '', '分销商', '分销商存放资金', '${客户售价}$', '0', '2023-08-03 17:40:34');
INSERT INTO `accounting_config` VALUES ('accc-33', '交易', '付费-底价-代付费', '分销商机构', '', '本机构', '底价收入', '${客户售价}$ - ${进价}$', '0', '2023-08-03 17:40:34');
INSERT INTO `accounting_config` VALUES ('accc-34', '交易', '付费-底价-代付费', '分销商机构', '', '供应商', '待结转底价销售收入', '${进价}$', '0', '2023-08-03 17:40:34');
INSERT INTO `accounting_config` VALUES ('accc-35', '交易', '付费-底价-代付费', '分销商机构', '', '本机构', '分销商代付费销售', '${客户售价}$', '0', '2023-08-03 17:40:34');
INSERT INTO `accounting_config` VALUES ('accc-36', '交易', '付费-底价-代付费', '分销商机构', '', '分销商', '代付费销售', '${客户售价}$', '0', '2023-08-03 17:40:34');
INSERT INTO `accounting_config` VALUES ('accc-37', '结算', '结算-折扣-实时', NULL, '', '供应商', '待结转折扣销售收入', '${贷-供应商-待结转折扣销售收入}$', '0', '2023-08-03 17:40:34');
INSERT INTO `accounting_config` VALUES ('accc-38', '结算', '结算-折扣-实时', NULL, '', '本机构', '资金账号', '${贷-供应商-待结转折扣销售收入}$', '0', '2023-08-03 17:40:34');
INSERT INTO `accounting_config` VALUES ('accc-39', '结算', '结算-代付费-实时', NULL, '', '供应商', '待结转代付费销售收入', '${贷-供应商-待结转代付费销售收入}$', '0', '2023-08-03 17:40:34');
INSERT INTO `accounting_config` VALUES ('accc-40', '结算', '结算-代付费-实时', NULL, '', '本机构', '资金账号', '${贷-供应商-待结转代付费销售收入}$', '0', '2023-08-03 17:40:34');
INSERT INTO `accounting_config` VALUES ('accc-41', '结算', '结算-底价-实时', NULL, '', '供应商', '待结转底价销售收入', '${贷-供应商-待结转底价销售收入}$', '0', '2023-08-03 17:40:34');
INSERT INTO `accounting_config` VALUES ('accc-42', '结算', '结算-底价-实时', NULL, '', '本机构', '资金账号', '${贷-供应商-待结转底价销售收入}$', '0', '2023-08-03 17:40:34');
INSERT INTO `accounting_config` VALUES ('accc-43', '结算', '结算-折扣', NULL, '', '供应商', '待结转折扣销售收入', '${交易金额}$', '0', '2023-08-03 17:40:34');
INSERT INTO `accounting_config` VALUES ('accc-44', '结算', '结算-折扣', NULL, '', '本机构', '资金账号', '${交易金额}$', '0', '2023-08-03 17:40:34');
INSERT INTO `accounting_config` VALUES ('accc-45', '结算', '结算-代付费', NULL, '', '供应商', '待结转代付费销售收入', '${交易金额}$', '0', '2023-08-03 17:40:34');
INSERT INTO `accounting_config` VALUES ('accc-46', '结算', '结算-代付费', NULL, '', '本机构', '资金账号', '${交易金额}$', '0', '2023-08-03 17:40:34');
INSERT INTO `accounting_config` VALUES ('accc-47', '结算', '结算-底价', NULL, '', '供应商', '待结转底价销售收入', '${交易金额}$', '0', '2023-08-03 17:40:34');
INSERT INTO `accounting_config` VALUES ('accc-48', '结算', '结算-底价', NULL, '', '本机构', '资金账号', '${交易金额}$', '0', '2023-08-03 17:40:34');
INSERT INTO `accounting_config` VALUES ('accc-49', '充值', '支付宝充值', '客户所在机构', '', '本机构', '资金账号', '${交易金额}$ - ${手续费}$', '0', '2023-08-03 17:40:34');
INSERT INTO `accounting_config` VALUES ('accc-50', '充值', '支付宝充值', '客户所在机构', '', '本机构', '支付宝手续费', '${手续费}$', '0', '2023-08-03 17:40:34');
INSERT INTO `accounting_config` VALUES ('accc-51', '充值', '支付宝充值', '客户所在机构', '', '客户', '业务账', '${交易金额}$', '0', '2023-08-03 17:40:34');
INSERT INTO `accounting_config` VALUES ('accc-52', '充值', '支付宝充值', '分销商机构', '', '本机构', '资金账号', '${交易金额}$ - ${手续费}$', '0', '2023-08-03 17:40:34');
INSERT INTO `accounting_config` VALUES ('accc-53', '充值', '支付宝充值', '分销商机构', '', '分销商', '分销商存放资金', '${交易金额}$ - ${手续费}$', '0', '2023-08-03 17:40:34');
INSERT INTO `accounting_config` VALUES ('accc-54', '算力劵发行', '算力劵充值', '算力劵承销方', '', '算力劵发行方', '算力劵发行', '${交易金额}$', '0', '2025-04-08 17:33:03');
INSERT INTO `accounting_config` VALUES ('accc-55', '算力劵发行', '算力劵充值', '算力劵承销方', '', '本机构', '算力劵临时账户', '${交易金额}$', '0', '2025-04-08 17:37:44');
INSERT INTO `accounting_config` VALUES ('accc-56', '算力劵发放', '算力劵充值', '分销商机构', '', '本机构', '算力劵临时账户', '${交易金额}$', '0', '2025-04-08 17:37:44');
INSERT INTO `accounting_config` VALUES ('accc-57', '算力劵发放', '算力劵充值', '分销商机构', '', '分销商', '算力劵临时账户', '${交易金额}$', '0', '2025-04-08 17:37:44');
INSERT INTO `accounting_config` VALUES ('accc-58', '算力劵发放', '算力劵充值', '客户所在机构', '', '本机构', '算力劵临时账户', '${交易金额}$', '0', '2025-04-08 17:37:44');
INSERT INTO `accounting_config` VALUES ('accc-59', '算力劵发放', '算力劵充值', '客户所在机构', '', '客户', '算力劵', '${交易金额}$', '0', '2025-04-08 17:37:44');
INSERT INTO `accounting_config` VALUES ('accc-60', '算力劵交易', '算力券-付费-折扣', '客户所在机构', '', '客户', '算力劵', '${交易金额}*${客户折扣}$', '0', '2025-04-08 17:37:44');
INSERT INTO `accounting_config` VALUES ('accc-61', '算力劵交易', '算力券-付费-折扣', '客户所在机构', '', '本机构', '算力劵临时账户', '${交易金额}*${客户折扣}$', '0', '2025-04-08 17:37:44');
INSERT INTO `accounting_config` VALUES ('accc-62', '算力劵交易', '算力券-付费-折扣', '客户所在机构', '', '供应商', '算力劵临时账户', '${交易金额}*${客户折扣}$', '0', '2025-04-08 17:37:44');
INSERT INTO `accounting_config` VALUES ('accc-63', '算力劵交易', '算力券-付费-折扣', '客户所在机构', '', '本机构', '折扣收入', '${交易金额}*(${客户折扣}-${本方折扣})$', '0', '2025-04-08 17:37:44');
INSERT INTO `accounting_config` VALUES ('accc-64', '算力劵交易', '算力券-付费-折扣', '客户所在机构', '', '供应商', '待结转折扣销售收入', '${交易金额}$*${分销商折扣}$', '0', '2025-04-08 17:37:44');
INSERT INTO `accounting_config` VALUES ('accc-65', '算力劵交易', '算力券-付费-折扣-折扣', '分销商机构', '', '本机构', '算力劵临时账户', '${交易金额}$*${客户折扣}$', '0', '2025-04-08 17:37:44');
INSERT INTO `accounting_config` VALUES ('accc-66', '算力劵交易', '算力券-付费-折扣-折扣', '分销商机构', '', '供应商', '算力劵临时账户', '${交易金额}$*${客户折扣}$', '0', '2025-04-08 17:37:44');
INSERT INTO `accounting_config` VALUES ('accc-67', '算力劵交易', '算力券-付费-折扣-折扣', '分销商机构', '', '本机构', '折扣收入', '${交易金额}$*(${分销商折扣}$ -${本方折扣}$)', '0', '2025-04-08 17:37:44');
INSERT INTO `accounting_config` VALUES ('accc-68', '算力劵交易', '算力券-付费-折扣-折扣', '分销商机构', '', '供应商', '待结转折扣销售收入', '${交易金额}$*${本方折扣}$', '0', '2025-04-08 17:37:44');
INSERT INTO `accounting_config` VALUES ('accc-69', '算力劵交易', '算力券-付费-折扣-折扣', '分销商机构', '', '分销商', '分销商存放资金', '${交易金额}$*${分销商折扣}$', '0', '2025-04-08 17:37:44');
INSERT INTO `accounting_config` VALUES ('accc-70', '算力劵交易', '算力券-付费-底价', '客户所在机构', '', '客户', '算力劵', '${客户售价}$', '0', '2025-04-08 17:37:44');
INSERT INTO `accounting_config` VALUES ('accc-71', '算力劵交易', '算力券-付费-底价', '客户所在机构', '', '本机构', '算力劵临时账户', '${客户售价}$ - ${进价}$', '0', '2025-04-08 17:37:44');
INSERT INTO `accounting_config` VALUES ('accc-72', '算力劵交易', '算力券-付费-底价', '客户所在机构', '', '供应商', '算力劵临时账户', '${客户售价}$ - ${进价}$', '0', '2025-04-08 17:37:44');
INSERT INTO `accounting_config` VALUES ('accc-73', '算力劵交易', '算力券-付费-底价', '客户所在机构', '', '本机构', '底价收入', '${客户售价}$ - ${进价}$', '0', '2025-04-08 17:37:44');
INSERT INTO `accounting_config` VALUES ('accc-74', '算力劵交易', '算力券-付费-底价', '客户所在机构', '', '供应商', '待结转底价销售收入', '${进价}$', '0', '2025-04-08 17:37:44');
INSERT INTO `accounting_config` VALUES ('accc-75', '算力劵交易', '算力券-付费-底价-底价', '分销商机构', '', '本机构', '算力劵临时账户', '${客户售价}$ - ${进价}$', '0', '2025-04-08 17:37:44');
INSERT INTO `accounting_config` VALUES ('accc-76', '算力劵交易', '算力券-付费-底价-底价', '分销商机构', '', '供应商', '算力劵临时账户', '${进价}$', '0', '2025-04-08 17:37:44');
INSERT INTO `accounting_config` VALUES ('accc-77', '算力劵交易', '算力券-付费-底价-底价', '分销商机构', '', '本机构', '底价收入', '${客户售价}$ - ${进价}$', '0', '2025-04-08 17:37:44');
INSERT INTO `accounting_config` VALUES ('accc-78', '算力劵交易', '算力券-付费-底价-底价', '分销商机构', '', '供应商', '待结转底价销售收入', '${进价}$', '0', '2025-04-08 17:37:44');
INSERT INTO `accounting_config` VALUES ('accc-79', '算力劵交易', '算力券-付费-底价-底价', '分销商机构', '', '分销商', '分销商存放资金', '${客户售价}$ * ${分销商售价}$ - ${进价}$', '0', '2025-04-08 17:37:44');
INSERT INTO `accounting_config` VALUES ('accc-80', '算力劵交易', '算力券-付费-底价', '客户所在机构', '', '客户', '算力劵', '${交易金额}$ * ${客户折扣}$', '0', '2025-04-08 17:37:44');
INSERT INTO `accounting_config` VALUES ('accc-81', '算力劵交易', '算力券-付费-底价', '客户所在机构', '', '本机构', '算力劵临时账户', '${交易金额}$ * ${客户折扣}$', '0', '2025-04-08 17:37:44');
INSERT INTO `accounting_config` VALUES ('accc-82', '算力劵交易', '算力券-付费-代付费', '客户所在机构', '', '供应商', '算力劵临时账户', '${交易金额}$ * ${客户折扣}$', '0', '2025-04-08 17:37:44');
INSERT INTO `accounting_config` VALUES ('accc-83', '算力劵交易', '算力券-付费-代付费', '客户所在机构', '', '本机构', '底价收入', '${交易金额}$ * (1 - ${客户折扣}$)', '0', '2025-04-08 17:37:44');
INSERT INTO `accounting_config` VALUES ('accc-84', '算力劵交易', '算力券-付费-代付费', '客户所在机构', '', '供应商', '待结转底价销售收入', '${交易金额}$ * ${客户折扣}$', '0', '2025-04-08 17:37:44');
INSERT INTO `accounting_config` VALUES ('accc-85', '算力劵交易', '算力券-付费-代付费-代付费', '分销商机构', '', '本机构', '算力劵临时账户', '${交易金额}$', '0', '2025-04-08 17:37:44');
INSERT INTO `accounting_config` VALUES ('accc-86', '算力劵交易', '算力券-付费-代付费-代付费', '分销商机构', '', '供应商', '算力劵临时账户', '${交易金额}$', '0', '2025-04-08 17:37:44');
INSERT INTO `accounting_config` VALUES ('accc-87', '算力劵交易', '算力券-付费-代付费-代付费', '分销商机构', '', '本机构', '底价收入', '${交易金额}$', '0', '2025-04-08 17:37:44');
INSERT INTO `accounting_config` VALUES ('accc-88', '算力劵交易', '算力券-付费-代付费-代付费', '分销商机构', '', '供应商', '待结转底价销售收入', '${交易金额}$', '0', '2025-04-08 17:37:44');
INSERT INTO `accounting_config` VALUES ('accc-89', '算力劵交易', '算力券-付费-代付费-代付费', '分销商机构', '', '分销商', '分销商存放资金', '${交易金额}$', '0', '2025-04-08 17:37:44');
SET FOREIGN_KEY_CHECKS = 1;

View File

@ -2396,6 +2396,67 @@ CREATE TABLE `subject` (
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb3 COLLATE = utf8mb3_general_ci COMMENT = '科目表' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for finance_settlement
-- ----------------------------
DROP TABLE IF EXISTS `finance_settlement`;
CREATE TABLE `finance_settlement` (
`id` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT 'id',
`settlement_no` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '结算单号',
`accounting_orgid` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '账本机构id',
`counterparty_type` varchar(16) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '对手方类型 supplier/reseller',
`counterparty_orgid` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '对手方机构id',
`counterparty_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '对手方名称',
`period_type` varchar(16) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '账期类型 day/month',
`period_start` date NULL DEFAULT NULL COMMENT '账期开始日期',
`period_end` date NULL DEFAULT NULL COMMENT '账期结束日期',
`sales_amount` double(18, 8) NULL DEFAULT 0.00000000 COMMENT '销售金额',
`settlement_amount` double(18, 8) NULL DEFAULT 0.00000000 COMMENT '结算金额',
`platform_income_amount` double(18, 8) NULL DEFAULT 0.00000000 COMMENT '平台收入',
`bill_count` int(11) NULL DEFAULT 0 COMMENT '账单数量',
`status` varchar(16) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT 'draft' COMMENT 'draft/approving/approved/rejected/settled/failed/cancelled',
`approval_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '审批id',
`approval_status` varchar(16) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '审批状态',
`failure_reason` varchar(1000) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '失败原因',
`settled_at` datetime NULL DEFAULT NULL COMMENT '结算记账时间',
`created_by` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '创建人',
`del_flg` varchar(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '0' COMMENT '删除标志',
`create_at` datetime NULL DEFAULT current_timestamp() COMMENT '创建时间',
`update_at` datetime NULL DEFAULT current_timestamp() COMMENT '更新时间',
PRIMARY KEY (`id`) USING BTREE,
UNIQUE INDEX `finance_settlement_un`(`accounting_orgid`, `counterparty_type`, `counterparty_orgid`, `period_start`, `period_end`, `del_flg`) USING BTREE,
INDEX `finance_settlement_idx1`(`accounting_orgid`, `status`) USING BTREE,
INDEX `finance_settlement_idx2`(`counterparty_type`, `counterparty_orgid`) USING BTREE,
INDEX `finance_settlement_idx3`(`period_start`, `period_end`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '财务结算单主表' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for finance_settlement_detail
-- ----------------------------
DROP TABLE IF EXISTS `finance_settlement_detail`;
CREATE TABLE `finance_settlement_detail` (
`id` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT 'id',
`settlement_id` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '结算单id',
`bill_id` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '账单id',
`order_id` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '订单id',
`bill_date` date NULL DEFAULT NULL COMMENT '账单日期',
`customerid` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '客户id',
`providerid` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '供应商机构id',
`productid` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '产品id',
`business_op` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '业务操作',
`sale_mode` varchar(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '销售模式 0折扣/1代付费/2底价',
`sales_amount` double(18, 8) NULL DEFAULT 0.00000000 COMMENT '销售金额',
`settlement_amount` double(18, 8) NULL DEFAULT 0.00000000 COMMENT '结算金额',
`platform_income_amount` double(18, 8) NULL DEFAULT 0.00000000 COMMENT '平台收入',
`amount_source` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT 'bill_detail' COMMENT '金额来源',
`del_flg` varchar(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '0' COMMENT '删除标志',
`create_at` datetime NULL DEFAULT current_timestamp() COMMENT '创建时间',
PRIMARY KEY (`id`) USING BTREE,
INDEX `finance_settlement_detail_idx1`(`settlement_id`) USING BTREE,
INDEX `finance_settlement_detail_idx2`(`bill_id`) USING BTREE,
INDEX `finance_settlement_detail_idx3`(`providerid`, `productid`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '财务结算单明细快照' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for transfer_record
-- ----------------------------

View File

@ -0,0 +1,301 @@
# -*- coding: utf-8 -*-
"""财务结算审批回调。"""
import datetime
DBNAME = 'kboss'
def _period_settle_mode(period_type):
if period_type == 'day':
return '1'
if period_type == 'month':
return '3'
return '0'
async def _business_date():
try:
return await get_business_date(sor=None)
except Exception:
return datetime.datetime.now().strftime('%Y-%m-%d')
async def _settle_supplier(sor, settlement):
rows = await sor.sqlExe("""
SELECT sale_mode, SUM(settlement_amount) AS amount
FROM finance_settlement_detail
WHERE settlement_id=${settlement_id}$
AND del_flg='0'
GROUP BY sale_mode
""", {'settlement_id': settlement['id']})
settle_date = await _business_date()
settle_mode = _period_settle_mode(settlement.get('period_type'))
failures = []
for row in rows:
amount = round(float(row.get('amount') or 0), 8)
if amount == 0:
continue
sale_mode = row.get('sale_mode') or '0'
try:
settle_log = {
'accounting_orgid': settlement.get('accounting_orgid'),
'providerid': settlement.get('counterparty_orgid'),
'settle_date': settle_date,
'settle_mode': settle_mode,
'sale_mode': sale_mode,
'settle_amt': amount,
'business_op': 'SETTLE',
}
ai = SettleAccounting(settle_log)
await ai.accounting(sor)
except Exception as e:
failures.append('sale_mode=%s: %s' % (sale_mode, str(e)))
if failures:
return False, '; '.join(failures)
return True, None
async def _get_account(sor, accounting_orgid, orgid, subjectname):
rows = await sor.sqlExe("""
SELECT a.*
FROM account a
INNER JOIN subject s ON a.subjectid=s.id
WHERE a.accounting_orgid=${accounting_orgid}$
AND a.orgid=${orgid}$
AND s.name=${subjectname}$
AND a.del_flg='0'
AND s.del_flg='0'
LIMIT 1
""", {
'accounting_orgid': accounting_orgid,
'orgid': orgid,
'subjectname': subjectname,
})
return rows[0] if rows else None
async def _latest_balance(sor, accountid):
rows = await sor.sqlExe("""
SELECT balance
FROM acc_balance
WHERE accountid=${accountid}$ AND del_flg='0'
ORDER BY acc_date DESC
LIMIT 1
""", {'accountid': accountid})
if not rows or rows[0].get('balance') is None:
return 0.0
return float(rows[0].get('balance') or 0)
def _next_balance(account, balance, acc_dir, amount):
balance_at = account.get('balance_at')
if (
(balance_at == '0' and acc_dir == '1')
or (balance_at == '1' and acc_dir == '0')
):
return balance - amount
return balance + amount
async def _write_balance(sor, accountid, acc_date, balance):
rows = await sor.sqlExe("""
SELECT id
FROM acc_balance
WHERE accountid=${accountid}$ AND acc_date=${acc_date}$ AND del_flg='0'
LIMIT 1
""", {'accountid': accountid, 'acc_date': acc_date})
if rows:
await sor.U('acc_balance', {
'id': rows[0]['id'],
'balance': balance,
})
else:
await sor.C('acc_balance', {
'id': uuid(),
'accountid': accountid,
'acc_date': acc_date,
'balance': balance,
'del_flg': '0',
'create_at': datetime.datetime.now(),
})
async def _write_reseller_leg(
sor,
billid,
acc_date,
accounting_orgid,
participantid,
participanttype,
subjectname,
accounting_dir,
amount,
description,
):
account = await _get_account(sor, accounting_orgid, participantid, subjectname)
if not account:
raise Exception(
'未找到账户 accounting_orgid=%s, orgid=%s, subject=%s'
% (accounting_orgid, participantid, subjectname)
)
acc_dir = '0' if accounting_dir == '借' else '1'
old_balance = await _latest_balance(sor, account['id'])
new_balance = _next_balance(account, old_balance, acc_dir, amount)
await _write_balance(sor, account['id'], acc_date, new_balance)
await sor.C('bill_detail', {
'id': uuid(),
'accounting_orgid': accounting_orgid,
'billid': billid,
'description': description,
'participantid': participantid,
'participanttype': participanttype,
'subjectname': subjectname,
'accounting_dir': accounting_dir,
'amount': amount,
'del_flg': '0',
'create_at': datetime.datetime.now(),
})
logid = uuid()
await sor.C('accounting_log', {
'id': logid,
'accountid': account['id'],
'acc_date': acc_date,
'acc_timestamp': datetime.datetime.now(),
'acc_dir': acc_dir,
'summary': 'SETTLE',
'amount': amount,
'billid': billid,
'del_flg': '0',
'create_at': datetime.datetime.now(),
})
await sor.C('acc_detail', {
'id': uuid(),
'accountid': account['id'],
'acc_date': acc_date,
'acc_timestamp': datetime.datetime.now(),
'acc_dir': acc_dir,
'summary': 'SETTLE',
'amount': amount,
'balance': new_balance,
'acclogid': logid,
'del_flg': '0',
'create_at': datetime.datetime.now(),
})
async def _settle_reseller(sor, settlement):
amount = round(float(settlement.get('settlement_amount') or 0), 8)
if amount == 0:
return True, None
if amount < 0:
return False, '分销商结算金额为负数,需先人工确认红冲口径'
accounting_orgid = settlement.get('accounting_orgid')
reseller_orgid = settlement.get('counterparty_orgid')
settle_date = await _business_date()
billid = uuid()
description = '分销商结算-%s' % settlement.get('settlement_no')
await sor.C('bill', {
'id': billid,
'customerid': reseller_orgid,
'business_op': 'SETTLE',
'amount': amount,
'bill_date': settle_date,
'bill_timestamp': datetime.datetime.now(),
'bill_state': '1',
'del_flg': '0',
'create_at': datetime.datetime.now(),
})
# 结算分录:借记分销商存放资金,贷记本机构资金账号。
await _write_reseller_leg(
sor,
billid,
settle_date,
accounting_orgid,
reseller_orgid,
'分销商',
'分销商存放资金',
'借',
amount,
description,
)
await _write_reseller_leg(
sor,
billid,
settle_date,
accounting_orgid,
accounting_orgid,
'本机构',
'资金账号',
'贷',
amount,
description,
)
return True, None
async def finance_settlement_apv_callback(ns={}):
approval_id = ns.get('approval_id') or ns.get('apv_id')
status = ns.get('status')
if not approval_id or not status:
return {'status': False, 'msg': '缺少 approval_id/apv_id 或 status'}
status_map = {
'start': 'approving',
'agree': 'approved',
'refuse': 'rejected',
'terminate': 'cancelled',
}
next_status = status_map.get(status, status)
db = DBPools()
async with db.sqlorContext(DBNAME) as sor:
try:
rows = await sor.R('finance_settlement', {'approval_id': approval_id, 'del_flg': '0'})
if not rows:
return {'status': False, 'msg': '未找到结算审批数据'}
now_time = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
results = []
for settlement in rows:
update_data = {
'id': settlement['id'],
'approval_status': status,
'status': next_status,
'update_at': now_time,
}
if status != 'agree':
await sor.U('finance_settlement', update_data)
results.append({'settlement_id': settlement['id'], 'status': next_status})
continue
if settlement.get('status') == 'settled':
results.append({'settlement_id': settlement['id'], 'status': 'settled'})
continue
if settlement.get('counterparty_type') == 'supplier':
ok, err = await _settle_supplier(sor, settlement)
else:
ok, err = await _settle_reseller(sor, settlement)
if ok:
update_data['status'] = 'settled'
update_data['settled_at'] = now_time
update_data['failure_reason'] = None
results.append({'settlement_id': settlement['id'], 'status': 'settled'})
else:
update_data['status'] = 'failed'
update_data['failure_reason'] = err
results.append({
'settlement_id': settlement['id'],
'status': 'failed',
'failure_reason': err,
})
await sor.U('finance_settlement', update_data)
return {'status': True, 'msg': 'ok', 'data': results}
except Exception as e:
return {'status': False, 'msg': '审批回调处理失败, %s' % str(e)}
ret = await finance_settlement_apv_callback(params_kw)
return ret

View File

@ -0,0 +1,283 @@
# -*- coding: utf-8 -*-
"""创建财务结算单并保存明细快照。"""
import datetime
DBNAME = 'kboss'
def _round_money(v):
return round(float(v or 0), 8)
def _is_reverse_op(op):
return str(op or '').upper().endswith('_REVERSE')
def _sale_mode_from_subject(subjectname):
text = str(subjectname or '')
if '代付费' in text or '返佣' in text:
return '1'
if '底价' in text or '低价' in text:
return '2'
return '0'
def _validate_params(ns):
for key in ('accounting_orgid', 'counterparty_type', 'counterparty_orgid'):
if not ns.get(key):
return None, '缺少 %s' % key
if ns.get('counterparty_type') not in ('supplier', 'reseller'):
return None, 'counterparty_type 仅支持 supplier / reseller'
period_type = ns.get('period_type') or 'day'
if period_type not in ('day', 'month'):
return None, 'period_type 仅支持 day / month'
period_start = ns.get('period_start') or ns.get('start_date')
period_end = ns.get('period_end') or ns.get('end_date')
if not period_start or not period_end:
return None, '缺少 period_start / period_end'
return {
'accounting_orgid': ns.get('accounting_orgid'),
'counterparty_type': ns.get('counterparty_type'),
'counterparty_orgid': ns.get('counterparty_orgid'),
'period_type': period_type,
'period_start': period_start,
'period_end': period_end,
'userid': ns.get('userid') or ns.get('user_id'),
}, None
async def _ensure_schema(sor):
await sor.sqlExe("""
CREATE TABLE IF NOT EXISTS finance_settlement (
id varchar(32) NOT NULL,
settlement_no varchar(64),
accounting_orgid varchar(32),
counterparty_type varchar(16),
counterparty_orgid varchar(32),
counterparty_name varchar(255),
period_type varchar(16),
period_start date,
period_end date,
sales_amount double(18,8) DEFAULT 0,
settlement_amount double(18,8) DEFAULT 0,
platform_income_amount double(18,8) DEFAULT 0,
bill_count int DEFAULT 0,
status varchar(16) DEFAULT 'draft',
approval_id varchar(64),
approval_status varchar(16),
failure_reason varchar(1000),
settled_at datetime,
created_by varchar(32),
del_flg varchar(1) DEFAULT '0',
create_at datetime DEFAULT CURRENT_TIMESTAMP,
update_at datetime DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id),
UNIQUE KEY finance_settlement_un (accounting_orgid, counterparty_type, counterparty_orgid, period_start, period_end, del_flg)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
""", {})
await sor.sqlExe("""
CREATE TABLE IF NOT EXISTS finance_settlement_detail (
id varchar(32) NOT NULL,
settlement_id varchar(32),
bill_id varchar(32),
order_id varchar(32),
bill_date date,
customerid varchar(32),
providerid varchar(32),
productid varchar(32),
business_op varchar(64),
sale_mode varchar(1),
sales_amount double(18,8) DEFAULT 0,
settlement_amount double(18,8) DEFAULT 0,
platform_income_amount double(18,8) DEFAULT 0,
amount_source varchar(32) DEFAULT 'bill_detail',
del_flg varchar(1) DEFAULT '0',
create_at datetime DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id),
KEY finance_settlement_detail_idx1 (settlement_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
""", {})
async def _existing_settlement(sor, args):
rows = await sor.sqlExe("""
SELECT id, settlement_no, status
FROM finance_settlement
WHERE accounting_orgid=${accounting_orgid}$
AND counterparty_type=${counterparty_type}$
AND counterparty_orgid=${counterparty_orgid}$
AND period_start=${period_start}$
AND period_end=${period_end}$
AND del_flg='0'
LIMIT 1
""", args)
return rows[0] if rows else None
async def _counterparty_name(sor, args):
if args['counterparty_type'] == 'supplier':
rows = await sor.sqlExe("""
SELECT COALESCE(p.name, o.orgname) AS name
FROM organization o
LEFT JOIN provider p ON p.orgid=o.id AND p.del_flg='0'
WHERE o.id=${counterparty_orgid}$ AND o.del_flg='0'
LIMIT 1
""", args)
else:
rows = await sor.sqlExe("""
SELECT orgname AS name FROM organization
WHERE id=${counterparty_orgid}$ AND del_flg='0'
LIMIT 1
""", args)
return rows[0].get('name') if rows else None
async def _fetch_source_rows(sor, args):
filters = [
"b.del_flg='0'",
"b.bill_state='1'",
"b.bill_date >= ${period_start}$",
"b.bill_date <= ${period_end}$",
]
if args['counterparty_type'] == 'supplier':
filters.append('b.providerid=${counterparty_orgid}$')
settlement_join = """
LEFT JOIN bill_detail sd ON sd.billid=b.id
AND sd.accounting_orgid=${accounting_orgid}$
AND sd.del_flg='0'
AND sd.accounting_dir='贷'
AND sd.subjectname LIKE '待结转%'
"""
else:
filters.append('cust.parentid=${counterparty_orgid}$')
settlement_join = """
LEFT JOIN bill_detail sd ON sd.billid=b.id
AND sd.accounting_orgid=${accounting_orgid}$
AND sd.del_flg='0'
AND sd.accounting_dir='借'
AND sd.subjectname='分销商存放资金'
AND sd.participantid=cust.parentid
"""
sql = """
SELECT b.id AS bill_id, b.orderid AS order_id, b.bill_date,
b.customerid, b.providerid, b.productid, b.business_op,
b.amount AS sales_amount,
COALESCE(sd.amount, 0) AS settlement_amount,
sd.subjectname AS settlement_subject,
COALESCE((
SELECT SUM(bd.amount)
FROM bill_detail bd
WHERE bd.billid=b.id
AND bd.accounting_orgid=${accounting_orgid}$
AND bd.del_flg='0'
AND bd.accounting_dir='贷'
AND bd.subjectname IN ('折扣收入', '底价收入')
), 0) AS platform_income_amount
FROM bill b
INNER JOIN organization cust ON cust.id=b.customerid AND cust.del_flg='0'
%s
WHERE %s
ORDER BY b.bill_date DESC, b.create_at DESC
""" % (settlement_join, ' AND '.join(filters))
rows = await sor.sqlExe(sql, args)
items = []
seen_platform_bill = set()
for row in rows:
sign = -1 if _is_reverse_op(row.get('business_op')) else 1
bill_id = row.get('bill_id')
platform_income = float(row.get('platform_income_amount') or 0)
if bill_id in seen_platform_bill:
platform_income = 0
seen_platform_bill.add(bill_id)
items.append({
'bill_id': bill_id,
'order_id': row.get('order_id'),
'bill_date': str(row.get('bill_date'))[:10] if row.get('bill_date') else None,
'customerid': row.get('customerid'),
'providerid': row.get('providerid'),
'productid': row.get('productid'),
'business_op': row.get('business_op'),
'sale_mode': _sale_mode_from_subject(row.get('settlement_subject')),
'sales_amount': _round_money(sign * float(row.get('sales_amount') or 0)),
'settlement_amount': _round_money(sign * float(row.get('settlement_amount') or 0)),
'platform_income_amount': _round_money(sign * platform_income),
'amount_source': 'bill_detail',
})
return items
def _summary(items):
return {
'sales_amount': _round_money(sum(i.get('sales_amount') or 0 for i in items)),
'settlement_amount': _round_money(sum(i.get('settlement_amount') or 0 for i in items)),
'platform_income_amount': _round_money(sum(i.get('platform_income_amount') or 0 for i in items)),
'bill_count': len({i.get('bill_id') for i in items if i.get('bill_id')}),
}
async def finance_settlement_create(ns={}):
args, err = _validate_params(ns)
if err:
return {'status': False, 'msg': err}
db = DBPools()
async with db.sqlorContext(DBNAME) as sor:
try:
await _ensure_schema(sor)
existing = await _existing_settlement(sor, args)
if existing:
return {'status': False, 'msg': '该账期已存在结算单', 'data': existing}
items = await _fetch_source_rows(sor, args)
if not items:
return {'status': False, 'msg': '该账期无可结算已记账账单'}
summary = _summary(items)
settlement_id = uuid()
settlement_no = 'FS%s%s' % (
datetime.datetime.now().strftime('%Y%m%d%H%M%S'),
str(settlement_id)[-6:],
)
master = {
'id': settlement_id,
'settlement_no': settlement_no,
'accounting_orgid': args['accounting_orgid'],
'counterparty_type': args['counterparty_type'],
'counterparty_orgid': args['counterparty_orgid'],
'counterparty_name': await _counterparty_name(sor, args),
'period_type': args['period_type'],
'period_start': args['period_start'],
'period_end': args['period_end'],
'sales_amount': summary['sales_amount'],
'settlement_amount': summary['settlement_amount'],
'platform_income_amount': summary['platform_income_amount'],
'bill_count': summary['bill_count'],
'status': 'draft',
'created_by': args.get('userid'),
'del_flg': '0',
'create_at': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
'update_at': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
}
await sor.C('finance_settlement', master)
for item in items:
detail = dict(item)
detail['id'] = uuid()
detail['settlement_id'] = settlement_id
detail['del_flg'] = '0'
detail['create_at'] = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
await sor.C('finance_settlement_detail', detail)
return {
'status': True,
'msg': 'ok',
'data': {
'settlement_id': settlement_id,
'settlement_no': settlement_no,
'status': 'draft',
'summary': summary,
},
}
except Exception as e:
return {'status': False, 'msg': '创建结算单失败, %s' % str(e)}
ret = await finance_settlement_create(params_kw)
return ret

View File

@ -0,0 +1,61 @@
# -*- coding: utf-8 -*-
"""财务结算单详情查询。"""
DBNAME = 'kboss'
def _parse_page(ns, default_page=1, default_size=100, max_size=500):
try:
current_page = int(ns.get('current_page', default_page) or default_page)
except (TypeError, ValueError):
current_page = default_page
try:
page_size = int(ns.get('page_size', default_size) or default_size)
except (TypeError, ValueError):
page_size = default_size
current_page = max(1, current_page)
page_size = max(1, min(page_size, max_size))
return current_page, page_size, (current_page - 1) * page_size
async def finance_settlement_detail(ns={}):
settlement_id = ns.get('settlement_id') or ns.get('id')
if not settlement_id:
return {'status': False, 'msg': '缺少 settlement_id'}
current_page, page_size, offset = _parse_page(ns)
db = DBPools()
async with db.sqlorContext(DBNAME) as sor:
try:
rows = await sor.R('finance_settlement', {'id': settlement_id, 'del_flg': '0'})
if not rows:
return {'status': False, 'msg': '结算单不存在'}
count_rows = await sor.sqlExe("""
SELECT COUNT(*) AS total_count
FROM finance_settlement_detail
WHERE settlement_id=${settlement_id}$ AND del_flg='0'
""", {'settlement_id': settlement_id})
total_count = count_rows[0]['total_count'] if count_rows else 0
detail_rows = await sor.sqlExe("""
SELECT *
FROM finance_settlement_detail
WHERE settlement_id=${settlement_id}$ AND del_flg='0'
ORDER BY bill_date DESC, create_at DESC
LIMIT %d OFFSET %d
""" % (page_size, offset), {'settlement_id': settlement_id})
return {
'status': True,
'msg': 'ok',
'data': {
'settlement': rows[0],
'detail_total_count': total_count,
'current_page': current_page,
'page_size': page_size,
'items': detail_rows,
},
}
except Exception as e:
return {'status': False, 'msg': '查询结算单详情失败, %s' % str(e)}
ret = await finance_settlement_detail(params_kw)
return ret

View File

@ -0,0 +1,70 @@
# -*- coding: utf-8 -*-
"""财务结算单列表查询。"""
DBNAME = 'kboss'
def _parse_page(ns, default_page=1, default_size=20, max_size=200):
try:
current_page = int(ns.get('current_page', default_page) or default_page)
except (TypeError, ValueError):
current_page = default_page
try:
page_size = int(ns.get('page_size', default_size) or default_size)
except (TypeError, ValueError):
page_size = default_size
current_page = max(1, current_page)
page_size = max(1, min(page_size, max_size))
return current_page, page_size, (current_page - 1) * page_size
async def finance_settlement_list(ns={}):
accounting_orgid = ns.get('accounting_orgid')
if not accounting_orgid:
return {'status': False, 'msg': '缺少 accounting_orgid'}
current_page, page_size, offset = _parse_page(ns)
conditions = ["accounting_orgid=${accounting_orgid}$", "del_flg='0'"]
params = {'accounting_orgid': accounting_orgid}
for key in ('counterparty_type', 'counterparty_orgid', 'status', 'period_type'):
if ns.get(key):
conditions.append('%s=${%s}$' % (key, key))
params[key] = ns[key]
if ns.get('start_date'):
conditions.append('period_end >= ${start_date}$')
params['start_date'] = ns['start_date']
if ns.get('end_date'):
conditions.append('period_start <= ${end_date}$')
params['end_date'] = ns['end_date']
where_sql = ' AND '.join(conditions)
db = DBPools()
async with db.sqlorContext(DBNAME) as sor:
try:
count_rows = await sor.sqlExe(
'SELECT COUNT(*) AS total_count FROM finance_settlement WHERE %s' % where_sql,
params,
)
total_count = count_rows[0]['total_count'] if count_rows else 0
sql = """
SELECT *
FROM finance_settlement
WHERE %s
ORDER BY period_end DESC, create_at DESC
LIMIT %d OFFSET %d
""" % (where_sql, page_size, offset)
rows = await sor.sqlExe(sql, params)
return {
'status': True,
'msg': 'ok',
'data': {
'total_count': total_count,
'current_page': current_page,
'page_size': page_size,
'items': rows,
},
}
except Exception as e:
return {'status': False, 'msg': '查询结算单失败, %s' % str(e)}
ret = await finance_settlement_list(params_kw)
return ret

View File

@ -0,0 +1,89 @@
/*
SQL
1. finance_settlement
2. finance_settlement_detail
3. sale_mode
- provider_settle_data
-
account(accounting_orgid=, orgid=, subject=)
account(accounting_orgid=, orgid=, subject=)
*/
CREATE TABLE IF NOT EXISTS `finance_settlement` (
`id` varchar(32) NOT NULL COMMENT 'id',
`settlement_no` varchar(64) DEFAULT NULL COMMENT '结算单号',
`accounting_orgid` varchar(32) DEFAULT NULL COMMENT '账本机构id',
`counterparty_type` varchar(16) DEFAULT NULL COMMENT '对手方类型 supplier/reseller',
`counterparty_orgid` varchar(32) DEFAULT NULL COMMENT '对手方机构id',
`counterparty_name` varchar(255) DEFAULT NULL COMMENT '对手方名称',
`period_type` varchar(16) DEFAULT NULL COMMENT '账期类型 day/month',
`period_start` date DEFAULT NULL COMMENT '账期开始日期',
`period_end` date DEFAULT NULL COMMENT '账期结束日期',
`sales_amount` double(18,8) DEFAULT 0.00000000 COMMENT '销售金额',
`settlement_amount` double(18,8) DEFAULT 0.00000000 COMMENT '结算金额',
`platform_income_amount` double(18,8) DEFAULT 0.00000000 COMMENT '平台收入',
`bill_count` int(11) DEFAULT 0 COMMENT '账单数量',
`status` varchar(16) DEFAULT 'draft' COMMENT 'draft/approving/approved/rejected/settled/failed/cancelled',
`approval_id` varchar(64) DEFAULT NULL COMMENT '审批id',
`approval_status` varchar(16) DEFAULT NULL COMMENT '审批状态',
`failure_reason` varchar(1000) DEFAULT NULL COMMENT '失败原因',
`settled_at` datetime DEFAULT NULL COMMENT '结算记账时间',
`created_by` varchar(32) DEFAULT NULL COMMENT '创建人',
`del_flg` varchar(1) DEFAULT '0' COMMENT '删除标志',
`create_at` datetime DEFAULT current_timestamp() COMMENT '创建时间',
`update_at` datetime DEFAULT current_timestamp() COMMENT '更新时间',
PRIMARY KEY (`id`),
UNIQUE KEY `finance_settlement_un` (`accounting_orgid`, `counterparty_type`, `counterparty_orgid`, `period_start`, `period_end`, `del_flg`),
KEY `finance_settlement_idx1` (`accounting_orgid`, `status`),
KEY `finance_settlement_idx2` (`counterparty_type`, `counterparty_orgid`),
KEY `finance_settlement_idx3` (`period_start`, `period_end`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='财务结算单主表';
CREATE TABLE IF NOT EXISTS `finance_settlement_detail` (
`id` varchar(32) NOT NULL COMMENT 'id',
`settlement_id` varchar(32) DEFAULT NULL COMMENT '结算单id',
`bill_id` varchar(32) DEFAULT NULL COMMENT '账单id',
`order_id` varchar(32) DEFAULT NULL COMMENT '订单id',
`bill_date` date DEFAULT NULL COMMENT '账单日期',
`customerid` varchar(32) DEFAULT NULL COMMENT '客户id',
`providerid` varchar(32) DEFAULT NULL COMMENT '供应商机构id',
`productid` varchar(32) DEFAULT NULL COMMENT '产品id',
`business_op` varchar(64) DEFAULT NULL COMMENT '业务操作',
`sale_mode` varchar(1) DEFAULT NULL COMMENT '销售模式 0折扣/1代付费/2底价',
`sales_amount` double(18,8) DEFAULT 0.00000000 COMMENT '销售金额',
`settlement_amount` double(18,8) DEFAULT 0.00000000 COMMENT '结算金额',
`platform_income_amount` double(18,8) DEFAULT 0.00000000 COMMENT '平台收入',
`amount_source` varchar(32) DEFAULT 'bill_detail' COMMENT '金额来源',
`del_flg` varchar(1) DEFAULT '0' COMMENT '删除标志',
`create_at` datetime DEFAULT current_timestamp() COMMENT '创建时间',
PRIMARY KEY (`id`),
KEY `finance_settlement_detail_idx1` (`settlement_id`),
KEY `finance_settlement_detail_idx2` (`bill_id`),
KEY `finance_settlement_detail_idx3` (`providerid`, `productid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='财务结算单明细快照';
ALTER TABLE `finance_settlement_detail`
ADD COLUMN IF NOT EXISTS `sale_mode` varchar(1) DEFAULT NULL COMMENT '销售模式 0折扣/1代付费/2底价' AFTER `business_op`;
-- 执行分销商结算前,可用以下 SQL 检查目标账户是否存在:
-- SELECT a.*
-- FROM account a
-- JOIN subject s ON a.subjectid=s.id
-- WHERE a.accounting_orgid='<上级机构orgid>'
-- AND a.orgid='<分销商orgid>'
-- AND s.name='分销商存放资金'
-- AND a.del_flg='0'
-- AND s.del_flg='0';
--
-- SELECT a.*
-- FROM account a
-- JOIN subject s ON a.subjectid=s.id
-- WHERE a.accounting_orgid='<上级机构orgid>'
-- AND a.orgid='<上级机构orgid>'
-- AND s.name='资金账号'
-- AND a.del_flg='0'
-- AND s.del_flg='0';

View File

@ -0,0 +1,281 @@
# -*- coding: utf-8 -*-
"""财务结算单生成前预览。"""
DBNAME = 'kboss'
def _round_money(v):
return round(float(v or 0), 8)
def _is_reverse_op(op):
return str(op or '').upper().endswith('_REVERSE')
def _sale_mode_from_subject(subjectname):
text = str(subjectname or '')
if '代付费' in text or '返佣' in text:
return '1'
if '底价' in text or '低价' in text:
return '2'
return '0'
def _parse_page(ns, default_page=1, default_size=50, max_size=500):
try:
current_page = int(ns.get('current_page', default_page) or default_page)
except (TypeError, ValueError):
current_page = default_page
try:
page_size = int(ns.get('page_size', default_size) or default_size)
except (TypeError, ValueError):
page_size = default_size
current_page = max(1, current_page)
page_size = max(1, min(page_size, max_size))
return current_page, page_size, (current_page - 1) * page_size
def _validate_params(ns):
required = ('accounting_orgid', 'counterparty_type', 'counterparty_orgid')
for key in required:
if not ns.get(key):
return None, '缺少 %s' % key
counterparty_type = ns.get('counterparty_type')
if counterparty_type not in ('supplier', 'reseller'):
return None, 'counterparty_type 仅支持 supplier / reseller'
period_type = ns.get('period_type') or 'day'
if period_type not in ('day', 'month'):
return None, 'period_type 仅支持 day / month'
period_start = ns.get('period_start') or ns.get('start_date')
period_end = ns.get('period_end') or ns.get('end_date')
if not period_start or not period_end:
return None, '缺少 period_start / period_end'
return {
'accounting_orgid': ns.get('accounting_orgid'),
'counterparty_type': counterparty_type,
'counterparty_orgid': ns.get('counterparty_orgid'),
'period_type': period_type,
'period_start': period_start,
'period_end': period_end,
}, None
async def _ensure_schema(sor):
await sor.sqlExe("""
CREATE TABLE IF NOT EXISTS finance_settlement (
id varchar(32) NOT NULL,
settlement_no varchar(64),
accounting_orgid varchar(32),
counterparty_type varchar(16),
counterparty_orgid varchar(32),
counterparty_name varchar(255),
period_type varchar(16),
period_start date,
period_end date,
sales_amount double(18,8) DEFAULT 0,
settlement_amount double(18,8) DEFAULT 0,
platform_income_amount double(18,8) DEFAULT 0,
bill_count int DEFAULT 0,
status varchar(16) DEFAULT 'draft',
approval_id varchar(64),
approval_status varchar(16),
failure_reason varchar(1000),
settled_at datetime,
created_by varchar(32),
del_flg varchar(1) DEFAULT '0',
create_at datetime DEFAULT CURRENT_TIMESTAMP,
update_at datetime DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id),
UNIQUE KEY finance_settlement_un (accounting_orgid, counterparty_type, counterparty_orgid, period_start, period_end, del_flg)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
""", {})
await sor.sqlExe("""
CREATE TABLE IF NOT EXISTS finance_settlement_detail (
id varchar(32) NOT NULL,
settlement_id varchar(32),
bill_id varchar(32),
order_id varchar(32),
bill_date date,
customerid varchar(32),
providerid varchar(32),
productid varchar(32),
business_op varchar(64),
sale_mode varchar(1),
sales_amount double(18,8) DEFAULT 0,
settlement_amount double(18,8) DEFAULT 0,
platform_income_amount double(18,8) DEFAULT 0,
amount_source varchar(32) DEFAULT 'bill_detail',
del_flg varchar(1) DEFAULT '0',
create_at datetime DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id),
KEY finance_settlement_detail_idx1 (settlement_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
""", {})
async def _existing_settlement(sor, args):
rows = await sor.sqlExe("""
SELECT id, settlement_no, status
FROM finance_settlement
WHERE accounting_orgid=${accounting_orgid}$
AND counterparty_type=${counterparty_type}$
AND counterparty_orgid=${counterparty_orgid}$
AND period_start=${period_start}$
AND period_end=${period_end}$
AND del_flg='0'
LIMIT 1
""", args)
return rows[0] if rows else None
async def _counterparty_name(sor, args):
if args['counterparty_type'] == 'supplier':
rows = await sor.sqlExe("""
SELECT COALESCE(p.name, o.orgname) AS name
FROM organization o
LEFT JOIN provider p ON p.orgid=o.id AND p.del_flg='0'
WHERE o.id=${counterparty_orgid}$ AND o.del_flg='0'
LIMIT 1
""", args)
else:
rows = await sor.sqlExe("""
SELECT orgname AS name FROM organization
WHERE id=${counterparty_orgid}$ AND del_flg='0'
LIMIT 1
""", args)
return rows[0].get('name') if rows else None
async def _fetch_source_rows(sor, args):
params = dict(args)
filters = [
"b.del_flg='0'",
"b.bill_state='1'",
"b.bill_date >= ${period_start}$",
"b.bill_date <= ${period_end}$",
]
if args['counterparty_type'] == 'supplier':
filters.append('b.providerid=${counterparty_orgid}$')
settlement_join = """
LEFT JOIN bill_detail sd ON sd.billid=b.id
AND sd.accounting_orgid=${accounting_orgid}$
AND sd.del_flg='0'
AND sd.accounting_dir='贷'
AND sd.subjectname LIKE '待结转%'
"""
settlement_expr = 'COALESCE(sd.amount, 0)'
sale_mode_expr = 'sd.subjectname'
else:
filters.append('cust.parentid=${counterparty_orgid}$')
settlement_join = """
LEFT JOIN bill_detail sd ON sd.billid=b.id
AND sd.accounting_orgid=${accounting_orgid}$
AND sd.del_flg='0'
AND sd.accounting_dir='借'
AND sd.subjectname='分销商存放资金'
AND sd.participantid=cust.parentid
"""
settlement_expr = 'COALESCE(sd.amount, 0)'
sale_mode_expr = 'sd.subjectname'
where_sql = ' AND '.join(filters)
sql = """
SELECT b.id AS bill_id,
b.orderid AS order_id,
b.bill_date,
b.customerid,
b.providerid,
b.productid,
b.business_op,
b.amount AS sales_amount,
%s AS settlement_amount,
%s AS settlement_subject,
COALESCE((
SELECT SUM(bd.amount)
FROM bill_detail bd
WHERE bd.billid=b.id
AND bd.accounting_orgid=${accounting_orgid}$
AND bd.del_flg='0'
AND bd.accounting_dir='贷'
AND bd.subjectname IN ('折扣收入', '底价收入')
), 0) AS platform_income_amount
FROM bill b
INNER JOIN organization cust ON cust.id=b.customerid AND cust.del_flg='0'
%s
WHERE %s
ORDER BY b.bill_date DESC, b.create_at DESC
""" % (settlement_expr, sale_mode_expr, settlement_join, where_sql)
rows = await sor.sqlExe(sql, params)
items = []
seen_platform_bill = set()
for row in rows:
sign = -1 if _is_reverse_op(row.get('business_op')) else 1
bill_id = row.get('bill_id')
platform_income = float(row.get('platform_income_amount') or 0)
if bill_id in seen_platform_bill:
platform_income = 0
seen_platform_bill.add(bill_id)
items.append({
'bill_id': bill_id,
'order_id': row.get('order_id'),
'bill_date': str(row.get('bill_date'))[:10] if row.get('bill_date') else None,
'customerid': row.get('customerid'),
'providerid': row.get('providerid'),
'productid': row.get('productid'),
'business_op': row.get('business_op'),
'sale_mode': _sale_mode_from_subject(row.get('settlement_subject')),
'sales_amount': _round_money(sign * float(row.get('sales_amount') or 0)),
'settlement_amount': _round_money(sign * float(row.get('settlement_amount') or 0)),
'platform_income_amount': _round_money(sign * platform_income),
'amount_source': 'bill_detail',
})
return items
def _summary(items):
return {
'sales_amount': _round_money(sum(i.get('sales_amount') or 0 for i in items)),
'settlement_amount': _round_money(sum(i.get('settlement_amount') or 0 for i in items)),
'platform_income_amount': _round_money(sum(i.get('platform_income_amount') or 0 for i in items)),
'bill_count': len({i.get('bill_id') for i in items if i.get('bill_id')}),
}
async def finance_settlement_preview(ns={}):
args, err = _validate_params(ns)
if err:
return {'status': False, 'msg': err}
current_page, page_size, offset = _parse_page(ns)
db = DBPools()
async with db.sqlorContext(DBNAME) as sor:
try:
await _ensure_schema(sor)
existing = await _existing_settlement(sor, args)
items = await _fetch_source_rows(sor, args)
total_count = len(items)
return {
'status': True,
'msg': 'ok',
'data': {
'accounting_orgid': args['accounting_orgid'],
'counterparty_type': args['counterparty_type'],
'counterparty_orgid': args['counterparty_orgid'],
'counterparty_name': await _counterparty_name(sor, args),
'period_type': args['period_type'],
'period_start': args['period_start'],
'period_end': args['period_end'],
'existing_settlement': existing,
'can_create': existing is None and total_count > 0,
'summary': _summary(items),
'total_count': total_count,
'current_page': current_page,
'page_size': page_size,
'items': items[offset:offset + page_size],
},
}
except Exception as e:
return {'status': False, 'msg': '预览失败, %s' % str(e)}
ret = await finance_settlement_preview(params_kw)
return ret

View File

@ -0,0 +1,100 @@
# -*- coding: utf-8 -*-
"""财务结算单提交审批。"""
import datetime
import json
DBNAME = 'kboss'
async def _finance_phone(sor, orgid):
rows = await sor.sqlExe("""
SELECT u.mobile
FROM users u
LEFT JOIN userrole ur ON u.id=ur.userid
LEFT JOIN role r ON ur.roleid=r.id
WHERE r.role='财务'
AND u.orgid=${orgid}$
AND ur.del_flg='0'
AND r.del_flg='0'
AND u.del_flg='0'
LIMIT 1
""", {'orgid': orgid})
return rows[0]['mobile'] if rows else None
async def finance_settlement_submit(ns={}):
settlement_id = ns.get('settlement_id') or ns.get('id')
userid = ns.get('userid') or ns.get('user_id')
business_name = ns.get('business_name') or '财务结算'
if not settlement_id or not userid:
return {'status': False, 'msg': '缺少 settlement_id 或 userid'}
db = DBPools()
async with db.sqlorContext(DBNAME) as sor:
try:
rows = await sor.R('finance_settlement', {'id': settlement_id, 'del_flg': '0'})
if not rows:
return {'status': False, 'msg': '结算单不存在'}
settlement = rows[0]
if settlement.get('status') not in ('draft', 'rejected', 'failed'):
return {'status': False, 'msg': '当前状态不可提交审批'}
phone = await _finance_phone(sor, settlement.get('accounting_orgid'))
if not phone:
return {'status': False, 'msg': '未找到当前机构财务审批人'}
business_rows = await sor.R('apv_business', {'business_name': business_name, 'del_flg': '0'})
if not business_rows:
return {'status': False, 'msg': '审批业务不存在: %s' % business_name}
form_component = {
'title': '财务结算审批',
'detail': {
'data': '%s %s-%s %s 金额:%s' % (
settlement.get('counterparty_name'),
settlement.get('period_start'),
settlement.get('period_end'),
settlement.get('counterparty_type'),
settlement.get('settlement_amount'),
)
},
}
apv_json = json.dumps({
'table': 'finance_settlement',
'settlement_id': settlement_id,
'settlement_no': settlement.get('settlement_no'),
}, ensure_ascii=False)
resp = await issue_approve(
phone,
settlement.get('accounting_orgid'),
userid,
business_rows[0]['id'],
form_component,
settlement.get('counterparty_orgid'),
settlement_id,
apv_json,
)
if not resp.get('status'):
return {'status': False, 'msg': '发起审批失败, %s' % resp.get('msg')}
now_time = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
await sor.U('finance_settlement', {
'id': settlement_id,
'status': 'approving',
'approval_status': 'start',
'approval_id': resp.get('instanceId'),
'created_by': userid,
'failure_reason': None,
'update_at': now_time,
})
return {
'status': True,
'msg': 'ok',
'data': {
'settlement_id': settlement_id,
'approval_id': resp.get('instanceId'),
'status': 'approving',
},
}
except Exception as e:
return {'status': False, 'msg': '提交审批失败, %s' % str(e)}
ret = await finance_settlement_submit(params_kw)
return ret

View File

@ -0,0 +1,225 @@
# -*- coding: utf-8 -*-
"""
财务结算汇总查询。
按供应商或分销商聚合日结/月结应结金额、平台收入和销售额。
金额以已记账 bill_detail 为准,不使用未记账估算。
"""
DBNAME = 'kboss'
INCOME_SUBJECTS = ('折扣收入', '底价收入')
PARENT_SETTLE_SUBJECT = '分销商存放资金'
def _round_money(v):
return round(float(v or 0), 8)
def _is_reverse_op(op):
return str(op or '').upper().endswith('_REVERSE')
def _parse_page(ns, default_page=1, default_size=20, max_size=200):
try:
current_page = int(ns.get('current_page', default_page) or default_page)
except (TypeError, ValueError):
current_page = default_page
try:
page_size = int(ns.get('page_size', default_size) or default_size)
except (TypeError, ValueError):
page_size = default_size
current_page = max(1, current_page)
page_size = max(1, min(page_size, max_size))
return current_page, page_size, (current_page - 1) * page_size
def _period_key(bill_date, period_type):
text = str(bill_date or '')[:10]
if period_type == 'month':
return text[:7]
return text
def _validate_params(ns):
debug(ns)
accounting_orgid = ns.get('accounting_orgid')
counterparty_type = ns.get('counterparty_type')
period_type = ns.get('period_type') or 'day'
start_date = ns.get('start_date') or ns.get('period_start')
end_date = ns.get('end_date') or ns.get('period_end')
if not accounting_orgid:
return None, '缺少 accounting_orgid'
if counterparty_type not in ('supplier', 'reseller'):
return None, 'counterparty_type 仅支持 supplier / reseller'
if period_type not in ('day', 'month'):
return None, 'period_type 仅支持 day / month'
if not start_date or not end_date:
return None, '缺少 start_date / end_date'
return {
'accounting_orgid': accounting_orgid,
'counterparty_type': counterparty_type,
'period_type': period_type,
'start_date': start_date,
'end_date': end_date,
'counterparty_orgid': ns.get('counterparty_orgid'),
}, None
async def _fetch_source_rows(sor, args):
params = {
'accounting_orgid': args['accounting_orgid'],
'start_date': args['start_date'],
'end_date': args['end_date'],
}
filters = [
"b.del_flg = '0'",
"b.bill_state = '1'",
"b.bill_date >= ${start_date}$",
"b.bill_date <= ${end_date}$",
]
if args['counterparty_type'] == 'supplier':
counterparty_expr = 'b.providerid'
name_expr = 'COALESCE(pv.name, cp.orgname)'
join_sql = """
LEFT JOIN provider pv ON pv.orgid = b.providerid AND pv.del_flg = '0'
LEFT JOIN organization cp ON cp.id = b.providerid AND cp.del_flg = '0'
"""
settlement_expr = """
SELECT SUM(bd.amount)
FROM bill_detail bd
WHERE bd.billid = b.id
AND bd.accounting_orgid = ${accounting_orgid}$
AND bd.del_flg = '0'
AND bd.accounting_dir = '贷'
AND bd.subjectname LIKE '待结转%'
"""
if args.get('counterparty_orgid'):
filters.append('b.providerid = ${counterparty_orgid}$')
params['counterparty_orgid'] = args['counterparty_orgid']
else:
counterparty_expr = 'cust.parentid'
name_expr = 'cp.orgname'
join_sql = "LEFT JOIN organization cp ON cp.id = cust.parentid AND cp.del_flg = '0'"
settlement_expr = """
SELECT SUM(bd.amount)
FROM bill_detail bd
WHERE bd.billid = b.id
AND bd.accounting_orgid = ${accounting_orgid}$
AND bd.del_flg = '0'
AND bd.accounting_dir = '借'
AND bd.subjectname = '分销商存放资金'
AND bd.participantid = cust.parentid
"""
filters.append("cust.parentid IS NOT NULL AND cust.parentid != ''")
if args.get('counterparty_orgid'):
filters.append('cust.parentid = ${counterparty_orgid}$')
params['counterparty_orgid'] = args['counterparty_orgid']
where_sql = ' AND '.join(filters)
sql = """
SELECT b.id AS bill_id,
b.orderid AS order_id,
b.bill_date,
b.customerid,
b.providerid,
b.productid,
b.business_op,
b.amount AS sales_amount,
%s AS counterparty_orgid,
%s AS counterparty_name,
COALESCE((%s), 0) AS settlement_amount,
COALESCE((
SELECT SUM(bd.amount)
FROM bill_detail bd
WHERE bd.billid = b.id
AND bd.accounting_orgid = ${accounting_orgid}$
AND bd.del_flg = '0'
AND bd.accounting_dir = '贷'
AND bd.subjectname IN ('折扣收入', '底价收入')
), 0) AS platform_income_amount
FROM bill b
INNER JOIN organization cust ON cust.id = b.customerid AND cust.del_flg = '0'
%s
WHERE %s
ORDER BY b.bill_date DESC, b.create_at DESC
""" % (counterparty_expr, name_expr, settlement_expr, join_sql, where_sql)
return await sor.sqlExe(sql, params)
def _aggregate_rows(rows, period_type):
buckets = {}
for row in rows:
counterparty_orgid = row.get('counterparty_orgid')
if not counterparty_orgid:
continue
period = _period_key(row.get('bill_date'), period_type)
key = (period, counterparty_orgid)
if key not in buckets:
buckets[key] = {
'period': period,
'counterparty_orgid': counterparty_orgid,
'counterparty_name': row.get('counterparty_name'),
'sales_amount': 0.0,
'settlement_amount': 0.0,
'platform_income_amount': 0.0,
'bill_count': 0,
}
sign = -1 if _is_reverse_op(row.get('business_op')) else 1
bucket = buckets[key]
bucket['sales_amount'] += sign * float(row.get('sales_amount') or 0)
bucket['settlement_amount'] += sign * float(row.get('settlement_amount') or 0)
bucket['platform_income_amount'] += sign * float(row.get('platform_income_amount') or 0)
bucket['bill_count'] += 1
items = []
for item in buckets.values():
item['sales_amount'] = _round_money(item['sales_amount'])
item['settlement_amount'] = _round_money(item['settlement_amount'])
item['platform_income_amount'] = _round_money(item['platform_income_amount'])
items.append(item)
return sorted(items, key=lambda x: (x['period'], x['settlement_amount']), reverse=True)
def _summary(items):
return {
'sales_amount': _round_money(sum(i.get('sales_amount') or 0 for i in items)),
'settlement_amount': _round_money(sum(i.get('settlement_amount') or 0 for i in items)),
'platform_income_amount': _round_money(sum(i.get('platform_income_amount') or 0 for i in items)),
'bill_count': sum(i.get('bill_count') or 0 for i in items),
}
async def finance_settlement_summary(ns={}):
args, err = _validate_params(ns)
if err:
return {'status': False, 'msg': err}
current_page, page_size, offset = _parse_page(ns)
db = DBPools()
async with db.sqlorContext(DBNAME) as sor:
try:
rows = await _fetch_source_rows(sor, args)
items = _aggregate_rows(rows, args['period_type'])
total_count = len(items)
page_items = items[offset:offset + page_size]
return {
'status': True,
'msg': 'ok',
'data': {
'accounting_orgid': args['accounting_orgid'],
'counterparty_type': args['counterparty_type'],
'period_type': args['period_type'],
'start_date': args['start_date'],
'end_date': args['end_date'],
'summary': _summary(items),
'total_count': total_count,
'current_page': current_page,
'page_size': page_size,
'items': page_items,
},
}
except Exception as e:
return {'status': False, 'msg': '查询失败, %s' % str(e)}
ret = await finance_settlement_summary(params_kw)
return ret

58
b/subject.sql Normal file
View File

@ -0,0 +1,58 @@
/*
Navicat Premium Data Transfer
Source Server : kbossdev
Source Server Type : MariaDB
Source Server Version : 100622
Source Host : db:3306
Source Schema : kboss_dev
Target Server Type : MariaDB
Target Server Version : 100622
File Encoding : 65001
Date: 18/06/2026 17:12:02
*/
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for subject
-- ----------------------------
DROP TABLE IF EXISTS `subject`;
CREATE TABLE `subject` (
`id` varchar(32) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL COMMENT 'id',
`name` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL COMMENT '科目名称',
`balance_side` varchar(1) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL COMMENT '余额方向',
`del_flg` varchar(1) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT '0' COMMENT '删除标志',
`create_at` timestamp NOT NULL DEFAULT current_timestamp() COMMENT '创建时间戳',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb3 COLLATE = utf8mb3_general_ci COMMENT = '科目表' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of subject
-- ----------------------------
INSERT INTO `subject` VALUES ('subject001', '资金账号', '0', '0', '2023-08-03 17:40:34');
INSERT INTO `subject` VALUES ('subject002', '折扣收入', '1', '0', '2023-08-03 17:40:34');
INSERT INTO `subject` VALUES ('subject003', '底价收入', '1', '0', '2023-08-03 17:40:34');
INSERT INTO `subject` VALUES ('subject004', '客户折扣支出', '0', '0', '2023-08-03 17:40:34');
INSERT INTO `subject` VALUES ('subject005', '存放供应商资金', '0', '0', '2023-08-03 17:40:34');
INSERT INTO `subject` VALUES ('subject006', '分销商代付费销售', '0', '0', '2023-08-03 17:40:34');
INSERT INTO `subject` VALUES ('subject007', '支付宝手续费', '0', '0', '2023-08-03 17:40:34');
INSERT INTO `subject` VALUES ('subject008', '业务账', '1', '0', '2023-08-03 17:40:34');
INSERT INTO `subject` VALUES ('subject009', '待结转折扣销售收入', '1', '0', '2023-08-03 17:40:34');
INSERT INTO `subject` VALUES ('subject010', '待结转代付费销售收入', '1', '0', '2023-08-03 17:40:34');
INSERT INTO `subject` VALUES ('subject011', '待结转底价销售收入', '1', '0', '2023-08-03 17:40:34');
INSERT INTO `subject` VALUES ('subject012', '分销商存放资金', '1', '0', '2023-08-03 17:40:34');
INSERT INTO `subject` VALUES ('subject013', '待结转折扣支出', '1', '0', '2023-08-03 17:40:34');
INSERT INTO `subject` VALUES ('subject014', '待结转代付费支出', '1', '0', '2023-08-03 17:40:34');
INSERT INTO `subject` VALUES ('subject015', '待结转底价支出', '1', '0', '2023-08-03 17:40:34');
INSERT INTO `subject` VALUES ('subject016', '代付费销售', '1', '0', '2023-08-03 17:40:34');
INSERT INTO `subject` VALUES ('subject017', '算力券', '1', '0', '2025-03-31 11:50:49');
INSERT INTO `subject` VALUES ('subject018', '算力券发放', '0', '0', '2025-04-08 11:20:16');
INSERT INTO `subject` VALUES ('subject019', '算力券消费', '1', '0', '2025-04-08 11:20:32');
INSERT INTO `subject` VALUES ('subject020', '待结算算力券', '0', '0', '2025-04-08 11:29:02');
INSERT INTO `subject` VALUES ('subject021', '算力劵临时账户', '0', '0', '2025-04-08 17:24:30');
SET FOREIGN_KEY_CHECKS = 1;