Spaces:
Paused
Paused
import streamlit as st | |
import pandas as pd | |
import numpy as np | |
import plotly.express as px | |
import plotly.graph_objects as go | |
def show_supply_chain(): | |
""" | |
عرض صفحة تحليل سلاسل الإمداد | |
""" | |
st.subheader("إدارة سلاسل الإمداد") | |
# إنشاء القائمة الجانبية للخيارات | |
options = st.sidebar.radio( | |
"اختر القسم", | |
["تحليل الموردين", "تحليل المخاطر", "التكاليف والتسعير", "التحسين والتوقعات"] | |
) | |
if options == "تحليل الموردين": | |
show_vendor_analysis() | |
elif options == "تحليل المخاطر": | |
show_risk_analysis() | |
elif options == "التكاليف والتسعير": | |
show_cost_pricing() | |
elif options == "التحسين والتوقعات": | |
show_optimization() | |
def show_vendor_analysis(): | |
""" | |
عرض تحليل الموردين | |
""" | |
st.markdown("## تحليل الموردين") | |
# إنشاء بيانات توضيحية للموردين | |
vendor_data = { | |
"المورد": [ | |
"شركة الصناعات السعودية", | |
"مؤسسة الخليج للمقاولات", | |
"شركة الرياض للإنشاءات", | |
"الشركة العربية للمعدات", | |
"مصنع المنتجات الإسمنتية", | |
"شركة تقنيات البناء", | |
"مؤسسة المدار للتوريدات", | |
"شركة البنية التحتية المتكاملة" | |
], | |
"الفئة": [ | |
"مواد بناء", | |
"مقاولات", | |
"خدمات هندسية", | |
"معدات", | |
"مواد خام", | |
"تقنيات", | |
"مواد متنوعة", | |
"خدمات هندسية" | |
], | |
"قيمة التوريدات (مليون ريال)": [25.4, 18.2, 12.7, 9.8, 8.5, 7.3, 6.1, 5.8], | |
"نسبة المحتوى المحلي (%)": [85, 92, 78, 65, 100, 70, 88, 75], | |
"متوسط وقت التسليم (أيام)": [14, 30, 21, 45, 7, 15, 10, 25], | |
"التقييم العام (5)": [4.2, 3.8, 4.5, 3.5, 4.0, 4.3, 3.7, 4.1] | |
} | |
vendor_df = pd.DataFrame(vendor_data) | |
# عرض بيانات الموردين | |
st.markdown("### بيانات الموردين الرئيسيين") | |
st.dataframe(vendor_df, use_container_width=True) | |
# تحليلات الموردين | |
col1, col2 = st.columns(2) | |
with col1: | |
# توزيع الموردين حسب الفئة | |
st.markdown("### توزيع الموردين حسب الفئة") | |
category_counts = vendor_df.groupby("الفئة")["قيمة التوريدات (مليون ريال)"].sum().reset_index() | |
fig1 = px.pie( | |
category_counts, | |
values="قيمة التوريدات (مليون ريال)", | |
names="الفئة", | |
title="توزيع قيمة التوريدات حسب الفئة", | |
color_discrete_sequence=px.colors.qualitative.Bold | |
) | |
fig1.update_traces(textposition="inside", textinfo="percent+label") | |
st.plotly_chart(fig1, use_container_width=True) | |
with col2: | |
# تقييم الموردين مقابل نسبة المحتوى المحلي | |
st.markdown("### تقييم الموردين مقابل نسبة المحتوى المحلي") | |
fig2 = px.scatter( | |
vendor_df, | |
x="نسبة المحتوى المحلي (%)", | |
y="التقييم العام (5)", | |
size="قيمة التوريدات (مليون ريال)", | |
color="الفئة", | |
hover_name="المورد", | |
title="تقييم الموردين مقابل نسبة المحتوى المحلي", | |
size_max=50 | |
) | |
st.plotly_chart(fig2, use_container_width=True) | |
# تحليل أوقات التسليم | |
st.markdown("### تحليل أوقات التسليم") | |
fig3 = px.bar( | |
vendor_df.sort_values("متوسط وقت التسليم (أيام)"), | |
x="المورد", | |
y="متوسط وقت التسليم (أيام)", | |
color="متوسط وقت التسليم (أيام)", | |
color_continuous_scale="Viridis", | |
title="متوسط وقت التسليم حسب المورد" | |
) | |
st.plotly_chart(fig3, use_container_width=True) | |
# توصيات تحسين سلسلة الإمداد | |
st.markdown("### توصيات لتحسين سلسلة الإمداد") | |
recommendations = [ | |
"تنويع قاعدة الموردين في فئة المعدات لتقليل المخاطر", | |
"العمل مع الموردين لتحسين أوقات التسليم خاصة مع الشركة العربية للمعدات", | |
"زيادة الاعتماد على الموردين ذوي نسب المحتوى المحلي الأعلى", | |
"وضع خطة لتقليل الاعتماد على الموردين ذوي التقييم المنخفض", | |
"تطوير برنامج لتحسين أداء الموردين من خلال التدريب والدعم الفني" | |
] | |
for i, rec in enumerate(recommendations): | |
st.markdown(f"{i+1}. {rec}") | |
def show_risk_analysis(): | |
""" | |
عرض تحليل مخاطر سلسلة الإمداد | |
""" | |
st.markdown("## تحليل مخاطر سلسلة الإمداد") | |
# إنشاء بيانات توضيحية للمخاطر | |
risk_data = { | |
"المخاطرة": [ | |
"تأخر توريد المواد الرئيسية", | |
"ارتفاع تكلفة المواد الخام", | |
"تعطل وسائل النقل", | |
"مشاكل جودة المنتجات", | |
"نقص في المخزون", | |
"تغير متطلبات المشروع", | |
"مخاطر تقلبات العملة", | |
"أزمات الموردين المالية", | |
"الكوارث الطبيعية", | |
"المخاطر السياسية والتشريعية" | |
], | |
"الاحتمالية": [0.4, 0.6, 0.3, 0.5, 0.4, 0.7, 0.2, 0.3, 0.1, 0.2], | |
"التأثير": [0.7, 0.6, 0.5, 0.8, 0.6, 0.5, 0.4, 0.7, 0.9, 0.8], | |
"الفئة": [ | |
"توريد", "تكلفة", "لوجستيات", "جودة", "تخطيط", | |
"متطلبات", "مالية", "موردين", "خارجية", "تنظيمية" | |
] | |
} | |
risk_df = pd.DataFrame(risk_data) | |
# إضافة درجة المخاطرة | |
risk_df["درجة المخاطرة"] = risk_df["الاحتمالية"] * risk_df["التأثير"] | |
# تصنيف المخاطر | |
conditions = [ | |
(risk_df["درجة المخاطرة"] >= 0.4), | |
(risk_df["درجة المخاطرة"] >= 0.2), | |
(risk_df["درجة المخاطرة"] < 0.2) | |
] | |
values = ["عالية", "متوسطة", "منخفضة"] | |
risk_df["مستوى المخاطرة"] = np.select(conditions, values) | |
# عرض مصفوفة المخاطر | |
st.markdown("### مصفوفة مخاطر سلسلة الإمداد") | |
fig1 = px.scatter( | |
risk_df, | |
x="الاحتمالية", | |
y="التأثير", | |
color="مستوى المخاطرة", | |
size="درجة المخاطرة", | |
hover_name="المخاطرة", | |
text="المخاطرة", | |
color_discrete_map={"عالية": "#FF5733", "متوسطة": "#FFC300", "منخفضة": "#33FF57"}, | |
title="مصفوفة تحليل المخاطر", | |
size_max=50 | |
) | |
fig1.update_layout( | |
xaxis_title="احتمالية الحدوث", | |
yaxis_title="مستوى التأثير", | |
xaxis=dict(range=[0, 1]), | |
yaxis=dict(range=[0, 1]) | |
) | |
fig1.update_traces( | |
textposition="top center", | |
textfont=dict(size=10) | |
) | |
st.plotly_chart(fig1, use_container_width=True) | |
# عرض جدول المخاطر | |
st.markdown("### قائمة المخاطر مرتبة حسب درجة الخطورة") | |
sorted_risks = risk_df.sort_values("درجة المخاطرة", ascending=False) | |
# تنسيق العرض | |
formatted_risks = sorted_risks.copy() | |
formatted_risks["الاحتمالية"] = formatted_risks["الاحتمالية"].apply(lambda x: f"{x:.1%}") | |
formatted_risks["التأثير"] = formatted_risks["التأثير"].apply(lambda x: f"{x:.1%}") | |
formatted_risks["درجة المخاطرة"] = formatted_risks["درجة المخاطرة"].apply(lambda x: f"{x:.1%}") | |
st.dataframe(formatted_risks, use_container_width=True) | |
# المخاطر حسب الفئة | |
st.markdown("### المخاطر حسب الفئة") | |
category_risks = risk_df.groupby("الفئة")["درجة المخاطرة"].mean().reset_index() | |
category_risks = category_risks.sort_values("درجة المخاطرة", ascending=False) | |
fig2 = px.bar( | |
category_risks, | |
x="الفئة", | |
y="درجة المخاطرة", | |
color="درجة المخاطرة", | |
color_continuous_scale="Reds", | |
title="متوسط درجة المخاطرة حسب الفئة" | |
) | |
fig2.update_layout(yaxis_tickformat=".1%") | |
st.plotly_chart(fig2, use_container_width=True) | |
# استراتيجيات تخفيف المخاطر | |
st.markdown("### استراتيجيات تخفيف المخاطر ذات الأولوية العالية") | |
high_risks = sorted_risks[sorted_risks["مستوى المخاطرة"] == "عالية"] | |
mitigation_strategies = { | |
"تأخر توريد المواد الرئيسية": "إنشاء قاعدة موردين بديلة وتطوير خطط طوارئ للتوريد", | |
"ارتفاع تكلفة المواد الخام": "توقيع عقود طويلة الأجل وتأمين أسعار ثابتة", | |
"مشاكل جودة المنتجات": "تعزيز نظام فحص الجودة وتطوير معايير قبول صارمة", | |
"تغير متطلبات المشروع": "تحسين عمليات إدارة التغيير وتوثيق المتطلبات بشكل أفضل", | |
"أزمات الموردين المالية": "تقييم الصحة المالية للموردين بشكل دوري واستخدام ضمانات التنفيذ", | |
"الكوارث الطبيعية": "تطوير خطط استمرارية الأعمال واستخدام موردين من مناطق جغرافية متنوعة" | |
} | |
for _, risk in high_risks.iterrows(): | |
risk_name = risk["المخاطرة"] | |
strategy = mitigation_strategies.get(risk_name, "وضع استراتيجية مخصصة للتخفيف من هذه المخاطر") | |
st.markdown(f"**{risk_name}** (درجة المخاطرة: {risk['درجة المخاطرة']})") | |
st.markdown(f"*استراتيجية التخفيف:* {strategy}") | |
st.markdown("---") | |
def show_cost_pricing(): | |
""" | |
عرض تحليل التكاليف والتسعير | |
""" | |
st.markdown("## تحليل التكاليف والتسعير") | |
# إنشاء بيانات توضيحية للمواد | |
materials_data = { | |
"المادة": [ | |
"حديد تسليح", | |
"إسمنت", | |
"خرسانة جاهزة", | |
"طوب", | |
"رمل", | |
"خشب", | |
"مواد عازلة", | |
"كابلات كهربائية", | |
"أنابيب", | |
"أصباغ" | |
], | |
"متوسط السعر (ريال/وحدة)": [3200, 15, 240, 2.5, 75, 1200, 85, 120, 160, 65], | |
"الكمية المتوقعة": [150, 8000, 1200, 25000, 350, 200, 750, 2000, 1500, 300], | |
"نسبة التغير السعري (%)": [12, 5, 8, 2, 3, 15, 7, 10, 6, 4], | |
"المصدر": [ | |
"محلي", "محلي", "محلي", "محلي", "محلي", | |
"مستورد", "مستورد", "محلي", "محلي", "مستورد" | |
] | |
} | |
materials_df = pd.DataFrame(materials_data) | |
# حساب إجمالي التكلفة | |
materials_df["إجمالي التكلفة (ريال)"] = materials_df["متوسط السعر (ريال/وحدة)"] * materials_df["الكمية المتوقعة"] | |
# حساب تأثير التغير السعري | |
materials_df["تأثير التغير السعري (ريال)"] = materials_df["إجمالي التكلفة (ريال)"] * (materials_df["نسبة التغير السعري (%)"] / 100) | |
# عرض بيانات المواد | |
st.markdown("### تحليل تكاليف المواد الرئيسية") | |
# تنسيق العرض | |
formatted_materials = materials_df.copy() | |
formatted_materials["إجمالي التكلفة (ريال)"] = formatted_materials["إجمالي التكلفة (ريال)"].map(lambda x: f"{x:,.0f}") | |
formatted_materials["تأثير التغير السعري (ريال)"] = formatted_materials["تأثير التغير السعري (ريال)"].map(lambda x: f"{x:,.0f}") | |
st.dataframe(formatted_materials, use_container_width=True) | |
# إجمالي التكاليف والمؤشرات | |
total_cost = materials_df["إجمالي التكلفة (ريال)"].sum() | |
total_price_impact = materials_df["تأثير التغير السعري (ريال)"].sum() | |
avg_price_change = materials_df["نسبة التغير السعري (%)"].mean() | |
col1, col2, col3 = st.columns(3) | |
with col1: | |
st.metric( | |
label="إجمالي تكلفة المواد (ريال)", | |
value=f"{total_cost:,.0f}" | |
) | |
with col2: | |
st.metric( | |
label="تأثير التغير السعري (ريال)", | |
value=f"{total_price_impact:,.0f}", | |
delta=f"{total_price_impact / total_cost:.1%}" | |
) | |
with col3: | |
st.metric( | |
label="متوسط نسبة التغير السعري", | |
value=f"{avg_price_change:.1f}%" | |
) | |
# توزيع التكاليف حسب المواد | |
st.markdown("### توزيع تكاليف المواد") | |
fig1 = px.pie( | |
materials_df, | |
values="إجمالي التكلفة (ريال)", | |
names="المادة", | |
title="توزيع تكاليف المواد", | |
color_discrete_sequence=px.colors.qualitative.Bold | |
) | |
fig1.update_traces(textposition="inside", textinfo="percent+label") | |
st.plotly_chart(fig1, use_container_width=True) | |
# تأثير التغير السعري حسب المادة | |
st.markdown("### تأثير التغير السعري حسب المادة") | |
sorted_impact = materials_df.sort_values("تأثير التغير السعري (ريال)", ascending=False) | |
fig2 = px.bar( | |
sorted_impact, | |
x="المادة", | |
y="تأثير التغير السعري (ريال)", | |
color="نسبة التغير السعري (%)", | |
color_continuous_scale="Reds", | |
title="تأثير التغير السعري حسب المادة" | |
) | |
st.plotly_chart(fig2, use_container_width=True) | |
# مقارنة المواد المحلية والمستوردة | |
st.markdown("### مقارنة المواد المحلية والمستوردة") | |
source_comparison = materials_df.groupby("المصدر").agg({ | |
"إجمالي التكلفة (ريال)": "sum", | |
"تأثير التغير السعري (ريال)": "sum", | |
"المادة": "count" | |
}).reset_index() | |
source_comparison.columns = ["المصدر", "إجمالي التكلفة (ريال)", "تأثير التغير السعري (ريال)", "عدد المواد"] | |
col1, col2 = st.columns(2) | |
with col1: | |
# نسبة التكلفة حسب المصدر | |
fig3 = px.pie( | |
source_comparison, | |
values="إجمالي التكلفة (ريال)", | |
names="المصدر", | |
title="توزيع التكاليف: محلي مقابل مستورد", | |
color_discrete_map={"محلي": "#1976D2", "مستورد": "#D32F2F"} | |
) | |
fig3.update_traces(textposition="inside", textinfo="percent+label") | |
st.plotly_chart(fig3, use_container_width=True) | |
with col2: | |
# متوسط تأثير التغير السعري حسب المصدر | |
source_comparison["متوسط تأثير التغير لكل مادة"] = source_comparison["تأثير التغير السعري (ريال)"] / source_comparison["عدد المواد"] | |
fig4 = px.bar( | |
source_comparison, | |
x="المصدر", | |
y="متوسط تأثير التغير لكل مادة", | |
color="المصدر", | |
title="متوسط تأثير التغير السعري حسب المصدر", | |
color_discrete_map={"محلي": "#1976D2", "مستورد": "#D32F2F"} | |
) | |
st.plotly_chart(fig4, use_container_width=True) | |
# توصيات لتحسين التكاليف | |
st.markdown("### توصيات لتحسين التكاليف") | |
recommendations = [ | |
"إبرام عقود طويلة الأجل للمواد ذات التغير السعري المرتفع (الحديد والخشب)", | |
"البحث عن موردين محليين بديلين للمواد المستوردة لتقليل تأثير تقلبات الأسعار", | |
"شراء المواد ذات الاستهلاك العالي بكميات كبيرة للحصول على خصومات الكمية", | |
"تطوير استراتيجية تخزين للمواد ذات التأثير السعري المرتفع", | |
"استخدام نماذج التنبؤ لتوقيت الشراء بشكل أفضل وتجنب فترات ارتفاع الأسعار" | |
] | |
for i, rec in enumerate(recommendations): | |
st.markdown(f"{i+1}. {rec}") | |
def show_optimization(): | |
""" | |
عرض تحسين سلسلة الإمداد والتوقعات | |
""" | |
st.markdown("## تحسين سلسلة الإمداد والتوقعات") | |
# 1. تحسين المخزون | |
st.markdown("### تحسين مستويات المخزون") | |
# إنشاء بيانات توضيحية للمخزون | |
inventory_data = { | |
"المادة": [ | |
"حديد تسليح", "إسمنت", "خرسانة جاهزة", "طوب", "رمل", | |
"خشب", "مواد عازلة", "كابلات كهربائية", "أنابيب", "أصباغ" | |
], | |
"المخزون الحالي": [35, 1200, 80, 8000, 120, 45, 200, 450, 320, 85], | |
"الحد الأدنى المطلوب": [20, 800, 60, 5000, 100, 30, 150, 300, 250, 50], | |
"الحد الأقصى": [50, 2000, 120, 12000, 200, 60, 300, 600, 400, 120], | |
"متوسط الاستهلاك اليومي": [2, 60, 10, 400, 8, 3, 12, 25, 15, 5], | |
"وقت إعادة الطلب (أيام)": [15, 7, 3, 10, 5, 20, 15, 12, 10, 8] | |
} | |
inventory_df = pd.DataFrame(inventory_data) | |
# حساب مؤشرات المخزون | |
inventory_df["أيام التغطية المتبقية"] = inventory_df["المخزون الحالي"] / inventory_df["متوسط الاستهلاك اليومي"] | |
inventory_df["حالة المخزون"] = np.where( | |
inventory_df["المخزون الحالي"] < inventory_df["الحد الأدنى المطلوب"], | |
"منخفض", | |
np.where( | |
inventory_df["المخزون الحالي"] > inventory_df["الحد الأقصى"], | |
"مرتفع", | |
"مناسب" | |
) | |
) | |
inventory_df["توصية"] = np.where( | |
inventory_df["أيام التغطية المتبقية"] < inventory_df["وقت إعادة الطلب (أيام)"], | |
"يجب الطلب الآن", | |
"المخزون كافي" | |
) | |
# تنسيق العرض | |
formatted_inventory = inventory_df.copy() | |
formatted_inventory["أيام التغطية المتبقية"] = formatted_inventory["أيام التغطية المتبقية"].round(1) | |
# عرض بيانات المخزون | |
st.dataframe(formatted_inventory, use_container_width=True) | |
# تحليل حالة المخزون | |
col1, col2 = st.columns(2) | |
with col1: | |
# مخطط حالة المخزون | |
status_counts = inventory_df["حالة المخزون"].value_counts().reset_index() | |
status_counts.columns = ["الحالة", "العدد"] | |
fig1 = px.pie( | |
status_counts, | |
values="العدد", | |
names="الحالة", | |
title="توزيع حالة المخزون", | |
color_discrete_map={ | |
"منخفض": "#D32F2F", | |
"مناسب": "#43A047", | |
"مرتفع": "#FFC107" | |
} | |
) | |
fig1.update_traces(textposition="inside", textinfo="percent+label") | |
st.plotly_chart(fig1, use_container_width=True) | |
with col2: | |
# أيام التغطية للمواد | |
st.markdown("### أيام التغطية المتبقية للمخزون") | |
fig2 = px.bar( | |
inventory_df.sort_values("أيام التغطية المتبقية"), | |
x="المادة", | |
y="أيام التغطية المتبقية", | |
color="حالة المخزون", | |
title="أيام التغطية المتبقية للمخزون", | |
color_discrete_map={ | |
"منخفض": "#D32F2F", | |
"مناسب": "#43A047", | |
"مرتفع": "#FFC107" | |
} | |
) | |
# إضافة خط لوقت إعادة الطلب | |
for i, row in inventory_df.iterrows(): | |
fig2.add_shape( | |
type="line", | |
x0=i-0.4, | |
x1=i+0.4, | |
y0=row["وقت إعادة الطلب (أيام)"], | |
y1=row["وقت إعادة الطلب (أيام)"], | |
line=dict(color="red", width=2, dash="dash") | |
) | |
st.plotly_chart(fig2, use_container_width=True) | |
# 2. تحليل التوقعات المستقبلية | |
st.markdown("### التوقعات المستقبلية للطلب والأسعار") | |
# إنشاء بيانات توضيحية للتوقعات | |
months = ["يناير", "فبراير", "مارس", "أبريل", "مايو", "يونيو", | |
"يوليو", "أغسطس", "سبتمبر", "أكتوبر", "نوفمبر", "ديسمبر"] | |
forecast_data = { | |
"الشهر": months, | |
"الطلب المتوقع (طن)": [250, 265, 280, 300] | |
} |