从历史销售订单里捞「客户 × 物料」价格底表:金蝶云星空 WebAPI 实操
一、场景与问题贸易企业做价格体系梳理第一步永远是同一件事把历史成交价捞出来做一张「客户 × 物料」的价格底表。没有这张表客户分级和等级价就没法定——全靠拍脑袋业务员一定会为每个客户的档位争论不休。手工做这件事不现实。一个中等规模的贸易公司近半年的销售订单动辄上千张每张十几行明细逐张导 Excel 再合并两个人干一周也未必干净。而且手工导的版本之间口径经常不一致这次导的是含税价下次导的是未税价。这件事的本质是一次批量取数 聚合正好适合用 WebAPI 做。本文给出完整可跑的实现用 ExecuteBillQuery 拉取已审核销售订单明细再用 pandas 聚合成价格底表。说明以下接口与字段基于金蝶云星空标准产品不同版本可能存在差异实际使用时请以贵司环境的接口文档与字段清单为准。二、环境与前置需要准备星空服务器地址形如 http:///k3cloud以及 WebAPI 已启用数据中心 IDacctID注意是数据中心 ID不是名称一个具备销售订单查询权限的账号Python 3.8依赖 requests 和 pandaspip install requests pandas一个前置提醒这个账号只用来取数不要用系统管理员账号跑长期脚本也不要给超过必要的权限。取数脚本只需要读权限。三、实现步骤步骤 1登录拿会话星空 WebAPI 的鉴权是会话式的先调 ValidateUser服务端返回 Cookie后续请求带上这个 Cookie。import requestsfrom urllib.parse import urljoinclass K3Client:definit(self, host, acct_id, username, password, lcid2052):self.base host.rstrip(‘/’) ‘/’self.acct_id acct_idself.username usernameself.password passwordself.lcid lcidself.session requests.Session()def login(self): url urljoin(self.base, Kingdee.BOS.WebApi.ServicesStub.AuthService.ValidateUser.common.kdsvc) payload { acctID: self.acct_id, username: self.username, password: self.password, lcid: self.lcid, } r self.session.post(url, jsonpayload, timeout30) r.raise_for_status() data r.json() # 登录成功返回 LoginResultType 1 if data.get(LoginResultType) ! 1: raise RuntimeError(登录失败: %s % data) return data步骤 2跑 ExecuteBillQuery 拉明细ExecuteBillQuery 是星空最通用的取数接口本质是按字段清单 过滤条件查单据。def query_bill(self, form_id, field_keys, filter_string‘’,order_string‘’, start_row0, limit2000):url urljoin(self.base,‘Kingdee.BOS.WebApi.ServicesStub.DynamicFormService.ExecuteBillQuery.common.kdsvc’)payload {“data”: {“FormId”: form_id,“FieldKeys”: field_keys,“FilterString”: filter_string,“OrderString”: order_string,“TopRowCount”: 0,“StartRow”: start_row,“Limit”: limit,}}r self.session.post(url, jsonpayload, timeout120)r.raise_for_status()return r.json()步骤 3分页拉取近半年已审核订单明细两个关键点只取已审核单据、必须分页。def fetch_sale_order_lines(client, date_from):form_id ‘SAL_SaleOrder’fields (‘FBillNo,FDate,FCustId.FNumber,FCustId.FName,’‘FDocumentStatus,FBillTypeID.FName,FCurrencyID.FNumber,’‘FSaleOrderEntry_FMaterialId.FNumber,’‘FSaleOrderEntry_FMaterialId.FName,’‘FSaleOrderEntry_FQty,FSaleOrderEntry_FPrice,’‘FSaleOrderEntry_FTaxPrice,FSaleOrderEntry_FTaxRate,’‘FSaleOrderEntry_FUnitID.FName’)filter_str (“FDocumentStatus‘C’ and FDate‘%s’” % date_from)rows, start [], 0 limit 2000 while True: data client.query_bill( form_id, fields, filter_str, order_stringFDate asc, start_rowstart, limitlimit) if not data: break rows.extend(data) if len(data) limit: break start limit return rows步骤 4聚合成「客户 × 物料」价格底表拿到明细后用 pandas 按客户和物料聚合。这一步比取数更需要注意口径同一组合的价格分布往往很宽只取平均值会掩盖问题所以最低价、最高价、均价三个都要留。import pandas as pddef build_price_base(rows):cols [‘FBillNo’, ‘FDate’, ‘FCustId.FNumber’, ‘FCustId.FName’,‘FDocumentStatus’, ‘FBillTypeID.FName’, ‘FCurrencyID.FNumber’,‘FMaterialNumber’, ‘FMaterialName’,‘FQty’, ‘FPrice’, ‘FTaxPrice’, ‘FTaxRate’, ‘FUnitName’]df pd.DataFrame(rows, columnscols)df[FDate] pd.to_datetime(df[FDate]) for c in [FQty, FPrice, FTaxPrice, FTaxRate]: df[c] pd.to_numeric(df[c], errorscoerce) # 剔除数量和价格异常的行避免脏数据污染底表 df df[(df[FQty] 0) (df[FTaxPrice] 0)] grp df.groupby([FCustId.FNumber, FCustId.FName, FMaterialNumber, FMaterialName, FCurrencyID.FNumber]) base grp.agg( 最低含税价(FTaxPrice, min), 最高含税价(FTaxPrice, max), 平均含税价(FTaxPrice, mean), 成交次数(FBillNo, nunique), 累计数量(FQty, sum), 最近成交日(FDate, max), ).reset_index() # 价差率最高价与最低价相差多少用来定位同客同货不同价 base[价差率] ((base[最高含税价] - base[最低含税价]) / base[最低含税价]).round(4) return base.sort_values([价差率, 累计数量], ascending[False, False])步骤 5导出并筛选重点ifname ‘main’:c K3Client(host‘http://k3.example.com/k3cloud’,acct_id‘数据中心ID’, username‘取数账号’, password‘******’)c.login()rows fetch_sale_order_lines(c, date_from‘2026-03-01’)base build_price_base(rows)base.to_excel(‘价格底表.xlsx’, indexFalse)# 只留成交量靠前的部分用于定等级价长尾不必处理 top base[base[累计数量] 50] print(组合数 %d重点跟踪 %d % (len(base), len(top)))为什么按累计数量截断 长尾组合半年只成交一两次整理成本高、参考价值低把它们混进来会稀释对主要矛盾的判断。先处理覆盖大部分业务的那批。四、字段对照表字段含义备注FBillNo单据编号用于统计成交次数FDate单据日期过滤格式必须是 yyyy-MM-ddFCustId.FNumber / .FName客户编码 / 名称用编码做分组键名称只作展示FDocumentStatus单据状态C 已审核必须过滤FBillTypeID.FName单据类型退货单会污染价格需剔除FCurrencyID.FNumber币别跨币种价格不可直接比较FSaleOrderEntry_FMaterialId.FNumber物料编码明细明细字段写法实体标识 下划线FSaleOrderEntry_FQty数量用于剔除红字与异常行FSaleOrderEntry_FPrice不含税单价口径与下面一项不同别混用FSaleOrderEntry_FTaxPrice含税单价业务口径通常看这个FSaleOrderEntry_FTaxRate税率%用于含税/未税换算核对FSaleOrderEntry_FUnitID.FName单位同一物料多单位会造成价格失真五、常见报错对照表现象原因处理登录返回 LoginResultType 不为 1账号密码错、或 acctID 写成了数据中心名称用数据中心 ID不是名称后续请求报会话失效 / 401没有复用登录返回的 Cookie每次新建了连接用同一个 requests.Session 串起登录与查询报字段不存在FieldKeys 用了数据库列名或明细字段没加实体前缀用实体属性名明细写成 FSaleOrderEntry_XXX报返回行数超限单次 Limit 过大降到 2000 分页取循环 StartRow日期过滤报错或查不到数据日期字符串格式不符统一 yyyy-MM-dd且需带单引号报您没有权限取数账号缺少该表单查询权限单独建只读角色只给需要的表单拉到几万行后内存吃紧一次性全量加载分批取、分批聚合或直接落库再聚合六、几个容易踩的坑含税与未税混用。 FPrice 是不含税单价FTaxPrice 是含税单价。价格底表用于业务定价时一般看含税用于毛利分析时要拆开。两个口径混在一张表里结论必然是错的。忘了过滤红字与退货。 退货单的价格往往是原单价格的镜像数量为负。不过滤会把底表的最低价拉到不真实的低位。跨币种直接比价。 外币订单的单价是外币金额直接和本位币放一起取 min/max 没有意义。要么按币别分组要么先按汇率折算——但折算参数必须固定否则每次跑出来的底表都不一样。未审核单据混入。 草稿和未审核单据里的价格经常是业务员随手填的。FDocumentStatus‘C’ 这个条件不能省。只看均价。 均价会掩盖同客同货价差很大这个最有价值的信号。所以脚本里特意算了 价差率并按它倒序排——价差率高的组合正是最需要拿出来讨论的那批。七、小结这套脚本解决的是价格体系梳理里最耗时的一步把散落在上千张单据里的历史成交价变成一张可以直接用于决策的价格底表。落地时建议分两步先跑一次全量看数据分布是否合理确认口径无误后再把它做成定期任务每月自动刷新。数据拿到之后业务侧的动作才是重点——按实际贡献给客户分级、定等级价、再上最低限价和让价审批。顺序不能反底表没有就定等级价会变成新的拍脑袋。

