@dazitech/cli 3.0.8 → 3.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 +1 -1
- package/dist/clis/dazi-app.js +1 -1
- package/dist/clis/dazi-flow.js +1 -1
- package/dist/clis/dazi-onto.js +1 -1
- package/dist/clis/dazi.js +1 -1
- package/dist/docs/flow/flow-project-guide.md +1 -1
- package/dist/docs/guides/troubleshooting.md +12 -1
- package/dist/docs/index.json +20 -1
- package/dist/docs/onto/dazi_script_sdk_reference.md +78 -12
- package/dist/docs/onto/function-guide.md +61 -33
- package/dist/docs/onto//346/234/254/344/275/223/345/210/206/347/261/273/350/247/204/345/210/222/344/270/216SDK/346/211/251/345/261/225/346/226/271/346/241/210.md +168 -0
- package/dist/docs/onto//346/234/254/344/275/223/345/221/275/345/220/215/350/247/204/350/214/203_/347/211/251/347/220/206/350/241/250Cube/344/270/216/345/257/271/350/261/241.md +402 -0
- 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 +63 -1
- package/dist/docs/onto//346/234/254/344/275/223/350/247/204/345/210/222/346/214/207/345/215/227.md +123 -15
- package/dist/docs/onto//350/204/232/346/234/254/350/277/220/350/241/214/347/272/240/351/224/231_/345/225/206/345/212/241/346/210/220/346/234/254/346/226/271/346/241/210/345/274/200/345/217/221/350/277/207/347/250/213.md +213 -0
- 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 +356 -233
- 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 +379 -296
- package/dist/examples/index.json +23 -17
- package/dist/examples/onto/README.md +13 -5
- package/dist/examples/onto/_templates/ontology_function_template.py +50 -0
- package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/functions/profit_fn_account_breakdown.py +62 -0
- package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/functions/profit_fn_budget_vs_actual.py +69 -0
- package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/functions/profit_fn_cost_center_profit.py +64 -0
- package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/functions/profit_fn_get_summary.py +61 -0
- package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/functions/profit_fn_mom_analysis.py +82 -0
- package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/functions/profit_fn_top_accounts.py +61 -0
- package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/functions/profit_fn_yoy_analysis.py +79 -0
- package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/functions/save_test_arguments.ps1 +38 -0
- package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/functions/test_arguments/profit.fn.account_breakdown.json +1 -0
- package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/functions/test_arguments/profit.fn.budget_vs_actual.json +1 -0
- package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/functions/test_arguments/profit.fn.cost_center_profit.json +1 -0
- package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/functions/test_arguments/profit.fn.get_summary.json +1 -0
- package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/functions/test_arguments/profit.fn.mom_analysis.json +1 -0
- package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/functions/test_arguments/profit.fn.top_accounts.json +1 -0
- package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/functions/test_arguments/profit.fn.yoy_analysis.json +1 -0
- package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/setup/profit_ontology_init.py +232 -74
- package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/setup/profit_seed_data.py +16 -13
- package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/functions/sales_fn_channel_mix.py +19 -16
- package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/functions/sales_fn_customer_segmentation.py +48 -50
- package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/functions/sales_fn_get_summary.py +3 -6
- package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/functions/sales_fn_mom_analysis.py +11 -12
- package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/functions/sales_fn_region_breakdown.py +6 -7
- package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/functions/sales_fn_top_products.py +5 -8
- package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/functions/sales_fn_yoy_analysis.py +3 -6
- package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/functions/save_test_arguments.ps1 +32 -19
- package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/functions/test_arguments/sales.fn.channel_mix.json +3 -6
- package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/functions/test_arguments/sales.fn.customer_segmentation.json +2 -7
- package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/functions/test_arguments/sales.fn.get_summary.json +2 -5
- package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/functions/test_arguments/sales.fn.mom_analysis.json +2 -5
- package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/functions/test_arguments/sales.fn.region_breakdown.json +2 -5
- package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/functions/test_arguments/sales.fn.top_products.json +2 -7
- package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/functions/test_arguments/sales.fn.yoy_analysis.json +2 -5
- package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/setup/sales_ontology_init.py +291 -155
- package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/setup/sales_seed_data.py +59 -20
- package/dist/prompts/index.json +1 -1
- package/dist/prompts/onto/function-design.md +42 -22
- package/dist/prompts/onto/script-publish-run.md +15 -1
- package/package.json +1 -1
- package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/function/profit_fn_account_breakdown.py +0 -99
- package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/function/profit_fn_budget_vs_actual.py +0 -116
- package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/function/profit_fn_cost_center_profit.py +0 -85
- package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/function/profit_fn_get_summary.py +0 -76
- package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/function/profit_fn_mom_analysis.py +0 -86
- package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/function/profit_fn_top_accounts.py +0 -103
- package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/function/profit_fn_yoy_analysis.py +0 -86
- package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/function/save_test_arguments.ps1 +0 -27
- package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/function/test_arguments/profit.fn.account_breakdown.json +0 -10
- 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 +0 -10
- 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 +0 -9
- package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/function/test_arguments/profit.fn.get_summary.json +0 -9
- package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/function/test_arguments/profit.fn.mom_analysis.json +0 -9
- package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/function/test_arguments/profit.fn.top_accounts.json +0 -11
- package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/function/test_arguments/profit.fn.yoy_analysis.json +0 -9
- package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/functions/README.md +0 -25
- package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/setup/README.md +0 -5
package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/setup/sales_seed_data.py
CHANGED
|
@@ -1,34 +1,69 @@
|
|
|
1
|
-
"""产品销售演示数据灌入 —
|
|
1
|
+
"""产品销售演示数据灌入 — space__misc_01
|
|
2
2
|
|
|
3
3
|
前置:先执行 sales_ontology_init.py 建表。
|
|
4
|
-
幂等:
|
|
4
|
+
幂等:fact_sales_order_line 已有数据则跳过。
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
发布:dazi onto script publish
|
|
6
|
+
放置:项目/DAZI_TEST/本体/ontos/销售本体示例/setup/sales_seed_data.py
|
|
7
|
+
发布:dazi onto script publish 项目/DAZI_TEST/本体/ontos/销售本体示例/setup/sales_seed_data.py --space space__misc_01 --type data
|
|
8
8
|
"""
|
|
9
9
|
|
|
10
10
|
import json
|
|
11
11
|
import random
|
|
12
12
|
from datetime import date, datetime, timedelta
|
|
13
13
|
|
|
14
|
+
|
|
14
15
|
_SEED_DT = datetime(2025, 1, 1, 0, 0, 0)
|
|
15
16
|
|
|
16
17
|
|
|
17
18
|
def main():
|
|
18
|
-
space_id = "
|
|
19
|
+
space_id = "space__misc_01"
|
|
19
20
|
s = space.get(space_id)
|
|
20
21
|
|
|
21
22
|
output.print("=== 产品销售演示数据灌入 ===")
|
|
22
23
|
|
|
24
|
+
# 检查是否已有数据
|
|
23
25
|
try:
|
|
24
|
-
n = int(s.sql.query_one("SELECT count() FROM
|
|
26
|
+
n = int(s.sql.query_one("SELECT count() FROM fact_sales_order_line") or 0)
|
|
25
27
|
except Exception:
|
|
26
28
|
n = 0
|
|
27
29
|
if n > 0:
|
|
28
|
-
output.print(f"
|
|
29
|
-
output.print("__JSON_SUMMARY__" + json.dumps({"ok": True, "skipped": True, "rows": n}, ensure_ascii=
|
|
30
|
+
output.print(f"fact_sales_order_line 已有 {n} 行,跳过灌数")
|
|
31
|
+
output.print("__JSON_SUMMARY__" + json.dumps({"ok": True, "skipped": True, "rows": n}, ensure_ascii=False))
|
|
30
32
|
return
|
|
31
33
|
|
|
34
|
+
# 1. 灌入 dim_date
|
|
35
|
+
output.print("\n[1/4] 灌入 dim_date...")
|
|
36
|
+
dim_date_rows = []
|
|
37
|
+
start_date = date(2025, 1, 1)
|
|
38
|
+
end_date = date(2026, 6, 30)
|
|
39
|
+
current = start_date
|
|
40
|
+
while current <= end_date:
|
|
41
|
+
date_key = int(current.strftime("%Y%m%d"))
|
|
42
|
+
year = current.year
|
|
43
|
+
month = current.month
|
|
44
|
+
quarter = (month - 1) // 3 + 1
|
|
45
|
+
week_of_year = current.isocalendar()[1]
|
|
46
|
+
day_of_week = current.weekday()
|
|
47
|
+
is_weekend = 1 if day_of_week >= 5 else 0
|
|
48
|
+
year_month = current.strftime("%Y-%m")
|
|
49
|
+
dim_date_rows.append({
|
|
50
|
+
"date_key": date_key,
|
|
51
|
+
"calendar_date": current,
|
|
52
|
+
"year": year,
|
|
53
|
+
"quarter": quarter,
|
|
54
|
+
"month": month,
|
|
55
|
+
"week_of_year": week_of_year,
|
|
56
|
+
"day_of_week": day_of_week,
|
|
57
|
+
"is_weekend": is_weekend,
|
|
58
|
+
"year_month": year_month,
|
|
59
|
+
})
|
|
60
|
+
current += timedelta(days=1)
|
|
61
|
+
s.sql.insert_rows("dim_date", dim_date_rows)
|
|
62
|
+
output.print(f"OK dim_date 插入 {len(dim_date_rows)} 行")
|
|
63
|
+
|
|
64
|
+
# 2. 灌入维表
|
|
65
|
+
output.print("\n[2/4] 灌入维表...")
|
|
66
|
+
|
|
32
67
|
products = [
|
|
33
68
|
{"product_id": "P001", "product_code": "OIL-92", "product_name": "92号汽油", "product_category": "成品油", "product_subcategory": "汽油", "brand": "潘达", "unit": "吨", "list_price": 8200.0, "cost_price": 7100.0, "status": "在售"},
|
|
34
69
|
{"product_id": "P002", "product_code": "OIL-0", "product_name": "0号柴油", "product_category": "成品油", "product_subcategory": "柴油", "brand": "潘达", "unit": "吨", "list_price": 7500.0, "cost_price": 6500.0, "status": "在售"},
|
|
@@ -57,11 +92,13 @@ def main():
|
|
|
57
92
|
{"channel_id": "CH03", "channel_code": "B2B", "channel_name": "B2B平台", "channel_type": "线上"},
|
|
58
93
|
]
|
|
59
94
|
|
|
60
|
-
s.sql.insert_rows("
|
|
61
|
-
s.sql.insert_rows("
|
|
62
|
-
s.sql.insert_rows("
|
|
63
|
-
output.print("OK
|
|
95
|
+
s.sql.insert_rows("dim_product", products)
|
|
96
|
+
s.sql.insert_rows("dim_customer", customers)
|
|
97
|
+
s.sql.insert_rows("dim_channel", channels)
|
|
98
|
+
output.print(f"OK dim_product {len(products)} 行, dim_customer {len(customers)} 行, dim_channel {len(channels)} 行")
|
|
64
99
|
|
|
100
|
+
# 3. 生成销售事实数据
|
|
101
|
+
output.print("\n[3/4] 生成销售事实数据...")
|
|
65
102
|
random.seed(627)
|
|
66
103
|
regions = {c["customer_id"]: c["customer_region"] for c in customers}
|
|
67
104
|
ctypes = {c["customer_id"]: c["customer_type"] for c in customers}
|
|
@@ -70,12 +107,10 @@ def main():
|
|
|
70
107
|
|
|
71
108
|
fact_rows = []
|
|
72
109
|
line_seq = 1
|
|
73
|
-
|
|
74
|
-
end = date(2026, 6, 30)
|
|
75
|
-
days = (end - start).days + 1
|
|
110
|
+
days = (end_date - start_date).days + 1
|
|
76
111
|
|
|
77
112
|
for d_offset in range(days):
|
|
78
|
-
order_date =
|
|
113
|
+
order_date = start_date + timedelta(days=d_offset)
|
|
79
114
|
if random.random() > 0.35:
|
|
80
115
|
continue
|
|
81
116
|
daily_orders = random.randint(2, 8)
|
|
@@ -90,9 +125,11 @@ def main():
|
|
|
90
125
|
unit_price = round(prices[product_id] * random.uniform(0.92, 1.0), 2)
|
|
91
126
|
discount = round(unit_price * qty * random.uniform(0, 0.05), 2)
|
|
92
127
|
sales_amount = round(unit_price * qty - discount, 2)
|
|
128
|
+
date_key = int(order_date.strftime("%Y%m%d"))
|
|
93
129
|
fact_rows.append({
|
|
94
130
|
"order_id": order_id,
|
|
95
131
|
"order_line_id": f"L{line_seq:06d}",
|
|
132
|
+
"date_key": date_key,
|
|
96
133
|
"order_date": order_date,
|
|
97
134
|
"product_id": product_id,
|
|
98
135
|
"product_category": pcats[product_id],
|
|
@@ -109,16 +146,18 @@ def main():
|
|
|
109
146
|
})
|
|
110
147
|
line_seq += 1
|
|
111
148
|
|
|
112
|
-
|
|
113
|
-
output.print(f"OK
|
|
149
|
+
s.sql.insert_rows("fact_sales_order_line", fact_rows)
|
|
150
|
+
output.print(f"OK fact_sales_order_line 插入 {len(fact_rows)} 行")
|
|
114
151
|
|
|
152
|
+
# 4. 完成
|
|
115
153
|
summary = {
|
|
116
154
|
"ok": True,
|
|
117
155
|
"space_id": space_id,
|
|
156
|
+
"dim_date": len(dim_date_rows),
|
|
118
157
|
"products": len(products),
|
|
119
158
|
"customers": len(customers),
|
|
120
159
|
"channels": len(channels),
|
|
121
|
-
"fact_inserted":
|
|
160
|
+
"fact_inserted": len(fact_rows),
|
|
122
161
|
}
|
|
123
162
|
output.success("灌数完成")
|
|
124
|
-
output.print("__JSON_SUMMARY__" + json.dumps(summary, ensure_ascii=
|
|
163
|
+
output.print("__JSON_SUMMARY__" + json.dumps(summary, ensure_ascii=False, default=str))
|
package/dist/prompts/index.json
CHANGED
|
@@ -7,47 +7,67 @@
|
|
|
7
7
|
|
|
8
8
|
你是一名搭子平台本体工程师。请根据以下需求设计一个本体函数(ontology function)。
|
|
9
9
|
|
|
10
|
+
**执行脚本前必读**:`资源/docs/onto/脚本运行纠错_商务成本方案开发过程.md`(`onto/script-run-troubleshooting`)。
|
|
11
|
+
**必须复制结构**:`资源/examples/onto/_templates/ontology_function_template.py` 或 `销售示例/functions/sales_fn_get_summary.py`。
|
|
12
|
+
|
|
10
13
|
## 函数需求
|
|
11
14
|
|
|
12
15
|
{{function_description}}
|
|
13
16
|
|
|
14
17
|
## 要求
|
|
15
18
|
|
|
16
|
-
1.
|
|
17
|
-
2. `main(params: dict)
|
|
18
|
-
3.
|
|
19
|
-
4.
|
|
20
|
-
5.
|
|
21
|
-
6.
|
|
22
|
-
7.
|
|
19
|
+
1. 文件名 `snake_case`,如 `cost_fn_get_summary.py`;`function_id` 如 `domain.fn.get_summary`
|
|
20
|
+
2. **`def main():` 无参** — 禁止 `main(params: dict)`;入参来自 `ctx.params`
|
|
21
|
+
3. 业务逻辑写在 **`_ontology_fn_body(p)`**;`main()` 末尾 **`return _ontology_fn_body(p)`**
|
|
22
|
+
4. 输出使用 **`return p.function_result(columns=..., data=..., row_count=...)`** — **禁止** `output.print_json()`、`print()` 作为平台输出
|
|
23
|
+
5. SQL 用 **`p.sql.query()`** 取多列;聚合勿误用 `query_one().get()`
|
|
24
|
+
6. 脚本顶部 **`TEST_ARGUMENTS`** 与 `functions/test_arguments/<function_id>.json` 同步
|
|
25
|
+
7. 中文 docstring:目的、参数、返回值、发布命令
|
|
23
26
|
|
|
24
|
-
##
|
|
27
|
+
## 输出格式(必须遵循)
|
|
25
28
|
|
|
26
29
|
```python
|
|
27
|
-
|
|
28
|
-
""
|
|
29
|
-
|
|
30
|
+
TEST_ARGUMENTS = {
|
|
31
|
+
"v": 1,
|
|
32
|
+
"arguments": { ... },
|
|
33
|
+
"object_type_code": "<ObjectTypeCode>",
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _ontology_fn_body(p):
|
|
38
|
+
params = dict(p.get_params() or {})
|
|
39
|
+
# ... p.sql.query(...)
|
|
40
|
+
return p.function_result(
|
|
41
|
+
columns=[...],
|
|
42
|
+
data=[{...}],
|
|
43
|
+
row_count=1,
|
|
44
|
+
)
|
|
30
45
|
|
|
31
|
-
Args:
|
|
32
|
-
params: 包含 xxx(说明)
|
|
33
46
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
47
|
+
def main():
|
|
48
|
+
s = space.get(ctx.space_id or "")
|
|
49
|
+
_Ports = type(
|
|
50
|
+
"_Ports",
|
|
51
|
+
(),
|
|
52
|
+
{
|
|
53
|
+
"get_params": lambda self: dict(ctx.params or {}),
|
|
54
|
+
"function_result": lambda self, **kw: onto.function_result(**kw),
|
|
55
|
+
},
|
|
56
|
+
)
|
|
57
|
+
p = _Ports()
|
|
58
|
+
p.sql = s.sql
|
|
59
|
+
return _ontology_fn_body(p)
|
|
38
60
|
```
|
|
39
61
|
|
|
40
|
-
|
|
41
|
-
`space_id` 取自同目录上级的 `README.md`(`ontos/<实现名>/README.md`)。
|
|
62
|
+
脚本落盘:`项目/<业务名>/本体/ontos/<实现名>/functions/<file>.py`
|
|
42
63
|
|
|
43
|
-
发布与运行(
|
|
64
|
+
发布与运行(dazi-work 根;**勿用** `dazi-onto`):
|
|
44
65
|
|
|
45
66
|
```powershell
|
|
46
67
|
dazi onto script publish 项目/<业务名>/本体/ontos/<实现名>/functions/<file>.py --space <space-id> --register-function-id <id>
|
|
47
68
|
dazi onto function run <id> --space <space-id>
|
|
48
|
-
# run 验证通过后,保存 test_arguments(侧栏「运行函数」预填;须用 ofn_xxx 内部 id,见 script-publish-run §7)
|
|
49
69
|
dazi onto function save-test-arguments <ofn_internal_id> --space <space-id> `
|
|
50
70
|
--arguments-json-file 项目/<业务名>/本体/ontos/<实现名>/functions/test_arguments/<id>.json
|
|
51
71
|
```
|
|
52
72
|
|
|
53
|
-
|
|
73
|
+
详见 `onto/script-publish-run`、`onto/function-guide`。
|
|
@@ -8,6 +8,16 @@
|
|
|
8
8
|
|
|
9
9
|
你是搭子平台 **dazi-vscode v3** 本体实施助手。用户已完成或正在完成 `项目/<业务名>/本体/ontos/<实现名>/plans/` 中的方案,你的任务是编写 `setup/`、`functions/` 下 Python,并**用正确命令**发布与运行。
|
|
10
10
|
|
|
11
|
+
> **规划阶段提醒**:`plans/` 方案须含 **Cube 层独立章节**(维度/度量/派生度量、`bind_source` 对照)。若规划缺 Cube,**不得**开始 init 脚本;先补规划并对照 `本体规划指南.md` **§规划文档完整性自检清单**。
|
|
12
|
+
|
|
13
|
+
## 执行前必读(强制)
|
|
14
|
+
|
|
15
|
+
在终端执行 **`dazi onto script publish` / `script run` / `function run` / `save-test-arguments` 之前**,须阅读同步文档:
|
|
16
|
+
|
|
17
|
+
- **`资源/docs/onto/脚本运行纠错_商务成本方案开发过程.md`**(`dazi docs show onto/script-run-troubleshooting`)
|
|
18
|
+
|
|
19
|
+
重点:setup API 勿猜方法名;多列聚合用 `query()` 勿误用 `query_one()`;**本体函数 `main()` 须 `return _ontology_fn_body(p)`,禁止 `output.print_json()`**;`function run` **不支持** `--arguments-json-file`;`save-test-arguments` 须 **`ofn_xxx` 内部 id**。
|
|
20
|
+
|
|
11
21
|
## 强制规则(违反会导致 CommandNotFound 或 404)
|
|
12
22
|
|
|
13
23
|
1. **禁止**在终端使用独立命令 `dazi-onto`(v3 未安装到系统 PATH)。
|
|
@@ -112,6 +122,8 @@ dazi onto script run --file <项目路径>/setup/xxx_seed_data.py --space <空
|
|
|
112
122
|
dazi onto function run <function_id> --space <空间>
|
|
113
123
|
```
|
|
114
124
|
|
|
125
|
+
> **`function run` 不支持 `--arguments-json-file`**。须先用 §7 `save-test-arguments` 写入默认参数,再不带 JSON 文件运行。误用示例见 `资源/docs/onto/脚本运行纠错_商务成本方案开发过程.md`(`onto/script-run-troubleshooting`)。
|
|
126
|
+
|
|
115
127
|
PowerShell 传 JSON 参数易丢引号;可省略 `--params` 使用函数默认,或:
|
|
116
128
|
|
|
117
129
|
```powershell
|
|
@@ -160,9 +172,10 @@ dazi onto function get <function_id> --space <空间>
|
|
|
160
172
|
|
|
161
173
|
## 编写脚本时注意
|
|
162
174
|
|
|
175
|
+
- **本体函数**:复制 `资源/examples/onto/_templates/ontology_function_template.py`;`def main():` 无参,末尾 **`return _ontology_fn_body(p)`**;结果用 **`p.function_result(columns=..., data=..., row_count=...)`**。**禁止** `output.print_json()`、`main(params: dict)`。详见 `function-guide.md`、`onto/script-run-troubleshooting` §3。
|
|
163
176
|
- 多列 SQL 聚合用 `s.sql.query()` 取 `rows[0]`,**勿**用 `query_one()`(只返回首列标量)。
|
|
164
177
|
- ClickHouse:`DateTime` 用 `datetime` 对象,`Date` 用 `date` 对象;见同步文档 `dazi_script_seed_data_guide.md`。
|
|
165
|
-
- 入口为 `main()`;函数脚本通过 `ctx.space_id`、`ctx.params` 与 `onto.function_result` 返回。
|
|
178
|
+
- setup/seed 入口为 `main()`;函数脚本通过 `ctx.space_id`、`ctx.params` 与 `onto.function_result` 返回。
|
|
166
179
|
|
|
167
180
|
## 无 `scripts/dazi.ps1` 时的等价命令
|
|
168
181
|
|
|
@@ -183,6 +196,7 @@ VS Code 用户也可在侧栏 **搭子 → Onto 本体** 使用「发布脚本
|
|
|
183
196
|
- `本体规划指南.md`、`本体脚本编写指南.md`
|
|
184
197
|
- `dazi_script_sdk_reference.md`、`dazi_script_seed_data_guide.md`
|
|
185
198
|
- `function-guide.md`
|
|
199
|
+
- `脚本运行纠错_商务成本方案开发过程.md`(API / CLI 踩坑实录)
|
|
186
200
|
|
|
187
201
|
---
|
|
188
202
|
|
package/package.json
CHANGED
|
@@ -1,99 +0,0 @@
|
|
|
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)
|
|
@@ -1,116 +0,0 @@
|
|
|
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)
|
|
@@ -1,85 +0,0 @@
|
|
|
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)
|