Spaces:
Runtime error
Runtime error
| """ | |
| وحدة التسعير المتكاملة | |
| """ | |
| import streamlit as st | |
| import pandas as pd | |
| import numpy as np | |
| import random | |
| from datetime import datetime | |
| import time | |
| class PricingApp: | |
| """ | |
| وحدة التسعير المتكاملة للنظام | |
| """ | |
| def __init__(self): | |
| """ | |
| تهيئة وحدة التسعير | |
| """ | |
| # تهيئة حالة الجلسة الخاصة بالتسعير إذا لم تكن موجودة | |
| if 'pricing_projects' not in st.session_state: | |
| # إنشاء بيانات تجريبية للمشاريع | |
| st.session_state.pricing_projects = self._generate_sample_projects() | |
| if 'pricing_templates' not in st.session_state: | |
| # إنشاء بيانات تجريبية لقوالب التسعير | |
| st.session_state.pricing_templates = self._generate_sample_templates() | |
| if 'pricing_resources' not in st.session_state: | |
| # إنشاء بيانات تجريبية للموارد | |
| st.session_state.pricing_resources = self._generate_sample_resources() | |
| def run(self): | |
| """ | |
| تشغيل وحدة التسعير | |
| """ | |
| st.markdown("<h2 class='module-title'>وحدة التسعير المتكاملة</h2>", unsafe_allow_html=True) | |
| # إنشاء تبويبات للتسعير المختلفة | |
| tabs = st.tabs(["لوحة التحكم", "تسعير المناقصات", "جداول الكميات", "تحليل الأسعار", "قوالب التسعير"]) | |
| with tabs[0]: | |
| self._render_dashboard() | |
| with tabs[1]: | |
| self._render_tender_pricing() | |
| with tabs[2]: | |
| self._render_bill_of_quantities() | |
| with tabs[3]: | |
| self._render_price_analysis() | |
| with tabs[4]: | |
| self._render_pricing_templates() | |
| def _render_dashboard(self): | |
| """ | |
| عرض لوحة التحكم | |
| """ | |
| st.markdown("### لوحة تحكم التسعير") | |
| # عرض المؤشرات الرئيسية | |
| col1, col2, col3, col4 = st.columns(4) | |
| with col1: | |
| active_tenders = len([p for p in st.session_state.pricing_projects if p['status'] == 'قيد التسعير']) | |
| st.info(f"### {active_tenders}\nمناقصات قيد التسعير", icon="📝") | |
| with col2: | |
| completed_tenders = len([p for p in st.session_state.pricing_projects if p['status'] == 'تم التسعير']) | |
| st.success(f"### {completed_tenders}\nمناقصات تم تسعيرها", icon="✅") | |
| with col3: | |
| awarded_tenders = len([p for p in st.session_state.pricing_projects if p['status'] == 'تمت الترسية']) | |
| st.success(f"### {awarded_tenders}\nمناقصات تمت ترسيتها", icon="🏆") | |
| with col4: | |
| rejected_tenders = len([p for p in st.session_state.pricing_projects if p['status'] == 'مرفوضة']) | |
| st.error(f"### {rejected_tenders}\nمناقصات مرفوضة", icon="❌") | |
| # عرض المناقصات الحالية | |
| st.markdown("### المناقصات الحالية") | |
| # تصفية المناقصات النشطة | |
| active_projects = [p for p in st.session_state.pricing_projects if p['status'] in ['قيد التسعير', 'تم التسعير']] | |
| if active_projects: | |
| # تحويل البيانات إلى DataFrame | |
| df = pd.DataFrame(active_projects) | |
| df = df[['id', 'name', 'client', 'value', 'deadline', 'status', 'completion']] | |
| df.columns = ['الرقم', 'اسم المناقصة', 'العميل', 'القيمة التقديرية', 'الموعد النهائي', 'الحالة', 'نسبة الإنجاز'] | |
| # تنسيق القيم | |
| df['القيمة التقديرية'] = df['القيمة التقديرية'].apply(lambda x: f"{x:,} ريال") | |
| df['نسبة الإنجاز'] = df['نسبة الإنجاز'].apply(lambda x: f"{x}%") | |
| # عرض الجدول | |
| st.dataframe(df, use_container_width=True) | |
| else: | |
| st.info("لا توجد مناقصات نشطة حالياً", icon="ℹ️") | |
| # عرض إحصائيات التسعير | |
| st.markdown("### إحصائيات التسعير") | |
| col1, col2 = st.columns(2) | |
| with col1: | |
| st.markdown("#### نسب النجاح حسب نوع المشروع") | |
| # إنشاء بيانات تجريبية | |
| success_by_type = { | |
| "طرق وجسور": 75, | |
| "مباني": 60, | |
| "بنية تحتية": 80, | |
| "مياه وصرف صحي": 65, | |
| "كهرباء": 70 | |
| } | |
| # تحويل البيانات إلى DataFrame | |
| success_df = pd.DataFrame({ | |
| "نوع المشروع": list(success_by_type.keys()), | |
| "نسبة النجاح": list(success_by_type.values()) | |
| }) | |
| # عرض الرسم البياني | |
| st.bar_chart(success_df.set_index("نوع المشروع")) | |
| with col2: | |
| st.markdown("#### متوسط هامش الربح حسب العميل") | |
| # إنشاء بيانات تجريبية | |
| margin_by_client = { | |
| "وزارة النقل": 12, | |
| "وزارة الإسكان": 15, | |
| "أمانة منطقة الرياض": 10, | |
| "شركة أرامكو": 8, | |
| "الهيئة الملكية": 14 | |
| } | |
| # تحويل البيانات إلى DataFrame | |
| margin_df = pd.DataFrame({ | |
| "العميل": list(margin_by_client.keys()), | |
| "هامش الربح (%)": list(margin_by_client.values()) | |
| }) | |
| # عرض الرسم البياني | |
| st.bar_chart(margin_df.set_index("العميل")) | |
| def _render_tender_pricing(self): | |
| """ | |
| عرض واجهة تسعير المناقصات | |
| """ | |
| st.markdown("### تسعير المناقصات") | |
| # اختيار المناقصة | |
| project_names = [p['name'] for p in st.session_state.pricing_projects] | |
| selected_project = st.selectbox("اختر المناقصة", options=project_names, key="selected_pricing_project") | |
| # الحصول على معلومات المناقصة المحددة | |
| project_info = next((p for p in st.session_state.pricing_projects if p['name'] == selected_project), None) | |
| if project_info: | |
| # عرض معلومات المناقصة | |
| st.markdown(f""" | |
| <div style="background-color: var(--gray-100); padding: 15px; border-radius: 8px; margin-bottom: 20px;"> | |
| <h4 style="color: var(--primary-color);">{project_info['name']}</h4> | |
| <p><strong>العميل:</strong> {project_info['client']}</p> | |
| <p><strong>القيمة التقديرية:</strong> {project_info['value']:,} ريال</p> | |
| <p><strong>الموعد النهائي:</strong> {project_info['deadline']}</p> | |
| <p><strong>الحالة:</strong> {project_info['status']}</p> | |
| <p><strong>نسبة الإنجاز:</strong> {project_info['completion']}%</p> | |
| </div> | |
| """, unsafe_allow_html=True) | |
| # تبويبات فرعية للتسعير | |
| subtabs = st.tabs(["ملخص التسعير", "جدول الكميات", "تحليل التكاليف", "هامش الربح", "المخاطر"]) | |
| with subtabs[0]: | |
| self._render_pricing_summary(project_info) | |
| with subtabs[1]: | |
| self._render_project_boq(project_info) | |
| with subtabs[2]: | |
| self._render_cost_analysis(project_info) | |
| with subtabs[3]: | |
| self._render_profit_margin(project_info) | |
| with subtabs[4]: | |
| self._render_risk_analysis(project_info) | |
| def _render_pricing_summary(self, project_info): | |
| """ | |
| عرض ملخص التسعير | |
| """ | |
| st.markdown("#### ملخص التسعير") | |
| # عرض شريط التقدم | |
| st.progress(project_info['completion'] / 100, text=f"نسبة إكمال التسعير: {project_info['completion']}%") | |
| # عرض ملخص التكاليف | |
| st.markdown("##### ملخص التكاليف") | |
| # إنشاء بيانات تجريبية للتكاليف | |
| direct_cost = project_info['value'] * 0.7 | |
| indirect_cost = project_info['value'] * 0.15 | |
| profit_margin = project_info['value'] * 0.15 | |
| costs = { | |
| "التكاليف المباشرة": direct_cost, | |
| "التكاليف غير المباشرة": indirect_cost, | |
| "هامش الربح": profit_margin, | |
| "إجمالي العرض": project_info['value'] | |
| } | |
| # عرض جدول التكاليف | |
| costs_df = pd.DataFrame({ | |
| "البند": list(costs.keys()), | |
| "القيمة (ريال)": [f"{value:,.2f}" for value in costs.values()], | |
| "النسبة": [f"{(value / project_info['value']) * 100:.1f}%" for value in costs.values()] | |
| }) | |
| st.dataframe(costs_df, use_container_width=True, hide_index=True) | |
| # عرض الرسم البياني للتكاليف | |
| cost_chart_data = pd.DataFrame({ | |
| "البند": ["التكاليف المباشرة", "التكاليف غير المباشرة", "هامش الربح"], | |
| "القيمة": [direct_cost, indirect_cost, profit_margin] | |
| }) | |
| st.bar_chart(cost_chart_data.set_index("البند")) | |
| # عرض ملاحظات التسعير | |
| st.markdown("##### ملاحظات التسعير") | |
| notes = [ | |
| "تم تحديث أسعار المواد وفقاً لآخر الأسعار في السوق", | |
| "تم زيادة هامش المخاطر بنسبة 2% نظراً لموقع المشروع", | |
| "تم تخفيض تكلفة النقل بناءً على توفر المعدات في الموقع", | |
| "يجب مراجعة أسعار الحديد قبل تقديم العرض النهائي" | |
| ] | |
| for note in notes: | |
| st.markdown(f"- {note}") | |
| # زر تحديث التسعير | |
| if st.button("تحديث التسعير", key="update_pricing_btn"): | |
| st.success("تم تحديث التسعير بنجاح", icon="✅") | |
| def _render_project_boq(self, project_info): | |
| """ | |
| عرض جدول الكميات للمشروع | |
| """ | |
| st.markdown("#### جدول الكميات") | |
| # إنشاء بيانات تجريبية لجدول الكميات | |
| boq_items = [] | |
| categories = ["أعمال ترابية", "أعمال خرسانية", "أعمال معمارية", "أعمال كهربائية", "أعمال ميكانيكية"] | |
| for i, category in enumerate(categories): | |
| # إنشاء عدة بنود لكل فئة | |
| for j in range(3): | |
| item_id = i * 3 + j + 1 | |
| item = { | |
| "الرقم": f"{i+1}.{j+1}", | |
| "الفئة": category, | |
| "البند": f"بند {item_id}", | |
| "الوصف": f"وصف تفصيلي للبند {item_id} ضمن فئة {category}", | |
| "الوحدة": random.choice(["متر", "متر مربع", "متر مكعب", "طن", "قطعة"]), | |
| "الكمية": random.randint(10, 1000), | |
| "سعر الوحدة": random.randint(100, 5000), | |
| "الإجمالي": 0 | |
| } | |
| item["الإجمالي"] = item["الكمية"] * item["سعر الوحدة"] | |
| boq_items.append(item) | |
| # تحويل البيانات إلى DataFrame | |
| boq_df = pd.DataFrame(boq_items) | |
| # تنسيق القيم | |
| boq_df["سعر الوحدة"] = boq_df["سعر الوحدة"].apply(lambda x: f"{x:,}") | |
| boq_df["الإجمالي"] = boq_df["الإجمالي"].apply(lambda x: f"{x:,}") | |
| # عرض جدول الكميات مع تجميع حسب الفئة | |
| st.dataframe(boq_df, use_container_width=True) | |
| # حساب الإجمالي | |
| total = sum(item["الإجمالي"] for item in boq_items) | |
| st.markdown(f"**الإجمالي:** {total:,} ريال") | |
| # أزرار التحكم | |
| col1, col2, col3 = st.columns(3) | |
| with col1: | |
| st.button("إضافة بند", key="add_boq_item_btn") | |
| with col2: | |
| st.button("استيراد من Excel", key="import_boq_btn") | |
| with col3: | |
| st.button("تصدير إلى Excel", key="export_boq_btn") | |
| def _render_cost_analysis(self, project_info): | |
| """ | |
| عرض تحليل التكاليف | |
| """ | |
| st.markdown("#### تحليل التكاليف") | |
| # تبويبات فرعية لتحليل التكاليف | |
| cost_tabs = st.tabs(["تكاليف المواد", "تكاليف العمالة", "تكاليف المعدات", "التكاليف غير المباشرة"]) | |
| with cost_tabs[0]: | |
| # تحليل تكاليف المواد | |
| st.markdown("##### تكاليف المواد") | |
| # إنشاء بيانات تجريبية لتكاليف المواد | |
| materials = [ | |
| {"المادة": "خرسانة", "الكمية": 500, "الوحدة": "متر مكعب", "سعر الوحدة": 250, "الإجمالي": 125000}, | |
| {"المادة": "حديد تسليح", "الكمية": 50, "الوحدة": "طن", "سعر الوحدة": 3000, "الإجمالي": 150000}, | |
| {"المادة": "طابوق", "الكمية": 10000, "الوحدة": "قطعة", "سعر الوحدة": 5, "الإجمالي": 50000}, | |
| {"المادة": "بلاط", "الكمية": 1000, "الوحدة": "متر مربع", "سعر الوحدة": 80, "الإجمالي": 80000}, | |
| {"المادة": "أسمنت", "الكمية": 1000, "الوحدة": "كيس", "سعر الوحدة": 20, "الإجمالي": 20000} | |
| ] | |
| # تحويل البيانات إلى DataFrame | |
| materials_df = pd.DataFrame(materials) | |
| # تنسيق القيم | |
| materials_df["سعر الوحدة"] = materials_df["سعر الوحدة"].apply(lambda x: f"{x:,}") | |
| materials_df["الإجمالي"] = materials_df["الإجمالي"].apply(lambda x: f"{x:,}") | |
| # عرض جدول المواد | |
| st.dataframe(materials_df, use_container_width=True) | |
| # حساب الإجمالي | |
| total_materials = sum(item["الإجمالي"] for item in materials) | |
| st.markdown(f"**إجمالي تكاليف المواد:** {total_materials:,} ريال") | |
| # عرض الرسم البياني | |
| materials_chart = pd.DataFrame({ | |
| "المادة": [item["المادة"] for item in materials], | |
| "التكلفة": [item["الإجمالي"] for item in materials] | |
| }) | |
| st.bar_chart(materials_chart.set_index("المادة")) | |
| with cost_tabs[1]: | |
| # تحليل تكاليف العمالة | |
| st.markdown("##### تكاليف العمالة") | |
| # إنشاء بيانات تجريبية لتكاليف العمالة | |
| labor = [ | |
| {"الوظيفة": "مهندس موقع", "العدد": 2, "المدة (شهر)": 12, "التكلفة الشهرية": 15000, "الإجمالي": 360000}, | |
| {"الوظيفة": "مشرف", "العدد": 4, "المدة (شهر)": 12, "التكلفة الشهرية": 8000, "الإجمالي": 384000}, | |
| {"الوظيفة": "فني", "العدد": 10, "المدة (شهر)": 12, "التكلفة الشهرية": 5000, "الإجمالي": 600000}, | |
| {"الوظيفة": "عامل", "العدد": 20, "المدة (شهر)": 12, "التكلفة الشهرية": 3000, "الإجمالي": 720000}, | |
| {"الوظيفة": "سائق", "العدد": 5, "المدة (شهر)": 12, "التكلفة الشهرية": 4000, "الإجمالي": 240000} | |
| ] | |
| # تحويل البيانات إلى DataFrame | |
| labor_df = pd.DataFrame(labor) | |
| # تنسيق القيم | |
| labor_df["التكلفة الشهرية"] = labor_df["التكلفة الشهرية"].apply(lambda x: f"{x:,}") | |
| labor_df["الإجمالي"] = labor_df["الإجمالي"].apply(lambda x: f"{x:,}") | |
| # عرض جدول العمالة | |
| st.dataframe(labor_df, use_container_width=True) | |
| # حساب الإجمالي | |
| total_labor = sum(item["الإجمالي"] for item in labor) | |
| st.markdown(f"**إجمالي تكاليف العمالة:** {total_labor:,} ريال") | |
| with cost_tabs[2]: | |
| # تحليل تكاليف المعدات | |
| st.markdown("##### تكاليف المعدات") | |
| # إنشاء بيانات تجريبية لتكاليف المعدات | |
| equipment = [ | |
| {"المعدة": "حفارة", "العدد": 2, "المدة (شهر)": 6, "التكلفة الشهرية": 20000, "الإجمالي": 240000}, | |
| {"المعدة": "لودر", "العدد": 2, "المدة (شهر)": 8, "التكلفة الشهرية": 15000, "الإجمالي": 240000}, | |
| {"المعدة": "شاحنة نقل", "العدد": 4, "المدة (شهر)": 12, "التكلفة الشهرية": 10000, "الإجمالي": 480000}, | |
| {"المعدة": "خلاطة خرسانة", "العدد": 2, "المدة (شهر)": 10, "التكلفة الشهرية": 8000, "الإجمالي": 160000}, | |
| {"المعدة": "رافعة", "العدد": 1, "المدة (شهر)": 4, "التكلفة الشهرية": 25000, "الإجمالي": 100000} | |
| ] | |
| # تحويل البيانات إلى DataFrame | |
| equipment_df = pd.DataFrame(equipment) | |
| # تنسيق القيم | |
| equipment_df["التكلفة الشهرية"] = equipment_df["التكلفة الشهرية"].apply(lambda x: f"{x:,}") | |
| equipment_df["الإجمالي"] = equipment_df["الإجمالي"].apply(lambda x: f"{x:,}") | |
| # عرض جدول المعدات | |
| st.dataframe(equipment_df, use_container_width=True) | |
| # حساب الإجمالي | |
| total_equipment = sum(item["الإجمالي"] for item in equipment) | |
| st.markdown(f"**إجمالي تكاليف المعدات:** {total_equipment:,} ريال") | |
| with cost_tabs[3]: | |
| # تحليل التكاليف غير المباشرة | |
| st.markdown("##### التكاليف غير المباشرة") | |
| # إنشاء بيانات تجريبية للتكاليف غير المباشرة | |
| indirect_costs = [ | |
| {"البند": "إدارة المشروع", "النسبة": "5%", "القيمة": 250000}, | |
| {"البند": "ضمانات بنكية", "النسبة": "2%", "القيمة": 100000}, | |
| {"البند": "تأمين", "النسبة": "1.5%", "القيمة": 75000}, | |
| {"البند": "مكاتب الموقع", "النسبة": "1%", "القيمة": 50000}, | |
| {"البند": "مصاريف إدارية", "النسبة": "3%", "القيمة": 150000}, | |
| {"البند": "مصاريف نثرية", "النسبة": "1.5%", "القيمة": 75000} | |
| ] | |
| # تحويل البيانات إلى DataFrame | |
| indirect_costs_df = pd.DataFrame(indirect_costs) | |
| # تنسيق القيم | |
| indirect_costs_df["القيمة"] = indirect_costs_df["القيمة"].apply(lambda x: f"{x:,}") | |
| # عرض جدول التكاليف غير المباشرة | |
| st.dataframe(indirect_costs_df, use_container_width=True) | |
| # حساب الإجمالي | |
| total_indirect = sum(item["القيمة"] for item in indirect_costs) | |
| st.markdown(f"**إجمالي التكاليف غير المباشرة:** {total_indirect:,} ريال") | |
| def _render_profit_margin(self, project_info): | |
| """ | |
| عرض تحليل هامش الربح | |
| """ | |
| st.markdown("#### تحليل هامش الربح") | |
| # إنشاء بيانات تجريبية لتحليل هامش الربح | |
| direct_cost = project_info['value'] * 0.7 | |
| indirect_cost = project_info['value'] * 0.15 | |
| total_cost = direct_cost + indirect_cost | |
| profit = project_info['value'] - total_cost | |
| profit_percentage = (profit / project_info['value']) * 100 | |
| # عرض ملخص هامش الربح | |
| col1, col2, col3 = st.columns(3) | |
| with col1: | |
| st.metric("إجمالي التكاليف", f"{total_cost:,.2f} ريال") | |
| with col2: | |
| st.metric("هامش الربح", f"{profit:,.2f} ريال") | |
| with col3: | |
| st.metric("نسبة الربح", f"{profit_percentage:.2f}%") | |
| # عرض تحليل الحساسية | |
| st.markdown("##### تحليل الحساسية") | |
| st.markdown("تأثير تغير التكاليف على هامش الربح") | |
| # إنشاء بيانات تحليل الحساسية | |
| sensitivity_data = [] | |
| cost_changes = [-10, -5, 0, 5, 10, 15, 20] | |
| for change in cost_changes: | |
| adjusted_cost = total_cost * (1 + change / 100) | |
| adjusted_profit = project_info['value'] - adjusted_cost | |
| adjusted_profit_percentage = (adjusted_profit / project_info['value']) * 100 | |
| sensitivity_data.append({ | |
| "تغير التكاليف": f"{change}%", | |
| "التكلفة المعدلة": adjusted_cost, | |
| "الربح المعدل": adjusted_profit, | |
| "نسبة الربح المعدلة": adjusted_profit_percentage | |
| }) | |
| # تحويل البيانات إلى DataFrame | |
| sensitivity_df = pd.DataFrame(sensitivity_data) | |
| # تنسيق القيم | |
| sensitivity_df["التكلفة المعدلة"] = sensitivity_df["التكلفة المعدلة"].apply(lambda x: f"{x:,.2f}") | |
| sensitivity_df["الربح المعدل"] = sensitivity_df["الربح المعدل"].apply(lambda x: f"{x:,.2f}") | |
| sensitivity_df["نسبة الربح المعدلة"] = sensitivity_df["نسبة الربح المعدلة"].apply(lambda x: f"{x:.2f}%") | |
| # عرض جدول تحليل الحساسية | |
| st.dataframe(sensitivity_df, use_container_width=True) | |
| # عرض الرسم البياني لتحليل الحساسية | |
| chart_data = pd.DataFrame({ | |
| "تغير التكاليف": cost_changes, | |
| "نسبة الربح": [row["نسبة الربح المعدلة"].replace("%", "") for row in sensitivity_data] | |
| }) | |
| chart_data["نسبة الربح"] = chart_data["نسبة الربح"].astype(float) | |
| st.line_chart(chart_data.set_index("تغير التكاليف")) | |
| # عرض توصيات هامش الربح | |
| st.markdown("##### توصيات هامش الربح") | |
| recommendations = [ | |
| "الحفاظ على هامش ربح لا يقل عن 10% لضمان تغطية المخاطر غير المتوقعة", | |
| "مراجعة أسعار المواد الرئيسية قبل تقديم العرض النهائي", | |
| "التفاوض مع الموردين للحصول على خصومات إضافية", | |
| "تقليل التكاليف غير المباشرة من خلال مشاركة الموارد مع مشاريع أخرى" | |
| ] | |
| for recommendation in recommendations: | |
| st.markdown(f"- {recommendation}") | |
| def _render_risk_analysis(self, project_info): | |
| """ | |
| عرض تحليل المخاطر | |
| """ | |
| st.markdown("#### تحليل المخاطر") | |
| # إنشاء بيانات تجريبية للمخاطر | |
| risks = [ | |
| {"المخاطرة": "ارتفاع أسعار المواد", "الاحتمالية": 70, "التأثير": 80, "المستوى": "مرتفع", "الاستجابة": "تضمين بند تعديل الأسعار في العقد"}, | |
| {"المخاطرة": "تأخر التوريدات", "الاحتمالية": 60, "التأثير": 70, "المستوى": "مرتفع", "الاستجابة": "طلب توريدات مبكرة وتخزين المواد الأساسية"}, | |
| {"المخاطرة": "نقص العمالة", "الاحتمالية": 50, "التأثير": 60, "المستوى": "متوسط", "الاستجابة": "التعاقد المسبق مع مقاولي الباطن"}, | |
| {"المخاطرة": "ظروف جوية", "الاحتمالية": 40, "التأثير": 50, "المستوى": "متوسط", "الاستجابة": "تضمين مدة إضافية في الجدول الزمني"}, | |
| {"المخاطرة": "تغيير المواصفات", "الاحتمالية": 30, "التأثير": 80, "المستوى": "متوسط", "الاستجابة": "تضمين بند تغيير الأوامر في العقد"}, | |
| {"المخاطرة": "مشاكل تمويلية", "الاحتمالية": 20, "التأثير": 90, "المستوى": "متوسط", "الاستجابة": "تأمين خط ائتمان احتياطي"} | |
| ] | |
| # تحويل البيانات إلى DataFrame | |
| risks_df = pd.DataFrame(risks) | |
| # عرض جدول المخاطر | |
| st.dataframe(risks_df, use_container_width=True) | |
| # عرض مصفوفة المخاطر | |
| st.markdown("##### مصفوفة المخاطر") | |
| # إنشاء بيانات مصفوفة المخاطر | |
| risk_matrix = np.zeros((5, 5)) | |
| # تعيين قيم المخاطر في المصفوفة | |
| for risk in risks: | |
| prob_index = min(int(risk["الاحتمالية"] / 20), 4) | |
| impact_index = min(int(risk["التأثير"] / 20), 4) | |
| risk_matrix[prob_index, impact_index] += 1 | |
| # تحويل المصفوفة إلى DataFrame | |
| prob_labels = ["0-20%", "21-40%", "41-60%", "61-80%", "81-100%"] | |
| impact_labels = ["منخفض جداً", "منخفض", "متوسط", "مرتفع", "مرتفع جداً"] | |
| matrix_df = pd.DataFrame(risk_matrix, index=prob_labels[::-1], columns=impact_labels) | |
| # عرض المصفوفة كخريطة حرارية | |
| st.markdown("الاحتمالية (عمودي) × التأثير (أفقي)") | |
| st.dataframe(matrix_df, use_container_width=True) | |
| # عرض تكلفة المخاطر | |
| st.markdown("##### تكلفة المخاطر") | |
| # حساب تكلفة المخاطر | |
| risk_cost = project_info['value'] * 0.05 | |
| contingency = project_info['value'] * 0.03 | |
| management_reserve = project_info['value'] * 0.02 | |
| col1, col2, col3 = st.columns(3) | |
| with col1: | |
| st.metric("تكلفة المخاطر المحددة", f"{risk_cost:,.2f} ريال") | |
| with col2: | |
| st.metric("احتياطي الطوارئ", f"{contingency:,.2f} ريال") | |
| with col3: | |
| st.metric("احتياطي الإدارة", f"{management_reserve:,.2f} ريال") | |
| # عرض خطة الاستجابة للمخاطر | |
| st.markdown("##### خطة الاستجابة للمخاطر") | |
| for risk in risks: | |
| if risk["المستوى"] == "مرتفع": | |
| st.markdown(f""" | |
| <div style="background-color: #f8d7da; padding: 10px; border-radius: 5px; margin-bottom: 10px;"> | |
| <strong>{risk['المخاطرة']} (مخاطرة مرتفعة):</strong> {risk['الاستجابة']} | |
| </div> | |
| """, unsafe_allow_html=True) | |
| elif risk["المستوى"] == "متوسط": | |
| st.markdown(f""" | |
| <div style="background-color: #fff3cd; padding: 10px; border-radius: 5px; margin-bottom: 10px;"> | |
| <strong>{risk['المخاطرة']} (مخاطرة متوسطة):</strong> {risk['الاستجابة']} | |
| </div> | |
| """, unsafe_allow_html=True) | |
| def _render_bill_of_quantities(self): | |
| """ | |
| عرض واجهة جداول الكميات | |
| """ | |
| st.markdown("### جداول الكميات") | |
| # تبويبات فرعية لجداول الكميات | |
| boq_tabs = st.tabs(["إنشاء جدول كميات", "قوالب جداول الكميات", "استيراد/تصدير"]) | |
| with boq_tabs[0]: | |
| # إنشاء جدول كميات جديد | |
| st.markdown("#### إنشاء جدول كميات جديد") | |
| # نموذج إنشاء جدول كميات | |
| col1, col2 = st.columns(2) | |
| with col1: | |
| boq_name = st.text_input("اسم جدول الكميات", key="new_boq_name") | |
| boq_project = st.selectbox("المشروع", options=[p['name'] for p in st.session_state.pricing_projects], key="new_boq_project") | |
| with col2: | |
| boq_template = st.selectbox("القالب", options=["قالب جديد"] + [t['name'] for t in st.session_state.pricing_templates], key="new_boq_template") | |
| boq_currency = st.selectbox("العملة", options=["ريال سعودي", "دولار أمريكي", "يورو"], key="new_boq_currency") | |
| # زر إنشاء جدول الكميات | |
| if st.button("إنشاء جدول الكميات", key="create_boq_btn"): | |
| if boq_name: | |
| st.success(f"تم إنشاء جدول الكميات '{boq_name}' بنجاح", icon="✅") | |
| # عرض جدول الكميات الفارغ | |
| st.markdown("#### جدول الكميات الجديد") | |
| # إنشاء جدول فارغ | |
| empty_boq = pd.DataFrame({ | |
| "الرقم": ["1", "2", "3", "4", "5"], | |
| "البند": ["", "", "", "", ""], | |
| "الوصف": ["", "", "", "", ""], | |
| "الوحدة": ["", "", "", "", ""], | |
| "الكمية": ["", "", "", "", ""], | |
| "سعر الوحدة": ["", "", "", "", ""], | |
| "الإجمالي": ["", "", "", "", ""] | |
| }) | |
| st.dataframe(empty_boq, use_container_width=True, hide_index=True) | |
| # أزرار التحكم | |
| col1, col2, col3 = st.columns(3) | |
| with col1: | |
| st.button("إضافة بند", key="add_item_to_new_boq_btn") | |
| with col2: | |
| st.button("إضافة فئة", key="add_category_to_new_boq_btn") | |
| with col3: | |
| st.button("حفظ", key="save_new_boq_btn") | |
| else: | |
| st.warning("يرجى إدخال اسم لجدول الكميات", icon="⚠️") | |
| with boq_tabs[1]: | |
| # قوالب جداول الكميات | |
| st.markdown("#### قوالب جداول الكميات") | |
| # عرض القوالب المتاحة | |
| templates = st.session_state.pricing_templates | |
| # تقسيم القوالب إلى صفوف | |
| for i in range(0, len(templates), 3): | |
| cols = st.columns(3) | |
| for j in range(3): | |
| if i + j < len(templates): | |
| template = templates[i + j] | |
| with cols[j]: | |
| st.markdown(f""" | |
| <div style="border: 1px solid #dee2e6; border-radius: 8px; padding: 15px; height: 100%;"> | |
| <h4 style="color: var(--primary-color);">{template['name']}</h4> | |
| <p><strong>النوع:</strong> {template['type']}</p> | |
| <p><strong>عدد البنود:</strong> {template['items_count']}</p> | |
| <p>{template['description']}</p> | |
| </div> | |
| """, unsafe_allow_html=True) | |
| # إضافة قالب جديد | |
| st.markdown("#### إضافة قالب جديد") | |
| col1, col2 = st.columns(2) | |
| with col1: | |
| new_template_name = st.text_input("اسم القالب", key="new_template_name") | |
| new_template_type = st.selectbox("نوع القالب", options=["طرق", "مباني", "بنية تحتية", "مياه وصرف صحي", "كهرباء", "أخرى"], key="new_template_type") | |
| with col2: | |
| new_template_desc = st.text_area("وصف القالب", key="new_template_desc") | |
| # زر إضافة القالب | |
| if st.button("إضافة القالب", key="add_template_btn"): | |
| if new_template_name: | |
| st.success(f"تم إضافة القالب '{new_template_name}' بنجاح", icon="✅") | |
| else: | |
| st.warning("يرجى إدخال اسم للقالب", icon="⚠️") | |
| with boq_tabs[2]: | |
| # استيراد/تصدير جداول الكميات | |
| st.markdown("#### استيراد/تصدير جداول الكميات") | |
| col1, col2 = st.columns(2) | |
| with col1: | |
| st.markdown("##### استيراد جدول كميات") | |
| st.file_uploader("اختر ملف جدول الكميات (Excel, CSV)", type=["xlsx", "csv"], key="import_boq_file") | |
| import_format = st.radio( | |
| "تنسيق الاستيراد", | |
| options=["تنسيق النظام", "تنسيق وزارة المالية", "تنسيق مخصص"], | |
| horizontal=True, | |
| key="import_boq_format" | |
| ) | |
| st.button("استيراد", key="import_boq_file_btn") | |
| with col2: | |
| st.markdown("##### تصدير جدول كميات") | |
| export_boq = st.selectbox( | |
| "اختر جدول الكميات", | |
| options=[p['name'] for p in st.session_state.pricing_projects], | |
| key="export_boq_select" | |
| ) | |
| export_format = st.radio( | |
| "تنسيق التصدير", | |
| options=["Excel", "CSV", "PDF"], | |
| horizontal=True, | |
| key="export_boq_format" | |
| ) | |
| if st.button("تصدير", key="export_boq_btn"): | |
| st.success(f"تم تصدير جدول الكميات '{export_boq}' بنجاح", icon="✅") | |
| # زر تنزيل الملف المصدر (وهمي) | |
| st.download_button( | |
| label="تنزيل جدول الكميات", | |
| data="محتوى وهمي لجدول الكميات", | |
| file_name=f"{export_boq}.{export_format.lower()}", | |
| mime="application/octet-stream", | |
| key="download_boq" | |
| ) | |
| def _render_price_analysis(self): | |
| """ | |
| عرض واجهة تحليل الأسعار | |
| """ | |
| st.markdown("### تحليل الأسعار") | |
| # تبويبات فرعية لتحليل الأسعار | |
| price_tabs = st.tabs(["تحليل أسعار المواد", "مقارنة الأسعار", "تحليل الاتجاهات", "تقارير"]) | |
| with price_tabs[0]: | |
| # تحليل أسعار المواد | |
| st.markdown("#### تحليل أسعار المواد") | |
| # اختيار المواد للتحليل | |
| materials = ["خرسانة", "حديد تسليح", "أسمنت", "رمل", "بلاط", "طابوق", "أسلاك كهربائية", "أنابيب"] | |
| selected_materials = st.multiselect("اختر المواد للتحليل", options=materials, default=materials[:3], key="selected_materials") | |
| if selected_materials: | |
| # إنشاء بيانات تجريبية لأسعار المواد | |
| price_data = {} | |
| months = ["يناير", "فبراير", "مارس", "أبريل", "مايو", "يونيو", "يوليو", "أغسطس", "سبتمبر", "أكتوبر", "نوفمبر", "ديسمبر"] | |
| for material in selected_materials: | |
| # إنشاء سلسلة أسعار عشوائية مع اتجاه تصاعدي | |
| base_price = random.randint(100, 1000) | |
| prices = [] | |
| for i in range(12): | |
| # إضافة تغير عشوائي مع اتجاه تصاعدي | |
| change = random.uniform(-0.05, 0.1) * base_price | |
| price = base_price + change | |
| base_price = price # تحديث السعر الأساسي للشهر التالي | |
| prices.append(price) | |
| price_data[material] = prices | |
| # تحويل البيانات إلى DataFrame | |
| price_df = pd.DataFrame(price_data, index=months) | |
| # عرض الرسم البياني | |
| st.line_chart(price_df) | |
| # عرض جدول الأسعار | |
| st.dataframe(price_df, use_container_width=True) | |
| # تحليل التغيرات | |
| st.markdown("##### تحليل التغيرات في الأسعار") | |
| for material in selected_materials: | |
| prices = price_data[material] | |
| first_price = prices[0] | |
| last_price = prices[-1] | |
| change = ((last_price - first_price) / first_price) * 100 | |
| if change > 0: | |
| st.markdown(f"- **{material}**: ارتفاع بنسبة {change:.2f}% خلال الفترة") | |
| else: | |
| st.markdown(f"- **{material}**: انخفاض بنسبة {abs(change):.2f}% خلال الفترة") | |
| with price_tabs[1]: | |
| # مقارنة الأسعار | |
| st.markdown("#### مقارنة الأسعار بين الموردين") | |
| # اختيار المادة للمقارنة | |
| material_for_comparison = st.selectbox("اختر المادة للمقارنة", options=materials, key="material_for_comparison") | |
| # إنشاء بيانات تجريبية للموردين | |
| suppliers = ["المورد أ", "المورد ب", "المورد ج", "المورد د", "المورد هـ"] | |
| # إنشاء أسعار عشوائية للموردين | |
| base_price = random.randint(100, 1000) | |
| supplier_prices = [base_price * random.uniform(0.9, 1.1) for _ in range(len(suppliers))] | |
| # تحويل البيانات إلى DataFrame | |
| comparison_df = pd.DataFrame({ | |
| "المورد": suppliers, | |
| "السعر": supplier_prices, | |
| "الخصم": [f"{random.randint(0, 15)}%" for _ in range(len(suppliers))], | |
| "شروط الدفع": [random.choice(["فوري", "30 يوم", "60 يوم", "90 يوم"]) for _ in range(len(suppliers))], | |
| "وقت التسليم": [f"{random.randint(1, 30)} يوم" for _ in range(len(suppliers))] | |
| }) | |
| # تنسيق القيم | |
| comparison_df["السعر"] = comparison_df["السعر"].apply(lambda x: f"{x:.2f}") | |
| # عرض جدول المقارنة | |
| st.dataframe(comparison_df, use_container_width=True) | |
| # عرض الرسم البياني للمقارنة | |
| chart_data = pd.DataFrame({ | |
| "المورد": suppliers, | |
| "السعر": supplier_prices | |
| }) | |
| st.bar_chart(chart_data.set_index("المورد")) | |
| # توصيات الشراء | |
| st.markdown("##### توصيات الشراء") | |
| best_supplier_index = supplier_prices.index(min(supplier_prices)) | |
| best_supplier = suppliers[best_supplier_index] | |
| st.markdown(f""" | |
| <div style="background-color: #d4edda; padding: 15px; border-radius: 8px; margin-top: 20px;"> | |
| <h5 style="color: #155724;">التوصية الأفضل</h5> | |
| <p>بناءً على تحليل الأسعار وشروط التوريد، يوصى بالشراء من <strong>{best_supplier}</strong> حيث يقدم أفضل سعر مع شروط دفع وتسليم مناسبة.</p> | |
| </div> | |
| """, unsafe_allow_html=True) | |
| with price_tabs[2]: | |
| # تحليل الاتجاهات | |
| st.markdown("#### تحليل اتجاهات الأسعار") | |
| # اختيار الفترة الزمنية | |
| time_period = st.radio( | |
| "الفترة الزمنية", | |
| options=["شهري", "ربع سنوي", "سنوي"], | |
| horizontal=True, | |
| key="price_trend_period" | |
| ) | |
| # إنشاء بيانات تجريبية للاتجاهات | |
| if time_period == "شهري": | |
| periods = months | |
| elif time_period == "ربع سنوي": | |
| periods = ["الربع الأول", "الربع الثاني", "الربع الثالث", "الربع الرابع"] | |
| else: | |
| periods = [f"20{year}" for year in range(18, 26)] | |
| # إنشاء مؤشر أسعار تجريبي | |
| base_index = 100 | |
| price_indices = [] | |
| for i in range(len(periods)): | |
| # إضافة تغير عشوائي مع اتجاه تصاعدي | |
| change = random.uniform(-2, 5) | |
| index_value = base_index + change | |
| base_index = index_value | |
| price_indices.append(index_value) | |
| # تحويل البيانات إلى DataFrame | |
| trend_df = pd.DataFrame({ | |
| "الفترة": periods, | |
| "مؤشر الأسعار": price_indices | |
| }) | |
| # عرض الرسم البياني للاتجاهات | |
| st.line_chart(trend_df.set_index("الفترة")) | |
| # تحليل الاتجاه | |
| first_index = price_indices[0] | |
| last_index = price_indices[-1] | |
| total_change = ((last_index - first_index) / first_index) * 100 | |
| st.markdown(f"##### تحليل الاتجاه العام") | |
| if total_change > 10: | |
| trend_message = "ارتفاع حاد في الأسعار" | |
| recommendation = "يوصى بالشراء المبكر وتخزين المواد الأساسية لتجنب الزيادات المستقبلية" | |
| elif total_change > 5: | |
| trend_message = "ارتفاع معتدل في الأسعار" | |
| recommendation = "يوصى بمراقبة الأسعار عن كثب والشراء عند انخفاض الأسعار مؤقتاً" | |
| elif total_change > 0: | |
| trend_message = "ارتفاع طفيف في الأسعار" | |
| recommendation = "الوضع مستقر نسبياً، يمكن الشراء وفق الجدول الزمني للمشروع" | |
| elif total_change > -5: | |
| trend_message = "انخفاض طفيف في الأسعار" | |
| recommendation = "يمكن تأجيل بعض المشتريات للاستفادة من انخفاض الأسعار المتوقع" | |
| else: | |
| trend_message = "انخفاض ملحوظ في الأسعار" | |
| recommendation = "يوصى بتأجيل المشتريات غير العاجلة للاستفادة من انخفاض الأسعار" | |
| st.markdown(f""" | |
| <div style="background-color: #f8f9fa; padding: 15px; border-radius: 8px; margin-top: 20px;"> | |
| <h5>الاتجاه العام: {trend_message}</h5> | |
| <p>التغير الإجمالي: {total_change:.2f}% خلال الفترة</p> | |
| <p><strong>التوصية:</strong> {recommendation}</p> | |
| </div> | |
| """, unsafe_allow_html=True) | |
| with price_tabs[3]: | |
| # تقارير تحليل الأسعار | |
| st.markdown("#### تقارير تحليل الأسعار") | |
| # قائمة التقارير المتاحة | |
| reports = [ | |
| "تقرير مقارنة الأسعار الشهري", | |
| "تقرير تحليل اتجاهات الأسعار", | |
| "تقرير أسعار المواد الرئيسية", | |
| "تقرير مقارنة أسعار الموردين", | |
| "تقرير تحليل تكاليف المشاريع" | |
| ] | |
| selected_report = st.selectbox("اختر التقرير", options=reports, key="selected_price_report") | |
| # خيارات التقرير | |
| col1, col2 = st.columns(2) | |
| with col1: | |
| report_format = st.radio( | |
| "تنسيق التقرير", | |
| options=["PDF", "Excel", "Word"], | |
| horizontal=True, | |
| key="price_report_format" | |
| ) | |
| with col2: | |
| report_period = st.selectbox( | |
| "الفترة", | |
| options=["الشهر الحالي", "الربع الحالي", "النصف الأول 2025", "النصف الثاني 2025", "السنة كاملة"], | |
| key="price_report_period" | |
| ) | |
| # زر إنشاء التقرير | |
| if st.button("إنشاء التقرير", key="generate_price_report_btn"): | |
| st.success(f"تم إنشاء التقرير '{selected_report}' بنجاح", icon="✅") | |
| # زر تنزيل التقرير (وهمي) | |
| st.download_button( | |
| label="تنزيل التقرير", | |
| data="محتوى وهمي للتقرير", | |
| file_name=f"{selected_report}.{report_format.lower()}", | |
| mime="application/octet-stream", | |
| key="download_price_report" | |
| ) | |
| def _render_pricing_templates(self): | |
| """ | |
| عرض واجهة قوالب التسعير | |
| """ | |
| st.markdown("### قوالب التسعير") | |
| # تبويبات فرعية لقوالب التسعير | |
| template_tabs = st.tabs(["القوالب المتاحة", "إنشاء قالب جديد", "إدارة القوالب"]) | |
| with template_tabs[0]: | |
| # عرض القوالب المتاحة | |
| st.markdown("#### القوالب المتاحة") | |
| # فلترة القوالب | |
| template_type_filter = st.selectbox( | |
| "تصفية حسب النوع", | |
| options=["الكل", "طرق", "مباني", "بنية تحتية", "مياه وصرف صحي", "كهرباء", "أخرى"], | |
| key="template_type_filter" | |
| ) | |
| # تطبيق التصفية | |
| filtered_templates = st.session_state.pricing_templates | |
| if template_type_filter != "الكل": | |
| filtered_templates = [t for t in filtered_templates if t['type'] == template_type_filter] | |
| # عرض القوالب | |
| if filtered_templates: | |
| # تقسيم القوالب إلى صفوف | |
| for i in range(0, len(filtered_templates), 3): | |
| cols = st.columns(3) | |
| for j in range(3): | |
| if i + j < len(filtered_templates): | |
| template = filtered_templates[i + j] | |
| with cols[j]: | |
| st.markdown(f""" | |
| <div style="border: 1px solid #dee2e6; border-radius: 8px; padding: 15px; height: 100%;"> | |
| <h4 style="color: var(--primary-color);">{template['name']}</h4> | |
| <p><strong>النوع:</strong> {template['type']}</p> | |
| <p><strong>عدد البنود:</strong> {template['items_count']}</p> | |
| <p>{template['description']}</p> | |
| <button style="background-color: var(--primary-color); color: white; border: none; padding: 5px 10px; border-radius: 5px; cursor: pointer;">استخدام القالب</button> | |
| </div> | |
| """, unsafe_allow_html=True) | |
| else: | |
| st.info("لا توجد قوالب تطابق معايير التصفية", icon="ℹ️") | |
| with template_tabs[1]: | |
| # إنشاء قالب جديد | |
| st.markdown("#### إنشاء قالب جديد") | |
| # نموذج إنشاء قالب | |
| col1, col2 = st.columns(2) | |
| with col1: | |
| new_template_name = st.text_input("اسم القالب", key="create_template_name") | |
| new_template_type = st.selectbox("نوع القالب", options=["طرق", "مباني", "بنية تحتية", "مياه وصرف صحي", "كهرباء", "أخرى"], key="create_template_type") | |
| with col2: | |
| new_template_desc = st.text_area("وصف القالب", key="create_template_desc") | |
| new_template_base = st.selectbox("القالب الأساسي", options=["قالب فارغ"] + [t['name'] for t in st.session_state.pricing_templates], key="create_template_base") | |
| # زر إنشاء القالب | |
| if st.button("إنشاء القالب", key="create_template_btn"): | |
| if new_template_name: | |
| st.success(f"تم إنشاء القالب '{new_template_name}' بنجاح", icon="✅") | |
| # عرض محرر القالب | |
| st.markdown("#### محرر القالب") | |
| # إنشاء جدول فارغ | |
| empty_template = pd.DataFrame({ | |
| "الرقم": ["1", "2", "3", "4", "5"], | |
| "البند": ["", "", "", "", ""], | |
| "الوصف": ["", "", "", "", ""], | |
| "الوحدة": ["", "", "", "", ""], | |
| "ملاحظات": ["", "", "", "", ""] | |
| }) | |
| st.dataframe(empty_template, use_container_width=True, hide_index=True) | |
| # أزرار التحكم | |
| col1, col2, col3 = st.columns(3) | |
| with col1: | |
| st.button("إضافة بند", key="add_item_to_template_btn") | |
| with col2: | |
| st.button("إضافة فئة", key="add_category_to_template_btn") | |
| with col3: | |
| st.button("حفظ القالب", key="save_template_btn") | |
| else: | |
| st.warning("يرجى إدخال اسم للقالب", icon="⚠️") | |
| with template_tabs[2]: | |
| # إدارة القوالب | |
| st.markdown("#### إدارة القوالب") | |
| # عرض قائمة القوالب | |
| templates_df = pd.DataFrame({ | |
| "اسم القالب": [t['name'] for t in st.session_state.pricing_templates], | |
| "النوع": [t['type'] for t in st.session_state.pricing_templates], | |
| "عدد البنود": [t['items_count'] for t in st.session_state.pricing_templates], | |
| "تاريخ الإنشاء": [t['created_at'] for t in st.session_state.pricing_templates], | |
| "آخر تحديث": [t['updated_at'] for t in st.session_state.pricing_templates] | |
| }) | |
| st.dataframe(templates_df, use_container_width=True) | |
| # خيارات إدارة القوالب | |
| st.markdown("##### خيارات الإدارة") | |
| col1, col2, col3 = st.columns(3) | |
| with col1: | |
| st.button("تعديل القالب المحدد", key="edit_template_btn") | |
| with col2: | |
| st.button("نسخ القالب المحدد", key="duplicate_template_btn") | |
| with col3: | |
| st.button("حذف القالب المحدد", key="delete_template_btn") | |
| # استيراد وتصدير القوالب | |
| st.markdown("##### استيراد وتصدير القوالب") | |
| col1, col2 = st.columns(2) | |
| with col1: | |
| st.file_uploader("استيراد قالب", type=["xlsx", "json"], key="import_template_file") | |
| st.button("استيراد", key="import_template_btn") | |
| with col2: | |
| export_template = st.selectbox( | |
| "تصدير قالب", | |
| options=[t['name'] for t in st.session_state.pricing_templates], | |
| key="export_template_select" | |
| ) | |
| export_template_format = st.radio( | |
| "تنسيق التصدير", | |
| options=["Excel", "JSON"], | |
| horizontal=True, | |
| key="export_template_format" | |
| ) | |
| if st.button("تصدير", key="export_template_btn"): | |
| st.success(f"تم تصدير القالب '{export_template}' بنجاح", icon="✅") | |
| # زر تنزيل القالب المصدر (وهمي) | |
| st.download_button( | |
| label="تنزيل القالب", | |
| data="محتوى وهمي للقالب", | |
| file_name=f"{export_template}.{export_template_format.lower()}", | |
| mime="application/octet-stream", | |
| key="download_template" | |
| ) | |
| def _generate_sample_projects(self): | |
| """ | |
| إنشاء بيانات تجريبية للمشاريع | |
| """ | |
| projects = [ | |
| { | |
| 'id': 1, | |
| 'name': 'مشروع تطوير الطرق في منطقة الرياض', | |
| 'client': 'وزارة النقل', | |
| 'value': 25000000, | |
| 'deadline': '2025-05-15', | |
| 'status': 'قيد التسعير', | |
| 'completion': 75 | |
| }, | |
| { | |
| 'id': 2, | |
| 'name': 'مشروع إنشاء مبنى إداري', | |
| 'client': 'وزارة الإسكان', | |
| 'value': 15000000, | |
| 'deadline': '2025-04-30', | |
| 'status': 'قيد التسعير', | |
| 'completion': 90 | |
| }, | |
| { | |
| 'id': 3, | |
| 'name': 'مشروع تطوير شبكة الصرف الصحي', | |
| 'client': 'أمانة منطقة الرياض', | |
| 'value': 18000000, | |
| 'deadline': '2025-05-10', | |
| 'status': 'تم التسعير', | |
| 'completion': 100 | |
| }, | |
| { | |
| 'id': 4, | |
| 'name': 'مشروع إنشاء جسر', | |
| 'client': 'وزارة النقل', | |
| 'value': 30000000, | |
| 'deadline': '2025-06-20', | |
| 'status': 'قيد التسعير', | |
| 'completion': 40 | |
| }, | |
| { | |
| 'id': 5, | |
| 'name': 'مشروع تطوير شبكة المياه', | |
| 'client': 'وزارة المياه', | |
| 'value': 12000000, | |
| 'deadline': '2025-04-25', | |
| 'status': 'تم التسعير', | |
| 'completion': 100 | |
| }, | |
| { | |
| 'id': 6, | |
| 'name': 'مشروع إنشاء مدرسة', | |
| 'client': 'وزارة التعليم', | |
| 'value': 8000000, | |
| 'deadline': '2025-05-05', | |
| 'status': 'تمت الترسية', | |
| 'completion': 100 | |
| }, | |
| { | |
| 'id': 7, | |
| 'name': 'مشروع تطوير شبكة الكهرباء', | |
| 'client': 'شركة الكهرباء السعودية', | |
| 'value': 20000000, | |
| 'deadline': '2025-06-10', | |
| 'status': 'مرفوضة', | |
| 'completion': 100 | |
| }, | |
| { | |
| 'id': 8, | |
| 'name': 'مشروع إنشاء مستشفى', | |
| 'client': 'وزارة الصحة', | |
| 'value': 40000000, | |
| 'deadline': '2025-07-15', | |
| 'status': 'قيد التسعير', | |
| 'completion': 30 | |
| }, | |
| { | |
| 'id': 9, | |
| 'name': 'مشروع تطوير حديقة عامة', | |
| 'client': 'أمانة منطقة الرياض', | |
| 'value': 5000000, | |
| 'deadline': '2025-04-20', | |
| 'status': 'تمت الترسية', | |
| 'completion': 100 | |
| }, | |
| { | |
| 'id': 10, | |
| 'name': 'مشروع إنشاء مركز تجاري', | |
| 'client': 'شركة تطوير العقارية', | |
| 'value': 35000000, | |
| 'deadline': '2025-08-10', | |
| 'status': 'قيد التسعير', | |
| 'completion': 20 | |
| } | |
| ] | |
| return projects | |
| def _generate_sample_templates(self): | |
| """ | |
| إنشاء بيانات تجريبية لقوالب التسعير | |
| """ | |
| templates = [ | |
| { | |
| 'id': 1, | |
| 'name': 'قالب مشاريع الطرق', | |
| 'type': 'طرق', | |
| 'items_count': 120, | |
| 'description': 'قالب شامل لمشاريع الطرق والجسور يتضمن جميع البنود القياسية', | |
| 'created_at': '2024-10-15', | |
| 'updated_at': '2025-01-20' | |
| }, | |
| { | |
| 'id': 2, | |
| 'name': 'قالب المباني الإدارية', | |
| 'type': 'مباني', | |
| 'items_count': 150, | |
| 'description': 'قالب متكامل للمباني الإدارية والتجارية مع تفاصيل التشطيبات', | |
| 'created_at': '2024-11-05', | |
| 'updated_at': '2025-02-10' | |
| }, | |
| { | |
| 'id': 3, | |
| 'name': 'قالب شبكات المياه', | |
| 'type': 'مياه وصرف صحي', | |
| 'items_count': 85, | |
| 'description': 'قالب لمشاريع شبكات المياه والصرف الصحي مع المواصفات القياسية', | |
| 'created_at': '2024-09-20', | |
| 'updated_at': '2025-01-15' | |
| }, | |
| { | |
| 'id': 4, | |
| 'name': 'قالب الأعمال الكهربائية', | |
| 'type': 'كهرباء', | |
| 'items_count': 95, | |
| 'description': 'قالب للأعمال الكهربائية في المشاريع المختلفة', | |
| 'created_at': '2024-12-10', | |
| 'updated_at': '2025-03-05' | |
| }, | |
| { | |
| 'id': 5, | |
| 'name': 'قالب البنية التحتية', | |
| 'type': 'بنية تحتية', | |
| 'items_count': 110, | |
| 'description': 'قالب شامل لمشاريع البنية التحتية والمرافق العامة', | |
| 'created_at': '2024-10-25', | |
| 'updated_at': '2025-02-15' | |
| }, | |
| { | |
| 'id': 6, | |
| 'name': 'قالب المدارس', | |
| 'type': 'مباني', | |
| 'items_count': 130, | |
| 'description': 'قالب متخصص لمشاريع المدارس والمنشآت التعليمية', | |
| 'created_at': '2024-11-15', | |
| 'updated_at': '2025-01-25' | |
| } | |
| ] | |
| return templates | |
| def _generate_sample_resources(self): | |
| """ | |
| إنشاء بيانات تجريبية للموارد | |
| """ | |
| resources = { | |
| 'materials': [ | |
| {'id': 1, 'name': 'خرسانة جاهزة', 'unit': 'متر مكعب', 'price': 250, 'last_update': '2025-03-15'}, | |
| {'id': 2, 'name': 'حديد تسليح', 'unit': 'طن', 'price': 3000, 'last_update': '2025-03-10'}, | |
| {'id': 3, 'name': 'طابوق', 'unit': 'قطعة', 'price': 5, 'last_update': '2025-03-05'}, | |
| {'id': 4, 'name': 'بلاط', 'unit': 'متر مربع', 'price': 80, 'last_update': '2025-03-12'}, | |
| {'id': 5, 'name': 'أسمنت', 'unit': 'كيس', 'price': 20, 'last_update': '2025-03-08'} | |
| ], | |
| 'labor': [ | |
| {'id': 1, 'name': 'مهندس موقع', 'unit': 'شهر', 'price': 15000, 'last_update': '2025-02-20'}, | |
| {'id': 2, 'name': 'مشرف', 'unit': 'شهر', 'price': 8000, 'last_update': '2025-02-20'}, | |
| {'id': 3, 'name': 'فني', 'unit': 'شهر', 'price': 5000, 'last_update': '2025-02-20'}, | |
| {'id': 4, 'name': 'عامل', 'unit': 'شهر', 'price': 3000, 'last_update': '2025-02-20'}, | |
| {'id': 5, 'name': 'سائق', 'unit': 'شهر', 'price': 4000, 'last_update': '2025-02-20'} | |
| ], | |
| 'equipment': [ | |
| {'id': 1, 'name': 'حفارة', 'unit': 'شهر', 'price': 20000, 'last_update': '2025-02-15'}, | |
| {'id': 2, 'name': 'لودر', 'unit': 'شهر', 'price': 15000, 'last_update': '2025-02-15'}, | |
| {'id': 3, 'name': 'شاحنة نقل', 'unit': 'شهر', 'price': 10000, 'last_update': '2025-02-15'}, | |
| {'id': 4, 'name': 'خلاطة خرسانة', 'unit': 'شهر', 'price': 8000, 'last_update': '2025-02-15'}, | |
| {'id': 5, 'name': 'رافعة', 'unit': 'شهر', 'price': 25000, 'last_update': '2025-02-15'} | |
| ] | |
| } | |
| return resources | |