cormort commited on
Commit
014227e
·
verified ·
1 Parent(s): 3c11621

Update src/app.py

Browse files
Files changed (1) hide show
  1. src/app.py +30 -48
src/app.py CHANGED
@@ -7,56 +7,42 @@ import sqlite3
7
  import os
8
  from database import init_db, load_full_project_example, backup_current_project, restore_database, list_backups, DB_PATH
9
 
10
- # 頁面初始化
11
- st.set_page_config(page_title="SPV 全方位財務監控", layout="wide")
12
  init_db()
 
13
 
14
- # Session State
15
  if "load_state" not in st.session_state:
16
  st.session_state.load_state = "idle"
17
 
18
- # --- 側邊欄:管理功能 ---
19
  with st.sidebar:
20
  st.header("⚙️ 系統管理")
21
  total_b = st.number_input("總工程預算 (萬)", value=50000, step=1000)
22
  project_start = st.date_input("計畫啟動日期", datetime.date(2026, 1, 20))
23
 
24
- st.divider()
25
- st.subheader("🔄 範例載入與還原")
26
  if st.session_state.load_state == "idle":
27
  if st.button("💡 載入全生命週期範例", use_container_width=True):
28
  st.session_state.load_state = "confirm"
29
  st.rerun()
30
  else:
31
- st.warning("載入將覆蓋現有數據並自動備份。")
32
  if st.button("✅ 確認載入", type="primary", use_container_width=True):
33
  backup_current_project()
34
  load_full_project_example(total_b)
35
  st.session_state.load_state = "idle"
36
- st.success("數據載入成功!")
37
  st.rerun()
38
  if st.button("❌ 取消", use_container_width=True):
39
  st.session_state.load_state = "idle"
40
  st.rerun()
41
 
42
- st.divider()
43
- uploaded_file = st.file_uploader("📤 上傳 .db 還原", type="db")
44
  if uploaded_file and st.button("🚀 執行還原"):
45
  restore_database(uploaded_file)
46
- st.success("還原成功!")
47
  st.rerun()
48
 
49
- st.subheader("📥 歷史備份下載")
50
- backups = list_backups()
51
- if backups:
52
- sel = st.selectbox("選擇檔案", backups)
53
- with open(sel, "rb") as f:
54
- st.download_button("下載備份", f, file_name=sel)
55
-
56
- # --- 主介面 Tabs ---
57
  tabs = st.tabs(["📊 損益與地主試算", "💰 現金流總表", "🏗️ 工程管理", "🏦 融資管理", "🏠 房屋銷售"])
58
 
59
- # --- Tab 1: 損益預測與地主試算 ---
60
  with tabs[0]:
61
  conn = sqlite3.connect(DB_PATH)
62
  inv = pd.read_sql_query("SELECT * FROM inventory", conn)
@@ -64,58 +50,54 @@ with tabs[0]:
64
  df_fin = pd.read_sql_query("SELECT * FROM finance_data WHERE 類型='實際'", conn)
65
  conn.close()
66
 
67
- if not inv.empty and not df_fin.empty:
68
  ratio = params[params['參數名稱']=='地主分回比例']['數值'].values[0] if not params.empty else 0.6
69
  total_val = (inv['預計售價'] * inv['總數量']).sum()
70
  landowner_val = total_val * ratio
71
  dev_val = total_val - landowner_val
72
-
73
- # 成本彙整
74
- costs = df_fin[['工程款', '管銷費用', '利息支出', '代銷費用', '其他稅費']].sum()
75
- total_cost = costs.sum()
76
- net_profit = dev_val - total_cost
77
-
78
- st.subheader("⚖️ 地主分回與實施者利潤分析")
79
- c1, c2, c3 = st.columns(3)
80
- c1.metric("全案總銷估值", f"{total_val:,.0f} 萬")
81
- c2.metric("地主分回價值", f"{landowner_val:,.0f} 萬", f"{ratio*100:.0f}%")
82
- c3.metric("實施者可售價值", f"{dev_val:,.0f} 萬")
83
 
