416 lines
21 KiB
Plaintext
416 lines
21 KiB
Plaintext
from PDAPI import *
|
||
from Utils.Models import GERPCashReceiptsPaymentsLedger, GERPAsset, GERPLiability
|
||
import datetime
|
||
from sqlalchemy import func
|
||
import flask
|
||
|
||
__metadata__ = {
|
||
"route": "{DEFAULT_ROUTE}",
|
||
"methods": ["GET"],
|
||
"blueprint": "GERP",
|
||
"permission": 1.0,
|
||
"attributes": [],
|
||
"limit": "1 per second",
|
||
"args": [
|
||
{"Name": "year", "Type": "int", "Required": False},
|
||
{"Name": "month", "Type": "int", "Required": False, "MinLen": 1, "MaxLen": 12}
|
||
]
|
||
}
|
||
|
||
def HandleRequest():
|
||
# 获取请求参数
|
||
year = flask.request.args.get('year', type=int)
|
||
month = flask.request.args.get('month', type=int)
|
||
|
||
# 获取当前时间
|
||
now = datetime.datetime.now()
|
||
current_year = year if year else now.year
|
||
|
||
# 判断是否为全年统计
|
||
is_full_year = not month
|
||
|
||
if is_full_year:
|
||
# 全年统计:计算全年的开始和结束时间
|
||
current_year_start = datetime.datetime(current_year, 1, 1)
|
||
current_year_end = datetime.datetime(current_year + 1, 1, 1)
|
||
|
||
# 上一年的开始和结束时间
|
||
last_year_start = datetime.datetime(current_year - 1, 1, 1)
|
||
last_year_end = current_year_start
|
||
|
||
# 查询今年数据
|
||
current_month_data = GERPCashReceiptsPaymentsLedger.query.filter(
|
||
GERPCashReceiptsPaymentsLedger.CashBookTransactionTime >= current_year_start,
|
||
GERPCashReceiptsPaymentsLedger.CashBookTransactionTime < current_year_end
|
||
).all()
|
||
|
||
# 查询去年数据
|
||
last_month_data = GERPCashReceiptsPaymentsLedger.query.filter(
|
||
GERPCashReceiptsPaymentsLedger.CashBookTransactionTime >= last_year_start,
|
||
GERPCashReceiptsPaymentsLedger.CashBookTransactionTime < last_year_end
|
||
).all()
|
||
|
||
# 设置当前月份为12(用于显示)
|
||
current_month = 12
|
||
else:
|
||
# 月度统计:使用提供的月份或当前月份
|
||
current_month = int(month)
|
||
|
||
# 计算本月和上月的开始和结束时间
|
||
current_month_start = datetime.datetime(current_year, current_month, 1)
|
||
if current_month == 12:
|
||
next_month_start = datetime.datetime(current_year + 1, 1, 1)
|
||
last_month_start = datetime.datetime(current_year, 11, 1)
|
||
last_month_end = current_month_start
|
||
elif current_month == 1:
|
||
next_month_start = datetime.datetime(current_year, current_month + 1, 1)
|
||
last_month_start = datetime.datetime(current_year - 1, 12, 1)
|
||
last_month_end = current_month_start
|
||
else:
|
||
next_month_start = datetime.datetime(current_year, current_month + 1, 1)
|
||
last_month_start = datetime.datetime(current_year, current_month - 1, 1)
|
||
last_month_end = current_month_start
|
||
|
||
# 查询本月数据
|
||
current_month_data = GERPCashReceiptsPaymentsLedger.query.filter(
|
||
GERPCashReceiptsPaymentsLedger.CashBookTransactionTime >= current_month_start,
|
||
GERPCashReceiptsPaymentsLedger.CashBookTransactionTime < next_month_start
|
||
).all()
|
||
|
||
# 查询上月数据
|
||
last_month_data = GERPCashReceiptsPaymentsLedger.query.filter(
|
||
GERPCashReceiptsPaymentsLedger.CashBookTransactionTime >= last_month_start,
|
||
GERPCashReceiptsPaymentsLedger.CashBookTransactionTime < last_month_end
|
||
).all()
|
||
|
||
# 计算本月营收(收入类型:CashBookTransactionType = 1)
|
||
if is_full_year:
|
||
current_month_revenue = db.session.query(func.sum(GERPCashReceiptsPaymentsLedger.CashBookAmount)).filter(
|
||
GERPCashReceiptsPaymentsLedger.CashBookTransactionTime >= current_year_start,
|
||
GERPCashReceiptsPaymentsLedger.CashBookTransactionTime < current_year_end,
|
||
GERPCashReceiptsPaymentsLedger.CashBookTransactionType == 1
|
||
).scalar() or 0
|
||
|
||
# 计算本月支出(支出类型:CashBookTransactionType = 0)
|
||
current_month_expense = db.session.query(func.sum(GERPCashReceiptsPaymentsLedger.CashBookAmount)).filter(
|
||
GERPCashReceiptsPaymentsLedger.CashBookTransactionTime >= current_year_start,
|
||
GERPCashReceiptsPaymentsLedger.CashBookTransactionTime < current_year_end,
|
||
GERPCashReceiptsPaymentsLedger.CashBookTransactionType == 0
|
||
).scalar() or 0
|
||
|
||
# 计算上月(去年)营收和支出
|
||
last_month_revenue = db.session.query(func.sum(GERPCashReceiptsPaymentsLedger.CashBookAmount)).filter(
|
||
GERPCashReceiptsPaymentsLedger.CashBookTransactionTime >= last_year_start,
|
||
GERPCashReceiptsPaymentsLedger.CashBookTransactionTime < last_year_end,
|
||
GERPCashReceiptsPaymentsLedger.CashBookTransactionType == 1
|
||
).scalar() or 0
|
||
|
||
last_month_expense = db.session.query(func.sum(GERPCashReceiptsPaymentsLedger.CashBookAmount)).filter(
|
||
GERPCashReceiptsPaymentsLedger.CashBookTransactionTime >= last_year_start,
|
||
GERPCashReceiptsPaymentsLedger.CashBookTransactionTime < last_year_end,
|
||
GERPCashReceiptsPaymentsLedger.CashBookTransactionType == 0
|
||
).scalar() or 0
|
||
else:
|
||
current_month_revenue = db.session.query(func.sum(GERPCashReceiptsPaymentsLedger.CashBookAmount)).filter(
|
||
GERPCashReceiptsPaymentsLedger.CashBookTransactionTime >= current_month_start,
|
||
GERPCashReceiptsPaymentsLedger.CashBookTransactionTime < next_month_start,
|
||
GERPCashReceiptsPaymentsLedger.CashBookTransactionType == 1
|
||
).scalar() or 0
|
||
|
||
# 计算本月支出(支出类型:CashBookTransactionType = 0)
|
||
current_month_expense = db.session.query(func.sum(GERPCashReceiptsPaymentsLedger.CashBookAmount)).filter(
|
||
GERPCashReceiptsPaymentsLedger.CashBookTransactionTime >= current_month_start,
|
||
GERPCashReceiptsPaymentsLedger.CashBookTransactionTime < next_month_start,
|
||
GERPCashReceiptsPaymentsLedger.CashBookTransactionType == 0
|
||
).scalar() or 0
|
||
|
||
# 计算上月营收和支出
|
||
last_month_revenue = db.session.query(func.sum(GERPCashReceiptsPaymentsLedger.CashBookAmount)).filter(
|
||
GERPCashReceiptsPaymentsLedger.CashBookTransactionTime >= last_month_start,
|
||
GERPCashReceiptsPaymentsLedger.CashBookTransactionTime < last_month_end,
|
||
GERPCashReceiptsPaymentsLedger.CashBookTransactionType == 1
|
||
).scalar() or 0
|
||
|
||
last_month_expense = db.session.query(func.sum(GERPCashReceiptsPaymentsLedger.CashBookAmount)).filter(
|
||
GERPCashReceiptsPaymentsLedger.CashBookTransactionTime >= last_month_start,
|
||
GERPCashReceiptsPaymentsLedger.CashBookTransactionTime < last_month_end,
|
||
GERPCashReceiptsPaymentsLedger.CashBookTransactionType == 0
|
||
).scalar() or 0
|
||
|
||
# 计算本月净利润
|
||
current_month_profit = current_month_revenue - current_month_expense
|
||
last_month_profit = last_month_revenue - last_month_expense
|
||
|
||
# 计算环比增长率
|
||
def calculate_growth(current, last):
|
||
if last == 0:
|
||
return "0%"
|
||
growth_rate = ((current - last) / last) * 100
|
||
return f"{growth_rate:.1f}%"
|
||
|
||
# 计算增长率数值(用于趋势判断)
|
||
def get_growth_value(current, last):
|
||
if last == 0:
|
||
return 0
|
||
return ((current - last) / last) * 100
|
||
|
||
revenue_growth = calculate_growth(current_month_revenue, last_month_revenue)
|
||
expense_growth = calculate_growth(current_month_expense, last_month_expense)
|
||
profit_growth = calculate_growth(current_month_profit, last_month_profit)
|
||
|
||
# 获取增长率数值
|
||
revenue_growth_value = get_growth_value(current_month_revenue, last_month_revenue)
|
||
expense_growth_value = get_growth_value(current_month_expense, last_month_expense)
|
||
profit_growth_value = get_growth_value(current_month_profit, last_month_profit)
|
||
|
||
# 计算各项财务指标
|
||
# 未结款项(状态为0的交易)
|
||
unsettled_amount = sum(item.CashBookAmount for item in current_month_data if item.CashBookStatus == 0)
|
||
|
||
# 现金余额(计算到本月末的累计值)
|
||
# 优化:使用SQL直接聚合计算现金余额
|
||
def calculate_cash_balance(end_time):
|
||
# 计算收入总额
|
||
income = db.session.query(func.sum(GERPCashReceiptsPaymentsLedger.CashBookAmount)).filter(
|
||
GERPCashReceiptsPaymentsLedger.CashBookStatus == 1,
|
||
GERPCashReceiptsPaymentsLedger.CashBookTransactionType == 1,
|
||
GERPCashReceiptsPaymentsLedger.CashBookTransactionTime < end_time
|
||
).scalar() or 0
|
||
|
||
# 计算支出总额
|
||
expense = db.session.query(func.sum(GERPCashReceiptsPaymentsLedger.CashBookAmount)).filter(
|
||
GERPCashReceiptsPaymentsLedger.CashBookStatus == 1,
|
||
GERPCashReceiptsPaymentsLedger.CashBookTransactionType == 0,
|
||
GERPCashReceiptsPaymentsLedger.CashBookTransactionTime < end_time
|
||
).scalar() or 0
|
||
|
||
# 现金余额 = 收入 - 支出
|
||
return income - expense
|
||
|
||
if is_full_year:
|
||
# 全年统计:计算到本年末的现金余额
|
||
cash_balance = calculate_cash_balance(current_year_end)
|
||
|
||
# 全年统计:比较上一年结束时的现金余额
|
||
last_month_cash_balance = calculate_cash_balance(last_year_end)
|
||
else:
|
||
# 月度统计:计算到本月末的现金余额
|
||
cash_balance = calculate_cash_balance(next_month_start)
|
||
|
||
# 月度统计:比较上月结束时的现金余额
|
||
last_month_cash_balance = calculate_cash_balance(last_month_end)
|
||
|
||
# 毛利率(简化计算:(营收 - 采购成本) / 营收)
|
||
# 假设采购成本是支出中类别为"计划采购"的部分
|
||
if is_full_year:
|
||
# 全年统计:使用SQL直接聚合计算采购成本
|
||
purchase_cost = db.session.query(func.sum(GERPCashReceiptsPaymentsLedger.CashBookAmount)).filter(
|
||
GERPCashReceiptsPaymentsLedger.CashBookTransactionTime >= current_year_start,
|
||
GERPCashReceiptsPaymentsLedger.CashBookTransactionTime < current_year_end,
|
||
GERPCashReceiptsPaymentsLedger.CashBookTransactionType == 0,
|
||
GERPCashReceiptsPaymentsLedger.CashBookCategory == "计划采购"
|
||
).scalar() or 0
|
||
|
||
# 上月(去年)采购成本
|
||
last_month_purchase_cost = db.session.query(func.sum(GERPCashReceiptsPaymentsLedger.CashBookAmount)).filter(
|
||
GERPCashReceiptsPaymentsLedger.CashBookTransactionTime >= last_year_start,
|
||
GERPCashReceiptsPaymentsLedger.CashBookTransactionTime < last_year_end,
|
||
GERPCashReceiptsPaymentsLedger.CashBookTransactionType == 0,
|
||
GERPCashReceiptsPaymentsLedger.CashBookCategory == "计划采购"
|
||
).scalar() or 0
|
||
else:
|
||
# 月度统计:使用SQL直接聚合计算采购成本
|
||
purchase_cost = db.session.query(func.sum(GERPCashReceiptsPaymentsLedger.CashBookAmount)).filter(
|
||
GERPCashReceiptsPaymentsLedger.CashBookTransactionTime >= current_month_start,
|
||
GERPCashReceiptsPaymentsLedger.CashBookTransactionTime < next_month_start,
|
||
GERPCashReceiptsPaymentsLedger.CashBookTransactionType == 0,
|
||
GERPCashReceiptsPaymentsLedger.CashBookCategory == "计划采购"
|
||
).scalar() or 0
|
||
|
||
# 上月采购成本
|
||
last_month_purchase_cost = db.session.query(func.sum(GERPCashReceiptsPaymentsLedger.CashBookAmount)).filter(
|
||
GERPCashReceiptsPaymentsLedger.CashBookTransactionTime >= last_month_start,
|
||
GERPCashReceiptsPaymentsLedger.CashBookTransactionTime < last_month_end,
|
||
GERPCashReceiptsPaymentsLedger.CashBookTransactionType == 0,
|
||
GERPCashReceiptsPaymentsLedger.CashBookCategory == "计划采购"
|
||
).scalar() or 0
|
||
|
||
# 修复毛利率计算:当营收>0时,毛利率取非负值(最低为0)
|
||
if current_month_revenue > 0:
|
||
gross_margin_value = current_month_revenue - purchase_cost
|
||
gross_margin = max(0, gross_margin_value / current_month_revenue * 100)
|
||
else:
|
||
gross_margin = 0
|
||
|
||
# 上月毛利率
|
||
if last_month_revenue > 0:
|
||
last_month_gross_margin_value = last_month_revenue - last_month_purchase_cost
|
||
last_month_gross_margin = max(0, last_month_gross_margin_value / last_month_revenue * 100)
|
||
else:
|
||
last_month_gross_margin = 0
|
||
|
||
# 计算现金余额趋势
|
||
if last_month_cash_balance != 0:
|
||
cash_balance_growth = (cash_balance - last_month_cash_balance) / last_month_cash_balance * 100
|
||
cash_balance_trend = f"{cash_balance_growth:.1f}% 环比增长" if cash_balance_growth > 0 else f"{abs(cash_balance_growth):.1f}% 环比下降"
|
||
else:
|
||
cash_balance_trend = "0.0% 环比持平"
|
||
|
||
# 计算毛利率趋势
|
||
if last_month_gross_margin != 0:
|
||
gross_margin_growth = (gross_margin - last_month_gross_margin) / last_month_gross_margin * 100
|
||
gross_margin_trend = f"{gross_margin_growth:.1f}% 环比增长" if gross_margin_growth > 0 else f"{abs(gross_margin_growth):.1f}% 环比下降"
|
||
else:
|
||
gross_margin_trend = "0.0% 环比持平"
|
||
|
||
# 回款率(简化计算:已结算收入 / 总收入)
|
||
settled_revenue = sum(
|
||
item.CashBookAmount for item in current_month_data
|
||
if item.CashBookTransactionType == 1 and item.CashBookStatus == 1
|
||
)
|
||
# 修复回款率计算:确保结果在合理范围内(0-100%)
|
||
if current_month_revenue > 0:
|
||
collection_rate = min(100, max(0, settled_revenue / current_month_revenue * 100))
|
||
else:
|
||
collection_rate = 0
|
||
|
||
# 主营业务收入和其他业务收入(简化分类)
|
||
# 修复:只将真正的收入类别归为主营业务收入,移除支出类别
|
||
main_business_income = sum(
|
||
item.CashBookAmount for item in current_month_data
|
||
if item.CashBookTransactionType == 1 and item.CashBookCategory in ["项目收入", "客户收入", "自愿赠与"]
|
||
)
|
||
other_business_income = current_month_revenue - main_business_income
|
||
|
||
# 人员工资支出
|
||
salary_expense = sum(
|
||
item.CashBookAmount for item in current_month_data
|
||
if item.CashBookTransactionType == 0 and item.CashBookCategory in ["工资", "社保", "加班酬劳"]
|
||
)
|
||
|
||
# 运营费用(除采购和工资外的其他支出)
|
||
operation_expense = current_month_expense - purchase_cost - salary_expense
|
||
|
||
# 现金流分析(简化计算)
|
||
operating_cash_inflow = current_month_revenue
|
||
operating_cash_outflow = current_month_expense
|
||
operating_cash_net = operating_cash_inflow - operating_cash_outflow
|
||
|
||
# 投资活动和筹资活动(暂时设为0,需要更多数据支持)
|
||
investing_cash_inflow = 0
|
||
investing_cash_outflow = 0
|
||
investing_cash_net = investing_cash_inflow - investing_cash_outflow
|
||
|
||
financing_cash_inflow = 0
|
||
financing_cash_outflow = 0
|
||
financing_cash_net = financing_cash_inflow - financing_cash_outflow
|
||
|
||
net_cash_flow = operating_cash_net + investing_cash_net + financing_cash_net
|
||
|
||
# 计算收支费用占比
|
||
expense_by_category = {}
|
||
income_by_category = {}
|
||
for item in current_month_data:
|
||
if item.CashBookTransactionType == 0: # 支出
|
||
category = item.CashBookCategory
|
||
if category not in expense_by_category:
|
||
expense_by_category[category] = 0
|
||
expense_by_category[category] += item.CashBookAmount
|
||
elif item.CashBookTransactionType == 1: # 收入
|
||
category = item.CashBookCategory
|
||
if category not in income_by_category:
|
||
income_by_category[category] = 0
|
||
income_by_category[category] += item.CashBookAmount
|
||
|
||
# 构建费用占比列表
|
||
expense_category_list = []
|
||
for category, amount in expense_by_category.items():
|
||
if current_month_expense > 0:
|
||
percentage = (amount / current_month_expense) * 100
|
||
else:
|
||
percentage = 0
|
||
expense_category_list.append({
|
||
"category": category,
|
||
"amount": float(amount),
|
||
"percentage": round(percentage, 2)
|
||
})
|
||
|
||
# 构建收入占比列表
|
||
income_category_list = []
|
||
for category, amount in income_by_category.items():
|
||
if current_month_revenue > 0:
|
||
percentage = (amount / current_month_revenue) * 100
|
||
else:
|
||
percentage = 0
|
||
income_category_list.append({
|
||
"category": category,
|
||
"amount": float(amount),
|
||
"percentage": round(percentage, 2)
|
||
})
|
||
|
||
# 按金额排序
|
||
expense_category_list.sort(key=lambda x: x['amount'], reverse=True)
|
||
income_category_list.sort(key=lambda x: x['amount'], reverse=True)
|
||
|
||
# 计算资产负债率(使用实际数据)
|
||
total_assets = db.session.query(func.sum(GERPAsset.AssetAmount)).scalar() or 0
|
||
total_liabilities = db.session.query(func.sum(GERPLiability.LiabilityAmount)).scalar() or 0
|
||
if total_assets > 0:
|
||
asset_liability_ratio = f"{total_liabilities / total_assets * 100:.1f}%"
|
||
else:
|
||
asset_liability_ratio = "0.0%"
|
||
|
||
# 格式化金额
|
||
def format_currency(amount):
|
||
return f"¥{amount:,.2f}".replace(".00", "")
|
||
|
||
# 构建财务数据字典
|
||
financial_data = {
|
||
"year": current_year,
|
||
"month": current_month,
|
||
"monthlyRevenue": format_currency(current_month_revenue),
|
||
"monthlyRevenueTrend": f"{revenue_growth} 环比增长" if revenue_growth_value > 0 else (f"{revenue_growth} 环比下降" if revenue_growth_value < 0 else f"{revenue_growth} 环比持平"),
|
||
|
||
|
||
"monthlyExpense": format_currency(current_month_expense),
|
||
"monthlyExpenseTrend": f"{expense_growth} 环比增长" if expense_growth_value > 0 else (f"{expense_growth} 环比下降" if expense_growth_value < 0 else f"{expense_growth} 环比持平"),
|
||
|
||
|
||
"monthlyProfit": format_currency(current_month_profit),
|
||
"monthlyProfitTrend": f"{profit_growth} 环比增长" if profit_growth_value > 0 else (f"{profit_growth} 环比下降" if profit_growth_value < 0 else f"{profit_growth} 环比持平"),
|
||
|
||
|
||
"unsettledAmount": format_currency(unsettled_amount),
|
||
|
||
"cashBalance": format_currency(cash_balance),
|
||
"cashBalanceTrend": cash_balance_trend,
|
||
"grossMargin": f"{gross_margin:.1f}%",
|
||
"grossMarginTrend": gross_margin_trend,
|
||
"revenueGrowth": f"+{format_currency(current_month_revenue - last_month_revenue)} ({revenue_growth})" if current_month_revenue > last_month_revenue else f"-{format_currency(last_month_revenue - current_month_revenue)} ({revenue_growth})",
|
||
"expenseGrowth": f"+{format_currency(current_month_expense - last_month_expense)} ({expense_growth})" if current_month_expense > last_month_expense else f"-{format_currency(last_month_expense - current_month_expense)} ({expense_growth})",
|
||
"profitGrowth": f"+{format_currency(current_month_profit - last_month_profit)} ({profit_growth})" if current_month_profit > last_month_profit else f"-{format_currency(last_month_profit - current_month_profit)} ({profit_growth})",
|
||
"collectionRate": f"{collection_rate:.1f}%",
|
||
"mainBusinessIncome": format_currency(main_business_income),
|
||
"otherBusinessIncome": format_currency(other_business_income),
|
||
"purchaseCost": format_currency(purchase_cost),
|
||
"salaryExpense": format_currency(salary_expense),
|
||
"operationExpense": format_currency(operation_expense),
|
||
"assetLiabilityRatio": asset_liability_ratio,
|
||
"operatingCashInflow": format_currency(operating_cash_inflow),
|
||
"operatingCashOutflow": format_currency(operating_cash_outflow),
|
||
"operatingCashNet": format_currency(operating_cash_net),
|
||
"investingCashInflow": format_currency(investing_cash_inflow),
|
||
"investingCashOutflow": format_currency(investing_cash_outflow),
|
||
"investingCashNet": format_currency(investing_cash_net),
|
||
"financingCashInflow": format_currency(financing_cash_inflow),
|
||
"financingCashOutflow": format_currency(financing_cash_outflow),
|
||
"financingCashNet": format_currency(financing_cash_net),
|
||
"netCashFlow": format_currency(net_cash_flow),
|
||
"expenseCategoryList": expense_category_list,
|
||
"incomeCategoryList": income_category_list,
|
||
"pageTitle": f"{current_year}年财务核心数据全景" if is_full_year else f"{current_year}年{current_month}月财务核心数据全景",
|
||
"updateTime": now.strftime("%Y-%m-%d %H:%M")
|
||
}
|
||
|
||
return {"code": 0, "msg": "success", "data": financial_data}
|