相关新闻

VMware中Ubuntu 22.04虚拟机磁盘扩容完整指南:从分区到LVM一步到位

VMware中Ubuntu 22.04虚拟机磁盘扩容完整指南:从分区到LVM一步到位

不知道你有没有遇到过这种情况:VMware里装了个Ubuntu 22.04,当时觉得自己挺有经验,硬盘随便给了20G,结果过了一两个月,编译一个大项目、拉几个Docker镜像、再装点ROS依赖,系统盘就飘红了。清理缓存、删日志…

2026/9/30 14:58:07 阅读更多 →
基于SpringBoot+Vue3的果蔬生鲜电商系统:前后端分离与JWT鉴权实战解析

基于SpringBoot+Vue3的果蔬生鲜电商系统:前后端分离与JWT鉴权实战解析

先把我做这个项目的真实感受放在最前面:没有任何一个技术项目能像果蔬生鲜电商这样,把SpringBoot和Vue3的实战价值体现得如此充分。前后端分离、JWT鉴权、商品与订单流转、后台管理……这些看上去很“教科书”的名词,落在一个卖菜平台上&…

2026/9/30 14:58:07 阅读更多 →
GEO专家孟庆涛:GEO 时代的信源布局方法论从内容优化到语境匹配

GEO专家孟庆涛:GEO 时代的信源布局方法论从内容优化到语境匹配

