diff --git a/backend/helpers.py b/backend/helpers.py index 90a8d12..efec6f7 100644 --- a/backend/helpers.py +++ b/backend/helpers.py @@ -65,7 +65,7 @@ def monthly_finance(conn, tenant="科普·无界"): _year = date.today().year months = [f"{_year}-{m:02d}" for m in range(1, 13)] pfs = rows(conn, - "SELECT sign_amount, sign_month, status, budget_data, expense_data FROM project_finances WHERE tenant=? AND status='已签约'", + "SELECT sign_amount, sign_month, budget_data, expense_data FROM project_finances WHERE tenant=?", [tenant]) parsed_budgets = [] @@ -104,7 +104,7 @@ def monthly_finance(conn, tenant="科普·无界"): key = month.replace("-", "_") revenue = gross = payment = cost = paid = sign = 0 for pf, budget_map in parsed_budgets: - if pf["status"] == "已签约" and (pf.get("sign_month") or "") == month: + if (pf.get("sign_month") or "") == month: sign += float(pf["sign_amount"] or 0) b = budget_map.get(key) if b: diff --git a/backend/migrations/__init__.py b/backend/migrations/__init__.py index ac433f6..0efdece 100644 --- a/backend/migrations/__init__.py +++ b/backend/migrations/__init__.py @@ -17,7 +17,7 @@ def run_migrations(): """ from migrations.tables import migrate_create_tables from migrations.columns import migrate_add_columns - from migrations.data_fixes import migrate_fix_task_status, migrate_rename_tenant, migrate_drop_product_fields, migrate_fix_service_fee_standard, migrate_fix_expense_status, migrate_move_pending_to_plan + from migrations.data_fixes import migrate_fix_task_status, migrate_rename_tenant, migrate_drop_product_fields, migrate_fix_service_fee_standard, migrate_fix_expense_status from migrations.data_split import migrate_data_split from migrations.seed import migrate_seed_users, migrate_seed_demo_data @@ -28,7 +28,6 @@ def run_migrations(): migrate_drop_product_fields() migrate_fix_service_fee_standard() migrate_fix_expense_status() - migrate_move_pending_to_plan() migrate_data_split() migrate_seed_users() migrate_seed_demo_data() diff --git a/backend/migrations/columns.py b/backend/migrations/columns.py index a5949f0..72d218c 100644 --- a/backend/migrations/columns.py +++ b/backend/migrations/columns.py @@ -122,6 +122,12 @@ def migrate_add_columns(): cur.execute("ALTER TABLE plan_finances DROP COLUMN status") conn.commit() print("[migrate] plan_finances.status 列已删除") + # 删除 project_finances.status 列(实际模块不再需要状态字段) + cur.execute("SHOW COLUMNS FROM project_finances LIKE 'status'") + if cur.fetchone(): + cur.execute("ALTER TABLE project_finances DROP COLUMN status") + conn.commit() + print("[migrate] project_finances.status 列已删除") cur.close() finally: conn.close() diff --git a/backend/migrations/data_fixes.py b/backend/migrations/data_fixes.py index 0a3a384..f1c10e3 100644 --- a/backend/migrations/data_fixes.py +++ b/backend/migrations/data_fixes.py @@ -114,34 +114,3 @@ def migrate_fix_expense_status(): conn.close() -def migrate_move_pending_to_plan(): - """迁移业务待签约项目到计划模块(幂等)""" - from db import db - - conn = db() - try: - cur = conn.cursor() - # 获取 plan_finances 的列名(排除 status,因计划模块已删除该列) - cur.execute("SHOW COLUMNS FROM plan_finances") - plan_cols = [row[0] for row in cur.fetchall()] - col_list = ", ".join(plan_cols) - - # 仅迁移 id 不存在于计划表的待签约项目 - cur.execute( - f"INSERT INTO plan_finances ({col_list}) " - f"SELECT {col_list} FROM project_finances WHERE status='待签约' " - f"AND id NOT IN (SELECT id FROM plan_finances)" - ) - moved = cur.rowcount - if moved: - # 删除已迁移的原记录 - cur.execute( - "DELETE FROM project_finances WHERE status='待签约' " - "AND id IN (SELECT id FROM plan_finances)" - ) - deleted = cur.rowcount - conn.commit() - print(f"[migrate] 待签约项目迁移到计划: {moved} 条 (已删除 {deleted} 条)") - cur.close() - finally: - conn.close() diff --git a/backend/migrations/seed_data.py b/backend/migrations/seed_data.py index b178b06..20f2517 100644 --- a/backend/migrations/seed_data.py +++ b/backend/migrations/seed_data.py @@ -72,7 +72,7 @@ def make_budget_data(sign_amount, signed_month, status): """生成12个月的预算数据""" months = [] start_m = int(signed_month.split("-")[1]) if signed_month else 1 - rev_total = sign_amount if status == "已签约" else int(sign_amount * random.uniform(0.3, 0.7)) + rev_total = sign_amount gross_rate = random.uniform(0.25, 0.55) payment_rate = random.uniform(0.7, 0.95) @@ -135,17 +135,17 @@ def seed_db(): cust_idx += i % 3 # 70% 已签约, 30% 待签约 - status = "已签约" if random.random() < 0.7 else "待签约" - sign_month = f"2026-{random.randint(1, 6):02d}" if status == "已签约" else "" + status = "已签约" + sign_month = f"2026-{random.randint(1, 6):02d}" project_code = f"{tenant[:2]}-2026-{i+1:03d}" business_type = random.choice(["学术推广", "医生教育", "患者管理", "科研合作", "科普内容"]) # 如果签约,总额 = 签约额;否则为0 - total_rev = int(sign_amount * random.uniform(0.8, 1.0)) if status == "已签约" else 0 - total_gross = int(total_rev * random.uniform(0.25, 0.5)) if status == "已签约" else 0 - total_payment = int(total_rev * random.uniform(0.7, 0.9)) if status == "已签约" else 0 - total_cost = int(total_rev * random.uniform(0.3, 0.5)) if status == "已签约" else 0 - total_paid = int(total_cost * random.uniform(0.7, 0.95)) if status == "已签约" else 0 + total_rev = int(sign_amount * random.uniform(0.8, 1.0)) + total_gross = int(total_rev * random.uniform(0.25, 0.5)) + total_payment = int(total_rev * random.uniform(0.7, 0.9)) + total_cost = int(total_rev * random.uniform(0.3, 0.5)) + total_paid = int(total_cost * random.uniform(0.7, 0.95)) budget_data = make_budget_data(sign_amount, sign_month, status) expense_data = make_expense_data() if status == "已签约" else "[]" @@ -157,16 +157,16 @@ def seed_db(): cur = _exec(conn, """ INSERT INTO project_finances - (tenant, project_id, business_type, customer_name, sign_amount, sign_month, status, + (tenant, project_id, business_type, customer_name, sign_amount, sign_month, sales_person, total_rev, total_gross, total_payment, total_cost, total_paid, budget_data, expense_data, task_data, project_code, start_date, end_date, task_type, task_count, service_fee_standard, project_manager, contact_name, contact_phone, other_info, created_at, updated_at) - VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?) + VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?) """, ( tenant, f"PF-{tenant[:2]}-{i+1:04d}", business_type, cust_name, - sign_amount, sign_month, status, + sign_amount, sign_month, f"销售{random.choice(['A','B','C','D'])}", total_rev, total_gross, total_payment, total_cost, total_paid, budget_data, expense_data, task_data, @@ -178,7 +178,7 @@ def seed_db(): today, today, )) # Store for later use - pf_ids.setdefault(tenant, []).append((cur.lastrowid, proj_name, cust_name, status)) + pf_ids.setdefault(tenant, []).append((cur.lastrowid, proj_name, cust_name)) # operation_projects: 每个工作台 3-5 个 op_count = random.randint(3, 5) diff --git a/backend/routes.py b/backend/routes.py index bbbe99c..4b360f9 100644 --- a/backend/routes.py +++ b/backend/routes.py @@ -23,7 +23,7 @@ TABLES = { "products": ("product_versions", ["product_name", "version", "version_goal", "priority", "start_date", "plan_date", "dev_done_date", "test_date", "launch_date", "status", "notes", "tenant"]), "finance": ("finance_records", ["month", "project_name", "record_type", "category", "amount", "occurred_date", "notes", "tenant"]), "tasks": ("project_tasks", ["project_id", "phase", "milestone", "task", "owner", "due_date", "blockers", "notes", "status", "sort_order", "priority", "tenant"]), - "projectFinances": ("project_finances", ["project_id", "tenant", "business_type", "customer_name", "sign_amount", "sign_month", "status", "sales_person", "owner", "total_rev", "total_gross", "total_payment", "total_cost", "total_paid", "budget_data", "expense_data", "start_date", "end_date", "task_type", "task_count", "service_fee_standard", "project_manager", "task_data", "project_code", "contact_name", "contact_phone", "other_info"]), + "projectFinances": ("project_finances", ["project_id", "tenant", "business_type", "customer_name", "sign_amount", "sign_month", "sales_person", "owner", "total_rev", "total_gross", "total_payment", "total_cost", "total_paid", "budget_data", "expense_data", "start_date", "end_date", "task_type", "task_count", "service_fee_standard", "project_manager", "task_data", "project_code", "contact_name", "contact_phone", "other_info"]), "expense": ("expense_records", ["expense_type", "expense_month", "amount", "incurred_amount", "notes", "status", "tenant"]), "planFinances": ("plan_finances", ["project_id", "tenant", "business_type", "customer_name", "sign_amount", "sign_month", "sales_person", "owner", "total_rev", "total_gross", "total_payment", "total_cost", "total_paid", "budget_data", "expense_data", "start_date", "end_date", "task_type", "task_count", "service_fee_standard", "project_manager", "task_data", "project_code", "contact_name", "contact_phone", "other_info"]), "planExpense": ("plan_expense_records", ["expense_type", "expense_month", "amount", "incurred_amount", "notes", "status", "tenant"]), @@ -276,7 +276,7 @@ def bootstrap(): t_products = attach_common(conn, "products", rows(conn, "SELECT * FROM product_versions WHERE tenant=? ORDER BY id DESC", [t])) t_expense = rows(conn, "SELECT * FROM expense_records WHERE tenant=?", [t]) t_proposals = attach_common(conn, "proposals", rows(conn, "SELECT * FROM business_proposals WHERE tenant=? ORDER BY id DESC", [t])) - t_signed_pfs = [x for x in t_pfs if x["status"] == "已签约"] + t_signed_pfs = t_pfs def t_parse_budget(pf): try: budget = json.loads(pf.get("budget_data") or "[]") @@ -326,12 +326,12 @@ def bootstrap(): "total_proposals": len(t_ops), "total_products": len(t_proposals), "upcoming_products": len(t_products), - "signed_amount": sum(x["sign_amount"] or 0 for x in t_pfs if x["status"] == "已签约"), - "signed_annual": sum(x["sign_amount"] or 0 for x in t_pfs if x["status"] == "已签约"), - "signed_q2": sum(x["sign_amount"] or 0 for x in t_pfs if x["status"] == "已签约" and (x.get("sign_month") or "")[:7] in _q_months), - "signed_month": sum(x["sign_amount"] or 0 for x in t_pfs if x["status"] == "已签约" and (x.get("sign_month") or "")[:7] == f"{_year}-{_now_month:02d}"), - "signed_prev_q": sum(x["sign_amount"] or 0 for x in t_pfs if x["status"] == "已签约" and (x.get("sign_month") or "")[:7] in _prev_q_months), - "signed_prev_month": sum(x["sign_amount"] or 0 for x in t_pfs if x["status"] == "已签约" and (x.get("sign_month") or "")[:7] == f"{_year}-{_prev_month:02d}") if _prev_month else 0, + "signed_amount": sum(x["sign_amount"] or 0 for x in t_pfs), + "signed_annual": sum(x["sign_amount"] or 0 for x in t_pfs), + "signed_q2": sum(x["sign_amount"] or 0 for x in t_pfs if (x.get("sign_month") or "")[:7] in _q_months), + "signed_month": sum(x["sign_amount"] or 0 for x in t_pfs if (x.get("sign_month") or "")[:7] == f"{_year}-{_now_month:02d}"), + "signed_prev_q": sum(x["sign_amount"] or 0 for x in t_pfs if (x.get("sign_month") or "")[:7] in _prev_q_months), + "signed_prev_month": sum(x["sign_amount"] or 0 for x in t_pfs if (x.get("sign_month") or "")[:7] == f"{_year}-{_prev_month:02d}") if _prev_month else 0, "revenue_annual": t_sum_budget("rev", range(1, 13)), "revenue_q2": t_sum_budget("rev", _q_range), "monthly_revenue": t_sum_budget("rev", [_now_month]), @@ -442,7 +442,7 @@ def bootstrap(): expense = q("SELECT * FROM expense_records WHERE tenant=? ORDER BY id DESC", tenant) planPfs = q("SELECT * FROM plan_finances WHERE tenant=? ORDER BY id DESC", tenant) planExpense = q("SELECT * FROM plan_expense_records WHERE tenant=? ORDER BY id DESC", tenant) - signed_pfs = [x for x in pfs if x["status"] == "已签约"] + signed_pfs = pfs def parse_budget(pf): try: @@ -563,16 +563,14 @@ def bootstrap(): profit_month = gross_month - proj_expense_month profit_prev_q = gross_prev_q - proj_expense_prev_q profit_prev_month = gross_prev_month - proj_expense_prev_month - def pf_status_sum(status): - return sum(x["sign_amount"] or 0 for x in pfs if x["status"] == status) - signed_amount = pf_status_sum("已签约") - signed_annual = sum(x["sign_amount"] or 0 for x in pfs if x["status"] == "已签约") + signed_amount = sum(x["sign_amount"] or 0 for x in pfs) + signed_annual = sum(x["sign_amount"] or 0 for x in pfs) _q_months = [f"{_year}-{m:02d}" for m in _q_range] - signed_q2 = sum(x["sign_amount"] or 0 for x in pfs if x["status"] == "已签约" and (x.get("sign_month") or "")[:7] in _q_months) - signed_month = sum(x["sign_amount"] or 0 for x in pfs if x["status"] == "已签约" and (x.get("sign_month") or "")[:7] == f"{_year}-{_now_month:02d}") + signed_q2 = sum(x["sign_amount"] or 0 for x in pfs if (x.get("sign_month") or "")[:7] in _q_months) + signed_month = sum(x["sign_amount"] or 0 for x in pfs if (x.get("sign_month") or "")[:7] == f"{_year}-{_now_month:02d}") _prev_q_months = [f"{_year}-{m:02d}" for m in _prev_q_range] - signed_prev_q = sum(x["sign_amount"] or 0 for x in pfs if x["status"] == "已签约" and (x.get("sign_month") or "")[:7] in _prev_q_months) - signed_prev_month = sum(x["sign_amount"] or 0 for x in pfs if x["status"] == "已签约" and (x.get("sign_month") or "")[:7] == f"{_year}-{_prev_month:02d}") if _prev_month else 0 + signed_prev_q = sum(x["sign_amount"] or 0 for x in pfs if (x.get("sign_month") or "")[:7] in _prev_q_months) + signed_prev_month = sum(x["sign_amount"] or 0 for x in pfs if (x.get("sign_month") or "")[:7] == f"{_year}-{_prev_month:02d}") if _prev_month else 0 pipeline_amount = sum(x["expected_contract_amount"] or 0 for x in operations if x["project_status"] not in ["已签约","已丢单","已归档","已完成"]) signed_not_executed = sum(x["expected_contract_amount"] or 0 for x in operations if x["project_type"] == "execution" and x["execution_progress"] < 100) summary = { diff --git a/static/modules/finance.js b/static/modules/finance.js index d705221..3b5f922 100644 --- a/static/modules/finance.js +++ b/static/modules/finance.js @@ -24,8 +24,8 @@ function renderFinance() { const months = displayMonths.map(d => d.key); const monthLabels = displayMonths.map(d => d.label); - const signed = pfs.filter(x => x.status === "已签约"); - const pending = pfs.filter(x => x.status === "待签约"); + const signed = pfs; + const pending = pfs; const sumSign = Math.round(signed.reduce((s,x) => s + (x.sign_amount||0), 0)); const sumPending = Math.round(pending.reduce((s,x) => s + (x.sign_amount||0), 0)); @@ -116,7 +116,7 @@ function renderFinance() { const finHeaderBase = `视图:${toolDateSelect}`; const finAddBtn = ``; - const finFilterTabs = `
`; + const finFilterTabs = ``; document.querySelector("#finance").innerHTML = `OPC Manager v1.0.0.22
+OPC Manager v1.0.0.23