@dazitech/cli 3.0.7 → 3.0.8

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.
Files changed (68) hide show
  1. package/README.md +1 -1
  2. package/dist/clis/dazi-app.js +1 -1
  3. package/dist/clis/dazi-flow.js +1 -1
  4. package/dist/clis/dazi-onto.js +73 -22
  5. package/dist/clis/dazi.js +266 -171
  6. package/dist/docs/flow/flow-project-guide.md +1 -1
  7. package/dist/docs/guides/quickstart.md +18 -4
  8. package/dist/docs/guides/troubleshooting.md +1 -1
  9. package/dist/docs/guides/workspace-v3.md +43 -23
  10. package/dist/docs/index.json +9 -3
  11. package/dist/docs/onto/action-guide.md +3 -3
  12. package/dist/docs/onto/dazi_script_sdk_reference.md +178 -174
  13. package/dist/docs/onto/dazi_script_seed_data_guide.md +158 -155
  14. package/dist/docs/onto/function-guide.md +37 -10
  15. package/dist/docs/onto/space-management.md +3 -1
  16. package/dist/docs/onto//346/234/254/344/275/223/350/204/232/346/234/254/347/274/226/345/206/231/346/214/207/345/215/227.md +138 -34
  17. package/dist/docs/onto//346/234/254/344/275/223/350/247/204/345/210/222/346/214/207/345/215/227.md +73 -31
  18. package/dist/docs/onto//350/247/204/345/210/222/347/244/272/344/276/213_/344/272/247/345/223/201/351/224/200/345/224/256/346/234/254/344/275/223/350/247/204/345/210/222/346/226/271/346/241/210.md +497 -0
  19. package/dist/docs/onto//350/247/204/345/210/222/347/244/272/344/276/213_/345/210/251/346/266/246/345/210/206/346/236/220/346/234/254/344/275/223/346/226/271/346/241/210.md +597 -541
  20. package/dist/examples/index.json +202 -22
  21. package/dist/examples/onto/README.md +43 -0
  22. package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/function/profit_fn_account_breakdown.py +99 -0
  23. package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/function/profit_fn_budget_vs_actual.py +116 -0
  24. package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/function/profit_fn_cost_center_profit.py +85 -0
  25. package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/function/profit_fn_get_summary.py +76 -0
  26. package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/function/profit_fn_mom_analysis.py +86 -0
  27. package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/function/profit_fn_top_accounts.py +103 -0
  28. package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/function/profit_fn_yoy_analysis.py +86 -0
  29. package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/function/save_test_arguments.ps1 +27 -0
  30. package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/function/test_arguments/profit.fn.account_breakdown.json +10 -0
  31. package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/function/test_arguments/profit.fn.budget_vs_actual.json +10 -0
  32. package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/function/test_arguments/profit.fn.cost_center_profit.json +9 -0
  33. package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/function/test_arguments/profit.fn.get_summary.json +9 -0
  34. package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/function/test_arguments/profit.fn.mom_analysis.json +9 -0
  35. package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/function/test_arguments/profit.fn.top_accounts.json +11 -0
  36. package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/function/test_arguments/profit.fn.yoy_analysis.json +9 -0
  37. package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/setup/profit_ontology_init.py +521 -0
  38. package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/setup/profit_seed_data.py +213 -0
  39. package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/functions/README.md +25 -0
  40. package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/functions/sales_fn_channel_mix.py +86 -0
  41. package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/functions/sales_fn_customer_segmentation.py +123 -0
  42. package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/functions/sales_fn_get_summary.py +81 -0
  43. package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/functions/sales_fn_mom_analysis.py +90 -0
  44. package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/functions/sales_fn_region_breakdown.py +85 -0
  45. package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/functions/sales_fn_top_products.py +101 -0
  46. package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/functions/sales_fn_yoy_analysis.py +90 -0
  47. package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/functions/save_test_arguments.ps1 +25 -0
  48. package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/functions/test_arguments/sales.fn.channel_mix.json +8 -0
  49. package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/functions/test_arguments/sales.fn.customer_segmentation.json +10 -0
  50. package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/functions/test_arguments/sales.fn.get_summary.json +8 -0
  51. package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/functions/test_arguments/sales.fn.mom_analysis.json +8 -0
  52. package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/functions/test_arguments/sales.fn.region_breakdown.json +8 -0
  53. package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/functions/test_arguments/sales.fn.top_products.json +10 -0
  54. package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/functions/test_arguments/sales.fn.yoy_analysis.json +8 -0
  55. package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/setup/README.md +5 -0
  56. package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/setup/sales_ontology_init.py +403 -0
  57. package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/setup/sales_seed_data.py +124 -0
  58. package/dist/prompts/index.json +2 -2
  59. package/dist/prompts/onto/action-design.md +4 -1
  60. package/dist/prompts/onto/function-design.md +9 -2
  61. package/dist/prompts/onto/rule-seed.md +5 -1
  62. package/dist/prompts/onto/script-publish-run.md +72 -24
  63. package/package.json +1 -1
  64. package/dist/examples/onto/function/profit_fn_customer_segmentation.py +0 -117
  65. package/dist/examples/onto/function/profit_fn_mom_analysis.py +0 -89
  66. package/dist/examples/onto/function/profit_fn_top_products.py +0 -89
  67. package/dist/examples/onto/function/profit_fn_yoy_analysis.py +0 -89
  68. package/dist/examples/onto/setup/profit_ontology_init.py +0 -388