84
- # 瀑布
85
  fig_w = go.Figure(go.Waterfall(
86
- orientation = "v",
87
- measure = ["absolute", "relative", "relative", "relative", "total"],
88
- x = ["可售總額", "工程成本", "利息支出", "代銷與稅", "剩餘毛利"],
89
- y = [dev_val, -costs['工程款'], -costs['利息支出'], -(costs['代銷費用']+costs['其他稅費']), 0],
90
  connector = {"line":{"color":"rgb(63, 63, 63)"}},
91
  ))
92
  st.plotly_chart(fig_w, use_container_width=True)
93
- else:
94
- st.info("請先載入範例數據以啟動損益分析。")
95
 
96
- # --- Tab 2: 現金流總表 (色系強化) ---
97
  with tabs[1]:
98
  conn = sqlite3.connect(DB_PATH)
99
  df_act = pd.read_sql_query("SELECT * FROM finance_data WHERE 類型='實際' ORDER BY 月份 ASC", conn)
100
  conn.close()
101
  if not df_act.empty:
102
- num_cols = ['工程款', '貸款撥入', '利息支出', '貸款還本', '預售收入', '代銷費用', '其他稅費']
103
  st.subheader("📅 現金流明細 (🟢 流入 / 🔴 流出)")
104
  st.dataframe(df_act.style.format(subset=num_cols, formatter="{:,.0f}")
105
  .applymap(lambda x: 'color: #228B22; font-weight: bold', subset=['貸款撥入', '預售收入'])
106
  .applymap(lambda x: 'color: #DC143C', subset=['工程款', '利息支出', '貸款還本', '代銷費用']),
107
  use_container_width=True)
108
- else:
109
- st.info("尚無執行數據。")
110
 
111
- # --- Tab 3: 工程管理 ---
112
  with tabs[2]:
113
  conn = sqlite3.connect(DB_PATH)
114
  stages = pd.read_sql_query("SELECT * FROM project_stages", conn)
115
- conn.close()
116
  if not stages.empty:
117
- st.subheader("🏗️ 工程進度排程")
118
  df_g = [dict(Task=r['工項名稱'], Start=(project_start + datetime.timedelta(days=r['開始月份']*30.4)),
119
  Finish=(project_start + datetime.timedelta(days=(r['開始月份']+r['持續月份'])*30.4)), Resource='工程') for _, r in stages.iterrows()]