当 AI 的推荐随问法、语言、城市与平台漂移,品牌要优化的就不再是内容本身,而是内容与语境的匹配概率。 2026 年 9 月 7 日,Semrush 与 Exploding Topics 联合发布了一项覆盖 2338 名美国成年人的调查:73.6% 的每周 AI 使用者曾依…

2026/9/30 14:58:07 阅读更多 →

最新新闻

Python3数据类型转换避坑指南:字符串拼接、Decimal精度与pandas批量转换实战

Python3数据类型转换避坑指南:字符串拼接、Decimal精度与pandas批量转换实战

先讲一个我实际踩过的坑。某次项目里从数据库读出一批订单金额,代码里直接用total fee计算合计数,结果数据全部变成了字符串拼接,比如"199" "1" "1991",不是 200。查了半天才发现,数…

2026/10/1 19:03:58 阅读更多 →
WorkBuddy接入自定义MCP连接器:SSE长连接实战与排查指南

WorkBuddy接入自定义MCP连接器:SSE长连接实战与排查指南

1. 为什么要在 WorkBuddy 里接一个自定义 MCP 连接器WorkBuddy 这类 AI 工作台用久了,你会发现一个很现实的问题:内置能力再全,也覆盖不了你手头那些"私有工具链"。比如团队内部的设计素材库、自研的图片生成服务、某个只在公司内网…

