diff --git a/config.py b/config.py index c8ad7ee..0db4bd1 100644 --- a/config.py +++ b/config.py @@ -320,7 +320,7 @@ YOUTUBE_API_KEY = os.getenv('YOUTUBE_API_KEY', '') # ========================================== # 系統版本與路徑 # ========================================== -SYSTEM_VERSION = "V10.162" +SYSTEM_VERSION = "V10.163" LOG_FILE_PATH = os.path.join(BASE_DIR, 'logs/system.log') public_url = PUBLIC_URL # 用於模板顯示 diff --git a/routes/daily_sales_routes.py b/routes/daily_sales_routes.py index 3025bd2..7c7125a 100644 --- a/routes/daily_sales_routes.py +++ b/routes/daily_sales_routes.py @@ -12,7 +12,7 @@ import os import pickle from datetime import datetime, timezone, timedelta from urllib.parse import quote -from flask import Blueprint, request, render_template, send_file +from flask import Blueprint, request, render_template, send_file, url_for from auth import login_required from sqlalchemy import inspect, text import pandas as pd @@ -46,6 +46,7 @@ _VIEW_CACHE_EXPIRY_SECONDS = 120 _SHARED_VIEW_CACHE_EXPIRY_SECONDS = 1800 _DAILY_SALES_VIEW_CACHE = {} _DAILY_SALES_VIEW_CACHE_MAX = 24 +_CATEGORY_TABLE_DEFAULT_LIMIT = 120 def _get_daily_view_cache(cache_key): @@ -112,6 +113,15 @@ def _set_shared_daily_view_cache(cache_key, context): pass +def _daily_sales_url_with_detail(detail_value): + args = request.args.to_dict(flat=True) + if detail_value == 'all': + args['detail'] = 'all' + else: + args.pop('detail', None) + return url_for('daily_sales.daily_sales', **args) + + def clear_daily_sales_cache(): """清除當日業績緩存(供匯入服務調用)""" _clear_daily_sales_cache() @@ -462,6 +472,8 @@ def prepare_category_summary(df, date_str=None, is_month_view=False, month_start if 'profit' not in category_df.columns: category_df['profit'] = 0 + if 'revenue' in category_df.columns: + category_df = category_df.sort_values(by='revenue', ascending=False) return category_df.to_dict('records') @@ -514,6 +526,7 @@ def daily_sales(): is_month_view = not selected_date_param and not request.args.get('month') if selected_month_param and not selected_date_param: is_month_view = True + show_all_categories = request.args.get('detail') == 'all' month_start = selected_month.replace(day=1) month_end = (month_start + pd.DateOffset(months=1)) - pd.Timedelta(days=1) @@ -531,6 +544,7 @@ def daily_sales(): selected_date.strftime('%Y-%m-%d'), selected_month.strftime('%Y-%m'), 'month' if is_month_view else 'day', + 'category_all' if show_all_categories else f'category_top{_CATEGORY_TABLE_DEFAULT_LIMIT}', str(current_fingerprint), ]) cached_context = _get_daily_view_cache(view_cache_key) or _get_shared_daily_view_cache(view_cache_key) @@ -601,6 +615,11 @@ def daily_sales(): month_start=month_start if is_month_view else None, month_end=month_end if is_month_view else None ) + category_total_count = len(category_list) + if show_all_categories: + visible_categories = category_list + else: + visible_categories = category_list[:_CATEGORY_TABLE_DEFAULT_LIMIT] calendar_data = prepare_calendar_data(df, selected_month) marketing_data = prepare_marketing_summary( df, @@ -619,7 +638,13 @@ def daily_sales(): 'month_kpi': month_kpi, 'is_month_view': is_month_view, 'chart_data': chart_data, - 'categories': category_list, + 'categories': visible_categories, + 'category_total_count': category_total_count, + 'category_visible_count': len(visible_categories), + 'category_table_limit': _CATEGORY_TABLE_DEFAULT_LIMIT, + 'category_show_all': show_all_categories, + 'category_show_all_url': _daily_sales_url_with_detail('all'), + 'category_limited_url': _daily_sales_url_with_detail(None), 'calendar_data': calendar_data, 'marketing_data': marketing_data, 'selected_month': selected_month.strftime('%Y-%m') if isinstance(selected_month, pd.Timestamp) else selected_month, diff --git a/templates/daily_sales.html b/templates/daily_sales.html index c52cc1b..800abc4 100644 --- a/templates/daily_sales.html +++ b/templates/daily_sales.html @@ -332,14 +332,26 @@ {% endif %} + {% set category_total = category_total_count|default(categories|length if categories else 0) %} + {% set category_visible = category_visible_count|default(categories|length if categories else 0) %}