UnionGas/gaasu-tools
0
1#!/usr/bin/env python32# -*- coding: utf-8 -*-3"""4高本爐具(中山)有限公司 — 報告工具 Dashboard5════════════════════════════════════════════════6只負責:頁面設定、路由分發、工具載入。7 8新增部門(零改此檔):9 → 在 core/config.py DEPTS 加入 ready=False 條目(自動顯示佔位頁)10 → 若需自定義部門頁:在 departments/ 建新 .py,再在下方路由 elif 加一行11 12新增工具(只改兩處):13 → core/config.py TOOLS 加一個 dict14 → 下方 TOOL_LOADERS 加一行 { "key": loader_func }15════════════════════════════════════════════════16"""17from __future__ import annotations18import os, sys19from pathlib import Path20import streamlit as st21 22# ─── 路徑設定 ─────────────────────────────────────────────────────────────────23PROJECT_ROOT = Path(__file__).resolve().parent24 25# 確保 PROJECT_ROOT 在 sys.path(HF 工作目錄不一定與 app.py 相同)26if str(PROJECT_ROOT) not in sys.path:27 sys.path.insert(0, str(PROJECT_ROOT))28 29TOOLS_DIR = PROJECT_ROOT / "tools"30ASSETS_DIR = PROJECT_ROOT / "assets"31OUTPUTS_DIR = PROJECT_ROOT / "outputs"32RECON_DIR = TOOLS_DIR / "supplier_reconciliation"33MATCALC_DIR = TOOLS_DIR / "material_calc"34INV_RECON_DIR = TOOLS_DIR / "invoice_recon"35FINBUDGET_DIR = TOOLS_DIR / "financial_budget"36PO_GENERATOR_DIR = TOOLS_DIR / "po_generator"37 38for _d in [OUTPUTS_DIR,39 RECON_DIR / "scripts",40 MATCALC_DIR / "scripts",41 INV_RECON_DIR / "scripts",42 FINBUDGET_DIR / "scripts",43 PO_GENERATOR_DIR / "scripts"]:44 _d.mkdir(parents=True, exist_ok=True)45for _p in [str(RECON_DIR / "scripts"),46 str(MATCALC_DIR / "scripts"),47 str(INV_RECON_DIR / "scripts"),48 str(FINBUDGET_DIR / "scripts"),49 str(PO_GENERATOR_DIR / "scripts")]:50 if _p not in sys.path:51 sys.path.insert(0, _p)52 53IS_CLOUD = bool(os.environ.get("SPACE_ID") or os.environ.get("STREAMLIT_CLOUD"))54 55# ─── 頁面設定(必須最先執行) ─────────────────────────────────────────────────56st.set_page_config(57 page_title="高本爐具(中山)有限公司",58 page_icon="🏭",59 layout="wide",60 initial_sidebar_state="collapsed",61)62 63# ─── 核心模組 ─────────────────────────────────────────────────────────────────64from core.config import VALID_PAGES, DEPT_MAP65from core.styles import inject_global_css66from core.nav import render_tool_topbar67from departments.company import render_company_home68from departments.material import render_dept_material69from departments.finance import render_dept_finance70from departments.placeholder import render_dept_placeholder71 72# ─── 全站 CSS 注入(每次 rerun 執行一次) ────────────────────────────────────73inject_global_css()74 75# ─── 路由(URL query_params 為唯一真相來源) ─────────────────────────────────76_q = st.query_params.get("page", "company_home")77st.session_state["page"] = _q if _q in VALID_PAGES else "company_home"78page = st.session_state["page"]79 80 81# ══════════════════════════════════════════════════════════════════════════════82# 工具載入函數83# 新增工具:在此加一個 def,然後在 TOOL_LOADERS 加一行84# ══════════════════════════════════════════════════════════════════════════════85def _load_material_calc():86 render_tool_topbar("material_calc")87 try:88 import material_calc as matcalc # noqa: PLC041589 matcalc.render(PROJECT_ROOT, MATCALC_DIR, OUTPUTS_DIR, IS_CLOUD)90 except Exception as e:91 st.error(f"物料計算載入失敗:{e}")92 st.exception(e)93 94 95def _load_supplier_recon():96 render_tool_topbar("supplier_recon")97 try:98 import supplier_recon_ui as recon_ui # noqa: PLC041599 recon_ui.render(PROJECT_ROOT, RECON_DIR, ASSETS_DIR, OUTPUTS_DIR, IS_CLOUD)100 except Exception as e:101 st.error(f"供應商對帳載入失敗:{e}")102 st.exception(e)103 104 105def _load_financial_budget():106 render_tool_topbar("financial_budget")107 try:108 import financial_budget as fin_budget # noqa: PLC0415109 fin_budget.render(PROJECT_ROOT, FINBUDGET_DIR, OUTPUTS_DIR, IS_CLOUD)110 except Exception as e:111 st.error(f"財務預算載入失敗:{e}")112 st.exception(e)113 114 115def _load_invoice_recon():116 render_tool_topbar("invoice_recon")117 try:118 import invoice_recon_ui as inv_ui # noqa: PLC0415119 inv_ui.render(PROJECT_ROOT, INV_RECON_DIR, ASSETS_DIR, OUTPUTS_DIR, IS_CLOUD)120 except Exception as e:121 st.error(f"供應商付款發票對帳單載入失敗:{e}")122 st.exception(e)123 124 125def _load_po_generator():126 render_tool_topbar("po_generator")127 try:128 import po_generator as pg # noqa: PLC0415129 pg.render()130 except Exception as e:131 st.error(f"採購單生成載入失敗:{e}")132 st.exception(e)133 134 135# ── 工具分發表(key → loader),新增工具只需在此加一行 ──────────────────────136TOOL_LOADERS: dict[str, object] = {137 "material_calc": _load_material_calc,138 "supplier_recon": _load_supplier_recon,139 "invoice_recon": _load_invoice_recon,140 "financial_budget": _load_financial_budget,141 "po_generator": _load_po_generator,142}143 144 145# ══════════════════════════════════════════════════════════════════════════════146# 路由分發147# ══════════════════════════════════════════════════════════════════════════════148if page == "company_home":149 render_company_home()150 151elif page == "dept_material":152 render_dept_material()153 154elif page == "dept_finance":155 render_dept_finance()156 157elif page.startswith("dept_"):158 # 所有其他部門 → 自動佔位頁(ready=False 時)159 # 若部門已上線,在此加 elif page == "dept_xxx": render_dept_xxx()160 dept_key = page[5:] # 去掉 "dept_" 前綴161 if dept_key in DEPT_MAP:162 render_dept_placeholder(dept_key)163 else:164 st.query_params["page"] = "company_home"165 st.rerun()166 167elif page in TOOL_LOADERS:168 TOOL_LOADERS[page]()169 170else:171 # 未知頁面 → 導回主頁172 st.query_params["page"] = "company_home"173 st.rerun()174 