120
- fig_g = ff.create_gantt(df_g, index_col='Resource', group_tasks=True)
121
- st.plotly_chart(fig_g, use_container_width=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
7
  import os
8
  from database import init_db, load_full_project_example, backup_current_project, restore_database, list_backups, DB_PATH
9
 
 
 
10
  init_db()
11
+ st.set_page_config(page_title="SPV 全方位財務監控", layout="wide")
12
 
 
13
  if "load_state" not in st.session_state:
14
  st.session_state.load_state = "idle"
15
 
16
+ # --- 側邊欄 ---
17
  with st.sidebar:
18
  st.header("⚙️ 系統管理")
19
  total_b = st.number_input("總工程預算 (萬)", value=50000, step=1000)
20
  project_start = st.date_input("計畫啟動日期", datetime.date(2026, 1, 20))
21
 
 
 
22
  if st.session_state.load_state == "idle":
23
  if st.button("💡 載入全生命週期範例", use_container_width=True):
24
  st.session_state.load_state = "confirm"
25
  st.rerun()
26
  else:
27
+ st.warning("⚠️ 載入將覆蓋數據並備份。")
28
  if st.button("✅ 確認載入", type="primary", use_container_width=True):
29
  backup_current_project()
30
  load_full_project_example(total_b)
31
  st.session_state.load_state = "idle"
 
32
  st.rerun()
33
  if st.button("❌ 取消", use_container_width=True):
34
  st.session_state.load_state = "idle"
35
  st.rerun()
36
 
37
+ uploaded_file = st.file_uploader("📤 上傳還原 (.db)", type="db")
 
38
  if uploaded_file and st.button("🚀 執行還原"):
39
  restore_database(uploaded_file)
 
40
  st.rerun()
41
 
42
+ # --- 主介面 ---
 
 
 
 
 
 
 
43
  tabs = st.tabs(["📊 損益與地主試算", "💰 現金流總表", "🏗️ 工程管理", "🏦 融資管理", "🏠 房屋銷售"])
44
 
45
+ # --- Tab 1: 損益與地主分回瀑布圖 ---
46
  with tabs[0]:
47
  conn = sqlite3.connect(DB_PATH)
48
  inv = pd.read_sql_query("SELECT * FROM inventory", conn)
 
50
  df_fin = pd.read_sql_query("SELECT * FROM finance_data WHERE 類型='實際'", conn)
51
  conn.close()
52
 
53
+ if not inv.empty:
54
  ratio = params[params['參數名稱']=='地主分回比例']['數值'].values[0] if not params.empty else 0.6
55
  total_val = (inv['預計售價'] * inv['總數量']).sum()
56
  landowner_val = total_val * ratio
57
  dev_val = total_val - landowner_val
58
+ costs = df_fin[['工程款', '利息支出', '代銷費用', '管銷費用']].sum()
59
+ net_profit = dev_val - costs.sum()
 
 
 
 
 
 
 
 
 
60
 
61
+ st.subheader("⚖️ 實施者利潤瀑布分析")
62
  fig_w = go.Figure(go.Waterfall(
63
+ orientation = "v", measure = ["absolute", "relative", "relative", "relative", "total"],
64
+ x = ["可售總額", "工程成本", "利息支出", "代銷與管銷", "預計淨利"],
65
+ y = [dev_val, -costs['工程款'], -costs['利息支出'], -(costs['代銷費用']+costs['管銷費用']), 0],
 
66
  connector = {"line":{"color":"rgb(63, 63, 63)"}},
67
  ))
68
  st.plotly_chart(fig_w, use_container_width=True)
 
 
69
 
70
+ # --- Tab 2: 現金流總表 (色系區分) ---
71
  with tabs[1]:
72
  conn = sqlite3.connect(DB_PATH)
73
  df_act = pd.read_sql_query("SELECT * FROM finance_data WHERE 類型='實際' ORDER BY 月份 ASC", conn)
74
  conn.close()
75
  if not df_act.empty:
76
+ num_cols = ['工程款', '貸款撥入', '利息支出', '貸款還本', '預售收入', '代銷費用']
77
  st.subheader("📅 現金流明細 (🟢 流入 / 🔴 流出)")
78
  st.dataframe(df_act.style.format(subset=num_cols, formatter="{:,.0f}")
79
  .applymap(lambda x: 'color: #228B22; font-weight: bold', subset=['貸款撥入', '預售收入'])
80
  .applymap(lambda x: 'color: #DC143C', subset=['工程款', '利息支出', '貸款還本', '代銷費用']),
81
  use_container_width=True)
 
 
82
 
83
+ # --- Tab 3, 4, 5: 修復空白分頁 ---
84
  with tabs[2]:
85
  conn = sqlite3.connect(DB_PATH)
86
  stages = pd.read_sql_query("SELECT * FROM project_stages", conn)
 
87
  if not stages.empty:
 
88
  df_g = [dict(Task=r['工項名稱'], Start=(project_start + datetime.timedelta(days=r['開始月份']*30.4)),
89
  Finish=(project_start + datetime.timedelta(days=(r['開始月份']+r['持續月份'])*30.4)), Resource='工程') for _, r in stages.iterrows()]
90
+ st.plotly_chart(ff.create_gantt(df_g, index_col='Resource', group_tasks=True), use_container_width=True)
91
+ conn.close()
92
+
93
+ with tabs[3]:
94
+ st.subheader("🏦 融資合約清單")
95
+ conn = sqlite3.connect(DB_PATH)
96
+ st.dataframe(pd.read_sql_query("SELECT 貸款名稱, 授信額度, 年利率, 狀態 FROM loan_contracts", conn), use_container_width=True)
97
+ conn.close()
98
+
99
+ with tabs[4]:
100
+ st.subheader("🏠 房屋銷售清單")
101
+ conn = sqlite3.connect(DB_PATH)
102
+ st.dataframe(pd.read_sql_query("SELECT 物件名稱, 預計售價, 總數量, 地主分回數量, 銷售狀態 FROM inventory", conn), use_container_width=True)
103
+ conn.close()