兆智经营分析
本文件是总说明。租户 M6NQW060G8A0022。先定业务阶段和对象,再决定用哪份已登记口径。栏目公式在 references/,不在这里。金额在提问时现查,不写回文件。
先定对象
客户不会说报表名。先读 references/process-routing.md,按流程阶段确定对象。劳务和设备分开,集采和非集采分开,签章不齐不要用发票金额解释。
报表能覆盖这个阶段时,再用下表选方案,并只读那一份栏目文件。公式以栏目文件为准。报表覆盖不了的阶段,只引用流程里的对象和单据自己的金额,不编一个报表指标。
先选表
对象已经落在已登记报表上时,按粒度选:
| 客户在问 | 用这张 | 栏目 |
|---|---|---|
| 一个项目整体还赚不赚钱、预算超了没有、材料或服务花了多少、这个项目收付了多少 | 项目收支分析表 A6XMBC648KM007S |
references/project-income-expense.md |
| 某一份收入合同拆成主合同、签证、补充协议、结算调整,或这张合同开了多少票、确认了多少收入、回了多少款 | 收入合同分析 6G3M0G648GU004E |
references/ar-contract.md |
两张都沾得上时,看粒度:没点名哪份合同,用项目收支分析表;点了合同,或在拆主合同和补充协议,用收入合同分析。粒度说不清就先问一句,不要两张表的数加在一起。
问的是深化或投标时手填的目标利润,两张表都不要用。那是项目利润测算表,尚未登记。
选中之后只读那一份栏目文件。公式以文件为准,不另起算法。方案必须是表里的公共方案,同名私人方案和备份方案不套。
界面上的「详情 / 汇总」是同一方案的明细行和合计,不是两套公式。
怎么取数
取数走 qiqi 网关,有两条通道,都支持 —— 先看环境有哪个,就用哪个:
| qiqi CLI 通道 | qiqi MCP 通道 | |
|---|---|---|
| 用在 | 装了 qiqi 命令的机器 | 云端 / 没装 CLI 的环境 |
| 谁在调 | 你 / 脚本 | 模型在会话里直接调工具 |
| 身份 | 本地登录身份,按租户反查别名 | 连接器里的 API Key,一个 Key 锁一个身份 |
| 入口 | qiqi --profile <别名> query … |
qiqi_records_query 等工具 |
两条通道连的是同一个网关、同一套对象与条件语义,取到的数字不会有差别。
2026-10-09 实测:复算脚本 --selftest 两通道全过;同一个项目 79 个指标逐值 0 差异。
scripts/qiqilib.py 两条通道都实现了,默认自动探测(PATH 里有 qiqi 走 CLI,
没有但设了 QIQI_API_KEY 就走 MCP),也可以用 QIQI_TRANSPORT=cli|mcp 强制指定。
所以没装 CLI 的云端环境照样能跑复算脚本。
两条通道都只认厦门兆智 M6NQW060G8A0022,校验不过就拒绝出数:
CLI 侧靠「按租户反查登录身份」,MCP 侧靠调 qiqi_whoami 核对 tenantId。
工具对照
| 要做什么 | MCP 工具 | CLI 等价写法 |
|---|---|---|
| 找对象 | qiqi_object_types_discover |
qiqi objects |
| 看字段 | qiqi_object_fields_describe、qiqi_object_query_describe |
qiqi describe fields <Object> |
| 看枚举 / 引用候选值 | qiqi_field_values_discover |
qiqi query values <Object.field> |
| 取明细 | qiqi_records_query |
qiqi query list <Object> --input … |
| 取合计 / 分组 | qiqi_records_aggregate |
qiqi query aggregate <Object> --input … |
| 找报表 | 报表发现 → qiqi_report_describe |
qiqi describe reports <关键词> |
| 跑报表 | qiqi_report_query |
qiqi query report <id> |
| 看当前身份 | qiqi_whoami |
qiqi auth list |
CLI 通道的身份(别名不写死)
qiqi 按登录身份决定租户。本技能的口径是厦门兆智 M6NQW060G8A0022。
租户是固定的,但登录身份的别名不是 —— 别名(qiqi 里叫「切换引用」)是本机
qiqi auth login --name <别名> 时自己起的,换台机器、换个人登录就不一样。
所以任何地方都不要写死别名。手工在终端查数前,先确认身份指向目标租户:
qiqi auth list # 看「切换引用」列,找 tenantId = M6NQW060G8A0022 那一行
qiqi --profile <该行别名> query list Project --fields id,name,code --format json
qiqi --profile <该行别名> describe fields PurOrderItem # 看字段名,不要猜
qiqi --profile <该行别名> query values PurOrder.purchaseTypeId # 看枚举/引用候选值
脚本不需要你操心别名:qiqilib 会跑 qiqi auth list --format json 按租户反查出本机
可用的 switchRef 再用。找不到、找到多个分不清、或显式指定的别名指向别的租户,一律报错
拒绝出数 —— 绝不静默退回默认身份(默认很可能不是本租户)。运行日志第一行会打印实际用的身份。
要强制指定时传别名(--profile <别名> 或环境变量 QIQI_PROFILE)即可,但仍然会校验租户。
这两个只对 CLI 通道有效;MCP 通道的身份由 Key 决定,传了会被忽略(脚本会在 stderr 提示一次)。
MCP 通道的四个限制(都实测过)
- 身份换不了。 一个 Key 一个身份。要换公司得换连接器里的 Key,会话里切不了。
- 读不到明细实体。
isDetail=true的对象(如预算指标表BudgetPlanTotal)
用qiqi_records_query一律报QUERY_RESULT_INVALID,而 CLI 的query list读得到。
但聚合(qiqi_records_aggregate)两边都能读,数值逐项一致 —— 所以脚本里凡是只要
「分组后的合计」的地方,都改走Q.group_sum(),两条通道同一套代码。
脚本把这类失败单独抛成Q.ChannelGap,绝不会当成 0 行,也不要改查询反复重试。 - 单次查询 offset 上限 10000(CLI 无此限制)。超了直接报错,不静默截断。
全租户比对请用聚合,或按组织 / 年份切批。 - 大结果不落盘。 MCP 把整包数据塞回响应体,没有
77hub.large-output指针文件。
拉几千行明细会撑爆上下文 —— 要落盘、要批量、要给同事复现,走 CLI。
两条通道共有的四个特性
scripts/README.md 有完整对照:
- 日期只收自然日
2026-08-31。传'2026-08-31 23:59:59'会报date_value_invalid。 - 关系要落到叶子:
receipt.billType.id可以、receipt.billType不行;另有几处要用扁平外键
(ReceiptItem用projectId、ArContractSubjectMatterItem用arContractId、PurReqItem用reqId)。 - 租户自定义字段是规范名,引用型读值带
Object后缀:purOrder.csCaiGouLeiXingCcddrxt0ccr01Object,
过滤用它的.id。读值不一定支持、过滤支持。 - 跨字段 OR / NOT / isnull 要用条件树(
{"OR":[{…},{…}]}、{"field":null});
CLI 的--where只能 AND、条件树走--input,MCP 的where直接收这棵树。
eq简写只收字符串。coalesce(x,'')!='v'就是OR[isnull, ne]。
报表能不能直接执行,是「报表级」的,不是「通道级」的。 2026-10-09 实测:
| 报表 | CLI qiqi query report |
MCP qiqi_report_query |
|---|---|---|
项目收支分析表 A6XMBC648KM007S |
❌ REPORT_NOT_FOUND |
❌ REPORT_NOT_FOUND |
收入合同分析 6G3M0G648GU004E |
✅ --validate-only 返回 appliedCriteria |
✅ 返回完整 criteria,实跑出数 |
所以遇到任何一张表,先试一次报表查询再决定要不要复算,不要一律假定不可执行。
qiqi query report 不是只覆盖系统预置报表 —— 租户自定义报表只要登记了入口就能跑,
上面「收入合同分析」就是自定义的。挑方案 id 用 qiqi describe reports <关键词>
(MCP:报表发现),--validate-only / validateOnly:true 先校验再取数。
项目收支分析表这张确实没有可执行的报表入口,原因查清了:
REST 的 /ReportDefinition/json/{id} 被应用前的鉴权层拦住,任何路径都返回 200 空响应;
queryType 的 2700 多个字段里也没有报表执行字段。它在 ReportDefinition 里存在且未删除,
但分组 VDQM0860NT10034 下的方案报表入口全部不可达。不要再花时间试执行这张表。
顺带一个踩过的坑:51HCE9648KM00DF 这个 id 也不可执行,返回
REPORT_CONFIGURATION_AMBIGUOUS("存在多个可选方案")—— 它是方案组级别的 id。
收入合同分析的可执行 id 是 6G3M0G648GU004E。
所以项目收支分析表的金额要按方案登记的定义逐项复算,别另起算法。这套复算已经写成可执行脚本,
放在 scripts/,见下面「复算脚本」节;下面 1–6 步是它的取数原理,用来读懂和修改脚本,
不要每次重新手拼。
ReportDefinition拿方案 →reportSchemaId(模型)、dataSourceId(数据源)。ReportDataProvider(按dataSourceId)拿 20 个提供者的title。ReportEqlProviderDefinition(按providerId)拿每个提供者的主实体objectTypeId(isMaster=true)和joinCriteriaStr关联条件。ReportDataSourceFieldMapping(按fieldId)拿字段落到哪个提供者、哪个字段路径(providerFieldPath),以及字段级expr(大小写拆分、乘成本那些都写在这里)。ReportDefinitionMeasure(按reportDefinitionId)拿列公式,列之间可以引用(如成本预算 = 劳务 + 费用 + 材料)。ReportDataSourceField(按dataSourceId)把内部字段名对回中文列名;ReportDefinitionCriteria拿方案的查询条件(projectId/businessDate等)。
以上都是 qiqi 里的普通业务对象,直接 query list 就能读,defs_snapshot.py 就是这么抓指纹的。
唯一的例外是提供者自己的条件串 ReportDataProvider.criteriaStr,qiqi 不对外暴露。
复算时对每个提供者用主实体 + 关系过滤即可。例:合同金额用 ArContractSubjectMatterItem,条件
projectId=<项目> + arContract.billStatus eq BillStatus.effective +
arContract.csHeTongZhuangTaiCcbnbff0cxe01 eq P7BR6160CXE002E(已归档)。已归档 常量
P7BR6160CXE002E;企业自定义字段(合同状态、合同归档月份、采购类型、实际回款时间等)
是 qiqi 里的规范字段名,直接写进 --fields / --where 即可,不确定就 qiqi describe fields 查。
已登记的两张表在 references/ 里各自写了 20 个提供者的实体和条件,复算前先读那一份。项目收支分析表的复算已固化成脚本,改取数只改脚本里的 CONFIG,不要在对话里重新拼查询。
一次算很多项目(横向排名、全租户比对)
客户问「哪些项目最赚」「哪些超标」这类要拿全租户比的问题时,别逐个项目查,直接用聚合按项目分组:
qiqi --profile <别名> query aggregate PurOrderItem \
--group-by projectId --measure sum originAmountWithoutTax \
--where purOrder.billStatus eq BillStatus.effective --limit 200 --format json
MCP 通道的等价写法是 qiqi_records_aggregate,参数 {objectType, groupBy, measures:[{fn,field,as}],
where, limit} —— 同一套条件树,度量别名规则也一致({fn}_{field})。
聚合还有一个额外好处:明细实体(isDetail=true)在 MCP 上只有聚合读得到,
records_query 读不到(见上面「MCP 通道的四个限制」)。脚本里用 Q.group_sum() 走这条。
三个必须知道的坑:
- 分组查询一次最多 200 行(
--limit上限),无分组时只能 1 行。项目数超过 200 时要按
--offset翻页,或加条件分批(按组织、按年份切)。不要以为一次拿全了。 - 命令行
--where只能 AND。跨字段 OR / NOT 要写--input的 JSON 条件树
(根字段groupBy/measures/where/having/orderBy/offset/limit),
eq简写只收字符串。要按度量过滤用--having <别名> <操作符> <值>,
别名规则是{fn}_{field}(点号换成下划线,如sum_originAmountWithoutTax)。 - qiqi 网关有限流,并行压会拿到
RATE_LIMITED。限流响应里没有业务数据,
判「可用/有数据」时必须显式识别限流码,否则会把 0 行当成「查无数据」。
串行跑、必要时sleep,别用多进程。scripts/qiqilib.py已把限流单独抛成RateLimited。
动态成本的化简:材料已执行 + 服务动态成本 就是「采购订单无税 + 应付合同无税」的全部行(方案的 expr 一个取 C-ZH.02 的金额、一个取非 C-ZH.02 的金额,互补)。所以全租户口径可以直接写:
动态成本 = Σ采购订单明细原币无税 + Σ应付合同标的原币无税 + 材料未执行
动态收入 = Σ应收合同标的原币无税(合同已生效且已归档)+ Σ甲方签证金额(已生效)
材料未执行仍要按三支线分开算(见 references 里的表),其中「请购未采购」要按 (项目, 存货) 分组求未订货数量后,再乘存货成本——成本按 Product.latestCost / Product.refCost 关联,不要读嵌套路径。
做排名时先做一次数据体检,否则榜单会被记录问题项目占满:
- 算成本率 = 动态成本 ÷ 动态收入。成本率过低的项目是「收入进了、成本没录」,它们的高毛利不是赚出来的。可信样本取成本率落在中间区间的项目,不要直接用字面毛利前几名。
- 别忘费用归集科目。租户里会混着外服人工归集、公司综合费用、测试用途这类不是工程项目的记录,成本很大、收入为 0,会把总量指标带偏。按项目名称判断,不要把这类记录算进工程榜。
- 年度框架合同会把整份合同额记在一个项目上,但采购和施工在别的项目,表现为极高毛利率。做榜单时要点名。
- 报「全量毛利」这类总量时,同时给一版剔除异常后的数。个别录错的项目就足以决定全租户结论。
复算脚本
项目收支分析表的复算固化在 scripts/,不要每次重新拼查询。改脚本或加取数前先读 scripts/README.md。
cd ~/.workbuddy/skills/zhaozhi-operating-reports/scripts
# 累计口径 ——「这个项目到底赚不赚钱」
python3 recalc_project_income_expense.py --project <项目编码> --to <YYYY-MM-DD>
# 本年口径(注意会连合同底座一起切掉,见易错点 8)
python3 recalc_project_income_expense.py --project <项目编码> --year <年>
# 动过脚本之后必跑:已知答案回归
python3 recalc_project_income_expense.py --selftest
# 云端 / 没装 qiqi 命令的环境:走 MCP 通道(身份由 Key 决定)
QIQI_TRANSPORT=mcp QIQI_API_KEY=<API Key> python3 recalc_project_income_expense.py \
--project <项目编码> --to <YYYY-MM-DD>
通道默认自动探测(PATH 里有 qiqi 走 CLI,否则有 QIQI_API_KEY 就走 MCP);
QIQI_TRANSPORT=cli|mcp 可强制。两条通道的 --selftest 结果完全一致(2026-10-09 实测)。
什么情况下允许把复算值写进报告:优先取界面导出的 Excel;没有导出件时才用脚本,且必须同时满足 ① 定义指纹一致 ② 字典值全部解析且探针命中 ③ 自洽性校验通过 ④ 页脚写明「数值来自按方案 A6XMBC648KM007S 登记定义复算」。任一项不通过就不要出数。
脚本的防线(完整说明见 scripts/README.md):
| 防线 | 抓什么 |
|---|---|
| ① 定位写死 | 防止把「快照」当「权威取数通道」用;每次运行打印用途边界 |
| ② 全部参数化 | 项目、窗口、身份走命令行;字典值集中在 CONFIG["dicts"] 并标注来源 |
| ③ 字典值解析与存在性校验 | 编码型字典先在业务主档里 code → 记录 id,再用该 id 数行;值型直接数行。任何一项解析不到或 0 行就拒绝执行 |
| ④ 定义指纹 | 报表方案被改过时拒绝执行,并指出是哪一节变了。抓不到提供者条件串(qiqi 不暴露 criteriaStr) |
| ⑤ 已知答案回归 | 读本机 scripts/known_answers.local.json,比对指标值、「无发生」列和行数。文件不在技能正文里 |
第 ③ 条最容易被跳过、也最要命,单独说明:材料/服务是用「等于 / 不等于」同一个值拆的,两个条件天然互补,所以枚举值一旦失效,「材料 + 服务 = 总数」永远成立、自洽性完全抓不到,金额会静默从材料侧整段挪到服务侧。把材料采购类型改成不存在的编码时,动态成本与动态毛利可以一分不差、零报错,只有两侧内部组成变了。只有独立校验字典值本身才拦得住 —— 所以脚本改成「先在主档里把编码解析成记录 id,再用它数行」,比只数行更强。
第 ⑤ 条里的「无发生」列抓的是静默多算:劳务签证 那条查询如果漏了项目条件,会把全租户的乙方签证单都拉回来,金额很大,而自洽性和其余已知值仍然通过。导出件留空的列不校验,等于没有回归。具体基线只放在本机 known_answers.local.json。
qiqi 表达不了、脚本已降级处理的口径(详见 scripts/README.md 的对照表,脚本会主动提醒):
- 深化已采购:界面方向是「订单明细 → 来源请购明细 → 深化清单明细」,其中
PurOrderItem.srcItemObjectPurReqItem是动态字段(能过滤、读不到值)。已改为反向取数:先取项目下「来源单据明细 =CsShenHuaQingDanMingXi」的请购明细(PurReqItem.srcItemObjectId可读),再用srcItemObjectPurReqItem.id eq <请购明细 id>反查订单明细。两条链的行数与订购数量必须一致,不一致就抛错拒绝出数。正式报告前用界面导出件核这一列。 - 深化合计:qiqi 没暴露清单明细的项目字段,改走「项目 → 应收合同 → 深化清单测算 → 明细」。
- 劳务签证:qiqi 的乙方签证单没有应付合同字段,改走「甲方签证单」间接收口。
- 提供者条件串:指纹抓不到,靠回归兜底。
脚本报错不要绕过,按 scripts/README.md 的对照表处理:定义变了就重新推导那一节再重建基线;字典值失效就去界面查新值改 CONFIG;--selftest 退出码 3 时不要出报告,先定位是哪一项偏了。确实要用 --force 强行跑时,务必看它退化的提醒。
改动脚本后重跑 --selftest(两条通道都跑一遍:QIQI_TRANSPORT=cli 与 QIQI_TRANSPORT=mcp,
结果必须一致);改过报表口径还要跑 defs_snapshot.py --write 重建定义基线,并把新口径写回 references/。
复算易错点
用真实项目对过账:漏规则时会整列算错;规则补齐后,关键指标列可以和界面导出逐项对齐。所以复算可以交付,但前提是下面这些规则一条都不能省,并且按「复算脚本」节的四个条件放行、页脚写明「数值来自按方案 A6XMBC648KM007S 登记定义复算」。手上有同方案导出件时仍应先对一遍——脚本已经把下面这些规则写进代码(含断言与拒绝执行),所以优先跑脚本,而不是照本节手写查询。
-
一定要带查询日期。 报表里所有按日期过滤的列(合同、签证、发票、收入确认、采购、收付款)都受日期窗口约束,没映射这个字段的只有 0001预算、0402累计收款、1402累计付款三个提供者(17 个提供者的日期字段路径见 references/project-income-expense-sources.md)。截止日之后开的红字销售发票,报表会排除;查询不加日期就会把它减进去,连带多列一起错。qiqi 的日期只收自然日
YYYY-MM-DD,传'YYYY-MM-DD 23:59:59'会报date_value_invalid(the date value must be a valid YYYY-MM-DD natural date)。 -
红字发票也是
BillStatus.effective。 判断发票不能只看状态;红字销售发票的单据类型名称是「红字销售发票」,金额为负。先按日期筛,再按单据类型。 -
签约金额不要用
customVersion判。 登记规则写的是「只覆盖仍为 V1.0 的已归档合同」,但customVersion=V2.0、没有变更单的合同,系统照样给出签约金额,且等于合同最新造价。按 V 版本号推断签约额会得到 0。真正的机制在 0101 变更单上:0102(非变更单 + V1.0)可以是 0 行,签约金额全部来自 0101。变更链是主合同当前版 + 若干changeVersion。某一版按「上一版 = 同 baseBill 且 changeVersion = 本单 cv−1」找不到上一版时,差额等于全额;与上一版标的相同的版本差额为 0。各版差额相加应等于合同最新造价。 -
查变更单必须显式写
isChangeBill=true。ArContract的实体查询默认只返回非变更单(主合同),不加这个条件时「上一版合同」的关联会静默返回空,于是每一个变更版本都被当成「没有上一版」、差额取全额——签约金额会翻成 2 倍。这个错不会报错,只会多算。 -
材料 / 服务的拆分必须把条件交给引擎,而且要先做编码解析。 采购类型和合同分类都是租户自定义的引用,
在 qiqi 里以规范名 +Object后缀出现,且读值不给支持、过滤支持:
| 用途 | 过滤路径 | 值从哪来 |
|---|---|---|
| 采购订单材料 | purOrder.csCaiGouLeiXingCcddrxt0ccr01Object.id |
主档 CsHeTongFenLei 里按编码 C-ZH.02 查出记录 id(NGPBSB648KM0026) |
| 应付合同材料 | apContract.contractCategoryId |
主档 ContractCategory 里按编码 C-ZH.02 查出记录 id(FCVBSB648KM0004) |
所以正确做法是:先 qiqi query list CsHeTongFenLei --where code eq C-ZH.02 把编码解析成记录 id,
再跑两条查询——...Object.id eq <id>(材料)与 ne <id>(服务)。不要在 Python 里按 id 自己判断,
也不要把编码直接塞进过滤条件(purOrder.purchaseTypeId eq C-ZH.02 会返回 0 行,标准采购类型字段和这个
自定义字段不是一回事)。材料侧命中后,服务动态成本可以是 0;不要拿标准采购类型字段来拆。
顺带:PurOrderItem 上直接读 purOrder.purchaseType.name 得到的是标准采购类型,本项目为空,
别拿它做拆分。
- 材料未执行(
材料动态成本、动态成本、动态毛利、成本差全部串在这条上):公式一向是对的,错在取价没实现回退。 公式 =max(0, 深化合计 − 深化已采购) + 请购未采购金额,三个分支各有各的提供者和项目来源(完整表见 references/project-income-expense-sources.md):
| 分支 | 提供者 | 主实体与关联 | 过滤条件 | 项目取自 |
|---|---|---|---|---|
| 深化合计 | CTBGXB648KM003R(0601深化金额) |
CsShenHuaQingDanMingXi,无 join |
csShenHuaQingDanCeSuanId.billStatus='BillStatus.effective' 且 isChangeBill=false |
m.csXiangMuCcdwr9f0c0901 |
| 深化已采购 | CTBGXB648KM003S(0602深化采购金额) |
PurOrderItem leftJoin PurOrder innerJoin PurReqItem innerJoin CsShenHuaQingDanMingXi |
订单/请购生效、PurReqItem.srcObjectTypeObject.id='CsShenHuaQingDanCeSuan'、测算生效;值 = transQty × cqslave3.深化单价 |
m.project(采购订单明细) |
| 请购未采购金额 | T8UEAC648KM00F4(1501请购单) |
PurReqItem leftJoin PurReq |
req.billStatus='BillStatus.effective' 且 coalesce(m.srcObjectType.id,'')!='CsShenHuaQingDanCeSuan' |
m.project(请购单明细) |
- 三路项目来源不同:清单明细自己的项目字段是
csXiangMuCcdwr9f0c0901,测算头上另有一个csXiangMuCcd7n6j0ccr01。判断「某项目有没有深化」只能看明细那一个。qiqi 没暴露csXiangMuCcdwr9f0c0901,所以 qiqi 版脚本改走「项目 → 应收合同 → 深化清单测算(csGuanLianYingShouHeTongCcjsaex1rbu01)→ 明细(csShenHuaQingDanCeSuanId)」;测算单若只挂项目指令单不挂应收合同,会少算。 - 取价必须回退:
case when coalesce(存货.最新成本,0) <= 0 then coalesce(存货.参考成本,0) else 最新成本 end。多数请购行的最新成本是 0,要走参考成本。只取最新成本会明显偏小,只取参考成本也对不上,按这条回退才和系统一致。 - 成本读存货主数据:成本字段不要顺着请购单的嵌套路径取,要另查
Product.latestCost/Product.refCost,按productId关联。嵌套路径返回空就当 0 会把整列算没。 - 「深化已采购」改走反向链(2026-10-09 修正):正面这条链要读订购单的「来源请购明细」(
srcItemObjectPurReqItem),它只能过滤(.id)、读不到值(读.code报QUERY_DSL_FIELD_UNAVAILABLE)。但请购明细侧是通的 ——PurReqItem.srcObjectType/srcItemObjectType/srcItemObjectId可读,srcItemObjectId直接指向深化清单明细行。所以做法是:先筛出「来源单据明细对象名 =CsShenHuaQingDanMingXi」的请购明细,再逐个srcItemObjectPurReqItem.id eq <请购明细 id>反查订单明细,取transQty × 深化单价。必须逐行对上:反查行数 = 挂来源请购的订单明细行数,且反查ΣtransQty= 请购侧ΣorderedTransQty。对不上就抛错拒绝出数。反查是逐行调用,注意 qiqi 限流,脚本已内置重试。 - 请购来源的排除条件要写成
OR[srcObjectType isnull, srcObjectType ne CsShenHuaQingDanCeSuan](qiqi 里ne会排除空值,只写ne会把空来源的行全部丢掉)。 - 没有深化、订单明细也没有来源请购时,材料未执行只剩请购未采购这一支。三支相加要和系统一致。
-
导出的列标题和栏目文件里的叫法不一样。 同一列在界面上叫「动态成本小计」「应收余额」「到款比例」「净现金流」的可能,在栏目文件里叫「动态成本」「已开票未回款」「回款率」「本期收付净额」。对照表在 references/project-income-expense-sources.md 里,对账前先按那张表认列,认错列会得出假差异。导出留空的列表示该项目下没有这类单据,照原样写「无发生」,不补 0。
-
客户说「本年」时先想清楚窗口切掉了什么。 日期的快捷选项里有「本年」(1/1~12/31),但它同时绑在合同归档月份上。合同和采购都发生在往年的项目,选本年会得到合同金额 0、动态收入 0、动态成本 0、动态毛利 0,而预算四列、累计收款、累计付款照旧——相减出来的收入差/成本差/毛利差全是口径假象。这时候要出两条线:本年发生额(发票、收入确认、回款)+ 累计经营底座(动态收入/成本/毛利、预算对比),并在页面上写清为什么动态毛利「无发生」。
-
能稳定复算的是预算口径、材料已执行/采购、合同最新造价、签约金额、发票与收入确认(带日期)、收付款与净现金流,以及第 6 条的
材料未执行(按三条规则)。
查询本身的几个语法坑(会白跑一轮)
ArContract默认过滤变更单,要查变更单必须显式写isChangeBill eq true(见易错点第 4 条)。- 关系字段要落到叶子:
receipt.billType.id可以、receipt.billType不行;报错里会直接给出可用写法。
另有几处必须用扁平外键、不能用嵌套的点路径:ReceiptItem用projectId、
ArContractSubjectMatterItem用arContractId、PurReqItem用reqId。
写错不会报错,只会静默返回 0 行——
2026-10-09 就因为把arContractId写成arContract.id,签约金额整整算成 0。 - 自定义引用字段读值带
Object后缀(...Ccddrxt0ccr01Object),过滤用它的.id。读值和过滤
有时只有一个支持,两个都试一次。 - 日期只收自然日,
between的上下界也是自然日。 - 命令行
--where只能 AND;OR / NOT / isnull 走--input的 JSON 条件树;eq简写只收字符串
(传数字会报field predicate must use a string…);in最多 100 个值。 - 大结果会落盘:返回
{"type":"77hub.large-output","path":…}指针文件,要读那个文件,
终端里的内容是被截断的。 - qiqi 网关有限流,返回
RATE_LIMITED。限流响应里没有业务数据,不要当成 0 行;
串行跑,别并行压。 - 项目上取不到的字段别急着当 0(
srcObjectType这类动态字段恒为空、...Object有时只能过滤不能读),
要换一条查询或换一个入口去取。
成稿
客户要的是分析、报告或经营状况时,按 references/report-format.md 写成 HTML。只问某一列是什么意思时,用文字回答,不产 HTML。
分析写细,版式固定:深色页头、灰底白卡片、从 01 编号的节。每一节先写判断,再放这一节的图或 KPI,再放表,最后给这一节能做的动作。节数按数据定,不凑固定大纲。口径不同的两列不能写成超支额或剩余利润。没查到的不写,也不补 0。
报告里的数值优先用系统导出值,没有导出件时用复算值,但页脚必须写明是哪种。 复算按「复算脚本」节跑 scripts/ 里的脚本(不要手写查询),放行前四个条件必须全过。导出留空的列在页面上写「无发生」,不写 0。页脚要写清本次的时间窗口(是「本年 1/1~12/31」还是「累计 ≤ 某日」)、创建组织范围、项目,以及有哪些列口径不同不能相减。数跟上一版比结论反转时,在页脚标一句版本和值来源,别悄悄换数。
客户问「本年经营情况」时,按易错点第 8 条出两条线:01 节先讲清窗口切掉了什么(本年动态毛利会是「无发生」),再给本年发生额,最后给累计经营底座和预算对比。不要只出本年口径那一张就交差。
同一模型上还有公共方案「收入合同分析(财务)」「收入合同分析(本年)」「本年收入合同分析」,列和默认日期可能不同,未逐列收录。用户明确在看其中某一个时,先读那个方案自己的栏目,不要直接套 6G3M0G648GU004E。
跨表禁区
两张表都出现这些词,含义不同,不能对减,也不能把合同行加总后叫做项目动态收入。
- 签证。 收支分析表是项目签证单上的签证金额。收入合同分析是应收合同标的类型「签证」的标的金额。
- 合同金额。 收支分析表要求合同已归档,日期用合同归档月份,金额含全部标的。收入合同分析只要求合同已生效,日期用主合同归档日期,并可按标的类型拆开。
- 回款。 两边都排除其他收款和坏账清理。收支分析表的回款只看实际回款时间。收入合同分析的当期回款还要求主合同归档日期落在合同日期里;累计回款不看回款日期。
列名
栏目标题优先,否则用数据源字段标题。自定义公式两边都没有时,用栏目文件里的解读名,并说明方案没起栏目标题。对用户用中文列名。
再加一张报表
- 确认是公共方案,记下方案、模型、数据源。私人方案不登记。
- 在「先选表」加一行,写客户会怎么问,不写栏目名。
- 在
references/新增栏目文件:开头写本表禁区,然后按栏目分组写公式和来源。来源长时另写一份 sources 文件,从栏目文件链过去。 - 新表如果也用到签证、合同金额、回款、预算、毛利,在跨表禁区补一条差异。
- 新表要做复算时,按
scripts/README.md「维护」节走一遍:在CONFIG["dicts"]注册用到的字典值(含probe)→ 抓定义基线 → 写known_answers.json(含spot_checks行数)→--selftest通过才算完成。 - 不把查询结果写进文件。