diff --git "a/fixed_pricing_app_complete.py" "b/fixed_pricing_app_complete.py"
--- "a/fixed_pricing_app_complete.py"
+++ "b/fixed_pricing_app_complete.py"
@@ -1,1760 +1,1760 @@
-"""
-وحدة التسعير - التطبيق الرئيسي
-"""
-
-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 time
-import io
-import os
-import json
-import base64
-from pathlib import Path
-
-class PricingApp:
- """وحدة التسعير"""
-
- def __init__(self):
- """تهيئة وحدة التسعير"""
-
- # تهيئة حالة الجلسة
- if 'bill_of_quantities' not in st.session_state:
- st.session_state.bill_of_quantities = [
- {
- 'id': 1,
- 'code': 'A-001',
- 'description': 'أعمال الحفر والردم',
- 'unit': 'م3',
- 'quantity': 1500,
- 'unit_price': 45,
- 'total_price': 67500,
- 'category': 'أعمال ترابية'
- },
- {
- 'id': 2,
- 'code': 'A-002',
- 'description': 'توريد وصب خرسانة عادية',
- 'unit': 'م3',
- 'quantity': 250,
- 'unit_price': 350,
- 'total_price': 87500,
- 'category': 'أعمال خرسانية'
- },
- {
- 'id': 3,
- 'code': 'A-003',
- 'description': 'توريد وصب خرسانة مسلحة للأساسات',
- 'unit': 'م3',
- 'quantity': 180,
- 'unit_price': 450,
- 'total_price': 81000,
- 'category': 'أعمال خرسانية'
- },
- {
- 'id': 4,
- 'code': 'A-004',
- 'description': 'توريد وصب خرسانة مسلحة للأعمدة',
- 'unit': 'م3',
- 'quantity': 120,
- 'unit_price': 500,
- 'total_price': 60000,
- 'category': 'أعمال خرسانية'
- },
- {
- 'id': 5,
- 'code': 'A-005',
- 'description': 'توريد وتركيب حديد تسليح',
- 'unit': 'طن',
- 'quantity': 45,
- 'unit_price': 3000,
- 'total_price': 135000,
- 'category': 'أعمال حديد'
- },
- {
- 'id': 6,
- 'code': 'A-006',
- 'description': 'توريد وبناء طابوق',
- 'unit': 'م2',
- 'quantity': 1200,
- 'unit_price': 45,
- 'total_price': 54000,
- 'category': 'أعمال بناء'
- },
- {
- 'id': 7,
- 'code': 'A-007',
- 'description': 'أعمال اللياسة والتشطيبات',
- 'unit': 'م2',
- 'quantity': 2400,
- 'unit_price': 35,
- 'total_price': 84000,
- 'category': 'أعمال تشطيبات'
- },
- {
- 'id': 8,
- 'code': 'A-008',
- 'description': 'أعمال الدهانات',
- 'unit': 'م2',
- 'quantity': 2400,
- 'unit_price': 25,
- 'total_price': 60000,
- 'category': 'أعمال تشطيبات'
- },
- {
- 'id': 9,
- 'code': 'A-009',
- 'description': 'توريد وتركيب أبواب خشبية',
- 'unit': 'عدد',
- 'quantity': 24,
- 'unit_price': 750,
- 'total_price': 18000,
- 'category': 'أعمال نجارة'
- },
- {
- 'id': 10,
- 'code': 'A-010',
- 'description': 'توريد وتركيب نوافذ ألمنيوم',
- 'unit': 'م2',
- 'quantity': 120,
- 'unit_price': 350,
- 'total_price': 42000,
- 'category': 'أعمال ألمنيوم'
- }
- ]
-
- if 'cost_analysis' not in st.session_state:
- st.session_state.cost_analysis = [
- {
- 'id': 1,
- 'category': 'تكاليف مباشرة',
- 'subcategory': 'مواد',
- 'description': 'خرسانة',
- 'amount': 120000,
- 'percentage': 17.9
- },
- {
- 'id': 2,
- 'category': 'تكاليف مباشرة',
- 'subcategory': 'مواد',
- 'description': 'حديد تسليح',
- 'amount': 135000,
- 'percentage': 20.1
- },
- {
- 'id': 3,
- 'category': 'تكاليف مباشرة',
- 'subcategory': 'مواد',
- 'description': 'طابوق',
- 'amount': 54000,
- 'percentage': 8.1
- },
- {
- 'id': 4,
- 'category': 'تكاليف مباشرة',
- 'subcategory': 'عمالة',
- 'description': 'عمالة تنفيذ',
- 'amount': 120000,
- 'percentage': 17.9
- },
- {
- 'id': 5,
- 'category': 'تكاليف مباشرة',
- 'subcategory': 'معدات',
- 'description': 'معدات إنشائية',
- 'amount': 85000,
- 'percentage': 12.7
- },
- {
- 'id': 6,
- 'category': 'تكاليف غير مباشرة',
- 'subcategory': 'إدارة',
- 'description': 'إدارة المشروع',
- 'amount': 45000,
- 'percentage': 6.7
- },
- {
- 'id': 7,
- 'category': 'تكاليف غير مباشرة',
- 'subcategory': 'إدارة',
- 'description': 'إشراف هندسي',
- 'amount': 35000,
- 'percentage': 5.2
- },
- {
- 'id': 8,
- 'category': 'تكاليف غير مباشرة',
- 'subcategory': 'عامة',
- 'description': 'تأمينات وضمانات',
- 'amount': 25000,
- 'percentage': 3.7
- },
- {
- 'id': 9,
- 'category': 'تكاليف غير مباشرة',
- 'subcategory': 'عامة',
- 'description': 'مصاريف إدارية',
- 'amount': 30000,
- 'percentage': 4.5
- },
- {
- 'id': 10,
- 'category': 'أرباح',
- 'subcategory': 'أرباح',
- 'description': 'هامش الربح',
- 'amount': 55000,
- 'percentage': 8.2
- }
- ]
-
- if 'price_scenarios' not in st.session_state:
- st.session_state.price_scenarios = [
- {
- 'id': 1,
- 'name': 'السيناريو الأساسي',
- 'description': 'التسعير الأساسي مع هامش ربح 8%',
- 'total_cost': 615000,
- 'profit_margin': 8.2,
- 'total_price': 670000,
- 'is_active': True
- },
- {
- 'id': 2,
- 'name': 'سيناريو تنافسي',
- 'description': 'تخفيض هامش الربح للمنافسة',
- 'total_cost': 615000,
- 'profit_margin': 5.0,
- 'total_price': 650000,
- 'is_active': False
- },
- {
- 'id': 3,
- 'name': 'سيناريو مرتفع',
- 'description': 'زيادة هامش الربح للمشاريع ذات المخاطر العالية',
- 'total_cost': 615000,
- 'profit_margin': 12.0,
- 'total_price': 700000,
- 'is_active': False
- }
- ]
-
- # إضافة بيانات المقارنة التنافسية
- if 'competitive_analysis' not in st.session_state:
- st.session_state.competitive_analysis = [
- {
- 'id': 1,
- 'competitor': 'شركة الإنشاءات المتحدة',
- 'project_type': 'مباني سكنية',
- 'price_per_sqm': 1800,
- 'delivery_time': 12,
- 'quality_rating': 4.2,
- 'market_share': 15.5
- },
- {
- 'id': 2,
- 'competitor': 'مجموعة البناء الحديث',
- 'project_type': 'مباني سكنية',
- 'price_per_sqm': 2100,
- 'delivery_time': 10,
- 'quality_rating': 4.5,
- 'market_share': 18.2
- },
- {
- 'id': 3,
- 'competitor': 'شركة الإعمار الدولية',
- 'project_type': 'مباني سكنية',
- 'price_per_sqm': 2300,
- 'delivery_time': 14,
- 'quality_rating': 4.7,
- 'market_share': 22.0
- },
- {
- 'id': 4,
- 'competitor': 'مؤسسة البناء المتكامل',
- 'project_type': 'مباني سكنية',
- 'price_per_sqm': 1750,
- 'delivery_time': 15,
- 'quality_rating': 3.8,
- 'market_share': 12.5
- },
- {
- 'id': 5,
- 'competitor': 'شركتنا',
- 'project_type': 'مباني سكنية',
- 'price_per_sqm': 1950,
- 'delivery_time': 11,
- 'quality_rating': 4.4,
- 'market_share': 14.8
- }
- ]
-
- def run(self):
- """تشغيل وحدة التسعير"""
- # استدعاء دالة العرض
- self.render()
-
- def render(self):
- """عرض واجهة وحدة التسعير"""
-
- st.markdown("
وحدة التسعير
", unsafe_allow_html=True)
-
- tabs = st.tabs([
- "لوحة التحكم",
- "جدول الكميات",
- "تحليل التكاليف",
- "سيناريوهات التسعير",
- "المقارنة التنافسية",
- "التقارير"
- ])
-
- with tabs[0]:
- self._render_dashboard_tab()
-
- with tabs[1]:
- self._render_bill_of_quantities_tab()
-
- with tabs[2]:
- self._render_cost_analysis_tab()
-
- with tabs[3]:
- self._render_pricing_scenarios_tab()
-
- with tabs[4]:
- self._render_competitive_analysis_tab()
-
- with tabs[5]:
- self._render_reports_tab()
-
- def _render_dashboard_tab(self):
- """عرض تبويب لوحة التحكم"""
-
- st.markdown("### لوحة تحكم التسعير")
-
- # عرض ملخص التسعير
- col1, col2, col3, col4 = st.columns(4)
-
- # حساب إجمالي التكاليف
- total_direct_cost = sum(item['amount'] for item in st.session_state.cost_analysis if item['category'] == 'تكاليف مباشرة')
- total_indirect_cost = sum(item['amount'] for item in st.session_state.cost_analysis if item['category'] == 'تكاليف غير مباشرة')
- total_profit = sum(item['amount'] for item in st.session_state.cost_analysis if item['category'] == 'أرباح')
- total_cost = total_direct_cost + total_indirect_cost
- total_price = total_cost + total_profit
-
- with col1:
- st.metric("إجمالي التكاليف المباشرة", f"{total_direct_cost:,.0f} ريال")
-
- with col2:
- st.metric("إجمالي التكاليف غير المباشرة", f"{total_indirect_cost:,.0f} ريال")
-
- with col3:
- st.metric("إجمالي التكاليف", f"{total_cost:,.0f} ريال")
-
- with col4:
- st.metric("السعر الإجمالي", f"{total_price:,.0f} ريال")
-
- # عرض توزيع التكاليف
- st.markdown("### توزيع التكاليف")
-
- # تجميع البيانات حسب الفئة
- cost_categories = {}
-
- for item in st.session_state.cost_analysis:
- category = item['category']
- if category in cost_categories:
- cost_categories[category] += item['amount']
- else:
- cost_categories[category] = item['amount']
-
- # إنشاء DataFrame للرسم البياني
- cost_df = pd.DataFrame({
- 'الفئة': list(cost_categories.keys()),
- 'المبلغ': list(cost_categories.values())
- })
-
- # إنشاء رسم بياني دائري
- fig = px.pie(
- cost_df,
- values='المبلغ',
- names='الفئة',
- title='توزيع التكاليف حسب الفئة',
- color_discrete_sequence=px.colors.qualitative.Set3
- )
-
- st.plotly_chart(fig, use_container_width=True)
-
- # عرض توزيع التكاليف المباشرة
- st.markdown("### توزيع التكاليف المباشرة")
-
- # تجميع البيانات حسب الفئة الفرعية للتكاليف المباشرة
- direct_cost_subcategories = {}
-
- for item in st.session_state.cost_analysis:
- if item['category'] == 'تكاليف مباشرة':
- subcategory = item['subcategory']
- if subcategory in direct_cost_subcategories:
- direct_cost_subcategories[subcategory] += item['amount']
- else:
- direct_cost_subcategories[subcategory] = item['amount']
-
- # إنشاء DataFrame للرسم البياني
- direct_cost_df = pd.DataFrame({
- 'الفئة الفرعية': list(direct_cost_subcategories.keys()),
- 'المبلغ': list(direct_cost_subcategories.values())
- })
-
- # إنشاء رسم بياني دائري
- fig = px.pie(
- direct_cost_df,
- values='المبلغ',
- names='الفئة الفرعية',
- title='توزيع التكاليف المباشرة',
- color_discrete_sequence=px.colors.qualitative.Pastel
- )
-
- st.plotly_chart(fig, use_container_width=True)
-
- # عرض توزيع التكاليف غير المباشرة
- st.markdown("### توزيع التكاليف غير المباشرة")
-
- # تجميع البيانات حسب الفئة الفرعية للتكاليف غير المباشرة
- indirect_cost_subcategories = {}
-
- for item in st.session_state.cost_analysis:
- if item['category'] == 'تكاليف غير مباشرة':
- subcategory = item['subcategory']
- if subcategory in indirect_cost_subcategories:
- indirect_cost_subcategories[subcategory] += item['amount']
- else:
- indirect_cost_subcategories[subcategory] = item['amount']
-
- # إنشاء DataFrame للرسم البياني
- indirect_cost_df = pd.DataFrame({
- 'الفئة الفرعية': list(indirect_cost_subcategories.keys()),
- 'المبلغ': list(indirect_cost_subcategories.values())
- })
-
- # إنشاء رسم بياني دائري
- fig = px.pie(
- indirect_cost_df,
- values='المبلغ',
- names='الفئة الفرعية',
- title='توزيع التكاليف غير المباشرة',
- color_discrete_sequence=px.colors.qualitative.Pastel1
- )
-
- st.plotly_chart(fig, use_container_width=True)
-
- def _render_bill_of_quantities_tab(self):
- """عرض تبويب جدول الكميات"""
-
- st.markdown("### جدول الكميات")
-
- # إنشاء DataFrame من بيانات جدول الكميات
- boq_df = pd.DataFrame(st.session_state.bill_of_quantities)
-
- # عرض جدول الكميات
- st.dataframe(
- boq_df[['code', 'description', 'unit', 'quantity', 'unit_price', 'total_price', 'category']],
- column_config={
- 'code': 'الكود',
- 'description': 'الوصف',
- 'unit': 'الوحدة',
- 'quantity': 'الكمية',
- 'unit_price': st.column_config.NumberColumn('سعر الوحدة', format='%d ريال'),
- 'total_price': st.column_config.NumberColumn('السعر الإجمالي', format='%d ريال'),
- 'category': 'الفئة'
- },
- hide_index=True,
- use_container_width=True
- )
-
- # إضافة بند جديد
- st.markdown("### إضافة بند جديد")
-
- col1, col2 = st.columns(2)
-
- with col1:
- new_code = st.text_input("الكود", key="new_boq_code")
- new_description = st.text_input("الوصف", key="new_boq_description")
- new_unit = st.selectbox("الوحدة", ["م3", "م2", "طن", "عدد", "متر طولي"], key="new_boq_unit")
-
- with col2:
- new_quantity = st.number_input("الكمية", min_value=0.0, step=1.0, key="new_boq_quantity")
- new_unit_price = st.number_input("سعر الوحدة", min_value=0.0, step=10.0, key="new_boq_unit_price")
- new_category = st.selectbox(
- "الفئة",
- [
- "أعمال ترابية",
- "أعمال خرسانية",
- "أعمال حديد",
- "أعمال بناء",
- "أعمال تشطيبات",
- "أعمال نجارة",
- "أعمال ألمنيوم",
- "أعمال كهربائية",
- "أعمال ميكانيكية",
- "أعمال صحية"
- ],
- key="new_boq_category"
- )
-
- if st.button("إضافة البند", key="add_boq_item"):
- if new_code and new_description and new_quantity > 0 and new_unit_price > 0:
- # حساب السعر الإجمالي
- new_total_price = new_quantity * new_unit_price
-
- # إضافة بند جديد
- new_id = max([item['id'] for item in st.session_state.bill_of_quantities]) + 1
-
- st.session_state.bill_of_quantities.append({
- 'id': new_id,
- 'code': new_code,
- 'description': new_description,
- 'unit': new_unit,
- 'quantity': new_quantity,
- 'unit_price': new_unit_price,
- 'total_price': new_total_price,
- 'category': new_category
- })
-
- st.success(f"تمت إضافة البند بنجاح: {new_description}")
-
- # تحديث الصفحة لعرض البند الجديد
- st.rerun()
- else:
- st.error("يرجى إدخال جميع البيانات المطلوبة بشكل صحيح")
-
- # عرض ملخص جدول الكميات (إزالة التكرار)
- st.markdown("### ملخص جدول الكميات")
-
- # تجميع البيانات حسب الفئة
- category_totals = {}
- for item in st.session_state.bill_of_quantities:
- category = item['category']
- if category in category_totals:
- category_totals[category] += item['total_price']
- else:
- category_totals[category] = item['total_price']
-
- # إنشاء DataFrame للرسم البياني
- category_df = pd.DataFrame({
- 'الفئة': list(category_totals.keys()),
- 'المبلغ': list(category_totals.values())
- })
-
- # ترتيب البيانات تنازليًا حسب المبلغ
- category_df = category_df.sort_values('المبلغ', ascending=False)
-
- # إنشاء رسم بياني شريطي
- fig = px.bar(
- category_df,
- x='الفئة',
- y='المبلغ',
- title='إجمالي تكلفة البنود حسب الفئة',
- color='الفئة',
- text_auto=True
- )
-
- st.plotly_chart(fig, use_container_width=True)
-
- # حساب إجمالي جدول الكميات
- total_boq = sum(item['total_price'] for item in st.session_state.bill_of_quantities)
-
- def _render_cost_analysis_tab(self):
- """عرض تبويب تحليل التكاليف"""
-
- st.markdown("### تحليل التكاليف")
-
- # عرض جدول تحليل التكاليف
- cost_df = pd.DataFrame(st.session_state.cost_analysis)
-
- st.dataframe(
- cost_df[['category', 'subcategory', 'description', 'amount', 'percentage']],
- column_config={
- 'category': 'الفئة',
- 'subcategory': 'الفئة الفرعية',
- 'description': 'الوصف',
- 'amount': st.column_config.NumberColumn('المبلغ', format='%d ريال'),
- 'percentage': st.column_config.NumberColumn('النسبة المئوية', format='%.1f%%')
- },
- hide_index=True,
- use_container_width=True
- )
-
- # إضافة بند تكلفة جديد
- st.markdown("### إضافة بند تكلفة جديد")
-
- col1, col2 = st.columns(2)
-
- with col1:
- new_category = st.selectbox(
- "الفئة",
- ["تكاليف مباشرة", "تكاليف غير مباشرة", "أرباح"],
- key="new_cost_category"
- )
-
- # تحديد الفئات الفرعية بناءً على الفئة المختارة
- subcategory_options = []
- if new_category == "تكاليف مباشرة":
- subcategory_options = ["مواد", "عمالة", "معدات"]
- elif new_category == "تكاليف غير مباشرة":
- subcategory_options = ["إدارة", "عامة", "تمويل"]
- else:
- subcategory_options = ["أرباح"]
-
- new_subcategory = st.selectbox(
- "الفئة الفرعية",
- subcategory_options,
- key="new_cost_subcategory"
- )
-
- new_description = st.text_input("الوصف", key="new_cost_description")
-
- with col2:
- new_amount = st.number_input("المبلغ", min_value=0.0, step=1000.0, key="new_cost_amount")
-
- # حساب إجمالي التكاليف الحالية
- total_cost = sum(item['amount'] for item in st.session_state.cost_analysis)
-
- # حساب النسبة المئوية التقريبية
- if total_cost > 0:
- estimated_percentage = (new_amount / total_cost) * 100
- else:
- estimated_percentage = 0
-
- st.metric("النسبة المئوية التقديرية", f"{estimated_percentage:.1f}%")
-
- if st.button("إضافة بند التكلفة", key="add_cost_item"):
- if new_description and new_amount > 0:
- # إضافة بند جديد
- new_id = max([item['id'] for item in st.session_state.cost_analysis]) + 1
-
- # حساب النسبة المئوية الفعلية بعد إضافة البند الجديد
- new_total = total_cost + new_amount
-
- # إعادة حساب النسب المئوية لجميع البنود
- for item in st.session_state.cost_analysis:
- item['percentage'] = (item['amount'] / new_total) * 100
-
- # إضافة البند الجديد
- st.session_state.cost_analysis.append({
- 'id': new_id,
- 'category': new_category,
- 'subcategory': new_subcategory,
- 'description': new_description,
- 'amount': new_amount,
- 'percentage': (new_amount / new_total) * 100
- })
-
- st.success(f"تمت إضافة بند التكلفة بنجاح: {new_description}")
-
- # تحديث الصفحة لعرض البند الجديد
- st.rerun()
- else:
- st.error("يرجى إدخال جميع البيانات المطلوبة بشكل صحيح")
-
- # تحليل التكاليف حسب الفئة والفئة الفرعية
- st.markdown("### تحليل التكاليف حسب الفئة والفئة الفرعية")
-
- # تجميع البيانات حسب الفئة والفئة الفرعية
- cost_by_category_subcategory = {}
-
- for item in st.session_state.cost_analysis:
- category = item['category']
- subcategory = item['subcategory']
- key = f"{category} - {subcategory}"
-
- if key in cost_by_category_subcategory:
- cost_by_category_subcategory[key] += item['amount']
- else:
- cost_by_category_subcategory[key] = item['amount']
-
- # إنشاء DataFrame للرسم البياني
- cost_category_subcategory_df = pd.DataFrame({
- 'الفئة والفئة الفرعية': list(cost_by_category_subcategory.keys()),
- 'المبلغ': list(cost_by_category_subcategory.values())
- })
-
- # ترتيب البيانات تنازليًا حسب المبلغ
- cost_category_subcategory_df = cost_category_subcategory_df.sort_values('المبلغ', ascending=False)
-
- # إنشاء رسم بياني شريطي
- fig = px.bar(
- cost_category_subcategory_df,
- x='الفئة والفئة الفرعية',
- y='المبلغ',
- title='تحليل التكاليف حسب الفئة والفئة الفرعية',
- color='الفئة والفئة الفرعية',
- text_auto=True
- )
-
- st.plotly_chart(fig, use_container_width=True)
-
- # تحليل نسب التكاليف
- st.markdown("### تحليل نسب التكاليف")
-
- # إنشاء رسم بياني للنسب المئوية
- percentage_df = pd.DataFrame(st.session_state.cost_analysis)
-
- fig = px.treemap(
- percentage_df,
- path=['category', 'subcategory', 'description'],
- values='amount',
- title='تحليل هيكل التكاليف',
- color='percentage',
- color_continuous_scale='RdBu',
- color_continuous_midpoint=np.average(percentage_df['percentage'])
- )
-
- st.plotly_chart(fig, use_container_width=True)
-
- # تحليل اتجاهات التكاليف (بيانات افتراضية)
- st.markdown("### تحليل اتجاهات التكاليف")
-
- # إنشاء بيانات افتراضية للاتجاهات
- months = ['يناير', 'فبراير', 'مارس', 'أبريل', 'مايو', 'يونيو']
- direct_costs = [510000, 520000, 515000, 525000, 530000, 514000]
- indirect_costs = [130000, 135000, 132000, 138000, 140000, 135000]
-
- # إنشاء DataFrame للرسم البياني
- trends_df = pd.DataFrame({
- 'الشهر': months * 2,
- 'نوع التكلفة': ['تكاليف مباشرة'] * 6 + ['تكاليف غير مباشرة'] * 6,
- 'المبلغ': direct_costs + indirect_costs
- })
-
- # إنشاء رسم بياني خطي
- fig = px.line(
- trends_df,
- x='الشهر',
- y='المبلغ',
- color='نوع التكلفة',
- title='اتجاهات التكاليف على مدار الأشهر الستة الماضية',
- markers=True
- )
-
- st.plotly_chart(fig, use_container_width=True)
-
- def _render_pricing_scenarios_tab(self):
- """عرض تبويب سيناريوهات التسعير"""
-
- st.markdown("### سيناريوهات التسعير")
-
- # عرض جدول سيناريوهات التسعير
- scenarios_df = pd.DataFrame(st.session_state.price_scenarios)
-
- st.dataframe(
- scenarios_df[['name', 'description', 'total_cost', 'profit_margin', 'total_price', 'is_active']],
- column_config={
- 'name': 'اسم السيناريو',
- 'description': 'الوصف',
- 'total_cost': st.column_config.NumberColumn('إجمالي التكلفة', format='%d ريال'),
- 'profit_margin': st.column_config.NumberColumn('هامش الربح', format='%.1f%%'),
- 'total_price': st.column_config.NumberColumn('السعر الإجمالي', format='%d ريال'),
- 'is_active': st.column_config.CheckboxColumn('نشط')
- },
- hide_index=True,
- use_container_width=True
- )
-
- # إنشاء سيناريو جديد
- st.markdown("### إنشاء سيناريو جديد")
-
- col1, col2 = st.columns(2)
-
- # حساب إجمالي التكاليف
- total_cost = sum(item['amount'] for item in st.session_state.cost_analysis
- if item['category'] != 'أرباح')
-
- with col1:
- new_name = st.text_input("اسم السيناريو", key="new_scenario_name")
- new_description = st.text_input("وصف السيناريو", key="new_scenario_description")
-
- with col2:
- new_profit_margin = st.slider(
- "هامش الربح (%)",
- min_value=0.0,
- max_value=30.0,
- value=10.0,
- step=0.5,
- key="new_scenario_profit_margin"
- )
-
- # حساب السعر الإجمالي بناءً على هامش الربح
- profit_amount = total_cost * (new_profit_margin / 100)
- new_total_price = total_cost + profit_amount
-
- st.metric("إجمالي التكلفة", f"{total_cost:,.0f} ريال")
- st.metric("السعر الإجمالي المقترح", f"{new_total_price:,.0f} ريال")
-
- if st.button("إضافة السيناريو", key="add_scenario"):
- if new_name and new_description:
- # إضافة سيناريو جديد
- new_id = max([item['id'] for item in st.session_state.price_scenarios]) + 1
-
- st.session_state.price_scenarios.append({
- 'id': new_id,
- 'name': new_name,
- 'description': new_description,
- 'total_cost': total_cost,
- 'profit_margin': new_profit_margin,
- 'total_price': new_total_price,
- 'is_active': False
- })
-
- st.success(f"تمت إضافة السيناريو بنجاح: {new_name}")
-
- # تحديث الصفحة لعرض السيناريو الجديد
- st.rerun()
- else:
- st.error("يرجى إدخال جميع البيانات المطلوبة بشكل صحيح")
-
- # مقارنة السيناريوهات
- st.markdown("### مقارنة السيناريوهات")
-
- # إنشاء رسم بياني للمقارنة
- fig = go.Figure()
-
- for scenario in st.session_state.price_scenarios:
- fig.add_trace(go.Bar(
- name=scenario['name'],
- x=['التكلفة', 'الربح', 'السعر الإجمالي'],
- y=[
- scenario['total_cost'],
- scenario['total_price'] - scenario['total_cost'],
- scenario['total_price']
- ],
- text=[
- f"{scenario['total_cost']:,.0f}",
- f"{scenario['total_price'] - scenario['total_cost']:,.0f}",
- f"{scenario['total_price']:,.0f}"
- ],
- textposition='auto'
- ))
-
- fig.update_layout(
- title='مقارنة السيناريوهات',
- barmode='group',
- xaxis_title='العنصر',
- yaxis_title='المبلغ (ريال)'
- )
-
- st.plotly_chart(fig, use_container_width=True)
-
- # تحليل حساسية هامش الربح
- st.markdown("### تحليل حساسية هامش الربح")
-
- # إنشاء بيانات لتحليل الحساسية
- profit_margins = list(range(5, 26, 1)) # من 5% إلى 25%
- total_prices = [total_cost * (1 + margin/100) for margin in profit_margins]
-
- # إنشاء DataFrame للرسم البياني
- sensitivity_df = pd.DataFrame({
- 'هامش الربح (%)': profit_margins,
- 'السعر الإجمالي': total_prices
- })
-
- # إنشاء رسم بياني خطي
- fig = px.line(
- sensitivity_df,
- x='هامش الربح (%)',
- y='السعر الإجمالي',
- title='تحليل حساسية هامش الربح',
- markers=True
- )
-
- # إضافة خط أفقي يمثل السعر التنافسي (افتراضي)
- competitive_price = 650000
- fig.add_hline(
- y=competitive_price,
- line_dash="dash",
- line_color="red",
- annotation_text="السعر التنافسي",
- annotation_position="bottom right"
- )
-
- st.plotly_chart(fig, use_container_width=True)
-
- # تفعيل/تعطيل السيناريوهات
- st.markdown("### تفعيل/تعطيل السيناريوهات")
-
- for i, scenario in enumerate(st.session_state.price_scenarios):
- col1, col2 = st.columns([4, 1])
-
- with col1:
- st.write(f"**{scenario['name']}**: {scenario['description']}")
-
- with col2:
- is_active = st.checkbox(
- "تفعيل",
- value=scenario['is_active'],
- key=f"activate_scenario_{scenario['id']}"
- )
-
- # تحديث حالة التفعيل
- if is_active != scenario['is_active']:
- # إذا تم تفعيل سيناريو، قم بتعطيل جميع السيناريوهات الأخرى
- if is_active:
- for j, other_scenario in enumerate(st.session_state.price_scenarios):
- if j != i:
- other_scenario['is_active'] = False
-
- # تحديث حالة السيناريو الحالي
- scenario['is_active'] = is_active
-
- # تحديث الصفحة
- st.rerun()
-
- def _render_competitive_analysis_tab(self):
- """عرض تبويب المقارنة التنافسية"""
-
- st.markdown("### المقارنة التنافسية")
-
- # عرض جدول المقارنة التنافسية
- competitive_df = pd.DataFrame(st.session_state.competitive_analysis)
-
- st.dataframe(
- competitive_df[['competitor', 'project_type', 'price_per_sqm', 'delivery_time', 'quality_rating', 'market_share']],
- column_config={
- 'competitor': 'المنافس',
- 'project_type': 'نوع المشروع',
- 'price_per_sqm': st.column_config.NumberColumn('السعر لكل متر مربع', format='%d ريال'),
- 'delivery_time': st.column_config.NumberColumn('مدة التسليم (شهر)', format='%d'),
- 'quality_rating': st.column_config.NumberColumn('تقييم الجودة', format='%.1f/5.0'),
- 'market_share': st.column_config.NumberColumn('الحصة السوقية', format='%.1f%%')
- },
- hide_index=True,
- use_container_width=True
- )
-
- # إضافة منافس جديد
- st.markdown("### إضافة منافس جديد")
-
- col1, col2 = st.columns(2)
-
- with col1:
- new_competitor = st.text_input("اسم المنافس", key="new_competitor_name")
- new_project_type = st.selectbox(
- "نوع المشروع",
- ["مباني سكنية", "مباني تجارية", "مباني صناعية", "بنية تحتية"],
- key="new_competitor_project_type"
- )
- new_price_per_sqm = st.number_input(
- "السعر لكل متر مربع (ريال)",
- min_value=0,
- step=50,
- key="new_competitor_price"
- )
-
- with col2:
- new_delivery_time = st.number_input(
- "مدة التسليم (شهر)",
- min_value=1,
- max_value=36,
- step=1,
- key="new_competitor_delivery"
- )
- new_quality_rating = st.slider(
- "تقييم الجودة",
- min_value=1.0,
- max_value=5.0,
- value=3.5,
- step=0.1,
- key="new_competitor_quality"
- )
- new_market_share = st.number_input(
- "الحصة السوقية (%)",
- min_value=0.0,
- max_value=100.0,
- step=0.5,
- key="new_competitor_market_share"
- )
-
- if st.button("إضافة منافس", key="add_competitor"):
- if new_competitor and new_price_per_sqm > 0:
- # إضافة منافس جديد
- new_id = max([item['id'] for item in st.session_state.competitive_analysis]) + 1
-
- st.session_state.competitive_analysis.append({
- 'id': new_id,
- 'competitor': new_competitor,
- 'project_type': new_project_type,
- 'price_per_sqm': new_price_per_sqm,
- 'delivery_time': new_delivery_time,
- 'quality_rating': new_quality_rating,
- 'market_share': new_market_share
- })
-
- st.success(f"تمت إضافة المنافس بنجاح: {new_competitor}")
-
- # تحديث الصفحة لعرض المنافس الجديد
- st.rerun()
- else:
- st.error("يرجى إدخال جميع البيانات المطلوبة بشكل صحيح")
-
- # تحليل مقارنة الأسعار
- st.markdown("### مقارنة الأسعار")
-
- # إنشاء DataFrame للرسم البياني
- price_comparison_df = pd.DataFrame(st.session_state.competitive_analysis)
-
- # ترتيب البيانات تصاعديًا حسب السعر
- price_comparison_df = price_comparison_df.sort_values('price_per_sqm')
-
- # إنشاء رسم بياني شريطي
- fig = px.bar(
- price_comparison_df,
- x='competitor',
- y='price_per_sqm',
- title='مقارنة الأسعار لكل متر مربع',
- color='competitor',
- text_auto=True
- )
-
- # تمييز شركتنا
- for i, competitor in enumerate(price_comparison_df['competitor']):
- if competitor == 'شركتنا':
- fig.data[0].marker.color = ['blue' if x != i else 'red' for x in range(len(price_comparison_df))]
- break
-
- st.plotly_chart(fig, use_container_width=True)
-
- # تحليل مقارنة الجودة والسعر
- st.markdown("### مقارنة الجودة والسعر")
-
- # إنشاء رسم بياني للعلاقة بين السعر والجودة
- fig = px.scatter(
- price_comparison_df,
- x='price_per_sqm',
- y='quality_rating',
- size='market_share',
- color='competitor',
- title='العلاقة بين السعر والجودة والحصة السوقية',
- labels={
- 'price_per_sqm': 'السعر لكل متر مربع (ريال)',
- 'quality_rating': 'تقييم الجودة',
- 'market_share': 'الحصة السوقية (%)'
- },
- text='competitor'
- )
-
- fig.update_traces(textposition='top center')
-
- # إضافة خط اتجاه
- fig.update_layout(
- shapes=[
- dict(
- type='line',
- x0=min(price_comparison_df['price_per_sqm']),
- y0=min(price_comparison_df['quality_rating']),
- x1=max(price_comparison_df['price_per_sqm']),
- y1=max(price_comparison_df['quality_rating']),
- line=dict(color='gray', dash='dash')
- )
- ]
- )
-
- st.plotly_chart(fig, use_container_width=True)
-
- # تحليل مقارنة مدة التسليم
- st.markdown("### مقارنة مدة التسليم")
-
- # ترتيب البيانات تصاعديًا حسب مدة التسليم
- delivery_comparison_df = price_comparison_df.sort_values('delivery_time')
-
- # إنشاء رسم بياني شريطي
- fig = px.bar(
- delivery_comparison_df,
- x='competitor',
- y='delivery_time',
- title='مقارنة مدة التسليم (شهر)',
- color='competitor',
- text_auto=True
- )
-
- # تمييز شركتنا
- for i, competitor in enumerate(delivery_comparison_df['competitor']):
- if competitor == 'شركتنا':
- fig.data[0].marker.color = ['blue' if x != i else 'red' for x in range(len(delivery_comparison_df))]
- break
-
- st.plotly_chart(fig, use_container_width=True)
-
- # تحليل الحصة السوقية
- st.markdown("### تحليل الحصة السوقية")
-
- # إنشاء رسم بياني دائري للحصة السوقية
- fig = px.pie(
- price_comparison_df,
- values='market_share',
- names='competitor',
- title='توزيع الحصة السوقية',
- hole=0.4
- )
-
- st.plotly_chart(fig, use_container_width=True)
-
- def _render_reports_tab(self):
- """عرض تبويب التقارير"""
-
- st.markdown("### تقارير التسعير")
-
- # اختيار نوع التقرير
- report_type = st.selectbox(
- "اختر نوع التقرير",
- [
- "ملخص التسعير",
- "تقرير جدول الكميات",
- "تقرير تحليل التكاليف",
- "تقرير سيناريوهات التسعير",
- "تقرير المقارنة التنافسية",
- "التقرير الشامل"
- ]
- )
-
- # عرض معلومات المشروع
- col1, col2 = st.columns(2)
-
- with col1:
- project_name = st.text_input("اسم المشروع", "مشروع إنشاء مبنى سكني")
- client_name = st.text_input("اسم العميل", "شركة التطوير العقاري")
-
- with col2:
- project_location = st.text_input("موقع المشروع", "الرياض، المملكة العربية السعودية")
- report_date = st.date_input("تاريخ التقرير", datetime.now())
-
- # إنشاء التقرير
- if st.button("إنشاء التقرير"):
- st.markdown("### معاينة التقرير")
-
- # عرض ترويسة التقرير
- st.markdown(f"""
- ## {report_type}
- **اسم المشروع:** {project_name}
- **اسم العميل:** {client_name}
- **موقع المشروع:** {project_location}
- **تاريخ التقرير:** {report_date.strftime('%Y-%m-%d')}
- """)
-
- # عرض محتوى التقرير حسب النوع المختار
- if report_type == "ملخص التسعير" or report_type == "التقرير الشامل":
- self._render_pricing_summary_report()
-
- if report_type == "تقرير جدول الكميات" or report_type == "التقرير الشامل":
- self._render_boq_report()
-
- if report_type == "تقرير تحليل التكاليف" or report_type == "التقرير الشامل":
- self._render_cost_analysis_report()
-
- if report_type == "تقرير سيناريوهات التسعير" or report_type == "التقرير الشامل":
- self._render_pricing_scenarios_report()
-
- if report_type == "تقرير المقارنة التنافسية" or report_type == "التقرير الشامل":
- self._render_competitive_analysis_report()
-
- # خيارات تصدير التقرير
- st.markdown("### تصدير التقرير")
-
- export_format = st.radio(
- "اختر صيغة التصدير",
- ["PDF", "Excel", "Word"],
- horizontal=True
- )
-
- if st.button("تصدير التقرير"):
- st.success(f"تم تصدير التقرير بصيغة {export_format} بنجاح!")
-
- def _render_pricing_summary_report(self):
- """عرض تقرير ملخص التسعير"""
-
- st.markdown("## ملخص التسعير")
-
- # حساب إجمالي التكاليف
- total_direct_cost = sum(item['amount'] for item in st.session_state.cost_analysis if item['category'] == 'تكاليف مباشرة')
- total_indirect_cost = sum(item['amount'] for item in st.session_state.cost_analysis if item['category'] == 'تكاليف غير مباشرة')
- total_profit = sum(item['amount'] for item in st.session_state.cost_analysis if item['category'] == 'أرباح')
- total_cost = total_direct_cost + total_indirect_cost
- total_price = total_cost + total_profit
-
- # عرض ملخص التكاليف
- st.markdown("### ملخص التكاليف")
-
- summary_data = {
- 'البند': ['التكاليف المباشرة', 'التكاليف غير المباشرة', 'إجمالي التكاليف', 'هامش الربح', 'السعر الإجمالي'],
- 'المبلغ (ريال)': [total_direct_cost, total_indirect_cost, total_cost, total_profit, total_price],
- 'النسبة المئوية': [
- total_direct_cost / total_price * 100,
- total_indirect_cost / total_price * 100,
- total_cost / total_price * 100,
- total_profit / total_price * 100,
- 100.0
- ]
- }
-
- summary_df = pd.DataFrame(summary_data)
-
- st.dataframe(
- summary_df,
- column_config={
- 'البند': st.column_config.TextColumn('البند'),
- 'المبلغ (ريال)': st.column_config.NumberColumn('المبلغ (ريال)', format='%d'),
- 'النسبة المئوية': st.column_config.NumberColumn('النسبة المئوية', format='%.1f%%')
- },
- hide_index=True,
- use_container_width=True
- )
-
- # عرض رسم بياني للملخص
- fig = px.pie(
- summary_df.iloc[0:3], # استخدام أول 3 صفوف فقط (التكاليف)
- values='المبلغ (ريال)',
- names='البند',
- title='توزيع التكاليف',
- color_discrete_sequence=px.colors.qualitative.Set3
- )
-
- st.plotly_chart(fig, use_container_width=True)
-
- # عرض رسم بياني للسعر الإجمالي
- fig = px.pie(
- summary_df.iloc[[2, 3]], # استخدام صفوف التكاليف والربح
- values='المبلغ (ريال)',
- names='البند',
- title='تركيبة السعر الإجمالي',
- color_discrete_sequence=px.colors.qualitative.Pastel
- )
-
- st.plotly_chart(fig, use_container_width=True)
-
- def _render_boq_report(self):
- """عرض تقرير جدول الكميات"""
-
- st.markdown("## تقرير جدول الكميات")
-
- # عرض جدول الكميات
- boq_df = pd.DataFrame(st.session_state.bill_of_quantities)
-
- st.dataframe(
- boq_df[['code', 'description', 'unit', 'quantity', 'unit_price', 'total_price', 'category']],
- column_config={
- 'code': 'الكود',
- 'description': 'الوصف',
- 'unit': 'الوحدة',
- 'quantity': 'الكمية',
- 'unit_price': st.column_config.NumberColumn('سعر الوحدة', format='%d ريال'),
- 'total_price': st.column_config.NumberColumn('السعر الإجمالي', format='%d ريال'),
- 'category': 'الفئة'
- },
- hide_index=True,
- use_container_width=True
- )
-
- # عرض ملخص جدول الكميات حسب الفئة
- st.markdown("### ملخص جدول الكميات حسب الفئة")
-
- # تجميع البيانات حسب الفئة
- category_totals = {}
- for item in st.session_state.bill_of_quantities:
- category = item['category']
- if category in category_totals:
- category_totals[category] += item['total_price']
- else:
- category_totals[category] = item['total_price']
-
- # إنشاء DataFrame للملخص
- category_summary_df = pd.DataFrame({
- 'الفئة': list(category_totals.keys()),
- 'المبلغ الإجمالي': list(category_totals.values())
- })
-
- # حساب النسبة المئوية
- total_boq = sum(category_totals.values())
- category_summary_df['النسبة المئوية'] = category_summary_df['المبلغ الإجمالي'] / total_boq * 100
-
- # ترتيب البيانات تنازليًا حسب المبلغ
- category_summary_df = category_summary_df.sort_values('المبلغ الإجمالي', ascending=False)
-
- st.dataframe(
- category_summary_df,
- column_config={
- 'الفئة': st.column_config.TextColumn('الفئة'),
- 'المبلغ الإجمالي': st.column_config.NumberColumn('المبلغ الإجمالي', format='%d ريال'),
- 'النسبة المئوية': st.column_config.NumberColumn('النسبة المئوية', format='%.1f%%')
- },
- hide_index=True,
- use_container_width=True
- )
-
- # عرض رسم بياني للملخص
- fig = px.pie(
- category_summary_df,
- values='المبلغ الإجمالي',
- names='الفئة',
- title='توزيع تكاليف البنود حسب الفئة',
- color_discrete_sequence=px.colors.qualitative.Set3
- )
-
- st.plotly_chart(fig, use_container_width=True)
-
- # عرض رسم بياني شريطي للملخص
- fig = px.bar(
- category_summary_df,
- x='الفئة',
- y='المبلغ الإجمالي',
- title='إجمالي تكلفة البنود حسب الفئة',
- color='الفئة',
- text_auto=True
- )
-
- st.plotly_chart(fig, use_container_width=True)
-
- def _render_cost_analysis_report(self):
- """عرض تقرير تحليل التكاليف"""
-
- st.markdown("## تقرير تحليل التكاليف")
-
- # عرض جدول تحليل التكاليف
- cost_df = pd.DataFrame(st.session_state.cost_analysis)
-
- st.dataframe(
- cost_df[['category', 'subcategory', 'description', 'amount', 'percentage']],
- column_config={
- 'category': 'الفئة',
- 'subcategory': 'الفئة الفرعية',
- 'description': 'الوصف',
- 'amount': st.column_config.NumberColumn('المبلغ', format='%d ريال'),
- 'percentage': st.column_config.NumberColumn('النسبة المئوية', format='%.1f%%')
- },
- hide_index=True,
- use_container_width=True
- )
-
- # عرض ملخص تحليل التكاليف حسب الفئة
- st.markdown("### ملخص تحليل التكاليف حسب الفئة")
-
- # تجميع البيانات حسب الفئة
- category_totals = {}
- for item in st.session_state.cost_analysis:
- category = item['category']
- if category in category_totals:
- category_totals[category] += item['amount']
- else:
- category_totals[category] = item['amount']
-
- # إنشاء DataFrame للملخص
- category_summary_df = pd.DataFrame({
- 'الفئة': list(category_totals.keys()),
- 'المبلغ الإجمالي': list(category_totals.values())
- })
-
- # حساب النسبة المئوية
- total_cost = sum(category_totals.values())
- category_summary_df['النسبة المئوية'] = category_summary_df['المبلغ الإجمالي'] / total_cost * 100
-
- # ترتيب البيانات تنازليًا حسب المبلغ
- category_summary_df = category_summary_df.sort_values('المبلغ الإجمالي', ascending=False)
-
- st.dataframe(
- category_summary_df,
- column_config={
- 'الفئة': st.column_config.TextColumn('الفئة'),
- 'المبلغ الإجمالي': st.column_config.NumberColumn('المبلغ الإجمالي', format='%d ريال'),
- 'النسبة المئوية': st.column_config.NumberColumn('النسبة المئوية', format='%.1f%%')
- },
- hide_index=True,
- use_container_width=True
- )
-
- # عرض رسم بياني للملخص
- fig = px.pie(
- category_summary_df,
- values='المبلغ الإجمالي',
- names='الفئة',
- title='توزيع التكاليف حسب الفئة',
- color_discrete_sequence=px.colors.qualitative.Set3
- )
-
- st.plotly_chart(fig, use_container_width=True)
-
- # عرض ملخص تحليل التكاليف حسب الفئة والفئة الفرعية
- st.markdown("### ملخص تحليل التكاليف حسب الفئة والفئة الفرعية")
-
- # تجميع البيانات حسب الفئة والفئة الفرعية
- subcategory_totals = {}
- for item in st.session_state.cost_analysis:
- category = item['category']
- subcategory = item['subcategory']
- key = f"{category} - {subcategory}"
- if key in subcategory_totals:
- subcategory_totals[key] += item['amount']
- else:
- subcategory_totals[key] = item['amount']
-
- # إنشاء DataFrame للملخص
- subcategory_summary_df = pd.DataFrame({
- 'الفئة والفئة الفرعية': list(subcategory_totals.keys()),
- 'المبلغ الإجمالي': list(subcategory_totals.values())
- })
-
- # حساب النسبة المئوية
- subcategory_summary_df['النسبة المئوية'] = subcategory_summary_df['المبلغ الإجمالي'] / total_cost * 100
-
- # ترتيب البيانات تنازليًا حسب المبلغ
- subcategory_summary_df = subcategory_summary_df.sort_values('المبلغ الإجمالي', ascending=False)
-
- st.dataframe(
- subcategory_summary_df,
- column_config={
- 'الفئة والفئة الفرعية': st.column_config.TextColumn('الفئة والفئة الفرعية'),
- 'المبلغ الإجمالي': st.column_config.NumberColumn('المبلغ الإجمالي', format='%d ريال'),
- 'النسبة المئوية': st.column_config.NumberColumn('النسبة المئوية', format='%.1f%%')
- },
- hide_index=True,
- use_container_width=True
- )
-
- # عرض رسم بياني للملخص
- fig = px.bar(
- subcategory_summary_df,
- x='الفئة والفئة الفرعية',
- y='المبلغ الإجمالي',
- title='توزيع التكاليف حسب الفئة والفئة الفرعية',
- color='الفئة والفئة الفرعية',
- text_auto=True
- )
-
- st.plotly_chart(fig, use_container_width=True)
-
- # عرض تحليل هيكل التكاليف
- st.markdown("### تحليل هيكل التكاليف")
-
- # إنشاء رسم بياني للنسب المئوية
- fig = px.treemap(
- cost_df,
- path=['category', 'subcategory', 'description'],
- values='amount',
- title='تحليل هيكل التكاليف',
- color='percentage',
- color_continuous_scale='RdBu',
- color_continuous_midpoint=np.average(cost_df['percentage'])
- )
-
- st.plotly_chart(fig, use_container_width=True)
-
- def _render_pricing_scenarios_report(self):
- """عرض تقرير سيناريوهات التسعير"""
-
- st.markdown("## تقرير سيناريوهات التسعير")
-
- # عرض جدول سيناريوهات التسعير
- scenarios_df = pd.DataFrame(st.session_state.price_scenarios)
-
- st.dataframe(
- scenarios_df[['name', 'description', 'total_cost', 'profit_margin', 'total_price', 'is_active']],
- column_config={
- 'name': 'اسم السيناريو',
- 'description': 'الوصف',
- 'total_cost': st.column_config.NumberColumn('إجمالي التكلفة', format='%d ريال'),
- 'profit_margin': st.column_config.NumberColumn('هامش الربح', format='%.1f%%'),
- 'total_price': st.column_config.NumberColumn('السعر الإجمالي', format='%d ريال'),
- 'is_active': st.column_config.CheckboxColumn('نشط')
- },
- hide_index=True,
- use_container_width=True
- )
-
- # عرض مقارنة السيناريوهات
- st.markdown("### مقارنة السيناريوهات")
-
- # إنشاء رسم بياني للمقارنة
- fig = go.Figure()
-
- for scenario in st.session_state.price_scenarios:
- fig.add_trace(go.Bar(
- name=scenario['name'],
- x=['التكلفة', 'الربح', 'السعر الإجمالي'],
- y=[
- scenario['total_cost'],
- scenario['total_price'] - scenario['total_cost'],
- scenario['total_price']
- ],
- text=[
- f"{scenario['total_cost']:,.0f}",
- f"{scenario['total_price'] - scenario['total_cost']:,.0f}",
- f"{scenario['total_price']:,.0f}"
- ],
- textposition='auto'
- ))
-
- fig.update_layout(
- title='مقارنة السيناريوهات',
- barmode='group',
- xaxis_title='العنصر',
- yaxis_title='المبلغ (ريال)'
- )
-
- st.plotly_chart(fig, use_container_width=True)
-
- # عرض مقارنة هوامش الربح
- st.markdown("### مقارنة هوامش الربح")
-
- # إنشاء DataFrame للمقارنة
- profit_comparison_df = pd.DataFrame({
- 'السيناريو': [scenario['name'] for scenario in st.session_state.price_scenarios],
- 'هامش الربح (%)': [scenario['profit_margin'] for scenario in st.session_state.price_scenarios],
- 'مبلغ الربح (ريال)': [scenario['total_price'] - scenario['total_cost'] for scenario in st.session_state.price_scenarios]
- })
-
- # ترتيب البيانات تنازليًا حسب هامش الربح
- profit_comparison_df = profit_comparison_df.sort_values('هامش الربح (%)', ascending=False)
-
- # إنشاء رسم بياني شريطي
- fig = px.bar(
- profit_comparison_df,
- x='السيناريو',
- y='هامش الربح (%)',
- title='مقارنة هوامش الربح',
- color='السيناريو',
- text_auto=True
- )
-
- st.plotly_chart(fig, use_container_width=True)
-
- # عرض تحليل حساسية هامش الربح
- st.markdown("### تحليل حساسية هامش الربح")
-
- # الحصول على التكلفة الإجمالية من السيناريو النشط أو الأول
- active_scenario = next((s for s in st.session_state.price_scenarios if s['is_active']), st.session_state.price_scenarios[0])
- total_cost = active_scenario['total_cost']
-
- # إنشاء بيانات لتحليل الحساسية
- profit_margins = list(range(5, 26, 1)) # من 5% إلى 25%
- total_prices = [total_cost * (1 + margin/100) for margin in profit_margins]
-
- # إنشاء DataFrame للرسم البياني
- sensitivity_df = pd.DataFrame({
- 'هامش الربح (%)': profit_margins,
- 'السعر الإجمالي': total_prices
- })
-
- # إنشاء رسم بياني خطي
- fig = px.line(
- sensitivity_df,
- x='هامش الربح (%)',
- y='السعر الإجمالي',
- title='تحليل حساسية هامش الربح',
- markers=True
- )
-
- # إضافة خط أفقي يمثل السعر التنافسي (افتراضي)
- competitive_price = 650000
- fig.add_hline(
- y=competitive_price,
- line_dash="dash",
- line_color="red",
- annotation_text="السعر التنافسي",
- annotation_position="bottom right"
- )
-
- st.plotly_chart(fig, use_container_width=True)
-
- def _render_competitive_analysis_report(self):
- """عرض تقرير المقارنة التنافسية"""
-
- st.markdown("## تقرير المقارنة التنافسية")
-
- # عرض جدول المقارنة التنافسية
- competitive_df = pd.DataFrame(st.session_state.competitive_analysis)
-
- st.dataframe(
- competitive_df[['competitor', 'project_type', 'price_per_sqm', 'delivery_time', 'quality_rating', 'market_share']],
- column_config={
- 'competitor': 'المنافس',
- 'project_type': 'نوع المشروع',
- 'price_per_sqm': st.column_config.NumberColumn('السعر لكل متر مربع', format='%d ريال'),
- 'delivery_time': st.column_config.NumberColumn('مدة التسليم (شهر)', format='%d'),
- 'quality_rating': st.column_config.NumberColumn('تقييم الجودة', format='%.1f/5.0'),
- 'market_share': st.column_config.NumberColumn('الحصة السوقية', format='%.1f%%')
- },
- hide_index=True,
- use_container_width=True
- )
-
- # عرض مقارنة الأسعار
- st.markdown("### مقارنة الأسعار")
-
- # ترتيب البيانات تصاعديًا حسب السعر
- price_comparison_df = competitive_df.sort_values('price_per_sqm')
-
- # إنشاء رسم بياني شريطي
- fig = px.bar(
- price_comparison_df,
- x='competitor',
- y='price_per_sqm',
- title='مقارنة الأسعار لكل متر مربع',
- color='competitor',
- text_auto=True
- )
-
- # تمييز شركتنا
- for i, competitor in enumerate(price_comparison_df['competitor']):
- if competitor == 'شركتنا':
- fig.data[0].marker.color = ['blue' if x != i else 'red' for x in range(len(price_comparison_df))]
- break
-
- st.plotly_chart(fig, use_container_width=True)
-
- # عرض مقارنة الجودة والسعر
- st.markdown("### مقارنة الجودة والسعر")
-
- # إنشاء رسم بياني للعلاقة بين السعر والجودة
- fig = px.scatter(
- price_comparison_df,
- x='price_per_sqm',
- y='quality_rating',
- size='market_share',
- color='competitor',
- title='العلاقة بين السعر والجودة والحصة السوقية',
- labels={
- 'price_per_sqm': 'السعر لكل متر مربع (ريال)',
- 'quality_rating': 'تقييم الجودة',
- 'market_share': 'الحصة السوقية (%)'
- },
- text='competitor'
- )
-
- fig.update_traces(textposition='top center')
-
- # إضافة خط اتجاه
- fig.update_layout(
- shapes=[
- dict(
- type='line',
- x0=min(price_comparison_df['price_per_sqm']),
- y0=min(price_comparison_df['quality_rating']),
- x1=max(price_comparison_df['price_per_sqm']),
- y1=max(price_comparison_df['quality_rating']),
- line=dict(color='gray', dash='dash')
- )
- ]
- )
-
- st.plotly_chart(fig, use_container_width=True)
-
- # عرض مقارنة مدة التسليم
- st.markdown("### مقارنة مدة التسليم")
-
- # ترتيب البيانات تصاعديًا حسب مدة التسليم
- delivery_comparison_df = competitive_df.sort_values('delivery_time')
-
- # إنشاء رسم بياني شريطي
- fig = px.bar(
- delivery_comparison_df,
- x='competitor',
- y='delivery_time',
- title='مقارنة مدة التسليم (شهر)',
- color='competitor',
- text_auto=True
- )
-
- # تمييز شركتنا
- for i, competitor in enumerate(delivery_comparison_df['competitor']):
- if competitor == 'شركتنا':
- fig.data[0].marker.color = ['blue' if x != i else 'red' for x in range(len(delivery_comparison_df))]
- break
-
- st.plotly_chart(fig, use_container_width=True)
-
- # عرض تحليل الحصة السوقية
- st.markdown("### تحليل الحصة السوقية")
-
- # إنشاء رسم بياني دائري للحصة السوقية
- fig = px.pie(
- competitive_df,
- values='market_share',
- names='competitor',
- title='توزيع الحصة السوقية',
- hole=0.4
- )
-
- st.plotly_chart(fig, use_container_width=True)
-
- # عرض تحليل الموقع التنافسي
- st.markdown("### تحليل الموقع التنافسي")
-
- # إيجاد بيانات شركتنا
- our_company = next((item for item in st.session_state.competitive_analysis if item['competitor'] == 'شركتنا'), None)
-
- if our_company:
- # حساب متوسطات السوق
- avg_price = competitive_df['price_per_sqm'].mean()
- avg_delivery = competitive_df['delivery_time'].mean()
- avg_quality = competitive_df['quality_rating'].mean()
-
- # إنشاء بيانات المقارنة
- comparison_data = {
- 'المؤشر': ['السعر لكل متر مربع', 'مدة التسليم', 'تقييم الجودة'],
- 'قيمة شركتنا': [our_company['price_per_sqm'], our_company['delivery_time'], our_company['quality_rating']],
- 'متوسط السوق': [avg_price, avg_delivery, avg_quality],
- 'الفرق (%)': [
- (our_company['price_per_sqm'] - avg_price) / avg_price * 100,
- (our_company['delivery_time'] - avg_delivery) / avg_delivery * 100,
- (our_company['quality_rating'] - avg_quality) / avg_quality * 100
- ]
- }
-
- comparison_df = pd.DataFrame(comparison_data)
-
- st.dataframe(
- comparison_df,
- column_config={
- 'المؤشر': st.column_config.TextColumn('المؤشر'),
- 'قيمة شركتنا': st.column_config.NumberColumn('قيمة شركتنا'),
- 'متوسط السوق': st.column_config.NumberColumn('متوسط السوق'),
- 'الفرق (%)': st.column_config.NumberColumn('الفرق (%)', format='%+.1f%%')
- },
- hide_index=True,
- use_container_width=True
- )
-
- # إنشاء رسم بياني راداري للموقع التنافسي
- # تحويل البيانات إلى نسب مئوية للمقارنة
- max_price = competitive_df['price_per_sqm'].max()
- min_price = competitive_df['price_per_sqm'].min()
- price_range = max_price - min_price
-
- max_delivery = competitive_df['delivery_time'].max()
- min_delivery = competitive_df['delivery_time'].min()
- delivery_range = max_delivery - min_delivery
-
- # ملاحظة: نقوم بعكس مقياس السعر ومدة التسليم لأن القيم الأقل أفضل
- normalized_price = 100 - ((our_company['price_per_sqm'] - min_price) / price_range * 100) if price_range > 0 else 50
- normalized_delivery = 100 - ((our_company['delivery_time'] - min_delivery) / delivery_range * 100) if delivery_range > 0 else 50
- normalized_quality = (our_company['quality_rating'] / 5) * 100
- normalized_market_share = (our_company['market_share'] / competitive_df['market_share'].max()) * 100
-
- # إنشاء رسم بياني راداري
- fig = go.Figure()
-
- fig.add_trace(go.Scatterpolar(
- r=[normalized_price, normalized_delivery, normalized_quality, normalized_market_share],
- theta=['السعر التنافسي', 'سرعة التسليم', 'الجودة', 'الحصة السوقية'],
- fill='toself',
- name='شركتنا'
- ))
-
- fig.update_layout(
- polar=dict(
- radialaxis=dict(
- visible=True,
- range=[0, 100]
- )
- ),
- title='تحليل الموقع التنافسي لشركتنا'
- )
-
- st.plotly_chart(fig, use_container_width=True)
+"""
+وحدة التسعير - التطبيق الرئيسي
+"""
+
+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 time
+import io
+import os
+import json
+import base64
+from pathlib import Path
+
+class PricingApp:
+ """وحدة التسعير"""
+
+ def __init__(self):
+ """تهيئة وحدة التسعير"""
+
+ # تهيئة حالة الجلسة
+ if 'bill_of_quantities' not in st.session_state:
+ st.session_state.bill_of_quantities = [
+ {
+ 'id': 1,
+ 'code': 'A-001',
+ 'description': 'أعمال الحفر والردم',
+ 'unit': 'م3',
+ 'quantity': 1500,
+ 'unit_price': 45,
+ 'total_price': 67500,
+ 'category': 'أعمال ترابية'
+ },
+ {
+ 'id': 2,
+ 'code': 'A-002',
+ 'description': 'توريد وصب خرسانة عادية',
+ 'unit': 'م3',
+ 'quantity': 250,
+ 'unit_price': 350,
+ 'total_price': 87500,
+ 'category': 'أعمال خرسانية'
+ },
+ {
+ 'id': 3,
+ 'code': 'A-003',
+ 'description': 'توريد وصب خرسانة مسلحة للأساسات',
+ 'unit': 'م3',
+ 'quantity': 180,
+ 'unit_price': 450,
+ 'total_price': 81000,
+ 'category': 'أعمال خرسانية'
+ },
+ {
+ 'id': 4,
+ 'code': 'A-004',
+ 'description': 'توريد وصب خرسانة مسلحة للأعمدة',
+ 'unit': 'م3',
+ 'quantity': 120,
+ 'unit_price': 500,
+ 'total_price': 60000,
+ 'category': 'أعمال خرسانية'
+ },
+ {
+ 'id': 5,
+ 'code': 'A-005',
+ 'description': 'توريد وتركيب حديد تسليح',
+ 'unit': 'طن',
+ 'quantity': 45,
+ 'unit_price': 3000,
+ 'total_price': 135000,
+ 'category': 'أعمال حديد'
+ },
+ {
+ 'id': 6,
+ 'code': 'A-006',
+ 'description': 'توريد وبناء طابوق',
+ 'unit': 'م2',
+ 'quantity': 1200,
+ 'unit_price': 45,
+ 'total_price': 54000,
+ 'category': 'أعمال بناء'
+ },
+ {
+ 'id': 7,
+ 'code': 'A-007',
+ 'description': 'أعمال اللياسة والتشطيبات',
+ 'unit': 'م2',
+ 'quantity': 2400,
+ 'unit_price': 35,
+ 'total_price': 84000,
+ 'category': 'أعمال تشطيبات'
+ },
+ {
+ 'id': 8,
+ 'code': 'A-008',
+ 'description': 'أعمال الدهانات',
+ 'unit': 'م2',
+ 'quantity': 2400,
+ 'unit_price': 25,
+ 'total_price': 60000,
+ 'category': 'أعمال تشطيبات'
+ },
+ {
+ 'id': 9,
+ 'code': 'A-009',
+ 'description': 'توريد وتركيب أبواب خشبية',
+ 'unit': 'عدد',
+ 'quantity': 24,
+ 'unit_price': 750,
+ 'total_price': 18000,
+ 'category': 'أعمال نجارة'
+ },
+ {
+ 'id': 10,
+ 'code': 'A-010',
+ 'description': 'توريد وتركيب نوافذ ألمنيوم',
+ 'unit': 'م2',
+ 'quantity': 120,
+ 'unit_price': 350,
+ 'total_price': 42000,
+ 'category': 'أعمال ألمنيوم'
+ }
+ ]
+
+ if 'cost_analysis' not in st.session_state:
+ st.session_state.cost_analysis = [
+ {
+ 'id': 1,
+ 'category': 'تكاليف مباشرة',
+ 'subcategory': 'مواد',
+ 'description': 'خرسانة',
+ 'amount': 120000,
+ 'percentage': 17.9
+ },
+ {
+ 'id': 2,
+ 'category': 'تكاليف مباشرة',
+ 'subcategory': 'مواد',
+ 'description': 'حديد تسليح',
+ 'amount': 135000,
+ 'percentage': 20.1
+ },
+ {
+ 'id': 3,
+ 'category': 'تكاليف مباشرة',
+ 'subcategory': 'مواد',
+ 'description': 'طابوق',
+ 'amount': 54000,
+ 'percentage': 8.1
+ },
+ {
+ 'id': 4,
+ 'category': 'تكاليف مباشرة',
+ 'subcategory': 'عمالة',
+ 'description': 'عمالة تنفيذ',
+ 'amount': 120000,
+ 'percentage': 17.9
+ },
+ {
+ 'id': 5,
+ 'category': 'تكاليف مباشرة',
+ 'subcategory': 'معدات',
+ 'description': 'معدات إنشائية',
+ 'amount': 85000,
+ 'percentage': 12.7
+ },
+ {
+ 'id': 6,
+ 'category': 'تكاليف غير مباشرة',
+ 'subcategory': 'إدارة',
+ 'description': 'إدارة المشروع',
+ 'amount': 45000,
+ 'percentage': 6.7
+ },
+ {
+ 'id': 7,
+ 'category': 'تكاليف غير مباشرة',
+ 'subcategory': 'إدارة',
+ 'description': 'إشراف هندسي',
+ 'amount': 35000,
+ 'percentage': 5.2
+ },
+ {
+ 'id': 8,
+ 'category': 'تكاليف غير مباشرة',
+ 'subcategory': 'عامة',
+ 'description': 'تأمينات وضمانات',
+ 'amount': 25000,
+ 'percentage': 3.7
+ },
+ {
+ 'id': 9,
+ 'category': 'تكاليف غير مباشرة',
+ 'subcategory': 'عامة',
+ 'description': 'مصاريف إدارية',
+ 'amount': 30000,
+ 'percentage': 4.5
+ },
+ {
+ 'id': 10,
+ 'category': 'أرباح',
+ 'subcategory': 'أرباح',
+ 'description': 'هامش الربح',
+ 'amount': 55000,
+ 'percentage': 8.2
+ }
+ ]
+
+ if 'price_scenarios' not in st.session_state:
+ st.session_state.price_scenarios = [
+ {
+ 'id': 1,
+ 'name': 'السيناريو الأساسي',
+ 'description': 'التسعير الأساسي مع هامش ربح 8%',
+ 'total_cost': 615000,
+ 'profit_margin': 8.2,
+ 'total_price': 670000,
+ 'is_active': True
+ },
+ {
+ 'id': 2,
+ 'name': 'سيناريو تنافسي',
+ 'description': 'تخفيض هامش الربح للمنافسة',
+ 'total_cost': 615000,
+ 'profit_margin': 5.0,
+ 'total_price': 650000,
+ 'is_active': False
+ },
+ {
+ 'id': 3,
+ 'name': 'سيناريو مرتفع',
+ 'description': 'زيادة هامش الربح للمشاريع ذات المخاطر العالية',
+ 'total_cost': 615000,
+ 'profit_margin': 12.0,
+ 'total_price': 700000,
+ 'is_active': False
+ }
+ ]
+
+ # إضافة بيانات المقارنة التنافسية
+ if 'competitive_analysis' not in st.session_state:
+ st.session_state.competitive_analysis = [
+ {
+ 'id': 1,
+ 'competitor': 'شركة الإنشاءات المتحدة',
+ 'project_type': 'مباني سكنية',
+ 'price_per_sqm': 1800,
+ 'delivery_time': 12,
+ 'quality_rating': 4.2,
+ 'market_share': 15.5
+ },
+ {
+ 'id': 2,
+ 'competitor': 'مجموعة البناء الحديث',
+ 'project_type': 'مباني سكنية',
+ 'price_per_sqm': 2100,
+ 'delivery_time': 10,
+ 'quality_rating': 4.5,
+ 'market_share': 18.2
+ },
+ {
+ 'id': 3,
+ 'competitor': 'شركة الإعمار الدولية',
+ 'project_type': 'مباني سكنية',
+ 'price_per_sqm': 2300,
+ 'delivery_time': 14,
+ 'quality_rating': 4.7,
+ 'market_share': 22.0
+ },
+ {
+ 'id': 4,
+ 'competitor': 'مؤسسة البناء المتكامل',
+ 'project_type': 'مباني سكنية',
+ 'price_per_sqm': 1750,
+ 'delivery_time': 15,
+ 'quality_rating': 3.8,
+ 'market_share': 12.5
+ },
+ {
+ 'id': 5,
+ 'competitor': 'شركتنا',
+ 'project_type': 'مباني سكنية',
+ 'price_per_sqm': 1950,
+ 'delivery_time': 11,
+ 'quality_rating': 4.4,
+ 'market_share': 14.8
+ }
+ ]
+
+ def run(self):
+ """تشغيل وحدة التسعير"""
+ # استدعاء دالة العرض
+ self.render()
+
+ def render(self):
+ """عرض واجهة وحدة التسعير"""
+
+ st.markdown("وحدة التسعير
", unsafe_allow_html=True)
+
+ tabs = st.tabs([
+ "لوحة التحكم",
+ "جدول الكميات",
+ "تحليل التكاليف",
+ "سيناريوهات التسعير",
+ "المقارنة التنافسية",
+ "التقارير"
+ ])
+
+ with tabs[0]:
+ self._render_dashboard_tab()
+
+ with tabs[1]:
+ self._render_bill_of_quantities_tab()
+
+ with tabs[2]:
+ self._render_cost_analysis_tab()
+
+ with tabs[3]:
+ self._render_pricing_scenarios_tab()
+
+ with tabs[4]:
+ self._render_competitive_analysis_tab()
+
+ with tabs[5]:
+ self._render_reports_tab()
+
+ def _render_dashboard_tab(self):
+ """عرض تبويب لوحة التحكم"""
+
+ st.markdown("### لوحة تحكم التسعير")
+
+ # عرض ملخص التسعير
+ col1, col2, col3, col4 = st.columns(4)
+
+ # حساب إجمالي التكاليف
+ total_direct_cost = sum(item['amount'] for item in st.session_state.cost_analysis if item['category'] == 'تكاليف مباشرة')
+ total_indirect_cost = sum(item['amount'] for item in st.session_state.cost_analysis if item['category'] == 'تكاليف غير مباشرة')
+ total_profit = sum(item['amount'] for item in st.session_state.cost_analysis if item['category'] == 'أرباح')
+ total_cost = total_direct_cost + total_indirect_cost
+ total_price = total_cost + total_profit
+
+ with col1:
+ st.metric("إجمالي التكاليف المباشرة", f"{total_direct_cost:,.0f} ريال")
+
+ with col2:
+ st.metric("إجمالي التكاليف غير المباشرة", f"{total_indirect_cost:,.0f} ريال")
+
+ with col3:
+ st.metric("إجمالي التكاليف", f"{total_cost:,.0f} ريال")
+
+ with col4:
+ st.metric("السعر الإجمالي", f"{total_price:,.0f} ريال")
+
+ # عرض توزيع التكاليف
+ st.markdown("### توزيع التكاليف")
+
+ # تجميع البيانات حسب الفئة
+ cost_categories = {}
+
+ for item in st.session_state.cost_analysis:
+ category = item['category']
+ if category in cost_categories:
+ cost_categories[category] += item['amount']
+ else:
+ cost_categories[category] = item['amount']
+
+ # إنشاء DataFrame للرسم البياني
+ cost_df = pd.DataFrame({
+ 'الفئة': list(cost_categories.keys()),
+ 'المبلغ': list(cost_categories.values())
+ })
+
+ # إنشاء رسم بياني دائري
+ fig = px.pie(
+ cost_df,
+ values='المبلغ',
+ names='الفئة',
+ title='توزيع التكاليف حسب الفئة',
+ color_discrete_sequence=px.colors.qualitative.Set3
+ )
+
+ st.plotly_chart(fig, use_container_width=True)
+
+ # عرض توزيع التكاليف المباشرة
+ st.markdown("### توزيع التكاليف المباشرة")
+
+ # تجميع البيانات حسب الفئة الفرعية للتكاليف المباشرة
+ direct_cost_subcategories = {}
+
+ for item in st.session_state.cost_analysis:
+ if item['category'] == 'تكاليف مباشرة':
+ subcategory = item['subcategory']
+ if subcategory in direct_cost_subcategories:
+ direct_cost_subcategories[subcategory] += item['amount']
+ else:
+ direct_cost_subcategories[subcategory] = item['amount']
+
+ # إنشاء DataFrame للرسم البياني
+ direct_cost_df = pd.DataFrame({
+ 'الفئة الفرعية': list(direct_cost_subcategories.keys()),
+ 'المبلغ': list(direct_cost_subcategories.values())
+ })
+
+ # إنشاء رسم بياني دائري
+ fig = px.pie(
+ direct_cost_df,
+ values='المبلغ',
+ names='الفئة الفرعية',
+ title='توزيع التكاليف المباشرة',
+ color_discrete_sequence=px.colors.qualitative.Pastel
+ )
+
+ st.plotly_chart(fig, use_container_width=True)
+
+ # عرض توزيع التكاليف غير المباشرة
+ st.markdown("### توزيع التكاليف غير المباشرة")
+
+ # تجميع البيانات حسب الفئة الفرعية للتكاليف غير المباشرة
+ indirect_cost_subcategories = {}
+
+ for item in st.session_state.cost_analysis:
+ if item['category'] == 'تكاليف غير مباشرة':
+ subcategory = item['subcategory']
+ if subcategory in indirect_cost_subcategories:
+ indirect_cost_subcategories[subcategory] += item['amount']
+ else:
+ indirect_cost_subcategories[subcategory] = item['amount']
+
+ # إنشاء DataFrame للرسم البياني
+ indirect_cost_df = pd.DataFrame({
+ 'الفئة الفرعية': list(indirect_cost_subcategories.keys()),
+ 'المبلغ': list(indirect_cost_subcategories.values())
+ })
+
+ # إنشاء رسم بياني دائري
+ fig = px.pie(
+ indirect_cost_df,
+ values='المبلغ',
+ names='الفئة الفرعية',
+ title='توزيع التكاليف غير المباشرة',
+ color_discrete_sequence=px.colors.qualitative.Pastel1
+ )
+
+ st.plotly_chart(fig, use_container_width=True)
+
+ def _render_bill_of_quantities_tab(self):
+ """عرض تبويب جدول الكميات"""
+
+ st.markdown("### جدول الكميات")
+
+ # إنشاء DataFrame من بيانات جدول الكميات
+ boq_df = pd.DataFrame(st.session_state.bill_of_quantities)
+
+ # عرض جدول الكميات
+ st.dataframe(
+ boq_df[['code', 'description', 'unit', 'quantity', 'unit_price', 'total_price', 'category']],
+ column_config={
+ 'code': 'الكود',
+ 'description': 'الوصف',
+ 'unit': 'الوحدة',
+ 'quantity': 'الكمية',
+ 'unit_price': st.column_config.NumberColumn('سعر الوحدة', format='%d ريال'),
+ 'total_price': st.column_config.NumberColumn('السعر الإجمالي', format='%d ريال'),
+ 'category': 'الفئة'
+ },
+ hide_index=True,
+ use_container_width=True
+ )
+
+ # إضافة بند جديد
+ st.markdown("### إضافة بند جديد")
+
+ col1, col2 = st.columns(2)
+
+ with col1:
+ new_code = st.text_input("الكود", key="new_boq_code")
+ new_description = st.text_input("الوصف", key="new_boq_description")
+ new_unit = st.selectbox("الوحدة", ["م3", "م2", "طن", "عدد", "متر طولي"], key="new_boq_unit")
+
+ with col2:
+ new_quantity = st.number_input("الكمية", min_value=0.0, step=1.0, key="new_boq_quantity")
+ new_unit_price = st.number_input("سعر الوحدة", min_value=0.0, step=10.0, key="new_boq_unit_price")
+ new_category = st.selectbox(
+ "الفئة",
+ [
+ "أعمال ترابية",
+ "أعمال خرسانية",
+ "أعمال حديد",
+ "أعمال بناء",
+ "أعمال تشطيبات",
+ "أعمال نجارة",
+ "أعمال ألمنيوم",
+ "أعمال كهربائية",
+ "أعمال ميكانيكية",
+ "أعمال صحية"
+ ],
+ key="new_boq_category"
+ )
+
+ if st.button("إضافة البند", key="add_boq_item"):
+ if new_code and new_description and new_quantity > 0 and new_unit_price > 0:
+ # حساب السعر الإجمالي
+ new_total_price = new_quantity * new_unit_price
+
+ # إضافة بند جديد
+ new_id = max([item['id'] for item in st.session_state.bill_of_quantities]) + 1
+
+ st.session_state.bill_of_quantities.append({
+ 'id': new_id,
+ 'code': new_code,
+ 'description': new_description,
+ 'unit': new_unit,
+ 'quantity': new_quantity,
+ 'unit_price': new_unit_price,
+ 'total_price': new_total_price,
+ 'category': new_category
+ })
+
+ st.success(f"تمت إضافة البند بنجاح: {new_description}")
+
+ # تحديث الصفحة لعرض البند الجديد
+ st.rerun()
+ else:
+ st.error("يرجى إدخال جميع البيانات المطلوبة بشكل صحيح")
+
+ # عرض ملخص جدول الكميات (إزالة التكرار)
+ st.markdown("### ملخص جدول الكميات")
+
+ # تجميع البيانات حسب الفئة
+ category_totals = {}
+ for item in st.session_state.bill_of_quantities:
+ category = item['category']
+ if category in category_totals:
+ category_totals[category] += item['total_price']
+ else:
+ category_totals[category] = item['total_price']
+
+ # إنشاء DataFrame للرسم البياني
+ category_df = pd.DataFrame({
+ 'الفئة': list(category_totals.keys()),
+ 'المبلغ': list(category_totals.values())
+ })
+
+ # ترتيب البيانات تنازليًا حسب المبلغ
+ category_df = category_df.sort_values('المبلغ', ascending=False)
+
+ # إنشاء رسم بياني شريطي
+ fig = px.bar(
+ category_df,
+ x='الفئة',
+ y='المبلغ',
+ title='إجمالي تكلفة البنود حسب الفئة',
+ color='الفئة',
+ text_auto=True
+ )
+
+ st.plotly_chart(fig, use_container_width=True)
+
+ # حساب إجمالي جدول الكميات
+ total_boq = sum(item['total_price'] for item in st.session_state.bill_of_quantities)
+
+ def _render_cost_analysis_tab(self):
+ """عرض تبويب تحليل التكاليف"""
+
+ st.markdown("### تحليل التكاليف")
+
+ # عرض جدول تحليل التكاليف
+ cost_df = pd.DataFrame(st.session_state.cost_analysis)
+
+ st.dataframe(
+ cost_df[['category', 'subcategory', 'description', 'amount', 'percentage']],
+ column_config={
+ 'category': 'الفئة',
+ 'subcategory': 'الفئة الفرعية',
+ 'description': 'الوصف',
+ 'amount': st.column_config.NumberColumn('المبلغ', format='%d ريال'),
+ 'percentage': st.column_config.NumberColumn('النسبة المئوية', format='%.1f%%')
+ },
+ hide_index=True,
+ use_container_width=True
+ )
+
+ # إضافة بند تكلفة جديد
+ st.markdown("### إضافة بند تكلفة جديد")
+
+ col1, col2 = st.columns(2)
+
+ with col1:
+ new_category = st.selectbox(
+ "الفئة",
+ ["تكاليف مباشرة", "تك��ليف غير مباشرة", "أرباح"],
+ key="new_cost_category"
+ )
+
+ # تحديد الفئات الفرعية بناءً على الفئة المختارة
+ subcategory_options = []
+ if new_category == "تكاليف مباشرة":
+ subcategory_options = ["مواد", "عمالة", "معدات"]
+ elif new_category == "تكاليف غير مباشرة":
+ subcategory_options = ["إدارة", "عامة", "تمويل"]
+ else:
+ subcategory_options = ["أرباح"]
+
+ new_subcategory = st.selectbox(
+ "الفئة الفرعية",
+ subcategory_options,
+ key="new_cost_subcategory"
+ )
+
+ new_description = st.text_input("الوصف", key="new_cost_description")
+
+ with col2:
+ new_amount = st.number_input("المبلغ", min_value=0.0, step=1000.0, key="new_cost_amount")
+
+ # حساب إجمالي التكاليف الحالية
+ total_cost = sum(item['amount'] for item in st.session_state.cost_analysis)
+
+ # حساب النسبة المئوية التقريبية
+ if total_cost > 0:
+ estimated_percentage = (new_amount / total_cost) * 100
+ else:
+ estimated_percentage = 0
+
+ st.metric("النسبة المئوية التقديرية", f"{estimated_percentage:.1f}%")
+
+ if st.button("إضافة بند التكلفة", key="add_cost_item"):
+ if new_description and new_amount > 0:
+ # إضافة بند جديد
+ new_id = max([item['id'] for item in st.session_state.cost_analysis]) + 1
+
+ # حساب النسبة المئوية الفعلية بعد إضافة البند الجديد
+ new_total = total_cost + new_amount
+
+ # إعادة حساب النسب المئوية لجميع البنود
+ for item in st.session_state.cost_analysis:
+ item['percentage'] = (item['amount'] / new_total) * 100
+
+ # إضافة البند الجديد
+ st.session_state.cost_analysis.append({
+ 'id': new_id,
+ 'category': new_category,
+ 'subcategory': new_subcategory,
+ 'description': new_description,
+ 'amount': new_amount,
+ 'percentage': (new_amount / new_total) * 100
+ })
+
+ st.success(f"تمت إضافة بند التكلفة بنجاح: {new_description}")
+
+ # تحديث الصفحة لعرض البند الجديد
+ st.rerun()
+ else:
+ st.error("يرجى إدخال جميع البيانات المطلوبة بشكل صحيح")
+
+ # تحليل التكاليف حسب الفئة والفئة الفرعية
+ st.markdown("### تحليل التكاليف حسب الفئة والفئة الفرعية")
+
+ # تجميع البيانات حسب الفئة والفئة الفرعية
+ cost_by_category_subcategory = {}
+
+ for item in st.session_state.cost_analysis:
+ category = item['category']
+ subcategory = item['subcategory']
+ key = f"{category} - {subcategory}"
+
+ if key in cost_by_category_subcategory:
+ cost_by_category_subcategory[key] += item['amount']
+ else:
+ cost_by_category_subcategory[key] = item['amount']
+
+ # إنشاء DataFrame للرسم البياني
+ cost_category_subcategory_df = pd.DataFrame({
+ 'الفئة والفئة الفرعية': list(cost_by_category_subcategory.keys()),
+ 'المبلغ': list(cost_by_category_subcategory.values())
+ })
+
+ # ترتيب البيانات تنازليًا حسب المبلغ
+ cost_category_subcategory_df = cost_category_subcategory_df.sort_values('المبلغ', ascending=False)
+
+ # إنشاء رسم بياني شريطي
+ fig = px.bar(
+ cost_category_subcategory_df,
+ x='الفئة والفئة الفرعية',
+ y='المبلغ',
+ title='تحليل التكاليف حسب الفئة والفئة الفرعية',
+ color='الفئة والفئة الفرعية',
+ text_auto=True
+ )
+
+ st.plotly_chart(fig, use_container_width=True)
+
+ # تحليل نسب التكاليف
+ st.markdown("### تحليل نسب التكاليف")
+
+ # إنشاء رسم بياني للنسب المئوية
+ percentage_df = pd.DataFrame(st.session_state.cost_analysis)
+
+ fig = px.treemap(
+ percentage_df,
+ path=['category', 'subcategory', 'description'],
+ values='amount',
+ title='تحليل هيكل التكاليف',
+ color='percentage',
+ color_continuous_scale='RdBu',
+ color_continuous_midpoint=np.average(percentage_df['percentage'])
+ )
+
+ st.plotly_chart(fig, use_container_width=True)
+
+ # تحليل اتجاهات التكاليف (بيانات افتراضية)
+ st.markdown("### تحليل اتجاهات التكاليف")
+
+ # إنشاء بيانات افتراضية للاتجاهات
+ months = ['يناير', 'فبراير', 'مارس', 'أبريل', 'مايو', 'يونيو']
+ direct_costs = [510000, 520000, 515000, 525000, 530000, 514000]
+ indirect_costs = [130000, 135000, 132000, 138000, 140000, 135000]
+
+ # إنشاء DataFrame للرسم البياني
+ trends_df = pd.DataFrame({
+ 'الشهر': months * 2,
+ 'نوع التكلفة': ['تكاليف مباشرة'] * 6 + ['تكاليف غير مباشرة'] * 6,
+ 'المبلغ': direct_costs + indirect_costs
+ })
+
+ # إنشاء رسم بياني خطي
+ fig = px.line(
+ trends_df,
+ x='الشهر',
+ y='المبلغ',
+ color='نوع التكلفة',
+ title='اتجاهات التكاليف على مدار الأشهر الستة الماضية',
+ markers=True
+ )
+
+ st.plotly_chart(fig, use_container_width=True)
+
+ def _render_pricing_scenarios_tab(self):
+ """عرض تبويب سيناريوهات التسعير"""
+
+ st.markdown("### سيناريوهات التسعير")
+
+ # عرض جدول سيناريوهات التسعير
+ scenarios_df = pd.DataFrame(st.session_state.price_scenarios)
+
+ st.dataframe(
+ scenarios_df[['name', 'description', 'total_cost', 'profit_margin', 'total_price', 'is_active']],
+ column_config={
+ 'name': 'اسم السيناريو',
+ 'description': 'الوصف',
+ 'total_cost': st.column_config.NumberColumn('إجمالي التكلفة', format='%d ريال'),
+ 'profit_margin': st.column_config.NumberColumn('هامش الربح', format='%.1f%%'),
+ 'total_price': st.column_config.NumberColumn('السعر الإجمالي', format='%d ريال'),
+ 'is_active': st.column_config.CheckboxColumn('نشط')
+ },
+ hide_index=True,
+ use_container_width=True
+ )
+
+ # إنشاء سيناريو جديد
+ st.markdown("### إنشاء سيناريو جديد")
+
+ col1, col2 = st.columns(2)
+
+ # حساب إجمالي التكاليف
+ total_cost = sum(item['amount'] for item in st.session_state.cost_analysis
+ if item['category'] != 'أرباح')
+
+ with col1:
+ new_name = st.text_input("اسم السيناريو", key="new_scenario_name")
+ new_description = st.text_input("وصف السيناريو", key="new_scenario_description")
+
+ with col2:
+ new_profit_margin = st.slider(
+ "هامش الربح (%)",
+ min_value=0.0,
+ max_value=30.0,
+ value=10.0,
+ step=0.5,
+ key="new_scenario_profit_margin"
+ )
+
+ # حساب السعر الإجمالي بناءً على هامش الربح
+ profit_amount = total_cost * (new_profit_margin / 100)
+ new_total_price = total_cost + profit_amount
+
+ st.metric("إجمالي التكلفة", f"{total_cost:,.0f} ريال")
+ st.metric("السعر الإجمالي المقترح", f"{new_total_price:,.0f} ريال")
+
+ if st.button("إضافة السيناريو", key="add_scenario"):
+ if new_name and new_description:
+ # إضافة سيناريو جديد
+ new_id = max([item['id'] for item in st.session_state.price_scenarios]) + 1
+
+ st.session_state.price_scenarios.append({
+ 'id': new_id,
+ 'name': new_name,
+ 'description': new_description,
+ 'total_cost': total_cost,
+ 'profit_margin': new_profit_margin,
+ 'total_price': new_total_price,
+ 'is_active': False
+ })
+
+ st.success(f"تمت إضافة السيناريو بنجاح: {new_name}")
+
+ # تحديث الصفحة لعرض السيناريو الجديد
+ st.rerun()
+ else:
+ st.error("يرجى إدخال جميع البيانات المطلوبة بشكل صحيح")
+
+ # مقارنة السيناريوهات
+ st.markdown("### مقارنة السيناريوهات")
+
+ # إنشاء رسم بياني للمقارنة
+ fig = go.Figure()
+
+ for scenario in st.session_state.price_scenarios:
+ fig.add_trace(go.Bar(
+ name=scenario['name'],
+ x=['التكلفة', 'الربح', 'السعر الإجمالي'],
+ y=[
+ scenario['total_cost'],
+ scenario['total_price'] - scenario['total_cost'],
+ scenario['total_price']
+ ],
+ text=[
+ f"{scenario['total_cost']:,.0f}",
+ f"{scenario['total_price'] - scenario['total_cost']:,.0f}",
+ f"{scenario['total_price']:,.0f}"
+ ],
+ textposition='auto'
+ ))
+
+ fig.update_layout(
+ title='مقارنة السيناريوهات',
+ barmode='group',
+ xaxis_title='العنصر',
+ yaxis_title='المبلغ (ريال)'
+ )
+
+ st.plotly_chart(fig, use_container_width=True)
+
+ # تحليل حساسية هامش الربح
+ st.markdown("### تحليل حساسية هامش الربح")
+
+ # إنشاء بيانات لتحليل الحساسية
+ profit_margins = list(range(5, 26, 1)) # من 5% إلى 25%
+ total_prices = [total_cost * (1 + margin/100) for margin in profit_margins]
+
+ # إنشاء DataFrame للرسم البياني
+ sensitivity_df = pd.DataFrame({
+ 'هامش الربح (%)': profit_margins,
+ 'السعر الإجمالي': total_prices
+ })
+
+ # إنشاء رسم بياني خطي
+ fig = px.line(
+ sensitivity_df,
+ x='هامش الربح (%)',
+ y='السعر الإجمالي',
+ title='تحليل حساسية هامش الربح',
+ markers=True
+ )
+
+ # إضافة خط أفقي يمثل السعر التنافسي (افتراضي)
+ competitive_price = 650000
+ fig.add_hline(
+ y=competitive_price,
+ line_dash="dash",
+ line_color="red",
+ annotation_text="السعر التنافسي",
+ annotation_position="bottom right"
+ )
+
+ st.plotly_chart(fig, use_container_width=True)
+
+ # تفعيل/تعطيل السيناريوهات
+ st.markdown("### تفعيل/تعطيل السيناريوهات")
+
+ for i, scenario in enumerate(st.session_state.price_scenarios):
+ col1, col2 = st.columns([4, 1])
+
+ with col1:
+ st.write(f"**{scenario['name']}**: {scenario['description']}")
+
+ with col2:
+ is_active = st.checkbox(
+ "تفعيل",
+ value=scenario['is_active'],
+ key=f"activate_scenario_{scenario['id']}"
+ )
+
+ # تحديث حالة التفعيل
+ if is_active != scenario['is_active']:
+ # إذا تم تفعيل سيناريو، قم بتعطيل جميع السيناريوهات الأخرى
+ if is_active:
+ for j, other_scenario in enumerate(st.session_state.price_scenarios):
+ if j != i:
+ other_scenario['is_active'] = False
+
+ # تحديث حالة السيناريو الحالي
+ scenario['is_active'] = is_active
+
+ # تحديث الصفحة
+ st.rerun()
+
+ def _render_competitive_analysis_tab(self):
+ """عرض تبويب المقارنة التنافسية"""
+
+ st.markdown("### المقارنة التنافسية")
+
+ # عرض جدول المقارنة التنافسية
+ competitive_df = pd.DataFrame(st.session_state.competitive_analysis)
+
+ st.dataframe(
+ competitive_df[['competitor', 'project_type', 'price_per_sqm', 'delivery_time', 'quality_rating', 'market_share']],
+ column_config={
+ 'competitor': 'المنافس',
+ 'project_type': 'نوع المشروع',
+ 'price_per_sqm': st.column_config.NumberColumn('السعر لكل متر مربع', format='%d ريال'),
+ 'delivery_time': st.column_config.NumberColumn('مدة التسليم (شهر)', format='%d'),
+ 'quality_rating': st.column_config.NumberColumn('تقييم الجودة', format='%.1f/5.0'),
+ 'market_share': st.column_config.NumberColumn('الحصة السوقية', format='%.1f%%')
+ },
+ hide_index=True,
+ use_container_width=True
+ )
+
+ # إضافة منافس جديد
+ st.markdown("### إضافة منافس جديد")
+
+ col1, col2 = st.columns(2)
+
+ with col1:
+ new_competitor = st.text_input("اسم المنافس", key="new_competitor_name")
+ new_project_type = st.selectbox(
+ "نوع المشروع",
+ ["مباني سكنية", "مباني تجارية", "مباني صناعية", "بنية تحتية"],
+ key="new_competitor_project_type"
+ )
+ new_price_per_sqm = st.number_input(
+ "السعر لكل متر مربع (ريال)",
+ min_value=0,
+ step=50,
+ key="new_competitor_price"
+ )
+
+ with col2:
+ new_delivery_time = st.number_input(
+ "مدة التسليم (شهر)",
+ min_value=1,
+ max_value=36,
+ step=1,
+ key="new_competitor_delivery"
+ )
+ new_quality_rating = st.slider(
+ "تقييم الجودة",
+ min_value=1.0,
+ max_value=5.0,
+ value=3.5,
+ step=0.1,
+ key="new_competitor_quality"
+ )
+ new_market_share = st.number_input(
+ "الحصة السوقية (%)",
+ min_value=0.0,
+ max_value=100.0,
+ step=0.5,
+ key="new_competitor_market_share"
+ )
+
+ if st.button("إضافة منافس", key="add_competitor"):
+ if new_competitor and new_price_per_sqm > 0:
+ # إضافة منافس جديد
+ new_id = max([item['id'] for item in st.session_state.competitive_analysis]) + 1
+
+ st.session_state.competitive_analysis.append({
+ 'id': new_id,
+ 'competitor': new_competitor,
+ 'project_type': new_project_type,
+ 'price_per_sqm': new_price_per_sqm,
+ 'delivery_time': new_delivery_time,
+ 'quality_rating': new_quality_rating,
+ 'market_share': new_market_share
+ })
+
+ st.success(f"تمت إضافة المنافس بنجاح: {new_competitor}")
+
+ # تحديث الصفحة لعرض المنافس الجديد
+ st.rerun()
+ else:
+ st.error("يرجى إدخال جميع البيانات المطلوبة بشكل صحيح")
+
+ # تحليل مقارنة الأسعار
+ st.markdown("### مقارنة الأسعار")
+
+ # إنشاء DataFrame للرسم البياني
+ price_comparison_df = pd.DataFrame(st.session_state.competitive_analysis)
+
+ # ترتيب البيانات تصاعديًا حسب السعر
+ price_comparison_df = price_comparison_df.sort_values('price_per_sqm')
+
+ # إنشاء رسم بياني شريطي
+ fig = px.bar(
+ price_comparison_df,
+ x='competitor',
+ y='price_per_sqm',
+ title='مقارنة الأسعار لكل متر مربع',
+ color='competitor',
+ text_auto=True
+ )
+
+ # تمييز شركتنا
+ for i, competitor in enumerate(price_comparison_df['competitor']):
+ if competitor == 'شركتنا':
+ fig.data[0].marker.color = ['blue' if x != i else 'red' for x in range(len(price_comparison_df))]
+ break
+
+ st.plotly_chart(fig, use_container_width=True)
+
+ # تحليل مقارنة الجودة والسعر
+ st.markdown("### مقارنة الجودة والسعر")
+
+ # إنشاء رسم بياني للعلاقة بين السعر والجودة
+ fig = px.scatter(
+ price_comparison_df,
+ x='price_per_sqm',
+ y='quality_rating',
+ size='market_share',
+ color='competitor',
+ title='العلاقة بين السعر والجودة والحصة السوقية',
+ labels={
+ 'price_per_sqm': 'السعر لكل متر مربع (ريال)',
+ 'quality_rating': 'تقييم الجودة',
+ 'market_share': 'الحصة السوقية (%)'
+ },
+ text='competitor'
+ )
+
+ fig.update_traces(textposition='top center')
+
+ # إضافة خط اتجاه
+ fig.update_layout(
+ shapes=[
+ dict(
+ type='line',
+ x0=min(price_comparison_df['price_per_sqm']),
+ y0=min(price_comparison_df['quality_rating']),
+ x1=max(price_comparison_df['price_per_sqm']),
+ y1=max(price_comparison_df['quality_rating']),
+ line=dict(color='gray', dash='dash')
+ )
+ ]
+ )
+
+ st.plotly_chart(fig, use_container_width=True)
+
+ # تحليل مقارنة مدة التسليم
+ st.markdown("### مقارنة مدة التسليم")
+
+ # ترتيب البيانات تصاعديًا حسب مدة التسليم
+ delivery_comparison_df = price_comparison_df.sort_values('delivery_time')
+
+ # إنشاء رسم بياني شريطي
+ fig = px.bar(
+ delivery_comparison_df,
+ x='competitor',
+ y='delivery_time',
+ title='مقارنة مدة التسليم (شهر)',
+ color='competitor',
+ text_auto=True
+ )
+
+ # تمييز شركتنا
+ for i, competitor in enumerate(delivery_comparison_df['competitor']):
+ if competitor == 'شركتنا':
+ fig.data[0].marker.color = ['blue' if x != i else 'red' for x in range(len(delivery_comparison_df))]
+ break
+
+ st.plotly_chart(fig, use_container_width=True)
+
+ # تحليل الحصة السوقية
+ st.markdown("### تحليل الحصة السوقية")
+
+ # إنشاء رسم بياني دائري للحصة السوقية
+ fig = px.pie(
+ price_comparison_df,
+ values='market_share',
+ names='competitor',
+ title='توزيع الحصة السوقية',
+ hole=0.4
+ )
+
+ st.plotly_chart(fig, use_container_width=True)
+
+ def _render_reports_tab(self):
+ """عرض تبويب التقارير"""
+
+ st.markdown("### تقارير التسعير")
+
+ # اختيار نوع التقرير
+ report_type = st.selectbox(
+ "اختر نوع التقرير",
+ [
+ "ملخص التسعير",
+ "تقرير جدول الكميات",
+ "تقرير تحليل التكاليف",
+ "تقرير سيناريوهات التسعير",
+ "تقرير المقارنة التنافسية",
+ "التقرير الشامل"
+ ]
+ )
+
+ # عرض معلومات المشروع
+ col1, col2 = st.columns(2)
+
+ with col1:
+ project_name = st.text_input("اسم المشروع", "مشروع إنشاء مبنى سكني")
+ client_name = st.text_input("اسم العميل", "شركة التطوير العقاري")
+
+ with col2:
+ project_location = st.text_input("موقع المشروع", "الرياض، المملكة العربية السعودية")
+ report_date = st.date_input("تاريخ التقرير", datetime.now())
+
+ # إنشاء التقرير
+ if st.button("إنشاء التقرير"):
+ st.markdown("### معاينة التقرير")
+
+ # عرض ترويسة التقرير
+ st.markdown(f"""
+ ## {report_type}
+ **اسم المشروع:** {project_name}
+ **اسم العميل:** {client_name}
+ **موقع المشروع:** {project_location}
+ **تاريخ التقرير:** {report_date.strftime('%Y-%m-%d')}
+ """)
+
+ # عرض محتوى التقرير حسب النوع المختار
+ if report_type == "ملخص التسعير" or report_type == "التقرير الشامل":
+ self._render_pricing_summary_report()
+
+ if report_type == "تقرير جدول الكميات" or report_type == "التقرير الشامل":
+ self._render_boq_report()
+
+ if report_type == "تقرير تحليل التكاليف" or report_type == "التقرير الشامل":
+ self._render_cost_analysis_report()
+
+ if report_type == "تقرير سيناريوهات التسعير" or report_type == "التقرير الشامل":
+ self._render_pricing_scenarios_report()
+
+ if report_type == "تقرير المقارنة التنافسية" or report_type == "التقرير الشامل":
+ self._render_competitive_analysis_report()
+
+ # خيارات تصدير التقرير
+ st.markdown("### تصدير التقرير")
+
+ export_format = st.radio(
+ "اختر صيغة التصدير",
+ ["PDF", "Excel", "Word"],
+ horizontal=True
+ )
+
+ if st.button("تصدير التقرير"):
+ st.success(f"تم تصدير التقرير بصيغة {export_format} بنجاح!")
+
+ def _render_pricing_summary_report(self):
+ """عرض تقرير ملخص التسعير"""
+
+ st.markdown("## ملخص التسعير")
+
+ # حساب إجمالي التكاليف
+ total_direct_cost = sum(item['amount'] for item in st.session_state.cost_analysis if item['category'] == 'تكاليف مباشرة')
+ total_indirect_cost = sum(item['amount'] for item in st.session_state.cost_analysis if item['category'] == 'تكاليف غير مباشرة')
+ total_profit = sum(item['amount'] for item in st.session_state.cost_analysis if item['category'] == 'أرباح')
+ total_cost = total_direct_cost + total_indirect_cost
+ total_price = total_cost + total_profit
+
+ # عرض ملخص التكاليف
+ st.markdown("### ملخص التكاليف")
+
+ summary_data = {
+ 'البند': ['التكاليف المباشرة', 'التكاليف غير المباشرة', 'إجمالي التكاليف', 'هامش الربح', 'السعر الإجمالي'],
+ 'المبلغ (ريال)': [total_direct_cost, total_indirect_cost, total_cost, total_profit, total_price],
+ 'النسبة المئوية': [
+ total_direct_cost / total_price * 100,
+ total_indirect_cost / total_price * 100,
+ total_cost / total_price * 100,
+ total_profit / total_price * 100,
+ 100.0
+ ]
+ }
+
+ summary_df = pd.DataFrame(summary_data)
+
+ st.dataframe(
+ summary_df,
+ column_config={
+ 'البند': st.column_config.TextColumn('البند'),
+ 'المبلغ (ريال)': st.column_config.NumberColumn('المبلغ (ريال)', format='%d'),
+ 'النسبة المئوية': st.column_config.NumberColumn('النسبة المئوية', format='%.1f%%')
+ },
+ hide_index=True,
+ use_container_width=True
+ )
+
+ # عرض رسم بياني للملخص
+ fig = px.pie(
+ summary_df.iloc[0:3], # استخدام أول 3 صفوف فقط (التكاليف)
+ values='المبلغ (ريال)',
+ names='البند',
+ title='توزيع التكاليف',
+ color_discrete_sequence=px.colors.qualitative.Set3
+ )
+
+ st.plotly_chart(fig, use_container_width=True)
+
+ # عرض رسم بياني للسعر الإجمالي
+ fig = px.pie(
+ summary_df.iloc[[2, 3]], # استخدام صفوف التكاليف والربح
+ values='المبلغ (ريال)',
+ names='البند',
+ title='تركيبة السعر الإجمالي',
+ color_discrete_sequence=px.colors.qualitative.Pastel
+ )
+
+ st.plotly_chart(fig, use_container_width=True)
+
+ def _render_boq_report(self):
+ """عرض تقرير جدول الكميات"""
+
+ st.markdown("## تقرير جدول الكميات")
+
+ # عرض جدول الكميات
+ boq_df = pd.DataFrame(st.session_state.bill_of_quantities)
+
+ st.dataframe(
+ boq_df[['code', 'description', 'unit', 'quantity', 'unit_price', 'total_price', 'category']],
+ column_config={
+ 'code': 'الكود',
+ 'description': 'الوصف',
+ 'unit': 'الوحدة',
+ 'quantity': 'الكمية',
+ 'unit_price': st.column_config.NumberColumn('سعر الوحدة', format='%d ريال'),
+ 'total_price': st.column_config.NumberColumn('السعر الإجمالي', format='%d ريال'),
+ 'category': 'الفئة'
+ },
+ hide_index=True,
+ use_container_width=True
+ )
+
+ # عرض ملخص جدول الكميات حسب الفئة
+ st.markdown("### ملخص جدول الكميات حسب الفئة")
+
+ # تجميع البيانات حسب الفئة
+ category_totals = {}
+ for item in st.session_state.bill_of_quantities:
+ category = item['category']
+ if category in category_totals:
+ category_totals[category] += item['total_price']
+ else:
+ category_totals[category] = item['total_price']
+
+ # إنشاء DataFrame للملخص
+ category_summary_df = pd.DataFrame({
+ 'الفئة': list(category_totals.keys()),
+ 'المبلغ الإجمالي': list(category_totals.values())
+ })
+
+ # حساب النسبة المئوية
+ total_boq = sum(category_totals.values())
+ category_summary_df['النسبة المئوية'] = category_summary_df['المبلغ الإجمالي'] / total_boq * 100
+
+ # ترتيب البيانات تنازليًا حسب المبلغ
+ category_summary_df = category_summary_df.sort_values('المبلغ الإجمالي', ascending=False)
+
+ st.dataframe(
+ category_summary_df,
+ column_config={
+ 'الفئة': st.column_config.TextColumn('الفئة'),
+ 'المبلغ الإجمالي': st.column_config.NumberColumn('المبلغ الإجمالي', format='%d ريال'),
+ 'النسبة المئوية': st.column_config.NumberColumn('النسبة المئوية', format='%.1f%%')
+ },
+ hide_index=True,
+ use_container_width=True
+ )
+
+ # عرض رسم بياني للملخص
+ fig = px.pie(
+ category_summary_df,
+ values='المبلغ الإجمالي',
+ names='الفئة',
+ title='توزيع تكاليف البنود حسب الفئة',
+ color_discrete_sequence=px.colors.qualitative.Set3
+ )
+
+ st.plotly_chart(fig, use_container_width=True)
+
+ # عرض رسم بياني شريطي للملخص
+ fig = px.bar(
+ category_summary_df,
+ x='الفئة',
+ y='المبلغ الإجمالي',
+ title='إجمالي تكلفة البنود حسب الفئة',
+ color='الفئة',
+ text_auto=True
+ )
+
+ st.plotly_chart(fig, use_container_width=True)
+
+ def _render_cost_analysis_report(self):
+ """عرض تقرير تحليل التكاليف"""
+
+ st.markdown("## تقرير تحليل التكاليف")
+
+ # عرض جدول تحليل التكاليف
+ cost_df = pd.DataFrame(st.session_state.cost_analysis)
+
+ st.dataframe(
+ cost_df[['category', 'subcategory', 'description', 'amount', 'percentage']],
+ column_config={
+ 'category': 'الفئة',
+ 'subcategory': 'الفئة الفرعية',
+ 'description': 'الوصف',
+ 'amount': st.column_config.NumberColumn('المبلغ', format='%d ريال'),
+ 'percentage': st.column_config.NumberColumn('النسبة المئوية', format='%.1f%%')
+ },
+ hide_index=True,
+ use_container_width=True
+ )
+
+ # عرض ملخص تحليل التكاليف حسب الفئة
+ st.markdown("### ملخص تحليل التكاليف حسب الفئة")
+
+ # تجميع البيانات حسب الفئة
+ category_totals = {}
+ for item in st.session_state.cost_analysis:
+ category = item['category']
+ if category in category_totals:
+ category_totals[category] += item['amount']
+ else:
+ category_totals[category] = item['amount']
+
+ # إنشاء DataFrame للملخص
+ category_summary_df = pd.DataFrame({
+ 'الفئة': list(category_totals.keys()),
+ 'المبلغ الإجمالي': list(category_totals.values())
+ })
+
+ # حساب النسبة المئوية
+ total_cost = sum(category_totals.values())
+ category_summary_df['النسبة المئوية'] = category_summary_df['المبلغ الإجمالي'] / total_cost * 100
+
+ # ترتيب البيانات تنازليًا حسب المبلغ
+ category_summary_df = category_summary_df.sort_values('المبلغ الإجمالي', ascending=False)
+
+ st.dataframe(
+ category_summary_df,
+ column_config={
+ 'الفئة': st.column_config.TextColumn('الفئة'),
+ 'المبلغ الإجمالي': st.column_config.NumberColumn('المبلغ الإجمالي', format='%d ريال'),
+ 'النسبة المئوية': st.column_config.NumberColumn('النسبة المئوية', format='%.1f%%')
+ },
+ hide_index=True,
+ use_container_width=True
+ )
+
+ # عرض رسم بياني للملخص
+ fig = px.pie(
+ category_summary_df,
+ values='المبلغ الإجمالي',
+ names='الفئة',
+ title='توزيع التكاليف حسب الفئة',
+ color_discrete_sequence=px.colors.qualitative.Set3
+ )
+
+ st.plotly_chart(fig, use_container_width=True)
+
+ # عرض ملخص تحليل التكاليف حسب الفئة والفئة الفرعية
+ st.markdown("### ملخص تحليل التكاليف حسب الفئة والفئة الفرعية")
+
+ # تجميع البيانات حسب الفئة والفئة الفرعية
+ subcategory_totals = {}
+ for item in st.session_state.cost_analysis:
+ category = item['category']
+ subcategory = item['subcategory']
+ key = f"{category} - {subcategory}"
+ if key in subcategory_totals:
+ subcategory_totals[key] += item['amount']
+ else:
+ subcategory_totals[key] = item['amount']
+
+ # إنشاء DataFrame للملخص
+ subcategory_summary_df = pd.DataFrame({
+ 'الفئة والفئة الفرعية': list(subcategory_totals.keys()),
+ 'المبلغ الإجمالي': list(subcategory_totals.values())
+ })
+
+ # حساب النسبة المئوية
+ subcategory_summary_df['النسبة المئوية'] = subcategory_summary_df['المبلغ الإجمالي'] / total_cost * 100
+
+ # ترتيب البيانات تنازليًا حسب المبلغ
+ subcategory_summary_df = subcategory_summary_df.sort_values('المبلغ الإجمالي', ascending=False)
+
+ st.dataframe(
+ subcategory_summary_df,
+ column_config={
+ 'الفئة والفئة الفرعية': st.column_config.TextColumn('الفئة والفئة الفرعية'),
+ 'المبلغ الإجمالي': st.column_config.NumberColumn('المبلغ الإجمالي', format='%d ريال'),
+ 'النسبة المئوية': st.column_config.NumberColumn('النسبة المئوية', format='%.1f%%')
+ },
+ hide_index=True,
+ use_container_width=True
+ )
+
+ # عرض رسم بياني للملخص
+ fig = px.bar(
+ subcategory_summary_df,
+ x='الفئة والفئة الفرعية',
+ y='المبلغ الإجمالي',
+ title='توزيع التكاليف حسب الفئة والفئة الفرعية',
+ color='الفئة والفئة الفرعية',
+ text_auto=True
+ )
+
+ st.plotly_chart(fig, use_container_width=True)
+
+ # عرض تحليل هيكل التكاليف
+ st.markdown("### تحليل هيكل التكاليف")
+
+ # إنشاء رسم بياني للنسب المئوية
+ fig = px.treemap(
+ cost_df,
+ path=['category', 'subcategory', 'description'],
+ values='amount',
+ title='تحليل هيكل التكاليف',
+ color='percentage',
+ color_continuous_scale='RdBu',
+ color_continuous_midpoint=np.average(cost_df['percentage'])
+ )
+
+ st.plotly_chart(fig, use_container_width=True)
+
+ def _render_pricing_scenarios_report(self):
+ """عرض تقرير سيناريوهات التسعير"""
+
+ st.markdown("## تقرير سيناريوهات التسعير")
+
+ # عرض جدول سيناريوهات التسعير
+ scenarios_df = pd.DataFrame(st.session_state.price_scenarios)
+
+ st.dataframe(
+ scenarios_df[['name', 'description', 'total_cost', 'profit_margin', 'total_price', 'is_active']],
+ column_config={
+ 'name': 'اسم السيناريو',
+ 'description': 'الوصف',
+ 'total_cost': st.column_config.NumberColumn('إجمالي التكلفة', format='%d ريال'),
+ 'profit_margin': st.column_config.NumberColumn('هامش الربح', format='%.1f%%'),
+ 'total_price': st.column_config.NumberColumn('السعر الإجمالي', format='%d ريال'),
+ 'is_active': st.column_config.CheckboxColumn('نشط')
+ },
+ hide_index=True,
+ use_container_width=True
+ )
+
+ # عرض مقارنة السيناريوهات
+ st.markdown("### مقارنة السيناريوهات")
+
+ # إنشاء رسم بياني للمقارنة
+ fig = go.Figure()
+
+ for scenario in st.session_state.price_scenarios:
+ fig.add_trace(go.Bar(
+ name=scenario['name'],
+ x=['التكلفة', 'الربح', 'السعر الإجمالي'],
+ y=[
+ scenario['total_cost'],
+ scenario['total_price'] - scenario['total_cost'],
+ scenario['total_price']
+ ],
+ text=[
+ f"{scenario['total_cost']:,.0f}",
+ f"{scenario['total_price'] - scenario['total_cost']:,.0f}",
+ f"{scenario['total_price']:,.0f}"
+ ],
+ textposition='auto'
+ ))
+
+ fig.update_layout(
+ title='مقارنة السيناريوهات',
+ barmode='group',
+ xaxis_title='العنصر',
+ yaxis_title='المبلغ (ريال)'
+ )
+
+ st.plotly_chart(fig, use_container_width=True)
+
+ # عرض مقارنة هوامش الربح
+ st.markdown("### مقارنة هوامش الربح")
+
+ # إنشاء DataFrame للمقارنة
+ profit_comparison_df = pd.DataFrame({
+ 'السيناريو': [scenario['name'] for scenario in st.session_state.price_scenarios],
+ 'هامش الربح (%)': [scenario['profit_margin'] for scenario in st.session_state.price_scenarios],
+ 'مبلغ الربح (ريال)': [scenario['total_price'] - scenario['total_cost'] for scenario in st.session_state.price_scenarios]
+ })
+
+ # ترتيب البيانات تنازليًا حسب هامش الربح
+ profit_comparison_df = profit_comparison_df.sort_values('هامش الربح (%)', ascending=False)
+
+ # إنشاء رسم بياني شريطي
+ fig = px.bar(
+ profit_comparison_df,
+ x='السيناريو',
+ y='هامش الربح (%)',
+ title='مقارنة هوامش الربح',
+ color='السيناريو',
+ text_auto=True
+ )
+
+ st.plotly_chart(fig, use_container_width=True)
+
+ # عرض تحليل حساسية هامش الربح
+ st.markdown("### تحليل حساسية هامش الربح")
+
+ # الحصول على التكلفة الإجمالية من السيناريو النشط أو الأول
+ active_scenario = next((s for s in st.session_state.price_scenarios if s['is_active']), st.session_state.price_scenarios[0])
+ total_cost = active_scenario['total_cost']
+
+ # إنشاء بيانات لتحليل الحساسية
+ profit_margins = list(range(5, 26, 1)) # من 5% إلى 25%
+ total_prices = [total_cost * (1 + margin/100) for margin in profit_margins]
+
+ # إنشاء DataFrame للرسم البياني
+ sensitivity_df = pd.DataFrame({
+ 'هامش الربح (%)': profit_margins,
+ 'السعر الإجمالي': total_prices
+ })
+
+ # إنشاء رسم بياني خطي
+ fig = px.line(
+ sensitivity_df,
+ x='هامش الربح (%)',
+ y='السعر الإجمالي',
+ title='تحليل حساسية هامش الربح',
+ markers=True
+ )
+
+ # إضافة خط أفقي يمثل السعر التنافسي (افتراضي)
+ competitive_price = 650000
+ fig.add_hline(
+ y=competitive_price,
+ line_dash="dash",
+ line_color="red",
+ annotation_text="السعر التنافسي",
+ annotation_position="bottom right"
+ )
+
+ st.plotly_chart(fig, use_container_width=True)
+
+ def _render_competitive_analysis_report(self):
+ """عرض تقرير المقارنة التنافسية"""
+
+ st.markdown("## تقرير المقارنة التنافسية")
+
+ # عرض جدول المقارنة التنافسية
+ competitive_df = pd.DataFrame(st.session_state.competitive_analysis)
+
+ st.dataframe(
+ competitive_df[['competitor', 'project_type', 'price_per_sqm', 'delivery_time', 'quality_rating', 'market_share']],
+ column_config={
+ 'competitor': 'المنافس',
+ 'project_type': 'نوع المشروع',
+ 'price_per_sqm': st.column_config.NumberColumn('السعر لكل متر مربع', format='%d ريال'),
+ 'delivery_time': st.column_config.NumberColumn('مدة التسليم (شهر)', format='%d'),
+ 'quality_rating': st.column_config.NumberColumn('تقييم الجودة', format='%.1f/5.0'),
+ 'market_share': st.column_config.NumberColumn('الحصة السوقية', format='%.1f%%')
+ },
+ hide_index=True,
+ use_container_width=True
+ )
+
+ # عرض مقارنة الأسعار
+ st.markdown("### مقارنة الأسعار")
+
+ # ترتي�� البيانات تصاعديًا حسب السعر
+ price_comparison_df = competitive_df.sort_values('price_per_sqm')
+
+ # إنشاء رسم بياني شريطي
+ fig = px.bar(
+ price_comparison_df,
+ x='competitor',
+ y='price_per_sqm',
+ title='مقارنة الأسعار لكل متر مربع',
+ color='competitor',
+ text_auto=True
+ )
+
+ # تمييز شركتنا
+ for i, competitor in enumerate(price_comparison_df['competitor']):
+ if competitor == 'شركتنا':
+ fig.data[0].marker.color = ['blue' if x != i else 'red' for x in range(len(price_comparison_df))]
+ break
+
+ st.plotly_chart(fig, use_container_width=True)
+
+ # عرض مقارنة الجودة والسعر
+ st.markdown("### مقارنة الجودة والسعر")
+
+ # إنشاء رسم بياني للعلاقة بين السعر والجودة
+ fig = px.scatter(
+ price_comparison_df,
+ x='price_per_sqm',
+ y='quality_rating',
+ size='market_share',
+ color='competitor',
+ title='العلاقة بين السعر والجودة والحصة السوقية',
+ labels={
+ 'price_per_sqm': 'السعر لكل متر مربع (ريال)',
+ 'quality_rating': 'تقييم الجودة',
+ 'market_share': 'الحصة السوقية (%)'
+ },
+ text='competitor'
+ )
+
+ fig.update_traces(textposition='top center')
+
+ # إضافة خط اتجاه
+ fig.update_layout(
+ shapes=[
+ dict(
+ type='line',
+ x0=min(price_comparison_df['price_per_sqm']),
+ y0=min(price_comparison_df['quality_rating']),
+ x1=max(price_comparison_df['price_per_sqm']),
+ y1=max(price_comparison_df['quality_rating']),
+ line=dict(color='gray', dash='dash')
+ )
+ ]
+ )
+
+ st.plotly_chart(fig, use_container_width=True)
+
+ # عرض مقارنة مدة التسليم
+ st.markdown("### مقارنة مدة التسليم")
+
+ # ترتيب البيانات تصاعديًا حسب مدة التسليم
+ delivery_comparison_df = competitive_df.sort_values('delivery_time')
+
+ # إنشاء رسم بياني شريطي
+ fig = px.bar(
+ delivery_comparison_df,
+ x='competitor',
+ y='delivery_time',
+ title='مقارنة مدة التسليم (شهر)',
+ color='competitor',
+ text_auto=True
+ )
+
+ # تمييز شركتنا
+ for i, competitor in enumerate(delivery_comparison_df['competitor']):
+ if competitor == 'شركتنا':
+ fig.data[0].marker.color = ['blue' if x != i else 'red' for x in range(len(delivery_comparison_df))]
+ break
+
+ st.plotly_chart(fig, use_container_width=True)
+
+ # عرض تحليل الحصة السوقية
+ st.markdown("### تحليل الحصة السوقية")
+
+ # إنشاء رسم بياني دائري للحصة السوقية
+ fig = px.pie(
+ competitive_df,
+ values='market_share',
+ names='competitor',
+ title='توزيع الحصة السوقية',
+ hole=0.4
+ )
+
+ st.plotly_chart(fig, use_container_width=True)
+
+ # عرض تحليل الموقع التنافسي
+ st.markdown("### تحليل الموقع التنافسي")
+
+ # إيجاد بيانات شركتنا
+ our_company = next((item for item in st.session_state.competitive_analysis if item['competitor'] == 'شركتنا'), None)
+
+ if our_company:
+ # حساب متوسطات السوق
+ avg_price = competitive_df['price_per_sqm'].mean()
+ avg_delivery = competitive_df['delivery_time'].mean()
+ avg_quality = competitive_df['quality_rating'].mean()
+
+ # إنشاء بيانات المقارنة
+ comparison_data = {
+ 'المؤشر': ['السعر لكل متر مربع', 'مدة التسليم', 'تقييم الجودة'],
+ 'قيمة شركتنا': [our_company['price_per_sqm'], our_company['delivery_time'], our_company['quality_rating']],
+ 'متوسط السوق': [avg_price, avg_delivery, avg_quality],
+ 'الفرق (%)': [
+ (our_company['price_per_sqm'] - avg_price) / avg_price * 100,
+ (our_company['delivery_time'] - avg_delivery) / avg_delivery * 100,
+ (our_company['quality_rating'] - avg_quality) / avg_quality * 100
+ ]
+ }
+
+ comparison_df = pd.DataFrame(comparison_data)
+
+ st.dataframe(
+ comparison_df,
+ column_config={
+ 'المؤشر': st.column_config.TextColumn('المؤشر'),
+ 'قيمة شركتنا': st.column_config.NumberColumn('قيمة شركتنا'),
+ 'متوسط السوق': st.column_config.NumberColumn('متوسط السوق'),
+ 'الفرق (%)': st.column_config.NumberColumn('الفرق (%)', format='%+.1f%%')
+ },
+ hide_index=True,
+ use_container_width=True
+ )
+
+ # إنشاء رسم بياني راداري للموقع التنافسي
+ # تحويل البيانات إلى نسب مئوية للمقارنة
+ max_price = competitive_df['price_per_sqm'].max()
+ min_price = competitive_df['price_per_sqm'].min()
+ price_range = max_price - min_price
+
+ max_delivery = competitive_df['delivery_time'].max()
+ min_delivery = competitive_df['delivery_time'].min()
+ delivery_range = max_delivery - min_delivery
+
+ # ملاحظة: نقوم بعكس مقياس السعر ومدة التسليم لأن القيم الأقل أفضل
+ normalized_price = 100 - ((our_company['price_per_sqm'] - min_price) / price_range * 100) if price_range > 0 else 50
+ normalized_delivery = 100 - ((our_company['delivery_time'] - min_delivery) / delivery_range * 100) if delivery_range > 0 else 50
+ normalized_quality = (our_company['quality_rating'] / 5) * 100
+ normalized_market_share = (our_company['market_share'] / competitive_df['market_share'].max()) * 100
+
+ # إنشاء رسم بياني راداري
+ fig = go.Figure()
+
+ fig.add_trace(go.Scatterpolar(
+ r=[normalized_price, normalized_delivery, normalized_quality, normalized_market_share],
+ theta=['السعر التنافسي', 'سرعة التسليم', 'الجودة', 'الحصة السوقية'],
+ fill='toself',
+ name='شركتنا'
+ ))
+
+ fig.update_layout(
+ polar=dict(
+ radialaxis=dict(
+ visible=True,
+ range=[0, 100]
+ )
+ ),
+ title='تحليل الموقع التنافسي لشركتنا'
+ )
+
+ st.plotly_chart(fig, use_container_width=True)