diff --git "a/modules/pricing/pricing_app.py" "b/modules/pricing/pricing_app.py"
deleted file mode 100644--- "a/modules/pricing/pricing_app.py"
+++ /dev/null
@@ -1,4358 +0,0 @@
-"""
-تطبيق وحدة التسعير المتكاملة
-"""
-
-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)
- :return: زر مُنسّق
- """
- # استخدام مكونات Streamlit فقط بدون HTML
- with st.container():
- # إنشاء مساحة تعرض الزر فقط
- col1 = st.columns([1])
-
- # إضافة الأيقونة للنص إذا تم تزويدها
- display_label = f"{icon} {label}" if icon else label
-
- # إنشاء الزر مباشرة باستخدام Streamlit
- clicked = col1[0].button(
- display_label,
- key=key,
- on_click=on_click,
- args=args,
- use_container_width=full_width
- )
-
- return clicked
-
-# وظيفة لإنشاء أزرار أيقونات صغيرة
-def icon_button(icon, key, type="primary", on_click=None, args=None, tooltip=""):
- """
- إنشاء زر أيقونة صغير
- :param icon: الأيقونة (emoji)
- :param key: مفتاح الزر الفريد
- :param type: نوع التنسيق
- :param on_click: الدالة التي سيتم تنفيذها عند النقر
- :param args: معاملات الدالة
- :param tooltip: تلميح عند تمرير المؤشر فوق الزر
- :return: زر أيقونة
- """
- # استخدام مكونات Streamlit فقط
- with st.container():
- # إضافة تلميح باستخدام Streamlit
- if tooltip:
- st.caption(tooltip)
-
- # إنشاء زر الأيقونة
- clicked = st.button(
- icon,
- key=key,
- on_click=on_click,
- args=args,
- help=tooltip # استخدام خاصية help لعرض التلميح
- )
-
- 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):
- """عرض واجهة وحدة التسعير"""
-
- # استخدام مكونات Streamlit مباشرة بدلاً من HTML
- st.title("وحدة التسعير المتكاملة")
-
- 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.subheader("بنود التسعير غير المتزن")
-
- # تطبيق التنسيق على الجدول
- 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.subheader("مقارنة التسعير المتوازن وغير المتوازن")
-
- 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.subheader("تحليل بصري للتسعير غير المتوازن")
-
- # إعداد البيانات للرسم البياني
- 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.divider()
- st.subheader("حفظ وتصدير البيانات")
-
- 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