sapbuddy-dsh 0.0.8 → 0.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  [![npm](https://img.shields.io/npm/v/sapbuddy-dsh)](https://www.npmjs.com/package/sapbuddy-dsh) [GitHub](https://github.com/gxx950224/sapbuddy-dsh)
4
4
 
5
- [SapBuddy](https://github.com/gxx950224/sapbuddy) 的 DeepSeek Harness 插件,提供 41 个 SAP ABAP 工具、8 个技能、SapBuddy agent preset 和 SAP 连接设置页。
5
+ [SapBuddy](https://github.com/gxx950224/sapbuddy) 的 DeepSeek Harness 插件,提供 41 个 SAP ABAP 工具、10 个技能、SapBuddy agent preset 和 SAP 连接设置页。
6
6
 
7
7
  ## 安装
8
8
 
@@ -10,13 +10,13 @@
10
10
  在 DSH 的插件管理页添加:
11
11
 
12
12
  ```text
13
- sapbuddy-dsh@0.0.8
13
+ sapbuddy-dsh@0.0.9
14
14
  ```
15
15
 
16
16
  也可以通过 DSH 命令行安装:
17
17
 
18
18
  ```bash
19
- dsh plugin --profile desktop add sapbuddy-dsh@0.0.8
19
+ dsh plugin --profile desktop add sapbuddy-dsh@0.0.9
20
20
  ```
21
21
 
22
22
  更新已安装插件后,完整退出并重新启动 DSH,再新建会话选择 **SapBuddy**。
@@ -57,7 +57,7 @@ dsh plugin --profile desktop add sapbuddy-dsh@0.0.8
57
57
 
58
58
  工具涵盖对象搜索与读码、代码导航、语法诊断与激活、ATC 与单元测试、SQL 查询、DDIC、文本池、屏幕文本、消息类、函数模块接口和传输请求。
59
59
 
60
- 技能包括 `abap-code-review`、`abap-explain`、`abap-templates`、`abap-wiki`、`clean-abap`、`dev-write`、`sap-abap-skills`、`translate-workflow`。
60
+ 技能包括 `abap-code-review`、`abap-explain`、`abap-templates`、`abap-wiki`、`clean-abap`、`dev-write`、`sap-abap-skills`、`translate-workflow`、`sap-fs`(四类 FS:MD + Claude 风 HTML)、`grilling`(经 sap-fs 加载的追问澄清)。
61
61
 
62
62
  ## 配置和写入保护
63
63
 
@@ -74,7 +74,7 @@ dsh plugin --profile desktop add sapbuddy-dsh@0.0.8
74
74
  从 GitHub 克隆仓库后,在 `packages/sapbuddy-dsh` 下运行激活回归测试(需要 Node.js 24+,无需连接 SAP):
75
75
 
76
76
  ```bash
77
- npm test # = test:activation + test:session + test:store + test:probe + test:route + test:bundle
77
+ npm test # = test:activation + test:session + test:store + test:probe + test:route + test:bundle + test:sap-fs
78
78
  npm pack --dry-run --ignore-scripts
79
79
  ```
80
80
 
@@ -32,7 +32,8 @@ SapBuddy — SAP ABAP AI 全能助手,面向**开发顾问与业务顾问**。
32
32
  - **工具硬化**:`create_object_programmatically` 缺包名/描述会被拦截;传 $TMP 需用户单独确认(不进传输请求);`replace_string` 无未释放请求时自动建请求——这些无需 LLM 自觉,工具强制执行。
33
33
  6c. **连接切换后先重确认(强制)**:用户切换/修改 SAP 连接后,**必须先调用 `get_connected_systems` 确认当前连接,再使用其他 SAP 工具**。工具层强制拦截:未确认前其他 SAP 工具一律失败(返回"连接配置已变更,先调用 get_connected_systems")。看到该拦截错误时,立即调用 `get_connected_systems`,再重试被拦的操作。
34
34
  7. **写操作谨慎**:写工具(创建/编辑/激活)需要服务器 `security.readOnly=false`;生产系统只读。
35
- 8. **按技能加载(强制)**:写代码/写 SAP 对象(函数模块/表结构/函数组 include/文本元素等)先遵循 `dev-write` 技能(编码规范、写入规范);批量翻译(Excel 清单→SAP)先遵循 `translate-workflow` 技能(先 list 后 set、匹配不上报告、**禁止猜 key 写入**)。技能按话题自动加载,做对应任务时**必须遵守**。
35
+ 8. **按技能加载(强制)**:写代码/写 SAP 对象(函数模块/表结构/函数组 include/文本元素等)先遵循 `dev-write` 技能(编码规范、写入规范);批量翻译(Excel 清单→SAP)先遵循 `translate-workflow` 技能(先 list 后 set、匹配不上报告、**禁止猜 key 写入**)。技能按话题自动加载,做对应任务时**必须遵守**;写 FS / 功能设计书时遵循 `sap-fs` 技能(意图不明先问,见铁律 9)。
36
+ 9. **FS 意图门禁(强制)**:仅当用户**明确**要写/改 FS、功能设计书、接口/报表/增强**规格文档**(或明确说按 sap-fs 出可交付文档)时,才加载并执行 `sap-fs`。日常问答、配置怎么配、表/事务码解释、程序解释/审查、写 ABAP 代码 → **禁止**触发 sap-fs(写码→`dev-write`;审查→`abap-code-review`;解释逻辑→`abap-explain` / `abap-wiki`)。用户只抛业务描述或方案讨论、**未明确**要出 FS → **先问一句**是否要按 sap-fs 产出 `FS.md` + `FS.html`(接口另附 JSON);未确认前只澄清/回答,**不**套 FS 模板、不落盘 `output/fs/`。用户确认后才 Skill 加载 `sap-fs`;追问业务决策时再按 sap-fs 流程加载 `grilling`。
36
37
 
37
38
  ## 防死循环与止损(软约束,靠自觉;能代码强制的已由工具层兜底)
38
39
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "sapbuddy-dsh",
3
- "version": "0.0.8",
4
- "description": "SapBuddy —— SAP ABAP AI 全能助手(DeepSeek Harness 插件):41 个 SAP 工具 + 8 个 ABAP 技能 + SapBuddy preset + 连接设置页。一个包名装完。",
3
+ "version": "0.0.9",
4
+ "description": "SapBuddy —— SAP ABAP AI 全能助手(DeepSeek Harness 插件):41 个 SAP 工具 + 10 个 ABAP 技能 + SapBuddy preset + 连接设置页。一个包名装完。",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
7
7
  "exports": {
@@ -77,13 +77,14 @@
77
77
  "typescript": "^5.6.0"
78
78
  },
79
79
  "scripts": {
80
- "test": "npm run test:activation && npm run test:session && npm run test:store && npm run test:probe && npm run test:route && npm run test:bundle",
80
+ "test": "npm run test:activation && npm run test:session && npm run test:store && npm run test:probe && npm run test:route && npm run test:bundle && npm run test:sap-fs",
81
81
  "test:activation": "node --experimental-vm-modules --test --test-isolation=none test/activation.test.mjs",
82
82
  "test:session": "node --experimental-vm-modules --test --test-isolation=none test/session-recovery.test.mjs",
83
83
  "test:store": "node --test --test-isolation=none test/connections-store.test.mjs",
84
84
  "test:probe": "node --test --test-isolation=none test/connection-probe.test.mjs",
85
85
  "test:route": "node --test --test-isolation=none test/connection-test-route.test.mjs",
86
- "test:bundle": "node --test --test-isolation=none test/client-bundle.test.mjs"
86
+ "test:bundle": "node --test --test-isolation=none test/client-bundle.test.mjs",
87
+ "test:sap-fs": "node --test --test-isolation=none test/sap-fs/loading.test.mjs"
87
88
  },
88
89
  "publishConfig": {
89
90
  "access": "public"
@@ -0,0 +1,43 @@
1
+ ---
2
+ name: grilling
3
+ description: >-
4
+ Grill the user relentlessly about a plan, decision, or idea. Use when the user
5
+ wants to stress-test their thinking, or uses any 'grill' trigger phrases
6
+ (including "grill me"). Also used by sap-fs when clarifying FS business decisions;
7
+ not the entry skill for FS intent (load via sap-fs after user confirms FS deliverable).
8
+ ---
9
+
10
+ <!--
11
+ Third-party: mattpocock/skills (MIT, Copyright 2026 Matt Pocock)
12
+ Upstream: https://github.com/mattpocock/skills
13
+ skills/productivity/grilling/SKILL.md
14
+ Pinned blob: df69d9936880cd3314c28858c6c52f48db23b856 (fetched 2026-10-28)
15
+ Adaptation: bundled under sapbuddy-dsh/skills for DSH discovery; description notes sap-fs;
16
+ "grill me" triggers are covered here directly (no separate grill-me alias).
17
+ -->
18
+
19
+ Interview the user relentlessly until you reach a shared understanding. Map this as a **design tree**: every decision branches into the decisions that hang off it.
20
+
21
+ Work the tree in **rounds**. The **frontier** is every decision whose prerequisites are already settled: the questions you can ask _now_ without guessing at answers you haven't heard yet. Ask the whole frontier in one round: number each question and give your recommended answer. Then wait for the user's answers before the next round.
22
+
23
+ Format a round like so:
24
+
25
+ ```
26
+ ❓ **Q1** - **<question title>**: <question body, might be multiple paragraphs, including multiple choices>
27
+
28
+ ➡️ <your recommended answer>
29
+
30
+ ---
31
+
32
+ ❓ **Q2** - **<question title>**: <question body, might be multiple paragraphs, including multiple choices>
33
+
34
+ ➡️ <your recommended answer>
35
+ ```
36
+
37
+ Word each question so "yes" accepts your recommended answer.
38
+
39
+ Each round the user answers reshapes the tree: settled decisions push the frontier outward and unblock questions that depended on them. Recompute the frontier and ask the next round. A question whose answer depends on another question still open in this round belongs to a _later_ round, not this one.
40
+
41
+ Finding _facts_ is your job, never the user's. When a frontier question needs a fact from the environment (filesystem, tools, etc.), dispatch a sub-agent to find it; don't ask the user for anything you could look up yourself. Don't block on it: a running exploration is an unsettled prerequisite, so only the questions downstream of it wait for the sub-agent to report; ask the rest of the frontier now. The _decisions_ are the user's: put each to them and wait.
42
+
43
+ The session is done when the frontier is empty: every branch of the design tree visited, nothing left silently assumed. Do not act on it until the user confirms you have reached a shared understanding.
@@ -0,0 +1,76 @@
1
+ ---
2
+ name: sap-fs
3
+ description: >-
4
+ 编写 SAP 功能规格书(FS / 功能设计书)。仅在用户明确要求写/改 FS、功能设计书、
5
+ 接口设计、报表设计、增强设计,或明确说按 sap-fs 出可交付规格文档时触发。
6
+ 日常问答、配置咨询、解释程序、代码审查、写 ABAP 不触发;意图不明时先问是否出 FS。
7
+ 确认后:识别需求类型 → 系统取证 → 用 grilling 追问业务决策 → 按模板生成 FS。
8
+ 触发关键词:写FS、功能规格、功能设计书、接口设计、报表设计、增强设计、出一份FS。
9
+ agent_created: true
10
+ disable: false
11
+ ---
12
+
13
+ # sap-fs — 写 SAP 功能规格书
14
+
15
+ > 草稿结构(Codex 第一版目录)。模板在 `assets/templates/`,过程规范在 `references/`。
16
+ > 追问逻辑不内嵌:加载 `grilling`。上游许可与钉选说明见 `grilling/SKILL.md` 顶部注释。
17
+
18
+ ## 何时使用
19
+
20
+ - 用户**明确**要写 / 改 FS、功能设计书、接口 / 报表 / 增强**规格文档**,或明确说按 sap-fs 出可交付文档。
21
+ - 用户确认「要出 FS」之后(见 SYSTEM 铁律 FS 意图门禁)。
22
+
23
+ 不触发:日常问答、配置怎么配、表/事务码解释、程序解释(`abap-explain` / `abap-wiki`)、代码审查(`abap-code-review`)、写 ABAP(`dev-write` / `abap-templates`)。
24
+ 意图不明(只抛业务描述 / 方案讨论、未说要出 FS):**先问一句**是否要按 sap-fs 产出 `FS.md` + `FS.html`(接口另附 JSON);未确认前不套模板、不落盘 `output/fs/`。
25
+
26
+ ## 资源地图(按需加载,勿一次全读)
27
+
28
+ | 时机 | 读什么 |
29
+ | --- | --- |
30
+ | 触发后立刻 | 本文件 + `references/fs-contract.md` |
31
+ | 需要连系统 | `references/system-verification.md` |
32
+ | 需要追问 | `references/grilling-integration.md`,并 **Skill 加载 `grilling`**(含 “grill me” 类触发) |
33
+ | 已判定类型 | `references/types/<type>.md` + `assets/templates/fs-base.md` + `assets/templates/<type>.md` |
34
+ | 对齐详略 | `references/example-enhancement.md`(仅增强类样例) |
35
+ | 交付前 | `references/review-checklist.md` |
36
+ | 出归档 HTML / 接口 JSON | `references/archive-html.md` + `assets/templates/fs-archive.html` |
37
+
38
+ 类型文件名:`interface` / `query-report` / `functional-report` / `enhancement`。
39
+
40
+ ## 工作流程
41
+
42
+ 1. **判类型**(混合取主导;ALV 按钮会改业务状态 → 功能报表)。读对应 `types/` 与模板。
43
+ 2. **整理已知**:用户已说内容分到需求 / 现状 / 规则,先不提问。
44
+ 3. **取证**:按 `system-verification.md` 只查能查的。
45
+ 4. **追问**:按 `grilling-integration.md` 加载 `grilling`,只问业务决策。
46
+ 5. **生成**:拼装 `fs-base` + 类型专项模板;规则 / 验收 / 待确认 / 附录按 `fs-contract.md`。
47
+ 6. **自检**:`review-checklist.md` 通过后再交付。
48
+
49
+ ## 拼装
50
+
51
+ ```
52
+ assets/templates/fs-base.md # 第1、2、4、5、6、7节 + 附录A
53
+ + assets/templates/<type>.md # 第3节
54
+ + 头部状态行
55
+ = 一份完整 FS
56
+ ```
57
+
58
+ 默认落盘目录(用户未指定时):`output/fs/<需求名>/`(允许的 `output` 子树);详见下方交付物。
59
+
60
+ ## 交付物(Markdown + HTML,接口另附 JSON)
61
+
62
+ 同一需求目录默认:`output/fs/<需求名>/`
63
+
64
+ 1. `FS.md` — 协作 / AI 主规格。
65
+ 2. `FS.html` — 企业归档:套 `assets/templates/fs-archive.html`,规则见 `references/archive-html.md`。
66
+ 3. **禁止 logo**(徽标、水印、品牌图都不放)。
67
+ 4. **接口类**另出:正文 3.6 样例 JSON + 同目录 `io-request.json` / `io-response.json`(及必要的回调文件)。
68
+
69
+ 先定稿 Markdown,再渲染 HTML / 写出 JSON;自检通过后一并交付路径。
70
+
71
+ ## 硬边界
72
+
73
+ - 本技能流程**只读 SAP**;FS 确认 ≠ 写入许可。
74
+ - 正文只写事实与决定;训诫与生成纪律留在技能文件,不进 FS。
75
+ - 不虚构表名 / 字段 / 增强点 / 签署与测试结果。
76
+ - HTML 归档不使用任何 logo。
@@ -0,0 +1,52 @@
1
+ <!-- 本片段插入骨架第3节。增强只描述"在哪个时点、改变哪段行为、其余保持原样"。
2
+ 第2节写整体现状;这里只写本次涉及的动作。 -->
3
+
4
+ ## 3. 增强设计
5
+
6
+ ### 3.1 改变的行为
7
+
8
+ | 动作 | 现状 | 目标 | 保持不变 |
9
+ | --- | --- | --- | --- |
10
+ | <保存 / 过账 / 审批> | <现在怎么处理> | <改变后怎么处理> | <仍须沿用的标准行为> |
11
+
12
+ **实施方式**:<标准配置能否满足;选增强的理由。未验证的判断标 Q。>
13
+
14
+ ### 3.2 触发点
15
+
16
+ | 入口 | 时点 | 触发前状态 | 取得的数据 |
17
+ | --- | --- | --- | --- |
18
+ | <事务码 / 接口 / 后台> | <校验阶段 / 保存前 / 提交后> | <订单已保存 / 未保存> | <当前输入值 / 数据库旧值> |
19
+
20
+ - **覆盖入口**:<前台、后台、接口分别是否覆盖;未确认的标 Q。>
21
+ - **新旧值**:<规则用的是用户当前输入,还是已落库的值;修改后是否重新校验。>
22
+ - **未保存数据**:<字段尚未落库时以什么为准。>
23
+ - **派生值**:<标准逻辑或其它增强是否也会改同一字段;先后关系。>
24
+
25
+ ### 3.3 适用与排除
26
+
27
+ | 项目 | 定义 |
28
+ | --- | --- |
29
+ | 命中条件 | <组织、单据类型、业务状态、角色> |
30
+ | 排除条件 | <必须绕过增强的场景> |
31
+ | 与其它规则的优先级 | <同时命中时先后;先报哪条> |
32
+ | 不命中时 | <维持原有行为> |
33
+ | 既有校验先失败时 | <是否继续执行本增强;用户最终看到什么> |
34
+
35
+ ### 3.4 处理逻辑
36
+
37
+ <按顺序写:取数 → 判断 → 结果。引用 R-01 等规则编号,不在这里重复条件。>
38
+
39
+ ### 3.5 重复触发与事务
40
+
41
+ | 场景 | 期望 |
42
+ | --- | --- |
43
+ | 同一操作多次触发 | <哪些可重复,哪些副作用只能发生一次> |
44
+ | 保存后再次修改 | <哪些字段变化需要重新判断> |
45
+ | 标准动作最终失败 | <本次已产生的变化是否必须一同撤销> |
46
+ | 已调用外部系统 | <外部成功但本地失败的核对与补偿> |
47
+
48
+ ### 3.6 停用与回归
49
+
50
+ - **停用后**:<恢复原有行为;已保存数据不回滚。>
51
+ - **在途业务**:<处理中的单据、任务、外部请求如何处理。>
52
+ - **回归范围**:<既有增强、标准校验、相关模块(凭证流、库存、财务)需要复测的部分。>
@@ -0,0 +1,320 @@
1
+ <!DOCTYPE html>
2
+ <html lang="zh-CN">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
6
+ <title>{{DOC_TITLE}}</title>
7
+ <style>
8
+ /* Claude Design System tokens (from open-claude-ui cds-theme) — archive sheet, no logo */
9
+ :root {
10
+ --brand: #c6613f;
11
+ --cds-clay: #d97757;
12
+ --cds-clay-light: #fbf3ef;
13
+ --cds-gray-0: #fff;
14
+ --cds-gray-10: #fcfcfb;
15
+ --cds-gray-20: #f9f9f7;
16
+ --cds-gray-40: #f3f3f0;
17
+ --cds-gray-100: #e1e0d9;
18
+ --cds-gray-200: #c3c2b7;
19
+ --cds-gray-500: #6d6b67;
20
+ --cds-gray-600: #52514e;
21
+ --cds-gray-850: #151515;
22
+ --cds-text-primary: #151515;
23
+ --cds-text-secondary: #525252;
24
+ --cds-text-muted: #8a8a8a;
25
+ --cds-border: hsla(0, 0%, 8%, 0.10);
26
+ --cds-border-strong: hsla(0, 0%, 8%, 0.20);
27
+ --cds-shadow-sm: 0 1px 2px rgba(0,0,0,.05), 0 2px 8px rgba(0,0,0,.08);
28
+ --cds-shadow-md: 0 2px 4px rgba(0,0,0,.05), 0 6px 16px rgba(0,0,0,.08);
29
+ --radius-md: 6px;
30
+ --radius-lg: 8px;
31
+ --radius-xl: 12px;
32
+ --radius-2xl: 16px;
33
+ --font: "Anthropic Sans", "Segoe UI", "PingFang SC", "Microsoft YaHei", "Noto Sans CJK SC", sans-serif;
34
+ --mono: "Anthropic Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
35
+ }
36
+ * { box-sizing: border-box; }
37
+ body {
38
+ margin: 0;
39
+ min-height: 100vh;
40
+ color: var(--cds-text-primary);
41
+ background: var(--cds-gray-20);
42
+ font: 15px/1.65 var(--font);
43
+ -webkit-font-smoothing: antialiased;
44
+ }
45
+ .sheet {
46
+ max-width: 880px;
47
+ margin: 28px auto 48px;
48
+ padding: 36px 40px 40px;
49
+ background: var(--cds-gray-0);
50
+ border: 1px solid var(--cds-border);
51
+ border-radius: var(--radius-2xl);
52
+ box-shadow: var(--cds-shadow-md);
53
+ }
54
+ .accent-bar {
55
+ height: 3px;
56
+ width: 48px;
57
+ border-radius: 999px;
58
+ background: var(--cds-clay);
59
+ margin-bottom: 18px;
60
+ }
61
+ h1 {
62
+ margin: 0 0 10px;
63
+ font-size: 1.55rem;
64
+ font-weight: 600;
65
+ letter-spacing: -0.02em;
66
+ line-height: 1.3;
67
+ }
68
+ h2 {
69
+ margin: 32px 0 12px;
70
+ padding-bottom: 8px;
71
+ font-size: 1.1rem;
72
+ font-weight: 600;
73
+ letter-spacing: -0.01em;
74
+ border-bottom: 1px solid var(--cds-border);
75
+ }
76
+ h3 {
77
+ margin: 20px 0 8px;
78
+ font-size: 0.98rem;
79
+ font-weight: 600;
80
+ color: var(--cds-text-primary);
81
+ }
82
+ h4 {
83
+ margin: 14px 0 6px;
84
+ font-size: 0.9rem;
85
+ font-weight: 600;
86
+ color: var(--cds-text-secondary);
87
+ }
88
+ p, li { margin: 6px 0; color: var(--cds-text-primary); }
89
+ .lede { color: var(--cds-text-secondary); font-size: 0.92rem; }
90
+ .status {
91
+ display: inline-flex;
92
+ align-items: center;
93
+ gap: 6px;
94
+ margin: 4px 0 18px;
95
+ padding: 4px 10px;
96
+ border-radius: 999px;
97
+ background: var(--cds-clay-light);
98
+ color: var(--brand);
99
+ border: 1px solid color-mix(in srgb, var(--cds-clay) 28%, transparent);
100
+ font-size: 0.78rem;
101
+ font-weight: 500;
102
+ }
103
+ .status::before {
104
+ content: "";
105
+ width: 6px; height: 6px;
106
+ border-radius: 50%;
107
+ background: var(--cds-clay);
108
+ }
109
+ .meta {
110
+ display: grid;
111
+ grid-template-columns: 7.5rem 1fr;
112
+ gap: 8px 14px;
113
+ margin: 0 0 8px;
114
+ padding: 14px 16px;
115
+ background: var(--cds-gray-10);
116
+ border: 1px solid var(--cds-border);
117
+ border-radius: var(--radius-xl);
118
+ font-size: 0.88rem;
119
+ }
120
+ .meta dt { color: var(--cds-text-muted); font-weight: 500; }
121
+ .meta dd { margin: 0; color: var(--cds-text-secondary); }
122
+ table {
123
+ width: 100%;
124
+ border-collapse: separate;
125
+ border-spacing: 0;
126
+ margin: 12px 0 18px;
127
+ font-size: 0.86rem;
128
+ border: 1px solid var(--cds-border);
129
+ border-radius: var(--radius-xl);
130
+ overflow: hidden;
131
+ background: var(--cds-gray-0);
132
+ }
133
+ th, td {
134
+ padding: 9px 11px;
135
+ text-align: left;
136
+ vertical-align: top;
137
+ border-bottom: 1px solid var(--cds-border);
138
+ }
139
+ tr:last-child td { border-bottom: none; }
140
+ th {
141
+ background: var(--cds-gray-40);
142
+ color: var(--cds-text-secondary);
143
+ font-weight: 600;
144
+ }
145
+ tbody tr:nth-child(even) td { background: var(--cds-gray-10); }
146
+ .card {
147
+ margin: 12px 0;
148
+ padding: 12px 14px;
149
+ border: 1px solid var(--cds-border);
150
+ border-radius: var(--radius-xl);
151
+ background: var(--cds-gray-10);
152
+ box-shadow: var(--cds-shadow-sm);
153
+ }
154
+ .card > :first-child { margin-top: 0; }
155
+ pre, code { font-family: var(--mono); font-size: 0.8rem; }
156
+ code {
157
+ padding: 1px 5px;
158
+ border-radius: var(--radius-md);
159
+ background: var(--cds-gray-40);
160
+ color: var(--cds-gray-850);
161
+ }
162
+ .code-block {
163
+ position: relative;
164
+ margin: 10px 0 16px;
165
+ }
166
+ pre {
167
+ margin: 10px 0 16px;
168
+ padding: 14px 16px;
169
+ background: #1e1e1d;
170
+ color: #f5f5f5;
171
+ border-radius: var(--radius-xl);
172
+ border: 1px solid var(--cds-gray-850);
173
+ overflow: auto;
174
+ white-space: pre-wrap;
175
+ word-break: break-word;
176
+ }
177
+ .code-block pre {
178
+ margin: 0;
179
+ padding-top: 40px;
180
+ }
181
+ pre code { background: transparent; color: inherit; padding: 0; }
182
+ .copy-btn {
183
+ position: absolute;
184
+ top: 8px;
185
+ right: 8px;
186
+ z-index: 1;
187
+ padding: 4px 10px;
188
+ font: 500 0.72rem/1.4 var(--font);
189
+ color: var(--cds-gray-600);
190
+ background: var(--cds-gray-10);
191
+ border: 1px solid var(--cds-border-strong);
192
+ border-radius: var(--radius-md);
193
+ cursor: pointer;
194
+ box-shadow: var(--cds-shadow-sm);
195
+ }
196
+ .copy-btn:hover {
197
+ color: var(--brand);
198
+ border-color: color-mix(in srgb, var(--cds-clay) 45%, var(--cds-border-strong));
199
+ background: var(--cds-clay-light);
200
+ }
201
+ .copy-btn.copied {
202
+ color: var(--brand);
203
+ border-color: color-mix(in srgb, var(--cds-clay) 45%, transparent);
204
+ background: var(--cds-clay-light);
205
+ }
206
+ ul { padding-left: 1.2rem; }
207
+ footer {
208
+ margin-top: 36px;
209
+ padding-top: 14px;
210
+ border-top: 1px solid var(--cds-border);
211
+ color: var(--cds-text-muted);
212
+ font-size: 0.75rem;
213
+ }
214
+ /* Explicitly no logo / brand mark images */
215
+ @media print {
216
+ body { background: #fff; }
217
+ .sheet {
218
+ margin: 0; max-width: none;
219
+ border: none; box-shadow: none; border-radius: 0; padding: 0;
220
+ }
221
+ h2, .card, table, pre { break-inside: avoid; }
222
+ }
223
+ </style>
224
+ </head>
225
+ <body>
226
+ <article class="sheet">
227
+ <div class="accent-bar" aria-hidden="true"></div>
228
+ <header>
229
+ <h1>{{DOC_TITLE}}</h1>
230
+ <p class="status">{{STATUS_LINE}}</p>
231
+ <dl class="meta">
232
+ <dt>项目 / 模块</dt><dd>{{PROJECT_MODULE}}</dd>
233
+ <dt>系统与客户端</dt><dd>{{SYSTEM_CLIENT}}</dd>
234
+ <dt>需求提出人</dt><dd>{{REQUESTER}}</dd>
235
+ <dt>类型</dt><dd>{{FS_TYPE}}</dd>
236
+ <dt>更新</dt><dd>{{UPDATED}}</dd>
237
+ </dl>
238
+ </header>
239
+ <div class="body">
240
+ {{BODY_HTML}}
241
+ </div>
242
+ <footer>
243
+ Generated by SapBuddy
244
+ </footer>
245
+ </article>
246
+ <script>
247
+ (function () {
248
+ function getCodeText(pre) {
249
+ var code = pre.querySelector("code");
250
+ return (code || pre).textContent;
251
+ }
252
+ function wrapPre(pre) {
253
+ if (pre.closest(".code-block")) return;
254
+ var wrap = document.createElement("div");
255
+ wrap.className = "code-block";
256
+ pre.parentNode.insertBefore(wrap, pre);
257
+ wrap.appendChild(pre);
258
+ var btn = document.createElement("button");
259
+ btn.type = "button";
260
+ btn.className = "copy-btn";
261
+ btn.setAttribute("aria-label", "复制代码");
262
+ btn.textContent = "复制";
263
+ btn.addEventListener("click", function () {
264
+ var text = getCodeText(pre);
265
+ function ok() {
266
+ btn.textContent = "已复制";
267
+ btn.classList.add("copied");
268
+ setTimeout(function () {
269
+ btn.textContent = "复制";
270
+ btn.classList.remove("copied");
271
+ }, 1500);
272
+ }
273
+ function fail() {
274
+ btn.textContent = "失败";
275
+ setTimeout(function () { btn.textContent = "复制"; }, 1500);
276
+ }
277
+ if (navigator.clipboard && navigator.clipboard.writeText) {
278
+ navigator.clipboard.writeText(text).then(ok).catch(function () {
279
+ try {
280
+ var ta = document.createElement("textarea");
281
+ ta.value = text;
282
+ ta.setAttribute("readonly", "");
283
+ ta.style.position = "fixed";
284
+ ta.style.left = "-9999px";
285
+ document.body.appendChild(ta);
286
+ ta.select();
287
+ document.execCommand("copy");
288
+ document.body.removeChild(ta);
289
+ ok();
290
+ } catch (e) { fail(); }
291
+ });
292
+ } else {
293
+ try {
294
+ var ta2 = document.createElement("textarea");
295
+ ta2.value = text;
296
+ ta2.setAttribute("readonly", "");
297
+ ta2.style.position = "fixed";
298
+ ta2.style.left = "-9999px";
299
+ document.body.appendChild(ta2);
300
+ ta2.select();
301
+ document.execCommand("copy");
302
+ document.body.removeChild(ta2);
303
+ ok();
304
+ } catch (e2) { fail(); }
305
+ }
306
+ });
307
+ wrap.appendChild(btn);
308
+ }
309
+ function init() {
310
+ document.querySelectorAll("pre").forEach(wrapPre);
311
+ }
312
+ if (document.readyState === "loading") {
313
+ document.addEventListener("DOMContentLoaded", init);
314
+ } else {
315
+ init();
316
+ }
317
+ })();
318
+ </script>
319
+ </body>
320
+ </html>
@@ -0,0 +1,93 @@
1
+ <!-- 写作约定(渲染时不可见,生成后删除本段)
2
+ - 只写事实与决定。建议标【建议】;未定标【待确认 Q-xx】;不适用写【不适用:原因】。
3
+ - 编号:R 业务规则、T 验收、Q 待确认、E 证据、M 消息。只在本文件内唯一,不重排。
4
+ - 字段用业务名,就地写清类型、必填、取值来源与格式。同一字段在不同位置格式不同时,各自写清换算。
5
+ - 每条规则必须能判真假,并且能被至少一个 T 用例验证。
6
+ - 不写"不得/禁止/注意"类训诫——生成纪律由技能规则约束,不进正文。
7
+ - 拿不准的内容不要写成事实;宁可留 Q,也不要补一个看似合理的值。
8
+ -->
9
+
10
+ # FS:<需求名称>(<类型>)
11
+
12
+ 状态:草稿 · 待确认 <N> 项 · 更新 <YYYY-MM-DD>
13
+ 项目 / 模块:<项目名 · 模块> | 系统与客户端:<连接名 / Client> | 需求提出人:<角色或姓名>
14
+
15
+ ## 1. 需求与范围
16
+
17
+ **背景与问题**
18
+ <业务现在遇到什么问题,具体表现是什么。>
19
+
20
+ **业务目标**
21
+ <期望达到的可观察结果,而不是"实现某功能"。>
22
+
23
+ **在范围内**
24
+ <业务对象、组织范围、业务入口、时间范围。>
25
+
26
+ **不在范围内**
27
+ <明确排除的部分。>
28
+
29
+ **依赖与前置条件**
30
+ <需要先具备的主数据、配置、外部系统、相关需求。>
31
+
32
+ ## 2. 现状与差异
33
+
34
+ | 环节 | 系统当前行为 | 依据 |
35
+ | --- | --- | --- |
36
+ | <业务环节> | <当前实际怎么处理> | E-01 / 需求确认 |
37
+ | | | |
38
+
39
+ **差异说明**:<这次要改变什么,哪些保持不变。>
40
+
41
+ ## 3. <类型专项:插入 02–05 之一>
42
+
43
+ ## 4. 业务规则
44
+
45
+ ### R-01 <规则名称>
46
+
47
+ - **条件**:<可判真假的条件;涉及金额数量时写清币种、单位、精度、含税口径。>
48
+ - **结果**:<系统应表现出的行为:阻止 / 提示 / 赋值 / 生成单据。>
49
+ - **异常**:<缺失、为空、越界、重复时怎么处理;不命中任何条件时怎样。>
50
+ - **依据**:<业务确认的时间与场合 / E-xx / Q-xx。建议未获确认时写【建议】。>
51
+ - **验收**:T-01
52
+
53
+ <多于一条规则时按 R-02、R-03 复制。规则之间有优先级时,在上一条的「条件」里写清。>
54
+
55
+ ## 5. 权限、消息与运行
56
+
57
+ **权限**
58
+ <角色、组织范围、权限对象;未核实写 Q。不新增权限时写明沿用哪个标准权限。>
59
+
60
+ **消息**
61
+
62
+ | 编号 | 触发条件 | 用户看到的文案 |
63
+ | --- | --- | --- |
64
+ | M-01 | R-01 不通过 | <可直接给用户看的文字> |
65
+
66
+ **运行**
67
+ <前台/后台、频率、单次数据量、时效要求;无特殊要求写「无特殊要求」。>
68
+
69
+ ## 6. 验收
70
+
71
+ ### T-01 <场景名称>
72
+
73
+ - **前置**:<可重复准备的初始状态。>
74
+ - **输入**:<具体取值或操作步骤。>
75
+ - **预期**:<可核对的结果:值、状态、消息、是否保存成功。>
76
+
77
+ <边界、异常、重复提交各有独立用例,按 T-02、T-03 复制。只在真实执行后补记结果。>
78
+
79
+ ## 7. 待确认
80
+
81
+ | 编号 | 问题 | 谁来定 | 影响 |
82
+ | --- | --- | --- | --- |
83
+ | Q-01 | <一个具体问题,不是话题> | <业务 / 技术 / 系统查询> | <影响哪条规则或测试> |
84
+
85
+ ## 附录 A 取证记录
86
+
87
+ | 编号 | 查了什么 | 结果 | 限制与结论 |
88
+ | --- | --- | --- | --- |
89
+ | E-01 | <工具与参数 / 对象与字段 / 查询条件与行数上限> | <客观返回:行数、字段、错误> | <这份证据能证明什么,不能证明什么> |
90
+
91
+ <只记录实际做过的查询。零行 ≠ 对象不存在;样本符合 ≠ 全量正确;对象存在 ≠ 时点与行为已验证。>
92
+
93
+ Generated by SapBuddy
@@ -0,0 +1,60 @@
1
+ <!-- 本片段插入骨架第3节。功能报表会改变业务状态:除查询与展示外,必须写清执行单元、
2
+ 哪些记录共同成功、失败后停在哪里。 -->
3
+
4
+ ## 3. 功能设计
5
+
6
+ ### 3.1 业务动作
7
+
8
+ | 动作 | 业务效果 | 前置状态 | 目标状态 |
9
+ | --- | --- | --- | --- |
10
+ | <批准 / 过账 / 创建 / 重处理> | <具体改变什么业务内容> | <执行前必须满足什么> | <成功后是什么状态> |
11
+
12
+ **实现方式与取舍**:<标准事务码 / 标准功能 / 自建程序;为何选这个;标准功能能否满足。未验证标 Q。>
13
+
14
+ - **不包含的动作**:<明确排除,避免开发自行扩大范围。>
15
+ - **前置确认**:<正式执行前向用户展示哪些对象、数量、金额与不可逆影响。>
16
+
17
+ ### 3.2 输入与候选
18
+
19
+ | 条件 / 字段 | 含义 | 技术名称 | 类型 | 必填 | 输入形式 | 默认值 |
20
+ | --- | --- | --- | --- | --- | --- | --- |
21
+ | 工厂 | 限制可处理工厂 | WERKS | CHAR(4) | 是 | 多选,F4 | 用户默认工厂 |
22
+
23
+ 上面一行为填写示范,生成实际 FS 时替换。
24
+
25
+ - **候选来源**:<从哪些对象、按什么条件筛出可处理记录;关联基数是 1:1 还是 1:N。>
26
+ - **已处理对象**:<是否仍出现在列表中;以什么状态显示;还能执行哪些动作。>
27
+ - **列表到执行的失效**:<展示后状态或数量发生变化时,执行前如何重新校验。>
28
+
29
+ ### 3.3 校验
30
+
31
+ | 顺序 | 校验层级 | 条件 | 未通过时 | 消息 |
32
+ | --- | --- | --- | --- | --- |
33
+ | 1 | 字段 | <…> | 阻止该行 | M-01 |
34
+ | 2 | 单据 | <…> | 阻止整张单据 | M-02 |
35
+ | 3 | 批次 | <…> | 阻止整批 | M-03 |
36
+
37
+ 校验顺序决定用户先看到哪条错误,必须写明;不同层级失败的阻断范围不同。
38
+
39
+ ### 3.4 执行单元与部分成功
40
+
41
+ - **执行单元**:<一行、一张单据还是整批;输入行与执行单元的对应关系。>
42
+ - **单据内原子性**:<同一单据多行是否必须整体成功;不适用时写原因。>
43
+ - **跨单据策略**:<一张失败后其余继续或停止;已成功的如何保留与告知。>
44
+ - **结果记录**:<逐行还是逐单据给出结果;成功与失败数量口径。>
45
+ - **重新处理**:<再次执行时如何排除已成功对象。>
46
+
47
+ ### 3.5 重复、并发与结果未知
48
+
49
+ - **重复判定键**:<哪些业务字段代表同一次意图;不能用每次新生成的批次号。>
50
+ - **重复提交**:<双击、重复导入、后台重跑时分别怎样。>
51
+ - **并发**:<哪些对象不能被同时处理;冲突时拒绝、排队还是重新校验。>
52
+ - **结果未知**:<超时或断连后,先用什么核实真实结果,再决定是否重试。>
53
+ - **恢复起点**:<从未处理单元恢复,还是从失败步骤恢复。>
54
+
55
+ ### 3.6 执行方式
56
+
57
+ - **前台 / 后台**:<是否都支持;不支持时说明。>
58
+ - **试运行**:<是否需要、系统是否具备;能模拟哪些校验、绝不能发生哪些写入。>
59
+ - **数据量与中断**:<单次处理量、时效;取消时已成功部分如何处理。>
60
+ - **日志**:<记录业务键、动作、结果、时间;保留要求。>
@@ -0,0 +1,92 @@
1
+ <!-- 本片段插入骨架第3节。只写与"消息怎么进出、字段怎么换算、什么算成功"有关的内容;
2
+ 权限、消息文案、验收、证据分别在骨架第5、6节和附录A。 -->
3
+
4
+ ## 3. 接口设计
5
+
6
+ ### 3.1 交互边界
7
+
8
+ | 项目 | 定义 |
9
+ | --- | --- |
10
+ | 业务用途与成功标志 | <对方拿到什么、能据此完成什么业务;什么结果才算这次交互成功> |
11
+ | 调用发起方 → 被调用方 | <哪边主动发起> |
12
+ | 业务数据源 → 业务数据目标 | <数据从哪个系统产生、最终落在哪里。可能与调用方向相反> |
13
+ | 协议 / 同步或异步 | <已定的协议;异步时说明返回的是受理还是完成> |
14
+ | 触发方式与频率 | <业务事件 / 定时 / 人工;时间窗口与时区> |
15
+ | 备选方案与取舍 | <考虑过的其它做法,为什么选这个;没有则写「无」> |
16
+
17
+ ### 3.2 消息与字段
18
+
19
+ 消息:<名称>(请求 / 响应 / 回调),方向 <A → B>
20
+
21
+ | 路径 | 含义 | 类型(长度) | 必填 | 取值来源与换算 | 空值处理 |
22
+ | --- | --- | --- | --- | --- | --- |
23
+ | Header.OrderId | 采购订单号 | CHAR(10) | 是 | 取自 EKKO-EBELN,补足前导零 | 为空即拒绝 |
24
+ | Items[].Price | 行项目净价 | DEC(11,2) | 是 | EKPO-NETPR,按价格单位换算为单价 | 0 与空分别处理:0 合法,空拒绝 |
25
+ | Items[].DelivDate | 交货日期 | CHAR(10) | 否 | EKPO-EINDT,`YYYY-MM-DD` | 空表示未定,不参与计算 |
26
+
27
+ <`Items[]` 表示重复集合,路径在全文保持一致。多级结构按实际层级展开,不限制层数。
28
+ 上面三行是填写示范,生成实际 FS 时替换。>
29
+
30
+ 固定部分:编码 <UTF-8 / 其它>;日期格式 <…>;标识类字段保留前导零 <是/否>。
31
+
32
+ ### 3.3 传输范围
33
+
34
+ | 项目 | 规则 |
35
+ | --- | --- |
36
+ | 全量 / 增量 | <哪一种;两者都要时写清切换条件> |
37
+ | 筛选条件 | <组织、业务类型、状态等;可与增量叠加> |
38
+ | 增量依据 | <变更时间 / 序号 / 事件;是否覆盖新增、修改、删除、冲销> |
39
+ | 边界与迟到 | <起止是否包含、时区、同时间戳的次序、迟到记录怎么处理> |
40
+ | 水位推进 | <在哪个节点推进:发送成功 / 受理成功 / 业务成功> |
41
+ | 空批次 | <没有数据时是否发送、如何记录> |
42
+ | 首次与补传 | <初始范围、历史补传、断点恢复> |
43
+
44
+ ### 3.4 结果与失败
45
+
46
+ | 场景 | 期望处理 | 由谁确认结果 |
47
+ | --- | --- | --- |
48
+ | 正常完成 | <响应里返回什么,对端据此判断什么> | <接收方 / 人工核对> |
49
+ | 重复请求 | <业务键是什么;再次到达时返回原结果还是拒绝> | |
50
+ | 同键不同内容 | <比较哪些字段;冲突怎么处理> | |
51
+ | 部分成功 | <业务原子单元是一行、一张单据还是整批;已成功部分是否保留> | |
52
+ | 超时或结果未知 | <先用什么核对真实结果;确认前是否允许重试> | |
53
+ | 需要补偿 | <SAP 与对端各自的实际状态;谁来修> | |
54
+
55
+ 结论层次写清楚:<传输到达 / 已受理 / 业务完成>分别以什么为证据——三者不能相互替代。
56
+
57
+ ### 3.5 日志与对账
58
+
59
+ - **日志**:<记录哪些关联标识、状态、业务编号、错误摘要;保留多久。>
60
+ - **对账**:<用哪些业务键核对数量或金额,在什么时间窗口,容差多少,差异由谁处理。>
61
+ - **技术对象**:<函数 / API / 服务的候选名称与用途,标清是已核实还是待验证。>
62
+ ### 3.6 样例 JSON(归档必出)
63
+
64
+ 按 3.2 字段表整理**一份可联调的示例**(虚构但类型/必填/格式与表一致)。HTML 归档时把下列代码块原样放入「样例 JSON」节。
65
+
66
+ **请求示例**
67
+
68
+ ```json
69
+ {
70
+ "Header": {
71
+ "OrderId": "4500001234"
72
+ },
73
+ "Items": [
74
+ {
75
+ "Price": 12.50,
76
+ "DelivDate": "2026-11-01"
77
+ }
78
+ ]
79
+ }
80
+ ```
81
+
82
+ **响应示例**
83
+
84
+ ```json
85
+ {
86
+ "Status": "ACCEPTED",
87
+ "BusinessKey": "4500001234",
88
+ "Message": "受理成功"
89
+ }
90
+ ```
91
+
92
+ 说明:路径、嵌套与 3.2 保持一致;空值/0 按表中「空值处理」示范;多消息(请求/响应/回调)各自给一组。未定字段不要编造业务值,标【待确认】或从示例中去掉。
@@ -0,0 +1,55 @@
1
+ <!-- 本片段插入骨架第3节。查询报表只读;若某个操作会改变业务状态,改用功能报表片段。 -->
2
+
3
+ ## 3. 报表设计
4
+
5
+ ### 3.1 输出与粒度
6
+
7
+ | 项目 | 定义 |
8
+ | --- | --- |
9
+ | 报表用途 | <使用者据此判断什么、随后做什么> |
10
+ | 一行代表什么 | <一张单据 / 一个行项目 / 一个期间汇总> |
11
+ | 业务唯一键 | <哪些字段组合唯一确定一行> |
12
+ | 多种视图 | <明细与汇总各自定义粒度;没有则写「仅明细」> |
13
+ | 数据时点 | <查询时点快照 / 实时状态 / 某个业务截止点> |
14
+ | 实现方式与取舍 | <标准事务码 / 标准报表 / 自建程序;为何选这个;标准功能能否满足> |
15
+
16
+ ### 3.2 选择条件
17
+
18
+ | 字段 | 含义 | 技术名称 | 类型 | 必填 | 输入形式 | 默认值 | 校验 |
19
+ | --- | --- | --- | --- | --- | --- | --- | --- |
20
+ | 工厂 | 限制工厂范围 | WERKS | CHAR(4) | 否 | 多选,F4 | <无 / 用户默认> | 必须属于用户权限范围 |
21
+
22
+ - **空值含义**:<未输入表示"不限"还是"必须输入";两者必须选一个。>
23
+ - **条件依赖**:<哪些条件互斥、成组必填、受上级条件约束。>
24
+ - **日期边界**:<用哪个业务日期、起止是否包含、时区。>
25
+ - **最小筛选范围**:<允许全量按组织范围执行,还是必须至少给一个条件。>
26
+
27
+ ### 3.3 输出字段
28
+
29
+ | 显示名 | 来源 / 计算 | 类型(长度,小数) | 格式 | 空值与零的显示 |
30
+ | --- | --- | --- | --- | --- |
31
+ | 采购订单号 | EKKO-EBELN | CHAR(10) | 去前导零,左对齐 | — |
32
+ | 订单净额 | SUM(EKPO-NETWR) | DEC(15,2) | 右对齐,2 位小数 | 无非空行时显示 0.00,不显示留白 |
33
+
34
+ <字段较多时按视图拆表;同一字段在不同视图格式不同,各自写清。>
35
+
36
+ ### 3.4 取数与关联
37
+
38
+ | 来源 | 用于 | 关联键 | 基数 | 无匹配 | 多匹配 |
39
+ | --- | --- | --- | --- | --- | --- |
40
+ | EKKO | 抬头 | EBELN | 1:1 | 排除该行 | 不应发生,出现则报错 |
41
+ | EKPO | 行项目 | EBELN | 1:N | 保留抬头、行值留空 | 正常展开 |
42
+
43
+ - **排除状态**:<删除、取消、冲销、冻结等状态是否纳入;逐个写清。>
44
+ - **当前值 vs 历史值**:<名称、组织归属、价格取当期值还是业务发生时值。>
45
+ - **重复放大风险**:<一对多关联后金额是否会被重复累加;如何避免。>
46
+
47
+ ### 3.5 计算与核对
48
+
49
+ - **计算顺序**:<过滤 → 换算 → 关联 → 汇总,写明先后。>
50
+ - **币种与单位**:<换算汇率类型与取值日期;不换算时说明理由。>
51
+ - **舍入**:<在哪一层舍入、保留几位;累加用未舍入值还是舍入值。>
52
+ - **比率与累计**:<分子分母、期间;不得对百分比直接相加。>
53
+ - **空值传播**:<关键字段缺失时该行不可计算,显示为空而不是 0。>
54
+ - **对账依据**:<用哪张标准报表或独立查询核对;容差与差异说明。>
55
+ - **数据量与时效**:<典型行数、峰值、可接受的响应时间;超出上限时如何提示。>
@@ -0,0 +1,34 @@
1
+ # HTML 归档交付
2
+
3
+ 企业归档除 Markdown(给 AI / 协作)外,**必须同时**生成一份 HTML。
4
+
5
+ ## 产出物
6
+
7
+ 默认目录:`output/fs/<需求名>/`
8
+
9
+ | 文件 | 说明 |
10
+ | --- | --- |
11
+ | `FS.md` | 主规格(AI 友好) |
12
+ | `FS.html` | 归档版:用 `assets/templates/fs-archive.html` 填入 |
13
+ | `io-request.json` / `io-response.json`(仅接口类) | 与正文 3.6 样例一致;若有回调可再加 `io-callback.json` |
14
+
15
+ 用户指定路径时,在同一目录并列输出。
16
+
17
+ ## HTML 规则
18
+
19
+ 1. **禁止 logo**:不放公司徽标、项目 Logo、水印、背景品牌图;页眉只用文字标题与元信息。
20
+ 2. **自包含**:单文件;CSS 内联;无外链脚本/字体 CDN。
21
+ 3. **内容来源**:由已定稿的 `FS.md` 渲染为 HTML 片段填入 `{{BODY_HTML}}`;表格保留为 `<table>`;代码块用 `<pre>`。
22
+ 4. **版式**:清晰层级(h1–h3)、可打印(`@media print` 已在模板);不模仿客户 Word 封面里的企业 VI。
23
+ 5. **替换占位符**:`{{DOC_TITLE}}` `{{STATUS_LINE}}` `{{PROJECT_MODULE}}` `{{SYSTEM_CLIENT}}` `{{REQUESTER}}` `{{FS_TYPE}}` `{{UPDATED}}` `{{BODY_HTML}}`;交付稿不得残留 `{{...}}`。
24
+
25
+ ## 接口类 JSON
26
+
27
+ - 字段表(3.2)是权威;JSON 样例必须与路径/类型/必填/空值处理一致。
28
+ - 至少各一份请求与响应;异步/回调按 3.1 再补。
29
+ - JSON 文件与 HTML 内「样例 JSON」节内容相同,便于联调拷贝。
30
+ - 未知字段不编造:留 Q,或从样例省略并在说明里写清。
31
+
32
+ ## 生成顺序
33
+
34
+ 定稿 Markdown →(接口类)整理 JSON 文件 → 填 HTML 模板 → 自检无 logo、无占位符、接口样例齐全。
@@ -0,0 +1,163 @@
1
+ <!-- 示例文件:需求、数据、对象名均为虚构,仅用于演示填写方式与详略程度。
2
+ 其中的数值和技术对象未在真实系统验证,不能作为任何项目的设计依据。 -->
3
+
4
+ # FS:采购订单保存校验(增强)
5
+
6
+ 状态:草稿 · 待确认 4 项 · 更新 2026-10-28
7
+ 项目 / 模块:示例项目 · MM-PUR | 系统与客户端:DEV / 100 | 需求提出人:采购部
8
+
9
+ ## 1. 需求与范围
10
+
11
+ **背景与问题**
12
+ 采购订单由多人分头维护,保存时既不校验行项目交货日期与抬头凭证日期的先后,也不识别大额订单。结果有两类问题:交货日期早于凭证日期的订单进入系统,后续 MRP 交期推算出负数;超过 10 万元的订单未走高额审批,靠财务事后抽查发现。
13
+
14
+ **业务目标**
15
+ 保存时拦截日期倒挂的订单;超过阈值时要求补审批标识。上线后不再新增这两类错误数据。
16
+
17
+ **在范围内**
18
+ ME21N / ME22N 的保存动作,行项目层面,公司代码 1000 与 2000。
19
+
20
+ **不在范围内**
21
+ 已保存订单的历史数据修正;审批流本身(沿用既有工作流);采购申请。
22
+
23
+ **依赖与前置条件**
24
+ 审批标识的存放字段未定(Q-02);接口创建采购订单是否同样校验未定(Q-03)。
25
+
26
+ ## 2. 现状与差异
27
+
28
+ | 环节 | 系统当前行为 | 依据 |
29
+ | --- | --- | --- |
30
+ | ME21N 保存 | 不校验 EKPO-EINDT 与 EKKO-BEDAT 的先后,日期倒挂可正常保存 | E-01 |
31
+ | 大额订单 | 无金额阈值判断,也没有审批标识相关校验 | E-02 |
32
+ | 增强点候选 | 候选 BAdI `ME_PROCESS_PO_CUST` 的 CHECK 方法,尚未验证本系统是否实现及其调用时点 | E-03 |
33
+
34
+ **差异说明**:两条校验在现状中都不存在,属于新增;既有字段校验、编号分配、后续审批流保持不变。
35
+
36
+ ## 3. 增强设计
37
+
38
+ ### 3.1 改变的行为
39
+
40
+ | 动作 | 现状 | 目标 | 保持不变 |
41
+ | --- | --- | --- | --- |
42
+ | 保存采购订单 | 校验通过即写库 | 先执行 R-01、R-02,不通过则不写库 | 既有字段校验、订单编号分配、审批流触发 |
43
+
44
+ **实施方式**:两条都是保存前的阻断式校验,标准配置无法覆盖,走增强。CHECK 方法是否在写库前调用,待技术确认(E-03)。
45
+
46
+ ### 3.2 触发点
47
+
48
+ | 入口 | 时点 | 触发前状态 | 取得的数据 |
49
+ | --- | --- | --- | --- |
50
+ | ME21N / ME22N 保存 | 抬头与行项目校验阶段,写库之前 | 订单未保存 | 用户当前输入值,含尚未落库的修改 |
51
+ | 接口 / 后台创建订单 | 待确认(Q-03) | — | — |
52
+
53
+ - **覆盖入口**:前台确定覆盖;接口与后台未确认。
54
+ - **新旧值**:使用当前输入值,不使用数据库旧值;修改行项目后重新校验。
55
+ - **未保存数据**:新增行项目的 EINDT 尚未落库,以屏幕输入为准。
56
+ - **派生值**:标准逻辑在保存时会重新计算净价与税额,不影响本规则的判断字段。
57
+
58
+ ### 3.3 适用与排除
59
+
60
+ | 项目 | 定义 |
61
+ | --- | --- |
62
+ | 命中条件 | 公司代码 1000 / 2000;订单类型 NB |
63
+ | 排除条件 | 退货订单是否排除待定(Q-04);当前按不排除处理 |
64
+ | 与其它规则的优先级 | R-01 先于 R-02;两条都不通过时先报 R-01 |
65
+ | 不命中时 | 维持原有保存行为 |
66
+ | 既有校验先失败时 | 不再执行本增强,用户先处理标准报错 |
67
+
68
+ ### 3.4 处理逻辑
69
+
70
+ 保存触发后,逐行项目执行 R-01;全部行通过后按整单执行 R-02。任一规则不通过即中止保存,消息中指出具体行号。变量取值、精度与舍入口径见各规则的「条件」。
71
+
72
+ ### 3.5 重复触发与事务
73
+
74
+ | 场景 | 期望 |
75
+ | --- | --- |
76
+ | 同一操作多次触发 | 校验无写入副作用,可重复执行 |
77
+ | 保存后再次修改 | 重新校验全部行,不沿用上次结论 |
78
+ | 标准动作最终失败 | 本增强不产生独立数据,无需回滚 |
79
+ | 已调用外部系统 | 不适用:本增强不调用外部系统 |
80
+
81
+ ### 3.6 停用与回归
82
+
83
+ - **停用后**:恢复原有保存行为;已保存数据不回滚,也不批量修复。
84
+ - **在途业务**:不适用:校验不产生在途状态。
85
+ - **回归范围**:正常保存、行项目增删改、既有字段校验、审批流创建、公司代码 1000 与 2000 之外的订单不受影响。
86
+
87
+ ## 4. 业务规则
88
+
89
+ ### R-01 交货日期不得早于凭证日期
90
+
91
+ - **条件**:行项目 EKPO-EINDT < 抬头 EKKO-BEDAT,按行项目逐条判断;两个字段都是日期,比较到日,不含时间。
92
+ - **结果**:阻止保存,输出 M-01,消息中带出行号。
93
+ - **异常**:任一日期为空视为未填,不参与比较,交回标准逻辑处理;两个日期相同时视为通过。
94
+ - **依据**:业务确认(2026-10-27 需求确认会),确认口径为"相等不算倒挂"。
95
+ - **验收**:T-01、T-02
96
+
97
+ ### R-02 大额订单需补审批标识
98
+
99
+ - **条件**:整单金额 > 100,000.00。币种、含税口径、按整单还是行项目计算均未确定(Q-01);当前按"整单含税人民币"计算,标【建议】。
100
+ - **结果**:输出 M-02 提示补审批标识;阻断保存或仅提示,待定(Q-01),当前按【建议:阻断】处理。
101
+ - **异常**:金额缺失或无法换算时不判断,避免把缺失当成 0;仅提示不阻断时仍记录日志。
102
+ - **依据**:业务提出阈值 10 万元,口径未确认(Q-01、Q-02)。
103
+ - **验收**:T-03、T-04
104
+
105
+ ## 5. 权限、消息与运行
106
+
107
+ **权限**
108
+ 沿用标准采购订单维护权限,不新增权限对象;校验对所有能保存该订单类型的用户生效,不设例外放行。
109
+
110
+ **消息**
111
+
112
+ | 编号 | 触发条件 | 用户看到的文案 |
113
+ | --- | --- | --- |
114
+ | M-01 | R-01 不通过 | 第 &1 行交货日期早于凭证日期(&2),请修改后保存 |
115
+ | M-02 | R-02 不通过 | 订单金额超过 10 万元,请补充审批标识后再保存 |
116
+
117
+ **运行**
118
+ 前台保存时同步执行,单次涉及行项目通常少于 50 行;无特殊时效要求。
119
+
120
+ ## 6. 验收
121
+
122
+ ### T-01 交货日期早于凭证日期
123
+
124
+ - **前置**:可创建 NB 类型订单的用户,公司代码 1000。
125
+ - **输入**:凭证日期 2026-10-20,某行交货日期 2026-10-19。
126
+ - **预期**:不能保存,M-01 提示该行行号。
127
+
128
+ ### T-02 日期相同(边界)
129
+
130
+ - **前置**:同 T-01。
131
+ - **输入**:凭证日期与交货日期均为 2026-10-20。
132
+ - **预期**:可以保存。
133
+
134
+ ### T-03 金额恰好等于阈值(边界)
135
+
136
+ - **前置**:同 T-01。
137
+ - **输入**:整单含税金额 100,000.00。
138
+ - **预期**:不提示、可保存(当前建议:整单含税人民币、等于阈值不触发)。依赖 Q-01;结论变化则本用例失效。【阻塞:Q-01 未关闭】
139
+
140
+ ### T-04 金额超过阈值
141
+
142
+ - **前置**:同 T-01。
143
+ - **输入**:整单含税金额 100,000.01。
144
+ - **预期**:输出 M-02,并阻止保存(当前建议:阻断)。依赖 Q-01;结论变化则本用例失效。【阻塞:Q-01 未关闭】
145
+
146
+ ## 7. 待确认
147
+
148
+ | 编号 | 问题 | 谁来定 | 影响 |
149
+ | --- | --- | --- | --- |
150
+ | Q-01 | 阈值按含税还是不含税、整单还是行项目、是否换算人民币、等于阈值是否触发 | 采购部 | R-02、T-03 |
151
+ | Q-02 | 审批标识放在哪个字段,是否需要新增字段 | 采购部 + 技术 | R-02 能否实现、M-02 的判断依据 |
152
+ | Q-03 | 接口与后台创建订单是否同样校验 | 采购部 | 3.2 覆盖入口、T 用例范围 |
153
+ | Q-04 | 退货订单是否排除 | 采购部 | 3.3 排除条件 |
154
+
155
+ ## 附录 A 取证记录
156
+
157
+ | 编号 | 查了什么 | 结果 | 限制与结论 |
158
+ | --- | --- | --- | --- |
159
+ | E-01 | `get_abap_object_info` 查 EKKO、EKPO 字段结构 | EINDT 为 DATS(8),BEDAT 为 DATS(8),字段存在且类型一致 | 只证明字段存在;未验证标准保存逻辑里是否已有同类校验 |
160
+ | E-02 | `execute_data_query` 查近 3 个月公司代码 1000 的订单币种与净额分布,限 200 行 | 出现 CNY 与 USD 两种币种;其中 2 单净额超过 10 万 | 限 200 行,不代表全量与历史;未覆盖其它公司代码 |
161
+ | E-03 | `search_abap_objects` 查 `ME_PROCESS_PO_CUST` 相关对象 | 返回增强实现类候选 | 未验证调用时点、是否在写库前、是否覆盖全部入口;需技术确认 |
162
+
163
+ Generated by SapBuddy
@@ -0,0 +1,64 @@
1
+ # FS 合同(编号、状态、字段、证据)
2
+
3
+ ## 状态行
4
+
5
+ ```
6
+ 状态:草稿 · 待确认 <N> 项 · 更新 <YYYY-MM-DD>
7
+ 项目 / 模块:… | 系统与客户端:… | 需求提出人:…
8
+ ```
9
+
10
+ `<N>` 必须等于正文「待确认」表中 Q 的实际条数。
11
+
12
+ ## 编号(本文件内唯一,不重排)
13
+
14
+ | 前缀 | 含义 | 出现位置 |
15
+ | --- | --- | --- |
16
+ | R | 业务规则 | 第4节 |
17
+ | T | 验收用例 | 第6节 |
18
+ | Q | 待确认问题 | 第7节;正文可用【待确认 Q-xx】 |
19
+ | E | 取证记录 | 附录 A;正文「依据」列引用 |
20
+ | M | 消息 | 第5节消息表 |
21
+
22
+ 不使用旧编号:`REQ` / `BR` / `EV` / `TC` 等。
23
+
24
+ ## 字段写法
25
+
26
+ - 用**业务名**,就地写清:类型、必填、取值来源与格式、空值含义。
27
+ - 同一字段在不同位置格式不同时,各自写清换算。
28
+ - 不发明跨文档人造字段键(field/source/action/…)。
29
+
30
+ ## 规则与验收
31
+
32
+ 每条 R 必须有:条件 / 结果 / 异常 / 依据 / 验收(至少一个 T)。
33
+ 每条 T 必须有:前置 / 输入 / **可核对**的预期(不是「成功」)。
34
+ 依赖未关闭 Q 的用例:预期写清当前建议值,并标注「依赖 Q-xx;结论变化则本用例失效」;必要时标【阻塞】。
35
+
36
+ ## 依据列
37
+
38
+ - 系统取证 → `E-xx`
39
+ - 业务口述 / 会议确认 → 写「需求确认(日期或场合)」,**不要**伪装成 E
40
+ - 未核实建议 → 标【建议】
41
+
42
+ ## 标记
43
+
44
+ - 未知:【待确认 Q-xx】
45
+ - 不适用:【不适用:原因】
46
+ - 建议未确认:【建议】
47
+
48
+ ## 增强类章节分工
49
+
50
+ - 第2节:业务流程**整体**现状(含未改变部分)
51
+ - 第3节:只写**本次改变**的动作
52
+
53
+ ## 证据
54
+
55
+ 证据在附录 A(查了什么 / 结果 / 限制与结论)。正文引用编号,不把证据详情塞进第2/3节。
56
+ 修订历史:默认**不**进 FS 正文(版本由交付/流转平台承载)。若项目要求文档内签署追溯,再加「修订记录」小节——需产品确认后启用。
57
+
58
+ ## 页脚
59
+
60
+ 可保留 `Generated by SapBuddy`。生成后删除模板顶部 `<!-- 写作约定 -->` 注释块。
61
+
62
+ ## HTML 与接口 JSON
63
+
64
+ 定稿后必须按 `archive-html.md` 产出无 logo 的 `FS.html`;接口类必须附样例 JSON(正文 + 独立文件)。
@@ -0,0 +1,26 @@
1
+ # 与 grilling 集成
2
+
3
+ ## 怎么加载
4
+
5
+ 1. 需要追问业务决策时:用 Skill 工具加载 **`grilling`**(用户说 grill me / 拷问 / 追问决策时同样加载它,不再经别名)。
6
+ 2. 若宿主无法加载技能:在对话中**按 grilling 合同**执行(设计树、按 frontier 分轮、每问带推荐答案),并在答复里说明已降级。
7
+ 3. 不要把 grilling 全文复制进 FS;不要在 sap-fs 内另写一套追问算法。
8
+
9
+ ## 问什么 / 不问什么
10
+
11
+ - **问**:业务决策(阈值、含税口径、是否阻断、覆盖哪些入口、排除哪些场景…)。
12
+ - **不问**:能从系统查到的事实(表名、字段、现有逻辑)——先取证。
13
+
14
+ ## 与 FS 编号的映射
15
+
16
+ | grilling 结果 | 写入 FS |
17
+ | --- | --- |
18
+ | 用户确认的决定 | 落到对应 R / 第3节字段,依据写需求确认 |
19
+ | 用户说不知道 | `Q-xx`,继续做不依赖部分 |
20
+ | 推荐答案被同意 | 可写入正文;若仍弱确认可标【建议】直至正式确认 |
21
+
22
+ 每轮结束后把已定决策写回草稿结构,避免只留在对话里。
23
+
24
+ ## 停止条件
25
+
26
+ grilling 的 frontier 为空,且用户确认已达成共识 → 再进入生成与自检。未确认前不假装 FS 已定稿。
@@ -0,0 +1,13 @@
1
+ # 交付前检查
2
+
3
+ 1. 每条 R 有条件 / 结果 / 异常 / 依据 / 验收,且可判真假。
4
+ 2. 每个 T 有前置、输入、可核对预期;依赖 Q 的用例已标注依赖/阻塞。
5
+ 3. 每个 Q 有「谁来定」和「影响哪条规则」;状态行待确认数 = Q 条数。
6
+ 4. 每条 E 有限制;正文引用的 E 都存在;口述依据未伪装成 E。
7
+ 5. 第2节与第3节不矛盾;未改变部分写明保持原样。
8
+ 6. 无残留 `<...>` / `{{...}}` 槽位(示例文件里的尖括号示范也不得出现在交付稿)。
9
+ 7. 无训诫句;无旧编号;有 M 时与消息表一致。
10
+ 8. 查询/功能报表写了「实现方式与取舍」(标准功能 vs 自建)。
11
+ 9. 未授权任何 SAP 写入;落盘路径在允许范围(默认 `output/fs/...`)。
12
+ 10. 已产出 `FS.html`,无 logo、无 `{{...}}` 占位符。
13
+ 11. 接口类:正文 3.6 与 `io-request.json` / `io-response.json` 一致且可解析。
@@ -0,0 +1,30 @@
1
+ # 系统取证
2
+
3
+ ## 连接
4
+
5
+ 1. 连接不明确 → 先 `get_connected_systems`。
6
+ 2. 用户已指定 → 只用指定连接,不要默认第一个。
7
+ 3. 切换连接后 → 再次 `get_connected_systems` 确认后再查。
8
+
9
+ ## 可用工具(只读)
10
+
11
+ `get_sap_system_info`、`search_abap_objects`、`get_abap_object_info`、`get_abap_object_lines`、`execute_data_query`、`read_table_contents`、`find_where_used`。
12
+
13
+ 查询限定组织、日期与字段;只取少量样本。返回行数上限 ≠ 扫描范围小。
14
+
15
+ ## 记录四种状态
16
+
17
+ 已取得 / 零行 / 部分取得 / 失败。
18
+
19
+ - 零行 ≠ 对象不存在
20
+ - 失败 ≠ 没有该功能
21
+ - 字段存在 ≠ 逻辑可用
22
+ - 对象存在 ≠ 调用时点与入口已验证
23
+
24
+ 每种限制写进附录 E 的「限制与结论」。
25
+
26
+ ## 边界
27
+
28
+ - 本流程不创建、不激活、不修改 SAP 对象。
29
+ - abap_wiki / 知识库仅作定位;必须与系统查询分开标注。
30
+ - 未核实的对象名标「候选,待验证」,写入 E 或 Q,不写进已确认口径。
@@ -0,0 +1,21 @@
1
+ # 类型:增强
2
+
3
+ **判定**:在现有流程某时点补充或改变行为。
4
+
5
+ ## 取证重点
6
+
7
+ - 入口事务/接口、候选增强点调用时点(未验证则 E + Q)
8
+ - 相关字段与标准校验是否已覆盖部分规则
9
+ - 其它增强是否改同一字段
10
+
11
+ ## 追问方向
12
+
13
+ - 覆盖哪些入口(前台/后台/接口)
14
+ - 命中与排除条件、优先级
15
+ - 停用后与在途、回归范围
16
+
17
+ ## 专项检查
18
+
19
+ - 模板:`assets/templates/enhancement.md`
20
+ - 第2节整体现状 vs 第3节只写本次改变
21
+ - 详略对照:`references/example-enhancement.md`
@@ -0,0 +1,22 @@
1
+ # 类型:功能报表
2
+
3
+ **判定**:会改变业务状态(创建、过账、审批、重处理等)。
4
+
5
+ ## 取证重点
6
+
7
+ - 候选对象与可处理状态
8
+ - 现有校验/锁定/日志
9
+ - 标准功能能否覆盖
10
+
11
+ ## 追问方向
12
+
13
+ - 执行单元与部分成功策略
14
+ - 重复判定键(不能用每次新批次号)
15
+ - 结果未知时的核对顺序
16
+ - 试运行能力边界
17
+
18
+ ## 专项检查
19
+
20
+ - 模板:`assets/templates/functional-report.md`
21
+ - 3.2 表有实际字段行(非空表头)
22
+ - 实现方式与取舍已写
@@ -0,0 +1,27 @@
1
+ # 类型:接口
2
+
3
+ **判定**:数据在系统之间进出。
4
+
5
+ ## 取证重点
6
+
7
+ - 现有接口对象、消息结构、日志与对账表(若有)
8
+ - 业务键、增量水位、重复请求处理线索
9
+ - 候选技术对象标「待验证」
10
+
11
+ ## 追问方向(交给 grilling)
12
+
13
+ - 成功标志:传输到达 / 已受理 / 业务完成 各自证据
14
+ - 全量 vs 增量、迟到与空批次
15
+ - 部分成功原子单元、超时后如何核对再重试
16
+ - 对账键、容差、责任方
17
+
18
+ ## 专项检查
19
+
20
+ - 第3节用 `assets/templates/interface.md`
21
+ - 结论层次不得互相替代
22
+ - 备选方案与取舍已填写(无则写「无」)
23
+
24
+ ## 归档
25
+
26
+ - 必出 3.6 样例 JSON,并写 `io-request.json` / `io-response.json`。
27
+ - HTML 中原样收录样例,无 logo。
@@ -0,0 +1,21 @@
1
+ # 类型:查询报表
2
+
3
+ **判定**:只读;筛选、展示、导出。若操作会改业务状态 → 改用功能报表。
4
+
5
+ ## 取证重点
6
+
7
+ - 来源表/视图、关联基数、排除状态
8
+ - 选择条件与权限相关字段
9
+ - 标准报表/事务能否覆盖(写入「实现方式与取舍」)
10
+
11
+ ## 追问方向
12
+
13
+ - 一行代表什么、业务唯一键
14
+ - 空条件含义、最小筛选范围
15
+ - 币种/单位/舍入与对账依据
16
+ - 数据量与时效上限
17
+
18
+ ## 专项检查
19
+
20
+ - 模板:`assets/templates/query-report.md`
21
+ - 一对多关联后金额是否重复放大已说明