diff --git "a/modules/pricing/pricing_app.py" "b/modules/pricing/pricing_app.py"
new file mode 100644--- /dev/null
+++ "b/modules/pricing/pricing_app.py"
@@ -0,0 +1,4338 @@
+"""
+تطبيق وحدة التسعير المتكاملة
+"""
+
+import streamlit as st
+import pandas as pd
+import numpy as np
+import matplotlib.pyplot as plt
+import plotly.express as px
+import plotly.graph_objects as go
+from datetime import datetime
+import random
+import os
+import time
+import io
+
+# ملاحظة: نحن لا نستخدم st.set_page_config هنا لأنه يجب أن يكون في ملف app.py الرئيسي فقط
+
+# تحسين المظهر العام باستخدام CSS
+st.markdown("""
+
+""", unsafe_allow_html=True)
+
+# تحسين شكل الأزرار بشكل متقدم
+st.markdown("""
+
+""", unsafe_allow_html=True)
+
+# وظيفة مساعدة لإنشاء أزرار بتنسيقات مختلفة
+def styled_button(label, key, type="primary", on_click=None, args=None, full_width=False, icon=None):
+ """
+ إنشاء زر بتنسيق معين
+ :param label: نص الزر
+ :param key: مفتاح الزر الفريد
+ :param type: نوع التنسيق ('primary', 'secondary', 'success', 'warning', 'danger', 'info', 'glass', 'flat')
+ :param on_click: الدالة التي سيتم تنفيذها عند النقر
+ :param args: معاملات الدالة
+ :param full_width: هل يأخذ الزر العرض كاملاً
+ :param icon: أيقونة لعرضها قبل النص (emoji أو HTML)
+ :return: زر مُنسّق
+ """
+ with st.container():
+ btn_class = f"{type}-btn"
+ if icon:
+ btn_class += " action-btn"
+ label = f"{icon} {label}"
+
+ st.markdown(f'
', unsafe_allow_html=True)
+ clicked = st.button(label, key=key, on_click=on_click, args=args, use_container_width=full_width)
+ st.markdown('
', unsafe_allow_html=True)
+ return clicked
+
+# وظيفة لإنشاء أزرار أيقونات صغيرة
+def icon_button(icon, key, type="primary", on_click=None, args=None, tooltip=""):
+ """
+ إنشاء زر أيقونة صغير
+ :param icon: الأيقونة (emoji أو HTML)
+ :param key: مفتاح الزر الفريد
+ :param type: نوع التنسيق
+ :param on_click: الدالة التي سيتم تنفيذها عند النقر
+ :param args: معاملات الدالة
+ :param tooltip: تلميح عند تمرير المؤشر فوق الزر
+ :return: زر أيقونة
+ """
+ with st.container():
+ st.markdown(f'', unsafe_allow_html=True)
+ clicked = st.button(icon, key=key, on_click=on_click, args=args)
+ st.markdown('
', unsafe_allow_html=True)
+ return clicked
+
+from modules.pricing.services.standard_pricing import StandardPricing
+from modules.pricing.services.unbalanced_pricing import UnbalancedPricing
+from modules.pricing.services.local_content_calculator import LocalContentCalculator
+from modules.pricing.services.price_prediction import PricePrediction
+from modules.pricing.services.construction_cost_calculator import ConstructionCostCalculator
+from modules.pricing.services.construction_templates import ConstructionTemplates
+from modules.pricing.services.templates_catalog.templates_catalog import TemplatesCatalog
+from utils.excel_handler import export_to_excel
+from utils.pdf_handler import export_pricing_to_pdf, export_pricing_with_analysis_to_pdf
+from utils.helpers import format_number, format_currency, create_directory_if_not_exists
+
+
+class PricingApp:
+ """وحدة التسعير المتكاملة"""
+
+ def __init__(self):
+ """تهيئة وحدة التسعير المتكاملة"""
+ self.pricing_methods = [
+ "التسعير القياسي",
+ "التسعير غير المتزن",
+ "التسعير التنافسي",
+ "التسعير الموجه بالربحية"
+ ]
+
+ # تهيئة خدمات التسعير
+ self.standard_pricing = StandardPricing()
+ self.unbalanced_pricing = UnbalancedPricing()
+ self.local_content = LocalContentCalculator()
+ self.price_prediction = PricePrediction()
+ self.construction_calculator = ConstructionCostCalculator()
+ self.construction_templates = ConstructionTemplates()
+ self.templates_catalog = TemplatesCatalog(self.construction_templates)
+
+ def render(self):
+ """عرض واجهة وحدة التسعير"""
+
+ st.markdown("وحدة التسعير المتكاملة
", unsafe_allow_html=True)
+
+ tabs = st.tabs([
+ "إنشاء تسعير جديد",
+ "تحليل سعر البند",
+ "نموذج التسعير الشامل",
+ "التسعير غير المتزن",
+ "المحتوى المحلي",
+ "حاسبة تكاليف البناء",
+ "كتالوج البنود النموذجية",
+ "الأدوات المساعدة"
+ ])
+
+ with tabs[0]:
+ self._render_new_pricing_tab()
+
+ with tabs[1]:
+ self._render_item_analysis_tab()
+
+ with tabs[2]:
+ self._render_comprehensive_pricing_tab()
+
+ with tabs[3]:
+ self._render_unbalanced_pricing_tab()
+
+ with tabs[4]:
+ self._render_local_content_tab()
+
+ with tabs[5]:
+ self._render_construction_calculator_tab()
+
+ with tabs[6]:
+ self._render_templates_catalog_tab()
+
+ with tabs[7]:
+ self._render_utilities_tab()
+
+ def _render_templates_catalog_tab(self):
+ """عرض تبويب كتالوج البنود النموذجية"""
+
+ st.markdown("### كتالوج البنود النموذجية")
+
+ # شرح كتالوج البنود النموذجية
+ with st.expander("دلي�� استخدام كتالوج البنود النموذجية", expanded=False):
+ st.markdown("""
+ **كتالوج البنود النموذجية** هو مكتبة شاملة من البنود الجاهزة لمختلف أنواع الأعمال الإنشائية (خرسانة، حديد، عزل، تشطيبات، إلخ).
+
+ ### مميزات الكتالوج:
+ - تفاصيل دقيقة للمواد والعمالة والمعدات المطلوبة لكل بند.
+ - تحليل تكلفة تفصيلي يمكن استخدامه مباشرة في عروض الأسعار.
+ - ربط مباشر مع حاسبة تكاليف البناء وحاسبة الأسعار.
+
+ ### كيفية الاستخدام:
+ - استخدام البنود النموذجية مباشرة في مشاريعك.
+ - تعديل البنود النموذجية لتناسب متطلبات المشروع.
+ - إضافة بنود جديدة إلى الكتالوج للاستخدام المستقبلي.
+ """)
+
+ # عرض الكتالوج باستخدام مكون TemplatesCatalog
+ self.templates_catalog.render()
+
+ def _render_item_analysis_tab(self):
+ """عرض تبويب تحليل سعر البند"""
+
+ st.markdown("### تحليل سعر البند")
+
+ # التحقق من وجود تسعير حالي
+ if 'current_pricing' not in st.session_state or st.session_state.current_pricing is None:
+ st.warning("ليس هناك تسعير حالي. يرجى إنشاء تسعير جديد أولاً.")
+ return
+
+ # اختيار البند للتحليل
+ if 'current_pricing' in st.session_state and st.session_state.current_pricing is not None:
+ items = st.session_state.current_pricing['items']
+ item_options = items['رقم البند'].tolist()
+ selected_item = st.selectbox("اختر البند للتحليل", item_options, key="item_analysis_selector")
+
+ if selected_item:
+ item_data = items[items['رقم البند'] == selected_item].iloc[0]
+
+ st.markdown(f"### تحليل البند: {selected_item}")
+ st.markdown(f"**وصف البند**: {item_data['وصف البند']}")
+ st.markdown(f"**الوحدة**: {item_data['الوحدة']}")
+ st.markdown(f"**الكمية**: {item_data['الكمية']}")
+ st.markdown(f"**سعر الوحدة**: {item_data['سعر الوحدة']:,.2f} ريال")
+
+ # تحليل مكونات السعر
+ st.markdown("### تحليل مكونات السعر")
+
+ # عناصر التكلفة الافتراضية
+ cost_components = {
+ 'المواد': 0.6, # 60% من التكلفة
+ 'العمالة': 0.25, # 25% من التكلفة
+ 'المعدات': 0.1, # 10% من التكلفة
+ 'نفقات عامة': 0.05 # 5% من التكلفة
+ }
+
+ # حساب تكلفة كل عنصر
+ unit_price = item_data['سعر الوحدة']
+ component_values = {k: v * unit_price for k, v in cost_components.items()}
+
+ # عرض مكونات التكلفة في جدول
+ components_df = pd.DataFrame({
+ 'العنصر': component_values.keys(),
+ 'نسبة من التكلفة': [f"{v*100:.1f}%" for v in cost_components.values()],
+ 'القيمة (ريال)': [f"{v:,.2f}" for v in component_values.values()]
+ })
+
+ st.table(components_df)
+
+ # رسم بياني لمكونات التكلفة
+ fig = px.pie(
+ names=list(component_values.keys()),
+ values=list(component_values.values()),
+ title='توزيع مكونات التكلفة'
+ )
+
+ st.plotly_chart(fig)
+
+ # تحليل تاريخي للأسعار
+ st.markdown("### تحليل تاريخي للأسعار")
+
+ # بيانات تاريخية افتراضية
+ historical_data = {
+ 'التاريخ': ['2020-01', '2020-07', '2021-01', '2021-07', '2022-01', '2022-07', '2023-01', '2023-07'],
+ 'السعر': [
+ unit_price * 0.7,
+ unit_price * 0.75,
+ unit_price * 0.8,
+ unit_price * 0.85,
+ unit_price * 0.9,
+ unit_price * 0.95,
+ unit_price,
+ unit_price * 1.05
+ ]
+ }
+
+ hist_df = pd.DataFrame(historical_data)
+
+ # رسم بياني للتحليل التاريخي
+ fig = px.line(
+ hist_df,
+ x='التاريخ',
+ y='السعر',
+ title='تطور سعر الوحدة عبر الزمن',
+ markers=True
+ )
+
+ st.plotly_chart(fig)
+
+ # المقارنة مع الأسعار المرجعية
+ st.markdown("### المقارنة مع الأسعار المرجعية")
+
+ # بيانات مرجعية افتراضية
+ reference_data = {
+ 'المصدر': ['قاعدة البيانات الداخلية', 'دليل الأسعار الاسترشادي', 'متوسط أسعار السوق', 'أسعار المشاريع المماثلة'],
+ 'السعر المرجعي': [
+ unit_price * 0.95,
+ unit_price * 1.05,
+ unit_price * 1.1,
+ unit_price * 0.9
+ ]
+ }
+
+ ref_df = pd.DataFrame(reference_data)
+ ref_df['الفرق عن السعر الحالي'] = ref_df['السعر المرجعي'] - unit_price
+ ref_df['نسبة الفرق'] = (ref_df['الفرق عن السعر الحالي'] / unit_price * 100).round(2).astype(str) + '%'
+
+ st.table(ref_df)
+
+ def _render_new_pricing_tab(self):
+ """عرض تبويب إنشاء تسعير جديد"""
+
+ st.markdown("### إنشاء تسعير جديد")
+
+ col1, col2 = st.columns(2)
+
+ with col1:
+ tender_name = st.text_input("اسم المناقصة", key="tender_name_input")
+ client = st.text_input("الجهة المالكة", key="client_input")
+ pricing_method = st.selectbox("طريقة التسعير", self.pricing_methods, key="pricing_method_selector")
+
+ with col2:
+ tender_number = st.text_input("رقم المناقصة", key="tender_number_input")
+ location = st.text_input("الموقع", key="location_input")
+ submission_date = st.date_input("تاريخ التقديم", key="submission_date_input")
+
+ # خيارات بيانات البنود
+ st.markdown("### بيانات البنود")
+
+ data_source = st.radio(
+ "مصدر بيانات البنود",
+ ["إدخال يدوي", "استيراد من Excel", "استيراد من وحدة تحليل المستندات", "استيراد من وحدة المشاريع"],
+ key="data_source_radio"
+ )
+
+ if data_source == "إدخال يدوي":
+ # ضبط CSS لتحسين ظهور الواجهة العربية
+ st.markdown("""
+
+ """, unsafe_allow_html=True)
+
+ # تهيئة قائمة الوحدات المتاحة
+ unit_options = ["م3", "م2", "طن", "متر طولي", "قطعة", "كجم", "لتر"]
+
+ # إنشاء بيانات افتراضية إذا لم تكن موجودة
+ if 'manual_items' not in st.session_state:
+ # إنشاء DataFrame فارغ
+ manual_items = pd.DataFrame(columns=[
+ 'رقم البند', 'وصف البند', 'الوحدة', 'الكمية', 'سعر الوحدة', 'الإجمالي'
+ ])
+
+ # إضافة بضعة صفوف افتراضية
+ default_items = pd.DataFrame({
+ 'رقم البند': ["A1", "A2", "A3", "A4", "A5"],
+ 'وصف البند': [
+ "توريد وتركيب أعمال الخرسانة المسلحة للأساسات",
+ "توريد وتركيب حديد التسليح للأساسات",
+ "أعمال العزل المائي للأساسات",
+ "أعمال الردم والدك للأساسات",
+ "توريد وتركيب أعمال الخرسانة المسلحة للأعمدة"
+ ],
+ 'الوحدة': ["م3", "طن", "م2", "م3", "م3"],
+ 'الكمية': [250.0, 25.0, 500.0, 300.0, 120.0],
+ 'سعر الوحدة': [0.0, 0.0, 0.0, 0.0, 0.0],
+ 'الإجمالي': [0.0, 0.0, 0.0, 0.0, 0.0]
+ })
+
+ manual_items = pd.concat([manual_items, default_items])
+ st.session_state.manual_items = manual_items
+
+ # عرض واجهة إدخال البنود
+ st.markdown("### إدخال تفاصيل البنود")
+
+ # التحقق من استخدام طريقة الإدخال البسيطة
+ use_simple_input = st.checkbox("استخدام طريقة الإدخال البسيطة", value=True, key="use_simple_input_checkbox")
+
+ if use_simple_input:
+ # عرض البنود الحالية كجدول للعرض فقط
+ st.markdown("### جدول البنود الحالية")
+ st.dataframe(st.session_state.manual_items, use_container_width=True, hide_index=True)
+
+ # إضافة بند جديد
+ st.markdown("### إضافة بند جديد")
+ col1, col2 = st.columns(2)
+
+ with col1:
+ new_id = st.text_input("رقم البند", value=f"A{len(st.session_state.manual_items)+1}", key="new_item_id")
+ new_desc = st.text_area("وصف البند", value="", key="new_item_description")
+
+ with col2:
+ new_unit = st.selectbox("الوحدة", options=unit_options, key="new_item_unit")
+ new_qty = st.number_input("الكمية", value=0.0, min_value=0.0, format="%.2f", key="new_item_qty")
+ new_price = st.number_input("سعر الوحدة", value=0.0, min_value=0.0, format="%.2f", key="new_item_price")
+
+ new_total = new_qty * new_price
+ st.info(f"إجمالي البند الجديد: {new_total:,.2f} ريال")
+
+ if st.button("إضافة البند", key="add_item_button"):
+ # التحقق من صحة البيانات
+ if new_id and new_desc and new_qty > 0:
+ # إنشاء صف جديد
+ new_row = pd.DataFrame({
+ 'رقم البند': [new_id],
+ 'وصف البند': [new_desc],
+ 'الوحدة': [new_unit],
+ 'الكمية': [float(new_qty)],
+ 'سعر الوحدة': [float(new_price)],
+ 'الإجمالي': [float(new_total)]
+ })
+
+ # إضافة الصف إلى DataFrame
+ st.session_state.manual_items = pd.concat([st.session_state.manual_items, new_row], ignore_index=True)
+ st.success("تم إضافة البند بنجاح!")
+ st.rerun()
+ else:
+ st.error("يرجى ملء جميع الحقول المطلوبة: رقم البند، الوصف، والكمية يجب أن تكون أكبر من صفر.")
+
+ # تعديل البنود الحالية
+ st.markdown("### تعديل البنود الحالية")
+
+ # تحديد البند المراد تعديله
+ item_to_edit = st.selectbox(
+ "اختر البند للتعديل",
+ options=st.session_state.manual_items['رقم البند'].tolist(),
+ format_func=lambda x: f"{x}: {st.session_state.manual_items[st.session_state.manual_items['رقم البند'] == x]['وصف البند'].values[0][:30]}...",
+ key="item_to_edit_selector"
+ )
+
+ if item_to_edit:
+ # الحصول على مؤشر الصف للبند المحدد
+ idx = st.session_state.manual_items[st.session_state.manual_items['رقم البند'] == item_to_edit].index[0]
+ row = st.session_state.manual_items.loc[idx]
+
+ # إنشاء نموذج تعديل
+ col1, col2 = st.columns(2)
+
+ with col1:
+ edited_id = st.text_input("رقم البند (تعديل)", value=row['رقم البند'], key="edit_id")
+ edited_desc = st.text_area("وصف البند (تعديل)", value=row['وصف البند'], key="edit_desc")
+
+ with col2:
+ edited_unit = st.selectbox(
+ "الوحدة (تعديل)",
+ options=unit_options,
+ index=unit_options.index(row['الوحدة']) if row['الوحدة'] in unit_options else 0,
+ key="edit_unit"
+ )
+ edited_qty = st.number_input("الكمية (تعديل)", value=float(row['الكمية']), min_value=0.0, format="%.2f", key="edit_qty")
+ edited_price = st.number_input("سعر الوحدة (تعديل)", value=float(row['سعر الوحدة']), min_value=0.0, format="%.2f", key="edit_price")
+
+ edited_total = edited_qty * edited_price
+ st.info(f"إجمالي البند بعد التعديل: {edited_total:,.2f} ريال")
+
+ col1, col2 = st.columns(2)
+ with col1:
+ if st.button("حفظ التعديلات", key="save_edit_button"):
+ # تحديث البند
+ st.session_state.manual_items.at[idx, 'رقم البند'] = edited_id
+ st.session_state.manual_items.at[idx, 'وصف البند'] = edited_desc
+ st.session_state.manual_items.at[idx, 'الوحدة'] = edited_unit
+ st.session_state.manual_items.at[idx, 'الكمية'] = edited_qty
+ st.session_state.manual_items.at[idx, 'سعر الوحدة'] = edited_price
+ st.session_state.manual_items.at[idx, 'الإجمالي'] = edited_total
+
+ st.success("تم تحديث البند بنجاح!")
+ st.rerun()
+
+ with col2:
+ if st.button("حذف هذا البند", key="delete_item_button"):
+ st.session_state.manual_items = st.session_state.manual_items.drop(idx).reset_index(drop=True)
+ st.warning("تم حذف البند!")
+ st.rerun()
+
+ # المجموع الكلي
+ total = st.session_state.manual_items['الإجمالي'].sum()
+ st.metric("المجموع الكلي", f"{total:,.2f} ريال")
+
+ # جعل هذه البيانات متاحة للاستخدام في الخطوات التالية
+ edited_items = st.session_state.manual_items.copy()
+
+ else:
+ # عرض رسالة توضح أن طريقة الإدخال البسيطة هي الأفضل
+ st.warning("لتجنب مشاكل عدم التوافق في أنواع البيانات، يُفضل استخدام طريقة الإدخال البسيطة.")
+
+ # محاولة استخدام المحرر القياسي مع معالجة الأخطاء
+ try:
+ # تحويل البيانات إلى الأنواع المناسبة
+ for col in st.session_state.manual_items.columns:
+ if col in ['رقم البند', 'وصف البند', 'الوحدة']:
+ st.session_state.manual_items[col] = st.session_state.manual_items[col].astype(str)
+
+ # عرض المحرر (للقراءة فقط)
+ st.dataframe(st.session_state.manual_items, use_container_width=True, hide_index=True)
+
+ # إنشاء نظام تعديل منفصل
+ st.markdown("### تعديل أسعار الوحدات")
+
+ for idx, row in st.session_state.manual_items.iterrows():
+ col1, col2 = st.columns([3, 1])
+
+ with col1:
+ st.text(f"{row['رقم البند']}: {row['وصف البند'][:50]}")
+
+ with col2:
+ price = st.number_input(
+ f"سعر الوحدة ({row['الوحدة']})",
+ value=float(row['سعر الوحدة']),
+ min_value=0.0,
+ key=f"price_{idx}"
+ )
+
+ # تحديث السعر والإجمالي
+ st.session_state.manual_items.at[idx, 'سعر الوحدة'] = price
+ st.session_state.manual_items.at[idx, 'الإجمالي'] = price * row['الكمية']
+
+ # المجموع الكلي
+ total = st.session_state.manual_items['الإجمالي'].sum()
+ st.metric("المجموع الكلي", f"{total:,.2f} ريال")
+
+ # جعل هذه البيانات متاحة للاستخدام في الخطوات التالية
+ edited_items = st.session_state.manual_items.copy()
+
+ except Exception as e:
+ st.error(f"حدث خطأ: {str(e)}")
+ st.info("يرجى استخدام طريقة الإدخال البسيطة لتجنب هذه المشكلة.")
+
+ elif data_source == "استيراد من Excel":
+ uploaded_file = st.file_uploader("رفع ملف Excel", type=["xlsx", "xls"])
+
+ if uploaded_file is not None:
+ st.success("تم رفع الملف بنجاح")
+ # محاكاة قراءة الملف
+ st.markdown("### معاينة البيانات المستوردة")
+
+ # إنشاء بيانات افتراضية
+ import_items = pd.DataFrame({
+ 'رقم البند': ["A1", "A2", "A3", "A4", "A5", "A6", "A7"],
+ 'وصف البند': [
+ "توريد وتركيب أعمال الخرسانة المسلحة للأساسات",
+ "توريد وتركيب حديد التسليح للأساسات",
+ "أعمال العزل المائي للأساسات",
+ "أعمال الردم والدك للأساسات",
+ "توريد وتركيب أعمال الخرسانة المسلحة للأعمدة",
+ "توريد وتركيب حديد التسليح للأعمدة",
+ "أعمال البلوك للجدران"
+ ],
+ 'الوحدة': ["م3", "طن", "م2", "م3", "م3", "طن", "م2"],
+ 'الكمية': [250.0, 25.0, 500.0, 300.0, 120.0, 10.0, 400.0],
+ 'سعر الوحدة': [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
+ 'الإجمالي': [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
+ })
+
+ st.dataframe(import_items)
+
+ if st.button("استيراد البيانات", key="import_excel_button"):
+ st.session_state.manual_items = import_items.copy()
+ st.session_state.manual_items_modified = True
+ st.success("تم استيراد البيانات بنجاح!")
+ st.rerun()
+
+ elif data_source == "استيراد من وحدة تحليل المستندات":
+ available_documents = [
+ "كراسة شروط مشروع توسعة مستشفى الملك فهد",
+ "جدول كميات صيانة محطات المياه",
+ "مخططات إنشاء مدرسة ثانوية"
+ ]
+
+ selected_doc = st.selectbox("اختر المستند", available_documents, key="document_selector")
+
+ if styled_button("استيراد البيانات من تحليل المستند", key="import_doc_analysis_button", type="info", icon="📄", full_width=True):
+ # محاكاة استيراد البيانات
+ with st.spinner("جاري استيراد البيانات..."):
+ time.sleep(2)
+
+ # إنشاء بيانات افتراضية
+ doc_items = pd.DataFrame({
+ 'رقم البند': ["A1", "A2", "A3", "A4", "A5", "A6", "A7"],
+ 'وصف البند': [
+ "توريد وتركيب أعمال الخرسانة المسلحة للأساسات",
+ "توريد وتركيب حديد التسليح للأساسات",
+ "أعمال العزل المائي للأساسات",
+ "أعمال الردم والدك للأساسات",
+ "توريد وتركيب أعمال الخرسانة المسلحة للأعمدة",
+ "توريد وتركيب حديد التسليح للأعمدة",
+ "أعمال البلوك للجدران"
+ ],
+ 'الوحدة': ["م3", "طن", "م2", "م3", "م3", "طن", "م2"],
+ 'الكمية': [250.0, 25.0, 500.0, 300.0, 120.0, 10.0, 400.0],
+ 'سعر الوحدة': [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
+ 'الإجمالي': [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
+ })
+
+ st.session_state.manual_items = doc_items.copy()
+ st.success("تم استيراد البيانات من تحليل المستند بنجاح!")
+ st.dataframe(doc_items)
+
+ elif data_source == "استيراد من وحدة المشاريع":
+ # قائمة المشاريع المتاحة للاستيراد منها
+ available_projects = [
+ "مشروع تطوير طريق ا��ملك عبدالعزيز",
+ "مشروع إنشاء محطة تحلية المياه بالجبيل",
+ "مشروع توسعة مستشفى الملك فهد",
+ "مشروع إنشاء مجمع سكني بالرياض"
+ ]
+
+ selected_project = st.selectbox("اختر المشروع", available_projects, key="project_selector")
+
+ if styled_button("استيراد البيانات من المشروع", key="import_project_data_button", type="primary", icon="🏗️", full_width=True):
+ # محاكاة استيراد البيانات من المشروع
+ with st.spinner("جاري استيراد بيانات المشروع..."):
+ time.sleep(1.5)
+
+ # إنشاء بيانات مشروع افتراضية بناءً على المشروع المختار
+ if selected_project == "مشروع تطوير طريق الملك عبدالعزيز":
+ project_items = pd.DataFrame({
+ 'رقم البند': ["R1", "R2", "R3", "R4", "R5"],
+ 'وصف البند': [
+ "أعمال الحفر والردم للطريق",
+ "توريد وتنفيذ طبقة الأساس",
+ "طبقة الأسفلت الأولى",
+ "طبقة الأسفلت النهائية",
+ "أعمال الدهانات والعلامات"
+ ],
+ 'الوحدة': ["م3", "م2", "م2", "م2", "م.ط"],
+ 'الكمية': [5000.0, 8000.0, 8000.0, 8000.0, 4000.0],
+ 'سعر الوحدة': [45.0, 120.0, 85.0, 95.0, 25.0],
+ 'الإجمالي': [225000.0, 960000.0, 680000.0, 760000.0, 100000.0]
+ })
+ elif selected_project == "مشروع إنشاء محطة تحلية المياه بالجبيل":
+ project_items = pd.DataFrame({
+ 'رقم البند': ["W1", "W2", "W3", "W4", "W5"],
+ 'وصف البند': [
+ "أعمال الخرسانة المسلحة للخزانات",
+ "توريد وتركيب معدات التحلية",
+ "أعمال التمديدات والأنابيب",
+ "تشطيبات المباني الإدارية",
+ "أعمال الكهرباء والتحكم"
+ ],
+ 'الوحدة': ["م3", "قطعة", "م.ط", "م2", "مقطوعية"],
+ 'الكمية': [1800.0, 12.0, 5000.0, 1200.0, 1.0],
+ 'سعر الوحدة': [1200.0, 250000.0, 850.0, 750.0, 1500000.0],
+ 'الإجمالي': [2160000.0, 3000000.0, 4250000.0, 900000.0, 1500000.0]
+ })
+ elif selected_project == "مشروع توسعة مستشفى الملك فهد":
+ project_items = pd.DataFrame({
+ 'رقم البند': ["H1", "H2", "H3", "H4", "H5", "H6"],
+ 'وصف البند': [
+ "أعمال الهيكل الخرساني",
+ "أعمال البناء والجدران",
+ "التشطيبات الداخلية",
+ "الأنظمة الكهربائية والميكانيكية",
+ "تجهيزات طبية",
+ "أعمال الموقع العام"
+ ],
+ 'الوحدة': ["م3", "م2", "م2", "غرفة", "قطعة", "م2"],
+ 'الكمية': [2800.0, 4500.0, 7500.0, 120.0, 45.0, 3000.0],
+ 'سعر الوحدة': [1500.0, 650.0, 1200.0, 35000.0, 80000.0, 350.0],
+ 'الإجمالي': [4200000.0, 2925000.0, 9000000.0, 4200000.0, 3600000.0, 1050000.0]
+ })
+ else: # مشروع إنشاء مجمع سكني بالرياض
+ project_items = pd.DataFrame({
+ 'رقم البند': ["B1", "B2", "B3", "B4", "B5", "B6", "B7"],
+ 'وصف البند': [
+ "الهياكل الخرسانية للفلل",
+ "الجدران والقواطع الداخلية",
+ "التشطيبات الخارجية",
+ "التشطيبات الداخلية",
+ "أعمال الكهرباء والسباكة",
+ "الملحقات والحدائق",
+ "البنية التحتية للموقع"
+ ],
+ 'الوحدة': ["م3", "م2", "م2", "م2", "فيلا", "م2", "مقطوعية"],
+ 'الكمية': [3500.0, 12000.0, 8000.0, 18000.0, 25.0, 5000.0, 1.0],
+ 'سعر الوحدة': [1350.0, 450.0, 380.0, 750.0, 85000.0, 280.0, 2500000.0],
+ 'الإجمالي': [4725000.0, 5400000.0, 3040000.0, 13500000.0, 2125000.0, 1400000.0, 2500000.0]
+ })
+
+ st.session_state.manual_items = project_items.copy()
+ st.success(f"تم استيراد بيانات المشروع '{selected_project}' بنجاح!")
+ st.dataframe(project_items)
+
+ # زر بدء التسعير
+ if styled_button("بدء التسعير", key="start_pricing_button", type="success", icon="✅", full_width=True):
+ # تحقق من صحة البيانات
+ if 'manual_items' in st.session_state and not st.session_state.manual_items.empty:
+ # التأكد من حساب الإجمالي قبل الحفظ
+ st.session_state.manual_items['الإجمالي'] = st.session_state.manual_items['الكمية'] * st.session_state.manual_items['سعر الوحدة']
+
+ # حفظ بيانات التسعير الحالي
+ st.session_state.current_pricing = {
+ 'name': tender_name,
+ 'number': tender_number,
+ 'client': client,
+ 'location': location,
+ 'method': pricing_method,
+ 'submission_date': submission_date,
+ 'items': st.session_state.manual_items.copy(),
+ 'status': 'جديد',
+ 'created_at': datetime.now()
+ }
+
+ # الانتقال إلى تبويب نموذج التسعير الشامل
+ st.success("تم إنشاء التسعير بنجاح! يمكنك الانتقال إلى نموذج التسعير الشامل.")
+ else:
+ st.error("يرجى إدخال بيانات البنود أولاً.")
+
+ def _render_comprehensive_pricing_tab(self):
+ """عرض تبويب نموذج التسعير الشامل"""
+
+ st.markdown("### نموذج التسعير الشامل")
+
+ # التحقق من وجود تسعير حالي
+ if 'current_pricing' not in st.session_state or st.session_state.current_pricing is None:
+ st.warning("ليس هناك تسعير حالي. يرجى إنشاء تسعير جديد أولاً.")
+ return
+
+ # عرض معلومات التسعير الحالي
+ pricing = st.session_state.current_pricing
+
+ col1, col2, col3 = st.columns(3)
+
+ with col1:
+ st.metric("اسم المناقصة", pricing['name'])
+ st.metric("الجهة المالكة", pricing['client'])
+
+ with col2:
+ st.metric("رقم المناقصة", pricing['number'])
+ st.metric("تاريخ التقديم", pricing['submission_date'].strftime("%Y-%m-%d"))
+
+ with col3:
+ st.metric("طريقة التسعير", pricing['method'])
+ st.metric("الموقع", pricing['location'])
+
+ # عرض البنود والتسعير
+ st.markdown("### بنود التسعير")
+
+ items = pricing['items'].copy()
+
+ # إضافة أسعار الوحدة للمحاكاة
+ if 'سعر الوحدة' in items.columns and (items['سعر الوحدة'] == 0).all():
+ items['سعر الوحدة'] = [
+ round(random.uniform(1000, 3000), 2), # الخرسانة
+ round(random.uniform(5000, 7000), 2), # الحديد
+ round(random.uniform(100, 200), 2), # العزل
+ round(random.uniform(50, 100), 2), # الردم
+ round(random.uniform(1200, 3500), 2), # الخرسانة للأعمدة
+ ]
+
+ if len(items) > 5:
+ for i in range(5, len(items)):
+ items.at[i, 'سعر الوحدة'] = round(random.uniform(500, 5000), 2)
+
+ # حساب الإجمالي
+ items['الإجمالي'] = items['الكمية'] * items['سعر الوحدة']
+
+ # عرض البنود
+ st.dataframe(items, use_container_width=True, hide_index=True)
+
+
+ # ✅ التوصية الذكية باستخدام OpenAI
+ with st.expander("🔍 توليد توصية ذكية باستخدام AI"):
+ if styled_button("توليد توصية ذك��ة باستخدام AI", key="gen_ai_recommendation_btn", type="secondary", icon="🤖", full_width=True):
+ import openai
+ import os
+
+ # تهيئة عميل OpenAI - استخدام واجهة الإصدار الجديد فقط (1.0.0 وما فوق)
+ api_key = os.environ.get("ai")
+ client = openai.OpenAI(api_key=api_key)
+
+ items_df = items.copy()
+ prompt = f"""قم بتحليل الجدول التالي للبنود في مشروع إنشاء، وقدم توصية ذكية لتحسين التسعير وضمان التوازن المالي. الجدول يحتوي على البنود، الكميات، الأسعار، والإجماليات:\n\n{items_df.to_string(index=False)}\n\nالتوصية:\n"""
+
+ try:
+ with st.spinner("جاري توليد التوصية..."):
+ # استخدام واجهة OpenAI الجديدة
+ response = client.chat.completions.create(
+ model="gpt-4o", # استخدام أحدث نموذج من OpenAI GPT-4o
+ messages=[
+ {"role": "system", "content": "أنت خبير في تسعير مشاريع البناء والبنية التحتية."},
+ {"role": "user", "content": prompt}
+ ],
+ temperature=0.4,
+ max_tokens=500
+ )
+
+ # استخراج محتوى الرسالة من واجهة OpenAI الجديدة
+ recommendation = response.choices[0].message.content
+
+ st.success("تم توليد التوصية بنجاح!")
+ st.markdown("#### التوصية الذكية:")
+ st.info(recommendation)
+
+ except Exception as e:
+ st.error(f"حدث خطأ أثناء الاتصال بنموذج OpenAI: {e}")
+ st.info("يجب التأكد من تثبيت أحدث إصدار من مكتبة OpenAI: `pip install openai --upgrade`")
+
+ # واجهة تعديل أسعار الوحدات
+ st.markdown("### تعديل أسعار الوحدات")
+
+ # تقسيم البنود إلى مجموعتين للعرض
+ col1, col2 = st.columns(2)
+ half = len(items) // 2 + len(items) % 2
+
+ with col1:
+ for idx in range(half):
+ if idx < len(items):
+ row = items.iloc[idx]
+ price = st.number_input(
+ f"{row['رقم البند']}: {row['وصف البند'][:30]}... ({row['الوحدة']})",
+ value=float(row['سعر الوحدة']),
+ min_value=0.0,
+ key=f"price1_{idx}"
+ )
+ items.at[idx, 'سعر الوحدة'] = price
+ items.at[idx, 'الإجمالي'] = price * items.at[idx, 'الكمية']
+
+ with col2:
+ for idx in range(half, len(items)):
+ row = items.iloc[idx]
+ price = st.number_input(
+ f"{row['رقم البند']}: {row['وصف البند'][:30]}... ({row['الوحدة']})",
+ value=float(row['سعر الوحدة']),
+ min_value=0.0,
+ key=f"price2_{idx}"
+ )
+ items.at[idx, 'سعر الوحدة'] = price
+ items.at[idx, 'الإجمالي'] = price * items.at[idx, 'الكمية']
+
+ # حساب وعرض إجماليات التسعير
+ total_price = items['الإجمالي'].sum()
+
+ st.markdown("### إجماليات التسعير")
+
+ col1, col2, col3 = st.columns(3)
+
+ with col1:
+ st.metric("إجمالي التكاليف المباشرة", f"{total_price:,.2f} ريال")
+
+ with col2:
+ overhead_percentage = st.slider("نسبة المصاريف العامة والأرباح (%)", 5, 30, 15)
+ overhead_value = total_price * overhead_percentage / 100
+ st.metric("المصاريف العامة والأرباح", f"{overhead_value:,.2f} ريال")
+
+ with col3:
+ grand_total = total_price + overhead_value
+ st.metric("الإجمالي النهائي", f"{grand_total:,.2f} ريال")
+
+ # رسم بياني لتوزيع التكاليف
+ st.markdown("### تحليل التكاليف")
+
+ # حساب النسب المئوية لكل بند
+ pie_data = items.copy()
+ pie_data['نسبة من إجمالي التكاليف'] = pie_data['الإجمالي'] / total_price * 100
+
+ fig = px.pie(
+ pie_data,
+ values='نسبة من إجمالي التكاليف',
+ names='وصف البند',
+ title='توزيع التكاليف حسب البنود',
+ hole=0.4
+ )
+
+ st.plotly_chart(fig, use_container_width=True)
+
+ # أزرار العمليات
+ col1, col2, col3 = st.columns(3)
+
+ with col1:
+ if styled_button("حفظ التسعير", key="save_comprehensive_pricing_button", type="primary", icon="💾"):
+ # تحديث بيانات التسعير الحالي
+ st.session_state.current_pricing['items'] = items.copy()
+ st.success("تم حفظ التسعير بنجاح!")
+
+ with col2:
+ if styled_button("تصدير إلى Excel", key="export_to_excel_button", type="info", icon="📊"):
+ try:
+ # إنشاء دليل الصادرات إذا لم يكن موجودًا
+ export_dir = "exports"
+ create_directory_if_not_exists(export_dir)
+
+ # تصدير البيانات إلى ملف Excel
+ file_path = os.path.join(export_dir, f"تسعير_{tender_name}_{datetime.now().strftime('%Y%m%d_%H%M%S')}.xlsx")
+ export_to_excel(items, file_path, tender_name)
+
+ st.success(f"تم تصدير التسعير إلى Excel بنجاح! مسار الملف: {file_path}")
+ except Exception as e:
+ st.error(f"حدث خطأ أثناء تصدير البيانات: {str(e)}")
+
+ if styled_button("تصدير إلى PDF", key="export_to_pdf_button", type="info", icon="📄"):
+ try:
+ # إنشاء دليل الصادرات إذا لم يكن موجودًا
+ export_dir = "exports"
+ create_directory_if_not_exists(export_dir)
+
+ # تصدير البيانات إلى ملف PDF
+ file_path = os.path.join(export_dir, f"تسعير_{tender_name}_{datetime.now().strftime('%Y%m%d_%H%M%S')}.pdf")
+
+ # التحقق من وجود بيانات تحليل الأسعار
+ if 'items_price_analysis' in st.session_state and st.session_state.items_price_analysis:
+ # استخراج المعلومات الأساسية للمشروع
+ project_info = {
+ 'اسم_المشروع': tender_name,
+ 'وصف_المشروع': f"مناقصة رقم: {tender_number} - الجهة المالكة: {client} - الموقع: {location}"
+ }
+
+ # تصدير البيانات مع تحليل الأسعار
+ export_pricing_with_analysis_to_pdf(
+ items,
+ st.session_state.items_price_analysis,
+ file_path,
+ title=f"تسعير مناقصة: {tender_name}",
+ project_info=project_info
+ )
+ else:
+ # تصدير البيانات بدون تحليل الأسعار
+ export_pricing_to_pdf(
+ items,
+ file_path,
+ title=f"تسعير مناقصة: {tender_name}",
+ description=f"مناقصة رقم: {tender_number} - الجهة المالكة: {client} - الموقع: {location}"
+ )
+
+ st.success(f"تم تصدير التسعير إلى PDF بنجاح! مسار الملف: {file_path}")
+ except Exception as e:
+ st.error(f"حدث خطأ أثناء تصدير البيانات: {str(e)}")
+
+ with col3:
+ if styled_button("تحليل المخاطر المالية", key="financial_risk_analysis_button", type="warning", icon="⚠️"):
+ st.success("تم إرسال الطلب إلى وحدة تحليل المخاطر!")
+
+ def _render_unbalanced_pricing_tab(self):
+ """عرض تبويب التسعير غير المتزن"""
+
+ st.markdown("### التسعير غير المتزن")
+
+ # التحقق من وجود تسعير حالي
+ if 'current_pricing' not in st.session_state or st.session_state.current_pricing is None:
+ st.warning("ليس هناك تسعير حالي. يرجى إنشاء تسعير جديد أولاً.")
+ return
+
+ # شرح التسعير غير المتزن
+ with st.expander("ما هو التسعير غير المتزن؟", expanded=False):
+ st.markdown("""
+ **التسعير غير المتزن** هو استراتيجية تسعير تقوم على توزيع التكاليف بين بنود المناقصة بشكل غير متساوٍ، مع الحفاظ على إجمالي قيمة العطاء.
+
+ ### استراتيجيات التسعير غير المتزن:
+
+ 1. **التحميل الأمامي (Front Loading)**: زيادة أسعار البنود المبكرة في المشروع للحصول على تدفق نقدي أفضل في بداية المشروع.
+ 2. **التحميل الخلفي (Back Loading)**: زيادة أسعار البنود المتأخرة في المشروع.
+ 3. **تحميل البنود المؤكدة**: زيادة أسعار البنود التي من المؤكد تنفيذها بالكميات المحددة.
+ 4. **تخفيض أسعار البنود المحتملة**: تخفيض أسعار البنود التي قد تزيد كمياتها أثناء التنفيذ.
+
+ ### مزايا التسعير غير المتزن:
+
+ - تحسين التدفق النقدي للمشروع.
+ - تعظيم الربحية في حالة التغييرات والأوامر التغييرية.
+ - زيادة فرص الفوز بالمناقصة.
+
+ ### مخاطر التسعير غير المتزن:
+
+ - قد يتم رفض العطاء إذا كان عدم التوازن واضحاً.
+ - قد تتأثر السمعة سلباً إذا تم استخدامه بشكل مفرط.
+ - قد يؤدي إلى خسائر إذا لم يتم تنفيذ البنود ذات الأسعار العالية.
+ """)
+
+ # عرض بنود التسعير الحالي
+ items = st.session_state.current_pricing['items'].copy()
+
+ # إضافة عمود إستراتيجية التسعير
+ if 'إستراتيجية التسعير' not in items.columns:
+ items['إستراتيجية التسعير'] = 'متوازن'
+
+ st.markdown("### إستراتيجية التسعير غير المتزن")
+
+ # اختيار الإستراتيجية
+ strategy = st.selectbox(
+ "اختر إستراتيجية التسعير",
+ [
+ "تحميل أمامي (Front Loading)",
+ "تحميل البنود المؤكدة",
+ "تخفيض البنود المحتمل زيادتها",
+ "إستراتيجية مخصصة"
+ ],
+ key="pricing_strategy_selector"
+ )
+
+ # تطبيق الإستراتيجية المختارة
+ if strategy == "تحميل أمامي (Front Loading)":
+ # محاكاة تحميل أمامي
+ items_count = len(items)
+ early_items = items.iloc[:items_count//3].index
+ middle_items = items.iloc[items_count//3:2*items_count//3].index
+ late_items = items.iloc[2*items_count//3:].index
+
+ # تطبيق الزيادة والنقصان
+ for idx in early_items:
+ items.at[idx, 'سعر الوحدة'] = items.at[idx, 'سعر الوحدة'] * 1.3 # زيادة 30%
+ items.at[idx, 'إستراتيجية التسعير'] = 'زيادة'
+
+ for idx in middle_items:
+ items.at[idx, 'إستراتيجية التسعير'] = 'متوازن'
+
+ for idx in late_items:
+ items.at[idx, 'سعر الوحدة'] = items.at[idx, 'سعر الوحدة'] * 0.7 # نقص 30%
+ items.at[idx, 'إستراتيجية التسعير'] = 'نقص'
+
+ elif strategy == "تحميل البنود المؤكدة":
+ # محاكاة - اعتبار بعض البنود مؤكدة
+ confirmed_items = [0, 2, 4] # الأصفار-مستندة
+ variable_items = [idx for idx in range(len(items)) if idx not in confirmed_items]
+
+ # تطبيق الزيادة والنقصان
+ for idx in confirmed_items:
+ if idx < len(items):
+ items.at[idx, 'سعر الوحدة'] = items.at[idx, 'سعر الوحدة'] * 1.25 # زيادة 25%
+ items.at[idx, 'إستراتيجية التسعير'] = 'زيادة'
+
+ for idx in variable_items:
+ if idx < len(items):
+ items.at[idx, 'سعر الوحدة'] = items.at[idx, 'سعر الوحدة'] * 0.85 # نقص 15%
+ items.at[idx, 'إستراتيجية التسعير'] = 'نقص'
+
+ elif strategy == "تخفيض البنود المحتمل زيادتها":
+ # محاكاة - اعتبار بعض البنود محتمل زيادتها
+ variable_items = [1, 3] # الأصفار-مستندة
+ other_items = [idx for idx in range(len(items)) if idx not in variable_items]
+
+ # تطبيق الزيادة والنقصان
+ for idx in variable_items:
+ if idx < len(items):
+ items.at[idx, 'سعر الوحدة'] = items.at[idx, 'سعر الوحدة'] * 0.7 # نقص 30%
+ items.at[idx, 'إستراتيجية التسعير'] = 'نقص'
+
+ for idx in other_items:
+ if idx < len(items):
+ items.at[idx, 'سعر الوحدة'] = items.at[idx, 'سعر الوحدة'] * 1.15 # زيادة 15%
+ items.at[idx, 'إستراتيجية التسعير'] = 'زيادة'
+
+ else: # إستراتيجية مخصصة
+ st.markdown("### تعديل أسعار البنود يدوياً")
+ st.markdown("قم بتعديل أسعار البنود وإستراتيجية التسعير يدوياً من خلال النموذج أدناه.")
+
+ # إضافة واجهة لتعديل الأسعار يدوياً
+ if 'edit_items' not in st.session_state:
+ st.session_state.edit_items = items.copy()
+
+ # عرض نموذج التعديل لكل بند
+ for index, row in items.iterrows():
+ with st.container():
+ col1, col2, col3, col4 = st.columns([3, 1, 1, 1])
+
+ with col1:
+ st.markdown(f"**البند:** {row['وصف البند']}")
+
+ with col2:
+ original_price = st.session_state.current_pricing['items'].iloc[index]['سعر الوحدة']
+ new_price = st.number_input(
+ "سعر الوحدة الجديد",
+ min_value=0.01,
+ value=float(row['سعر الوحدة']),
+ key=f"price_{index}"
+ )
+ items.at[index, 'سعر الوحدة'] = new_price
+
+ with col3:
+ percent_change = ((new_price - original_price) / original_price) * 100
+ st.metric(
+ "نسبة التغيير",
+ f"{percent_change:.1f}%",
+ delta=f"{new_price - original_price:.2f}"
+ )
+
+ with col4:
+ strategy_options = ['متوازن', 'زيادة', 'نقص']
+ current_strategy = row['إستراتيجية التسعير']
+ strategy_index = strategy_options.index(current_strategy) if current_strategy in strategy_options else 0
+
+ new_strategy = st.selectbox(
+ "الإستراتيجية",
+ options=strategy_options,
+ index=strategy_index,
+ key=f"strategy_{index}"
+ )
+ items.at[index, 'إستراتيجية التسعير'] = new_strategy
+
+ st.markdown("---")
+
+ # حساب الإجمالي بعد التعديل
+ items['الإجمالي'] = items['الكمية'] * items['سعر الوحدة']
+
+ # تعيين ألوان للإستراتيجيات وتنسيق الجدول بشكل متقدم
+ def highlight_row(row):
+ strategy = row['إستراتيجية التسعير']
+ styles = [''] * len(row)
+
+ # تطبيق لون خلفية لكل صف حسب الإستراتيجية
+ if strategy == 'زيادة':
+ background = 'linear-gradient(90deg, rgba(168, 230, 207, 0.3), rgba(168, 230, 207, 0.1))'
+ text_color = '#1F7A8C'
+ elif strategy == 'نقص':
+ background = 'linear-gradient(90deg, rgba(255, 154, 162, 0.3), rgba(255, 154, 162, 0.1))'
+ text_color = '#9D2A45'
+ else:
+ background = 'linear-gradient(90deg, rgba(220, 237, 255, 0.3), rgba(220, 237, 255, 0.1))'
+ text_color = '#555555'
+
+ # تطبيق النمط على جميع الخلايا في الصف
+ for i in range(len(styles)):
+ styles[i] = f'background: {background}; color: {text_color}; border-bottom: 1px solid #ddd;'
+
+ # تطبيق نمط خاص على خلية الإستراتيجية
+ if strategy == 'زيادة':
+ styles[list(row.index).index('إستراتيجية التسعير')] = 'background-color: #a8e6cf; color: #007263; font-weight: bold; border-radius: 5px; text-align: center;'
+ elif strategy == 'نقص':
+ styles[list(row.index).index('إستراتيجية التسعير')] = 'background-color: #ff9aa2; color: #9D2A45; font-weight: bold; border-radius: 5px; text-align: center;'
+ else:
+ styles[list(row.index).index('إستراتيجية التسعير')] = 'background-color: #dceeff; color: #555555; font-weight: bold; border-radius: 5px; text-align: center;'
+
+ # تنسيق عمود السعر
+ price_idx = list(row.index).index('سعر الوحدة')
+ styles[price_idx] = styles[price_idx] + 'font-weight: bold;'
+
+ # تنسيق عمود الإجمالي
+ total_idx = list(row.index).index('الإجمالي')
+ styles[total_idx] = styles[total_idx] + 'font-weight: bold;'
+
+ return styles
+
+ # عرض الجدول مع تنسيق متقدم
+ st.markdown("بنود التسعير غير المتزن
", unsafe_allow_html=True)
+
+ # تطبيق التنسيق على الجدول
+ styled_items = items.style.apply(highlight_row, axis=1)
+
+ # تنسيق تنسيق الأرقام
+ styled_items = styled_items.format({
+ 'الكمية': '{:,.2f}',
+ 'سعر الوحدة': '{:,.2f}',
+ 'الإجمالي': '{:,.2f}'
+ })
+
+ st.dataframe(styled_items, use_container_width=True, height=None)
+
+ # المقارنة بين التسعير المتوازن وغير المتوازن
+ st.markdown("مقارنة التسعير المتوازن وغير المتوازن
", unsafe_allow_html=True)
+
+ original_items = st.session_state.current_pricing['items'].copy()
+ original_total = original_items['الإجمالي'].sum()
+ unbalanced_total = items['الإجمالي'].sum()
+
+ # عرض بطاقات المقارنة بتصميم متقدم
+ st.markdown("""
+
+ """, unsafe_allow_html=True)
+
+ col1, col2, col3 = st.columns(3)
+
+ with col1:
+ st.markdown("""
+
+
إجمالي التسعير المتوازن
+
{:,.2f} ريال
+
التسعير الأصلي
+
+ """.format(original_total), unsafe_allow_html=True)
+
+ with col2:
+ st.markdown("""
+
+
إجمالي التسعير غير المتوازن
+
{:,.2f} ريال
+
بعد إعادة توزيع الأسعار
+
+ """.format(
+ unbalanced_total,
+ "positive-delta" if unbalanced_total > original_total else "negative-delta" if unbalanced_total < original_total else "neutral-delta"
+ ), unsafe_allow_html=True)
+
+ with col3:
+ diff = unbalanced_total - original_total
+ delta_percent = diff/original_total*100 if original_total > 0 else 0
+
+ st.markdown("""
+
+
الفرق بين التسعيرين
+
{:,.2f} ريال
+
نسبة الفرق: {:+.1f}%
+
+ """.format(
+ diff,
+ "positive-delta" if diff > 0 else "negative-delta" if diff < 0 else "neutral-delta",
+ delta_percent
+ ), unsafe_allow_html=True)
+
+ # المعايرة للحفاظ على إجمالي التسعير
+ if abs(diff) > 1: # إذا كان هناك فرق كبير
+ if styled_button("معايرة الأسعار للحفاظ على إجمالي التسعير", key="calibrate_prices_button", type="primary", icon="⚖️", full_width=True):
+ # تعديل الأسعار للحفاظ على إجمالي التكلفة
+ adjustment_factor = original_total / unbalanced_total
+ items['سعر الوحدة'] = items['سعر الوحدة'] * adjustment_factor
+ items['الإجمالي'] = items['الكمية'] * items['سعر الوحدة']
+
+ st.success(f"تم تعديل الأسعار للحفاظ على إجمالي التسعير الأصلي ({original_total:,.2f} ريال)")
+ st.dataframe(items, use_container_width=True)
+
+ # رسم بياني للمقارنة
+ st.markdown("تحليل بصري للتسعير غير المتوازن
", unsafe_allow_html=True)
+
+ # إعداد البيانات للرسم البياني
+ chart_data = pd.DataFrame({
+ 'وصف البند': original_items['وصف البند'],
+ 'التسعير المتوازن': original_items['الإجمالي'],
+ 'التسعير غير المتوازن': items['الإجمالي']
+ })
+
+ # إضافة عمود للنسبة المئوية للتغيير
+ chart_data['نسبة التغيير'] = (chart_data['التسعير غير المتوازن'] - chart_data['التسعير المتوازن']) / chart_data['التسعير المتوازن'] * 100
+
+ # تحديد لون الأعمدة بناءً على نسبة التغيير
+ bar_colors = []
+ for change in chart_data['نسبة التغيير']:
+ if change > 5: # زيادة كبيرة
+ bar_colors.append('#1F7A8C') # أزرق مخضر
+ elif change > 0: # زيادة صغيرة
+ bar_colors.append('#81B29A') # أخضر فاتح
+ elif change > -5: # نقص صغير
+ bar_colors.append('#F2CC8F') # أصفر
+ else: # نقص كبير
+ bar_colors.append('#E07A5F') # أحمر
+
+ # التبويب بين مخططات مختلفة للمقارنة
+ chart_tabs = st.tabs(["مخطط شريطي", "مخطط مقارنة", "مخطط نسبة التغيير"])
+
+ with chart_tabs[0]: # رسم بياني شريطي
+ # رسم بياني شريطي للمقارنة
+ fig = go.Figure()
+
+ fig.add_trace(go.Bar(
+ x=chart_data['وصف البند'],
+ y=chart_data['التسعير المتوازن'],
+ name='التسعير المتوازن',
+ marker_color='rgba(55, 83, 109, 0.7)'
+ ))
+
+ fig.add_trace(go.Bar(
+ x=chart_data['وصف البند'],
+ y=chart_data['التسعير غير المتوازن'],
+ name='التسعير غير المتوازن',
+ marker_color=bar_colors
+ ))
+
+ fig.update_layout(
+ title='مقارنة بين التسعير المتوازن وغير المتوازن',
+ xaxis_tickfont_size=14,
+ yaxis=dict(
+ title='الإجمالي (ريال)',
+ titlefont_size=16,
+ tickfont_size=14,
+ ),
+ legend=dict(
+ x=0.01,
+ y=0.99,
+ bgcolor='rgba(255, 255, 255, 0.8)',
+ bordercolor='rgba(0, 0, 0, 0.1)',
+ borderwidth=1
+ ),
+ barmode='group',
+ bargap=0.15,
+ bargroupgap=0.1,
+ plot_bgcolor='rgba(240, 249, 255, 0.5)',
+ margin=dict(t=50, b=50, l=20, r=20)
+ )
+
+ st.plotly_chart(fig, use_container_width=True)
+
+ with chart_tabs[1]: # رسم مقارنة
+ # رسم مقارنة بين التسعيرين
+ fig = go.Figure()
+
+ # إضافة خط للتسعير المتوازن
+ fig.add_trace(go.Scatter(
+ x=chart_data['وصف البند'],
+ y=chart_data['التسعير المتوازن'],
+ name='التسعير المتوازن',
+ mode='lines+markers',
+ line=dict(color='rgb(55, 83, 109)', width=3),
+ marker=dict(size=10, color='rgb(55, 83, 109)')
+ ))
+
+ # إضافة نقاط للتسعير غير المتوازن
+ fig.add_trace(go.Scatter(
+ x=chart_data['وصف البند'],
+ y=chart_data['التسعير غير المتوازن'],
+ name='التسعير غير المتوازن',
+ mode='lines+markers',
+ line=dict(color='rgb(26, 118, 255)', width=3),
+ marker=dict(
+ size=12,
+ color=bar_colors,
+ line=dict(width=2, color='white')
+ )
+ ))
+
+ # تحديثات التخطيط
+ fig.update_layout(
+ title='مقارنة مرئية بين استراتيجيات التسعير',
+ xaxis_tickfont_size=14,
+ yaxis=dict(
+ title='القيمة الإجمالية (ريال)',
+ titlefont_size=16,
+ tickfont_size=14,
+ gridcolor='rgba(200, 200, 200, 0.2)'
+ ),
+ legend=dict(
+ x=0.01,
+ y=0.99,
+ bgcolor='rgba(255, 255, 255, 0.8)',
+ bordercolor='rgba(0, 0, 0, 0.1)',
+ borderwidth=1
+ ),
+ plot_bgcolor='rgba(240, 249, 255, 0.5)',
+ margin=dict(t=50, b=50, l=20, r=20)
+ )
+
+ st.plotly_chart(fig, use_container_width=True)
+
+ with chart_tabs[2]: # مخطط نسبة التغيير
+ # مخطط للنسبة المئوية للتغيير
+ fig = go.Figure()
+
+ # إضافة أعمدة لنسبة التغيير مع ألوان مختلفة حسب القيمة
+ fig.add_trace(go.Bar(
+ x=chart_data['وصف البند'],
+ y=chart_data['نسبة التغيير'],
+ name='نسبة التغيير',
+ marker_color=bar_colors,
+ text=[f"{val:.1f}%" for val in chart_data['نسبة التغيير']],
+ textposition='auto'
+ ))
+
+ # إضافة خط أفقي عند الصفر
+ fig.add_shape(
+ type="line",
+ x0=-0.5,
+ y0=0,
+ x1=len(chart_data['وصف البند'])-0.5,
+ y1=0,
+ line=dict(
+ color="black",
+ width=2,
+ dash="dash",
+ )
+ )
+
+ # تحديثات التخطيط
+ fig.update_layout(
+ title='نسبة التغيير في أسعار البنود (%)',
+ xaxis_tickfont_size=14,
+ yaxis=dict(
+ title='نسبة التغيير (%)',
+ titlefont_size=16,
+ tickfont_size=14,
+ gridcolor='rgba(200, 200, 200, 0.2)',
+ zeroline=True,
+ zerolinecolor='black',
+ zerolinewidth=2
+ ),
+ plot_bgcolor='rgba(240, 249, 255, 0.5)',
+ margin=dict(t=50, b=50, l=20, r=20)
+ )
+
+ st.plotly_chart(fig, use_container_width=True)
+
+ # إضافة جدول مع نسب التغيير
+ st.markdown("#### جدول مفصل بنسب التغيير")
+
+ # إعداد بيانات الجدول
+ table_data = chart_data[['وصف البند', 'التسعير المتوازن', 'التسعير غير المتوازن', 'نسبة التغيير']]
+
+ # تنسيق الجدول
+ def highlight_change(row):
+ change = row['نسبة التغيير']
+ if change > 5:
+ return ['', '', '', 'background-color: rgba(31, 122, 140, 0.3); color: #1F7A8C; font-weight: bold;']
+ elif change > 0:
+ return ['', '', '', 'background-color: rgba(129, 178, 154, 0.3); color: #2A9D8F; font-weight: bold;']
+ elif change > -5:
+ return ['', '', '', 'background-color: rgba(242, 204, 143, 0.3); color: #BC6C25; font-weight: bold;']
+ else:
+ return ['', '', '', 'background-color: rgba(224, 122, 95, 0.3); color: #AE2012; font-weight: bold;']
+
+ # تطبيق التنسيق
+ styled_table = table_data.style.apply(highlight_change, axis=1).format({
+ 'التسعير المتوازن': '{:,.2f} ريال',
+ 'التسعير غير المتوازن': '{:,.2f} ريال',
+ 'نسبة التغيير': '{:+.1f}%'
+ })
+
+ st.dataframe(styled_table, use_container_width=True)
+
+ # قسم لإضافة أو حذف البنود
+ st.markdown("### إدارة بنود التسعير")
+
+ # إضافة بند جديد
+ with st.expander("إضافة بند جديد"):
+ col1, col2, col3, col4 = st.columns([3, 1, 1, 1])
+
+ with col1:
+ new_item_desc = st.text_input("وصف البند", placeholder="أدخل وصف البند الجديد")
+
+ with col2:
+ new_item_unit = st.selectbox(
+ "الوحدة",
+ options=["م3", "م2", "متر طولي", "طن", "قطعة", "كجم", "لتر"],
+ index=0,
+ key="construction_item_unit"
+ )
+
+ with col3:
+ new_item_qty = st.number_input("الكمية", min_value=0.1, value=1.0, format="%.2f")
+
+ with col4:
+ new_item_price = st.number_input("سعر الوحدة", min_value=0.1, value=100.0, format="%.2f")
+
+ if st.button("إضافة البند"):
+ if new_item_desc:
+ # إنشاء رقم البند الجديد
+ new_id = f"UB{len(items)+1}"
+
+ # إنشاء صف جديد
+ new_row = pd.DataFrame({
+ 'رقم البند': [new_id],
+ 'وصف البند': [new_item_desc],
+ 'الوحدة': [new_item_unit],
+ 'الكمية': [float(new_item_qty)],
+ 'سعر الوحدة': [float(new_item_price)],
+ 'الإجمالي': [float(new_item_qty * new_item_price)],
+ 'إستراتيجية التسعير': ['متوازن']
+ })
+
+ # إضافة الصف إلى DataFrame
+ items = pd.concat([items, new_row], ignore_index=True)
+
+ # تحديث الإجمالي
+ items['الإجمالي'] = items['الكمية'] * items['سعر الوحدة']
+
+ st.success(f"تم إضافة البند \"{new_item_desc}\" بنجاح!")
+ st.rerun()
+ else:
+ st.warning("يرجى إدخال وصف للبند")
+
+ # حذف بند
+ with st.expander("حذف بند"):
+ if len(items) > 0:
+ # قائمة بالبنود الحالية
+ item_options = [f"{row['رقم البند']} - {row['وصف البند']}" for idx, row in items.iterrows()]
+ selected_item_to_delete = st.selectbox(
+ "اختر البند المراد حذفه",
+ options=item_options,
+ key="item_to_delete_selector"
+ )
+
+ # استخراج رقم البند
+ item_id_to_delete = selected_item_to_delete.split(" - ")[0]
+
+ if st.button("حذف البند", key="delete_item_button_2"):
+ # البحث عن البند وحذفه
+ items = items[items['رقم البند'] != item_id_to_delete]
+ st.success(f"تم حذف البند {item_id_to_delete} بنجاح!")
+ st.rerun()
+ else:
+ st.info("لا توجد بنود لحذفها")
+
+ # أزرار الحفظ والتصدير مع تصميم محسن
+ st.markdown("
", unsafe_allow_html=True)
+ st.markdown("حفظ وتصدير البيانات
", unsafe_allow_html=True)
+
+ st.markdown("""
+
+ """, unsafe_allow_html=True)
+
+ col1, col2 = st.columns(2)
+
+ with col1:
+ # بطاقة حفظ التسعير
+ st.markdown("""
+
+
💾
+
حفظ التسعير غير المتوازن
+
قم بحفظ التسعير الحالي في المشروع لاستخدامه لاحقاً في التقارير وفي إجمالي التسعير.
+
+ """, unsafe_allow_html=True)
+
+ # زر حفظ التسعير غير المتوازن
+ if st.button("حفظ التسعير غير المتوازن", type="primary", use_container_width=True, key="save_unbalanced_pricing_button"):
+ st.session_state.current_pricing['items'] = items.copy()
+ st.session_state.current_pricing['method'] = "التسعير غير المتزن"
+ st.success("تم حفظ التسعير غير المتوازن بنجاح!")
+ st.balloons() # إضافة تأثير احتفالي عند الحفظ
+
+ with col2:
+ # بطاقة تصدير التسعير
+ st.markdown("""
+
+
📊
+
تصدير البيانات
+
قم بتصدير جدول التسعير الحالي بصيغة CSV لاستخدامه في برامج أخرى مثل Excel.
+
+ """, unsafe_allow_html=True)
+
+ # زر تصدير التسعير
+ export_button = st.button("تجهيز ملف للتصدير", use_container_width=True, key="prepare_export_file_button")
+ if export_button:
+ # تحويل البيانات إلى CSV
+ csv = items.to_csv(index=False)
+ st.success("تم تجهيز ملف التصدير بنجاح! يمكنك تنزيله الآن.")
+ # تقديم البيانات للتنزيل
+ st.download_button(
+ label="تنزيل ملف CSV",
+ data=csv,
+ file_name="unbalanced_pricing.csv",
+ mime="text/csv",
+ use_container_width=True
+ )
+
+ def _render_construction_calculator_tab(self):
+ """عرض تبويب حاسبة تكاليف البناء المتكاملة"""
+
+ # استدعاء حاسبة تكاليف البناء المتكاملة الجديدة
+ from .construction_calculator import render_construction_calculator
+
+ st.markdown("### حاسبة تكاليف البناء المتكاملة")
+
+ # شرح حاسبة تكاليف البناء
+ with st.expander("دليل استخدام حاسبة تكاليف البناء", expanded=False):
+ st.markdown("""
+ **حاسبة تكاليف البناء المتكاملة** هي أداة تساعد في حساب تكاليف البناء بشكل تفصيلي، مع مراعاة جميع عناصر التكلفة:
+
+ ### مكونات التكلفة:
+ - المواد الخام
+ - العمالة
+ - المعدات
+ - المصاريف الإدارية
+ - هامش الربح
+
+ ### كيفية الاستخدام:
+ 1. اختر إما حساب تكلفة بند واحد أو حساب تكلفة مشروع.
+ 2. أدخل بيانات المواد والعمالة والمعدات.
+ 3. حدد نسب المصاريف الإدارية وهامش الربح.
+ 4. اضبط عوامل التعديل حسب ظروف المشروع.
+ 5. احصل على تحليل تفصيلي للتكاليف والسعر النهائي.
+
+ ### مميزات الحاسبة:
+ - قاعدة بيانات مدمجة للأسعار المرجعية للمواد والعمالة.
+ - تحليل نسب مساهمة كل عنصر في التكلفة.
+ - إمكانية تعديل عوامل التكلفة حسب الموقع والظروف.
+ - تصدير النتائج بتنسيقات متعددة.
+ - الربط مع وحدة التسعير لنقل النتائج مباشرة إلى جدول البنود.
+ - قاعدة بيانات للبنود النموذجية في أعمال المقاولات.
+ """)
+
+ render_construction_calculator()
+
+ # شرح حاسبة تكاليف البناء
+ with st.expander("دليل استخدام حاسبة تكاليف البناء", expanded=False):
+ st.markdown("""
+ **حاسبة تكاليف البناء المتكاملة** هي أداة تساعد في حساب تكاليف البناء بشكل تفصيلي، مع مراعاة جميع عناصر التكلفة:
+
+ ### مكونات التكلفة:
+
+ 1. **المواد الخام**: جميع المواد المستخدمة في البناء مثل الخرسانة، الحديد، الطوب، الأسمنت، وغيرها.
+ 2. **العمالة**: تكاليف جميع العمالة المطلوبة بمختلف تخصصاتها.
+ 3. **المعدات**: تكاليف استخدام أو استئجار المعدات اللازمة للمشروع.
+ 4. **المصاريف الإدارية**: النفقات العامة والإدارية للمشروع (نسبة من التكلفة المباشرة).
+ 5. **هامش الربح**: نسبة الربح المضافة على التكلفة.
+
+ ### طريقة الاستخدام:
+
+ 1. اختر إما حساب تكلفة بند واحد أو حساب تكلفة مشروع كامل.
+ 2. أدخل بيانات المواد والعمالة والمعدات المستخدمة.
+ 3. حدد نسب المصاريف الإدارية وهامش الربح.
+ 4. اضبط عوامل التعديل حسب ظروف المشروع.
+ 5. احصل على تحليل تفصيلي للتكاليف والسعر النهائي.
+
+ ### ميزات الحاسبة:
+
+ - قاعدة بيانات مدمجة للأسعار المرجعية للمواد والعمالة والمعدات.
+ - تحليل نسب مساهمة كل عنصر في التكلفة الإجمالية.
+ - إمكانية تعديل عوامل التكلفة حسب الموقع والظروف السوقية.
+ - تصدير النتائج بتنسيقات مختلفة.
+ - الربط مع وحدة التسعير لنقل النتائج مباشرة إلى جدول التسعير.
+ - قاعدة بيانات للبنود النموذجية في أعمال المقاولات (مناهل، مواسير، إسفلت، إلخ).
+ """)
+
+ # تبويبات حاسبة التكاليف
+ calc_tabs = st.tabs(["حساب تكلفة بند", "حساب تكلفة مشروع", "قوائم الأسعار المرجعية", "كتالوج أعمال المقاولات"])
+
+ with calc_tabs[0]: # حساب تكلفة بند
+ st.markdown("#### حساب تكلفة بند بناء")
+
+ # تهيئة بيانات البند الافتراضية إذا لم تكن موجودة
+ if 'construction_item' not in st.session_state:
+ st.session_state.construction_item = {
+ 'وصف_البند': "توريد وصب خرسانة مسلحة للأساسات",
+ 'الكمية': 25.0,
+ 'الوحدة': "م3",
+ 'المواد': [
+ {'الاسم': 'خرسانة جاهزة', 'الكمية': 25.0, 'الوحدة': 'م3', 'سعر_الوحدة': 750.0},
+ {'الاسم': 'حديد تسليح', 'الكمية': 3.5, 'الوحدة': 'طن', 'سعر_الوحدة': 5500.0}
+ ],
+ 'العمالة': [
+ {'النوع': 'عامل خرسانة', 'العدد': 6, 'المدة': 1.0, 'سعر_اليوم': 150.0},
+ {'النوع': 'حداد مسلح', 'العدد': 4, 'المدة': 3.0, 'سعر_اليوم': 250.0},
+ {'النوع': 'نجار مسلح', 'العدد': 4, 'المدة': 3.0, 'سعر_اليوم': 250.0}
+ ],
+ 'المعدات': [
+ {'النوع': 'مضخة خرسانة', 'العدد': 1.0, 'المدة': 0.5, 'سعر_اليوم': 5000.0},
+ {'النوع': 'هزاز خرسانة', 'العدد': 2.0, 'المدة': 1.0, 'سعر_اليوم': 150.0}
+ ],
+ 'المصاريف_الإدارية': 0.05, # 5%
+ 'هامش_الربح': 0.10, # 10%
+ 'عوامل_التعديل': {
+ 'location_factor': 1.0,
+ 'time_factor': 1.0,
+ 'risk_factor': 1.0,
+ 'market_factor': 1.0
+ }
+ }
+
+ # نموذج إدخال بيانات البند
+ col1, col2 = st.columns(2)
+
+ with col1:
+ st.session_state.construction_item['وصف_البند'] = st.text_area(
+ "وصف البند",
+ value=st.session_state.construction_item['وصف_البند'],
+ key="construction_item_description"
+ )
+
+ with col2:
+ st.session_state.construction_item['الكمية'] = st.number_input(
+ "الكمية",
+ value=st.session_state.construction_item['الكمية'],
+ min_value=0.1,
+ format="%.2f"
+ )
+
+ unit_options = ["م3", "م2", "طن", "متر طولي", "قطعة", "كجم", "لتر"]
+ st.session_state.construction_item['الوحدة'] = st.selectbox(
+ "الوحدة",
+ options=unit_options,
+ index=unit_options.index(st.session_state.construction_item['الوحدة']) if st.session_state.construction_item['الوحدة'] in unit_options else 0,
+ key="construction_item_unit_2"
+ )
+
+ # إدخال تفاصيل المواد
+ st.markdown("#### تفاصيل المواد")
+
+ material_controls = []
+ for i, material in enumerate(st.session_state.construction_item['المواد']):
+ col1, col2, col3, col4, col5 = st.columns([3, 2, 1, 2, 1])
+
+ with col1:
+ material_name = st.text_input(
+ "اسم المادة",
+ value=material['الاسم'],
+ key=f"material_name_{i}"
+ )
+
+ with col2:
+ material_qty = st.number_input(
+ "الكمية",
+ value=material['الكمية'],
+ min_value=0.0,
+ format="%.2f",
+ key=f"material_qty_{i}"
+ )
+
+ with col3:
+ material_unit = st.selectbox(
+ "الوحدة",
+ options=unit_options,
+ index=unit_options.index(material['الوحدة']) if material['الوحدة'] in unit_options else 0,
+ key=f"material_unit_{i}"
+ )
+
+ with col4:
+ material_price = st.number_input(
+ "سعر الوحدة",
+ value=material['سعر_الوحدة'],
+ min_value=0.0,
+ format="%.2f",
+ key=f"material_price_{i}"
+ )
+
+ with col5:
+ delete_button = st.button("حذف", key=f"delete_material_{i}")
+
+ material_controls.append({
+ 'الاسم': material_name,
+ 'الكمية': material_qty,
+ 'الوحدة': material_unit,
+ 'سعر_الوحدة': material_price,
+ 'delete': delete_button
+ })
+
+ # إضافة مادة جديدة
+ if st.button("إضافة مادة جديدة"):
+ st.session_state.construction_item['المواد'].append({
+ 'الاسم': '',
+ 'الكمية': 0.0,
+ 'الوحدة': 'م3',
+ 'سعر_الوحدة': 0.0
+ })
+ st.rerun()
+
+ # تحديث أو حذف المواد
+ new_materials = []
+ for i, control in enumerate(material_controls):
+ if not control['delete']:
+ new_materials.append({
+ 'الاسم': control['الاسم'],
+ 'الكمية': control['الكمية'],
+ 'الوحدة': control['الوحدة'],
+ 'سعر_الوحدة': control['سعر_الوحدة']
+ })
+
+ if len(new_materials) != len(st.session_state.construction_item['المواد']):
+ st.session_state.construction_item['المواد'] = new_materials
+ st.rerun()
+ else:
+ for i, material in enumerate(new_materials):
+ st.session_state.construction_item['المواد'][i] = material
+
+ # إدخال تفاصيل العمالة
+ st.markdown("#### تفاصيل العمالة")
+
+ labor_controls = []
+ for i, labor in enumerate(st.session_state.construction_item['العمالة']):
+ col1, col2, col3, col4, col5 = st.columns([3, 1, 1, 2, 1])
+
+ with col1:
+ labor_type = st.text_input(
+ "نوع العامل",
+ value=labor['النوع'],
+ key=f"labor_type_{i}"
+ )
+
+ with col2:
+ labor_count = st.number_input(
+ "العدد",
+ value=float(labor['العدد']),
+ min_value=1.0,
+ step=1.0,
+ key=f"labor_count_{i}"
+ )
+
+ with col3:
+ labor_days = st.number_input(
+ "المدة (أيام)",
+ value=labor['المدة'],
+ min_value=0.1,
+ format="%.1f",
+ key=f"labor_days_{i}"
+ )
+
+ with col4:
+ labor_daily_rate = st.number_input(
+ "سعر اليوم",
+ value=labor['سعر_اليوم'],
+ min_value=0.0,
+ format="%.2f",
+ key=f"labor_daily_rate_{i}"
+ )
+
+ with col5:
+ delete_button = st.button("حذف", key=f"delete_labor_{i}")
+
+ labor_controls.append({
+ 'النوع': labor_type,
+ 'العدد': labor_count,
+ 'المدة': labor_days,
+ 'سعر_اليوم': labor_daily_rate,
+ 'delete': delete_button
+ })
+
+ # إضافة عامل جديد
+ if st.button("إضافة عامل جديد"):
+ st.session_state.construction_item['العمالة'].append({
+ 'النوع': '',
+ 'العدد': 1.0,
+ 'المدة': 1.0,
+ 'سعر_اليوم': 0.0
+ })
+ st.rerun()
+
+ # تحديث أو حذف العمالة
+ new_labor = []
+ for i, control in enumerate(labor_controls):
+ if not control['delete']:
+ new_labor.append({
+ 'النوع': control['النوع'],
+ 'العدد': control['العدد'],
+ 'المدة': control['المدة'],
+ 'سعر_اليوم': control['سعر_اليوم']
+ })
+
+ if len(new_labor) != len(st.session_state.construction_item['العمالة']):
+ st.session_state.construction_item['العمالة'] = new_labor
+ st.rerun()
+ else:
+ for i, labor in enumerate(new_labor):
+ st.session_state.construction_item['العمالة'][i] = labor
+
+ # إدخال تفاصيل المعدات
+ st.markdown("#### تفاصيل المعدات")
+
+ equipment_controls = []
+ for i, equipment in enumerate(st.session_state.construction_item['المعدات']):
+ col1, col2, col3, col4, col5 = st.columns([3, 1, 1, 2, 1])
+
+ with col1:
+ equip_type = st.text_input(
+ "نوع المعدة",
+ value=equipment['النوع'],
+ key=f"equip_type_{i}"
+ )
+
+ with col2:
+ equip_count = st.number_input(
+ "العدد",
+ value=float(equipment['العدد']),
+ min_value=1.0,
+ step=1.0,
+ key=f"equip_count_{i}"
+ )
+
+ with col3:
+ equip_days = st.number_input(
+ "المدة (أيام)",
+ value=equipment['المدة'],
+ min_value=0.1,
+ format="%.1f",
+ key=f"equip_days_{i}"
+ )
+
+ with col4:
+ equip_daily_rate = st.number_input(
+ "سعر اليوم",
+ value=equipment['سعر_اليوم'],
+ min_value=0.0,
+ format="%.2f",
+ key=f"equip_daily_rate_{i}"
+ )
+
+ with col5:
+ delete_button = st.button("حذف", key=f"delete_equipment_{i}")
+
+ equipment_controls.append({
+ 'النوع': equip_type,
+ 'العدد': equip_count,
+ 'المدة': equip_days,
+ 'سعر_اليوم': equip_daily_rate,
+ 'delete': delete_button
+ })
+
+ # إضافة معدة جديدة
+ if st.button("إضافة معدة جديدة"):
+ st.session_state.construction_item['المعدات'].append({
+ 'النوع': '',
+ 'العدد': 1.0,
+ 'المدة': 1.0,
+ 'سعر_اليوم': 0.0
+ })
+ st.rerun()
+
+ # تحديث أو حذف المعدات
+ new_equipment = []
+ for i, control in enumerate(equipment_controls):
+ if not control['delete']:
+ new_equipment.append({
+ 'النوع': control['النوع'],
+ 'العدد': control['العدد'],
+ 'المدة': control['المدة'],
+ 'سعر_اليوم': control['سعر_اليوم']
+ })
+
+ if len(new_equipment) != len(st.session_state.construction_item['المعدات']):
+ st.session_state.construction_item['المعدات'] = new_equipment
+ st.rerun()
+ else:
+ for i, equipment in enumerate(new_equipment):
+ st.session_state.construction_item['المعدات'][i] = equipment
+
+ # النسب والعوامل
+ st.markdown("#### المصاريف الإدارية وهامش الربح")
+
+ col1, col2 = st.columns(2)
+
+ with col1:
+ st.session_state.construction_item['المصاريف_الإدارية'] = st.slider(
+ "نسبة المصاريف الإدارية (%)",
+ min_value=0.0,
+ max_value=20.0,
+ value=st.session_state.construction_item['المصاريف_الإدارية'] * 100,
+ step=0.5
+ ) / 100
+
+ with col2:
+ st.session_state.construction_item['هامش_الربح'] = st.slider(
+ "نسبة هامش الربح (%)",
+ min_value=0.0,
+ max_value=30.0,
+ value=st.session_state.construction_item['هامش_الربح'] * 100,
+ step=0.5
+ ) / 100
+
+ # عوامل التعديل
+ st.markdown("#### عوامل تعديل التكلفة")
+
+ col1, col2 = st.columns(2)
+
+ with col1:
+ st.session_state.construction_item['عوامل_التعديل']['location_factor'] = st.slider(
+ "معامل الموقع",
+ min_value=0.5,
+ max_value=2.0,
+ value=st.session_state.construction_item['عوامل_التعديل']['location_factor'],
+ step=0.05,
+ help="يؤثر في التكلفة حسب صعوبة أو سهولة الوصول للموقع وتوفر الخدمات"
+ )
+
+ st.session_state.construction_item['عوامل_التعديل']['time_factor'] = st.slider(
+ "معامل الوقت",
+ min_value=0.8,
+ max_value=1.5,
+ value=st.session_state.construction_item['عوامل_التعديل']['time_factor'],
+ step=0.05,
+ help="يؤثر في التكلفة حسب الجدول الزمني للمشروع وضرورة الإسراع في التنفيذ"
+ )
+
+ with col2:
+ st.session_state.construction_item['عوامل_التعديل']['risk_factor'] = st.slider(
+ "معامل المخاطر",
+ min_value=1.0,
+ max_value=1.5,
+ value=st.session_state.construction_item['عوامل_التعديل']['risk_factor'],
+ step=0.05,
+ help="يؤثر في التكلفة حسب المخاطر المتوقعة في المشروع"
+ )
+
+ st.session_state.construction_item['عوامل_التعديل']['market_factor'] = st.slider(
+ "معامل السوق",
+ min_value=0.8,
+ max_value=1.3,
+ value=st.session_state.construction_item['عوامل_التعديل']['market_factor'],
+ step=0.05,
+ help="يؤثر في التكلفة حسب حالة السوق الحالية وتغيرات الأسعار"
+ )
+
+ # صف أزرار العمليات
+ col1, col2 = st.columns(2)
+
+ with col1:
+ # زر حساب التكلفة
+ if st.button("حساب تكلفة البند", type="primary"):
+ with st.spinner("جاري حساب التكلفة..."):
+ # حساب التكلفة باستخدام الحاسبة
+ item_cost = self.construction_calculator.calculate_item_cost(st.session_state.construction_item)
+ st.session_state.item_cost_result = item_cost
+
+ with col2:
+ # زر استيراد بند من كتالوج أعمال المقاولات
+ if st.button("استيراد بند من الكتالوج"):
+ # تهيئة session state للاختيار من الكتالوج
+ if 'show_catalog_selection' not in st.session_state:
+ st.session_state.show_catalog_selection = True
+ else:
+ st.session_state.show_catalog_selection = True
+ st.rerun()
+
+ # عرض واجهة اختيار البند من الكتالوج
+ if 'show_catalog_selection' in st.session_state and st.session_state.show_catalog_selection:
+ st.markdown("#### اختيار بند من كتالوج أعمال المقاولات")
+
+ # الحصول على فئات البنود
+ categories = self.construction_templates.get_all_templates()['categories']
+ category_options = [f"{cat_data['name']}" for cat_id, cat_data in categories.items()]
+ category_ids = list(categories.keys())
+
+ selected_category_index = st.selectbox(
+ "اختر فئة البند",
+ options=range(len(category_options)),
+ format_func=lambda i: category_options[i],
+ key="construction_category_selector"
+ )
+
+ selected_category_id = category_ids[selected_category_index]
+
+ # الحصول على البنود في الفئة المحددة
+ templates = self.construction_templates.get_templates_by_category(selected_category_id)
+
+ if templates:
+ template_options = [f"{template['name']}" for template in templates]
+
+ selected_template_index = st.selectbox(
+ "اختر البند",
+ options=range(len(template_options)),
+ format_func=lambda i: template_options[i],
+ key="construction_template_selector"
+ )
+
+ selected_template = templates[selected_template_index]
+
+ # عرض تفاصيل البند المحدد
+ st.markdown(f"**وصف البند**: {selected_template['description']}")
+ st.markdown(f"**الوحدة**: {selected_template['unit']}")
+
+ if st.button("استخدام هذا البند", type="primary"):
+ # تحويل البند إلى صيغة الحاسبة
+ template_id = selected_template['id']
+ construction_item = self.construction_templates.convert_template_to_item(template_id)
+
+ # تحديث بيانات البند في session state
+ st.session_state.construction_item = construction_item
+ st.session_state.show_catalog_selection = False
+ st.rerun()
+ else:
+ st.info("لا توجد بنود في هذه الفئة")
+
+ if st.button("إلغاء"):
+ st.session_state.show_catalog_selection = False
+ st.rerun()
+
+ # عرض نتائج الحساب
+ if 'item_cost_result' in st.session_state:
+ st.markdown("### نتائج حساب تكلفة البند")
+
+ result = st.session_state.item_cost_result
+
+ # ملخص البند
+ st.markdown(f"**البند:** {result['وصف_البند']}")
+ st.markdown(f"**الكمية:** {result['الكمية']} {result['الوحدة']}")
+
+ # المكونات الرئيسية للتكلفة
+ col1, col2, col3, col4 = st.columns(4)
+
+ with col1:
+ st.metric(
+ "تكلفة المواد",
+ f"{result['تكاليف_مباشرة']['المواد']['الإجمالي']:,.2f} ريال"
+ )
+
+ with col2:
+ st.metric(
+ "تكلفة العمالة",
+ f"{result['تكاليف_مباشرة']['العمالة']['الإجمالي']:,.2f} ريال"
+ )
+
+ with col3:
+ st.metric(
+ "تكلفة المعدات",
+ f"{result['تكاليف_مباشرة']['المعدات']['الإجمالي']:,.2f} ريال"
+ )
+
+ with col4:
+ st.metric(
+ "التكلفة المباشرة",
+ f"{result['تكاليف_مباشرة']['إجمالي_تكاليف_مباشرة']:,.2f} ريال"
+ )
+
+ # تفاصيل المصاريف والربح والسعر النهائي
+ col1, col2, col3 = st.columns(3)
+
+ with col1:
+ st.metric(
+ f"المصاريف الإدارية ({result['مصاريف_إدارية']['نسبة']}%)",
+ f"{result['مصاريف_إدارية']['قيمة']:,.2f} ريال"
+ )
+
+ with col2:
+ st.metric(
+ f"هامش الربح ({result['هامش_ربح']['نسبة']}%)",
+ f"{result['هامش_ربح']['قيمة']:,.2f} ريال"
+ )
+
+ with col3:
+ st.metric(
+ "التكلفة الإجمالية",
+ f"{result['التكلفة_الإجمالية']:,.2f} ريال"
+ )
+
+ # التكلفة بعد تطبيق عوامل التعديل
+ adjustment_factor = result['عوامل_التعديل']['المعامل_الإجمالي']
+ st.metric(
+ f"السعر النهائي المعدل (معامل التعديل: {adjustment_factor:.2f})",
+ f"{result['السعر_المعدل']['إجمالي']:,.2f} ريال",
+ delta=f"{(adjustment_factor - 1) * 100:.1f}%"
+ )
+
+ # سعر الوحدة وزر نقل السعر إلى جدول التسعير
+ col1, col2 = st.columns([2, 1])
+
+ with col1:
+ st.metric(
+ f"سعر الوحدة ({result['الوحدة']})",
+ f"{result['السعر_المعدل']['سعر_الوحدة']:,.2f} ريال"
+ )
+
+ with col2:
+ if st.button("نقل السعر إلى جدول التسعير", key="transfer_to_pricing"):
+ if 'manual_items' not in st.session_state:
+ st.session_state.manual_items = pd.DataFrame(columns=[
+ 'رقم البند', 'وصف البند', 'الوحدة', 'الكمية', 'سعر الوحدة', 'الإجمالي'
+ ])
+
+ # إنشاء رقم البند الجديد
+ new_id = f"B{len(st.session_state.manual_items)+1}"
+
+ # إنشاء صف جديد
+ new_row = pd.DataFrame({
+ 'رقم البند': [new_id],
+ 'وصف البند': [result['وصف_البند']],
+ 'الوحدة': [result['الوحدة']],
+ 'الكمية': [float(result['الكمية'])],
+ 'سعر الوحدة': [float(result['السعر_المعدل']['سعر_الوحدة'])],
+ 'الإجمالي': [float(result['السعر_المعدل']['إجمالي'])]
+ })
+
+ # إضافة الصف إلى DataFrame
+ st.session_state.manual_items = pd.concat([st.session_state.manual_items, new_row], ignore_index=True)
+
+ # إنشاء حالة التسعير الحالي إذا لم تكن موجودة
+ if 'current_pricing' not in st.session_state:
+ st.session_state.current_pricing = {
+ 'name': 'تسعير جديد',
+ 'method': 'تسعير مستورد من حاسبة تكاليف البناء',
+ 'items': st.session_state.manual_items
+ }
+ else:
+ # تحديث البنود في التسعير الحالي
+ st.session_state.current_pricing['items'] = st.session_state.manual_items
+
+ st.success(f"تم نقل البند \"{result['وصف_البند']}\" إلى جدول التسعير بنجاح!")
+
+ # تفاصيل التكلفة - المواد
+ st.markdown("#### تفاصيل تكلفة المواد")
+ if len(result['تكاليف_مباشرة']['المواد']['التفاصيل']) > 0:
+ materials_df = pd.DataFrame(result['تكاليف_مباشرة']['المواد']['التفاصيل'])
+ st.dataframe(materials_df, use_container_width=True, hide_index=True)
+ else:
+ st.info("لا توجد مواد مضافة")
+
+ # تفاصيل ال��كلفة - العمالة
+ st.markdown("#### تفاصيل تكلفة العمالة")
+ if len(result['تكاليف_مباشرة']['العمالة']['التفاصيل']) > 0:
+ labor_df = pd.DataFrame(result['تكاليف_مباشرة']['العمالة']['التفاصيل'])
+ st.dataframe(labor_df, use_container_width=True, hide_index=True)
+ else:
+ st.info("لا توجد عمالة مضافة")
+
+ # تفاصيل التكلفة - المعدات
+ st.markdown("#### تفاصيل تكلفة المعدات")
+ if len(result['تكاليف_مباشرة']['المعدات']['التفاصيل']) > 0:
+ equipment_df = pd.DataFrame(result['تكاليف_مباشرة']['المعدات']['التفاصيل'])
+ st.dataframe(equipment_df, use_container_width=True, hide_index=True)
+ else:
+ st.info("لا توجد معدات مضافة")
+
+ # رسم بياني لتوزيع التكلفة
+ st.markdown("#### توزيع مكونات التكلفة")
+
+ cost_components = {
+ 'المواد': result['تكاليف_مباشرة']['المواد']['الإجمالي'],
+ 'العمالة': result['تكاليف_مباشرة']['العمالة']['الإجمالي'],
+ 'المعدات': result['تكاليف_مباشرة']['المعدات']['الإجمالي'],
+ 'المصاريف الإدارية': result['مصاريف_إدارية']['قيمة'],
+ 'هامش الربح': result['هامش_ربح']['قيمة']
+ }
+
+ colors = ['#2E86C1', '#28B463', '#EB984E', '#8E44AD', '#C0392B']
+
+ fig = px.pie(
+ values=list(cost_components.values()),
+ names=list(cost_components.keys()),
+ title='توزيع مكونات التكلفة',
+ color_discrete_sequence=colors
+ )
+
+ st.plotly_chart(fig, use_container_width=True)
+
+ with calc_tabs[1]: # حساب تكلفة مشروع
+ st.markdown("#### حساب تكلفة مشروع كامل")
+
+ # تهيئة بيانات المشروع الافتراضية إذا لم تكن موجودة
+ if 'construction_project' not in st.session_state:
+ st.session_state.construction_project = self.construction_calculator.generate_sample_project_data()
+
+ # نموذج إدخال بيانات المشروع
+ col1, col2 = st.columns(2)
+
+ with col1:
+ st.session_state.construction_project['اسم_المشروع'] = st.text_input(
+ "اسم المشروع",
+ value=st.session_state.construction_project['اسم_المشروع']
+ )
+
+ with col2:
+ st.session_state.construction_project['وصف_المشروع'] = st.text_area(
+ "وصف المشروع",
+ value=st.session_state.construction_project['وصف_المشروع'],
+ height=100,
+ key="construction_project_description"
+ )
+
+ # النسب والعوامل الإجمالية للمشروع
+ st.markdown("#### النسب والعوامل الإجمالية للمشروع")
+
+ col1, col2 = st.columns(2)
+
+ with col1:
+ st.session_state.construction_project['المصاريف_الإدارية'] = st.slider(
+ "نسبة المصاريف الإدارية الإجمالية (%)",
+ min_value=0.0,
+ max_value=20.0,
+ value=st.session_state.construction_project['المصاريف_الإدارية'] * 100,
+ step=0.5,
+ key="project_admin_expenses"
+ ) / 100
+
+ with col2:
+ st.session_state.construction_project['هامش_الربح'] = st.slider(
+ "نسبة هامش الربح الإجمالي (%)",
+ min_value=0.0,
+ max_value=30.0,
+ value=st.session_state.construction_project['هامش_الربح'] * 100,
+ step=0.5,
+ key="project_profit_margin"
+ ) / 100
+
+ # عوامل التعديل للمشروع
+ st.markdown("#### عوامل تعديل التكلفة للمشروع")
+
+ col1, col2 = st.columns(2)
+
+ with col1:
+ st.session_state.construction_project['عوامل_التع��يل']['location_factor'] = st.slider(
+ "معامل الموقع",
+ min_value=0.5,
+ max_value=2.0,
+ value=st.session_state.construction_project['عوامل_التعديل']['location_factor'],
+ step=0.05,
+ help="يؤثر في التكلفة حسب صعوبة أو سهولة الوصول للموقع وتوفر الخدمات",
+ key="project_location_factor"
+ )
+
+ st.session_state.construction_project['عوامل_التعديل']['time_factor'] = st.slider(
+ "معامل الوقت",
+ min_value=0.8,
+ max_value=1.5,
+ value=st.session_state.construction_project['عوامل_التعديل']['time_factor'],
+ step=0.05,
+ help="يؤثر في التكلفة حسب الجدول الزمني للمشروع وضرورة الإسراع في التنفيذ",
+ key="project_time_factor"
+ )
+
+ with col2:
+ st.session_state.construction_project['عوامل_التعديل']['risk_factor'] = st.slider(
+ "معامل المخاطر",
+ min_value=1.0,
+ max_value=1.5,
+ value=st.session_state.construction_project['عوامل_التعديل']['risk_factor'],
+ step=0.05,
+ help="يؤثر في التكلفة حسب المخاطر المتوقعة في المشروع",
+ key="project_risk_factor"
+ )
+
+ st.session_state.construction_project['عوامل_التعديل']['market_factor'] = st.slider(
+ "معامل السوق",
+ min_value=0.8,
+ max_value=1.3,
+ value=st.session_state.construction_project['عوامل_التعديل']['market_factor'],
+ step=0.05,
+ help="يؤثر في التكلفة حسب حالة السوق الحالية وتغيرات الأسعار",
+ key="project_market_factor"
+ )
+
+ # زر حساب تكلفة المشروع
+ if st.button("حساب تكلفة المشروع", type="primary"):
+ with st.spinner("جاري حساب تكلفة المشروع..."):
+ # حساب التكلفة باستخدام الحاسبة
+ project_cost = self.construction_calculator.calculate_project_cost(st.session_state.construction_project)
+ st.session_state.project_cost_result = project_cost
+
+ # عرض نتائج حساب المشروع
+ if 'project_cost_result' in st.session_state:
+ st.markdown("### نتائج حساب تكلفة المشروع")
+
+ result = st.session_state.project_cost_result
+
+ # ملخص المشروع
+ st.markdown(f"**المشروع:** {result['اسم_المشروع']}")
+ st.markdown(f"**الوصف:** {result['وصف_المشروع']}")
+ st.markdown(f"**عدد البنود:** {result['عدد_البنود']} بند")
+
+ # المكونات الرئيسية للتكلفة
+ col1, col2, col3 = st.columns(3)
+
+ with col1:
+ st.metric(
+ "إجمالي تكلفة المواد",
+ f"{result['تكاليف_مباشرة']['المواد']['الإجمالي']:,.2f} ريال",
+ delta=f"{result['تكاليف_مباشرة']['المواد']['النسبة_المئوية']:.1f}% من التكلفة المباشرة"
+ )
+
+ with col2:
+ st.metric(
+ "إجمالي تكلفة العمالة",
+ f"{result['تكاليف_مباشرة']['العمالة']['الإجمالي']:,.2f} ريال",
+ delta=f"{result['تكاليف_مباشرة']['العمالة']['النسبة_المئوية']:.1f}% من التكلفة المباشرة"
+ )
+
+ with col3:
+ st.metric(
+ "إجمالي تكلفة المعدات",
+ f"{result['تكاليف_مباشرة']['المعدات']['الإجمالي']:,.2f} ريال",
+ delta=f"{result['تكاليف_مباشرة']['المعدات']['النسبة_المئوية']:.1f}% من التكلفة المباشرة"
+ )
+
+ # إجمالي التكاليف المباشرة
+ st.metric(
+ "إجمالي التكاليف المباشرة",
+ f"{result['تكاليف_مباشرة']['إجمالي_تكاليف_مباشرة']:,.2f} ريال"
+ )
+
+ # تفاصيل المصاريف والربح والسعر النهائي
+ col1, col2, col3 = st.columns(3)
+
+ with col1:
+ st.metric(
+ f"المصاريف الإدارية ({result['مصاريف_إدارية']['نسبة']}%)",
+ f"{result['مصاريف_إدارية']['قيمة']:,.2f} ريال"
+ )
+
+ with col2:
+ st.metric(
+ f"هامش الربح ({result['هامش_ربح']['نسبة']}%)",
+ f"{result['هامش_ربح']['قيمة']:,.2f} ريال"
+ )
+
+ with col3:
+ st.metric(
+ "التكلفة الإجمالية",
+ f"{result['التكلفة_الإجمالية']:,.2f} ريال"
+ )
+
+ # التكلفة بعد تطبيق عوامل التعديل
+ adjustment_factor = result['عوامل_التعديل']['المعامل_الإجمالي']
+ st.metric(
+ f"السعر النهائي المعدل (معامل التعديل: {adjustment_factor:.2f})",
+ f"{result['التكلفة_النهائية_المعدلة']:,.2f} ريال",
+ delta=f"{(adjustment_factor - 1) * 100:.1f}%"
+ )
+
+ # رسم بياني لتوزيع التكلفة
+ st.markdown("#### توزيع مكونات التكلفة")
+
+ cost_components = {
+ 'المواد': result['تكاليف_مباشرة']['المواد']['الإجمالي'],
+ 'العمالة': result['تكاليف_مباشرة']['العمالة']['الإجمالي'],
+ 'المعدات': result['تكاليف_مباشرة']['المعدات']['الإجمالي'],
+ 'المصاريف الإدارية': result['مصاريف_إدارية']['قيمة'],
+ 'هامش الربح': result['هامش_ربح']['قيمة']
+ }
+
+ colors = ['#2E86C1', '#28B463', '#EB984E', '#8E44AD', '#C0392B']
+
+ fig = px.pie(
+ values=list(cost_components.values()),
+ names=list(cost_components.keys()),
+ title='توزيع مكونات التكلفة',
+ color_discrete_sequence=colors
+ )
+
+ st.plotly_chart(fig, use_container_width=True)
+
+ # جدول تفاصيل البنود
+ st.markdown("#### تفاصيل بنود المشروع")
+
+ items_data = []
+ for i, item in enumerate(result['تفاصيل_البنود']):
+ items_data.append({
+ 'رقم': i + 1,
+ 'الوصف': item['وصف_البند'],
+ 'الكمية': item['الكمية'],
+ 'الوحدة': item['الوحدة'],
+ 'سعر الوحدة': item['سعر_الوحدة'],
+ 'الإجمالي': item['التكلفة_الإجمالية'],
+ 'بعد التعديل': item['السعر_المعدل']['إجمالي']
+ })
+
+ if len(items_data) > 0:
+ items_df = pd.DataFrame(items_data)
+
+ # تنسيق الجدول لعرض الأرقام بشكل أفضل
+ def highlight_row(row):
+ """تنسيق الجدول مع تمييز الصفوف بالتناوب"""
+ color = '#F0F8FF' if row.name % 2 == 0 else 'white'
+ return ['background-color: %s' % color] * len(row)
+
+ styled_df = items_df.style.apply(highlight_row, axis=1)
+ styled_df = styled_df.format({
+ 'الكمية': '{:,.2f}',
+ 'سعر الوحدة': '{:,.2f}',
+ 'الإجمالي': '{:,.2f}',
+ 'بعد التعديل': '{:,.2f}'
+ })
+
+ st.dataframe(styled_df, use_container_width=True)
+ else:
+ st.info("لا توجد بنود في المشروع")
+
+ with calc_tabs[2]: # قوائم الأسعار المرجعية
+ st.markdown("#### قوائم الأسعار المرجعية")
+
+ ref_tabs = st.tabs(["قائمة المواد", "قائمة العم��لة", "قائمة المعدات"])
+
+ with ref_tabs[0]: # قائمة المواد
+ st.markdown("#### قائمة أسعار المواد المرجعية")
+
+ # الحصول على قائمة المواد
+ materials = self.construction_calculator.get_all_rates(item_type='مادة')
+
+ if materials and 'المواد' in materials:
+ # تحويل قاموس المواد إلى DataFrame
+ materials_list = []
+ for name, data in materials['المواد'].items():
+ materials_list.append({
+ 'اسم المادة': name,
+ 'الوحدة': data.get('وحدة', ''),
+ 'سعر الوحدة': data.get('سعر_الوحدة', 0.0),
+ 'الوصف': data.get('وصف', ''),
+ 'الفئة': data.get('فئة', '')
+ })
+
+ if materials_list:
+ materials_df = pd.DataFrame(materials_list)
+
+ # تصفية حسب الفئة
+ categories = ['الكل'] + sorted(materials_df['الفئة'].unique().tolist())
+ selected_category = st.selectbox("تصفية حسب الفئة", categories, key="materials_cat_filter_section1")
+
+ if selected_category != 'الكل':
+ filtered_df = materials_df[materials_df['الفئة'] == selected_category]
+ else:
+ filtered_df = materials_df
+
+ # تنسيق الجدول
+ def highlight_materials_row(row):
+ """تنسيق الجدول مع تمييز الصفوف بالتناوب"""
+ color = '#F0F8FF' if row.name % 2 == 0 else 'white'
+ return ['background-color: %s' % color] * len(row)
+
+ styled_df = filtered_df.style.apply(highlight_materials_row, axis=1)
+ styled_df = styled_df.format({
+ 'سعر الوحدة': '{:,.2f}'
+ })
+
+ st.dataframe(styled_df, use_container_width=True, hide_index=True)
+ else:
+ st.info("لا توجد مواد في قاعدة البيانات")
+ else:
+ st.info("لا توجد قائمة مواد متاحة")
+
+ with ref_tabs[1]: # قائمة العمالة
+ st.markdown("#### قائمة أسعار العمالة المرجعية")
+
+ # الحصول على قائمة العمالة
+ labor = self.construction_calculator.get_all_rates(item_type='عمالة')
+
+ if labor and 'العمالة' in labor:
+ # تحويل قاموس العمالة إلى DataFrame
+ labor_list = []
+ for name, data in labor['العمالة'].items():
+ labor_list.append({
+ 'نوع العامل': name,
+ 'وحدة الأجر': data.get('وحدة', ''),
+ 'سعر الوحدة': data.get('سعر_الوحدة', 0.0),
+ 'الوصف': data.get('وصف', ''),
+ 'الفئة': data.get('فئة', '')
+ })
+
+ if labor_list:
+ labor_df = pd.DataFrame(labor_list)
+
+ # تصفية حسب الفئة
+ categories = ['الكل'] + sorted(labor_df['الفئة'].unique().tolist())
+ selected_category = st.selectbox("تصفية حسب الفئة", categories, key="labor_cat_filter_section1")
+
+ if selected_category != 'الكل':
+ filtered_df = labor_df[labor_df['الفئة'] == selected_category]
+ else:
+ filtered_df = labor_df
+
+ # تنسيق الجدول
+ def highlight_labor_row(row):
+ """تنسيق الجدول مع تمييز الصفوف بالتناوب"""
+ color = '#F0F8FF' if row.name % 2 == 0 else 'white'
+ return ['background-color: %s' % color] * len(row)
+
+ styled_df = filtered_df.style.apply(highlight_labor_row, axis=1)
+ styled_df = styled_df.format({
+ 'سعر الوحدة': '{:,.2f}'
+ })
+
+ st.dataframe(styled_df, use_container_width=True, hide_index=True)
+ else:
+ st.info("لا توجد عمالة في قاعدة البيانات")
+ else:
+ st.info("لا توجد قائمة عمالة متاحة")
+
+ with ref_tabs[2]: # قائمة المعدات
+ st.markdown("#### قائمة أسعار المعدات المرجعية")
+
+ # الحصول على قائمة المعدات
+ equipment = self.construction_calculator.get_all_rates(item_type='معدة')
+
+ if equipment and 'المعدات' in equipment:
+ # تحويل قاموس المعدات إلى DataFrame
+ equipment_list = []
+ for name, data in equipment['المعدات'].items():
+ equipment_list.append({
+ 'نوع المعدة': name,
+ 'وحدة الإيجار': data.get('وحدة', ''),
+ 'سعر الوحدة': data.get('سعر_الوحدة', 0.0),
+ 'الوصف': data.get('وصف', ''),
+ 'الفئة': data.get('فئة', '')
+ })
+
+ if equipment_list:
+ equipment_df = pd.DataFrame(equipment_list)
+
+ # تصفية حسب الفئة
+ categories = ['الكل'] + sorted(equipment_df['الفئة'].unique().tolist())
+ selected_category = st.selectbox("تصفية حسب الفئة", categories, key="equipment_cat_filter_section1")
+
+ if selected_category != 'الكل':
+ filtered_df = equipment_df[equipment_df['الفئة'] == selected_category]
+ else:
+ filtered_df = equipment_df
+
+ # تنسيق الجدول
+ def highlight_equipment_row(row):
+ """تنسيق الجدول مع تمييز الصفوف بالتناوب"""
+ color = '#F0F8FF' if row.name % 2 == 0 else 'white'
+ return ['background-color: %s' % color] * len(row)
+
+ styled_df = filtered_df.style.apply(highlight_equipment_row, axis=1)
+ styled_df = styled_df.format({
+ 'سعر الوحدة': '{:,.2f}'
+ })
+
+ st.dataframe(styled_df, use_container_width=True, hide_index=True)
+ else:
+ st.info("لا توجد معدات في قاعدة البيانات")
+ else:
+ st.info("لا توجد قائمة معدات متاحة")
+
+ with calc_tabs[3]: # كتالوج أعمال المقاولات
+ st.markdown("#### كتالوج أعمال المقاولات")
+
+ # شرح كتالوج أعمال المقاولات
+ with st.expander("معلومات عن كتالوج أعمال المقاولات", expanded=False):
+ st.markdown("""
+ **كتالوج أعمال المقاولات** هو قاعدة بيانات شاملة للبنود النموذجية المستخدمة في مشاريع المقاولات ويوفر:
+
+ - بنود جاهزة لمختلف أنواع الأعمال الإنشائية (خرسانة مناهل مواسير طرق إلخ).
+ - تفاصيل دقيقة للمواد والعمالة والمعدات المطلوبة لكل بند.
+ - تحليل تكلفة تفصيلي يمكن استخدامه مباشرة في عروض الأسعار والمناقصات.
+ - ربط مباشر مع حاسبة تكاليف البناء وحاسبة التسعير.
+
+ **استخدامات الكتالوج:**
+
+ 1. استخدام البنود النموذجية مباشرة في التسعير.
+ 2. تعديل البنود النموذجية لتناسب متطلبات المشروع.
+ 3. إضافة بنود جديدة إلى الكتالوج للاستخدام المستقبلي.
+ """)
+
+ # الفئات وعرض محتوى الكتالوج
+ category_col, template_col = st.columns([1, 2])
+
+ with category_col:
+ st.markdown("### فئات البنود")
+
+ # الحصول على فئ��ت البنود
+ templates = self.construction_templates.get_all_templates()
+ categories = templates['categories']
+
+ for cat_id, cat_data in categories.items():
+ st.markdown(f"#### {cat_data['name']}")
+ st.markdown(f"{cat_data['description']}")
+
+ if st.button(f"عرض بنود {cat_data['name']}", key=f"cat_btn_{cat_id}"):
+ st.session_state.selected_category = cat_id
+ st.rerun()
+
+ with template_col:
+ st.markdown("### البنود النموذجية")
+
+ selected_category = st.session_state.get("selected_category", list(categories.keys())[0])
+
+ # عرض البنود في الفئة المحددة
+ cat_templates = self.construction_templates.get_templates_by_category(selected_category)
+
+ if cat_templates:
+ st.markdown(f"#### بنود فئة {categories[selected_category]['name']}")
+
+ for template in cat_templates:
+ with st.expander(template['name'], expanded=False):
+ st.markdown(f"**الوصف**: {template['description']}")
+ st.markdown(f"**الوحدة**: {template['unit']}")
+
+ # عرض مكونات البند
+ st.markdown("##### مكونات البند")
+
+ # المواد
+ materials = template['components']['materials']
+ if materials:
+ materials_df = pd.DataFrame(materials)
+ st.markdown("**المواد:**")
+ st.dataframe(materials_df, hide_index=True)
+
+ # العمالة
+ labor = template['components']['labor']
+ if labor:
+ labor_df = pd.DataFrame(labor)
+ st.markdown("**العمالة:**")
+ st.dataframe(labor_df, hide_index=True)
+
+ # المعدات
+ equipment = template['components']['equipment']
+ if equipment:
+ equipment_df = pd.DataFrame(equipment)
+ st.markdown("**المعدات:**")
+ st.dataframe(equipment_df, hide_index=True)
+
+ # أزرار العمليات
+ col1, col2 = st.columns(2)
+
+ with col1:
+ if st.button("استخدام هذا البند في حاسبة التكاليف", key=f"use_template_{template['id']}"):
+ # تحويل البند إلى صيغة الحاسبة
+ construction_item = self.construction_templates.convert_template_to_item(template['id'])
+
+ # تحديث بيانات البند في session state
+ st.session_state.construction_item = construction_item
+ st.session_state.active_tab = 0 # الانتقال إلى تبويب حساب تكلفة البند
+ st.rerun()
+
+ with col2:
+ if st.button("إضافة مباشرة إلى جدول التسعير", key=f"add_to_pricing_{template['id']}"):
+ # تحويل البند إلى صيغة الحاسبة
+ construction_item = self.construction_templates.convert_template_to_item(template['id'])
+
+ # حساب التكلفة
+ item_cost = self.construction_calculator.calculate_item_cost(construction_item)
+
+ # إضافة البند إلى جدول التسعير
+ if 'manual_items' not in st.session_state:
+ st.session_state.manual_items = pd.DataFrame(columns=[
+ 'رقم البند', 'وصف البند', 'الوحدة', 'الكمية', 'سعر الوحدة', 'الإجمالي'
+ ])
+
+ # إنشاء رقم البن�� الجديد
+ new_id = f"C{len(st.session_state.manual_items)+1}"
+
+ # إنشاء صف جديد
+ new_row = pd.DataFrame({
+ 'رقم البند': [new_id],
+ 'وصف البند': [item_cost['وصف_البند']],
+ 'الوحدة': [item_cost['الوحدة']],
+ 'الكمية': [float(item_cost['الكمية'])],
+ 'سعر الوحدة': [float(item_cost['السعر_المعدل']['سعر_الوحدة'])],
+ 'الإجمالي': [float(item_cost['السعر_المعدل']['إجمالي'])]
+ })
+
+ # إضافة الصف إلى DataFrame
+ st.session_state.manual_items = pd.concat([st.session_state.manual_items, new_row], ignore_index=True)
+
+ # إنشاء حالة التسعير الحالي إذا لم تكن موجودة
+ if 'current_pricing' not in st.session_state:
+ st.session_state.current_pricing = {
+ 'name': 'تسعير جديد',
+ 'method': 'تسعير مستورد من كتالوج المقاولات',
+ 'items': st.session_state.manual_items
+ }
+ else:
+ # تحديث البنود في التسعير الحالي
+ st.session_state.current_pricing['items'] = st.session_state.manual_items
+
+ st.success(f"تم إضافة البند \"{item_cost['وصف_البند']}\" إلى جدول التسعير بنجاح!")
+
+ # إضافة قسم لإضافة بند جديد إلى الكتالوج
+ st.markdown("### إضافة بند جديد إلى الكتالوج")
+
+ if st.button("إضافة البند الحالي إلى الكتالوج"):
+ if 'item_cost_result' in st.session_state:
+ # عرض نموذج لإضافة البند إلى الكتالوج
+ st.session_state.show_add_to_catalog = True
+ st.rerun()
+ else:
+ st.warning("يجب حساب تكلفة البند أولاً في تبويب 'حساب تكلفة بند' قبل إضافته إلى الكتالوج.")
+
+ # عرض نموذج إضافة البند إلى الكتالوج
+ if 'show_add_to_catalog' in st.session_state and st.session_state.show_add_to_catalog:
+ st.markdown("#### إضافة البند الحالي إلى كتالوج المقاولات")
+
+ # اختيار الفئة
+ category_options = [f"{cat_data['name']}" for cat_id, cat_data in categories.items()]
+ category_ids = list(categories.keys())
+
+ selected_category_index = st.selectbox(
+ "اختر فئة البند",
+ options=range(len(category_options)),
+ format_func=lambda i: category_options[i],
+ key="new_template_category"
+ )
+
+ selected_category_id = category_ids[selected_category_index]
+
+ # معلومات البند
+ item_result = st.session_state.item_cost_result
+
+ template_name = st.text_input("اسم البند في الكتالوج", value=item_result['وصف_البند'][:50])
+ template_description = st.text_area("وصف البند", value=item_result['وصف_البند'], key="template_item_description")
+
+ # الكلمات المفتاحية
+ tags_input = st.text_input("الكلمات المفتاحية (مفصولة بفواصل)", value="بناء, مقاولات")
+ tags = [tag.strip() for tag in tags_input.split(",")]
+
+ if st.button("إضافة إلى الكتالوج", type="primary"):
+ # تحويل البند إلى صيغة قالب
+ template_data = {
+ "category": selected_category_id,
+ "name": template_name,
+ "description": template_description,
+ "unit": item_result['الوحدة'],
+ "components": {
+ "materials": item_result['تكاليف_مباشرة']['المواد']['التفاصيل'],
+ "labor": item_result['تكاليف_مباشرة']['العمالة']['التفاصيل'],
+ "equipment": item_result['تكاليف_مباشرة']['المعدات']['التفاصيل']
+ },
+ "admin_expenses": item_result['مصاريف_إدارية']['نسبة'] / 100,
+ "profit_margin": item_result['هامش_ربح']['نسبة'] / 100,
+ "tags": tags
+ }
+
+ # إضافة القالب إلى الكتالوج
+ template_id = self.construction_templates.add_template(template_data)
+
+ st.success(f"تم إضافة البند \"{template_name}\" إلى كتالوج المقاولات بنجاح!")
+ st.session_state.show_add_to_catalog = False
+ st.rerun()
+
+ if st.button("إلغاء", key="cancel_add_to_catalog"):
+ st.session_state.show_add_to_catalog = False
+ st.rerun()
+
+ with ref_tabs[0]: # قائمة المواد
+ st.markdown("#### قائمة أسعار المواد المرجعية")
+
+ # الحصول على قائمة المواد
+ materials = self.construction_calculator.get_all_rates(item_type='مادة')
+
+ if materials and 'المواد' in materials:
+ # تحويل قاموس المواد إلى DataFrame
+ materials_list = []
+ for name, data in materials['المواد'].items():
+ materials_list.append({
+ 'اسم المادة': name,
+ 'الوحدة': data.get('وحدة', ''),
+ 'سعر الوحدة': data.get('سعر_الوحدة', 0.0),
+ 'الوصف': data.get('وصف', ''),
+ 'الفئة': data.get('فئة', '')
+ })
+
+ if materials_list:
+ materials_df = pd.DataFrame(materials_list)
+
+ # تصفية حسب الفئة
+ categories = ['الكل'] + sorted(materials_df['الفئة'].unique().tolist())
+ selected_category = st.selectbox("تصفية حسب الفئة", categories, key="materials_cat_filter_section2")
+
+ if selected_category != 'الكل':
+ filtered_df = materials_df[materials_df['الفئة'] == selected_category]
+ else:
+ filtered_df = materials_df
+
+ # تنسيق الجدول
+ def highlight_materials_row(row):
+ """تنسيق الجدول مع تمييز الصفوف بالتناوب"""
+ color = '#F0F8FF' if row.name % 2 == 0 else 'white'
+ return ['background-color: %s' % color] * len(row)
+
+ styled_df = filtered_df.style.apply(highlight_materials_row, axis=1)
+ styled_df = styled_df.format({
+ 'سعر الوحدة': '{:,.2f}'
+ })
+
+ st.dataframe(styled_df, use_container_width=True, hide_index=True)
+ else:
+ st.info("لا توجد مواد في قاعدة البيانات")
+ else:
+ st.info("لا توجد قائمة مواد متاحة")
+
+ with ref_tabs[1]: # قائمة العمالة
+ st.markdown("#### قائمة أسعار العمالة المرجعية")
+
+ # الحصول على قائمة العمالة
+ labor = self.construction_calculator.get_all_rates(item_type='عمالة')
+
+ if labor and 'العمالة' in labor:
+ # تحويل قاموس العمالة إلى DataFrame
+ labor_list = []
+ for name, data in labor['العمالة'].items():
+ labor_list.append({
+ 'نوع العامل': name,
+ 'وحدة الأجر': data.get('وحدة', ''),
+ 'سعر الوحدة': data.get('سعر_الوحدة', 0.0),
+ 'الوصف': data.get('وصف', ''),
+ 'الفئة': data.get('فئة', '')
+ })
+
+ if labor_list:
+ labor_df = pd.DataFrame(labor_list)
+
+ # تصفية حسب الفئة
+ categories = ['الكل'] + sorted(labor_df['الفئة'].unique().tolist())
+ selected_category = st.selectbox("تصفية حسب الفئة", categories, key="labor_cat_filter_section2")
+
+ if selected_category != 'الكل':
+ filtered_df = labor_df[labor_df['الفئة'] == selected_category]
+ else:
+ filtered_df = labor_df
+
+ # تنسيق الجدول
+ def highlight_labor_row(row):
+ """تنسيق الجدول مع تمييز الصفوف بالتناوب"""
+ color = '#F0F8FF' if row.name % 2 == 0 else 'white'
+ return ['background-color: %s' % color] * len(row)
+
+ styled_df = filtered_df.style.apply(highlight_labor_row, axis=1)
+ styled_df = styled_df.format({
+ 'سعر الوحدة': '{:,.2f}'
+ })
+
+ st.dataframe(styled_df, use_container_width=True, hide_index=True)
+ else:
+ st.info("لا توجد عمالة في قاعدة البيانات")
+ else:
+ st.info("لا توجد قائمة عمالة متاحة")
+
+ with ref_tabs[2]: # قائمة المعدات
+ st.markdown("#### قائمة أسعار المعدات المرجعية")
+
+ # الحصول على قائمة المعدات
+ equipment = self.construction_calculator.get_all_rates(item_type='معدة')
+
+ if equipment and 'المعدات' in equipment:
+ # تحويل قاموس المعدات إلى DataFrame
+ equipment_list = []
+ for name, data in equipment['المعدات'].items():
+ equipment_list.append({
+ 'نوع المعدة': name,
+ 'وحدة الأجر': data.get('وحدة', ''),
+ 'سعر الوحدة': data.get('سعر_الوحدة', 0.0),
+ 'الوصف': data.get('وصف', ''),
+ 'الفئة': data.get('فئة', '')
+ })
+
+ if equipment_list:
+ equipment_df = pd.DataFrame(equipment_list)
+
+ # تصفية حسب الفئة
+ categories = ['الكل'] + sorted(equipment_df['الفئة'].unique().tolist())
+ selected_category = st.selectbox("تصفية حسب الفئة", categories, key="equipment_cat_filter_section2")
+
+ if selected_category != 'الكل':
+ filtered_df = equipment_df[equipment_df['الفئة'] == selected_category]
+ else:
+ filtered_df = equipment_df
+
+ # تنسيق الجدول
+ def highlight_equipment_row(row):
+ """تنسيق الجدول مع تمييز الصفوف بالتناوب"""
+ color = '#F0F8FF' if row.name % 2 == 0 else 'white'
+ return ['background-color: %s' % color] * len(row)
+
+ styled_df = filtered_df.style.apply(highlight_equipment_row, axis=1)
+ styled_df = styled_df.format({
+ 'سعر الوحدة': '{:,.2f}'
+ })
+
+ st.dataframe(styled_df, use_container_width=True, hide_index=True)
+ else:
+ st.info("لا توجد معدات في قاعدة البيانات")
+ else:
+ st.info("لا توجد قائمة معدات متاحة")
+
+ def _render_local_content_tab(self):
+ """عرض تبويب المحتوى المحلي"""
+
+ st.markdown("تحليل المحتوى المحلي
", unsafe_allow_html=True)
+
+ # التحقق من وجود تسعير حالي
+ if 'current_pricing' not in st.session_state or st.session_state.current_pricing is None:
+ st.warning("ليس هناك تسعير حالي. يرجى إنشاء تسعير جديد أولاً.")
+ return
+
+ # شرح المحتوى المحلي
+ with st.expander("ما هو المحتوى المحلي؟", expanded=False):
+ st.markdown("""
+ **المحتوى المحلي** هو نسبة المنتجات والخدمات والقوى العاملة المحلية المستخدمة في المشروع. يهدف إلى زيادة مساهمة المنتجات والخدمات المحلية في المشاريع.
+
+ ### مكونات المحتوى المحلي:
+
+ 1. **المنتجات**: المنتجات والمواد المصنعة محلياً.
+ 2. **الخدمات**: الخدمات المقدمة من شركات محلية.
+ 3. **القوى العاملة**: العمالة والكوادر الفنية والإدارية المحلية.
+
+ ### أهمية المحتوى المحلي:
+
+ - تعزيز الاقتصاد المحلي وخلق فرص عمل.
+ - تحقيق أهداف رؤية 2030 في زيادة المحتوى المحلي.
+ - التأهل للمشاريع الحكومية التي تتطلب نسبة محتوى محلي محددة.
+ - الحصول على حوافز وأفضلية في المناقصات الحكومية.
+
+ ### متطلبات المحتوى المحلي:
+
+ - نسبة المحتوى المحلي للقوى العاملة: 80%
+ - نسبة المحتوى المحلي للمنتجات: 70%
+ - نسبة المحتوى المحلي للخدمات: 60%
+ """)
+
+ # عرض لوحة إدخال بيانات المحتوى المحلي
+ st.markdown("### بيانات المحتوى المحلي")
+
+ # التبويبات لأنواع المحتوى المحلي
+ lc_tabs = st.tabs(["المنتجات", "الخدمات", "القوى العاملة", "التحليل"])
+
+ with lc_tabs[0]: # المنتجات
+ st.markdown("#### بيانات المنتجات")
+
+ # إنشاء بيانات افتراضية للمنتجات إذا لم تكن موجودة
+ if 'local_content_products' not in st.session_state:
+ st.session_state.local_content_products = pd.DataFrame({
+ 'المنتج': [
+ "خرسانة مسلحة",
+ "حديد تسليح",
+ "بلوك خرساني",
+ "عزل مائي",
+ "دهانات"
+ ],
+ 'الكمية': [250, 25, 400, 500, 600],
+ 'سعر_الوحدة': [1200, 6000, 200, 100, 50],
+ 'التكلفة_الإجمالية': [300000, 150000, 80000, 50000, 30000],
+ 'نسبة_المحتوى_المحلي': [0.95, 0.70, 0.98, 0.60, 0.80]
+ })
+
+ # حساب التكلفة الإجمالية
+ st.session_state.local_content_products['التكلفة_الإجمالية'] = st.session_state.local_content_products['الكمية'] * st.session_state.local_content_products['سعر_الوحدة']
+
+ # نموذج إضافة منتج جديد
+ st.markdown("#### إضافة منتج جديد")
+ col1, col2, col3, col4 = st.columns(4)
+
+ with col1:
+ new_product_name = st.text_input("اسم المنتج", key="new_product_name", value="")
+ with col2:
+ new_product_quantity = st.number_input("الكمية", key="new_product_quantity", min_value=0, value=0)
+ with col3:
+ new_product_price = st.number_input("سعر الوحدة", key="new_product_price", min_value=0, value=0)
+ with col4:
+ new_product_local_content = st.slider("نسبة المحتوى المحلي", key="new_product_local_content", min_value=0.0, max_value=1.0, value=0.8, step=0.01, format="%.2f")
+
+ if st.button("إضافة المنتج"):
+ if new_product_name:
+ # حساب التكلفة الإجمالية
+ total_cost = new_product_quantity * new_product_price
+
+ # إضافة منتج جديد للجدول
+ new_product = pd.DataFrame({
+ 'المنتج': [new_product_name],
+ 'الكمية': [new_product_quantity],
+ 'سعر_الوحدة': [new_product_price],
+ 'التكلفة_الإجمالية': [total_cost],
+ 'نسبة_المحتوى_المحلي': [new_product_local_content]
+ })
+
+ # إضافة المنتج الجديد للجدول الحالي
+ st.session_state.local_content_products = pd.concat([st.session_state.local_content_products, new_product], ignore_index=True)
+ st.success(f"تم إضافة المنتج {new_product_name} بنجاح!")
+ else:
+ st.warning("يرجى إدخال اسم المنتج")
+
+ # عرض جدول البنود مع إمكانية التعديل
+ st.markdown("#### جدول المنتجات")
+ edited_products = st.data_editor(
+ st.session_state.local_content_products,
+ use_container_width=True,
+ hide_index=True,
+ key="products_editor"
+ )
+ st.session_state.local_content_products = edited_products
+
+ # زر لحذف المنتجات المحددة
+ if st.button("حذف المنتجات المحددة"):
+ st.session_state.local_content_products = pd.DataFrame({
+ 'المنتج': [],
+ 'الكمية': [],
+ 'سعر_الوحدة': [],
+ 'التكلفة_الإجمالية': [],
+ 'نسبة_المحتوى_المحلي': []
+ })
+ st.success("تم حذف جميع المنتجات!")
+ st.rerun()
+
+ # عرض ملخص المنتجات
+ total_products_cost = edited_products['التكلفة_الإجمالية'].sum()
+ avg_local_content = (edited_products['التكلفة_الإجمالية'] * edited_products['نسبة_المحتوى_المحلي']).sum() / total_products_cost if total_products_cost > 0 else 0
+
+ st.markdown(f"""
+ **إجمالي تكلفة المنتجات**: {total_products_cost:,.2f} ريال
+
+ **متوسط نسبة المحتوى المحلي للمنتجات**: {avg_local_content*100:.2f}%
+
+ **المستهدف**: 70%
+
+ **الحالة**: {"✅ ملتزم" if avg_local_content >= 0.7 else "❌ غير ملتزم"}
+ """)
+
+ with lc_tabs[1]: # الخدمات
+ st.markdown("#### بيانات الخدمات")
+
+ # إنشاء بيانات افتراضية للخدمات إذا لم تكن موجودة
+ if 'local_content_services' not in st.session_state:
+ st.session_state.local_content_services = pd.DataFrame({
+ 'الخدمة': [
+ "تصميم معماري",
+ "إشراف هندسي",
+ "خدمات نقل",
+ "خدمات أمن وسلامة",
+ "صيانة ونظافة"
+ ],
+ 'التكلفة': [100000, 120000, 50000, 30000, 20000],
+ 'نسبة_المحتوى_المحلي': [0.90, 0.85, 0.90, 0.95, 0.95]
+ })
+
+ # نموذج إضافة خدمة جديدة
+ st.markdown("#### إضافة خدمة جديدة")
+ col1, col2, col3 = st.columns(3)
+
+ with col1:
+ new_service_name = st.text_input("اسم الخدمة", key="new_service_name", value="")
+ with col2:
+ new_service_cost = st.number_input("التكلفة", key="new_service_cost", min_value=0, value=0)
+ with col3:
+ new_service_local_content = st.slider("نسبة المحتوى المحلي", key="new_service_local_content", min_value=0.0, max_value=1.0, value=0.8, step=0.01, format="%.2f")
+
+ if st.button("إضافة الخدمة"):
+ if new_service_name:
+ # إضافة خدمة جديدة للجدول
+ new_service = pd.DataFrame({
+ 'الخدمة': [new_service_name],
+ 'التكلفة': [new_service_cost],
+ 'نسبة_المحتوى_المحلي': [new_service_local_content]
+ })
+
+ # إضافة الخدمة الجديدة للجدول الحالي
+ st.session_state.local_content_services = pd.concat([st.session_state.local_content_services, new_service], ignore_index=True)
+ st.success(f"تم إضافة الخدمة {new_service_name} بنجاح!")
+ else:
+ st.warning("يرجى إدخال اسم الخدمة")
+
+ # عرض جدول الخدمات مع إمكانية التعديل
+ st.markdown("#### جدول الخدمات")
+ edited_services = st.data_editor(
+ st.session_state.local_content_services,
+ use_container_width=True,
+ hide_index=True,
+ key="services_editor"
+ )
+ st.session_state.local_content_services = edited_services
+
+ # زر لحذف الخدمات المحددة
+ if st.button("حذف الخدمات المحددة"):
+ st.session_state.local_content_services = pd.DataFrame({
+ 'الخدمة': [],
+ 'التكلفة': [],
+ 'نسبة_المحتوى_المحلي': []
+ })
+ st.success("تم حذف جميع الخدمات!")
+ st.rerun()
+
+ # عرض ملخص الخدمات
+ total_services_cost = edited_services['التكلفة'].sum()
+ avg_local_content = (edited_services['التكلفة'] * edited_services['نسبة_المحتوى_المحلي']).sum() / total_services_cost if total_services_cost > 0 else 0
+
+ st.markdown(f"""
+ **إجمالي تكلفة الخدمات**: {total_services_cost:,.2f} ريال
+
+ **متوسط نسبة المحتوى المحلي للخدمات**: {avg_local_content*100:.2f}%
+
+ **المستهدف**: 60%
+
+ **الحالة**: {"✅ ملتزم" if avg_local_content >= 0.6 else "❌ غير ملتزم"}
+ """)
+
+ with lc_tabs[2]: # القوى العاملة
+ st.markdown("#### بيانات القوى العاملة")
+
+ # إنشاء بيانات افتراضية للقوى العاملة إذا لم تكن موجودة
+ if 'local_content_labor' not in st.session_state:
+ st.session_state.local_content_labor = pd.DataFrame({
+ 'فئة_العمالة': [
+ "مهندسون",
+ "فنيون",
+ "عمال بناء",
+ "إداريون",
+ "مشرفون"
+ ],
+ 'العدد': [5, 10, 30, 3, 4],
+ 'الراتب_الشهري': [15000, 8000, 3000, 10000, 12000],
+ 'المدة_بالأشهر': [12, 12, 12, 12, 12],
+ 'نسبة_المحتوى_المحلي': [0.75, 0.65, 0.60, 0.90, 0.80]
+ })
+
+ # حساب التكلفة الإجمالية
+ st.session_state.local_content_labor['التكلفة_الإجمالية'] = st.session_state.local_content_labor['العدد'] * st.session_state.local_content_labor['الراتب_الشهري'] * st.session_state.local_content_labor['المدة_بالأشهر']
+
+ # نموذج إضافة فئة عمالة جديدة
+ st.markdown("#### إضافة فئة عمالة جديدة")
+ col1, col2 = st.columns(2)
+
+ with col1:
+ new_labor_category = st.text_input("فئة العمالة", key="new_labor_category", value="")
+ new_labor_count = st.number_input("العدد", key="new_labor_count", min_value=0, value=0)
+
+ with col2:
+ new_labor_salary = st.number_input("الراتب الشهري", key="new_labor_salary", min_value=0, value=0)
+ new_labor_months = st.number_input("المدة بالأشهر", key="new_labor_months", min_value=1, value=12)
+
+ new_labor_local_content = st.slider("نسبة المحتوى المحلي", key="new_labor_local_content", min_value=0.0, max_value=1.0, value=0.8, step=0.01, format="%.2f")
+
+ if st.button("إضافة فئة العمالة"):
+ if new_labor_category:
+ # حساب التكلفة الإجمالية
+ total_cost = new_labor_count * new_labor_salary * new_labor_months
+
+ # إضافة فئة عمالة جديدة للجدول
+ new_labor = pd.DataFrame({
+ 'فئة_العمالة': [new_labor_category],
+ 'العدد': [new_labor_count],
+ 'الراتب_الشهري': [new_labor_salary],
+ 'المدة_بالأشهر': [new_labor_months],
+ 'نسبة_المحتوى_المحلي': [new_labor_local_content],
+ 'التكلفة_الإجمالية': [total_cost]
+ })
+
+ # إضافة فئة العمالة الجديدة للجدول الحالي
+ st.session_state.local_content_labor = pd.concat([st.session_state.local_content_labor, new_labor], ignore_index=True)
+ st.success(f"تم إضافة فئة العمالة {new_labor_category} بنجاح!")
+ else:
+ st.warning("يرجى إدخال اسم فئة العمالة")
+
+ # عرض جدول القوى العاملة مع إمكانية التعديل
+ st.markdown("#### جدول القوى العاملة")
+ edited_labor = st.data_editor(
+ st.session_state.local_content_labor,
+ use_container_width=True,
+ hide_index=True,
+ key="labor_editor"
+ )
+
+ # إعادة حساب التكلفة الإجمالية بعد التعديل
+ edited_labor['التكلفة_الإجمالية'] = edited_labor['العدد'] * edited_labor['الراتب_الشهري'] * edited_labor['المدة_بالأشهر']
+ st.session_state.local_content_labor = edited_labor
+
+ # زر لحذف فئات العمالة المحددة
+ if st.button("حذف فئات العمالة المحددة"):
+ st.session_state.local_content_labor = pd.DataFrame({
+ 'فئة_العمالة': [],
+ 'العدد': [],
+ 'الراتب_الشهري': [],
+ 'المدة_بالأشهر': [],
+ 'نسبة_المحتوى_المحلي': [],
+ 'التكلفة_الإجمالية': []
+ })
+ st.success("تم حذف جميع فئات العمالة!")
+ st.rerun()
+
+ # عرض ملخص القوى العاملة
+ total_labor_cost = edited_labor['التكلفة_الإجمالية'].sum()
+ avg_local_content = (edited_labor['التكلفة_الإجمالية'] * edited_labor['نسبة_المحتوى_المحلي']).sum() / total_labor_cost if total_labor_cost > 0 else 0
+
+ st.markdown(f"""
+ **إجمالي تكلفة القوى العاملة**: {total_labor_cost:,.2f} ريال
+
+ **متوسط نسبة المحتوى المحلي للقوى العاملة**: {avg_local_content*100:.2f}%
+
+ **المستهدف**: 80%
+
+ **الحالة**: {"✅ ملتزم" if avg_local_content >= 0.8 else "❌ غير ملتزم"}
+ """)
+
+ with lc_tabs[3]: # التحليل
+ st.markdown("#### تحليل المحتوى المحلي")
+
+ # حساب المحتوى المحلي الإجمالي
+ try:
+ # تجميع بيانات تحليل المحتوى المحلي
+ products_cost = st.session_state.local_content_products['التكلفة_الإجمالية'].sum()
+ products_local_content = (st.session_state.local_content_products['التكلفة_الإجمالية'] * st.session_state.local_content_products['نسبة_المحتوى_المحلي']).sum() / products_cost if products_cost > 0 else 0
+
+ services_cost = st.session_state.local_content_services['التكلفة'].sum()
+ services_local_content = (st.session_state.local_content_services['التكلفة'] * st.session_state.local_content_services['نسبة_المحتوى_المحلي']).sum() / services_cost if services_cost > 0 else 0
+
+ labor_cost = st.session_state.local_content_labor['التكلفة_الإجمالية'].sum()
+ labor_local_content = (st.session_state.local_content_labor['التكلفة_الإجمالية'] * st.session_state.local_content_labor['نسبة_المحتوى_المحلي']).sum() / labor_cost if labor_cost > 0 else 0
+
+ # حساب الوزن النسبي لكل مكون
+ total_cost = products_cost + services_cost + labor_cost
+ products_weight = products_cost / total_cost if total_cost > 0 else 0
+ services_weight = services_cost / total_cost if total_cost > 0 else 0
+ labor_weight = labor_cost / total_cost if total_cost > 0 else 0
+
+ # حساب المحتوى المحلي الإجمالي
+ total_local_content = (products_local_content * products_weight) + (services_local_content * services_weight) + (labor_local_content * labor_weight)
+
+ # عرض ملخص المحتوى المحلي
+ st.markdown("### ملخص المحتوى المحلي")
+
+ col1, col2, col3 = st.columns(3)
+
+ with col1:
+ st.metric("إجمالي التكاليف", f"{total_cost:,.2f} ريال")
+
+ with col2:
+ st.metric("نسبة المحتوى المحلي الإجمالية", f"{total_local_content*100:.2f}%")
+
+ with col3:
+ target_local_content = 0.7 # 70%
+ st.metric("الحالة", "ملتزم" if total_local_content >= target_local_content else "غير ملتزم", delta=f"{(total_local_content - target_local_content)*100:.2f}%")
+
+ # عرض رسم بياني للمقارنة
+ st.markdown("### تحليل بصري للمحتوى المحلي")
+
+ # رسم بياني شريطي لنسب المحتوى المحلي
+ categories = ['المنتجات', 'الخدمات', 'القوى العاملة', 'الإجمالي']
+ actual_values = [products_local_content * 100, services_local_content * 100, labor_local_content * 100, total_local_content * 100]
+ target_values = [70, 60, 80, 70] # المستهدفات
+
+ # تهيئة البيانات للرسم البياني
+ chart_data = pd.DataFrame({
+ 'الفئة': categories,
+ 'النسبة الفعلية': actual_values,
+ 'النسبة المستهدفة': target_values
+ })
+
+ # رسم بياني شريطي للمقارنة
+ fig = go.Figure()
+
+ fig.add_trace(go.Bar(
+ x=chart_data['الفئة'],
+ y=chart_data['النسبة الفعلية'],
+ name='النسبة الفعلية',
+ marker_color='rgb(26, 118, 255)'
+ ))
+
+ fig.add_trace(go.Bar(
+ x=chart_data['الفئة'],
+ y=chart_data['النسبة المستهدفة'],
+ name='النسبة المستهدفة',
+ marker_color='rgb(55, 83, 109)'
+ ))
+
+ fig.update_layout(
+ title='مقارنة بين النسب الفعلية والمستهدفة للمحتوى المحلي',
+ xaxis_tickfont_size=14,
+ yaxis=dict(
+ title='النسبة %',
+ titlefont_size=16,
+ tickfont_size=14,
+ ),
+ legend=dict(
+ x=0,
+ y=1.0,
+ bgcolor='rgba(255, 255, 255, 0)',
+ bordercolor='rgba(255, 255, 255, 0)'
+ ),
+ barmode='group',
+ bargap=0.15,
+ bargroupgap=0.1
+ )
+
+ st.plotly_chart(fig, use_container_width=True)
+
+ # عرض توصيات لتحسين نسبة المحتوى المحلي
+ st.markdown("### توصيات لتحسين نسبة المحتوى المحلي")
+
+ recommendations = []
+
+ if products_local_content < 0.7:
+ recommendations.append("- زيادة نسبة المحتوى المحلي للمنتجات من خلال:")
+ recommendations.append(" - البحث عن موردين محليين للمنتجات ذات النسبة المنخفضة")
+ recommendations.append(" - استبدال المنتجات المستوردة ببدائل محلية")
+ recommendations.append(" - التعاون مع المصانع المحلية لتوطين صناعة المنتجات")
+
+ if services_local_content < 0.6:
+ recommendations.append("- زيادة نسبة المحتوى المحلي للخدمات من خلال:")
+ recommendations.append(" - التعاقد مع شركات خدمات محلية")
+ recommendations.append(" - تحويل الخدمات المستعان بها من الخارج إلى شركات محلية")
+ recommendations.append(" - تأهيل الشركات المحلية لتقديم الخدمات المطلوبة")
+
+ if labor_local_content < 0.8:
+ recommendations.append("- زيادة نسبة المحتوى المحلي للقوى العاملة من خلال:")
+ recommendations.append(" - زيادة توظيف الكوادر المحلية")
+ recommendations.append(" - تدريب وتأهيل العمالة المحلية")
+ recommendations.append(" - استبدال العمالة الأجنبية بكوادر محلية تدريجياً")
+
+ if total_local_content < 0.7:
+ recommendations.append("- زيادة نسبة المحتوى المحلي الإجمالية من خلال:")
+ recommendations.append(" - إعادة توزيع الميزانية لصالح المكونات ذات النسبة العالية من المحتوى المحلي")
+ recommendations.append(" - وضع خطة مرحلية لزيادة المحتوى المحلي")
+ recommendations.append(" - التعاون مع اللجنة المحلية لزيادة المحتوى المحلي")
+
+ if recommendations:
+ for rec in recommendations:
+ st.markdown(rec)
+ else:
+ st.success("تهانينا! نسبة المحتوى المحلي متوافقة مع المتطلبات.")
+
+ # حساب تأثير المحتوى المحلي على التسعير
+ st.markdown("### تأثير المحتوى المحلي على التسعير")
+
+ # تحديد عامل تعديل السعر بناءً على نسبة المحتوى المحلي
+ price_adjustment_factor = 1.0
+
+ if total_local_content >= 0.9:
+ price_adjustment_factor = 0.92 # خصم 8% للمحتوى المحلي العالي جداً
+ price_discount = "8%"
+ elif total_local_content >= 0.8:
+ price_adjustment_factor = 0.94 # خصم 6% للمحتوى المحلي العالي
+ price_discount = "6%"
+ elif total_local_content >= 0.7:
+ price_adjustment_factor = 0.96 # خصم 4% للمحتوى المحلي المتوسط
+ price_discount = "4%"
+ elif total_local_content >= 0.6:
+ price_adjustment_factor = 0.98 # خصم 2% للمحتوى المحلي المنخفض
+ price_discount = "2%"
+ else:
+ price_adjustment_factor = 1.0 # لا خصم
+ price_discount = "0%"
+
+ # عرض تأثير المحتوى المحلي على التسعير
+ original_total = st.session_state.current_pricing['items']['الإجمالي'].sum()
+ adjusted_total = original_total * price_adjustment_factor
+ discount_amount = original_total - adjusted_total
+
+ col1, col2, col3 = st.columns(3)
+
+ with col1:
+ st.metric("إجمالي التسعير الأصلي", f"{original_total:,.2f} ريال")
+
+ with col2:
+ st.metric("نسبة الخصم بسبب المحتوى المحلي", price_discount)
+
+ with col3:
+ st.metric("إجمالي التسعير بعد الخصم", f"{adjusted_total:,.2f} ريال", delta=f"-{discount_amount:,.2f} ريال")
+
+ # أزرار العمليات
+ col1, col2 = st.columns(2)
+
+ with col1:
+ if st.button("حفظ تحليل المحتوى المحلي"):
+ # حفظ بيانات المحتوى المحلي في التسعير الحالي
+ st.session_state.current_pricing['local_content'] = {
+ 'products': st.session_state.local_content_products.copy(),
+ 'services': st.session_state.local_content_services.copy(),
+ 'labor': st.session_state.local_content_labor.copy(),
+ 'total_local_content': total_local_content,
+ 'price_adjustment_factor': price_adjustment_factor
+ }
+
+ st.success("تم حفظ تحليل المحتوى المحلي بنجاح!")
+
+ with col2:
+ if st.button("تصدير تقرير المحتوى المحلي"):
+ st.success("تم تصدير تقرير المحتوى المحلي بنجاح!")
+
+ except Exception as e:
+ st.error(f"حدث خطأ أثناء تحليل المحتوى المحلي: {str(e)}")
+ st.warning("تأكد من إدخال بيانات المحتوى المحلي بشكل صحيح في التبويبات السابقة.")
+
+ def _render_utilities_tab(self):
+ """عرض تبويب الأدوات المساعدة"""
+ import json
+ import copy
+ from datetime import datetime
+
+ st.markdown("## الأدوات المساعدة")
+
+ utilities_tab1, utilities_tab2, utilities_tab3, utilities_tab4, utilities_tab5 = st.tabs([
+ "الرسوم البيانية المتقدمة",
+ "استيراد/تصدير الإعدادات",
+ "النسخ الاحتياطي والاستعادة",
+ "مقارنة نماذج التسعير",
+ "إنشاء التقارير"
+ ])
+
+ with utilities_tab1:
+ st.markdown("### الرسوم البيانية المتقدمة لتحليل ا��تكلفة")
+
+ if 'item_cost_result' in st.session_state:
+ result = st.session_state.item_cost_result
+
+ # تبويب الرسوم البيانية
+ chart_tab1, chart_tab2, chart_tab3 = st.tabs(["توزيع التكلفة", "مقارنة المكونات", "تأثير العوامل"])
+
+ with chart_tab1:
+ # رسم بياني دائري لتوزيع التكلفة
+ fig = go.Figure(data=[go.Pie(
+ labels=["المواد", "العمالة", "المعدات", "المصاريف الإدارية", "هامش الربح"],
+ values=[
+ result['تكاليف_مباشرة']['المواد']['الإجمالي'],
+ result['تكاليف_مباشرة']['العمالة']['الإجمالي'],
+ result['تكاليف_مباشرة']['المعدات']['الإجمالي'],
+ result['مصاريف_إدارية']['قيمة'],
+ result['هامش_ربح']['قيمة']
+ ],
+ hole=.3,
+ marker_colors=['#36a2eb', '#ff6384', '#4bc0c0', '#ffcd56', '#9966ff']
+ )])
+ fig.update_layout(title_text="توزيع مكونات التكلفة")
+ st.plotly_chart(fig, use_container_width=True)
+
+ with chart_tab2:
+ # رسم بياني شريطي للمكونات الرئيسية
+ categories = ["المواد", "العمالة", "المعدات"]
+ values = [
+ result['تكاليف_مباشرة']['المواد']['الإجمالي'],
+ result['تكاليف_مباشرة']['العمالة']['الإجمالي'],
+ result['تكاليف_مباشرة']['المعدات']['الإجمالي']
+ ]
+
+ fig = go.Figure(data=[go.Bar(x=categories, y=values, marker_color=['#36a2eb', '#ff6384', '#4bc0c0'])])
+ fig.update_layout(title_text="مقارنة بين المكونات الرئيسية للتكلفة")
+ st.plotly_chart(fig, use_container_width=True)
+
+ with chart_tab3:
+ # مخطط شريطي يوضح تأثير عوامل التعديل على التكلفة
+ original_cost = result['التكلفة_الإجمالية']
+ final_cost = result['السعر_المعدل']['إجمالي']
+
+ # التحقق من وجود العوامل، وإضافتها بقيم افتراضية إذا كانت غير موجودة
+ if 'عوامل_التعديل' not in result:
+ result['عوامل_التعديل'] = {}
+
+ # إضافة المفاتيح الناقصة بقيم افتراضية
+ default_factors = {
+ 'location_factor': 1.0,
+ 'time_factor': 1.0,
+ 'risk_factor': 1.0,
+ 'market_factor': 1.0
+ }
+
+ for key, default_value in default_factors.items():
+ if key not in result['عوامل_التعديل']:
+ result['عوامل_التعديل'][key] = default_value
+
+ factors = {
+ "معامل الموقع": result['عوامل_التعديل']['location_factor'],
+ "معامل الوقت": result['عوامل_التعديل']['time_factor'],
+ "معامل المخاطر": result['عوامل_التعديل']['risk_factor'],
+ "معامل السوق": result['عوامل_التعديل']['market_factor']
+ }
+
+ # حساب القيمة المضافة من كل عامل
+ factor_effects = {}
+ for factor_name, factor_value in factors.items():
+ factor_effects[factor_name] = original_cost * (factor_value - 1)
+
+ fig = go.Figure()
+ fig.add_trace(go.Waterfall(
+ name="تأثير العوامل",
+ orientation="v",
+ measure=["absolute"] + ["relative"] * len(factor_effects) + ["total"],
+ x=["التكلفة الأصلية"] + list(factor_effects.keys()) + ["التكلفة النهائية"],
+ y=[original_cost] + list(factor_effects.values()) + [0],
+ text=[f"{original_cost:,.2f}"] + [f"{val:,.2f}" for val in factor_effects.values()] + [f"{final_cost:,.2f}"],
+ connector={"line": {"color": "rgb(63, 63, 63)"}},
+ ))
+
+ fig.update_layout(title_text="تأثير عوامل التعديل على التكلفة النهائية")
+ st.plotly_chart(fig, use_container_width=True)
+ else:
+ st.warning("لم يتم العثور على بيانات تحليل التكلفة. يرجى إجراء تحليل تكلفة في تبويب 'تحليل سعر البند' أولاً.")
+
+ with utilities_tab2:
+ st.markdown("### استيراد/تصدير إعدادات التسعير")
+
+ export_col, import_col = st.columns(2)
+
+ with export_col:
+ st.markdown("#### تصدير الإعدادات الحالية")
+ if st.button("تصدير إعدادات التسعير", key="export_pricing_settings"):
+ pricing_settings = {
+ "construction_item": st.session_state.construction_item if 'construction_item' in st.session_state else None,
+ "current_pricing": st.session_state.current_pricing if 'current_pricing' in st.session_state else None
+ }
+ settings_json = json.dumps(pricing_settings, ensure_ascii=False, indent=2)
+ st.download_button(
+ label="تنزيل إعدادات التسعير",
+ data=settings_json,
+ file_name="pricing_settings.json",
+ mime="application/json",
+ key="download_settings_button"
+ )
+
+ with import_col:
+ st.markdown("#### استيراد إعدادات سابقة")
+ uploaded_file = st.file_uploader("استيراد إعدادات تسعير سابقة", type=["json"], key="upload_pricing_settings")
+ if uploaded_file is not None:
+ try:
+ settings_data = json.loads(uploaded_file.getvalue().decode('utf-8'))
+ # تحديث بيانات التسعير في الجلسة
+ if 'construction_item' in settings_data and settings_data['construction_item']:
+ st.session_state.construction_item = settings_data['construction_item']
+ if 'current_pricing' in settings_data and settings_data['current_pricing']:
+ st.session_state.current_pricing = settings_data['current_pricing']
+ st.success("تم استيراد الإعدادات بنجاح!")
+ st.rerun()
+ except Exception as e:
+ st.error(f"حدث خطأ أثناء استيراد الإعدادات: {str(e)}")
+
+ with utilities_tab3:
+ st.markdown("### النسخ الاحتياطي والاستعادة")
+ backup_tab1, backup_tab2 = st.tabs(["إنشاء نسخة احتياطية", "استعادة من نسخة احتياطية"])
+
+ with backup_tab1:
+ st.markdown("#### إنشاء نسخة احتياطية كاملة")
+ st.markdown("تقوم هذه العملية بإنشاء نسخة احتياطية كاملة لجميع بيانات التسعير الحالية.")
+
+ if st.button("إنشاء نسخة احتياطية كاملة", key="create_full_backup"):
+ backup_data = {
+ "timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
+ "construction_item": st.session_state.construction_item if 'construction_item' in st.session_state else None,
+ "current_pricing": st.session_state.current_pricing if 'current_pricing' in st.session_state else None,
+ "pricing_models": st.session_state.pricing_models if 'pricing_models' in st.session_state else [],
+ "manual_items": st.session_state.manual_items.to_dict('records') if 'manual_items' in st.session_state else []
+ }
+
+ backup_json = json.dumps(backup_data, ensure_ascii=False, indent=2)
+
+ filename = f"wahbi_pricing_backup_{datetime.now().strftime('%Y%m%d_%H%M%S')}.json"
+ st.download_button(
+ label="تنزيل النسخة الاحتياطية",
+ data=backup_json,
+ file_name=filename,
+ mime="application/json",
+ key="download_backup_button"
+ )
+ st.success("تم إنشاء النسخة الاحتياطية بنجاح!")
+
+ with backup_tab2:
+ st.markdown("#### استعادة من نسخة احتياطية")
+ st.markdown("يمكنك استعادة بيانات التسعير من نسخة احتياطية سابقة.")
+
+ backup_file = st.file_uploader("استعادة من نسخة احتياطية", type=["json"], key="restore_backup_file")
+ if backup_file is not None:
+ if st.button("استعادة البيانات", key="restore_from_backup"):
+ try:
+ backup_data = json.loads(backup_file.getvalue().decode('utf-8'))
+
+ # استعادة البيانات إلى الحالة الحالية
+ if 'construction_item' in backup_data and backup_data['construction_item']:
+ st.session_state.construction_item = backup_data['construction_item']
+
+ if 'current_pricing' in backup_data and backup_data['current_pricing']:
+ st.session_state.current_pricing = backup_data['current_pricing']
+
+ if 'pricing_models' in backup_data:
+ st.session_state.pricing_models = backup_data['pricing_models']
+
+ if 'manual_items' in backup_data and backup_data['manual_items']:
+ st.session_state.manual_items = pd.DataFrame(backup_data['manual_items'])
+
+ st.success(f"تم استعادة البيانات من النسخة الاحتياطية بنجاح! (تاريخ النسخة: {backup_data.get('timestamp', 'غير معروف')})")
+ st.rerun()
+ except Exception as e:
+ st.error(f"حدث خطأ أثناء استعادة البيانات: {str(e)}")
+
+ with utilities_tab4:
+ st.markdown("### مقارنة نماذج التسعير")
+ st.markdown("هذه الأداة تتيح لك مقارنة نماذج التسعير المختلفة واختيار الأفضل منها.")
+
+ # تهيئة قائمة نماذج التسعير إذا لم تكن موجودة
+ if 'pricing_models' not in st.session_state:
+ st.session_state.pricing_models = []
+
+ # إضافة النموذج الحالي للمقارنة
+ if st.button("إضافة النموذج الحالي للمقارنة", key="add_current_model"):
+ if 'current_pricing' in st.session_state and st.session_state.current_pricing is not None:
+ model_name = st.session_state.current_pricing.get('name', 'نموذج بدون اسم')
+ model_data = copy.deepcopy(st.session_state.current_pricing)
+ # تحقق من عدم وجود نموذج بنفس الاسم
+ exists = False
+ for model in st.session_state.pricing_models:
+ if model.get('name') == model_name:
+ exists = True
+ break
+
+ if not exists:
+ st.session_state.pricing_models.append(model_data)
+ st.success(f"تم إضافة نموذج '{model_name}' للمقارنة!")
+ else:
+ st.warning("يوجد نموذج بنفس الاسم في المقارنة بالفعل!")
+ else:
+ st.error("لا يوجد نموذج تسعير حالي للإضافة. يرجى إنشاء تسعير جديد أولاً.")
+
+ # عرض جدول المقارنة إذا كان هناك نماذج مضافة
+ if len(st.session_state.pricing_models) > 0:
+ st.markdown("### جدول مقارنة نماذج التسعير")
+
+ comparison_data = []
+ for model in st.session_state.pricing_models:
+ # حساب إجمالي التكلفة
+ items_df = pd.DataFrame(model.get('items', {}))
+ total_price = 0
+ if not items_df.empty and 'الإجمالي' in items_df.columns:
+ total_price = items_df['الإجمالي'].sum()
+
+ comparison_data.append({
+ "اسم النموذج": model.get('name', 'غير معروف'),
+ "طريقة التسعير": model.get('method', 'غير معروفة'),
+ "إجمالي التكلفة": f"{total_price:,.2f} ريال",
+ "عدد البنود": len(items_df) if not items_df.empty else 0,
+ })
+
+ comparison_df = pd.DataFrame(comparison_data)
+ st.dataframe(comparison_df, use_container_width=True, hide_index=True)
+
+ # عرض رسم بياني للمقارنة
+ if len(comparison_data) > 1:
+ st.markdown("### رسم بياني للمقارنة")
+
+ # استخراج البيانات للرسم البياني
+ models = [data["اسم النموذج"] for data in comparison_data]
+ values = [float(data["إجمالي التكلفة"].replace("ريال", "").replace(",", "")) for data in comparison_data]
+
+ # رسم بياني شريطي
+ fig = go.Figure(data=[
+ go.Bar(x=models, y=values, marker_color='rgb(26, 118, 255)')
+ ])
+
+ fig.update_layout(
+ title="مقارنة التكلفة الإجمالية بين نماذج التسعير",
+ xaxis_title="نموذج التسعير",
+ yaxis_title="التكلفة الإجمالية (ريال)"
+ )
+
+ st.plotly_chart(fig, use_container_width=True)
+
+ # أزرار إدارة النماذج
+ col1, col2 = st.columns(2)
+ with col1:
+ if st.button("حذف جميع النماذج", key="clear_comparison"):
+ st.session_state.pricing_models = []
+ st.success("تم مسح جميع نماذج المقارنة!")
+ st.rerun()
+
+ with col2:
+ # تحديد نموذج للحذف
+ model_to_delete = st.selectbox(
+ "اختر نموذج للحذف من المقارنة",
+ options=[model.get('name', f"نموذج {i+1}") for i, model in enumerate(st.session_state.pricing_models)],
+ key="model_to_delete"
+ )
+
+ if st.button("حذف النموذج المحدد", key="delete_selected_model"):
+ for i, model in enumerate(st.session_state.pricing_models):
+ if model.get('name') == model_to_delete:
+ st.session_state.pricing_models.pop(i)
+ st.warning(f"تم حذف النموذج '{model_to_delete}'")
+ st.rerun()
+ break
+ else:
+ st.info("لا توجد نماذج للمقارنة حالياً. يرجى إضافة النموذج الحالي للمقارنة أولاً.")
+
+ with utilities_tab5:
+ st.markdown("### إنشاء تقارير التسعير")
+ st.markdown("يمكنك استخدام هذه الأداة لإنشاء تقارير مفصلة عن التسعير.")
+
+ report_type = st.selectbox(
+ "اختر نوع التقرير",
+ options=["تقرير ملخص", "تقرير تفصيلي", "تقرير المقارنة"],
+ key="report_type_selector"
+ )
+
+ if st.button("إنشاء التقرير", key="generate_report_button"):
+ if report_type == "تقرير ملخص" and 'current_pricing' in st.session_state and st.session_state.current_pricing is not None:
+ # إنشاء تقرير ملخص
+ if isinstance(st.session_state.current_pricing.get('items'), pd.DataFrame):
+ df = st.session_state.current_pricing['items'].copy()
+
+ # حساب الإجماليات
+ total_price = df['الإجمالي'].sum() if 'الإجمالي' in df.columns else 0
+
+ # تقدير تكاليف المكونات
+ materials_cost = total_price * 0.6 # تقدير تقريبي للمواد
+ labor_cost = total_price * 0.25 # تقدير تقريبي للعمالة
+ equipment_cost = total_price * 0.15 # تقدير تقريبي للمعدات
+ admin_cost = total_price * 0.05 # تقدير تقريبي للمصاريف الإدارية
+ profit_margin = total_price * 0.1 # تقدير تقريبي لهامش الربح
+ final_total = total_price * 1.15 # إجمالي بعد إضافة المصاريف الإدارية وهامش الربح
+
+ # إنشاء جدول الملخص
+ summary = pd.DataFrame({
+ "بند التكلفة": ["إجمالي المواد", "إجمالي الأجور", "إجمالي المعدات", "المصاريف الإدارية", "هامش الربح", "الإجمالي النهائي"],
+ "القيمة": [
+ materials_cost,
+ labor_cost,
+ equipment_cost,
+ admin_cost,
+ profit_margin,
+ final_total
+ ]
+ })
+
+ # تنسيق التقرير
+ styled_df = summary.style.format({
+ "القيمة": "{:,.2f} ريال"
+ })
+
+ # تحويل الجدول إلى HTML
+ html = f"""
+
+
+
+ تقرير ملخص التسعير
+
+
+
+
+
+ ملخص التكاليف
+ {styled_df.to_html()}
+
+ البيانات الأساسية
+
+ - عدد البنود: {len(df)}
+ - طريقة التسعير: {st.session_state.current_pricing.get('method', 'غير محددة')}
+
+
+
+
+
+ """
+
+ # تقديم زر التنزيل
+ st.download_button(
+ label="تنزيل التقرير الملخص",
+ data=html,
+ file_name="pricing_summary_report.html",
+ mime="text/html",
+ key="download_summary_report"
+ )
+
+ st.success("تم إنشاء التقرير الملخص بنجاح!")
+ else:
+ st.error("تعذر قراءة بيانات التسعير الحالي. يرجى التأكد من وجود تسعير صالح.")
+
+ elif report_type == "تقرير تفصيلي" and 'current_pricing' in st.session_state and st.session_state.current_pricing is not None:
+ # سيتم تنفيذ التقرير التفصيلي
+ st.info("جاري إعداد التقرير التفصيلي...")
+ # هنا يمكن تنفيذ كود إنشاء التقرير التفصيلي
+ st.success("تم إنشاء التقرير التفصيلي. سيتم تطوير هذه الميزة قريباً.")
+
+ elif report_type == "تقرير المقارنة" and 'pricing_models' in st.session_state and len(st.session_state.pricing_models) > 0:
+ # سيتم تنفيذ تقرير المقارنة
+ st.info("جاري إعداد تقرير المقارنة...")
+ # هنا يمكن تنفيذ كود إنشاء تقرير المقارنة
+ st.success("تم إنشاء تقرير المقارنة. سيتم تطوير هذه الميزة قريباً.")
+
+ else:
+ st.error("لا توجد بيانات كافية لإنشاء التقرير المطلوب. يرجى التأكد من وجود تسعير أو نماذج مقارنة.")
\ No newline at end of file