commit
a8bb1140cb
60
b/account_config.sql
Normal file
60
b/account_config.sql
Normal 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
131
b/accounting_config.sql
Normal 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;
|
||||||
@ -2396,6 +2396,67 @@ CREATE TABLE `subject` (
|
|||||||
PRIMARY KEY (`id`) USING BTREE
|
PRIMARY KEY (`id`) USING BTREE
|
||||||
) ENGINE = InnoDB CHARACTER SET = utf8mb3 COLLATE = utf8mb3_general_ci COMMENT = '科目表' ROW_FORMAT = Dynamic;
|
) 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
|
-- Table structure for transfer_record
|
||||||
-- ----------------------------
|
-- ----------------------------
|
||||||
|
|||||||
301
b/bill/finance_settlement_apv_callback.dspy
Normal file
301
b/bill/finance_settlement_apv_callback.dspy
Normal 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
|
||||||
283
b/bill/finance_settlement_create.dspy
Normal file
283
b/bill/finance_settlement_create.dspy
Normal 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
|
||||||
61
b/bill/finance_settlement_detail.dspy
Normal file
61
b/bill/finance_settlement_detail.dspy
Normal 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
|
||||||
70
b/bill/finance_settlement_list.dspy
Normal file
70
b/bill/finance_settlement_list.dspy
Normal 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
|
||||||
89
b/bill/finance_settlement_migration.sql
Normal file
89
b/bill/finance_settlement_migration.sql
Normal 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';
|
||||||
281
b/bill/finance_settlement_preview.dspy
Normal file
281
b/bill/finance_settlement_preview.dspy
Normal 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
|
||||||
100
b/bill/finance_settlement_submit.dspy
Normal file
100
b/bill/finance_settlement_submit.dspy
Normal 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
|
||||||
225
b/bill/finance_settlement_summary.dspy
Normal file
225
b/bill/finance_settlement_summary.dspy
Normal 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
|
||||||
@ -35,6 +35,8 @@ async def model_management_search(ns={}):
|
|||||||
offset = (current_page - 1) * page_size
|
offset = (current_page - 1) * page_size
|
||||||
|
|
||||||
conditions = ['1=1']
|
conditions = ['1=1']
|
||||||
|
if ns.get('model_name'):
|
||||||
|
ns['display_name'] = ns.get('model_name')
|
||||||
if ns.get('display_name'):
|
if ns.get('display_name'):
|
||||||
display_name = ns.get('display_name')
|
display_name = ns.get('display_name')
|
||||||
# 模糊查询
|
# 模糊查询
|
||||||
|
|||||||
@ -103,6 +103,34 @@ def _group_key(dt, group_by):
|
|||||||
return dt.strftime('%Y-%m-%d')
|
return dt.strftime('%Y-%m-%d')
|
||||||
|
|
||||||
|
|
||||||
|
def _round_amount(value):
|
||||||
|
return round(float(value or 0), 8)
|
||||||
|
|
||||||
|
|
||||||
|
def _format_amount(value):
|
||||||
|
return '%.8f' % _round_amount(value)
|
||||||
|
|
||||||
|
|
||||||
|
def _format_amount_item(item):
|
||||||
|
formatted = dict(item)
|
||||||
|
formatted['amount'] = _format_amount(formatted.get('amount'))
|
||||||
|
return formatted
|
||||||
|
|
||||||
|
|
||||||
|
def _format_amount_items(items):
|
||||||
|
return [_format_amount_item(item) for item in items]
|
||||||
|
|
||||||
|
|
||||||
|
def _format_amount_summary(summary):
|
||||||
|
formatted = dict(summary)
|
||||||
|
formatted['amount'] = _format_amount(formatted.get('amount'))
|
||||||
|
return formatted
|
||||||
|
|
||||||
|
|
||||||
|
def _usage_time_sort_key(item):
|
||||||
|
return str(item.get('last_usage_time') or '')
|
||||||
|
|
||||||
|
|
||||||
def _normalize_usage_row(row, bill_amount_map=None):
|
def _normalize_usage_row(row, bill_amount_map=None):
|
||||||
usage = _parse_usage_content(row.get('usage_content'))
|
usage = _parse_usage_content(row.get('usage_content'))
|
||||||
orderid = row.get('orderid')
|
orderid = row.get('orderid')
|
||||||
@ -118,7 +146,7 @@ def _normalize_usage_row(row, bill_amount_map=None):
|
|||||||
'prompt_tokens': int(usage.get('prompt_tokens') or 0),
|
'prompt_tokens': int(usage.get('prompt_tokens') or 0),
|
||||||
'completion_tokens': int(usage.get('completion_tokens') or 0),
|
'completion_tokens': int(usage.get('completion_tokens') or 0),
|
||||||
'total_tokens': int(usage.get('total_tokens') or 0),
|
'total_tokens': int(usage.get('total_tokens') or 0),
|
||||||
'amount': round(amount, 8),
|
'amount': _round_amount(amount),
|
||||||
'bill_status': row.get('bill_status'),
|
'bill_status': row.get('bill_status'),
|
||||||
'orderid': orderid,
|
'orderid': orderid,
|
||||||
'usage_time': row.get('created_at'),
|
'usage_time': row.get('created_at'),
|
||||||
@ -303,17 +331,21 @@ def _aggregate_admin_summary(items, user_map, org_map):
|
|||||||
bucket['prompt_tokens'] += item.get('prompt_tokens') or 0
|
bucket['prompt_tokens'] += item.get('prompt_tokens') or 0
|
||||||
bucket['completion_tokens'] += item.get('completion_tokens') or 0
|
bucket['completion_tokens'] += item.get('completion_tokens') or 0
|
||||||
bucket['total_tokens'] += item.get('total_tokens') or 0
|
bucket['total_tokens'] += item.get('total_tokens') or 0
|
||||||
bucket['amount'] = round(bucket['amount'] + float(item.get('amount') or 0), 8)
|
bucket['amount'] = _round_amount(bucket['amount'] + float(item.get('amount') or 0))
|
||||||
bucket['request_count'] += 1
|
bucket['request_count'] += 1
|
||||||
usage_time = item.get('usage_time')
|
usage_time = item.get('usage_time')
|
||||||
if usage_time:
|
if usage_time:
|
||||||
|
if (
|
||||||
|
not bucket.get('last_usage_time')
|
||||||
|
or str(usage_time) > str(bucket['last_usage_time'])
|
||||||
|
):
|
||||||
bucket['last_usage_time'] = usage_time
|
bucket['last_usage_time'] = usage_time
|
||||||
if (
|
if (
|
||||||
not bucket.get('first_usage_time')
|
not bucket.get('first_usage_time')
|
||||||
or str(usage_time) < str(bucket['first_usage_time'])
|
or str(usage_time) < str(bucket['first_usage_time'])
|
||||||
):
|
):
|
||||||
bucket['first_usage_time'] = usage_time
|
bucket['first_usage_time'] = usage_time
|
||||||
return sorted(buckets.values(), key=lambda x: x['amount'], reverse=True)
|
return sorted(buckets.values(), key=_usage_time_sort_key, reverse=True)
|
||||||
|
|
||||||
|
|
||||||
def _aggregate_items(items, group_by=None):
|
def _aggregate_items(items, group_by=None):
|
||||||
@ -346,7 +378,7 @@ def _aggregate_items(items, group_by=None):
|
|||||||
bucket['prompt_tokens'] += item.get('prompt_tokens') or 0
|
bucket['prompt_tokens'] += item.get('prompt_tokens') or 0
|
||||||
bucket['completion_tokens'] += item.get('completion_tokens') or 0
|
bucket['completion_tokens'] += item.get('completion_tokens') or 0
|
||||||
bucket['total_tokens'] += item.get('total_tokens') or 0
|
bucket['total_tokens'] += item.get('total_tokens') or 0
|
||||||
bucket['amount'] = round(bucket['amount'] + float(item.get('amount') or 0), 8)
|
bucket['amount'] = _round_amount(bucket['amount'] + float(item.get('amount') or 0))
|
||||||
bucket['request_count'] += 1
|
bucket['request_count'] += 1
|
||||||
|
|
||||||
return sorted(buckets.values(), key=lambda x: x['period'], reverse=True)
|
return sorted(buckets.values(), key=lambda x: x['period'], reverse=True)
|
||||||
@ -358,7 +390,7 @@ def _summarize(items):
|
|||||||
'prompt_tokens': sum(i.get('prompt_tokens') or 0 for i in items),
|
'prompt_tokens': sum(i.get('prompt_tokens') or 0 for i in items),
|
||||||
'completion_tokens': sum(i.get('completion_tokens') or 0 for i in items),
|
'completion_tokens': sum(i.get('completion_tokens') or 0 for i in items),
|
||||||
'total_tokens': sum(i.get('total_tokens') or 0 for i in items),
|
'total_tokens': sum(i.get('total_tokens') or 0 for i in items),
|
||||||
'amount': round(sum(float(i.get('amount') or 0) for i in items), 8),
|
'amount': _round_amount(sum(float(i.get('amount') or 0) for i in items)),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -425,9 +457,9 @@ async def model_usage_user_report(ns={}):
|
|||||||
'userid': userid,
|
'userid': userid,
|
||||||
'start_time': _format_datetime(start_dt),
|
'start_time': _format_datetime(start_dt),
|
||||||
'end_time': _format_datetime(end_dt),
|
'end_time': _format_datetime(end_dt),
|
||||||
'summary': _summarize(all_items),
|
'summary': _format_amount_summary(_summarize(all_items)),
|
||||||
'group_by': group_by,
|
'group_by': group_by,
|
||||||
'groups': grouped,
|
'groups': _format_amount_items(grouped),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -445,11 +477,11 @@ async def model_usage_user_report(ns={}):
|
|||||||
'userid': userid,
|
'userid': userid,
|
||||||
'start_time': _format_datetime(start_dt),
|
'start_time': _format_datetime(start_dt),
|
||||||
'end_time': _format_datetime(end_dt),
|
'end_time': _format_datetime(end_dt),
|
||||||
'summary': _summarize(all_items),
|
'summary': _format_amount_summary(_summarize(all_items)),
|
||||||
'total_count': total_count,
|
'total_count': total_count,
|
||||||
'current_page': current_page,
|
'current_page': current_page,
|
||||||
'page_size': page_size,
|
'page_size': page_size,
|
||||||
'items': items,
|
'items': _format_amount_items(items),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@ -515,7 +547,7 @@ async def model_usage_admin_report(ns={}):
|
|||||||
'orgid': orgid,
|
'orgid': orgid,
|
||||||
'start_time': _format_datetime(start_dt),
|
'start_time': _format_datetime(start_dt),
|
||||||
'end_time': _format_datetime(end_dt),
|
'end_time': _format_datetime(end_dt),
|
||||||
'summary': _summarize([]),
|
'summary': _format_amount_summary(_summarize([])),
|
||||||
'total_count': 0,
|
'total_count': 0,
|
||||||
'current_page': current_page,
|
'current_page': current_page,
|
||||||
'page_size': page_size,
|
'page_size': page_size,
|
||||||
@ -526,7 +558,7 @@ async def model_usage_admin_report(ns={}):
|
|||||||
'orgid': orgid,
|
'orgid': orgid,
|
||||||
'start_time': _format_datetime(start_dt),
|
'start_time': _format_datetime(start_dt),
|
||||||
'end_time': _format_datetime(end_dt),
|
'end_time': _format_datetime(end_dt),
|
||||||
'summary': _summarize([]),
|
'summary': _format_amount_summary(_summarize([])),
|
||||||
'group_by': group_by,
|
'group_by': group_by,
|
||||||
'groups': [],
|
'groups': [],
|
||||||
}
|
}
|
||||||
@ -560,9 +592,9 @@ async def model_usage_admin_report(ns={}):
|
|||||||
'orgid': orgid,
|
'orgid': orgid,
|
||||||
'start_time': _format_datetime(start_dt),
|
'start_time': _format_datetime(start_dt),
|
||||||
'end_time': _format_datetime(end_dt),
|
'end_time': _format_datetime(end_dt),
|
||||||
'summary': _summarize(all_items),
|
'summary': _format_amount_summary(_summarize(all_items)),
|
||||||
'group_by': group_by,
|
'group_by': group_by,
|
||||||
'groups': grouped,
|
'groups': _format_amount_items(grouped),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -577,11 +609,11 @@ async def model_usage_admin_report(ns={}):
|
|||||||
'orgid': orgid,
|
'orgid': orgid,
|
||||||
'start_time': _format_datetime(start_dt),
|
'start_time': _format_datetime(start_dt),
|
||||||
'end_time': _format_datetime(end_dt),
|
'end_time': _format_datetime(end_dt),
|
||||||
'summary': _summarize(all_items),
|
'summary': _format_amount_summary(_summarize(all_items)),
|
||||||
'total_count': total_count,
|
'total_count': total_count,
|
||||||
'current_page': current_page,
|
'current_page': current_page,
|
||||||
'page_size': page_size,
|
'page_size': page_size,
|
||||||
'items': page_items,
|
'items': _format_amount_items(page_items),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|||||||
58
b/subject.sql
Normal file
58
b/subject.sql
Normal 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;
|
||||||
@ -12,6 +12,8 @@
|
|||||||
size="small"
|
size="small"
|
||||||
prefix-icon="el-icon-search"
|
prefix-icon="el-icon-search"
|
||||||
placeholder="请输入模型名称"
|
placeholder="请输入模型名称"
|
||||||
|
@keyup.enter.native="$emit('search')"
|
||||||
|
@clear="$emit('search')"
|
||||||
></el-input>
|
></el-input>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="模型类型">
|
<el-form-item label="模型类型">
|
||||||
@ -21,6 +23,8 @@
|
|||||||
filterable
|
filterable
|
||||||
size="small"
|
size="small"
|
||||||
placeholder="请选择模型类型"
|
placeholder="请选择模型类型"
|
||||||
|
@change="$emit('search')"
|
||||||
|
@clear="$emit('search')"
|
||||||
>
|
>
|
||||||
<el-option
|
<el-option
|
||||||
v-for="item in modelTypeOptions"
|
v-for="item in modelTypeOptions"
|
||||||
@ -37,6 +41,8 @@
|
|||||||
filterable
|
filterable
|
||||||
size="small"
|
size="small"
|
||||||
placeholder="请选择供应商"
|
placeholder="请选择供应商"
|
||||||
|
@change="$emit('search')"
|
||||||
|
@clear="$emit('search')"
|
||||||
>
|
>
|
||||||
<el-option
|
<el-option
|
||||||
v-for="item in providerOptions"
|
v-for="item in providerOptions"
|
||||||
|
|||||||
@ -6,7 +6,7 @@
|
|||||||
<div class="decor decor--deep-purple"></div>
|
<div class="decor decor--deep-purple"></div>
|
||||||
|
|
||||||
<section class="case-hero">
|
<section class="case-hero">
|
||||||
<img class="case-hero__bg" :src="bannerImg" alt="">
|
<img class="case-hero__bg" :src="bannerImg" alt="" @load="refreshTabsOrigin">
|
||||||
<div class="case-container">
|
<div class="case-container">
|
||||||
<div class="case-hero__content">
|
<div class="case-hero__content">
|
||||||
<h1>投策智能体</h1>
|
<h1>投策智能体</h1>
|
||||||
@ -233,7 +233,8 @@ export default {
|
|||||||
mounted() {
|
mounted() {
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
const scroller = this.getScrollContainer()
|
const scroller = this.getScrollContainer()
|
||||||
this.tabsOriginTop = this.getElementTopInScroller(this.$refs.caseTabs, scroller)
|
this.resetScrollTop(scroller)
|
||||||
|
this.refreshTabsOrigin()
|
||||||
scroller.addEventListener('scroll', this.handlePageScroll, { passive: true })
|
scroller.addEventListener('scroll', this.handlePageScroll, { passive: true })
|
||||||
this.handlePageScroll()
|
this.handlePageScroll()
|
||||||
})
|
})
|
||||||
@ -245,6 +246,18 @@ export default {
|
|||||||
getScrollContainer() {
|
getScrollContainer() {
|
||||||
return document.getElementById('homeOut') || window
|
return document.getElementById('homeOut') || window
|
||||||
},
|
},
|
||||||
|
resetScrollTop(scroller) {
|
||||||
|
if (scroller === window) {
|
||||||
|
window.scrollTo(0, 0)
|
||||||
|
} else {
|
||||||
|
scroller.scrollTop = 0
|
||||||
|
}
|
||||||
|
this.isTabsFixed = false
|
||||||
|
},
|
||||||
|
refreshTabsOrigin() {
|
||||||
|
const scroller = this.getScrollContainer()
|
||||||
|
this.tabsOriginTop = this.getElementTopInScroller(this.$refs.caseTabs, scroller)
|
||||||
|
},
|
||||||
getScrollOffset() {
|
getScrollOffset() {
|
||||||
const tabs = this.$el.querySelector('.case-tabs')
|
const tabs = this.$el.querySelector('.case-tabs')
|
||||||
const tabsHeight = tabs ? tabs.offsetHeight : 0
|
const tabsHeight = tabs ? tabs.offsetHeight : 0
|
||||||
@ -311,8 +324,34 @@ export default {
|
|||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.decision-case-page h1,
|
||||||
|
.decision-case-page h2,
|
||||||
|
.decision-case-page h3,
|
||||||
|
.decision-case-page h4,
|
||||||
|
.decision-case-page p {
|
||||||
|
padding: 0;
|
||||||
|
margin-top: 0;
|
||||||
|
font-family: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
.decision-case-page button {
|
||||||
|
font-family: inherit;
|
||||||
|
line-height: 1.5;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.decision-case-page svg {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.decision-case-page img {
|
||||||
|
display: block;
|
||||||
|
max-width: none;
|
||||||
|
}
|
||||||
|
|
||||||
.case-container {
|
.case-container {
|
||||||
width: min(100%, 1280px);
|
width: 100%;
|
||||||
|
max-width: 1280px;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
padding: 0 24px;
|
padding: 0 24px;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -232,7 +232,24 @@
|
|||||||
computed: {
|
computed: {
|
||||||
filteredModelList() {
|
filteredModelList() {
|
||||||
const selectedStatus = this.activeStatus === 'pending' ? 0 : 1
|
const selectedStatus = this.activeStatus === 'pending' ? 0 : 1
|
||||||
return this.modelList.filter(model => Number(model.listing_status) === selectedStatus)
|
const modelName = this.normalizeSearchText(this.searchForm.name)
|
||||||
|
const modelType = this.normalizeSearchText(this.searchForm.type)
|
||||||
|
const provider = this.normalizeSearchText(this.searchForm.provider)
|
||||||
|
|
||||||
|
return this.modelList.filter(model => {
|
||||||
|
if (Number(model.listing_status) !== selectedStatus) return false
|
||||||
|
|
||||||
|
const displayName = this.normalizeSearchText(this.getModelDisplayName(model))
|
||||||
|
const id = this.normalizeSearchText(this.getModelId(model))
|
||||||
|
const type = this.normalizeSearchText(this.getModelType(model))
|
||||||
|
const modelProvider = this.normalizeSearchText(this.getProvider(model))
|
||||||
|
|
||||||
|
const nameMatched = !modelName || displayName.includes(modelName) || id.includes(modelName)
|
||||||
|
const typeMatched = !modelType || type === modelType || type.includes(modelType)
|
||||||
|
const providerMatched = !provider || modelProvider === provider || modelProvider.includes(provider)
|
||||||
|
|
||||||
|
return nameMatched && typeMatched && providerMatched
|
||||||
|
})
|
||||||
},
|
},
|
||||||
pagedModelList() {
|
pagedModelList() {
|
||||||
const start = (this.currentPage - 1) * this.pageSize
|
const start = (this.currentPage - 1) * this.pageSize
|
||||||
@ -282,6 +299,9 @@
|
|||||||
if (this.searchForm.provider) params.provider = this.searchForm.provider
|
if (this.searchForm.provider) params.provider = this.searchForm.provider
|
||||||
return params
|
return params
|
||||||
},
|
},
|
||||||
|
normalizeSearchText(value) {
|
||||||
|
return String(value || '').trim().toLowerCase()
|
||||||
|
},
|
||||||
extractModelData(res) {
|
extractModelData(res) {
|
||||||
const data = res?.data ?? res
|
const data = res?.data ?? res
|
||||||
if (data?.model_list) return data
|
if (data?.model_list) return data
|
||||||
|
|||||||
@ -24,7 +24,10 @@
|
|||||||
<div class="stat-title">{{ item.label }}</div>
|
<div class="stat-title">{{ item.label }}</div>
|
||||||
<i :class="item.icon"></i>
|
<i :class="item.icon"></i>
|
||||||
</div>
|
</div>
|
||||||
<div class="stat-value">{{ item.value }}</div>
|
<div class="stat-value">
|
||||||
|
{{ item.value }}
|
||||||
|
<span v-if="item.unit" class="stat-unit">{{ item.unit }}</span>
|
||||||
|
</div>
|
||||||
<div class="stat-desc">{{ item.desc }}</div>
|
<div class="stat-desc">{{ item.desc }}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -91,18 +94,35 @@
|
|||||||
<el-tag size="mini" type="info">{{ scope.row.model || '-' }}</el-tag>
|
<el-tag size="mini" type="info">{{ scope.row.model || '-' }}</el-tag>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column prop="request_count" label="请求次数" width="100"></el-table-column>
|
<el-table-column prop="request_count" label="请求次数" width="110">
|
||||||
<el-table-column prop="prompt_tokens" label="输入Token" min-width="110">
|
<template slot-scope="scope">
|
||||||
<template slot-scope="scope">{{ formatNumber(scope.row.prompt_tokens) }}</template>
|
<span class="usage-number">{{ formatNumber(scope.row.request_count) }}</span>
|
||||||
|
<span class="usage-unit">次</span>
|
||||||
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column prop="completion_tokens" label="输出Token" min-width="110">
|
<el-table-column prop="prompt_tokens" label="输入Token" min-width="130">
|
||||||
<template slot-scope="scope">{{ formatNumber(scope.row.completion_tokens) }}</template>
|
<template slot-scope="scope">
|
||||||
|
<span class="usage-number">{{ formatNumber(scope.row.prompt_tokens) }}</span>
|
||||||
|
<span class="usage-unit">Token</span>
|
||||||
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column prop="total_tokens" label="总Token" min-width="110">
|
<el-table-column prop="completion_tokens" label="输出Token" min-width="130">
|
||||||
<template slot-scope="scope">{{ formatNumber(scope.row.total_tokens) }}</template>
|
<template slot-scope="scope">
|
||||||
|
<span class="usage-number">{{ formatNumber(scope.row.completion_tokens) }}</span>
|
||||||
|
<span class="usage-unit">Token</span>
|
||||||
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column prop="amount" label="费用(元)" min-width="110">
|
<el-table-column prop="total_tokens" label="总Token" min-width="130">
|
||||||
<template slot-scope="scope">¥{{ formatAmount(scope.row.amount) }}</template>
|
<template slot-scope="scope">
|
||||||
|
<span class="usage-number">{{ formatNumber(scope.row.total_tokens) }}</span>
|
||||||
|
<span class="usage-unit">Token</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="amount" label="费用" min-width="110">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<span class="usage-amount">¥{{ formatAmount(scope.row.amount) }}</span>
|
||||||
|
<span class="usage-unit">元</span>
|
||||||
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
|
|
||||||
<el-table-column prop="last_usage_time" label="最近使用时间" min-width="160" show-overflow-tooltip></el-table-column>
|
<el-table-column prop="last_usage_time" label="最近使用时间" min-width="160" show-overflow-tooltip></el-table-column>
|
||||||
@ -158,9 +178,9 @@ export default {
|
|||||||
computed: {
|
computed: {
|
||||||
statCards() {
|
statCards() {
|
||||||
return [
|
return [
|
||||||
{ label: '请求次数', value: this.formatNumber(this.summary.request_count), desc: '当前筛选范围', type: 'purple', icon: 'el-icon-s-promotion' },
|
{ label: '请求次数', value: this.formatNumber(this.summary.request_count), unit: '次', desc: '当前筛选范围', type: 'purple', icon: 'el-icon-s-promotion' },
|
||||||
{ label: 'Token消耗', value: this.formatNumber(this.summary.total_tokens), desc: `输入 ${this.formatNumber(this.summary.prompt_tokens)} / 输出 ${this.formatNumber(this.summary.completion_tokens)}`, type: 'green', icon: 'el-icon-coin' },
|
{ label: 'Token消耗', value: this.formatNumber(this.summary.total_tokens), unit: 'Token', desc: `输入 ${this.formatNumber(this.summary.prompt_tokens)} Token / 输出 ${this.formatNumber(this.summary.completion_tokens)} Token`, type: 'green', icon: 'el-icon-coin' },
|
||||||
{ label: 'Token总费用', value: `¥${this.formatAmount(this.summary.amount)}`, desc: '按调用记录汇总', type: 'orange', icon: 'el-icon-wallet' }
|
{ label: 'Token总费用', value: `¥${this.formatAmount(this.summary.amount)}`, unit: '元', desc: '按调用记录汇总', type: 'orange', icon: 'el-icon-wallet' }
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
filterTimeText() {
|
filterTimeText() {
|
||||||
@ -245,7 +265,7 @@ export default {
|
|||||||
return Number(value || 0).toLocaleString()
|
return Number(value || 0).toLocaleString()
|
||||||
},
|
},
|
||||||
formatAmount(value) {
|
formatAmount(value) {
|
||||||
return Number(value || 0).toFixed(6).replace(/0+$/, '').replace(/\.$/, '.00')
|
return Number(value || 0).toFixed(2)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -400,6 +420,13 @@ export default {
|
|||||||
line-height: 1;
|
line-height: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.stat-unit {
|
||||||
|
margin-left: 6px;
|
||||||
|
color: #667085;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
.stat-desc {
|
.stat-desc {
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
color: #98a2b3;
|
color: #98a2b3;
|
||||||
@ -505,6 +532,19 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.usage-number,
|
||||||
|
.usage-amount {
|
||||||
|
color: #344054;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.usage-unit {
|
||||||
|
margin-left: 4px;
|
||||||
|
color: #98a2b3;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
|
||||||
@media (max-width: 900px) {
|
@media (max-width: 900px) {
|
||||||
.stat-grid {
|
.stat-grid {
|
||||||
grid-template-columns: 1fr;
|
grid-template-columns: 1fr;
|
||||||
|
|||||||
@ -191,12 +191,12 @@ export default {
|
|||||||
try {
|
try {
|
||||||
const res = await reqApikeyList(params)
|
const res = await reqApikeyList(params)
|
||||||
if (res && res.status === false) {
|
if (res && res.status === false) {
|
||||||
throw new Error(res.msg || '获取 API Key 列表失败')
|
// throw new Error(res.msg || '获取 API Key 列表失败')
|
||||||
}
|
}
|
||||||
this.tokenList = this.normalizeApiKeyList(res)
|
this.tokenList = this.normalizeApiKeyList(res)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.tokenList = []
|
this.tokenList = []
|
||||||
this.$message.error(error && error.message ? error.message : '获取 API Key 列表失败')
|
// this.$message.error(error && error.message ? error.message : '获取 API Key 列表失败')
|
||||||
} finally {
|
} finally {
|
||||||
this.tableLoading = false
|
this.tableLoading = false
|
||||||
}
|
}
|
||||||
|
|||||||
@ -24,7 +24,10 @@
|
|||||||
<span>{{ item.label }}</span>
|
<span>{{ item.label }}</span>
|
||||||
<i :class="item.icon"></i>
|
<i :class="item.icon"></i>
|
||||||
</div>
|
</div>
|
||||||
<strong>{{ item.value }}</strong>
|
<strong>
|
||||||
|
{{ item.value }}
|
||||||
|
<span v-if="item.unit" class="stat-unit">{{ item.unit }}</span>
|
||||||
|
</strong>
|
||||||
<em>{{ item.desc }}</em>
|
<em>{{ item.desc }}</em>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -35,7 +38,10 @@
|
|||||||
<h3>Token 使用概览</h3>
|
<h3>Token 使用概览</h3>
|
||||||
<span>{{ rangeLabel }}</span>
|
<span>{{ rangeLabel }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="token-total">{{ formatNumber(summary.total_tokens) }}</div>
|
<div class="token-total">
|
||||||
|
{{ formatNumber(summary.total_tokens) }}
|
||||||
|
<span>Token</span>
|
||||||
|
</div>
|
||||||
<p>总 Token 消耗</p>
|
<p>总 Token 消耗</p>
|
||||||
<div ref="tokenRatioChart" class="chart-box token-ratio-chart"></div>
|
<div ref="tokenRatioChart" class="chart-box token-ratio-chart"></div>
|
||||||
</div>
|
</div>
|
||||||
@ -85,11 +91,17 @@
|
|||||||
<el-table v-loading="loading" :data="usageList" class="usage-table" style="width: 100%">
|
<el-table v-loading="loading" :data="usageList" class="usage-table" style="width: 100%">
|
||||||
<el-table-column prop="model" label="模型名称" min-width="180" show-overflow-tooltip></el-table-column>
|
<el-table-column prop="model" label="模型名称" min-width="180" show-overflow-tooltip></el-table-column>
|
||||||
<!-- <el-table-column prop="request_count" label="调用次数" width="120"></el-table-column> -->
|
<!-- <el-table-column prop="request_count" label="调用次数" width="120"></el-table-column> -->
|
||||||
<el-table-column prop="prompt_tokens" label="输入Token" min-width="140"></el-table-column>
|
<el-table-column prop="prompt_tokens" label="输入Token(Token)" min-width="150">
|
||||||
<el-table-column prop="completion_tokens" label="输出Token" min-width="140"></el-table-column>
|
<template slot-scope="scope">{{ formatNumber(scope.row.prompt_tokens) }} Token</template>
|
||||||
<el-table-column prop="total_tokens" label="总Token" min-width="140"></el-table-column>
|
</el-table-column>
|
||||||
<el-table-column prop="amount" label="费用" width="130">
|
<el-table-column prop="completion_tokens" label="输出Token(Token)" min-width="150">
|
||||||
<template slot-scope="scope">¥ {{ formatAmount(scope.row.amount) }}</template>
|
<template slot-scope="scope">{{ formatNumber(scope.row.completion_tokens) }} Token</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="total_tokens" label="总Token(Token)" min-width="150">
|
||||||
|
<template slot-scope="scope">{{ formatNumber(scope.row.total_tokens) }} Token</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="amount" label="费用(元)" width="140">
|
||||||
|
<template slot-scope="scope">¥ {{ formatAmount(scope.row.amount) }} 元</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column prop="usage_time" label="使用时间" min-width="170" show-overflow-tooltip></el-table-column>
|
<el-table-column prop="usage_time" label="使用时间" min-width="170" show-overflow-tooltip></el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
@ -129,10 +141,10 @@ export default {
|
|||||||
amount: 0
|
amount: 0
|
||||||
},
|
},
|
||||||
statCards: [
|
statCards: [
|
||||||
{ label: '总消耗 Token', value: '0', desc: '当前筛选范围', type: 'primary', icon: 'el-icon-coin' },
|
{ label: '总消耗 Token', value: '0', unit: 'Token', desc: '当前筛选范围', type: 'primary', icon: 'el-icon-coin' },
|
||||||
{ label: '调用次数', value: '0', desc: '当前筛选范围', type: 'success', icon: 'el-icon-s-promotion' },
|
{ label: '调用次数', value: '0', unit: '次', desc: '当前筛选范围', type: 'success', icon: 'el-icon-s-promotion' },
|
||||||
{ label: ' 费用', value: '¥ 0.00', desc: '按当前单价估算', type: 'warning', icon: 'el-icon-wallet' },
|
{ label: '预估费用', value: '¥ 0.00', unit: '元', desc: '按当前单价估算', type: 'warning', icon: 'el-icon-wallet' },
|
||||||
{ label: '输入/输出 Token', value: '0 / 0', desc: 'Prompt / Completion', type: 'purple', icon: 'el-icon-pie-chart' }
|
{ label: '输入/输出 Token', value: '0 / 0', unit: 'Token', desc: 'Prompt / Completion', type: 'purple', icon: 'el-icon-pie-chart' }
|
||||||
],
|
],
|
||||||
usageList: [],
|
usageList: [],
|
||||||
total: 0,
|
total: 0,
|
||||||
@ -259,17 +271,17 @@ export default {
|
|||||||
},
|
},
|
||||||
updateStatCards() {
|
updateStatCards() {
|
||||||
this.statCards = [
|
this.statCards = [
|
||||||
{ label: '总消耗 Token', value: this.formatNumber(this.summary.total_tokens), desc: '当前筛选范围', type: 'primary', icon: 'el-icon-coin' },
|
{ label: '总消耗 Token', value: this.formatNumber(this.summary.total_tokens), unit: 'Token', desc: '当前筛选范围', type: 'primary', icon: 'el-icon-coin' },
|
||||||
{ label: '调用次数', value: this.formatNumber(this.summary.request_count), desc: '当前筛选范围', type: 'success', icon: 'el-icon-s-promotion' },
|
{ label: '调用次数', value: this.formatNumber(this.summary.request_count), unit: '次', desc: '当前筛选范围', type: 'success', icon: 'el-icon-s-promotion' },
|
||||||
{ label: '预估费用', value: `¥ ${this.formatAmount(this.summary.amount)}`, desc: '按当前单价估算', type: 'warning', icon: 'el-icon-wallet' },
|
{ label: '预估费用', value: `¥ ${this.formatAmount(this.summary.amount)}`, unit: '元', desc: '按当前单价估算', type: 'warning', icon: 'el-icon-wallet' },
|
||||||
{ label: '输入/输出 Token', value: `${this.formatNumber(this.summary.prompt_tokens)} / ${this.formatNumber(this.summary.completion_tokens)}`, desc: 'Prompt / Completion', type: 'purple', icon: 'el-icon-pie-chart' }
|
{ label: '输入/输出 Token', value: `${this.formatNumber(this.summary.prompt_tokens)} / ${this.formatNumber(this.summary.completion_tokens)}`, unit: 'Token', desc: 'Prompt / Completion', type: 'purple', icon: 'el-icon-pie-chart' }
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
formatNumber(value) {
|
formatNumber(value) {
|
||||||
return Number(value || 0).toLocaleString()
|
return Number(value || 0).toLocaleString()
|
||||||
},
|
},
|
||||||
formatAmount(value) {
|
formatAmount(value) {
|
||||||
return Number(value || 0).toFixed(6).replace(/0+$/, '').replace(/\.$/, '.00')
|
return Number(value || 0).toFixed(2)
|
||||||
},
|
},
|
||||||
initCharts() {
|
initCharts() {
|
||||||
if (this.$refs.tokenRatioChart && !this.tokenRatioChart) {
|
if (this.$refs.tokenRatioChart && !this.tokenRatioChart) {
|
||||||
@ -361,15 +373,23 @@ export default {
|
|||||||
return [
|
return [
|
||||||
item.name,
|
item.name,
|
||||||
`${this.formatNumber(item.value)} Token`,
|
`${this.formatNumber(item.value)} Token`,
|
||||||
`调用次数:${this.formatNumber(rankItem.request_count)}`,
|
`调用次数:${this.formatNumber(rankItem.request_count)} 次`,
|
||||||
`预估费用:¥ ${this.formatAmount(rankItem.amount)}`
|
`预估费用:¥ ${this.formatAmount(rankItem.amount)} 元`
|
||||||
].join('<br/>')
|
].join('<br/>')
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
xAxis: {
|
xAxis: {
|
||||||
type: 'value',
|
type: 'value',
|
||||||
|
name: 'Token',
|
||||||
|
nameTextStyle: {
|
||||||
|
color: '#909399',
|
||||||
|
padding: [0, 0, 0, 8]
|
||||||
|
},
|
||||||
axisLine: { show: false },
|
axisLine: { show: false },
|
||||||
axisTick: { show: false },
|
axisTick: { show: false },
|
||||||
|
axisLabel: {
|
||||||
|
formatter: value => this.formatNumber(value)
|
||||||
|
},
|
||||||
splitLine: {
|
splitLine: {
|
||||||
lineStyle: {
|
lineStyle: {
|
||||||
color: '#edf1f7'
|
color: '#edf1f7'
|
||||||
@ -406,7 +426,7 @@ export default {
|
|||||||
position: 'right',
|
position: 'right',
|
||||||
color: '#344054',
|
color: '#344054',
|
||||||
fontSize: 11,
|
fontSize: 11,
|
||||||
formatter: params => this.formatNumber(params.value)
|
formatter: params => `${this.formatNumber(params.value)} Token`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@ -573,6 +593,14 @@ export default {
|
|||||||
font-size: 26px;
|
font-size: 26px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.stat-unit {
|
||||||
|
display: inline;
|
||||||
|
margin-left: 6px;
|
||||||
|
color: #667085;
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
&:before {
|
&:before {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
@ -659,6 +687,13 @@ export default {
|
|||||||
font-size: 34px;
|
font-size: 34px;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
line-height: 1.2;
|
line-height: 1.2;
|
||||||
|
|
||||||
|
span {
|
||||||
|
margin-left: 8px;
|
||||||
|
color: #667085;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.token-ratio-card {
|
.token-ratio-card {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user