2026/10/1 19:03:58 阅读更多 →
[光学原理与应用-651]:低频电磁波走电路介质,超高频电磁波走光学介质,所谓光电差异,只是频率跨越了多个数量级、换了一套传输介质,底层物理体系完全统一。

[光学原理与应用-651]:低频电磁波走电路介质,超高频电磁波走光学介质,所谓光电差异,只是频率跨越了多个数量级、换了一套传输介质,底层物理体系完全统一。

详解:低频电磁波走电路介质,超高频电磁波走光学介质核心观点:电信号与光信号都属于电磁波。二者之间的光电差异,本质并不是两套完全不一样的物理,主要是频率跨越十几个数量级,传输与调控介质发生切换&#…

2026/10/1 19:03:58 阅读更多 →
杭州前端工程师如何度过职业发展的瓶颈期?

杭州前端工程师如何度过职业发展的瓶颈期?

对于大多数Web前端工程师来说, 职业瓶颈这个问题几乎是每个人都会碰到的。但是每个人具体的情况不一样, 所以这个瓶颈会出现在不同的工作时期和具体的时间点上。针对这种情况, 比较推荐的处理办法就是主动离开自己那个习惯的区域, 打破原有的思考方式, 并且从提高自身的业务水平…

2026/10/1 19:03:58 阅读更多 →
Agent记忆系统实战:从存储选型到混合检索与安全防护

Agent记忆系统实战:从存储选型到混合检索与安全防护

做Agent开发的人,几乎都会在某个阶段被同一个问题卡住:系统越做越像一个“对话接口”,而不是一个有记忆、能成长的个体。用户上一轮刚说过“我现在搬到上海了”,下一轮问“我上次说的地址你记得吗”,Agent只能沉默——…

2026/10/1 19:03:58 阅读更多 →
Agent上生产:系统接入才是拦路虎,MCP与适配层实战复盘

Agent上生产:系统接入才是拦路虎,MCP与适配层实战复盘

这个项目上线那天,我们在会议室里等第一个真实工单。演示环境里模型表现得像个十年老员工,能总结、能推断、能把完整执行计划列得清清楚楚。但生产环境里,它要做的第一件事,是把 OA 里一张审批单读进来,再对着 ERP 里的…

2026/10/1 19:02:58 阅读更多 →

日新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/1 0:00:30 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/1 0:00:30 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/1 1:01:17 阅读更多 →

周新闻

如何划分训练/验证集:Spirula Studio五种eval_mode策略详解

如何划分训练/验证集:Spirula Studio五种eval_mode策略详解

如何划分训练/验证集:Spirula Studio五种eval_mode策略详解 【免费下载链接】spirula-studio Cross-vendor 3D Gaussian Splatting trainer - video to splat to mesh, Vulkan or CUDA. 项目地址: https://gitcode.com/GitHub_Trending/sp/spirula-studio Sp…

2026/9/30 13:14:22 阅读更多 →
SEO怎么推广速查手册新手避坑实战指南

SEO怎么推广速查手册新手避坑实战指南

SEO怎么推广速查手册新手避坑实战指南 模板网站太丑不够用?别急着加滤镜,那是治标不治本。很多老板盯着后台流量掉得眼红,却还在纠结首页Banner的圆角是不是3像素。这就像穿着西装去挖土,姿势不对,努力白费。我整理这份 速查手册…

2026/9/30 18:13:06 阅读更多 →
FireRed-OpenStoryline少样本仿写深度解析:AI Agent如何复刻你的独特文案风格与节奏

FireRed-OpenStoryline少样本仿写深度解析:AI Agent如何复刻你的独特文案风格与节奏

FireRed-OpenStoryline少样本仿写深度解析:AI Agent如何复刻你的独特文案风格与节奏 【免费下载链接】FireRed-OpenStoryline FireRed-OpenStoryline is an AI video editing agent that transforms manual editing into intention-driven directing through natural language …

2026/9/30 13:14:49 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/1 0:00:30 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/1 0:00:30 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/1 1:01:17 阅读更多 →