diff --git "a/modules/pricing/pricing_app.py" "b/modules/pricing/pricing_app.py"
--- "a/modules/pricing/pricing_app.py"
+++ "b/modules/pricing/pricing_app.py"
@@ -1,1366 +1,937 @@
-"""
-وحدة التسعير المتكاملة
-"""
-
import streamlit as st
import pandas as pd
import numpy as np
-import random
-from datetime import datetime
-import time
+import io
+import base64
+import datetime
+import json
+import os
+from PIL import Image
+import matplotlib.pyplot as plt
+import seaborn as sns
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 render(self):
- """
- طريقة للتوافق مع الواجهة القديمة
- تقوم باستدعاء طريقة run
- """
- self.run()
-
- def run(self):
- """
- تشغيل وحدة التسعير
- """
- st.markdown("
وحدة التسعير المتكاملة
", unsafe_allow_html=True)
-
- # إضافة زر إنشاء تسعير جديد
- if st.button("إنشاء تسعير جديد", key="create_new_pricing_btn", type="primary"):
- st.session_state.show_new_pricing_form = True
-
- # عرض نموذج إنشاء تسعير جديد
- if 'show_new_pricing_form' not in st.session_state:
- st.session_state.show_new_pricing_form = False
+ تهيئة وحدة التسعير المحسنة
+ """
+ self.initialize_session_state()
+ self.set_page_config()
+
+ def initialize_session_state(self):
+ """
+ تهيئة متغيرات حالة الجلسة
+ """
+ if 'projects' not in st.session_state:
+ st.session_state.projects = [
+ {
+ "id": 1,
+ "name": "مشروع تطوير البنية التحتية",
+ "client": "وزارة الإسكان",
+ "estimated_value": 5000000,
+ "deadline": "2023-12-31",
+ "status": "قيد التنفيذ",
+ "completion": 35,
+ "profit_margin": 15
+ },
+ {
+ "id": 2,
+ "name": "مشروع إنشاء مجمع سكني",
+ "client": "شركة الإعمار",
+ "estimated_value": 8500000,
+ "deadline": "2024-06-30",
+ "status": "قيد التخطيط",
+ "completion": 0,
+ "profit_margin": 18
+ }
+ ]
- if st.session_state.show_new_pricing_form:
- st.markdown("### إنشاء تسعير جديد")
+ if 'current_project' not in st.session_state:
+ st.session_state.current_project = None
+
+ if 'bill_of_quantities' not in st.session_state:
+ st.session_state.bill_of_quantities = [
+ {
+ "project_id": 1,
+ "items": [
+ {"id": 1, "description": "حفر أساسات", "unit": "م³", "quantity": 250, "unit_price": 120, "total_price": 30000},
+ {"id": 2, "description": "صب خرسانة", "unit": "م³", "quantity": 180, "unit_price": 350, "total_price": 63000},
+ {"id": 3, "description": "توريد وتركيب حديد تسليح", "unit": "طن", "quantity": 25, "unit_price": 5000, "total_price": 125000}
+ ]
+ },
+ {
+ "project_id": 2,
+ "items": [
+ {"id": 1, "description": "أعمال حفر", "unit": "م³", "quantity": 500, "unit_price": 100, "total_price": 50000},
+ {"id": 2, "description": "أعمال خرسانة", "unit": "م³", "quantity": 300, "unit_price": 400, "total_price": 120000}
+ ]
+ }
+ ]
- col1, col2 = st.columns(2)
+ if 'materials_catalog' not in st.session_state:
+ st.session_state.materials_catalog = [
+ {"id": 1, "name": "أسمنت بورتلاندي", "unit": "طن", "price": 400, "category": "مواد بناء أساسية"},
+ {"id": 2, "name": "حديد تسليح 8 مم", "unit": "طن", "price": 4800, "category": "حديد تسليح"},
+ {"id": 3, "name": "حديد تسليح 10 مم", "unit": "طن", "price": 4700, "category": "حديد تسليح"},
+ {"id": 4, "name": "حديد تسليح 12 مم", "unit": "طن", "price": 4600, "category": "حديد تسليح"},
+ {"id": 5, "name": "رمل", "unit": "م³", "price": 80, "category": "مواد بناء أساسية"},
+ {"id": 6, "name": "زلط", "unit": "م³", "price": 150, "category": "مواد بناء أساسية"},
+ {"id": 7, "name": "طوب أحمر", "unit": "1000 طوبة", "price": 800, "category": "مواد بناء أساسية"},
+ {"id": 8, "name": "طوب أسمنتي", "unit": "1000 طوبة", "price": 1200, "category": "مواد بناء أساسية"},
+ {"id": 9, "name": "خشب", "unit": "م³", "price": 3500, "category": "مواد نجارة"},
+ {"id": 10, "name": "دهان بلاستيك", "unit": "برميل", "price": 500, "category": "دهانات"}
+ ]
- with col1:
- new_project_name = st.text_input("اسم المشروع", key="new_project_name")
- new_project_client = st.text_input("العميل", key="new_project_client")
+ if 'equipment_catalog' not in st.session_state:
+ st.session_state.equipment_catalog = [
+ {"id": 1, "name": "حفار", "daily_rate": 1500, "category": "معدات حفر"},
+ {"id": 2, "name": "لودر", "daily_rate": 1200, "category": "معدات حفر"},
+ {"id": 3, "name": "خلاطة خرسانة", "daily_rate": 800, "category": "معدات خرسانة"},
+ {"id": 4, "name": "هزاز خرسانة", "daily_rate": 200, "category": "معدات خرسانة"},
+ {"id": 5, "name": "شاحنة نقل", "daily_rate": 1000, "category": "معدات نقل"},
+ {"id": 6, "name": "رافعة برجية", "daily_rate": 2500, "category": "معدات رفع"},
+ {"id": 7, "name": "مضخة خرسانة", "daily_rate": 1800, "category": "معدات خرسانة"},
+ {"id": 8, "name": "مولد كهرباء", "daily_rate": 600, "category": "معدات كهربائية"},
+ {"id": 9, "name": "كمبروسر هواء", "daily_rate": 400, "category": "معدات متنوعة"},
+ {"id": 10, "name": "معدات يدوية", "daily_rate": 200, "category": "معدات متنوعة"}
+ ]
- with col2:
- new_project_value = st.number_input("القيمة التقديرية", min_value=0, value=1000000, step=100000, key="new_project_value")
- new_project_deadline = st.date_input("الموعد النهائي", key="new_project_deadline")
-
- if st.button("حفظ التسعير الجديد", key="save_new_pricing_btn"):
- if new_project_name and new_project_client:
- # إضافة المشروع الجديد إلى قائمة المشاريع
- new_project = {
- 'id': len(st.session_state.pricing_projects) + 1,
- 'name': new_project_name,
- 'client': new_project_client,
- 'value': new_project_value,
- 'deadline': new_project_deadline.strftime('%Y-%m-%d'),
- 'status': 'قيد التسعير',
- 'completion': 0
- }
-
- st.session_state.pricing_projects.append(new_project)
- st.success(f"تم إنشاء تسعير جديد للمشروع '{new_project_name}' بنجاح")
- st.session_state.show_new_pricing_form = False
- st.experimental_rerun()
- else:
- st.warning("يرجى إدخال اسم المشروع والعميل")
-
- if st.button("إلغاء", key="cancel_new_pricing_btn"):
- st.session_state.show_new_pricing_form = False
- st.experimental_rerun()
-
- # إنشاء تبويبات للتسعير المختلفة
- 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 = ['الرقم', 'اسم المناقصة', 'العميل', 'القيمة التقديرية', 'الموعد النهائي', 'الحالة', 'نسبة الإنجاز']
+ if 'labor_rates' not in st.session_state:
+ st.session_state.labor_rates = [
+ {"id": 1, "role": "عامل عادي", "daily_rate": 150, "category": "عمالة عادية"},
+ {"id": 2, "role": "نجار", "daily_rate": 250, "category": "عمالة ماهرة"},
+ {"id": 3, "role": "حداد", "daily_rate": 250, "category": "عمالة ماهرة"},
+ {"id": 4, "role": "مبيض محارة", "daily_rate": 230, "category": "عمالة ماهرة"},
+ {"id": 5, "role": "سباك", "daily_rate": 270, "category": "عمالة ماهرة"},
+ {"id": 6, "role": "كهربائي", "daily_rate": 280, "category": "عمالة ماهرة"},
+ {"id": 7, "role": "مشغل معدات", "daily_rate": 300, "category": "عمالة ماهرة"},
+ {"id": 8, "role": "مهندس موقع", "daily_rate": 600, "category": "مهندسين"},
+ {"id": 9, "role": "مهندس مدني", "daily_rate": 700, "category": "مهندسين"},
+ {"id": 10, "role": "مدير مشروع", "daily_rate": 1000, "category": "إدارة"}
+ ]
- # تنسيق القيم
- df['القيمة التقديرية'] = df['القيمة التقديرية'].apply(lambda x: f"{x:,} ريال")
- df['نسبة الإنجاز'] = df['نسبة الإنجاز'].apply(lambda x: f"{x}%")
+ if 'item_analysis' not in st.session_state:
+ st.session_state.item_analysis = [
+ {
+ "project_id": 1,
+ "item_id": 1,
+ "materials": [
+ {"material_id": 5, "quantity": 0.2, "cost": 16},
+ ],
+ "equipment": [
+ {"equipment_id": 1, "days": 0.05, "cost": 75},
+ {"equipment_id": 2, "days": 0.05, "cost": 60}
+ ],
+ "labor": [
+ {"labor_id": 1, "days": 0.1, "cost": 15},
+ {"labor_id": 7, "days": 0.05, "cost": 15}
+ ],
+ "overhead": 10,
+ "profit": 15,
+ "total_cost": 120
+ },
+ {
+ "project_id": 1,
+ "item_id": 2,
+ "materials": [
+ {"material_id": 1, "quantity": 0.3, "cost": 120},
+ {"material_id": 5, "quantity": 0.4, "cost": 32},
+ {"material_id": 6, "quantity": 0.8, "cost": 120}
+ ],
+ "equipment": [
+ {"equipment_id": 3, "days": 0.05, "cost": 40},
+ {"equipment_id": 4, "days": 0.05, "cost": 10},
+ {"equipment_id": 7, "days": 0.02, "cost": 36}
+ ],
+ "labor": [
+ {"labor_id": 1, "days": 0.2, "cost": 30},
+ {"labor_id": 7, "days": 0.05, "cost": 15}
+ ],
+ "overhead": 12,
+ "profit": 15,
+ "total_cost": 350
+ }
+ ]
- # عرض الجدول
- 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
- }
+ if 'show_new_project_form' not in st.session_state:
+ st.session_state.show_new_project_form = False
- # تحويل البيانات إلى DataFrame
- success_df = pd.DataFrame({
- "نوع المشروع": list(success_by_type.keys()),
- "نسبة النجاح": list(success_by_type.values())
- })
+ if 'show_item_analysis' not in st.session_state:
+ st.session_state.show_item_analysis = False
- # عرض الرسم البياني
- st.bar_chart(success_df.set_index("نوع المشروع"))
-
- with col2:
- st.markdown("#### متوسط هامش الربح حسب العميل")
-
- # إنشاء بيانات تجريبية
- margin_by_client = {
- "وزارة النقل": 12,
- "وزارة الإسكان": 15,
- "أمانة منطقة الرياض": 10,
- "شركة أرامكو": 8,
- "الهيئة الملكية": 14
- }
+ if 'current_item' not in st.session_state:
+ st.session_state.current_item = None
- # تحويل البيانات إلى DataFrame
- margin_df = pd.DataFrame({
- "العميل": list(margin_by_client.keys()),
- "هامش الربح (%)": list(margin_by_client.values())
- })
+ if 'show_add_item_form' not in st.session_state:
+ st.session_state.show_add_item_form = False
- # عرض الرسم البياني
- st.bar_chart(margin_df.set_index("العميل"))
-
- def _render_tender_pricing(self):
+ def set_page_config(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")
+ st.set_page_config(
+ page_title="وحدة التسعير المتكاملة",
+ page_icon="💰",
+ layout="wide"
+ )
- # الحصول على معلومات المناقصة المحددة
- project_info = next((p for p in st.session_state.pricing_projects if p['name'] == selected_project), None)
-
- if project_info:
- # عرض معلومات المناقصة
- st.markdown(f"""
-
-
{project_info['name']}
-
العميل: {project_info['client']}
-
القيمة التقديرية: {project_info['value']:,} ريال
-
الموعد النهائي: {project_info['deadline']}
-
الحالة: {project_info['status']}
-
نسبة الإنجاز: {project_info['completion']}%
-
- """, 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):
+ def render(self):
"""
- عرض ملخص التسعير
+ طريقة للتوافق مع الواجهة القديمة
+ تقوم باستدعاء طريقة run
"""
- 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}")
+ self.run()
- # زر تحديث التسعير
- if st.button("تحديث التسعير", key="update_pricing_summary_btn"):
- st.success("تم تحديث التسعير بنجاح", icon="✅")
-
- def _render_project_boq(self, project_info):
+ def run(self):
"""
- عرض جدول الكميات للمشروع
+ تشغيل وحدة التسعير المحسنة
"""
- 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)
+ st.title("وحدة التسعير المتكاملة")
+ # إضافة زر إنشاء تسعير جديد في أعلى الصفحة
+ col1, col2, col3 = st.columns([1, 2, 1])
with col1:
- st.button("إضافة بند", key="add_boq_item_project_btn")
-
- with col2:
- st.button("استيراد من Excel", key="import_boq_project_btn")
+ if st.button("➕ إنشاء تسعير جديد", key="create_new_pricing_btn", type="primary"):
+ st.session_state.show_new_project_form = True
+
+ # عرض نموذج إنشاء تسعير جديد
+ if st.session_state.show_new_project_form:
+ self._render_new_project_form()
+
+ # عرض قائمة المشاريع
+ self._render_projects_list()
- with col3:
- st.button("تصدير إلى Excel", key="export_boq_project_btn")
-
- def _render_cost_analysis(self, project_info):
+ # عرض تفاصيل المشروع المحدد
+ if st.session_state.current_project:
+ project_info = next((p for p in st.session_state.projects if p["id"] == st.session_state.current_project), None)
+ if project_info:
+ self._render_project_details(project_info)
+
+ # عرض تحليل الربحية
+ self._render_profit_margin(project_info)
+
+ # عرض جدول الكميات
+ self._render_bill_of_quantities()
+
+ # عرض تحليل سعر البند
+ if st.session_state.show_item_analysis and st.session_state.current_item:
+ self._render_item_price_analysis()
+
+ def _render_new_project_form(self):
"""
- عرض تحليل التكاليف
+ عرض نموذج إنشاء مشروع جديد
"""
- st.markdown("#### تحليل التكاليف")
+ st.subheader("إنشاء تسعير جديد")
- # تبويبات فرعية لتحليل التكاليف
- 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)
+ with st.form(key="new_project_form"):
+ project_name = st.text_input("اسم المشروع", key="new_project_name")
+ client = st.text_input("العميل", key="new_project_client")
+ estimated_value = st.number_input("القيمة التقديرية", min_value=0, key="new_project_value")
+ deadline = st.date_input("الموعد النهائي", key="new_project_deadline")
- # تنسيق القيم
- 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)
+ col1, col2 = st.columns(2)
+ with col1:
+ submit_button = st.form_submit_button("حفظ")
+ with col2:
+ cancel_button = st.form_submit_button("إلغاء")
+
+ if submit_button and project_name:
+ # إنشاء مشروع جديد
+ new_project_id = max([p["id"] for p in st.session_state.projects], default=0) + 1
+ new_project = {
+ "id": new_project_id,
+ "name": project_name,
+ "client": client,
+ "estimated_value": estimated_value,
+ "deadline": deadline.strftime("%Y-%m-%d"),
+ "status": "قيد التخطيط",
+ "completion": 0,
+ "profit_margin": 15
+ }
- # حساب الإجمالي
- total_materials = sum(item["الإجمالي"] for item in materials)
- st.markdown(f"**إجمالي تكاليف المواد:** {total_materials:,} ريال")
+ # إضافة المشروع الجديد
+ st.session_state.projects.append(new_project)
- # عرض الرسم البياني
- materials_chart = pd.DataFrame({
- "المادة": [item["المادة"] for item in materials],
- "التكلفة": [item["الإجمالي"] for item in materials]
+ # إنشاء جدول كميات فارغ للمشروع الجديد
+ st.session_state.bill_of_quantities.append({
+ "project_id": new_project_id,
+ "items": []
})
- 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}
- ]
+ # تحديد المشروع الحالي
+ st.session_state.current_project = new_project_id
- # تحويل البيانات إلى DataFrame
- labor_df = pd.DataFrame(labor)
+ # إخفاء النموذج
+ st.session_state.show_new_project_form = False
- # تنسيق القيم
- labor_df["التكلفة الشهرية"] = labor_df["التكلفة الشهرية"].apply(lambda x: f"{x:,}")
- labor_df["الإجمالي"] = labor_df["الإجمالي"].apply(lambda x: f"{x:,}")
+ # إعادة تحميل الصفحة
+ st.rerun()
- # عرض جدول العمالة
- st.dataframe(labor_df, use_container_width=True)
+ if cancel_button:
+ # إخفاء النموذج
+ st.session_state.show_new_project_form = False
+ st.rerun()
- # حساب الإجمالي
- total_labor = sum(item["الإجمالي"] for item in labor)
- st.markdown(f"**إجمالي تكاليف العمالة:** {total_labor:,} ريال")
+ def _render_projects_list(self):
+ """
+ عرض قائمة المشاريع
+ """
+ st.subheader("قائمة المشاريع")
- 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:,} ريال")
+ # إنشاء جدول المشاريع
+ projects_df = pd.DataFrame(st.session_state.projects)
- 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)
+ # تنسيق الجدول
+ if not projects_df.empty:
+ projects_df = projects_df[["id", "name", "client", "estimated_value", "deadline", "status", "completion"]]
+ projects_df.columns = ["الرقم", "اسم المشروع", "العميل", "القيمة التقديرية", "الموعد النهائي", "الحالة", "نسبة الإنجاز %"]
- # تنسيق القيم
- indirect_costs_df["القيمة"] = indirect_costs_df["القيمة"].apply(lambda x: f"{x:,}")
+ # عرض الجدول
+ st.dataframe(projects_df, use_container_width=True)
+
+ # اختيار مشروع
+ selected_project = st.selectbox(
+ "اختر مشروع للتسعير",
+ options=st.session_state.projects,
+ format_func=lambda x: x["name"],
+ key="project_selector"
+ )
- # عرض جدول التكاليف غير المباشرة
- st.dataframe(indirect_costs_df, use_container_width=True)
+ if selected_project:
+ st.session_state.current_project = selected_project["id"]
+ else:
+ st.info("لا توجد مشاريع. قم بإنشاء مشروع جديد.")
- # حساب الإجمالي
- total_indirect = sum(item["القيمة"] for item in indirect_costs)
- st.markdown(f"**إجمالي التكاليف غير المباشرة:** {total_indirect:,} ريال")
-
- def _render_profit_margin(self, project_info):
+ def _render_project_details(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
+ st.subheader(f"تفاصيل المشروع: {project_info['name']}")
- # عرض ملخص هامش الربح
col1, col2, col3 = st.columns(3)
-
with col1:
- st.metric("إجمالي التكاليف", f"{total_cost:,.2f} ريال")
-
+ st.metric("العميل", project_info["client"])
+ st.metric("الحالة", project_info["status"])
with col2:
- st.metric("هامش الربح", f"{profit:,.2f} ريال")
-
+ st.metric("القيمة التقديرية", f"{project_info['estimated_value']:,} ريال")
+ st.metric("نسبة الإنجاز", f"{project_info['completion']}%")
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["نسبة الربح المعدلة"] for row in sensitivity_data]
- })
-
- # تحويل نسبة الربح من نص إلى رقم عشري
- chart_data["نسبة الربح"] = chart_data["نسبة الربح"].apply(lambda x: float(x.replace("%", "")) if isinstance(x, str) else x)
-
- 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.metric("الموعد النهائي", project_info["deadline"])
+ st.metric("هامش الربح", f"{project_info['profit_margin']}%")
+
+ def _render_profit_margin(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 = ["منخفض جداً", "منخفض", "متوسط", "مرتفع", "مرتفع جداً"]
+ st.subheader("تحليل الربحية")
- 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)
+ col1, col2 = st.columns([2, 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"""
-