@@ -1,36 +1,216 @@
1
1
  {
2
- "version": "3.0.5",
3
- "updatedAt": "2026-05-25T00:00:00.000Z",
2
+ "version": "3.1.0",
3
+ "updatedAt": "2026-06-06T12:00:00.000Z",
4
4
  "examples": [
5
5
  {
6
- "id": "onto/setup/profit-ontology-init",
7
- "title": "利润分析本体初始化",
8
- "category": "onto-setup",
9
- "file": "onto/setup/profit_ontology_init.py"
6
+ "id": "onto/readme",
7
+ "title": "Onto 示例总览(利润 + 销售)",
8
+ "category": "onto",
9
+ "file": "onto/README.md"
10
10
  },
11
11
  {
12
- "id": "onto/function/profit-fn-yoy",
13
- "title": "利润同比分析 (YoY)",
14
- "category": "onto-function",
15
- "file": "onto/function/profit_fn_yoy_analysis.py"
12
+ "id": "onto/profit/setup/ontology-init",
13
+ "title": "利润分析 · 本体初始化",
14
+ "category": "onto-利润-初始化",
15
+ "file": "onto/利润示例/setup/profit_ontology_init.py"
16
16
  },
17
17
  {
18
- "id": "onto/function/profit-fn-mom",
19
- "title": "利润环比分析 (MoM)",
20
- "category": "onto-function",
21
- "file": "onto/function/profit_fn_mom_analysis.py"
18
+ "id": "onto/profit/setup/seed-data",
19
+ "title": "利润分析 · 演示灌数",
20
+ "category": "onto-利润-初始化",
21
+ "file": "onto/利润示例/setup/profit_seed_data.py"
22
22
  },
23
23
  {
24
- "id": "onto/function/profit-fn-top-products",
25
- "title": "产品利润排行",
26
- "category": "onto-function",
27
- "file": "onto/function/profit_fn_top_products.py"
24
+ "id": "onto/profit/function/get-summary",
25
+ "title": "利润分析 · 利润总览",
26
+ "category": "onto-利润-函数",
27
+ "file": "onto/利润示例/function/profit_fn_get_summary.py"
28
28
  },
29
29
  {
30
- "id": "onto/function/profit-fn-customer-segmentation",
31
- "title": "客户分层",
32
- "category": "onto-function",
33
- "file": "onto/function/profit_fn_customer_segmentation.py"
30
+ "id": "onto/profit/function/yoy",
31
+ "title": "利润分析 · 同比 (YoY)",
32
+ "category": "onto-利润-函数",
33
+ "file": "onto/利润示例/function/profit_fn_yoy_analysis.py"
34
+ },
35
+ {
36
+ "id": "onto/profit/function/mom",
37
+ "title": "利润分析 · 环比 (MoM)",
38
+ "category": "onto-利润-函数",
39
+ "file": "onto/利润示例/function/profit_fn_mom_analysis.py"
40
+ },
41
+ {
42
+ "id": "onto/profit/function/budget-vs-actual",
43
+ "title": "利润分析 · 预实对比",
44
+ "category": "onto-利润-函数",
45
+ "file": "onto/利润示例/function/profit_fn_budget_vs_actual.py"
46
+ },
47
+ {
48
+ "id": "onto/profit/function/account-breakdown",
49
+ "title": "利润分析 · 科目结构",
50
+ "category": "onto-利润-函数",
51
+ "file": "onto/利润示例/function/profit_fn_account_breakdown.py"
52
+ },
53
+ {
54
+ "id": "onto/profit/function/cost-center-profit",
55
+ "title": "利润分析 · 组织利润",
56
+ "category": "onto-利润-函数",
57
+ "file": "onto/利润示例/function/profit_fn_cost_center_profit.py"
58
+ },
59
+ {
60
+ "id": "onto/profit/function/top-accounts",
61
+ "title": "利润分析 · 科目 Top N",
62
+ "category": "onto-利润-函数",
63
+ "file": "onto/利润示例/function/profit_fn_top_accounts.py"
64
+ },
65
+ {
66
+ "id": "onto/profit/function/save-test-arguments",
67
+ "title": "利润分析 · 批量保存 test_arguments",
68
+ "category": "onto-利润-工具",
69
+ "file": "onto/利润示例/function/save_test_arguments.ps1"
70
+ },
71
+ {
72
+ "id": "onto/profit/test-args/get-summary",
73
+ "title": "test_arguments · profit.fn.get_summary",
74
+ "category": "onto-利润-测试参数",
75
+ "file": "onto/利润示例/function/test_arguments/profit.fn.get_summary.json"
76
+ },
77
+ {
78
+ "id": "onto/profit/test-args/yoy",
79
+ "title": "test_arguments · profit.fn.yoy_analysis",
80
+ "category": "onto-利润-测试参数",
81
+ "file": "onto/利润示例/function/test_arguments/profit.fn.yoy_analysis.json"
82
+ },
83
+ {
84
+ "id": "onto/profit/test-args/mom",
85
+ "title": "test_arguments · profit.fn.mom_analysis",
86
+ "category": "onto-利润-测试参数",
87
+ "file": "onto/利润示例/function/test_arguments/profit.fn.mom_analysis.json"
88
+ },
89
+ {
90
+ "id": "onto/profit/test-args/budget-vs-actual",
91
+ "title": "test_arguments · profit.fn.budget_vs_actual",
92
+ "category": "onto-利润-测试参数",
93
+ "file": "onto/利润示例/function/test_arguments/profit.fn.budget_vs_actual.json"
94
+ },
95
+ {
96
+ "id": "onto/profit/test-args/account-breakdown",
97
+ "title": "test_arguments · profit.fn.account_breakdown",
98
+ "category": "onto-利润-测试参数",
99
+ "file": "onto/利润示例/function/test_arguments/profit.fn.account_breakdown.json"
100
+ },
101
+ {
102
+ "id": "onto/profit/test-args/cost-center-profit",
103
+ "title": "test_arguments · profit.fn.cost_center_profit",
104
+ "category": "onto-利润-测试参数",
105
+ "file": "onto/利润示例/function/test_arguments/profit.fn.cost_center_profit.json"
106
+ },
107
+ {
108
+ "id": "onto/profit/test-args/top-accounts",
109
+ "title": "test_arguments · profit.fn.top_accounts",
110
+ "category": "onto-利润-测试参数",
111
+ "file": "onto/利润示例/function/test_arguments/profit.fn.top_accounts.json"
112
+ },
113
+ {
114
+ "id": "onto/sales/setup/ontology-init",
115
+ "title": "产品销售 · 本体初始化",
116
+ "category": "onto-销售-初始化",
117
+ "file": "onto/销售示例/setup/sales_ontology_init.py"
118
+ },
119
+ {
120
+ "id": "onto/sales/setup/seed-data",
121
+ "title": "产品销售 · 演示灌数",
122
+ "category": "onto-销售-初始化",
123
+ "file": "onto/销售示例/setup/sales_seed_data.py"
124
+ },
125
+ {
126
+ "id": "onto/sales/function/get-summary",
127
+ "title": "产品销售 · 销售概览",
128
+ "category": "onto-销售-函数",
129
+ "file": "onto/销售示例/functions/sales_fn_get_summary.py"
130
+ },
131
+ {
132
+ "id": "onto/sales/function/yoy",
133
+ "title": "产品销售 · 同比分析",
134
+ "category": "onto-销售-函数",
135
+ "file": "onto/销售示例/functions/sales_fn_yoy_analysis.py"
136
+ },
137
+ {
138
+ "id": "onto/sales/function/mom",
139
+ "title": "产品销售 · 环比分析",
140
+ "category": "onto-销售-函数",
141
+ "file": "onto/销售示例/functions/sales_fn_mom_analysis.py"
142
+ },
143
+ {
144
+ "id": "onto/sales/function/top-products",
145
+ "title": "产品销售 · Top 产品",
146
+ "category": "onto-销售-函数",
147
+ "file": "onto/销售示例/functions/sales_fn_top_products.py"
148
+ },
149
+ {
150
+ "id": "onto/sales/function/customer-segmentation",
151
+ "title": "产品销售 · 客户分层",
152
+ "category": "onto-销售-函数",
153
+ "file": "onto/销售示例/functions/sales_fn_customer_segmentation.py"
154
+ },
155
+ {
156
+ "id": "onto/sales/function/region-breakdown",
157
+ "title": "产品销售 · 区域分布",
158
+ "category": "onto-销售-函数",
159
+ "file": "onto/销售示例/functions/sales_fn_region_breakdown.py"
160
+ },
161
+ {
162
+ "id": "onto/sales/function/channel-mix",
163
+ "title": "产品销售 · 渠道结构",
164
+ "category": "onto-销售-函数",
165
+ "file": "onto/销售示例/functions/sales_fn_channel_mix.py"
166
+ },
167
+ {
168
+ "id": "onto/sales/function/save-test-arguments",
169
+ "title": "产品销售 · 批量保存 test_arguments",
170
+ "category": "onto-销售-工具",
171
+ "file": "onto/销售示例/functions/save_test_arguments.ps1"
172
+ },
173
+ {
174
+ "id": "onto/sales/test-args/get-summary",
175
+ "title": "test_arguments · sales.fn.get_summary",
176
+ "category": "onto-销售-测试参数",
177
+ "file": "onto/销售示例/functions/test_arguments/sales.fn.get_summary.json"
178
+ },
179
+ {
180
+ "id": "onto/sales/test-args/yoy",
181
+ "title": "test_arguments · sales.fn.yoy_analysis",
182
+ "category": "onto-销售-测试参数",
183
+ "file": "onto/销售示例/functions/test_arguments/sales.fn.yoy_analysis.json"
184
+ },
185
+ {
186
+ "id": "onto/sales/test-args/mom",
187
+ "title": "test_arguments · sales.fn.mom_analysis",
188
+ "category": "onto-销售-测试参数",
189
+ "file": "onto/销售示例/functions/test_arguments/sales.fn.mom_analysis.json"
190
+ },
191
+ {
192
+ "id": "onto/sales/test-args/top-products",
193
+ "title": "test_arguments · sales.fn.top_products",
194
+ "category": "onto-销售-测试参数",
195
+ "file": "onto/销售示例/functions/test_arguments/sales.fn.top_products.json"
196
+ },
197
+ {
198
+ "id": "onto/sales/test-args/customer-segmentation",
199
+ "title": "test_arguments · sales.fn.customer_segmentation",
200
+ "category": "onto-销售-测试参数",
201
+ "file": "onto/销售示例/functions/test_arguments/sales.fn.customer_segmentation.json"
202
+ },
203
+ {
204
+ "id": "onto/sales/test-args/region-breakdown",
205
+ "title": "test_arguments · sales.fn.region_breakdown",
206
+ "category": "onto-销售-测试参数",
207
+ "file": "onto/销售示例/functions/test_arguments/sales.fn.region_breakdown.json"
208
+ },
209
+ {
210
+ "id": "onto/sales/test-args/channel-mix",
211
+ "title": "test_arguments · sales.fn.channel_mix",
212
+ "category": "onto-销售-测试参数",
213
+ "file": "onto/销售示例/functions/test_arguments/sales.fn.channel_mix.json"
34
214
  },
35
215
  {
36
216
  "id": "flow/minimal-excel-python",
@@ -0,0 +1,43 @@
1
+ # Onto 内置示例
2
+
3
+ `dazi examples sync` 后位于 **`资源/examples/onto/`**。复制到业务项目的 **`项目/<业务名>/本体/ontos/<实现名>/`** 对应子目录后再改 `space_id` 与业务字段。
4
+
5
+ ## 利润示例(`利润示例/`)
6
+
7
+ | 路径 | 说明 | 规划文档 |
8
+ |------|------|----------|
9
+ | `setup/profit_ontology_init.py` | GL 域 init(科目/分录/预算表、Cube、对象、链接) | [规划示例_利润分析本体方案](../../docs/onto/规划示例_利润分析本体方案.md) |
10
+ | `setup/profit_seed_data.py` | 演示灌数(科目树、成本中心、实际分录、预算) | 同上 |
11
+ | `function/profit_fn_*.py` | 7 个 GL 分析函数(总览/同比/环比/预实/科目/组织/Top 科目) | 同上 |
12
+ | `function/test_arguments/*.json` | 各 `profit.fn.*` 默认测试入参 | 规划 §6.5 |
13
+ | `function/save_test_arguments.ps1` | 批量写入平台 `test_arguments` | 发布函数后执行 |
14
+
15
+ 发布后须 **`function run` 验证** → **`save-test-arguments`** 写入默认测试入参(见规划 §6.5)。
16
+
17
+ 推荐空间:`space__zlj`(以你的实现单元 README 为准)。
18
+
19
+ ## 销售示例(`销售示例/`)
20
+
21
+ | 路径 | 说明 | 规划文档 |
22
+ |------|------|----------|
23
+ | `setup/sales_ontology_init.py` | 产品销售 init(表间关系 + Cube + 本体) | [规划示例_产品销售本体规划方案](../../docs/onto/规划示例_产品销售本体规划方案.md) |
24
+ | `setup/sales_seed_data.py` | 演示灌数(产品/销售/维表) | 同上 |
25
+ | `functions/sales_fn_*.py` | 7 个销售分析函数 | 同上 |
26
+ | `functions/test_arguments/*.json` | 各 `function_id` 默认测试入参 | 与规划 §函数清单一致 |
27
+ | `functions/save_test_arguments.ps1` | 批量写入平台 `test_arguments` | 发布函数后执行 |
28
+
29
+ 推荐空间:`space__zlj`(以你的实现单元 README 为准)。
30
+
31
+ ## 复制示例
32
+
33
+ ```text
34
+ 资源/examples/onto/利润示例/setup/*.py → 项目/<业务>/本体/ontos/<实现>/setup/
35
+ 资源/examples/onto/利润示例/function/*.py → 项目/<业务>/本体/ontos/<实现>/functions/
36
+ 资源/examples/onto/利润示例/function/test_arguments/ → .../functions/test_arguments/
37
+
38
+ 资源/examples/onto/销售示例/setup/*.py → 项目/<业务>/本体/ontos/<实现>/setup/
39
+ 资源/examples/onto/销售示例/functions/*.py → 项目/<业务>/本体/ontos/<实现>/functions/
40
+ 资源/examples/onto/销售示例/functions/test_arguments/ → .../functions/test_arguments/
41
+ ```
42
+
43
+ 侧栏 **帮助 → 示例** 可按分类浏览;`dazi examples list --category onto-利润-函数` 可过滤。
@@ -0,0 +1,99 @@
1
+ """科目结构分析函数 profit.fn.account_breakdown
2
+
3
+ 参数:start_date, end_date, account_level, pl_category(均可选)
4
+ 返回:科目层级的收入、成本、费用、净影响、占比
5
+
6
+ 发布:
7
+ dazi onto script publish 项目/潘达石化/本体/ontos/利润分析本体方案/functions/profit_fn_account_breakdown.py \\
8
+ --space space__zlj --register-function-id profit.fn.account_breakdown
9
+ """
10
+
11
+ TEST_ARGUMENTS = {
12
+ "v": 1,
13
+ "arguments": {"start_date": "2025-01-01", "end_date": "2026-06-30", "account_level": 2, "pl_category": ""},
14
+ "object_type_code": "ProfitAnalysis",
15
+ }
16
+
17
+
18
+ def _build_where(start_date, end_date, account_level, pl_category):
19
+ clauses = ["account_type IN ('收入','成本','费用')"]
20
+ if start_date and end_date:
21
+ clauses.append(f"posting_date >= '{start_date}' AND posting_date <= '{end_date}'")
22
+ if account_level:
23
+ clauses.append(f"account_level = {int(account_level)}")
24
+ if pl_category:
25
+ clauses.append(f"pl_category = '{pl_category}'")
26
+ return "WHERE " + " AND ".join(clauses)
27
+
28
+
29
+ def _ontology_fn_body(p):
30
+ params = dict(p.get_params() or {})
31
+ where_clause = _build_where(
32
+ params.get("start_date", ""),
33
+ params.get("end_date", ""),
34
+ params.get("account_level", ""),
35
+ params.get("pl_category", ""),
36
+ )
37
+
38
+ sql = f"""
39
+ SELECT
40
+ account_code,
41
+ account_name,
42
+ account_level,
43
+ pl_category,
44
+ account_type,
45
+ sum(if(account_type = '收入', amount_signed, 0)) AS revenue,
46
+ sum(if(account_type = '成本', amount_signed, 0)) AS cost,
47
+ sum(if(account_type = '费用', amount_signed, 0)) AS expense
48
+ FROM actual_journal_entry
49
+ {where_clause}
50
+ GROUP BY account_code, account_name, account_level, pl_category, account_type
51
+ ORDER BY account_code
52
+ """
53
+
54
+ rows = p.sql.query(sql)
55
+ total_impact = 0.0
56
+ parsed = []
57
+ for row in rows:
58
+ rev = float(row.get("revenue", 0) or 0)
59
+ cost = float(row.get("cost", 0) or 0)
60
+ exp = float(row.get("expense", 0) or 0)
61
+ acct_type = row.get("account_type", "")
62
+ if acct_type == "收入":
63
+ net = rev
64
+ else:
65
+ net = -(cost + exp)
66
+ total_impact += abs(net)
67
+ parsed.append((row, rev, cost, exp, net))
68
+
69
+ data = []
70
+ for row, rev, cost, exp, net in parsed:
71
+ share = abs(net) / total_impact * 100 if total_impact > 0 else 0.0
72
+ data.append({
73
+ "account_code": row.get("account_code", ""),
74
+ "account_name": row.get("account_name", ""),
75
+ "account_level": int(row.get("account_level", 0) or 0),
76
+ "pl_category": row.get("pl_category", ""),
77
+ "revenue": round(rev, 2),
78
+ "cost": round(cost, 2),
79
+ "expense": round(exp, 2),
80
+ "net_impact": round(net, 2),
81
+ "share_pct": round(share, 2),
82
+ })
83
+
84
+ return p.function_result(
85
+ columns=["account_code", "account_name", "account_level", "pl_category", "revenue", "cost", "expense", "net_impact", "share_pct"],
86
+ data=data,
87
+ row_count=len(data),
88
+ )
89
+
90
+
91
+ def main():
92
+ s = space.get(ctx.space_id or "")
93
+ _Ports = type("_Ports", (), {
94
+ "get_params": lambda self: dict(ctx.params or {}),
95
+ "function_result": lambda self, **kw: onto.function_result(**kw),
96
+ })
97
+ p = _Ports()
98
+ p.sql = s.sql
99
+ return _ontology_fn_body(p)
@@ -0,0 +1,116 @@
1
+ """预实对比函数 profit.fn.budget_vs_actual
2
+
3
+ 参数:fiscal_year, fiscal_period(0=全年), budget_version, cost_center_id(可选)
4
+ 返回:科目×成本中心的预算额、实际额、差异、执行率
5
+
6
+ 发布:
7
+ dazi onto script publish 项目/潘达石化/本体/ontos/利润分析本体方案/functions/profit_fn_budget_vs_actual.py \\
8
+ --space space__zlj --register-function-id profit.fn.budget_vs_actual
9
+ """
10
+
11
+ TEST_ARGUMENTS = {
12
+ "v": 1,
13
+ "arguments": {
14
+ "fiscal_year": 2026,
15
+ "fiscal_period": 0,
16
+ "budget_version": "2026年度预算",
17
+ "cost_center_id": "",
18
+ },
19
+ "object_type_code": "BudgetAnalysis",
20
+ }
21
+
22
+
23
+ def _ontology_fn_body(p):
24
+ params = dict(p.get_params() or {})
25
+ fiscal_year = int(params.get("fiscal_year", 2026) or 2026)
26
+ fiscal_period = int(params.get("fiscal_period", 0) or 0)
27
+ budget_version = params.get("budget_version", "2026年度预算")
28
+ cost_center_id = params.get("cost_center_id", "")
29
+
30
+ period_filter_b = ""
31
+ period_filter_a = ""
32
+ if fiscal_period > 0:
33
+ period_filter_b = f"AND fiscal_period = {fiscal_period}"
34
+ period_filter_a = f"AND fiscal_period = {fiscal_period}"
35
+
36
+ cc_filter_b = f"AND cost_center_id = '{cost_center_id}'" if cost_center_id else ""
37
+ cc_filter_a = cc_filter_b
38
+
39
+ sql = f"""
40
+ SELECT
41
+ coalesce(b.account_code, a.account_code) AS account_code,
42
+ coalesce(b.account_name, a.account_name) AS account_name,
43
+ coalesce(b.pl_category, a.pl_category) AS pl_category,
44
+ coalesce(b.cost_center_name, a.cost_center_name) AS cost_center_name,
45
+ coalesce(b.budget_amount, 0) AS budget_amount,
46
+ coalesce(a.actual_amount, 0) AS actual_amount
47
+ FROM (
48
+ SELECT
49
+ account_id,
50
+ account_code,
51
+ account_name,
52
+ pl_category,
53
+ cost_center_id,
54
+ cost_center_name,
55
+ sum(budget_amount) AS budget_amount
56
+ FROM budget_entry
57
+ WHERE fiscal_year = {fiscal_year}
58
+ AND budget_version = '{budget_version}'
59
+ AND status = '已发布'
60
+ {period_filter_b}
61
+ {cc_filter_b}
62
+ GROUP BY account_id, account_code, account_name, pl_category, cost_center_id, cost_center_name
63
+ ) b
64
+ FULL OUTER JOIN (
65
+ SELECT
66
+ account_id,
67
+ account_code,
68
+ account_name,
69
+ pl_category,
70
+ cost_center_id,
71
+ cost_center_name,
72
+ sum(amount_signed) AS actual_amount
73
+ FROM actual_journal_entry
74
+ WHERE fiscal_year = {fiscal_year}
75
+ AND account_type IN ('收入','成本','费用')
76
+ {period_filter_a}
77
+ {cc_filter_a}
78
+ GROUP BY account_id, account_code, account_name, pl_category, cost_center_id, cost_center_name
79
+ ) a ON b.account_id = a.account_id AND b.cost_center_id = a.cost_center_id
80
+ ORDER BY abs(coalesce(a.actual_amount, 0) - coalesce(b.budget_amount, 0)) DESC
81
+ """
82
+
83
+ rows = p.sql.query(sql)
84
+ data = []
85
+ for row in rows:
86
+ budget_amt = float(row.get("budget_amount", 0) or 0)
87
+ actual_amt = float(row.get("actual_amount", 0) or 0)
88
+ variance = actual_amt - budget_amt
89
+ exec_rate = actual_amt / budget_amt if budget_amt != 0 else 0.0
90
+ data.append({
91
+ "account_code": row.get("account_code", ""),
92
+ "account_name": row.get("account_name", ""),
93
+ "pl_category": row.get("pl_category", ""),
94
+ "cost_center_name": row.get("cost_center_name", ""),
95
+ "budget_amount": round(budget_amt, 2),
96
+ "actual_amount": round(actual_amt, 2),
97
+ "variance": round(variance, 2),
98
+ "execution_rate": round(exec_rate, 4),
99
+ })
100
+
101
+ return p.function_result(
102
+ columns=["account_code", "account_name", "pl_category", "cost_center_name", "budget_amount", "actual_amount", "variance", "execution_rate"],
103
+ data=data,
104
+ row_count=len(data),
105
+ )
106
+
107
+
108
+ def main():
109
+ s = space.get(ctx.space_id or "")
110
+ _Ports = type("_Ports", (), {
111
+ "get_params": lambda self: dict(ctx.params or {}),
112
+ "function_result": lambda self, **kw: onto.function_result(**kw),
113
+ })
114
+ p = _Ports()
115
+ p.sql = s.sql
116
+ return _ontology_fn_body(p)
@@ -0,0 +1,85 @@
1
+ """组织利润分析函数 profit.fn.cost_center_profit
2
+
3
+ 参数:start_date, end_date, department(可选)
4
+ 返回:各成本中心收入、成本、费用、营业利润、利润率
5
+
6
+ 发布:
7
+ dazi onto script publish 项目/潘达石化/本体/ontos/利润分析本体方案/functions/profit_fn_cost_center_profit.py \\
8
+ --space space__zlj --register-function-id profit.fn.cost_center_profit
9
+ """
10
+
11
+ TEST_ARGUMENTS = {
12
+ "v": 1,
13
+ "arguments": {"start_date": "2025-01-01", "end_date": "2026-06-30", "department": ""},
14
+ "object_type_code": "ProfitAnalysis",
15
+ }
16
+
17
+
18
+ def _build_where(start_date, end_date, department):
19
+ clauses = ["account_type IN ('收入','成本','费用')"]
20
+ if start_date and end_date:
21
+ clauses.append(f"posting_date >= '{start_date}' AND posting_date <= '{end_date}'")
22
+ if department:
23
+ clauses.append(f"department = '{department}'")
24
+ return "WHERE " + " AND ".join(clauses)
25
+
26
+
27
+ def _ontology_fn_body(p):
28
+ params = dict(p.get_params() or {})
29
+ where_clause = _build_where(
30
+ params.get("start_date", ""),
31
+ params.get("end_date", ""),
32
+ params.get("department", ""),
33
+ )
34
+
35
+ sql = f"""
36
+ SELECT
37
+ cost_center_id,
38
+ cost_center_name,
39
+ department,
40
+ profit_center,
41
+ sum(if(account_type = '收入', amount_signed, 0)) AS revenue,
42
+ sum(if(account_type = '成本', amount_signed, 0)) AS cost,
43
+ sum(if(account_type = '费用', amount_signed, 0)) AS expense
44
+ FROM actual_journal_entry
45
+ {where_clause}
46
+ GROUP BY cost_center_id, cost_center_name, department, profit_center
47
+ ORDER BY revenue - cost - expense DESC
48
+ """
49
+
50
+ rows = p.sql.query(sql)
51
+ data = []
52
+ for row in rows:
53
+ rev = float(row.get("revenue", 0) or 0)
54
+ cost = float(row.get("cost", 0) or 0)
55
+ exp = float(row.get("expense", 0) or 0)
56
+ profit = rev - cost - exp
57
+ margin = profit / rev if rev > 0 else 0.0
58
+ data.append({
59
+ "cost_center_id": row.get("cost_center_id", ""),
60
+ "cost_center_name": row.get("cost_center_name", ""),
61
+ "department": row.get("department", ""),
62
+ "profit_center": row.get("profit_center", ""),
63
+ "revenue": round(rev, 2),
64
+ "cost": round(cost, 2),
65
+ "expense": round(exp, 2),
66
+ "operating_profit": round(profit, 2),
67
+ "profit_margin": round(margin, 4),
68
+ })
69
+
70
+ return p.function_result(
71
+ columns=["cost_center_id", "cost_center_name", "department", "profit_center", "revenue", "cost", "expense", "operating_profit", "profit_margin"],
72
+ data=data,
73
+ row_count=len(data),
74
+ )
75
+
76
+
77
+ def main():
78
+ s = space.get(ctx.space_id or "")
79
+ _Ports = type("_Ports", (), {
80
+ "get_params": lambda self: dict(ctx.params or {}),
81
+ "function_result": lambda self, **kw: onto.function_result(**kw),
82
+ })
83
+ p = _Ports()
84
+ p.sql = s.sql
85
+ return _ontology_fn_body(p)
@@ -0,0 +1,76 @@
1
+ """利润总览函数 profit.fn.get_summary
2
+
3
+ 参数:start_date, end_date, cost_center_id(可选)
4
+ 返回:总收入、总成本、总费用、营业利润、利润率、分录行数
5
+
6
+ 发布:
7
+ dazi onto script publish 项目/潘达石化/本体/ontos/利润分析本体方案/functions/profit_fn_get_summary.py \\
8
+ --space space__zlj --register-function-id profit.fn.get_summary
9
+ """
10
+
11
+ TEST_ARGUMENTS = {
12
+ "v": 1,
13
+ "arguments": {"start_date": "2025-01-01", "end_date": "2026-06-30", "cost_center_id": ""},
14
+ "object_type_code": "ProfitAnalysis",
15
+ }
16
+
17
+
18
+ def _build_where(start_date, end_date, cost_center_id):
19
+ clauses = ["account_type IN ('收入','成本','费用')"]
20
+ if start_date and end_date:
21
+ clauses.append(f"posting_date >= '{start_date}' AND posting_date <= '{end_date}'")
22
+ if cost_center_id:
23
+ clauses.append(f"cost_center_id = '{cost_center_id}'")
24
+ return "WHERE " + " AND ".join(clauses)
25
+
26
+
27
+ def _ontology_fn_body(p):
28
+ params = dict(p.get_params() or {})
29
+ start_date = params.get("start_date", "")
30
+ end_date = params.get("end_date", "")
31
+ cost_center_id = params.get("cost_center_id", "")
32
+ where_clause = _build_where(start_date, end_date, cost_center_id)
33
+
34
+ sql = f"""
35
+ SELECT
36
+ sum(if(account_type = '收入', amount_signed, 0)) AS total_revenue,
37
+ sum(if(account_type = '成本', amount_signed, 0)) AS total_cost,
38
+ sum(if(account_type = '费用', amount_signed, 0)) AS total_expense,
39
+ count(line_id) AS line_count
40
+ FROM actual_journal_entry
41
+ {where_clause}
42
+ """
43
+
44
+ rows = p.sql.query(sql)
45
+ row = rows[0] if rows else {}
46
+ rev = float(row.get("total_revenue", 0) or 0)
47
+ cost = float(row.get("total_cost", 0) or 0)
48
+ exp = float(row.get("total_expense", 0) or 0)
49
+ profit = rev - cost - exp
50
+ margin = profit / rev if rev > 0 else 0.0
51
+
52
+ data = [{
53
+ "total_revenue": round(rev, 2),
54
+ "total_cost": round(cost, 2),
55
+ "total_expense": round(exp, 2),
56
+ "operating_profit": round(profit, 2),
57
+ "profit_margin": round(margin, 4),
58
+ "line_count": int(row.get("line_count", 0) or 0),
59
+ }]
60
+
61
+ return p.function_result(
62
+ columns=["total_revenue", "total_cost", "total_expense", "operating_profit", "profit_margin", "line_count"],
63
+ data=data,
64
+ row_count=1,
65
+ )
66
+
67
+
68
+ def main():
69
+ s = space.get(ctx.space_id or "")
70
+ _Ports = type("_Ports", (), {
71
+ "get_params": lambda self: dict(ctx.params or {}),
72
+ "function_result": lambda self, **kw: onto.function_result(**kw),
73
+ })
74
+ p = _Ports()
75
+ p.sql = s.sql
76
+ return _ontology_fn_body(p)