@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
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"""同比分析函数
|
|
2
|
+
|
|
3
|
+
功能:对比当期与去年同期的利润指标变化
|
|
4
|
+
参数:start_date, end_date
|
|
5
|
+
|
|
6
|
+
放置:项目/DAZI_TEST/本体/ontos/利润分析示例/functions/profit_fn_yoy_analysis.py
|
|
7
|
+
发布:dazi onto script publish 项目/DAZI_TEST/本体/ontos/利润分析示例/functions/profit_fn_yoy_analysis.py --space space__misc_01 --register-function-id profit.fn.yoy_analysis
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from datetime import datetime, timedelta
|
|
11
|
+
import json
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def _ontology_fn_body(p):
|
|
15
|
+
start_date = p.get("start_date", "2025-01-01")
|
|
16
|
+
end_date = p.get("end_date", datetime.now().strftime("%Y-%m-%d"))
|
|
17
|
+
|
|
18
|
+
start_dt = datetime.strptime(start_date, "%Y-%m-%d")
|
|
19
|
+
end_dt = datetime.strptime(end_date, "%Y-%m-%d")
|
|
20
|
+
|
|
21
|
+
last_year_start = (start_dt - timedelta(days=365)).strftime("%Y-%m-%d")
|
|
22
|
+
last_year_end = (end_dt - timedelta(days=365)).strftime("%Y-%m-%d")
|
|
23
|
+
|
|
24
|
+
output.print(f"当期: {start_date} ~ {end_date}")
|
|
25
|
+
output.print(f"去年同期: {last_year_start} ~ {last_year_end}")
|
|
26
|
+
|
|
27
|
+
def get_period_data(sd, ed):
|
|
28
|
+
sql = """
|
|
29
|
+
SELECT
|
|
30
|
+
sumIf(amount_signed, account_type='收入') as revenue,
|
|
31
|
+
sumIf(amount_signed, account_type='成本') as cost,
|
|
32
|
+
sumIf(amount_signed, account_type='费用') as expense
|
|
33
|
+
FROM fact_gl_journal_entry
|
|
34
|
+
WHERE posting_date >= '{sd}' AND posting_date <= '{ed}'
|
|
35
|
+
""".format(sd=sd, ed=ed)
|
|
36
|
+
result = p.space.sql.query_one(sql)
|
|
37
|
+
return {
|
|
38
|
+
"revenue": result.get("revenue", 0) or 0,
|
|
39
|
+
"cost": result.get("cost", 0) or 0,
|
|
40
|
+
"expense": result.get("expense", 0) or 0,
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
current = get_period_data(start_date, end_date)
|
|
44
|
+
last_year = get_period_data(last_year_start, last_year_end)
|
|
45
|
+
|
|
46
|
+
current_profit = current["revenue"] - current["cost"] - current["expense"]
|
|
47
|
+
last_year_profit = last_year["revenue"] - last_year["cost"] - last_year["expense"]
|
|
48
|
+
|
|
49
|
+
def calc_yoy(current, last):
|
|
50
|
+
if last == 0:
|
|
51
|
+
return 0 if current == 0 else None
|
|
52
|
+
return (current - last) / last
|
|
53
|
+
|
|
54
|
+
result = {
|
|
55
|
+
"period": f"{start_date} ~ {end_date}",
|
|
56
|
+
"last_year_period": f"{last_year_start} ~ {last_year_end}",
|
|
57
|
+
"current": {
|
|
58
|
+
"revenue": round(current["revenue"], 2),
|
|
59
|
+
"cost": round(current["cost"], 2),
|
|
60
|
+
"expense": round(current["expense"], 2),
|
|
61
|
+
"operating_profit": round(current_profit, 2),
|
|
62
|
+
},
|
|
63
|
+
"last_year": {
|
|
64
|
+
"revenue": round(last_year["revenue"], 2),
|
|
65
|
+
"cost": round(last_year["cost"], 2),
|
|
66
|
+
"expense": round(last_year["expense"], 2),
|
|
67
|
+
"operating_profit": round(last_year_profit, 2),
|
|
68
|
+
},
|
|
69
|
+
"yoy": {
|
|
70
|
+
"revenue": round(calc_yoy(current["revenue"], last_year["revenue"]), 4) if calc_yoy(current["revenue"], last_year["revenue"]) is not None else None,
|
|
71
|
+
"cost": round(calc_yoy(current["cost"], last_year["cost"]), 4) if calc_yoy(current["cost"], last_year["cost"]) is not None else None,
|
|
72
|
+
"expense": round(calc_yoy(current["expense"], last_year["expense"]), 4) if calc_yoy(current["expense"], last_year["expense"]) is not None else None,
|
|
73
|
+
"operating_profit": round(calc_yoy(current_profit, last_year_profit), 4) if calc_yoy(current_profit, last_year_profit) is not None else None,
|
|
74
|
+
},
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
p.function_result(result)
|
|
78
|
+
|
|
79
|
+
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# save_test_arguments.ps1
|
|
2
|
+
# 批量保存各函数的 test_arguments
|
|
3
|
+
# 用法:在 dazi-work 根目录执行 .\项目\DAZI_TEST\本体\ontos\利润分析示例\functions\save_test_arguments.ps1
|
|
4
|
+
|
|
5
|
+
$spaceId = "space__misc_01"
|
|
6
|
+
$itemPath = "项目/DAZI_TEST/本体/ontos/利润分析示例/functions"
|
|
7
|
+
|
|
8
|
+
$functions = @(
|
|
9
|
+
@{fn_id="profit.fn.get_summary"; file="profit.fn.get_summary.json"},
|
|
10
|
+
@{fn_id="profit.fn.yoy_analysis"; file="profit.fn.yoy_analysis.json"},
|
|
11
|
+
@{fn_id="profit.fn.mom_analysis"; file="profit.fn.mom_analysis.json"},
|
|
12
|
+
@{fn_id="profit.fn.budget_vs_actual"; file="profit.fn.budget_vs_actual.json"},
|
|
13
|
+
@{fn_id="profit.fn.account_breakdown"; file="profit.fn.account_breakdown.json"},
|
|
14
|
+
@{fn_id="profit.fn.cost_center_profit"; file="profit.fn.cost_center_profit.json"},
|
|
15
|
+
@{fn_id="profit.fn.top_accounts"; file="profit.fn.top_accounts.json"}
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
Write-Host "获取函数列表..."
|
|
19
|
+
$fnList = & dazi onto function list --space $spaceId 2>$null
|
|
20
|
+
|
|
21
|
+
foreach ($fn in $functions) {
|
|
22
|
+
$fnId = $fn.fn_id
|
|
23
|
+
$fileName = $fn.file
|
|
24
|
+
$jsonPath = "$PSScriptRoot/test_arguments/$fileName"
|
|
25
|
+
|
|
26
|
+
$ofnId = $fnList | Where-Object { $_ -match $fnId } | ForEach-Object {
|
|
27
|
+
if ($_ -match 'id:\s*(\S+)') { $matches[1] }
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if ($ofnId) {
|
|
31
|
+
Write-Host "保存 $fnId (ofnId=$ofnId)..."
|
|
32
|
+
& dazi onto function save-test-arguments $ofnId --space $spaceId --arguments-json-file $jsonPath
|
|
33
|
+
} else {
|
|
34
|
+
Write-Host "函数 $fnId 未找到,请先发布函数"
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
Write-Host "完成"
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"v":1,"meta":{"last_source":"admin_panel","last_saved_at":"2026-06-06T01:00:00.000000+00:00","last_input_shape":"json"},"arguments":{"start_date":"2025-01-01","end_date":"2026-06-30","account_type":"收入"},"object_type_code":"Account"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"v":1,"meta":{"last_source":"admin_panel","last_saved_at":"2026-06-06T01:00:00.000000+00:00","last_input_shape":"json"},"arguments":{"start_date":"2025-01-01","end_date":"2026-06-30"},"object_type_code":"BudgetAnalysis"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"v":1,"meta":{"last_source":"admin_panel","last_saved_at":"2026-06-06T01:00:00.000000+00:00","last_input_shape":"json"},"arguments":{"start_date":"2025-01-01","end_date":"2026-06-30"},"object_type_code":"CostCenter"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"v":1,"meta":{"last_source":"admin_panel","last_saved_at":"2026-06-06T01:00:00.000000+00:00","last_input_shape":"json"},"arguments":{"start_date":"2025-01-01","end_date":"2026-06-30"},"object_type_code":"ProfitAnalysis"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"v":1,"meta":{"last_source":"admin_panel","last_saved_at":"2026-06-06T01:00:00.000000+00:00","last_input_shape":"json"},"arguments":{"start_date":"2025-01-01","end_date":"2026-06-30"},"object_type_code":"ProfitAnalysis"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"v":1,"meta":{"last_source":"admin_panel","last_saved_at":"2026-06-06T01:00:00.000000+00:00","last_input_shape":"json"},"arguments":{"start_date":"2025-01-01","end_date":"2026-06-30","limit":10},"object_type_code":"Account"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"v":1,"meta":{"last_source":"admin_panel","last_saved_at":"2026-06-06T01:00:00.000000+00:00","last_input_shape":"json"},"arguments":{"start_date":"2025-01-01","end_date":"2026-06-30"},"object_type_code":"ProfitAnalysis"}
|
|
@@ -1,32 +1,176 @@
|
|
|
1
|
-
"""利润分析本体初始化脚本 —
|
|
1
|
+
"""利润分析本体初始化脚本 — space__misc_01
|
|
2
2
|
|
|
3
3
|
初始化内容:
|
|
4
|
-
1.
|
|
5
|
-
2.
|
|
6
|
-
3. 注册表间关系(
|
|
7
|
-
4. 注册
|
|
8
|
-
5. 定义对象类型(6
|
|
4
|
+
1. 创建物理表(科目表、成本中心维表、实际分录表、预算表)
|
|
5
|
+
2. 注册表到空间(含 display_name / description)
|
|
6
|
+
3. 注册表间关系(7条)
|
|
7
|
+
4. 注册Cube(5个)及派生度量
|
|
8
|
+
5. 定义对象类型(6种)、绑定数据源、属性、链接
|
|
9
9
|
6. 同步指标引用
|
|
10
|
+
7. 配置 347 对齐分类(ads_categories + 桥表)
|
|
10
11
|
|
|
11
|
-
|
|
12
|
-
发布:dazi onto script publish
|
|
12
|
+
放置:资源/examples/onto/利润示例/setup/profit_ontology_init.py(复制到项目 ontos/<实现名>/setup/)
|
|
13
|
+
发布:dazi onto script publish <item-path>/setup/profit_ontology_init.py --space <space-id> --type setup
|
|
14
|
+
规划对照:资源/docs/onto/规划示例_利润分析本体方案.md
|
|
13
15
|
"""
|
|
14
16
|
|
|
15
17
|
import json
|
|
16
18
|
|
|
19
|
+
# 与 规划示例_利润分析本体方案.md §2.3、§3.x 对齐:display_name=侧栏显示名,description=业务说明
|
|
20
|
+
TABLE_REGISTRY = {
|
|
21
|
+
"dim_account": {
|
|
22
|
+
"display_name": "科目维表",
|
|
23
|
+
"description": "会计科目主数据",
|
|
24
|
+
"columns": [
|
|
25
|
+
{"name": "account_id", "display_name": "科目 ID", "description": "主键"},
|
|
26
|
+
{"name": "account_code", "display_name": "科目编码"},
|
|
27
|
+
{"name": "account_name", "display_name": "科目名称"},
|
|
28
|
+
{"name": "account_type", "display_name": "科目类型", "description": "资产/负债/权益/收入/成本/费用"},
|
|
29
|
+
{"name": "pl_category", "display_name": "损益大类"},
|
|
30
|
+
{"name": "parent_account_id", "display_name": "上级科目"},
|
|
31
|
+
{"name": "account_level", "display_name": "层级"},
|
|
32
|
+
{"name": "is_leaf", "display_name": "末级"},
|
|
33
|
+
{"name": "normal_balance", "display_name": "余额方向", "description": "借/贷"},
|
|
34
|
+
{"name": "status", "display_name": "状态", "description": "启用/停用"},
|
|
35
|
+
{"name": "created_at", "display_name": "创建时间"},
|
|
36
|
+
],
|
|
37
|
+
},
|
|
38
|
+
"dim_cost_center": {
|
|
39
|
+
"display_name": "成本中心维表",
|
|
40
|
+
"description": "组织/利润中心主数据",
|
|
41
|
+
"columns": [
|
|
42
|
+
{"name": "cost_center_id", "display_name": "成本中心 ID", "description": "主键"},
|
|
43
|
+
{"name": "cost_center_code", "display_name": "编码"},
|
|
44
|
+
{"name": "cost_center_name", "display_name": "名称"},
|
|
45
|
+
{"name": "department", "display_name": "部门"},
|
|
46
|
+
{"name": "company_code", "display_name": "公司代码"},
|
|
47
|
+
{"name": "profit_center", "display_name": "利润中心"},
|
|
48
|
+
{"name": "status", "display_name": "状态"},
|
|
49
|
+
{"name": "created_at", "display_name": "创建时间"},
|
|
50
|
+
],
|
|
51
|
+
},
|
|
52
|
+
"fact_gl_journal_entry": {
|
|
53
|
+
"display_name": "总账实际分录",
|
|
54
|
+
"description": "凭证行粒度损益流水",
|
|
55
|
+
"columns": [
|
|
56
|
+
{"name": "entry_id", "display_name": "凭证 ID"},
|
|
57
|
+
{"name": "line_id", "display_name": "凭证行 ID"},
|
|
58
|
+
{"name": "date_key", "display_name": "日期键", "description": "关联 dim_date,YYYYMMDD"},
|
|
59
|
+
{"name": "posting_date", "display_name": "记账日期"},
|
|
60
|
+
{"name": "fiscal_year", "display_name": "会计年度"},
|
|
61
|
+
{"name": "fiscal_period", "display_name": "会计期间"},
|
|
62
|
+
{"name": "account_id", "display_name": "科目 ID", "description": "关联 dim_account"},
|
|
63
|
+
{"name": "account_code", "display_name": "科目编码", "description": "冗余"},
|
|
64
|
+
{"name": "account_name", "display_name": "科目名称", "description": "冗余"},
|
|
65
|
+
{"name": "account_type", "display_name": "科目类型", "description": "冗余"},
|
|
66
|
+
{"name": "pl_category", "display_name": "损益大类", "description": "冗余"},
|
|
67
|
+
{"name": "account_level", "display_name": "科目层级", "description": "冗余"},
|
|
68
|
+
{"name": "cost_center_id", "display_name": "成本中心 ID", "description": "关联 dim_cost_center"},
|
|
69
|
+
{"name": "cost_center_name", "display_name": "成本中心", "description": "冗余"},
|
|
70
|
+
{"name": "department", "display_name": "部门", "description": "冗余"},
|
|
71
|
+
{"name": "profit_center", "display_name": "利润中心", "description": "冗余"},
|
|
72
|
+
{"name": "debit_amount", "display_name": "借方"},
|
|
73
|
+
{"name": "credit_amount", "display_name": "贷方"},
|
|
74
|
+
{"name": "amount_signed", "display_name": "损益金额", "description": "收入为正、成本费用为负"},
|
|
75
|
+
{"name": "currency", "display_name": "币种"},
|
|
76
|
+
{"name": "voucher_no", "display_name": "凭证号"},
|
|
77
|
+
{"name": "source_system", "display_name": "来源系统"},
|
|
78
|
+
{"name": "description", "display_name": "摘要", "description": "凭证行摘要文本"},
|
|
79
|
+
{"name": "created_at", "display_name": "创建时间"},
|
|
80
|
+
],
|
|
81
|
+
},
|
|
82
|
+
"fact_budget_entry": {
|
|
83
|
+
"display_name": "预算明细",
|
|
84
|
+
"description": "预算行粒度编制数据",
|
|
85
|
+
"columns": [
|
|
86
|
+
{"name": "budget_id", "display_name": "预算批次"},
|
|
87
|
+
{"name": "line_id", "display_name": "预算行 ID"},
|
|
88
|
+
{"name": "date_key", "display_name": "日期键", "description": "关联 dim_date"},
|
|
89
|
+
{"name": "budget_version", "display_name": "预算版本"},
|
|
90
|
+
{"name": "fiscal_year", "display_name": "预算年度"},
|
|
91
|
+
{"name": "fiscal_period", "display_name": "预算期间", "description": "1-12"},
|
|
92
|
+
{"name": "account_id", "display_name": "科目 ID", "description": "关联 dim_account"},
|
|
93
|
+
{"name": "account_code", "display_name": "科目编码", "description": "冗余"},
|
|
94
|
+
{"name": "account_name", "display_name": "科目名称", "description": "冗余"},
|
|
95
|
+
{"name": "account_type", "display_name": "科目类型", "description": "冗余"},
|
|
96
|
+
{"name": "pl_category", "display_name": "损益大类", "description": "冗余"},
|
|
97
|
+
{"name": "cost_center_id", "display_name": "成本中心 ID", "description": "关联 dim_cost_center"},
|
|
98
|
+
{"name": "cost_center_name", "display_name": "成本中心", "description": "冗余"},
|
|
99
|
+
{"name": "department", "display_name": "部门", "description": "冗余"},
|
|
100
|
+
{"name": "budget_amount", "display_name": "预算金额"},
|
|
101
|
+
{"name": "currency", "display_name": "币种"},
|
|
102
|
+
{"name": "status", "display_name": "状态", "description": "草稿/已发布"},
|
|
103
|
+
{"name": "created_at", "display_name": "创建时间"},
|
|
104
|
+
],
|
|
105
|
+
},
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
# 与 347 + 附录 B 对齐;category 值必须是 347 标准中文名
|
|
109
|
+
CATEGORY_REGISTRY = {
|
|
110
|
+
"table": {
|
|
111
|
+
"维度表": ["dim_account", "dim_cost_center"],
|
|
112
|
+
"事实表": ["fact_gl_journal_entry", "fact_budget_entry"],
|
|
113
|
+
},
|
|
114
|
+
"cube": {
|
|
115
|
+
"流程型": ["ActualCube", "BudgetCube"],
|
|
116
|
+
"主体型": ["AccountActualCube", "CostCenterActualCube", "TimeActualCube"],
|
|
117
|
+
},
|
|
118
|
+
"object": {
|
|
119
|
+
"主数据": ["Account", "CostCenter"],
|
|
120
|
+
"事务": ["JournalEntry", "BudgetLine"],
|
|
121
|
+
"分析": ["ProfitAnalysis", "BudgetAnalysis"],
|
|
122
|
+
},
|
|
123
|
+
"relation": {
|
|
124
|
+
"时间关联": [
|
|
125
|
+
("fact_gl_journal_entry", "dim_date"),
|
|
126
|
+
("fact_budget_entry", "dim_date"),
|
|
127
|
+
],
|
|
128
|
+
"主数据关联": [
|
|
129
|
+
("fact_gl_journal_entry", "dim_account"),
|
|
130
|
+
("fact_gl_journal_entry", "dim_cost_center"),
|
|
131
|
+
("fact_budget_entry", "dim_account"),
|
|
132
|
+
("fact_budget_entry", "dim_cost_center"),
|
|
133
|
+
],
|
|
134
|
+
"层级自关联": [("dim_account", "dim_account")],
|
|
135
|
+
},
|
|
136
|
+
"link": {
|
|
137
|
+
"归属关系": [
|
|
138
|
+
"entry_belongs_account",
|
|
139
|
+
"entry_belongs_cost_center",
|
|
140
|
+
"budget_for_account",
|
|
141
|
+
"budget_for_cost_center",
|
|
142
|
+
],
|
|
143
|
+
"分析归因": [
|
|
144
|
+
"analysis_by_account",
|
|
145
|
+
"analysis_by_cost_center",
|
|
146
|
+
"account_contributes_profit",
|
|
147
|
+
"cost_center_contributes_profit",
|
|
148
|
+
],
|
|
149
|
+
"层级关系": ["account_has_parent"],
|
|
150
|
+
"对比关系": ["budget_compared_to_actual"],
|
|
151
|
+
},
|
|
152
|
+
"function": {
|
|
153
|
+
"总览分析": ["profit.fn.get_summary"],
|
|
154
|
+
"趋势分析": ["profit.fn.yoy_analysis", "profit.fn.mom_analysis"],
|
|
155
|
+
"结构分析": ["profit.fn.account_breakdown", "profit.fn.top_accounts"],
|
|
156
|
+
"预实分析": ["profit.fn.budget_vs_actual"],
|
|
157
|
+
"组织分析": ["profit.fn.cost_center_profit"],
|
|
158
|
+
},
|
|
159
|
+
}
|
|
160
|
+
|
|
17
161
|
|
|
18
162
|
def main():
|
|
19
|
-
space_id = "
|
|
163
|
+
space_id = "space__misc_01"
|
|
20
164
|
s = space.get(space_id)
|
|
21
165
|
|
|
22
166
|
output.print("=== 利润分析本体初始化 ===")
|
|
23
167
|
output.print(f"空间: {space_id}")
|
|
24
168
|
|
|
25
169
|
# 1. 创建物理表
|
|
26
|
-
output.print("\n[1/
|
|
170
|
+
output.print("\n[1/10] 创建物理表...")
|
|
27
171
|
|
|
28
172
|
s.sql.execute("""
|
|
29
|
-
CREATE TABLE IF NOT EXISTS
|
|
173
|
+
CREATE TABLE IF NOT EXISTS dim_account (
|
|
30
174
|
account_id String,
|
|
31
175
|
account_code String,
|
|
32
176
|
account_name String,
|
|
@@ -41,10 +185,10 @@ def main():
|
|
|
41
185
|
) ENGINE = MergeTree()
|
|
42
186
|
ORDER BY (account_code)
|
|
43
187
|
""")
|
|
44
|
-
output.print("OK
|
|
188
|
+
output.print("OK dim_account")
|
|
45
189
|
|
|
46
190
|
s.sql.execute("""
|
|
47
|
-
CREATE TABLE IF NOT EXISTS
|
|
191
|
+
CREATE TABLE IF NOT EXISTS dim_cost_center (
|
|
48
192
|
cost_center_id String,
|
|
49
193
|
cost_center_code String,
|
|
50
194
|
cost_center_name String,
|
|
@@ -56,12 +200,13 @@ def main():
|
|
|
56
200
|
) ENGINE = MergeTree()
|
|
57
201
|
ORDER BY (cost_center_id)
|
|
58
202
|
""")
|
|
59
|
-
output.print("OK
|
|
203
|
+
output.print("OK dim_cost_center")
|
|
60
204
|
|
|
61
205
|
s.sql.execute("""
|
|
62
|
-
CREATE TABLE IF NOT EXISTS
|
|
206
|
+
CREATE TABLE IF NOT EXISTS fact_gl_journal_entry (
|
|
63
207
|
entry_id String,
|
|
64
208
|
line_id String,
|
|
209
|
+
date_key Int32,
|
|
65
210
|
posting_date Date,
|
|
66
211
|
fiscal_year Int32,
|
|
67
212
|
fiscal_period Int32,
|
|
@@ -84,14 +229,15 @@ def main():
|
|
|
84
229
|
description String,
|
|
85
230
|
created_at DateTime DEFAULT now()
|
|
86
231
|
) ENGINE = MergeTree()
|
|
87
|
-
ORDER BY (
|
|
232
|
+
ORDER BY (date_key, entry_id, line_id)
|
|
88
233
|
""")
|
|
89
|
-
output.print("OK
|
|
234
|
+
output.print("OK fact_gl_journal_entry")
|
|
90
235
|
|
|
91
236
|
s.sql.execute("""
|
|
92
|
-
CREATE TABLE IF NOT EXISTS
|
|
237
|
+
CREATE TABLE IF NOT EXISTS fact_budget_entry (
|
|
93
238
|
budget_id String,
|
|
94
239
|
line_id String,
|
|
240
|
+
date_key Int32,
|
|
95
241
|
budget_version String,
|
|
96
242
|
fiscal_year Int32,
|
|
97
243
|
fiscal_period Int32,
|
|
@@ -110,88 +256,91 @@ def main():
|
|
|
110
256
|
) ENGINE = MergeTree()
|
|
111
257
|
ORDER BY (fiscal_year, fiscal_period, account_id, line_id)
|
|
112
258
|
""")
|
|
113
|
-
output.print("OK
|
|
114
|
-
|
|
115
|
-
# 2.
|
|
116
|
-
output.print("\n[2/
|
|
117
|
-
|
|
118
|
-
for
|
|
119
|
-
(
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
output.print(f"OK {
|
|
259
|
+
output.print("OK fact_budget_entry")
|
|
260
|
+
|
|
261
|
+
# 2. 注册表(含 display_name / description)
|
|
262
|
+
output.print("\n[2/10] 注册表到空间...")
|
|
263
|
+
|
|
264
|
+
for tbl_name, meta in TABLE_REGISTRY.items():
|
|
265
|
+
s.tables.register_with_meta(
|
|
266
|
+
table_name=tbl_name,
|
|
267
|
+
display_name=meta["display_name"],
|
|
268
|
+
description=meta.get("description"),
|
|
269
|
+
columns=meta["columns"],
|
|
270
|
+
force_column_meta=True,
|
|
271
|
+
)
|
|
272
|
+
output.print(f"OK {tbl_name} ({meta['display_name']})")
|
|
127
273
|
|
|
128
274
|
# 3. 注册表间关系
|
|
129
|
-
output.print("\n[3/
|
|
275
|
+
output.print("\n[3/10] 注册表间关系...")
|
|
130
276
|
|
|
131
277
|
table_relationships = [
|
|
132
278
|
{
|
|
133
|
-
"from_table": "
|
|
134
|
-
"to_table": "
|
|
135
|
-
"join_sql": "
|
|
279
|
+
"from_table": "fact_gl_journal_entry",
|
|
280
|
+
"to_table": "dim_date",
|
|
281
|
+
"join_sql": "fact_gl_journal_entry.date_key = dim_date.date_key",
|
|
282
|
+
"join_keys": [{"from": "date_key", "to": "date_key"}],
|
|
283
|
+
"relationship_type": "many_to_one",
|
|
284
|
+
"description": "分录关联日历",
|
|
285
|
+
},
|
|
286
|
+
{
|
|
287
|
+
"from_table": "fact_budget_entry",
|
|
288
|
+
"to_table": "dim_date",
|
|
289
|
+
"join_sql": "fact_budget_entry.date_key = dim_date.date_key",
|
|
290
|
+
"join_keys": [{"from": "date_key", "to": "date_key"}],
|
|
291
|
+
"relationship_type": "many_to_one",
|
|
292
|
+
"description": "预算关联日历",
|
|
293
|
+
},
|
|
294
|
+
{
|
|
295
|
+
"from_table": "fact_gl_journal_entry",
|
|
296
|
+
"to_table": "dim_account",
|
|
297
|
+
"join_sql": "fact_gl_journal_entry.account_id = dim_account.account_id",
|
|
136
298
|
"join_keys": [{"from": "account_id", "to": "account_id"}],
|
|
137
299
|
"relationship_type": "many_to_one",
|
|
138
300
|
"description": "实际分录关联会计科目",
|
|
139
301
|
},
|
|
140
302
|
{
|
|
141
|
-
"from_table": "
|
|
142
|
-
"to_table": "
|
|
143
|
-
"join_sql": "
|
|
303
|
+
"from_table": "fact_gl_journal_entry",
|
|
304
|
+
"to_table": "dim_cost_center",
|
|
305
|
+
"join_sql": "fact_gl_journal_entry.cost_center_id = dim_cost_center.cost_center_id",
|
|
144
306
|
"join_keys": [{"from": "cost_center_id", "to": "cost_center_id"}],
|
|
145
307
|
"relationship_type": "many_to_one",
|
|
146
308
|
"description": "实际分录关联成本中心",
|
|
147
309
|
},
|
|
148
310
|
{
|
|
149
|
-
"from_table": "
|
|
150
|
-
"to_table": "
|
|
151
|
-
"join_sql": "
|
|
311
|
+
"from_table": "fact_budget_entry",
|
|
312
|
+
"to_table": "dim_account",
|
|
313
|
+
"join_sql": "fact_budget_entry.account_id = dim_account.account_id",
|
|
152
314
|
"join_keys": [{"from": "account_id", "to": "account_id"}],
|
|
153
315
|
"relationship_type": "many_to_one",
|
|
154
316
|
"description": "预算关联会计科目",
|
|
155
317
|
},
|
|
156
318
|
{
|
|
157
|
-
"from_table": "
|
|
158
|
-
"to_table": "
|
|
159
|
-
"join_sql": "
|
|
319
|
+
"from_table": "fact_budget_entry",
|
|
320
|
+
"to_table": "dim_cost_center",
|
|
321
|
+
"join_sql": "fact_budget_entry.cost_center_id = dim_cost_center.cost_center_id",
|
|
160
322
|
"join_keys": [{"from": "cost_center_id", "to": "cost_center_id"}],
|
|
161
323
|
"relationship_type": "many_to_one",
|
|
162
324
|
"description": "预算关联成本中心",
|
|
163
325
|
},
|
|
164
326
|
{
|
|
165
|
-
"from_table": "
|
|
166
|
-
"to_table": "
|
|
167
|
-
"join_sql": "
|
|
327
|
+
"from_table": "dim_account",
|
|
328
|
+
"to_table": "dim_account",
|
|
329
|
+
"join_sql": "dim_account.parent_account_id = dim_account.account_id",
|
|
168
330
|
"join_keys": [{"from": "parent_account_id", "to": "account_id"}],
|
|
169
331
|
"relationship_type": "many_to_one",
|
|
170
332
|
"description": "科目上级(树形)",
|
|
171
333
|
},
|
|
172
|
-
{
|
|
173
|
-
"from_table": "budget_entry",
|
|
174
|
-
"to_table": "actual_journal_entry",
|
|
175
|
-
"join_sql": "budget_entry.account_id = actual_journal_entry.account_id AND budget_entry.cost_center_id = actual_journal_entry.cost_center_id AND budget_entry.fiscal_year = actual_journal_entry.fiscal_year AND budget_entry.fiscal_period = actual_journal_entry.fiscal_period",
|
|
176
|
-
"join_keys": [
|
|
177
|
-
{"from": "account_id", "to": "account_id"},
|
|
178
|
-
{"from": "cost_center_id", "to": "cost_center_id"},
|
|
179
|
-
{"from": "fiscal_year", "to": "fiscal_year"},
|
|
180
|
-
{"from": "fiscal_period", "to": "fiscal_period"},
|
|
181
|
-
],
|
|
182
|
-
"relationship_type": "many_to_one",
|
|
183
|
-
"description": "预算与实际同维对齐",
|
|
184
|
-
},
|
|
185
334
|
]
|
|
186
335
|
for rel in table_relationships:
|
|
187
336
|
rid = s.tables.add_relationship(**rel)
|
|
188
|
-
output.print(f"OK {rel['from_table']} -> {rel['to_table']}
|
|
337
|
+
output.print(f"OK {rel['from_table']} -> {rel['to_table']}")
|
|
189
338
|
|
|
190
339
|
# 4. 注册 Cube
|
|
191
|
-
output.print("\n[4/
|
|
340
|
+
output.print("\n[4/10] 注册 Cube...")
|
|
192
341
|
|
|
193
|
-
actual = "
|
|
194
|
-
budget = "
|
|
342
|
+
actual = "fact_gl_journal_entry"
|
|
343
|
+
budget = "fact_budget_entry"
|
|
195
344
|
|
|
196
345
|
s.register_cube(
|
|
197
346
|
name="ActualCube",
|
|
@@ -206,6 +355,7 @@ def main():
|
|
|
206
355
|
dimensions=[
|
|
207
356
|
{"name": "entry_id", "col": "entry_id", "type": "string", "title": "凭证ID"},
|
|
208
357
|
{"name": "line_id", "col": "line_id", "type": "string", "title": "行ID"},
|
|
358
|
+
{"name": "date_key", "col": "date_key", "type": "int", "title": "日期键"},
|
|
209
359
|
{"name": "posting_date", "col": "posting_date", "type": "date", "title": "记账日期"},
|
|
210
360
|
{"name": "fiscal_year", "col": "fiscal_year", "type": "int", "title": "会计年度"},
|
|
211
361
|
{"name": "fiscal_period", "col": "fiscal_period", "type": "int", "title": "会计期间"},
|
|
@@ -294,17 +444,16 @@ def main():
|
|
|
294
444
|
{"name": "line_count", "col": "line_id", "agg": "count", "title": "分录行数"},
|
|
295
445
|
],
|
|
296
446
|
dimensions=[
|
|
447
|
+
{"name": "date_key", "col": "date_key", "type": "int", "title": "日期键"},
|
|
297
448
|
{"name": "posting_date", "col": "posting_date", "type": "date", "title": "记账日期"},
|
|
298
449
|
{"name": "fiscal_year", "col": "fiscal_year", "type": "int", "title": "会计年度"},
|
|
299
450
|
{"name": "fiscal_period", "col": "fiscal_period", "type": "int", "title": "会计期间"},
|
|
300
|
-
{"name": "year_month", "col": "posting_date", "type": "string", "title": "年月"},
|
|
301
|
-
{"name": "quarter", "col": "posting_date", "type": "string", "title": "季度"},
|
|
302
451
|
],
|
|
303
452
|
)
|
|
304
453
|
output.print("OK TimeActualCube")
|
|
305
454
|
|
|
306
455
|
# 5. 派生度量
|
|
307
|
-
output.print("\n[5/
|
|
456
|
+
output.print("\n[5/10] 配置派生度量...")
|
|
308
457
|
|
|
309
458
|
def _pl_measures(cube_name, full=False):
|
|
310
459
|
base = [
|
|
@@ -375,7 +524,7 @@ def main():
|
|
|
375
524
|
output.print("OK 派生度量")
|
|
376
525
|
|
|
377
526
|
# 6. 对象类型
|
|
378
|
-
output.print("\n[6/
|
|
527
|
+
output.print("\n[6/10] 定义对象类型...")
|
|
379
528
|
|
|
380
529
|
object_types = [
|
|
381
530
|
("Account", "会计科目", "会计科目业务对象"),
|
|
@@ -390,7 +539,7 @@ def main():
|
|
|
390
539
|
output.print(f"OK {code}")
|
|
391
540
|
|
|
392
541
|
# 7. 绑定数据源
|
|
393
|
-
output.print("\n[7/
|
|
542
|
+
output.print("\n[7/10] 绑定数据源...")
|
|
394
543
|
|
|
395
544
|
bindings = [
|
|
396
545
|
("Account", "AccountActualCube"),
|
|
@@ -405,7 +554,7 @@ def main():
|
|
|
405
554
|
output.print(f"OK {obj} -> {cube}")
|
|
406
555
|
|
|
407
556
|
# 8. 属性
|
|
408
|
-
output.print("\n[8/
|
|
557
|
+
output.print("\n[8/10] 定义属性...")
|
|
409
558
|
|
|
410
559
|
def define_props(obj_code, props):
|
|
411
560
|
for code, name, role, qn in props:
|
|
@@ -478,7 +627,7 @@ def main():
|
|
|
478
627
|
output.print("OK BudgetAnalysis 属性 (6)")
|
|
479
628
|
|
|
480
629
|
# 9. 链接类型
|
|
481
|
-
output.print("\n[9/
|
|
630
|
+
output.print("\n[9/10] 定义链接与同步指标...")
|
|
482
631
|
|
|
483
632
|
link_types = [
|
|
484
633
|
("entry_belongs_account", "分录归属科目", "JournalEntry", "Account", "分录行对应科目"),
|
|
@@ -505,17 +654,26 @@ def main():
|
|
|
505
654
|
s.sync_metric_refs()
|
|
506
655
|
output.print("OK sync_metric_refs")
|
|
507
656
|
|
|
657
|
+
# 10. 347 对齐分类
|
|
658
|
+
output.print("\n[10/10] 配置对象分类(347 对齐)...")
|
|
659
|
+
|
|
660
|
+
cat_counts = s.categories.apply_registry(CATEGORY_REGISTRY, skip_missing=True)
|
|
661
|
+
for kind, cnt in cat_counts.items():
|
|
662
|
+
if cnt:
|
|
663
|
+
output.print(f"OK 分类[{kind}] 挂载 {cnt} 项")
|
|
664
|
+
|
|
508
665
|
summary = {
|
|
509
666
|
"ok": True,
|
|
510
667
|
"space_id": space_id,
|
|
511
668
|
"tables": 4,
|
|
512
|
-
"table_relationships":
|
|
669
|
+
"table_relationships": 7,
|
|
513
670
|
"cubes": 5,
|
|
514
671
|
"object_types": 6,
|
|
515
672
|
"properties": 42,
|
|
516
673
|
"link_types": 10,
|
|
674
|
+
"category_mounts": cat_counts,
|
|
517
675
|
}
|
|
518
676
|
|
|
519
677
|
output.print("\n=== 利润分析本体初始化完成 ===")
|
|
520
678
|
output.success("初始化成功")
|
|
521
|
-
output.print("__JSON_SUMMARY__" + json.dumps(summary, ensure_ascii=True, default=str))
|
|
679
|
+
output.print("__JSON_SUMMARY__" + json.dumps(summary, ensure_ascii=True, default=str))
|