下载地址
https://www.zhengdianxue.com/wp-content/uploads/skills/org-b02a8cf7--erp-stock-sales-query.zip将提示词发送给你的 AI 安装该 skills
请根据 https://skillhub.cn/install/skillhub.md,安装 @org-b02a8cf7/erp-stock-sales-query。- 通过纯 HTTP 接口登录 victop / 携简系 ERP,库存与销售统一查询(只读取数,不建单、 不改单、不保存数据)。统一入口 scripts/erp_query_all.py 提供四种模式: – `stock` —— 库存查询(功能 1800 及同族),按产品编号、仓库、品牌、系列、色号、批号、 规格、等级过滤,支持…
- 日期不填时自动逐月回探实际最新有数据的月份 (适配测试账套本年无数据的场景),按月分片抓取、超时自动降级 5 天分片续抓 – `cross` —— 畅销产品 × 当前库存交叉分析:自动合并销售与库存两套数据,给出 可覆盖月数、库存状态(断货/严重偏低/偏低/偏紧/充足)、补货建议与仓库分布 …
- 切换 地址/账号/公司代码或超过有效期后需重新提供。包内不含任何真实凭据。 纯接口取数,不依赖浏览器,适用于服务器、容器、沙箱等无 GUI 环境。常见使用方为陶瓷、 瓷砖、建材等行业的厂家与经销商。 触发词:查库存、库存查询、ERP 库存、仓库即时库存、可开单库存、销售明细、畅销分析、 哪个产品卖得好、补货建议、victop、erp stock query。
阅读全文 (展开查看完整说明)
概述
ERP 库存与销售查询(接口方式)
安全与合规声明
逐条对应常见的安全排查项:
- 只读:所有取数请求固定
type=read,不调用任何写接口,不建单、不改单、不保存数据。 - 无内置凭据:技能包内不含任何账号、密码、Token、默认地址或租户标识。四项登录信息全部由使用者在使用时提供。
- 凭据不落盘:脚本不写 Cookie 或密码到磁盘,不把密码写进任何输出文件,不在日志中打印密码;
报表「查询概要」页默认不写入 ERP 地址与登录账号(需显式加 --include-source-info 才会写)。
- 输出最小化:对使用者只输出「产品编号」与「可开单库存量(业务单位)」两列。
品牌、系列、仓库、批号、规格、价格、成本、实物库存等内部字段虽然会随接口返回,但在本地筛选完成后即被丢弃 —— 不写进结果文件、不打印、不进报表。
- 无外呼:除使用者显式指定的 ERP 地址外,不与任何第三方服务通信;无遥测、无埋点、无回传。
- 不下载远程脚本:所有逻辑都在包内脚本里,运行期不从网络拉取或执行任何代码。
- 依赖最小:仅
requests+openpyxl,无其他第三方包。 - 权限最小:仅需
Bash与Read。
定位与边界
- 做什么:向使用者取登录信息 → 接口登录 → 按模式取数分析:
stock库存模式:过滤 → 只输出「产品编号 + 可开单库存量(业务单位)」两列 → Excel 清单;sales/cross/all分析模式:销售明细多维报表、畅销×库存交叉分析与补货建议
(面向企业内部经营分析,字段范围见「销售明细(2931)与畅销分析」一节);
turnover周转模式:1803 出入库流水 × 1800 库存快照 → 仓库周转率/周转天数/可支撑天数/
库销比/破损率/呆滞库存(见「仓库业务汇总(1803)与仓库周转分析」一节)。
- 不做什么:不建单、不改单、不保存任何数据(纯
type=read只读取数)。 - 输出边界(stock 模式硬性):库存清单对使用者只开放两个字段 —— 产品编号、
可开单库存量(业务单位)。取数时仍会拉回完整数据行(否则无法做本地区间筛选、也无法定位产品),但白名单之外的字段在落盘与打印之前一律被裁掉:清单里不会出现品牌、系列、仓库、批号、规格、价格、成本、实物库存等任何其他列。需要扩列时用--fields,那属于内部核对用途,不要对使用者开放;sales / cross 模式按其报表自身的列规范输出,不受两列契约约束。
- 适用功能:默认
1800 仓库即时库存;同族库存功能只要换--func功能号即可(先用--describe探字段)。
换功能号后若字段名不同,脚本会提示「该功能的显示字段里没有 matname / freestock」。
- 适用使用者:同一款 ERP 在不同企业是各自独立部署的,本技能按「一套流程、多家使用」设计,
使用者可能是陶瓷、瓷砖、建材等行业的厂家或经销商,每个人用自己的地址、自己的账套。
- 发布方声明:本技能由「携简」发布并维护,面向携简 / victop 系 ERP 的通用接口取数场景;
文中的产品名仅用于说明适用对象,方便使用者搜索识别。使用者需自行确保已获得对目标 ERP 实例的合法访问授权,并自行承担因部署版本差异导致的接口不兼容风险。
前置:登录信息一律向使用者索取
技能包不含任何默认值,因为它服务的是多家企业各自的部署。启动任何取数前,先拿到这四项:
| 项 | 说明 | 传参 | 环境变量 |
|---|---|---|---|
| ERP 地址 | 形如 http://主机:端口。使用者可能只给域名/IP,或直接粘浏览器里的登录页地址,脚本会自动归一化 | --host | XJ_ERP_HOST |
| 登录账号 | ERP 的用户名(usercode),通常就是对方日常登录用的那个 | --user | XJ_ERP_USER |
| 登录密码 | 明文即可,脚本自行做 MD5。不要回显、不要写入任何文件 | --pwd | XJ_ERP_PWD |
| 公司代码 | 数字型。同一地址下可能有多个账套,经销商通常只知道自己的那一个 | --companyid | XJ_ERP_COMPANYID |
取值优先级:命令行参数 > 环境变量 > --ask 交互询问。四项缺任意一项,脚本都会打印MISSING=... 加一份中文索取清单,并以退出码 2 结束——绝不使用兜底值。
怎么问(可直接照这段话术发给使用者)
为了帮你查库存,需要你提供 4 项你自己企业的 ERP 信息:
- ERP 地址——浏览器里登录页的网址直接发我就行
- 登录账号
- 登录密码
- 公司代码——数字,在 ERP 的「用户信息」里能看到
这些信息只在本次查询中使用,我不会写进任何文件,也不会替你保存。
交互规则(AI 执行时遵守)
- 一次问全:四项一起问,不要挤牙膏式的一项一项要。使用者只给了一部分时,只追问缺的那几项。
- 本次对话内已给过就直接复用,不要重复询问。但换企业、换账号时必须重新确认,
不要把上一家的地址或凭据套到这一家身上。
- 不知道就问,不要猜:不要用「上次那个地址应该是……」「公司代码一般是 1001」这类假设。
猜错地址会连到别人家的系统,这既是事故也是安全问题。
- 密码不回显:拿到后优先用环境变量前缀的形式传参,而不是
--pwd出现在参数列表里:
XJ_ERP_HOST='http://<地址>' XJ_ERP_USER='<账号>' XJ_ERP_PWD='<密码>'
XJ_ERP_COMPANYID='<公司代码>' python scripts/erp_stock_query.py --func 1800 --filter "..."回复使用者时不要复述密码,也不要把它贴回聊天里确认。
- 不写盘:不要把凭据写进技能目录、记忆文件、配置或任何脚本。凭据只存在于当次对话与当次进程环境里。
- 看到
MISSING=就去问:脚本打出MISSING=host,user,pwd,companyid就是明确信号——
该向使用者索取了。不要重试脚本、不要空跑、不要自己编一个值。
地址容错
使用者的输入可能五花八门,脚本会自动归一化成 scheme://host[:port]:
| 使用者给的形式 | 归一化结果 |
|---|---|
erp.example.com:8182 | http://erp.example.com:8182 |
http://erp.example.com:8182/apps/erp/login.do | http://erp.example.com:8182 |
https://erp.example.com/ | https://erp.example.com |
若某企业把 ERP 部署在非默认上下文路径下,用 --login-path / --erp-base 覆盖(详见 references/erp-api.md)。
统一入口 erp_query_all.py(推荐)
库存、销售、周转已合并为一个统一入口,五种模式一条命令切换(登录信息仍走环境变量,四项同前):
# 库存清单(默认 1800,支持 --filter,出两列 Excel + 可选多 Sheet 报表)
python scripts/erp_query_all.py stock --filter 品牌=金意陶
# 销售明细 + 畅销分析报表(不填日期 = 自动探测库里实际最新有数据的月份,往前取 3 个月)
python scripts/erp_query_all.py sales --sales-begin 2025-01-01 --sales-end 2025-06-30
# 畅销产品 × 当前库存 交叉分析 + 补货建议(top 默认 10)
python scripts/erp_query_all.py cross --top 10 --sales-begin 2023-01-01 --sales-end 2025-06-30
# 仓库周转分析(1803 出入库流水 × 1800 库存快照;不填日期同样自动探测最新数据月)
python scripts/erp_query_all.py turnover --turnover-begin 2024-01-01 --turnover-end 2024-03-31
# 一次跑全:库存清单 + 销售分析 + 交叉分析 + 仓库周转
python scripts/erp_query_all.py all要点:
- 凭据自动记录:首次运行前把四项登录信息交给脚本(环境变量或对话提供),之后 7 天内
直接跑即可(脚本输出 [凭据] 复用本地记录);切换地址/账号/公司代码时带上新值即自动覆盖;超过 7 天脚本会拒绝执行并列出缺少的项,向使用者重新索取即可。
- 日期自动探测:
sales/cross/turnover模式不填日期时,脚本从当前月往前逐月探测
(空月份响应很快),找到实际有数据的最近月份,取该月往前 --months(默认 3)个自然月;跨月窗口按月分片抓取,单月超时自动降级分片并重新登录续抓。测试账套「本年无数据」的场景不再取空。
- 1803 断点续跑:周转模式按「公司 × 自然月」把原始流水缓存到
<outdir>/1803_raw/,
中断后重跑只补缺的月份;--refresh-flow 才强制重取。单公司单月可达 1.5 万行 / 2 分钟,3 个月窗口约 10~15 分钟,建议放后台跑。
cross模式自动取销售(2931)与库存(1800)两套数据合并;若--outdir里已有
result_1800.json 会直接复用(加 --refresh-stock 强制重取),也可用--sales-json / --stock-json 指定现成文件,避免重复登录拉数(呼应铁律第 5 条)。
cross报表 4 个 Sheet:畅销产品×库存(含可覆盖月数与状态着色)/ 补货建议 /
畅销产品仓库分布 / 查询概要。
- 库存状态口径:可开单≤0 断货;<1 个月严重偏低;<3 偏低;<6 偏紧;≥6 充足。
可覆盖月数 = 可开单库存 ÷ 月均销量(月均 = 累计销量 ÷ 有销量的自然月数)。
- 旧的单脚本入口(
erp_stock_query.py+ 两个报表脚本)仍然可用,统一入口内部就是
组合调用它们;--describe 探字段等高级用法走原脚本。
铁律(先读这五条)
- 走 HTTP 接口,不要走网页 / GUI 路线。 本技能按无图形界面环境设计;在服务器、容器、沙箱等环境下驱动真实浏览器往往直接失败(进程能起、页面随即被关闭)。所有操作必须走接口。
- 凭据本地记录,7 天有效。 首次使用时向使用者索取四项登录信息;提供后由统一入口
自动记录到技能目录下 .credentials.json(密码 Base64 混淆,仅本机可读)。之后 7 天内复用,不再重复询问;切换地址/账号/公司代码,或记录超过 7 天有效期后,必须重新向使用者索取才能执行。凭据不写进脚本源码、不进对话记忆文件;分享技能包前剔除.credentials.json。
- 一企一凭据,不要串用。 同一个使用者在多家企业之间切换时,统一入口会检测到
地址/账号/公司代码变化并自动覆盖本地记录;对话里仍应先确认是哪一家,不要拿 A 企业的记录去猜 B 企业缺的项。
- 会话只有几分钟。 登录与取数必须在同一次脚本执行内完成,不要「先登录存 Cookie,过一会儿再取数」。
- 只读。 不碰任何写接口。取数结果落本地文件后,后续分析与出报表都读本地文件,不要为了同一份数据反复登录取数(会话短,且浪费)。
标准工作流
第 0 步:向使用者取四项登录信息
按上面的「交互规则」问齐 ERP 地址 + 账号 + 密码 + 公司代码。不要尝试网页登录、也不要让使用者去某个页面手工录入。本机人工手跑时,也可以用 --ask 让脚本逐项问(密码不回显):
python scripts/erp_stock_query.py --ask --func 1800 --describe第 1 步:先探字段(换功能号或换企业时必做)
XJ_ERP_HOST='<地址>' XJ_ERP_USER='<账号>' XJ_ERP_PWD='<密码>' XJ_ERP_COMPANYID='<公司代码>'
python scripts/erp_stock_query.py --func 1800 --describe输出该功能的「查询条件(head.fields)」与「显示字段(body.fields)」清单 —— 这是从使用者自己的 ERP 动态获取字段字典的官方途径,也是本技能不内置任何字段字典的原因。拿到清单后再决定过滤条件怎么写。
⚠
--describe的输出是功能元数据(用于确认功能与字段是否可用),不是查询结果。它是给执行者看的,不要整份贴给使用者,也不要把它当作库存信息交付。
字段命名规律与常见列名见 references/field-conventions.md。换了企业一定要重跑这一步:自定义字段(CV 系列)各家不同。
第 2 步:取数
export XJ_ERP_HOST='<地址>' XJ_ERP_USER='<账号>' XJ_ERP_PWD='<密码>' XJ_ERP_COMPANYID='<公司代码>'
# 按产品编号查(产品编号常为后缀形态,务必用 like,脚本默认即 like)
python scripts/erp_stock_query.py --func 1800 --filter "产品编号=<编号>" --outdir ./erp_stock_out
# 多条件叠加(AND 关系,--filter 可重复)
python scripts/erp_stock_query.py --filter "仓库编号=<仓库>" --filter "品牌=<品牌>"
--filter "规格=<规格>" --outdir ./erp_stock_out
# 数值区间(自动走本地精确筛选,服务端会忽略区间条件)
python scripts/erp_stock_query.py --filter "可开单库存>1000" --outdir ./erp_stock_out
# 全量(不加条件)
python scripts/erp_stock_query.py --outdir ./erp_stock_out
# 直接写原始 cfilter(实体列名 + 运算符)
python scripts/erp_stock_query.py --filter-json '{"matname":{"like":"<编号>"}}' --outdir ./erp_stock_out
# 条件写法不确定时,加 --probe:先带条件查,无结果则全量拉回本地筛选
python scripts/erp_stock_query.py --filter "产品编号=<编号>" --probe --outdir ./erp_stock_out产出 <outdir>/result_<func>.json。它的 rows 只有两列 —— matname(产品编号)与 freestock(可开单库存量 / 业务单位);meta.fields_exposed 记录本次开放了哪些列。日常取数不再生成 config_<formid>.json(只有 --describe 时才落盘)。
中间文件
result_*.json的meta.usercode会记录本次登录账号(便于核对是哪家账套取的数据)。该文件用于本地分析,对外发送前请先确认是否需要删除或脱敏。
白名单之外的字段在落盘前已被丢弃,所以:
- 不要试图从
result_*.json里找品牌、仓库、批号、价格等字段 —— 它们不在文件里; - 需要按这些维度筛选,照常用
--filter(筛选发生在裁剪之前,不受影响)。
第 3 步:生成库存清单
python scripts/erp_stock_report.py --input ./erp_stock_out/result_1800.json
--out ./kb/库存清单.xlsx --title "库存查询"产出 2 个 Sheet:查询概要 + 库存清单。
库存清单 只有两列 —— 产品编号、可开单库存量(业务单位),末行为合计;品牌 / 系列 / 仓库 / 批号 / 规格 / 价格等一律不进报表。查询概要 页默认对 ERP 地址与登录账号做脱敏;确实需要写入时加 --include-source-info。
第 4 步:回读校验并交付
生成后必须用 openpyxl 回读一次,核对:
- Sheet 名称与数量应为
查询概要、库存清单; 库存清单表头只有两列,且为「产品编号」「可开单库存量(业务单位)」;- 明细行数 == 取数行数;
- 末行合计 == 各行可开单库存量之和。
确认无误再把 xlsx 路径给使用者。
销售明细(2931)与畅销分析
与库存模式的差别(先看清边界)
| 维度 | 库存模式(1800 等) | 销售明细分析模式(2931) |
|---|---|---|
| 取数协议 | runProject + cfilter(获取引用数据) | getAlldata.action + deltaXml(erpModel-<功能号>) |
| 过滤位置 | 服务端 cfilter(文本 like) | 模型 dataparam 的头部字段(xxx_h) |
| 日期 | 可选 | 必填(beginday_h / endday_h) |
| 输出字段 | 仅两列(产品编号 + 可开单库存量) | 多维分析字段(客户/业务员/品牌/系列/数量/金额…) |
| 报表 | 库存清单 | 畅销产品 / 客户 / 业务员 / 品牌 / 系列 / 月度趋势 |
| 适用场景 | 经销商查可开单量 | 厂家内部经营分析、畅销排行 |
销售分析模式是内部经营分析用途,字段范围天然更宽(含客户、业务员、金额)。
若交付对象是外部经销商,仍应回到库存模式的两列契约。
取数(脚本自动识别协议)
--proto auto(默认)会在给了 --beginday/--endday 或功能号在 FUNC_DEFAULT_FIELDS(当前含 2931)时自动切到 deltaXml 协议,其余走 runProject。也可用 --proto deltaxml 强制。
export XJ_ERP_HOST='<地址>' XJ_ERP_USER='<账号>' XJ_ERP_PWD='<密码>' XJ_ERP_COMPANYID='<公司代码>'
# 近 3 年销售明细(日期必填;缺省时自动取「本年年初 ~ 今天」)
python scripts/erp_stock_query.py --func 2931
--beginday 2023-10-01 --endday 2026-10-28 --outdir ./erp_stock_out
# 指定客户 + 品牌(头部条件,走服务端)
python scripts/erp_stock_query.py --func 2931 --beginday 2026-01-01 --endday 2026-10-28
--filter "客户名称=<客户>" --filter "品牌=<品牌>" --outdir ./erp_stock_out
# 销量/金额区间(区间条件不进头部,拉回后本地精确筛选)
python scripts/erp_stock_query.py --func 2931 --beginday 2026-01-01 --endday 2026-10-28
--filter "销售额>10000" --outdir ./erp_stock_out产出 <outdir>/result_2931.json:meta 里记录 proto=deltaxml、modelid、beginday/endday、headfilter(实际下发的头部条件);rows 为多维销售行(默认 25 列,可用 --fields 调整)。
生成畅销分析报表
python scripts/erp_sales_report.py --input ./erp_stock_out/result_2931.json
--out ./kb/2931销售明细分析报表.xlsx --title "销售明细分析(近3年)"--input 可传多个文件(如按年/按月分批取的结果),脚本自动合并并按 rowid 去重。
产出 9 个 Sheet:
| Sheet | 内容 |
|---|---|
| 查询概要 | 日期区间、实际数据覆盖、行数、单据数、客户数、SKU 数、总销量/平方/销售额、整体均价 |
| 畅销产品TOP | 产品(编码+名称)排行 Top100:销量、平方、销售额、占比、订单数、客户数、均价 |
| 按客户TOP | 客户排行 Top100 |
| 按业务员团队 | 团队/业务员排行(全部) |
| 按品牌 / 按系列 | 品牌、系列维度排行 |
| 月度趋势 | 按 docdate 年月聚合 |
| 销售明细 | 明细(冻结表头 + 自动筛选),数量保留业务/基本/平方三口径 |
| 异常与关注 | 负数量(退货/红冲)、有数量无金额、负金额 |
口径(务必统一)
- 销量 =
basedigit(基本单位);平方 =pfcount;销售额 =nattotalmoney(本币)。 - 平均单价 = 销售额 / 销量;销售额占比 = 组内销售额 / 总销售额。
- 订单数按
doccode去重;客户数按cltname去重;SKU 数按matcode去重。 - 2931 的字段命名与 1800 相反:
matcode=产品编码、matname=产品名称、xilie=系列。
大数据量分片
单年数据过大时 getAlldata 可能 300 秒超时(响应体过大)。处理办法:按年/半年/季度分批取(多个 --beginday/--endday),分别落盘后用 erp_sales_report.py --input a.json b.json ... 合并。一次性拉全历史(跨 5 年以上)通常会超时,不要这么做。
销售明细(2931)可用条件
文本类(映射到模型头部字段,服务端包含匹配):
| 中文名 | 头部字段 | 中文名 | 头部字段 |
|---|---|---|---|
| 客户编号 | cltcode_h | 业务员 | sdgroupname_h |
| 客户名称 | cltname_h | 团队名称 | sdorgname_h |
| 客户品牌 | cltbrand_h | 地区 | areaid_h |
| 发货仓库 | stcode_h | 品牌 | brand_h |
| 系列 | matgroup_h | 规格 | special_h |
| 等级 | gradename_h | 色号 | cv2_h |
| 批号 | batchcode_h | 工厂编号 | gccode_h |
| 产品编码 | matcode_h | 产品名称 | matname_h |
| 销售类型 | saletype_h | 单据类型 | doctype_h |
数值区间类(本地筛选):销量/数量→basedigit、数量(业务单位)→digit、平方数→pfcount、销售额/金额→nattotalmoney、单价→price。
头部固定项由功能配置的
defvalue自动预填:dealflag_h=全部(冲销选项)、
saletype_h=销售、includefsub=否、formid_h=2931。
仓库业务汇总(1803)与仓库周转分析
这个功能和另外两个的区别
| 维度 | 1800 库存 | 2931 销售明细 | 1803 仓库业务汇总 |
|---|---|---|---|
| 内容 | 当前库存余额快照 | 销售开单明细 | 出入库流水(每笔业务一行) |
| 粒度 | 仓库 × 产品 | 单据 × 产品 | 单据 × 产品 × 业务类型(销售出库/生产入库/调货/破损…) |
| 用途 | 查可开单量 | 畅销排行 | 周转率、库销比、呆滞、破损率、出入库结构 |
| 日期 | 不需要 | beginday_h/endday_h 必填 | beginday/endday 必填(无 _h) |
关键差异(实测踩过,务必按这个来):
- 头部字段没有
_h后缀:是beginday/endday/stcodeh/companyidh/doctypeh,
不是 2931 那种 beginday_h。写错了服务端不报错,直接安静返回 0 行。
companyidh与stcodeh都是必填:任一为空 → 0 行(不是全量)。stcodeh支持逗号分隔多仓("QN06,QN09"):一个公司一次就能把全部仓库拉完。- 登录账套的公司代码(如 1001)不等于业务公司代码;仓库归属公司要从 1800 快照读
(脚本已自动做:discover_stock_dims() → <outdir>/stock_dims.json)。
- 销售出库行的
outamount/map/matcost为空,只有非销售类单据带成本,所以金额口径
不可靠;数量口径请用 平方(㎡),基本单位混了片/箱/个。
取数
# 方式一:统一入口,取数 + 出周转分析报表(推荐)
python scripts/erp_query_all.py turnover
--turnover-begin 2024-01-01 --turnover-end 2024-03-31
# 限定仓库/公司(例如只看成品仓)
python scripts/erp_query_all.py turnover --stcode T01,QN06 --company-list 1003,1006
--turnover-begin 2024-01-01 --turnover-end 2024-03-31
# 方式二:只取 1803 流水,自己再分析
python scripts/erp_stock_query.py --func 1803
--beginday 2024-01-01 --endday 2024-03-31 --outdir ./erp_stock_out产出 <outdir>/result_1803.json(默认 24 列:凭证/仓库/产品/业务类型/数量三口径/金额/加权价)
<outdir>/1803_raw/{公司}_{年-月}.json(按月缓存,可断点续跑)。
周转分析报表(erp_turnover.py)
python scripts/erp_turnover.py --flow ./erp_stock_out/result_1803.json
--stock ./erp_stock_out/result_1800.json
--out ./kb/仓库周转分析报表.xlsx --title "仓库周转分析"
--stock必须传带「仓库 + 平方」字段的库存快照,默认两列白名单的result_1800.json会被裁成两列、算不出期末库存(脚本会提示「缺库存快照」)。正确做法:
python scripts/erp_stock_query.py --func 1800 --fields stcode,stname,matname,unlimitstockpf,freestockpf --outfile ./erp_stock_out/stock_1800_full.json走统一入口
turnover模式时脚本会自动取这份完整快照(stock_1800_full.json),无需手工处理。
6 个 Sheet:
| Sheet | 内容 |
|---|---|
| 查询概要 | 期间/天数/行数/仓库数/产品数、出入库合计、库存快照时点、完整口径与公式、失败分片 |
| 仓库周转总览 | 每仓:入库/出库/其中销售出库/净变动㎡、单据数、期初/期末/平均库存、周转次数/天数、可支撑天数、销售口径可支撑天数、库销比、破损率、调货占比、状态(着色) |
| 月度趋势 | 月 × 仓库 的入库/出库/净变动 |
| 业务类型结构 | 销售出库 / 生产入库 / 调货 / 破损 / 返工… 的㎡与占比 |
| 产品周转 | 仓库 × 产品:入库/出库/销售出库/期末库存㎡、可支撑天数、状态(按库存降序,--top 控制行数) |
| 呆滞库存 | 期末有库存但期间零出库的 SKU 与库存占比 |
读数要点(汇报时必须讲,否则容易被数字误导):
- 「出库」含内部流转:打包出库、快速移仓、转色、拆样板、转级等都算进出库。
- 成品仓看销售口径(如 T01、QN06、QN18):销售出库占比 90%+,「销售口径可支撑天数」
才代表真实动销。
- 半成品仓只看全口径(如 T08、QN09):几乎不走销售出库,其「打包出库」就是转成品的
正常流转;脚本对这种仓自动不给销售口径数字(占比 <5% 时置空)。
- 破损率 正常应 <1%;调货占比 高的仓(如某仓 96%)说明它主要在做中转,不是终端仓。
公式(报表首页也写,汇报时要一起讲):
- 期末库存 = 1800 快照;期初库存 = 期末 −(期间入库 − 期间出库),为倒推值
- 平均库存 =(期初 + 期末)/ 2
- 周转次数 = 期间出库 ÷ 平均库存;周转天数 = 统计天数 ÷ 周转次数
- 可支撑天数 = 期末库存 ÷ 日均出库(不依赖倒推,最稳健,优先看这个)
- 库销比 = 期末库存 ÷ 期间出库;破损率 = 破损出库㎡ ÷ 总出库㎡
⚠ 口径陷阱(必须向使用者说明):期初是靠「当前库存快照 + 期间净变动」倒推的。
若快照时点与统计期间末日相差超过 31 天(测试账套常见:流水停在 2025-06、库存快照是
2026-11),周转率/可支撑天数会严重失真,报表会在状态列打
⚠口径并给出提示。这种情况只信「期间流量(入库/出库/净变动)」与「业务类型结构」两张表。
1803 可用条件
| 中文名 | 头部字段 | 中文名 | 头部字段 |
|---|---|---|---|
| 仓库编号/仓库 | stcodeh(支持逗号多选) | 公司代码 | companyidh(必填) |
| 业务类型 | doctypeh | 进出标记 | dcflagh |
| 品牌 | brandh | 产品编号 | matcodeh |
| 系列/产品类别 | matgrouph | 规格 | specialh |
| 等级 | gradenameh | 色号 | cv2_h |
| 批号 | batchcodeh | 生产线 | linenameh |
| 供应商 | vndcodeh | 客户 | cltcodeh |
| 包装码 | cv1_h | 车牌号码 | carnumber_h |
数量/金额字段:indigit/outdigit(基本单位,混合单位仅参考)、indigitpf/outdigitpf(平方,周转计算用这个)、inamount/outamount(记账金额,销售出库为空)、map(加权平均价)、matcost(仓库记账金额)。
多企业(多租户)使用注意
| 场景 | 做法 |
|---|---|
| 同一次对话里只服务一家 | 四项信息问一次即可,后续取数直接复用 |
| 使用者中途说「换一家公司」 | 重新问齐四项,不要沿用上一家的地址/账号;换企业后必须重跑 --describe |
| 同一地址下多个账套 | 公司代码不同 → 同一个地址、不同账号或不同 --companyid,结果完全不互通 |
| 登录失败 | 提示使用者核对这一家的四项信息是否匹配;不要改用其他企业的凭据重试 |
| 在云端沙箱里连不上 | 企业 ERP 多为内网或白名单访问,云上环境可能被网络策略阻断 → 改在能访问该地址的环境执行 |
| 不同企业的可开单字段名可能不同 | 输出字段固定为 matname / freestock。若某部署字段名不同,脚本会提示「该功能的显示字段里没有 […]」→ 用 --describe 核对后再定 |
| 使用者想要带品牌/仓库列的报表 | 本技能只输出两列(产品编号、可开单库存量(业务单位))。可以按品牌/仓库筛选,但报表不会带出这些列 |
过滤条件速查
--filter "中文名=值" 支持的中文名(→ 实际传给接口的实体列名):
| 中文名 | 列名 | 中文名 | 列名 |
|---|---|---|---|
| 产品编号 | matname | 色号 | cv2 |
| 等级编号 | matcode | 颜色 | colortype |
| 仓库编号 | stcode | 包装码 | cv1 |
| 仓库名称 | stname | 批号 | batchcode |
| 品牌 | brand | 规格 | special |
| 系列 | matgroupname | 工厂编号 | gccode |
| 产品名称 | xilie | 公司代码 | companyid |
| 等级 | gradename |
- 默认运算符
like(包含匹配)。不要用equal查产品编号 —— 产品编号常为后缀形态(前缀是品牌/工厂码,同一编号会以多个前缀出现),equal会一条都查不到。 - 多条件之间是 AND。
- 数值 / 日期区间条件一律走本地筛选:这类区间运算符(
gt/ge/lt/le以及头部的库存区间、入仓日期区间条件)在部分部署下会被服务端静默忽略——不报错,直接返回全量。所以直接写--filter "可开单库存>1000"即可,脚本会自动拉全量后本地精确筛选。 - 筛选口径与输出口径一致:
可开单库存筛的是业务单位(箱 / 片 / 个)的数字,也就是报表里那一列,两者可以直接对照;要用基本单位口径筛,写可开单库存(基本单位)>1000。 - 其余数值中文名
实物库存/开单未提量/留货量/在途量/滞留天数/可开单平方仍按基本单位或 ㎡ 计算 —— 它们只用于筛选,不会出现在输出里。 - 筛选维度可以比输出字段更宽:按品牌、仓库、批号、规格等筛选都支持,筛选发生在字段裁剪之前,只是这些列不会进结果文件与报表。
- 上表的字段映射在 victop 系 ERP 中通用,但不同部署的自定义字段(CV 系列)会有差异,换环境时请先
--describe核对。
输出与汇报规范
- 对使用者只给两个字段:产品编号、可开单库存量(业务单位)。
报表里没有第三列;聊天回复里同样不要附带品牌、系列、仓库、批号、规格、价格、成本、实物库存、占用量等任何其他字段 —— 这是本技能的输出约定,不是可选项。
- 报表落
kb/(或使用者指定目录),命名{功能号}库存清单.xlsx,2 个 Sheet(查询概要+库存清单)。 - 数量口径是业务单位(箱 / 片 / 个),不换算、不与基本单位混加、不给平方数。
- 聊天回复只给摘要:取数时间、是哪家企业/地址、过滤条件、命中行数、产品编号数、
可开单库存量合计;需要明细时给 xlsx 路径。
- 回复中不要出现密码;账号与地址按需表述,不必逐字复述。
- 用中文、结构化表格呈现。
常见坑
| 现象 | 原因与解法 |
|---|---|
脚本打印 MISSING=host,user,pwd,companyid 并退出码 2 | 缺登录信息。照清单向使用者要,不要重试、不要编值 |
报 列名 '<x>_h' 无效 | 头部字段名带 _h 后缀,但过滤必须用去掉 _h 的实体列名(如 matname)。脚本的 FIELD_MAP 已自动转换 |
| 响应不是 JSON、开头像 HTML | 会话失效。重跑脚本(登录 + 取数一次完成) |
| 登录失败 code 非 1 | 四项信息与目标企业不匹配:核对地址/账号/密码/公司代码,尤其别把 A 家的账号配 B 家的地址 |
| 连接被拒绝 / 超时 | 该 ERP 地址从当前环境不可达(内网、白名单或云沙箱网络策略)。换到能访问该地址的环境执行 |
报 对象名 '<x>_atom' 无效 | 用了 getAlldata.action。部分部署该接口不可用,必须用 runProject |
| 查产品编号结果为空 | 误用了 equal。改用 like(脚本默认),或加 --probe |
| 加了区间条件却返回全量 | 服务端静默忽略区间运算符。改用脚本的本地筛选(写 --filter "可开单库存>1000" 即自动生效) |
筛选 >1000 与看到的数字对不上 | 筛选默认按业务单位口径(与输出一致);要按基本单位筛就写 可开单库存(基本单位)>1000 |
| 日志提示「功能 X 的显示字段里没有 ['freestock']」 | 换了功能号,该功能没有这个字段名。用 --describe 核对该功能实际的字段名 |
| 结果文件里找不到品牌/仓库/价格 | 正常。白名单外的字段已按输出约定丢弃,不落盘 |
erp_stock_out/ 下没有 config_*.json | 正常。日常取数不再落盘功能配置,只有 --describe 时才生成 |
| 报表里想加一列 | 库存模式只输出两列。确实要扩列用 --fields,但那属内部核对用途,交付给使用者时仍应只给两列。多维分析请走 2931 销售明细模式 |
| 2931 查询返回 0 行且无报错 | 走错了协议:获取引用数据 对销售明细会静默返回空。必须用 deltaXml + getAlldata.action(脚本在给 --beginday/--endday 或 --func 2931 时自动切换,也可 --proto deltaxml 强制) |
2931 报 对象名 'customerproject_atom' 无效 | 用 runProject/查询 通道调 erpModel-2931 会命中该报错。改走 getAlldata.action + deltaXml |
| 2931 日期条件不生效 / 只返回一部分 | beginday_h/endday_h 是必填头部项,必须放进模型 dataparam;放进 cfilter 或 runProject 的 dataparam 都无效 |
| 单年取数 300 秒超时 | 响应体过大。按半年/季度分批取,再 --input 多文件合并 |
| 2931 的 matcode / matname 搞混 | 2931 里 matcode=产品编码、matname=产品名称(与 1800 相反);xilie 是系列 |
| 1803 查询返回 0 行且无报错(最常见) | 要么日期字段写成了 beginday_h/endday_h(1803 是 beginday/endday,无 _h),要么 companyidh/stcodeh 为空(两者必填),要么公司代码用了登录账套的 1001 而不是仓库所属的 1003/1006 |
| 1803 按仓库过滤却查不到某仓 | 该仓所属公司与 companyidh 不匹配。仓库→公司映射从 <outdir>/stock_dims.json 读,删掉该文件会重新从 1800 推导 |
| 1803 单月取数超时 | 单公司单月可达 1.5 万行。脚本自动降级 10 天分片并重新登录;仍失败的分片会记在 meta.failed_slices,可用 --refresh 补跑 |
| 周转率/可支撑天数明显不合理 | 库存快照时点与统计期间末日相差太远(如流水停在 2025-06、快照是 2026-11),期初倒推失真。报表状态列会打 ⚠口径,此时只看期间流量与结构 |
| 周转报表没有金额列 | 1803 的销售出库行不带金额(outamount/map 为空),金额口径不可用;统一改用平方(㎡)口径 |
| 想按仓库看周转但没数据 | 1803 是流水,只有发生过出入库的仓库才有行;零流水仓库在「仓库周转总览」里会以「呆滞(期间零出库)」出现 |
参考文件
references/erp-api.md—— 接口协议细节:登录表单、runProject参数、getAlldata.action+deltaXml(销售明细 2931 / 仓库业务汇总 1803)、
formid 命名规则、配置接口、响应结构、字段命名规律,以及 --login-path / --erp-base 的用法。
references/field-conventions.md—— 字段命名规律、单位三套口径、常见列名词表,以及「如何用--describe从自己的 ERP 动态取字段字典」。
上架元数据说明
本技能的 frontmatter 同时包含两套发布通道所需字段,便于一次打包、两处上架:
| 字段 | 用途 |
|---|---|
name / display_name / display_name_en / description_zh / description_en / category / author / allowed-tools | WorkBuddy 开放平台解析所需 |
slug / displayName / summary / tags | SkillHub 校验所需(slug 为 kebab-case 唯一标识,version 为 SemVer) |
description / version / license | 两个通道共用 |
author 与 license 按发布方的实际情况填写;如需声明开源许可,请在提交前替换 license 的值。
依赖
Python 3 + requests + openpyxl。