Spaces:
Runtime error
Runtime error
| import pandas as pd | |
| import numpy as np | |
| import plotly.express as px | |
| import plotly.graph_objects as go | |
| import io | |
| # Month order for Persian calendar | |
| MONTH_ORDER = [ | |
| "فروردین", "اردیبهشت", "خرداد", "تیر", "مرداد", "شهریور", | |
| "مهر", "آبان", "آذر", "دی", "بهمن", "اسفند" | |
| ] | |
| # Available charts list | |
| AVAILABLE_CHARTS = [ | |
| "مقایسه روند فروش ماهانه (ناخالص، خالص با/بدون ارزش افزوده)", | |
| "روند فروش ماهانه بر اساس فروش ناخالص", | |
| "روند فروش ماهانه بر اساس فروش خالص (بدون ارزش افزوده)", | |
| "روند فروش ماهانه بر اساس فروش خالص (با ارزش افزوده)", | |
| "تحلیل کالاها (80/20)", | |
| "محصولات با بیشترین مرجوعی", | |
| "گروه محصولات پرفروش", | |
| "ده تولید کننده پرفروش", | |
| "نرخ مرجوعی بر اساس برند (درصد)", | |
| "سهم فروش گروه محصولات در ماه", | |
| "تحلیل همبستگی تخفیف و فروش", | |
| "میانگین ارزش هر فاکتور (ریالی)", | |
| "ماتریس همبستگی متغیرها (Heatmap)" | |
| ] | |
| def read_data_file(file_obj): | |
| """Read data file based on its format.""" | |
| file_name = file_obj.name | |
| file_extension = file_name.split('.')[-1].lower() | |
| if file_extension == 'csv': | |
| try: | |
| return pd.read_csv(file_obj, encoding='utf-8') | |
| except UnicodeDecodeError: | |
| file_obj.seek(0) | |
| try: | |
| return pd.read_csv(file_obj, encoding='windows-1256') | |
| except: | |
| file_obj.seek(0) | |
| return pd.read_csv(file_obj, encoding='latin1') | |
| elif file_extension in ['xls', 'xlsx']: | |
| return pd.read_excel(file_obj) | |
| else: | |
| raise ValueError("فرمت فایل پشتیبانی نمیشود") | |
| def load_and_process_data(file_obj): | |
| """Upload file and prepare data with Persian headers.""" | |
| try: | |
| df = read_data_file(file_obj) | |
| # Standardize column names | |
| rename_map = { | |
| "نام تجاری": "نام برند", | |
| "گروه کامل کالا": "گروه کامل کالا", | |
| "Net_Sales_Amount": "مبلغ فروش خالص (با مالیات بر ارزش افزوده)", | |
| "ناخالص فروش با کسر تخفیف": "مبلغ فروش خالص (بدون مالیات بر ارزش افزوده)", | |
| "ناخالص فروش با کسر تخفيف": "مبلغ فروش خالص (بدون مالیات بر ارزش افزوده)", | |
| "مبلغ فروش خالص": "مبلغ فروش خالص (با مالیات بر ارزش افزوده)" | |
| } | |
| df.rename(columns=rename_map, inplace=True) | |
| # Validate required columns | |
| required_columns = [ | |
| "ماه", "کد کالا", "نام برند", "تولید کننده", | |
| "نام کالا", "مبلغ فروش ناخالص", "مبلغ تخفیف", | |
| "مبلغ فروش خالص (با مالیات بر ارزش افزوده)", | |
| "مبلغ برگشتی خالص", | |
| "مبلغ فروش خالص (بدون مالیات بر ارزش افزوده)" | |
| ] | |
| missing_cols = [col for col in required_columns if col not in df.columns] | |
| if missing_cols: | |
| return { | |
| 'success': False, | |
| 'error': f"ستونهای زیر در فایل یافت نشدند: {', '.join(missing_cols)}", | |
| 'data': None, | |
| 'kpis': None | |
| } | |
| # Clean data | |
| df["ماه"] = df["ماه"].astype(str) | |
| present_months = [m for m in MONTH_ORDER if m in df["ماه"].unique()] | |
| if present_months: | |
| df["ماه"] = pd.Categorical(df["ماه"], categories=MONTH_ORDER, ordered=True) | |
| numeric_cols = [ | |
| "مبلغ فروش ناخالص", | |
| "مبلغ فروش خالص (با مالیات بر ارزش افزوده)", | |
| "مبلغ فروش خالص (بدون مالیات بر ارزش افزوده)", | |
| "مبلغ برگشتی خالص", | |
| "تعداد فاکتور", | |
| "مبلغ تخفیف" | |
| ] | |
| for col in numeric_cols: | |
| if col in df.columns: | |
| df[col] = pd.to_numeric(df[col], errors='coerce').fillna(0) | |
| if "گروه محصول" in df.columns: | |
| df["Analyzed_Group"] = df["گروه محصول"] | |
| else: | |
| df["Analyzed_Group"] = df["نام برند"] | |
| # Calculate KPIs | |
| kpis = { | |
| 'net_sale_without_VAT': df["مبلغ فروش خالص (بدون مالیات بر ارزش افزوده)"].sum(), | |
| 'net_sale_with_VAT': df["مبلغ فروش خالص (با مالیات بر ارزش افزوده)"].sum(), | |
| 'gross_sales': df["مبلغ فروش ناخالص"].sum(), | |
| 'total_product': df["کد کالا"].nunique(), | |
| 'total_brand': df["نام برند"].nunique(), | |
| 'total_return': df["مبلغ برگشتی خالص"].sum() | |
| } | |
| return { | |
| 'success': True, | |
| 'error': None, | |
| 'data': df, | |
| 'kpis': kpis | |
| } | |
| except Exception as e: | |
| return { | |
| 'success': False, | |
| 'error': str(e), | |
| 'data': None, | |
| 'kpis': None | |
| } | |
| def create_specific_chart(chart_name, df_state): | |
| """Create specific chart based on selection.""" | |
| if df_state is None or df_state.empty: | |
| fig = go.Figure() | |
| fig.update_layout( | |
| title="لطفا ابتدا فایل داده را بارگذاری کنید 📂", | |
| xaxis={"visible": False}, | |
| yaxis={"visible": False}, | |
| annotations=[{ | |
| "text": "No Data", | |
| "xref": "paper", | |
| "yref": "paper", | |
| "showarrow": False, | |
| "font": {"size": 20} | |
| }] | |
| ) | |
| return fig | |
| df = df_state.copy() | |
| fig = None | |
| Y_LABEL_GROSS = 'مبلغ فروش ناخالص (ریال)' | |
| Y_LABEL_NET_NO_TAX = 'مبلغ فروش خالص بدون ارزش افزوده (ریال)' | |
| Y_LABEL_NET = 'مبلغ فروش خالص (ریال)' | |
| X_LABEL_MONTH = 'ماه' | |
| try: | |
| if chart_name == "مقایسه روند فروش ماهانه (ناخالص، خالص با/بدون ارزش افزوده)": | |
| cols_to_agg = { | |
| "مبلغ فروش ناخالص": "sum", | |
| "مبلغ فروش خالص (بدون مالیات بر ارزش افزوده)": "sum", | |
| "مبلغ فروش خالص (با مالیات بر ارزش افزوده)": "sum" | |
| } | |
| monthly = df.groupby("ماه").agg(cols_to_agg).reset_index().sort_values("ماه") | |
| monthly_long = monthly.melt( | |
| id_vars='ماه', | |
| var_name='نوع فروش', | |
| value_name='مبلغ' | |
| ) | |
| fig = px.line( | |
| monthly_long, x="ماه", y="مبلغ", color='نوع فروش', markers=True, | |
| labels={'ماه': X_LABEL_MONTH, 'مبلغ': 'مبلغ فروش (ریال)', 'نوع فروش': 'شاخص فروش'} | |
| ) | |
| fig.update_layout( | |
| title={"text": "مقایسه جامع روند ماهانه شاخصهای فروش", "x": 0.5, "y": 0.95}, | |
| legend_title_text='شاخص فروش', | |
| legend=dict(orientation="h", y=-0.2) | |
| ) | |
| elif chart_name == "روند فروش ماهانه بر اساس فروش ناخالص": | |
| monthly = df.groupby("ماه")["مبلغ فروش ناخالص"].sum().reset_index().sort_values("ماه") | |
| fig = px.line( | |
| monthly, x="ماه", y="مبلغ فروش ناخالص", markers=True, | |
| labels={'ماه': X_LABEL_MONTH, 'مبلغ فروش ناخالص': Y_LABEL_GROSS} | |
| ) | |
| fig.update_layout(title={"text": "روند فروش بر اساس فروش ناخالص", "x": 0.5, "y": 0.95}) | |
| elif chart_name == "روند فروش ماهانه بر اساس فروش خالص (بدون ارزش افزوده)": | |
| monthly = df.groupby("ماه")["مبلغ فروش خالص (بدون مالیات بر ارزش افزوده)"].sum().reset_index().sort_values("ماه") | |
| fig = px.line( | |
| monthly, x="ماه", y="مبلغ فروش خالص (بدون مالیات بر ارزش افزوده)", markers=True, | |
| labels={'ماه': X_LABEL_MONTH, 'مبلغ فروش خالص (بدون مالیات بر ارزش افزوده)': Y_LABEL_NET_NO_TAX} | |
| ) | |
| fig.update_layout(title={"text": "روند فروش خالص بدون ارزش افزوده ماهانه", "x": 0.5, "y": 0.95}) | |
| elif chart_name == "روند فروش ماهانه بر اساس فروش خالص (با ارزش افزوده)": | |
| col_name = "مبلغ فروش خالص (با مالیات بر ارزش افزوده)" | |
| monthly = df.groupby("ماه")[col_name].sum().reset_index().sort_values("ماه") | |
| fig = px.line( | |
| monthly, x="ماه", y=col_name, markers=True, | |
| labels={'ماه': X_LABEL_MONTH, col_name: Y_LABEL_NET} | |
| ) | |
| fig.update_layout(title={"text": "روند خالص فروش ماهانه (با ارزش افزوده)", "x": 0.5, "y": 0.95}) | |
| elif chart_name == "تحلیل کالاها (80/20)": | |
| sales_data = df.groupby("نام کالا")["مبلغ فروش خالص (بدون مالیات بر ارزش افزوده)"].sum().reset_index() | |
| sales_data = sales_data[sales_data['مبلغ فروش خالص (بدون مالیات بر ارزش افزوده)'] > 0] | |
| sales_data = sales_data.sort_values("مبلغ فروش خالص (بدون مالیات بر ارزش افزوده)", ascending=False).reset_index(drop=True) | |
| if sales_data.empty: | |
| return go.Figure().update_layout(title="هیچ فروش مثبتی برای تحلیل پارتو وجود ندارد") | |
| sales_data['Cumulative %'] = 100 * ( | |
| sales_data["مبلغ فروش خالص (بدون مالیات بر ارزش افزوده)"].cumsum() / | |
| sales_data["مبلغ فروش خالص (بدون مالیات بر ارزش افزوده)"].sum() | |
| ) | |
| over_80_subset = sales_data[sales_data['Cumulative %'] >= 80] | |
| pareto_rank = over_80_subset.index[0] + 1 if not over_80_subset.empty else len(sales_data) | |
| total_product = df["کد کالا"].nunique() | |
| display_count = max(20, pareto_rank + 3) | |
| top_sales = sales_data.head(display_count) | |
| fig = go.Figure() | |
| fig.add_trace(go.Bar( | |
| x=top_sales["نام کالا"], | |
| y=top_sales["مبلغ فروش خالص (بدون مالیات بر ارزش افزوده)"], | |
| name="فروش", | |
| marker_color='#667eea' | |
| )) | |
| fig.add_trace(go.Scatter( | |
| x=top_sales["نام کالا"], | |
| y=top_sales["Cumulative %"], | |
| name="تجمعی %", | |
| yaxis="y2", | |
| mode="lines+markers", | |
| marker_color='#f5576c' | |
| )) | |
| fig.add_shape( | |
| type="line", xref="paper", yref="y2", | |
| x0=0, y0=80, x1=1, y1=80, | |
| line=dict(color="red", width=2, dash="dash") | |
| ) | |
| fig.update_layout( | |
| title={"text": f"تحلیل پارتو: {pareto_rank} کالا از سبد {total_product} کالا 80% فروش را تامین میکند", "x": 0.5, "y": 0.95}, | |
| xaxis_title="نام کالا", | |
| yaxis_title="مبلغ فروش خالص (بدون ارزش افزوده) (ریال)", | |
| yaxis2=dict(title="درصد تجمعی", overlaying="y", side="right", range=[0, 105]), | |
| legend=dict(x=0.4, y=0.5, orientation='h'), | |
| barmode='overlay' | |
| ) | |
| elif chart_name == "محصولات با بیشترین مرجوعی": | |
| top_returns = df.groupby("نام کالا")["مبلغ برگشتی خالص"].sum().abs().reset_index() | |
| top_returns = top_returns[top_returns["مبلغ برگشتی خالص"] > 0] | |
| if not top_returns.empty: | |
| top_returns = top_returns.sort_values("مبلغ برگشتی خالص", ascending=False).head(10) | |
| fig = px.bar( | |
| top_returns, x="مبلغ برگشتی خالص", y="نام کالا", | |
| orientation='h', color_discrete_sequence=['#f5576c'] | |
| ) | |
| fig.update_layout( | |
| title={"text": "محصولات با بیشترین مرجوعی", "x": 0.5, "y": 0.95}, | |
| yaxis={'categoryorder': 'total ascending'} | |
| ) | |
| fig.update_xaxes(title="مبلغ مرجوعی (ریال)") | |
| else: | |
| fig = go.Figure().update_layout(title="هیچ مرجوعی ثبت نشده است") | |
| elif chart_name == "گروه محصولات پرفروش": | |
| top_group = df.groupby("Analyzed_Group")["مبلغ فروش خالص (بدون مالیات بر ارزش افزوده)"].sum().reset_index() | |
| top_group = top_group.sort_values("مبلغ فروش خالص (بدون مالیات بر ارزش افزوده)", ascending=False).head(10) | |
| fig = px.bar( | |
| top_group, x="مبلغ فروش خالص (بدون مالیات بر ارزش افزوده)", y="Analyzed_Group", | |
| orientation="h", color_discrete_sequence=['#667eea'] | |
| ) | |
| fig.update_layout( | |
| title={"text": "گروه محصولات پرفروش", "x": 0.5, "y": 0.95}, | |
| yaxis={'categoryorder': 'total ascending'} | |
| ) | |
| elif chart_name == "ده تولید کننده پرفروش": | |
| top_prod = df.groupby("تولید کننده")["مبلغ فروش خالص (بدون مالیات بر ارزش افزوده)"].sum().reset_index() | |
| top_prod = top_prod.sort_values("مبلغ فروش خالص (بدون مالیات بر ارزش افزوده)", ascending=False).head(10) | |
| fig = px.bar( | |
| top_prod, x="مبلغ فروش خالص (بدون مالیات بر ارزش افزوده)", y="تولید کننده", | |
| orientation="h", color_discrete_sequence=['#38ef7d'] | |
| ) | |
| fig.update_layout( | |
| title={"text": "ده تولید کننده پرفروش", "x": 0.5, "y": 0.95}, | |
| yaxis={'categoryorder': 'total ascending'} | |
| ) | |
| elif chart_name == "نرخ مرجوعی بر اساس برند (درصد)": | |
| brand_stats = df.groupby("نام برند")[["مبلغ برگشتی خالص", "مبلغ فروش ناخالص"]].sum().reset_index() | |
| brand_stats = brand_stats[brand_stats["مبلغ فروش ناخالص"] > 0] | |
| brand_stats["Return_Rate"] = (brand_stats["مبلغ برگشتی خالص"] / brand_stats["مبلغ فروش ناخالص"]) * 100 | |
| brand_stats = brand_stats[brand_stats["مبلغ فروش ناخالص"] > 10000000] | |
| brand_stats = brand_stats.sort_values("Return_Rate", ascending=False).head(15) | |
| fig = px.bar( | |
| brand_stats, x="Return_Rate", y="نام برند", orientation='h', | |
| color="Return_Rate", color_continuous_scale="Reds", | |
| hover_data={"Return_Rate": ":,.2f"} | |
| ) | |
| fig.update_layout( | |
| title={"text": "درصد نرخ مرجوعی بر اساس برند", "x": 0.5, "y": 0.95}, | |
| yaxis={'categoryorder': 'total ascending'} | |
| ) | |
| fig.update_xaxes(title="درصد مرجوعی از فروش (%)") | |
| fig.update_yaxes(title="نام برند") | |
| elif chart_name == "سهم فروش گروه محصولات در ماه": | |
| cat_trend = df.groupby(["ماه", "Analyzed_Group"])["مبلغ فروش خالص (بدون مالیات بر ارزش افزوده)"].sum().reset_index() | |
| fig = px.bar( | |
| cat_trend, x="ماه", y="مبلغ فروش خالص (بدون مالیات بر ارزش افزوده)", | |
| color="Analyzed_Group", title="ترکیب فروش ماهانه بر اساس گروه محصول" | |
| ) | |
| fig.update_layout(title={"x": 0.5, "y": 0.95}, barmode='stack') | |
| fig.update_yaxes(title="فروش (ریال)") | |
| elif chart_name == "تحلیل همبستگی تخفیف و فروش": | |
| prod_perf = df.groupby("نام کالا")[["مبلغ فروش ناخالص", "مبلغ تخفیف", "تعداد فاکتور"]].sum().reset_index() | |
| prod_perf = prod_perf[prod_perf["مبلغ فروش ناخالص"] > 0] | |
| prod_perf["Discount_Pct"] = (prod_perf["مبلغ تخفیف"] / prod_perf["مبلغ فروش ناخالص"]) * 100 | |
| prod_perf = prod_perf[prod_perf["Discount_Pct"] <= 100] | |
| fig = px.scatter( | |
| prod_perf, x="مبلغ فروش ناخالص", y="Discount_Pct", | |
| size="تعداد فاکتور", hover_name="نام کالا", | |
| color="Discount_Pct", color_continuous_scale="Viridis", | |
| title="آیا تخفیف بیشتر منجر به فروش بیشتر میشود؟", | |
| hover_data={ | |
| "مبلغ فروش ناخالص": ":,.2f", | |
| "Discount_Pct": ":.2f", | |
| "تعداد فاکتور": ":,.0f" | |
| } | |
| ) | |
| fig.update_layout(title={"x": 0.5, "y": 0.95}) | |
| fig.update_xaxes(title="مبلغ فروش ناخالص (ریال)") | |
| fig.update_yaxes(title="درصد تخفیف اعطا شده (%)") | |
| elif chart_name == "میانگین ارزش هر فاکتور (ریالی)": | |
| col_name = "مبلغ فروش خالص (با مالیات بر ارز |