修复求和数据统计的问题
This commit is contained in:
@@ -57,6 +57,10 @@ def create_config():
|
||||
|
||||
custom_service_fee_name = data.get('custom_service_fee_name')
|
||||
|
||||
show_custom_service_fee = 1 if data.get('show_custom_service_fee') else 0
|
||||
show_total_amount = 1 if data.get('show_total_amount') else 0
|
||||
total_amount_name = data.get('total_amount_name')
|
||||
|
||||
if not config_name or not split_type or not split_value:
|
||||
log_warning('[配置API] 创建配置失败:缺少必填字段', 'api')
|
||||
return jsonify({'success': False, 'message': '缺少必填字段'}), 400
|
||||
@@ -76,8 +80,10 @@ def create_config():
|
||||
INSERT INTO t_daily_report_config
|
||||
(id, config_name, split_type, split_value, split_name, selected_fields,
|
||||
sum_fields, time_periods, merge_telecom, telecom_vehicle_no, field_custom_names,
|
||||
show_monthly_total, custom_service_fee_price, custom_service_fee_name, is_active, sort_order, create_time, update_time)
|
||||
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, 1, %s, NOW(), NOW())
|
||||
show_monthly_total, custom_service_fee_price, custom_service_fee_name,
|
||||
show_custom_service_fee, show_total_amount, total_amount_name,
|
||||
is_active, sort_order, create_time, update_time)
|
||||
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, 1, %s, NOW(), NOW())
|
||||
"""
|
||||
|
||||
params = (
|
||||
@@ -95,6 +101,9 @@ def create_config():
|
||||
show_monthly_total,
|
||||
custom_service_fee_price,
|
||||
custom_service_fee_name if custom_service_fee_name else None,
|
||||
show_custom_service_fee,
|
||||
show_total_amount,
|
||||
total_amount_name if total_amount_name else None,
|
||||
sort_order
|
||||
)
|
||||
|
||||
@@ -136,6 +145,10 @@ def update_config(config_id):
|
||||
|
||||
custom_service_fee_name = data.get('custom_service_fee_name')
|
||||
|
||||
show_custom_service_fee = 1 if data.get('show_custom_service_fee') else 0
|
||||
show_total_amount = 1 if data.get('show_total_amount') else 0
|
||||
total_amount_name = data.get('total_amount_name')
|
||||
|
||||
# 查询原配置,检查拆分是否变化
|
||||
old_configs = execute_query('SELECT * FROM t_daily_report_config WHERE id = %s', (config_id,))
|
||||
if not old_configs:
|
||||
@@ -148,6 +161,7 @@ def update_config(config_id):
|
||||
selected_fields = %s, sum_fields = %s, time_periods = %s,
|
||||
merge_telecom = %s, telecom_vehicle_no = %s, field_custom_names = %s,
|
||||
show_monthly_total = %s, custom_service_fee_price = %s, custom_service_fee_name = %s,
|
||||
show_custom_service_fee = %s, show_total_amount = %s, total_amount_name = %s,
|
||||
update_time = NOW()
|
||||
WHERE id = %s
|
||||
"""
|
||||
@@ -166,6 +180,9 @@ def update_config(config_id):
|
||||
show_monthly_total,
|
||||
custom_service_fee_price,
|
||||
custom_service_fee_name if custom_service_fee_name else None,
|
||||
show_custom_service_fee,
|
||||
show_total_amount,
|
||||
total_amount_name if total_amount_name else None,
|
||||
config_id
|
||||
)
|
||||
|
||||
@@ -240,8 +257,8 @@ def copy_config(config_id):
|
||||
# 插入新配置
|
||||
execute_insert(
|
||||
'''INSERT INTO t_daily_report_config
|
||||
(id, config_name, split_type, split_value, split_name, selected_fields, sum_fields, time_periods, merge_telecom, telecom_vehicle_no, field_custom_names, show_monthly_total, custom_service_fee_price, custom_service_fee_name, is_active, sort_order, create_time, update_time)
|
||||
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, NOW(), NOW())''',
|
||||
(id, config_name, split_type, split_value, split_name, selected_fields, sum_fields, time_periods, merge_telecom, telecom_vehicle_no, field_custom_names, show_monthly_total, custom_service_fee_price, custom_service_fee_name, show_custom_service_fee, show_total_amount, total_amount_name, is_active, sort_order, create_time, update_time)
|
||||
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, NOW(), NOW())''',
|
||||
(
|
||||
new_id,
|
||||
new_name,
|
||||
@@ -257,6 +274,9 @@ def copy_config(config_id):
|
||||
original.get('show_monthly_total', 0),
|
||||
original.get('custom_service_fee_price'),
|
||||
original.get('custom_service_fee_name'),
|
||||
original.get('show_custom_service_fee', 0),
|
||||
original.get('show_total_amount', 0),
|
||||
original.get('total_amount_name'),
|
||||
original['is_active'],
|
||||
sort_order
|
||||
)
|
||||
|
||||
@@ -93,12 +93,13 @@ def update_sum_data_api(sum_data_id):
|
||||
"""更新求和数据"""
|
||||
try:
|
||||
data = request.json
|
||||
log_info(f'[求和数据API] 更新求和数据,ID: {sum_data_id}', 'api')
|
||||
log_info(f'[求和数据API] 更新求和数据,ID: {sum_data_id}, 请求数据: {data}', 'api')
|
||||
result = update_sum_data(sum_data_id, data)
|
||||
if result['success']:
|
||||
log_info(f'[求和数据API] 更新成功,ID: {sum_data_id}', 'api')
|
||||
return jsonify(result)
|
||||
else:
|
||||
log_warning(f'[求和数据API] 更新失败,ID: {sum_data_id}, 原因: {result["message"]}', 'api')
|
||||
return jsonify(result), 400
|
||||
except Exception as e:
|
||||
log_error(f'[求和数据API] 更新求和数据失败: {e}', 'api')
|
||||
@@ -143,6 +144,57 @@ def batch_delete_sum_data_api():
|
||||
return jsonify({'success': False, 'message': str(e)}), 500
|
||||
|
||||
|
||||
@sum_data_bp.route('/sum-data/batch-query', methods=['POST'])
|
||||
def batch_query_sum_data():
|
||||
"""根据ID列表批量查询求和数据"""
|
||||
try:
|
||||
data = request.json
|
||||
ids = data.get('ids', [])
|
||||
log_info(f'[求和数据API] 批量查询求和数据,ID数量: {len(ids)}', 'api')
|
||||
|
||||
if not ids:
|
||||
return jsonify({'success': False, 'message': '请提供ID列表'}), 400
|
||||
|
||||
from lib.db import execute_query
|
||||
|
||||
placeholders = ','.join(['%s'] * len(ids))
|
||||
sql = f'SELECT * FROM t_daily_report_sum_data WHERE id IN ({placeholders})'
|
||||
|
||||
result = execute_query(sql, tuple(ids))
|
||||
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'data': result
|
||||
})
|
||||
except Exception as e:
|
||||
log_error(f'[求和数据API] 批量查询求和数据失败: {e}', 'api')
|
||||
return jsonify({'success': False, 'message': str(e)}), 500
|
||||
|
||||
|
||||
@sum_data_bp.route('/sum-data/batch-add', methods=['POST'])
|
||||
def batch_add_sum_data():
|
||||
"""批量添加求和数据"""
|
||||
try:
|
||||
data = request.json
|
||||
fields = data.get('fields', [])
|
||||
log_info(f'[求和数据API] 批量添加求和数据,字段数量: {len(fields)}', 'api')
|
||||
|
||||
if not fields:
|
||||
return jsonify({'success': False, 'message': '请至少添加一个字段'}), 400
|
||||
|
||||
from lib.sum_data_collector import batch_add_sum_data
|
||||
|
||||
result = batch_add_sum_data(data)
|
||||
if result['success']:
|
||||
log_info(f'[求和数据API] 批量添加成功,添加{result["count"]}条记录', 'api')
|
||||
return jsonify(result)
|
||||
else:
|
||||
return jsonify(result), 400
|
||||
except Exception as e:
|
||||
log_error(f'[求和数据API] 批量添加求和数据失败: {e}', 'api')
|
||||
return jsonify({'success': False, 'message': str(e)}), 500
|
||||
|
||||
|
||||
@sum_data_bp.route('/sum-data/monthly-total', methods=['GET'])
|
||||
def monthly_charge_degree_total():
|
||||
"""获取指定配置当月充电电量累计(从sum_data表查询,对账后的数据)
|
||||
|
||||
95
lib/db.py
95
lib/db.py
@@ -18,8 +18,8 @@ def get_db_config():
|
||||
'cursorclass': pymysql.cursors.DictCursor,
|
||||
'autocommit': True,
|
||||
'connect_timeout': 30,
|
||||
'read_timeout': 60,
|
||||
'write_timeout': 60
|
||||
'read_timeout': 120,
|
||||
'write_timeout': 120
|
||||
}
|
||||
|
||||
DB_CONFIG = get_db_config()
|
||||
@@ -41,39 +41,72 @@ def get_connection():
|
||||
pass
|
||||
|
||||
|
||||
def execute_query(sql, params=None):
|
||||
def execute_query(sql, params=None, retry=2):
|
||||
"""执行查询并返回结果"""
|
||||
try:
|
||||
with get_connection() as conn:
|
||||
with conn.cursor() as cursor:
|
||||
cursor.execute(sql, params)
|
||||
result = cursor.fetchall()
|
||||
return result
|
||||
except Exception as e:
|
||||
log_error(f'查询失败: {e}\nSQL: {sql}\nParams: {params}', 'db', exc_info=True)
|
||||
raise
|
||||
for attempt in range(retry + 1):
|
||||
try:
|
||||
with get_connection() as conn:
|
||||
with conn.cursor() as cursor:
|
||||
cursor.execute(sql, params)
|
||||
result = cursor.fetchall()
|
||||
if attempt > 0:
|
||||
log_info(f'[数据库] 查询重试成功,第{attempt+1}次尝试', 'db')
|
||||
return result
|
||||
except pymysql.err.OperationalError as e:
|
||||
if attempt < retry and (e.args[0] == 2013 or e.args[0] == 2006):
|
||||
log_warning(f'[数据库] 查询连接断开,正在重试(第{attempt+1}次): {e}', 'db')
|
||||
import time
|
||||
time.sleep(1)
|
||||
continue
|
||||
log_error(f'查询失败: {e}\nSQL: {sql}\nParams: {params}', 'db', exc_info=True)
|
||||
raise
|
||||
except Exception as e:
|
||||
log_error(f'查询失败: {e}\nSQL: {sql}\nParams: {params}', 'db', exc_info=True)
|
||||
raise
|
||||
|
||||
|
||||
def execute_update(sql, params=None):
|
||||
def execute_update(sql, params=None, retry=2):
|
||||
"""执行更新操作"""
|
||||
try:
|
||||
with get_connection() as conn:
|
||||
with conn.cursor() as cursor:
|
||||
affected = cursor.execute(sql, params)
|
||||
return cursor.rowcount
|
||||
except Exception as e:
|
||||
log_error(f'更新失败: {e}\nSQL: {sql}\nParams: {params}', 'db', exc_info=True)
|
||||
raise
|
||||
for attempt in range(retry + 1):
|
||||
try:
|
||||
with get_connection() as conn:
|
||||
with conn.cursor() as cursor:
|
||||
affected = cursor.execute(sql, params)
|
||||
if attempt > 0:
|
||||
log_info(f'[数据库] 更新重试成功,第{attempt+1}次尝试', 'db')
|
||||
return cursor.rowcount
|
||||
except pymysql.err.OperationalError as e:
|
||||
if attempt < retry and (e.args[0] == 2013 or e.args[0] == 2006):
|
||||
log_warning(f'[数据库] 更新连接断开,正在重试(第{attempt+1}次): {e}', 'db')
|
||||
import time
|
||||
time.sleep(1)
|
||||
continue
|
||||
log_error(f'更新失败: {e}\nSQL: {sql}\nParams: {params}', 'db', exc_info=True)
|
||||
raise
|
||||
except Exception as e:
|
||||
log_error(f'更新失败: {e}\nSQL: {sql}\nParams: {params}', 'db', exc_info=True)
|
||||
raise
|
||||
|
||||
|
||||
def execute_insert(sql, params=None):
|
||||
def execute_insert(sql, params=None, retry=2):
|
||||
"""执行插入操作并返回插入ID"""
|
||||
try:
|
||||
with get_connection() as conn:
|
||||
with conn.cursor() as cursor:
|
||||
cursor.execute(sql, params)
|
||||
last_id = cursor.lastrowid
|
||||
return last_id
|
||||
except Exception as e:
|
||||
log_error(f'插入失败: {e}\nSQL: {sql}\nParams: {params}', 'db', exc_info=True)
|
||||
raise
|
||||
for attempt in range(retry + 1):
|
||||
try:
|
||||
with get_connection() as conn:
|
||||
with conn.cursor() as cursor:
|
||||
cursor.execute(sql, params)
|
||||
last_id = cursor.lastrowid
|
||||
if attempt > 0:
|
||||
log_info(f'[数据库] 插入重试成功,第{attempt+1}次尝试', 'db')
|
||||
return last_id
|
||||
except pymysql.err.OperationalError as e:
|
||||
if attempt < retry and (e.args[0] == 2013 or e.args[0] == 2006):
|
||||
log_warning(f'[数据库] 插入连接断开,正在重试(第{attempt+1}次): {e}', 'db')
|
||||
import time
|
||||
time.sleep(1)
|
||||
continue
|
||||
log_error(f'插入失败: {e}\nSQL: {sql}\nParams: {params}', 'db', exc_info=True)
|
||||
raise
|
||||
except Exception as e:
|
||||
log_error(f'插入失败: {e}\nSQL: {sql}\nParams: {params}', 'db', exc_info=True)
|
||||
raise
|
||||
@@ -161,6 +161,27 @@ def init_database_tables():
|
||||
"custom_service_fee_name VARCHAR(100) NULL COMMENT '自定义服务费报表表头名称'",
|
||||
'custom_service_fee_name'
|
||||
)
|
||||
|
||||
# 给配置表增加 show_custom_service_fee 字段(是否显示自定义服务费列)
|
||||
_add_column_if_not_exists(
|
||||
't_daily_report_config',
|
||||
"show_custom_service_fee TINYINT NULL COMMENT '是否显示自定义服务费列(0=不显示,1=显示)'",
|
||||
'show_custom_service_fee'
|
||||
)
|
||||
|
||||
# 给配置表增加 show_total_amount 字段(是否显示实收金额列)
|
||||
_add_column_if_not_exists(
|
||||
't_daily_report_config',
|
||||
"show_total_amount TINYINT NULL COMMENT '是否显示实收金额列(0=不显示,1=显示)'",
|
||||
'show_total_amount'
|
||||
)
|
||||
|
||||
# 给配置表增加 total_amount_name 字段(实收金额表头名称)
|
||||
_add_column_if_not_exists(
|
||||
't_daily_report_config',
|
||||
"total_amount_name VARCHAR(100) NULL COMMENT '实收金额报表表头名称'",
|
||||
'total_amount_name'
|
||||
)
|
||||
|
||||
# 数据迁移:给 sort_order 为 NULL 的配置记录按 id 顺序赋值
|
||||
try:
|
||||
|
||||
@@ -169,6 +169,10 @@ FIELD_MAPPING = {
|
||||
|
||||
# 数据来源
|
||||
'data_source': '数据来源',
|
||||
|
||||
# 自定义计算字段(虚拟字段)
|
||||
'custom_service_fee': '自定义服务费(元)',
|
||||
'total_amount': '实收金额(元)',
|
||||
}
|
||||
|
||||
# 获取字段中文名称
|
||||
|
||||
@@ -340,10 +340,14 @@ def generate_daily_report(config_id, start_time=None, end_time=None):
|
||||
report_date = start_time.strftime('%Y-%m-%d')
|
||||
delete_old_history(config_id, report_date, config['split_type'], config['split_value'])
|
||||
|
||||
# 构建查询SQL - 过滤掉虚拟字段(时段电量等)
|
||||
# 构建查询SQL - 过滤掉虚拟字段(时段电量、自定义服务费、实收金额等)
|
||||
# 虚拟字段不是数据库表中的实际字段,需要动态计算
|
||||
TIME_PERIOD_FIELDS = {'sharp_electricity', 'peak_electricity', 'flat_electricity', 'valley_electricity'}
|
||||
db_fields = [f for f in selected_fields if f not in TIME_PERIOD_FIELDS]
|
||||
VIRTUAL_FIELDS = {'sharp_electricity', 'peak_electricity', 'flat_electricity', 'valley_electricity', 'custom_service_fee', 'total_amount'}
|
||||
db_fields = [f for f in selected_fields if f not in VIRTUAL_FIELDS]
|
||||
|
||||
# 如果选择了实收金额字段,需要确保查询 charge_elecfee_amount(电费金额)
|
||||
if 'total_amount' in selected_fields and 'charge_elecfee_amount' not in db_fields:
|
||||
db_fields.append('charge_elecfee_amount')
|
||||
|
||||
# 确保至少有 order_no 字段用于关联分时数据
|
||||
if 'order_no' not in db_fields:
|
||||
@@ -501,7 +505,10 @@ def generate_daily_report(config_id, start_time=None, end_time=None):
|
||||
split_type=config.get('split_type'),
|
||||
split_value=config.get('split_value'),
|
||||
custom_service_fee_price=config.get('custom_service_fee_price'),
|
||||
custom_service_fee_name=config.get('custom_service_fee_name')
|
||||
custom_service_fee_name=config.get('custom_service_fee_name'),
|
||||
show_custom_service_fee=config.get('show_custom_service_fee', 0) == 1,
|
||||
show_total_amount=config.get('show_total_amount', 0) == 1,
|
||||
total_amount_name=config.get('total_amount_name')
|
||||
)
|
||||
|
||||
log_info(f"[日报生成] Excel文件已生成: {file_path}", 'report')
|
||||
@@ -591,7 +598,8 @@ def generate_excel(orders, selected_fields, sum_fields, sum_results,
|
||||
id_name_maps=None, field_custom_names=None, has_supplement=False,
|
||||
show_monthly_total=False, config_id=None, report_date=None, sort_order=None,
|
||||
split_type=None, split_value=None,
|
||||
custom_service_fee_price=None, custom_service_fee_name=None):
|
||||
custom_service_fee_price=None, custom_service_fee_name=None,
|
||||
show_custom_service_fee=False, show_total_amount=False, total_amount_name=None):
|
||||
"""
|
||||
生成Excel文件
|
||||
|
||||
@@ -711,35 +719,36 @@ def generate_excel(orders, selected_fields, sum_fields, sum_results,
|
||||
cell.alignment = header_alignment
|
||||
cell.border = thin_border
|
||||
|
||||
has_custom_service_fee = custom_service_fee_price is not None and custom_service_fee_price != ''
|
||||
|
||||
for col_idx, field in enumerate(selected_fields, 2):
|
||||
cell = ws.cell(row=header_row, column=col_idx)
|
||||
if field in field_custom_names and field_custom_names[field]:
|
||||
|
||||
if field == 'custom_service_fee':
|
||||
if custom_service_fee_name and str(custom_service_fee_name).strip():
|
||||
display_name = str(custom_service_fee_name).strip()
|
||||
elif has_custom_service_fee:
|
||||
display_name = f'自定义服务费({custom_service_fee_price}元/kWh)'
|
||||
else:
|
||||
display_name = '自定义服务费'
|
||||
elif field == 'total_amount':
|
||||
if total_amount_name and str(total_amount_name).strip():
|
||||
display_name = str(total_amount_name).strip()
|
||||
else:
|
||||
display_name = '实收金额'
|
||||
elif field in field_custom_names and field_custom_names[field]:
|
||||
display_name = field_custom_names[field]
|
||||
elif field in id_name_maps and id_name_maps[field]:
|
||||
display_name = get_field_display_name(field).replace('ID', '')
|
||||
else:
|
||||
display_name = get_field_display_name(field)
|
||||
|
||||
cell.value = display_name
|
||||
cell.font = header_font
|
||||
cell.fill = header_fill
|
||||
cell.alignment = header_alignment
|
||||
cell.border = thin_border
|
||||
|
||||
# 自定义服务费列表头(如果设置了单价)
|
||||
has_custom_service_fee = custom_service_fee_price is not None and custom_service_fee_price != ''
|
||||
if has_custom_service_fee:
|
||||
custom_fee_col = len(selected_fields) + 2
|
||||
cell = ws.cell(row=header_row, column=custom_fee_col)
|
||||
if custom_service_fee_name and str(custom_service_fee_name).strip():
|
||||
header_name = str(custom_service_fee_name).strip()
|
||||
else:
|
||||
header_name = f'自定义服务费({custom_service_fee_price}元/kWh)'
|
||||
cell.value = header_name
|
||||
cell.font = header_font
|
||||
cell.fill = header_fill
|
||||
cell.alignment = header_alignment
|
||||
cell.border = thin_border
|
||||
|
||||
ws.row_dimensions[header_row].height = 25
|
||||
|
||||
# 写入数据
|
||||
@@ -758,29 +767,44 @@ def generate_excel(orders, selected_fields, sum_fields, sum_results,
|
||||
|
||||
for col_idx, field in enumerate(selected_fields, 2):
|
||||
cell = ws.cell(row=row_idx, column=col_idx)
|
||||
value = order.get(field, '')
|
||||
|
||||
# 格式化时间字段
|
||||
if field in ['report_time', 'start_time', 'end_time', 'create_time', 'update_time', 'pay_time']:
|
||||
if isinstance(value, datetime):
|
||||
value = value.strftime('%Y-%m-%d %H:%M:%S')
|
||||
|
||||
# 将ID转换为名称
|
||||
if field in id_name_maps and value is not None and value != '':
|
||||
name_map = id_name_maps[field]
|
||||
name_value = name_map.get(str(value))
|
||||
if name_value:
|
||||
value = name_value
|
||||
if field == 'custom_service_fee':
|
||||
if has_custom_service_fee:
|
||||
charge_degree = float(order.get('charge_degree', 0) or 0)
|
||||
value = round(charge_degree * float(custom_service_fee_price), 2)
|
||||
else:
|
||||
value = ''
|
||||
elif field == 'total_amount':
|
||||
actual_money = float(order.get('charge_elecfee_amount', 0) or 0)
|
||||
if has_custom_service_fee:
|
||||
charge_degree = float(order.get('charge_degree', 0) or 0)
|
||||
custom_fee = round(charge_degree * float(custom_service_fee_price), 2)
|
||||
value = round(actual_money + custom_fee, 2)
|
||||
else:
|
||||
value = round(actual_money, 2)
|
||||
else:
|
||||
value = order.get(field, '')
|
||||
|
||||
# 格式化时间字段
|
||||
if field in ['report_time', 'start_time', 'end_time', 'create_time', 'update_time', 'pay_time']:
|
||||
if isinstance(value, datetime):
|
||||
value = value.strftime('%Y-%m-%d %H:%M:%S')
|
||||
|
||||
# 将ID转换为名称
|
||||
if field in id_name_maps and value is not None and value != '':
|
||||
name_map = id_name_maps[field]
|
||||
name_value = name_map.get(str(value))
|
||||
if name_value:
|
||||
value = name_value
|
||||
|
||||
cell.value = value
|
||||
cell.border = thin_border
|
||||
|
||||
# 数字字段靠右对齐,其他居中或靠左
|
||||
if field in numeric_fields:
|
||||
if field in numeric_fields or field in ['custom_service_fee', 'total_amount']:
|
||||
cell.alignment = Alignment(horizontal='right', vertical='center')
|
||||
if isinstance(value, (int, float)):
|
||||
if field in decimal3_fields:
|
||||
cell.number_format = '0.000'
|
||||
cell.number_format = '0.00'
|
||||
elif field in ['order_no', 'vehicle_no', 'gun_no', 'connector_no']:
|
||||
cell.alignment = Alignment(horizontal='center', vertical='center')
|
||||
else:
|
||||
@@ -790,19 +814,6 @@ def generate_excel(orders, selected_fields, sum_fields, sum_results,
|
||||
if is_even:
|
||||
cell.fill = even_row_fill
|
||||
|
||||
# 自定义服务费列(如果设置了单价)
|
||||
if has_custom_service_fee:
|
||||
custom_fee_col = len(selected_fields) + 2
|
||||
cell = ws.cell(row=row_idx, column=custom_fee_col)
|
||||
charge_degree = float(order.get('charge_degree', 0) or 0)
|
||||
custom_fee = round(charge_degree * float(custom_service_fee_price), 2)
|
||||
cell.value = custom_fee
|
||||
cell.border = thin_border
|
||||
cell.alignment = Alignment(horizontal='right', vertical='center')
|
||||
cell.number_format = '0.00'
|
||||
if is_even:
|
||||
cell.fill = even_row_fill
|
||||
|
||||
ws.row_dimensions[row_idx].height = 22
|
||||
|
||||
# 写入求和行
|
||||
@@ -819,25 +830,33 @@ def generate_excel(orders, selected_fields, sum_fields, sum_results,
|
||||
cell = ws.cell(row=sum_row, column=col_idx)
|
||||
cell.fill = sum_fill
|
||||
cell.border = thin_border
|
||||
if field in sum_fields and field in sum_results:
|
||||
|
||||
if field == 'custom_service_fee':
|
||||
if has_custom_service_fee:
|
||||
total_charge_degree = sum_results.get('charge_degree', 0)
|
||||
value = round(float(total_charge_degree) * float(custom_service_fee_price), 2)
|
||||
cell.value = value
|
||||
cell.font = sum_font
|
||||
cell.alignment = Alignment(horizontal='right', vertical='center')
|
||||
cell.number_format = '0.00'
|
||||
elif field == 'total_amount':
|
||||
total_actual_money = sum_results.get('charge_elecfee_amount', 0)
|
||||
if has_custom_service_fee:
|
||||
total_charge_degree = sum_results.get('charge_degree', 0)
|
||||
total_custom_fee = round(float(total_charge_degree) * float(custom_service_fee_price), 2)
|
||||
value = round(float(total_actual_money) + total_custom_fee, 2)
|
||||
else:
|
||||
value = round(float(total_actual_money), 2)
|
||||
cell.value = value
|
||||
cell.font = sum_font
|
||||
cell.alignment = Alignment(horizontal='right', vertical='center')
|
||||
cell.number_format = '0.00'
|
||||
elif field in sum_fields and field in sum_results:
|
||||
cell.value = sum_results[field]
|
||||
cell.font = sum_font
|
||||
cell.alignment = Alignment(horizontal='right', vertical='center')
|
||||
cell.number_format = '0.000'
|
||||
|
||||
# 自定义服务费合计
|
||||
if has_custom_service_fee:
|
||||
custom_fee_col = len(selected_fields) + 2
|
||||
cell = ws.cell(row=sum_row, column=custom_fee_col)
|
||||
cell.fill = sum_fill
|
||||
cell.border = thin_border
|
||||
total_charge_degree = sum_results.get('charge_degree', 0)
|
||||
total_custom_fee = round(float(total_charge_degree) * float(custom_service_fee_price), 2)
|
||||
cell.value = total_custom_fee
|
||||
cell.font = sum_font
|
||||
cell.alignment = Alignment(horizontal='right', vertical='center')
|
||||
cell.number_format = '0.00'
|
||||
|
||||
ws.row_dimensions[sum_row].height = 25
|
||||
|
||||
# 写入当月充电量累计总计行(从求和采集表查询,对账后数据)
|
||||
@@ -881,8 +900,6 @@ def generate_excel(orders, selected_fields, sum_fields, sum_results,
|
||||
|
||||
# 总列数
|
||||
total_cols = len(selected_fields) + 2
|
||||
if has_custom_service_fee:
|
||||
total_cols += 1
|
||||
|
||||
# 标题文字
|
||||
title_text = f'当月累计充电量(共{total_days}天,截止{report_date})'
|
||||
@@ -935,10 +952,15 @@ def generate_excel(orders, selected_fields, sum_fields, sum_results,
|
||||
log_error(f"[日报生成] 写入月度总计时出错: {e}", 'report')
|
||||
|
||||
# 调整列宽(中文按2个字符宽度计算)
|
||||
# 序号列固定宽度
|
||||
from openpyxl.utils import get_column_letter
|
||||
|
||||
# 序号列固定宽度
|
||||
ws.column_dimensions['A'].width = 8
|
||||
|
||||
# 计算所有字段的宽度
|
||||
col_widths = {}
|
||||
numeric_cols = []
|
||||
|
||||
for col_idx, field in enumerate(selected_fields, 2):
|
||||
header_name = get_field_display_name(field)
|
||||
if field in field_custom_names and field_custom_names[field]:
|
||||
@@ -955,21 +977,25 @@ def generate_excel(orders, selected_fields, sum_fields, sum_results,
|
||||
cell_len = len(cell_str) + cn_count
|
||||
max_length = max(max_length, cell_len)
|
||||
|
||||
from openpyxl.utils import get_column_letter
|
||||
col_letter = get_column_letter(col_idx)
|
||||
ws.column_dimensions[col_letter].width = min(max_length + 2, 50)
|
||||
col_widths[col_idx] = min(max_length + 2, 50)
|
||||
|
||||
# 标记数值类型字段(求和类列)
|
||||
if field in numeric_fields or field in ['custom_service_fee', 'total_amount']:
|
||||
numeric_cols.append(col_idx)
|
||||
|
||||
# 自定义服务费列宽度
|
||||
if has_custom_service_fee:
|
||||
custom_fee_col = len(selected_fields) + 2
|
||||
if custom_service_fee_name and str(custom_service_fee_name).strip():
|
||||
header_name = str(custom_service_fee_name).strip()
|
||||
else:
|
||||
header_name = f'自定义服务费({custom_service_fee_price}元/kWh)'
|
||||
cn_count = sum(1 for c in str(header_name) if '\u4e00' <= c <= '\u9fff')
|
||||
max_length = len(str(header_name)) + cn_count
|
||||
col_letter = get_column_letter(custom_fee_col)
|
||||
ws.column_dimensions[col_letter].width = min(max_length + 2, 50)
|
||||
# 计算数值类型列的最大宽度,使所有求和类列宽度一致
|
||||
if numeric_cols:
|
||||
max_numeric_width = max(col_widths[col] for col in numeric_cols)
|
||||
for col_idx in numeric_cols:
|
||||
col_letter = get_column_letter(col_idx)
|
||||
ws.column_dimensions[col_letter].width = max_numeric_width
|
||||
|
||||
# 设置非数值类型列的宽度
|
||||
for col_idx, width in col_widths.items():
|
||||
if col_idx not in numeric_cols:
|
||||
col_letter = get_column_letter(col_idx)
|
||||
ws.column_dimensions[col_letter].width = width
|
||||
|
||||
# 冻结首行
|
||||
ws.freeze_panes = 'A2'
|
||||
|
||||
@@ -373,6 +373,72 @@ def add_sum_data(data):
|
||||
return {'success': False, 'message': str(e)}
|
||||
|
||||
|
||||
def batch_add_sum_data(data):
|
||||
"""
|
||||
批量添加求和数据
|
||||
|
||||
Args:
|
||||
data: 求和数据字典,包含 fields 列表
|
||||
|
||||
Returns:
|
||||
dict: 操作结果
|
||||
"""
|
||||
try:
|
||||
fields = data.get('fields', [])
|
||||
if not fields:
|
||||
return {'success': False, 'message': '请至少添加一个字段'}
|
||||
|
||||
# 获取配置的排序号
|
||||
sort_order = None
|
||||
config_id = data.get('config_id')
|
||||
if config_id:
|
||||
try:
|
||||
cfg_result = execute_query(
|
||||
'SELECT sort_order FROM t_daily_report_config WHERE id = %s LIMIT 1',
|
||||
(config_id,)
|
||||
)
|
||||
if cfg_result and cfg_result[0]['sort_order'] is not None:
|
||||
sort_order = cfg_result[0]['sort_order']
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
sql = '''
|
||||
INSERT INTO t_daily_report_sum_data
|
||||
(id, report_date, config_id, config_name, split_type, split_value, split_name,
|
||||
sum_field_key, sum_field_name, sum_value, total_orders, sort_order, data_source, remark,
|
||||
create_time, update_time)
|
||||
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, NOW(), NOW())
|
||||
'''
|
||||
|
||||
count = 0
|
||||
for field in fields:
|
||||
sum_data_id = int(datetime.now().timestamp() * 1000) + count
|
||||
params = (
|
||||
sum_data_id,
|
||||
data.get('report_date', ''),
|
||||
data.get('config_id'),
|
||||
data.get('config_name', ''),
|
||||
data.get('split_type', ''),
|
||||
data.get('split_value', ''),
|
||||
data.get('split_name', ''),
|
||||
field.get('sum_field_key', ''),
|
||||
field.get('sum_field_name', ''),
|
||||
float(field.get('sum_value', 0)),
|
||||
data.get('total_orders', 0),
|
||||
sort_order,
|
||||
'manual',
|
||||
data.get('remark', '')
|
||||
)
|
||||
execute_insert(sql, params)
|
||||
count += 1
|
||||
|
||||
return {'success': True, 'message': f'添加成功,共{count}条记录', 'count': count}
|
||||
except Exception as e:
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
return {'success': False, 'message': str(e)}
|
||||
|
||||
|
||||
def update_sum_data(sum_data_id, data):
|
||||
"""
|
||||
更新求和数据
|
||||
@@ -549,16 +615,30 @@ def export_sum_data_to_excel(start_date=None, end_date=None, config_id=None, dat
|
||||
if where_clauses:
|
||||
where_sql = 'WHERE ' + ' AND '.join(where_clauses)
|
||||
|
||||
# 查询数据(不分页,全量导出)
|
||||
sql = f'''
|
||||
SELECT id, report_date, config_id, config_name, split_type, split_value, split_name,
|
||||
sum_field_key, sum_field_name, sum_value, total_orders, sort_order, data_source, remark,
|
||||
create_time, update_time
|
||||
FROM t_daily_report_sum_data
|
||||
{where_sql}
|
||||
ORDER BY report_date DESC, sort_order ASC, config_id ASC, sum_field_key ASC
|
||||
'''
|
||||
rows = execute_query(sql, tuple(params) if params else ())
|
||||
# 使用分页查询避免大数据量超时
|
||||
rows = []
|
||||
page_size = 500
|
||||
page = 1
|
||||
|
||||
while True:
|
||||
offset = (page - 1) * page_size
|
||||
sql = f'''
|
||||
SELECT id, report_date, config_id, config_name, split_type, split_value, split_name,
|
||||
sum_field_key, sum_field_name, sum_value, total_orders, sort_order, data_source, remark,
|
||||
create_time, update_time
|
||||
FROM t_daily_report_sum_data
|
||||
{where_sql}
|
||||
ORDER BY report_date DESC, sort_order ASC, config_id ASC, sum_field_key ASC
|
||||
LIMIT %s OFFSET %s
|
||||
'''
|
||||
query_params = tuple(params) + (page_size, offset)
|
||||
page_rows = execute_query(sql, query_params)
|
||||
if not page_rows:
|
||||
break
|
||||
rows.extend(page_rows)
|
||||
if len(page_rows) < page_size:
|
||||
break
|
||||
page += 1
|
||||
|
||||
# 创建Excel
|
||||
wb = Workbook()
|
||||
@@ -723,21 +803,22 @@ def get_monthly_charge_degree_total(config_id=None, month=None, split_type=None,
|
||||
next_month = month[:5] + str(int(month[5:7]) + 1).zfill(2)
|
||||
end_date_str = (datetime.strptime(next_month + '-01', '%Y-%m-%d') - timedelta(days=1)).strftime('%Y-%m-%d')
|
||||
|
||||
# 构建查询SQL
|
||||
# 构建查询SQL - 查询所有字段,按日期统计(包括手动添加的数据)
|
||||
# 使用 MAX(total_orders) 避免同一天多个字段重复计算订单数
|
||||
sql = """
|
||||
SELECT
|
||||
report_date,
|
||||
SUM(sum_value) as total_degree,
|
||||
SUM(total_orders) as total_orders
|
||||
SUM(CASE WHEN sum_field_key = 'charge_degree' THEN sum_value ELSE 0 END) as total_degree,
|
||||
MAX(total_orders) as total_orders
|
||||
FROM t_daily_report_sum_data
|
||||
WHERE sum_field_key = 'charge_degree'
|
||||
AND report_date LIKE %s
|
||||
WHERE report_date LIKE %s
|
||||
AND report_date <= %s
|
||||
"""
|
||||
params = [date_prefix + '%', end_date_str]
|
||||
|
||||
# 按配置过滤(包含手动添加的数据,手动添加的config_id为NULL)
|
||||
if config_id:
|
||||
sql += ' AND config_id = %s'
|
||||
sql += ' AND (config_id = %s OR config_id IS NULL)'
|
||||
params.append(config_id)
|
||||
|
||||
# 按拆分值过滤(以配置当前拆分为准)
|
||||
|
||||
Reference in New Issue
Block a user