""" وحدة التسعير - التطبيق الرئيسي """ 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': 'B-001', 'description': 'توريد وتركيب حديد تسليح', 'unit': 'طن', 'quantity': 15, 'unit_price': 3500, 'total_price': 52500, 'category': 'أعمال حديد' }, { 'id': 5, 'code': 'C-001', 'description': 'توريد وبناء طابوق', 'unit': 'م2', 'quantity': 450, 'unit_price': 120, 'total_price': 54000, 'category': 'أعمال بناء' } ] if 'cost_analysis' not in st.session_state: st.session_state.cost_analysis = [ { 'id': 1, 'category': 'تكاليف مباشرة', 'subcategory': 'مواد', 'description': 'خرسانة', 'amount': 168500, 'percentage': 25.2 }, { 'id': 2, 'category': 'تكاليف مباشرة', 'subcategory': 'مواد', 'description': 'حديد تسليح', 'amount': 52500, 'percentage': 7.8 }, { '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 } ] 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.bar( direct_cost_df, x='الفئة الفرعية', y='المبلغ', title='توزيع التكاليف المباشرة', color='الفئة الفرعية', text_auto='.2s' ) st.plotly_chart(fig, use_container_width=True) # عرض مقارنة سيناريوهات التسعير st.markdown("### مقارنة سيناريوهات التسعير") # إنشاء DataFrame للرسم البياني scenarios_df = pd.DataFrame({ 'السيناريو': [item['name'] for item in st.session_state.price_scenarios], 'التكلفة الإجمالية': [item['total_cost'] for item in st.session_state.price_scenarios], 'هامش الربح (%)': [item['profit_margin'] for item in st.session_state.price_scenarios], 'السعر الإجمالي': [item['total_price'] for item in st.session_state.price_scenarios] }) # إنشاء رسم بياني شريطي مزدوج fig = go.Figure() # إضافة شريط للتكلفة الإجمالية fig.add_trace(go.Bar( x=scenarios_df['السيناريو'], y=scenarios_df['التكلفة الإجمالية'], name='التكلفة الإجمالية', marker_color='indianred' )) # إضافة شريط للسعر الإجمالي fig.add_trace(go.Bar( x=scenarios_df['السيناريو'], y=scenarios_df['السعر الإجمالي'], name='السعر الإجمالي', marker_color='lightsalmon' )) # إضافة خط لهامش الربح fig.add_trace(go.Scatter( x=scenarios_df['السيناريو'], y=scenarios_df['هامش الربح (%)'] * 10000, # تكبير القيم لتظهر على الرسم البياني name='هامش الربح (%)', yaxis='y2', line=dict(color='royalblue', width=4) )) # تعديل تخطيط الرسم البياني fig.update_layout( title='مقارنة سيناريوهات التسعير', xaxis_title='السيناريو', yaxis_title='المبلغ (ريال)', yaxis2=dict( title='هامش الربح (%)', titlefont=dict(color='royalblue'), tickfont=dict(color='royalblue'), overlaying='y', side='right', range=[0, 20] ), barmode='group', legend=dict( x=0, y=1.2, orientation='h' ) ) # تعديل النص على الأشرطة fig.update_traces( texttemplate='%{y:,.0f}', textposition='outside' ) st.plotly_chart(fig, use_container_width=True) # عرض مؤشرات الأداء الرئيسية st.markdown("### مؤشرات الأداء الرئيسية") col1, col2, col3 = st.columns(3) with col1: # حساب نسبة التكاليف المباشرة من إجمالي التكاليف direct_cost_percentage = (total_direct_cost / total_cost) * 100 st.metric("نسبة التكاليف المباشرة", f"{direct_cost_percentage:.1f}%") with col2: # حساب نسبة التكاليف غير المباشرة من إجمالي التكاليف indirect_cost_percentage = (total_indirect_cost / total_cost) * 100 st.metric("نسبة التكاليف غير المباشرة", f"{indirect_cost_percentage:.1f}%") with col3: # حساب نسبة هامش الربح من السعر الإجمالي profit_margin = (total_profit / total_price) * 100 st.metric("هامش الربح", f"{profit_margin:.1f}%") def _render_bill_of_quantities_tab(self): """عرض تبويب جدول الكميات""" st.markdown("### جدول الكميات") # عرض جدول الكميات الحالي st.markdown("#### قائمة البنود") # تحويل قائمة البنود إلى DataFrame boq_df = pd.DataFrame(st.session_state.bill_of_quantities) # عرض البنود كجدول قابل للتعديل edited_df = st.data_editor( boq_df, column_config={ "id": st.column_config.NumberColumn("الرقم", disabled=True), "code": st.column_config.TextColumn("الكود"), "description": st.column_config.TextColumn("الوصف"), "unit": st.column_config.SelectboxColumn( "الوحدة", options=["م3", "م2", "طن", "كجم", "عدد", "لتر", "متر"] ), "quantity": st.column_config.NumberColumn("الكمية", min_value=0), "unit_price": st.column_config.NumberColumn("سعر الوحدة (ريال)", min_value=0, format="%.2f"), "total_price": st.column_config.NumberColumn("السعر الإجمالي (ريال)", min_value=0, format="%.2f", disabled=True), "category": st.column_config.SelectboxColumn( "الفئة", options=["أعمال ترابية", "أعمال خرسانية", "أعمال حديد", "أعمال بناء", "أعمال تشطيب", "أعمال كهربائية", "أعمال ميكانيكية", "أعمال صحية", "أخرى"] ) }, use_container_width=True, hide_index=True, num_rows="dynamic" ) # تحديث السعر الإجمالي لكل بند for i, row in edited_df.iterrows(): edited_df.at[i, 'total_price'] = row['quantity'] * row['unit_price'] # تحديث قائمة البنود if not edited_df.equals(boq_df): st.session_state.bill_of_quantities = edited_df.to_dict('records') st.success("تم تحديث جدول الكميات بنجاح!") # عرض إجمالي جدول الكميات total_boq = sum(item['total_price'] for item in st.session_state.bill_of_quantities) st.metric("إجمالي جدول الكميات", f"{total_boq:,.2f} ريال") # إضافة بند جديد st.markdown("#### إضافة بند جديد") with st.form(key="add_boq_item_form"): col1, col2 = st.columns(2) with col1: new_code = st.text_input("الكود", key="new_boq_code") new_description = st.text_area("الوصف", key="new_boq_description") new_category = st.selectbox( "الفئة", ["أعمال ترابية", "أعمال خرسانية", "أعمال حديد", "أعمال بناء", "أعمال تشطيب", "أعمال كهربائية", "أعمال ميكانيكية", "أعمال صحية", "أخرى"], key="new_boq_category" ) with col2: new_unit = st.selectbox( "الوحدة", ["م3", "م2", "طن", "كجم", "عدد", "لتر", "متر"], key="new_boq_unit" ) new_quantity = st.number_input("الكمية", min_value=0.0, key="new_boq_quantity") new_unit_price = st.number_input("سعر الوحدة (ريال)", min_value=0.0, key="new_boq_unit_price") submit_button = st.form_submit_button("إضافة بند") if submit_button: if new_code and new_description: # إنشاء معرف جديد new_id = max([item['id'] for item in st.session_state.bill_of_quantities], default=0) + 1 # حساب السعر الإجمالي new_total_price = new_quantity * new_unit_price # إضافة البند الجديد 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_code}' بنجاح!") st.rerun() else: st.error("يرجى إدخال الكود والوصف.") # تحليل جدول الكميات st.markdown("#### تحليل جدول الكميات") col1, col2 = st.columns(2) with col1: # توزيع البنود حسب الفئة 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'] category_df = pd.DataFrame({ 'الفئة': list(category_totals.keys()), 'المبلغ': list(category_totals.values()) }) fig = px.pie( category_df, values='المبلغ', names='الفئة', title='توزيع جدول الكميات حسب الفئة' ) st.plotly_chart(fig, use_container_width=True) with col2: # ترتيب البنود حسب القيمة top_items = sorted(st.session_state.bill_of_quantities, key=lambda x: x['total_price'], reverse=True)[:5] top_items_df = pd.DataFrame({ 'البند': [item['code'] + ' - ' + item['description'][:20] + '...' for item in top_items], 'القيمة': [item['total_price'] for item in top_items] }) fig = px.bar( top_items_df, x='البند', y='القيمة', title='أعلى 5 بنود من حيث القيمة', color='القيمة', text_auto='.2s' ) st.plotly_chart(fig, use_container_width=True) # استيراد وتصدير جدول الكميات st.markdown("#### استيراد وتصدير جدول الكميات") col1, col2 = st.columns(2) with col1: if st.button("تصدير جدول الكميات إلى Excel", key="export_boq_button"): # محاكاة تصدير البيانات st.success("تم تصدير جدول الكميات إلى Excel بنجاح!") with col2: uploaded_file = st.file_uploader("استيراد جدول الكميات من Excel", type=["xlsx"], key="import_boq_file") if uploaded_file is not None: if st.button("استيراد البيانات", key="import_boq_button"): # محاكاة استيراد البيانات st.success("تم استيراد جدول الكميات بنجاح!") def _render_cost_analysis_tab(self): """عرض تبويب تحليل التكاليف""" st.markdown("### تحليل التكاليف") # عرض تحليل التكاليف الحالي st.markdown("#### قائمة التكاليف") # تحويل قائمة التكاليف إلى DataFrame cost_df = pd.DataFrame(st.session_state.cost_analysis) # عرض التكاليف كجدول قابل للتعديل edited_df = st.data_editor( cost_df, column_config={ "id": st.column_config.NumberColumn("الرقم", disabled=True), "category": st.column_config.SelectboxColumn( "الفئة", options=["تكاليف مباشرة", "تكاليف غير مباشرة", "أرباح"] ), "subcategory": st.column_config.TextColumn("الفئة الفرعية"), "description": st.column_config.TextColumn("الوصف"), "amount": st.column_config.NumberColumn("المبلغ (ريال)", min_value=0, format="%.2f"), "percentage": st.column_config.NumberColumn("النسبة (%)", min_value=0, format="%.1f", disabled=True) }, use_container_width=True, hide_index=True, num_rows="dynamic" ) # حساب إجمالي التكاليف total_amount = sum(item['amount'] for item in st.session_state.cost_analysis) # تحديث النسبة المئوية لكل بند for i, row in edited_df.iterrows(): edited_df.at[i, 'percentage'] = (row['amount'] / total_amount) * 100 # تحديث قائمة التكاليف if not edited_df.equals(cost_df): st.session_state.cost_analysis = edited_df.to_dict('records') st.success("تم تحديث تحليل التكاليف بنجاح!") # عرض إجمالي التكاليف st.metric("إجمالي التكاليف", f"{total_amount:,.2f} ريال") # إضافة تكلفة جديدة st.markdown("#### إضافة تكلفة جديدة") with st.form(key="add_cost_item_form"): col1, col2 = st.columns(2) with col1: new_category = st.selectbox( "الفئة", ["تكاليف مباشرة", "تكاليف غير مباشرة", "أرباح"], key="new_cost_category" ) new_subcategory = st.text_input("الفئة الفرعية", key="new_cost_subcategory") with col2: new_description = st.text_input("الوصف", key="new_cost_description") new_amount = st.number_input("المبلغ (ريال)", min_value=0.0, key="new_cost_amount") submit_button = st.form_submit_button("إضافة تكلفة") if submit_button: if new_description and new_subcategory: # إنشاء معرف جديد new_id = max([item['id'] for item in st.session_state.cost_analysis], default=0) + 1 # حساب النسبة المئوية new_percentage = (new_amount / (total_amount + new_amount)) * 100 # إضافة التكلفة الجديدة st.session_state.cost_analysis.append({ 'id': new_id, 'category': new_category, 'subcategory': new_subcategory, 'description': new_description, 'amount': new_amount, 'percentage': new_percentage }) # إعادة حساب النسب المئوية لجميع البنود new_total = total_amount + new_amount for item in st.session_state.cost_analysis: item['percentage'] = (item['amount'] / new_total) * 100 st.success(f"تمت إضافة التكلفة '{new_description}' بنجاح!") st.rerun() else: st.error("يرجى إدخال الفئة الفرعية والوصف.") # تحليل التكاليف st.markdown("#### تحليل التكاليف") # تحليل التكاليف حسب الفئة 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'] category_df = pd.DataFrame({ 'الفئة': list(category_totals.keys()), 'المبلغ': list(category_totals.values()) }) fig = px.pie( category_df, values='المبلغ', names='الفئة', title='توزيع التكاليف حسب الفئة', color_discrete_sequence=px.colors.qualitative.Set3 ) st.plotly_chart(fig, use_container_width=True) # تحليل التكاليف المباشرة st.markdown("##### تحليل التكاليف المباشرة") col1, col2 = st.columns(2) with col1: # تجميع البيانات حسب الفئة الفرعية للتكاليف المباشرة direct_subcategory_totals = {} for item in st.session_state.cost_analysis: if item['category'] == 'تكاليف مباشرة': subcategory = item['subcategory'] if subcategory in direct_subcategory_totals: direct_subcategory_totals[subcategory] += item['amount'] else: direct_subcategory_totals[subcategory] = item['amount'] direct_subcategory_df = pd.DataFrame({ 'الفئة الفرعية': list(direct_subcategory_totals.keys()), 'المبلغ': list(direct_subcategory_totals.values()) }) fig = px.pie( direct_subcategory_df, values='المبلغ', names='الفئة الفرعية', title='توزيع التكاليف المباشرة حسب الفئة الفرعية' ) st.plotly_chart(fig, use_container_width=True) with col2: # تجميع البيانات حسب الوصف للتكاليف المباشرة direct_description_totals = {} for item in st.session_state.cost_analysis: if item['category'] == 'تكاليف مباشرة': description = item['description'] if description in direct_description_totals: direct_description_totals[description] += item['amount'] else: direct_description_totals[description] = item['amount'] direct_description_df = pd.DataFrame({ 'الوصف': list(direct_description_totals.keys()), 'المبلغ': list(direct_description_totals.values()) }) # ترتيب البيانات تنازلياً حسب المبلغ direct_description_df = direct_description_df.sort_values(by='المبلغ', ascending=False) fig = px.bar( direct_description_df, x='الوصف', y='المبلغ', title='توزيع التكاليف المباشرة حسب البند', color='المبلغ', text_auto='.2s' ) st.plotly_chart(fig, use_container_width=True) # تحليل التكاليف غير المباشرة st.markdown("##### تحليل التكاليف غير المباشرة") col1, col2 = st.columns(2) with col1: # تجميع البيانات حسب الفئة الفرعية للتكاليف غير المباشرة indirect_subcategory_totals = {} for item in st.session_state.cost_analysis: if item['category'] == 'تكاليف غير مباشرة': subcategory = item['subcategory'] if subcategory in indirect_subcategory_totals: indirect_subcategory_totals[subcategory] += item['amount'] else: indirect_subcategory_totals[subcategory] = item['amount'] indirect_subcategory_df = pd.DataFrame({ 'الفئة الفرعية': list(indirect_subcategory_totals.keys()), 'المبلغ': list(indirect_subcategory_totals.values()) }) fig = px.pie( indirect_subcategory_df, values='المبلغ', names='الفئة الفرعية', title='توزيع التكاليف غير المباشرة حسب الفئة الفرعية' ) st.plotly_chart(fig, use_container_width=True) with col2: # تجميع البيانات حسب الوصف للتكاليف غير المباشرة indirect_description_totals = {} for item in st.session_state.cost_analysis: if item['category'] == 'تكاليف غير مباشرة': description = item['description'] if description in indirect_description_totals: indirect_description_totals[description] += item['amount'] else: indirect_description_totals[description] = item['amount'] indirect_description_df = pd.DataFrame({ 'الوصف': list(indirect_description_totals.keys()), 'المبلغ': list(indirect_description_totals.values()) }) # ترتيب البيانات تنازلياً حسب المبلغ indirect_description_df = indirect_description_df.sort_values(by='المبلغ', ascending=False) fig = px.bar( indirect_description_df, x='الوصف', y='المبلغ', title='توزيع التكاليف غير المباشرة حسب البند', color='المبلغ', text_auto='.2s' ) st.plotly_chart(fig, use_container_width=True) # استيراد وتصدير تحليل التكاليف st.markdown("#### استيراد وتصدير تحليل التكاليف") col1, col2 = st.columns(2) with col1: if st.button("تصدير تحليل التكاليف إلى Excel", key="export_cost_button"): # محاكاة تصدير البيانات st.success("تم تصدير تحليل التكاليف إلى Excel بنجاح!") with col2: uploaded_file = st.file_uploader("استيراد تحليل التكاليف من Excel", type=["xlsx"], key="import_cost_file") if uploaded_file is not None: if st.button("استيراد البيانات", key="import_cost_button"): # محاكاة استيراد البيانات st.success("تم استيراد تحليل التكاليف بنجاح!") def _render_pricing_scenarios_tab(self): """عرض تبويب سيناريوهات التسعير""" st.markdown("### سيناريوهات التسعير") # عرض سيناريوهات التسعير الحالية st.markdown("#### قائمة السيناريوهات") # تحويل قائمة السيناريوهات إلى DataFrame scenarios_df = pd.DataFrame(st.session_state.price_scenarios) # عرض السيناريوهات كجدول قابل للتعديل edited_df = st.data_editor( scenarios_df, column_config={ "id": st.column_config.NumberColumn("الرقم", disabled=True), "name": st.column_config.TextColumn("اسم السيناريو"), "description": st.column_config.TextColumn("الوصف"), "total_cost": st.column_config.NumberColumn("إجمالي التكلفة (ريال)", min_value=0, format="%.2f"), "profit_margin": st.column_config.NumberColumn("هامش الربح (%)", min_value=0, format="%.1f"), "total_price": st.column_config.NumberColumn("السعر الإجمالي (ريال)", min_value=0, format="%.2f"), "is_active": st.column_config.CheckboxColumn("نشط") }, use_container_width=True, hide_index=True, num_rows="dynamic" ) # تحديث قائمة السيناريوهات if not edited_df.equals(scenarios_df): # التأكد من وجود سيناريو نشط واحد فقط active_count = sum(edited_df['is_active']) if active_count != 1: st.error("يجب أن يكون هناك سيناريو نشط واحد فقط.") else: st.session_state.price_scenarios = edited_df.to_dict('records') st.success("تم تحديث سيناريوهات التسعير بنجاح!") # إضافة سيناريو جديد st.markdown("#### إضافة سيناريو جديد") with st.form(key="add_scenario_form"): col1, col2 = st.columns(2) with col1: new_name = st.text_input("اسم السيناريو", key="new_scenario_name") new_description = st.text_area("الوصف", key="new_scenario_description") with col2: # حساب إجمالي التكاليف الحالي total_cost = sum(item['amount'] for item in st.session_state.cost_analysis if item['category'] != 'أرباح') st.number_input("إجمالي التكلفة (ريال)", min_value=0.0, value=total_cost, key="new_scenario_total_cost", disabled=True) new_profit_margin = st.number_input("هامش الربح (%)", min_value=0.0, max_value=100.0, value=10.0, key="new_scenario_profit_margin") # حساب السعر الإجمالي new_profit_amount = total_cost * (new_profit_margin / 100) new_total_price = total_cost + new_profit_amount st.number_input("السعر الإجمالي (ريال)", min_value=0.0, value=new_total_price, key="new_scenario_total_price", disabled=True) new_is_active = st.checkbox("نشط", key="new_scenario_is_active") submit_button = st.form_submit_button("إضافة سيناريو") if submit_button: if new_name: # التحقق من حالة التنشيط if new_is_active: # إلغاء تنشيط جميع السيناريوهات الأخرى for scenario in st.session_state.price_scenarios: scenario['is_active'] = False # إنشاء معرف جديد new_id = max([item['id'] for item in st.session_state.price_scenarios], default=0) + 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': new_is_active }) st.success(f"تمت إضافة السيناريو '{new_name}' بنجاح!") st.rerun() else: st.error("يرجى إدخال اسم السيناريو.") # تحليل السيناريوهات st.markdown("#### تحليل السيناريوهات") # مقارنة السيناريوهات st.markdown("##### مقارنة السيناريوهات") # إنشاء DataFrame للرسم البياني scenarios_comparison_df = pd.DataFrame({ 'السيناريو': [item['name'] for item in st.session_state.price_scenarios], 'التكلفة الإجمالية': [item['total_cost'] for item in st.session_state.price_scenarios], 'هامش الربح (%)': [item['profit_margin'] for item in st.session_state.price_scenarios], 'السعر الإجمالي': [item['total_price'] for item in st.session_state.price_scenarios], 'الحالة': ['نشط' if item['is_active'] else 'غير نشط' for item in st.session_state.price_scenarios] }) # إنشاء رسم بياني شريطي مزدوج fig = go.Figure() # إضافة شريط للتكلفة الإجمالية fig.add_trace(go.Bar( x=scenarios_comparison_df['السيناريو'], y=scenarios_comparison_df['التكلفة الإجمالية'], name='التكلفة الإجمالية', marker_color='indianred' )) # إضافة شريط للسعر الإجمالي fig.add_trace(go.Bar( x=scenarios_comparison_df['السيناريو'], y=scenarios_comparison_df['السعر الإجمالي'], name='السعر الإجمالي', marker_color='lightsalmon' )) # إضافة خط لهامش الربح fig.add_trace(go.Scatter( x=scenarios_comparison_df['السيناريو'], y=scenarios_comparison_df['هامش الربح (%)'] * 10000, # تكبير القيم لتظهر على الرسم البياني name='هامش الربح (%)', yaxis='y2', line=dict(color='royalblue', width=4) )) # تعديل تخطيط الرسم البياني fig.update_layout( title='مقارنة سيناريوهات التسعير', xaxis_title='السيناريو', yaxis_title='المبلغ (ريال)', yaxis2=dict( title='هامش الربح (%)', titlefont=dict(color='royalblue'), tickfont=dict(color='royalblue'), overlaying='y', side='right', range=[0, 20] ), barmode='group', legend=dict( x=0, y=1.2, orientation='h' ) ) # تعديل النص على الأشرطة fig.update_traces( texttemplate='%{y:,.0f}', textposition='outside' ) st.plotly_chart(fig, use_container_width=True) # تحليل تأثير هامش الربح st.markdown("##### تحليل تأثير هامش الربح") # إنشاء نطاق من هوامش الربح profit_margins = list(range(0, 21, 2)) # من 0% إلى 20% بزيادة 2% # حساب السعر الإجمالي لكل هامش ربح total_cost = st.session_state.price_scenarios[0]['total_cost'] # استخدام التكلفة الإجمالية من السيناريو الأول total_prices = [total_cost * (1 + margin / 100) for margin in profit_margins] # إنشاء DataFrame للرسم البياني profit_analysis_df = pd.DataFrame({ 'هامش الربح (%)': profit_margins, 'السعر الإجمالي': total_prices }) # إنشاء رسم بياني خطي fig = px.line( profit_analysis_df, x='هامش الربح (%)', y='السعر الإجمالي', title='تأثير هامش الربح على السعر الإجمالي', markers=True ) # تعديل النص على النقاط fig.update_traces( texttemplate='%{y:,.0f}', textposition='top center' ) st.plotly_chart(fig, use_container_width=True) # تحليل نقطة التعادل st.markdown("##### تحليل نقطة التعادل") # افتراض تكاليف ثابتة ومتغيرة fixed_costs = sum(item['amount'] for item in st.session_state.cost_analysis if item['category'] == 'تكاليف غير مباشرة') variable_costs = sum(item['amount'] for item in st.session_state.cost_analysis if item['category'] == 'تكاليف مباشرة') # افتراض سعر البيع من السيناريو النشط active_scenario = next((item for item in st.session_state.price_scenarios if item['is_active']), None) if active_scenario: selling_price = active_scenario['total_price'] else: selling_price = st.session_state.price_scenarios[0]['total_price'] # حساب نقطة التعادل if selling_price > variable_costs: breakeven_point = fixed_costs / (selling_price - variable_costs) st.metric("نقطة التعادل", f"{breakeven_point:.2f} وحدة") # إنشاء رسم بياني لنقطة التعادل units = list(range(0, int(breakeven_point * 2) + 1, max(1, int(breakeven_point / 10)))) total_costs = [fixed_costs + variable_costs * unit for unit in units] total_revenues = [selling_price * unit for unit in units] profits = [revenue - cost for revenue, cost in zip(total_revenues, total_costs)] breakeven_df = pd.DataFrame({ 'الوحدات': units, 'إجمالي التكاليف': total_costs, 'إجمالي الإيرادات': total_revenues, 'الربح': profits }) fig = go.Figure() fig.add_trace(go.Scatter( x=breakeven_df['الوحدات'], y=breakeven_df['إجمالي التكاليف'], name='إجمالي التكاليف', line=dict(color='red', width=2) )) fig.add_trace(go.Scatter( x=breakeven_df['الوحدات'], y=breakeven_df['إجمالي الإيرادات'], name='إجمالي الإيرادات', line=dict(color='green', width=2) )) fig.add_trace(go.Scatter( x=breakeven_df['الوحدات'], y=breakeven_df['الربح'], name='الربح', line=dict(color='blue', width=2) )) # إضافة خط عمودي عند نقطة التعادل fig.add_vline( x=breakeven_point, line_dash="dash", line_color="black", annotation_text=f"نقطة التعادل: {breakeven_point:.2f}", annotation_position="top right" ) # إضافة خط أفقي عند الصفر fig.add_hline( y=0, line_dash="dash", line_color="gray" ) fig.update_layout( title='تحليل نقطة التعادل', xaxis_title='الوحدات', yaxis_title='المبلغ (ريال)' ) st.plotly_chart(fig, use_container_width=True) else: st.warning("لا يمكن حساب نقطة التعادل لأن سعر البيع أقل من التكاليف المتغيرة.") def _render_competitive_analysis_tab(self): """عرض تبويب المقارنة التنافسية""" st.markdown("### المقارنة التنافسية") # بيانات افتراضية للمنافسين competitors_data = [ { 'name': 'شركتنا', 'price': 670000, 'quality': 4.5, 'delivery_time': 180, 'experience': 8, 'local_content': 85 }, { 'name': 'المنافس أ', 'price': 700000, 'quality': 4.2, 'delivery_time': 200, 'experience': 10, 'local_content': 75 }, { 'name': 'المنافس ب', 'price': 650000, 'quality': 3.8, 'delivery_time': 160, 'experience': 5, 'local_content': 90 }, { 'name': 'المنافس ج', 'price': 680000, 'quality': 4.0, 'delivery_time': 190, 'experience': 12, 'local_content': 80 } ] # عرض بيانات المنافسين st.markdown("#### بيانات المنافسين") competitors_df = pd.DataFrame(competitors_data) st.dataframe(competitors_df, use_container_width=True, hide_index=True) # مقارنة الأسعار st.markdown("#### مقارنة الأسعار") fig = px.bar( competitors_df, x='name', y='price', title='مقارنة الأسعار بين المنافسين', color='price', text_auto='.2s' ) fig.update_layout( xaxis_title='المنافس', yaxis_title='السعر (ريال)' ) st.plotly_chart(fig, use_container_width=True) # مقارنة متعددة الأبعاد st.markdown("#### مقارنة متعددة الأبعاد") # تحويل البيانات إلى تنسيق مناسب للرسم البياني الراداري categories = ['price', 'quality', 'delivery_time', 'experience', 'local_content'] # تطبيع البيانات (لجعل القيم بين 0 و 1) normalized_data = {} for category in categories: if category == 'price' or category == 'delivery_time': # للسعر ووقت التسليم، القيمة الأقل أفضل min_val = min(item[category] for item in competitors_data) max_val = max(item[category] for item in competitors_data) normalized_data[category] = [(max_val - item[category]) / (max_val - min_val) if max_val != min_val else 0.5 for item in competitors_data] else: # للجودة والخبرة والمحتوى المحلي، القيمة الأعلى أفضل min_val = min(item[category] for item in competitors_data) max_val = max(item[category] for item in competitors_data) normalized_data[category] = [(item[category] - min_val) / (max_val - min_val) if max_val != min_val else 0.5 for item in competitors_data] # إنشاء الرسم البياني الراداري fig = go.Figure() for i, competitor in enumerate(competitors_data): fig.add_trace(go.Scatterpolar( r=[normalized_data[category][i] for category in categories], theta=['السعر', 'الجودة', 'وقت التسليم', 'الخبرة', 'المحتوى المحلي'], fill='toself', name=competitor['name'] )) fig.update_layout( polar=dict( radialaxis=dict( visible=True, range=[0, 1] ) ), title='مقارنة متعددة الأبعاد بين المنافسين', showlegend=True ) st.plotly_chart(fig, use_container_width=True) # تحليل نقاط القوة والضعف st.markdown("#### تحليل نقاط القوة والضعف") # تحديد نقاط القوة والضعف لشركتنا our_company = competitors_data[0] strengths = [] weaknesses = [] # مقارنة السعر other_prices = [comp['price'] for comp in competitors_data[1:]] if our_company['price'] <= min(other_prices): strengths.append("السعر تنافسي جداً") elif our_company['price'] <= sum(other_prices) / len(other_prices): strengths.append("السعر تنافسي") else: weaknesses.append("السعر أعلى من المتوسط") # مقارنة الجودة other_qualities = [comp['quality'] for comp in competitors_data[1:]] if our_company['quality'] >= max(other_qualities): strengths.append("الجودة ممتازة") elif our_company['quality'] >= sum(other_qualities) / len(other_qualities): strengths.append("الجودة جيدة") else: weaknesses.append("الجودة أقل من المتوسط") # مقارنة وقت التسليم other_delivery_times = [comp['delivery_time'] for comp in competitors_data[1:]] if our_company['delivery_time'] <= min(other_delivery_times): strengths.append("وقت التسليم سريع جداً") elif our_company['delivery_time'] <= sum(other_delivery_times) / len(other_delivery_times): strengths.append("وقت التسليم جيد") else: weaknesses.append("وقت التسليم أطول من المتوسط") # مقارنة الخبرة other_experiences = [comp['experience'] for comp in competitors_data[1:]] if our_company['experience'] >= max(other_experiences): strengths.append("خبرة واسعة جداً") elif our_company['experience'] >= sum(other_experiences) / len(other_experiences): strengths.append("خبرة جيدة") else: weaknesses.append("خبرة أقل من المتوسط") # مقارنة المحتوى المحلي other_local_contents = [comp['local_content'] for comp in competitors_data[1:]] if our_company['local_content'] >= max(other_local_contents): strengths.append("محتوى محلي ممتاز") elif our_company['local_content'] >= sum(other_local_contents) / len(other_local_contents): strengths.append("محتوى محلي جيد") else: weaknesses.append("محتوى محلي أقل من المتوسط") # عرض نقاط القوة والضعف col1, col2 = st.columns(2) with col1: st.markdown("##### نقاط القوة") for strength in strengths: st.markdown(f"- {strength}") with col2: st.markdown("##### نقاط الضعف") for weakness in weaknesses: st.markdown(f"- {weakness}") # توصيات للتسعير st.markdown("#### توصيات للتسعير") # تحديد التوصيات بناءً على المقارنة recommendations = [] # توصية بناءً على السعر avg_price = sum(comp['price'] for comp in competitors_data) / len(competitors_data) if our_company['price'] > avg_price: recommendations.append("النظر في تخفيض السعر للمنافسة بشكل أفضل.") # توصية بناءً على الجودة if our_company['quality'] > sum(comp['quality'] for comp in competitors_data[1:]) / len(competitors_data[1:]): recommendations.append("التأكيد على جودة الخدمات في العروض التسويقية.") # توصية بناءً على وقت التسليم if our_company['delivery_time'] < sum(comp['delivery_time'] for comp in competitors_data[1:]) / len(competitors_data[1:]): recommendations.append("التأكيد على سرعة التسليم كميزة تنافسية.") # توصية بناءً على الخبرة if our_company['experience'] < max(comp['experience'] for comp in competitors_data[1:]): recommendations.append("تعزيز فريق العمل بخبرات إضافية.") # توصية بناءً على المحتوى المحلي if our_company['local_content'] > sum(comp['local_content'] for comp in competitors_data[1:]) / len(competitors_data[1:]): recommendations.append("التأكيد على نسبة المحتوى المحلي العالية في العروض.") # توصية عامة recommendations.append("مراجعة هيكل التكاليف بشكل دوري للحفاظ على القدرة التنافسية.") # عرض التوصيات for recommendation in recommendations: st.markdown(f"- {recommendation}") def _render_reports_tab(self): """عرض تبويب التقارير""" st.markdown("### التقارير") # قائمة التقارير المتاحة reports = [ "تقرير جدول الكميات", "تقرير تحليل التكاليف", "تقرير سيناريوهات التسعير", "تقرير المقارنة التنافسية", "تقرير ملخص التسعير" ] # اختيار التقرير selected_report = st.selectbox("اختر التقرير", reports) # خيارات التصدير export_format = st.radio("صيغة التصدير", ["PDF", "Excel", "Word"]) # زر إنشاء التقرير if st.button("إنشاء التقرير"): st.success(f"تم إنشاء {selected_report} بصيغة {export_format} بنجاح!") # عرض نموذج للتقرير st.markdown("#### نموذج التقرير") if selected_report == "تقرير جدول الكميات": self._render_boq_report() elif selected_report == "تقرير تحليل التكاليف": self._render_cost_analysis_report() elif selected_report == "تقرير سيناريوهات التسعير": self._render_pricing_scenarios_report() elif selected_report == "تقرير المقارنة التنافسية": self._render_competitive_analysis_report() elif selected_report == "تقرير ملخص التسعير": self._render_pricing_summary_report() def _render_boq_report(self): """عرض نموذج تقرير جدول الكميات""" st.markdown("### تقرير جدول الكميات") st.markdown("**تاريخ التقرير:** " + time.strftime("%Y-%m-%d")) st.markdown("**اسم المشروع:** مشروع إنشاء مبنى إداري") st.markdown("**رقم المناقصة:** T-2024-001") st.markdown("#### جدول الكميات") # عرض جدول الكميات boq_df = pd.DataFrame(st.session_state.bill_of_quantities) st.dataframe(boq_df, use_container_width=True, hide_index=True) # عرض إجمالي جدول الكميات total_boq = sum(item['total_price'] for item in st.session_state.bill_of_quantities) st.metric("إجمالي جدول الكميات", f"{total_boq:,.2f} ريال") # عرض توزيع البنود حسب الفئة 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'] category_df = pd.DataFrame({ 'الفئة': list(category_totals.keys()), 'المبلغ': list(category_totals.values()) }) fig = px.pie( category_df, values='المبلغ', names='الفئة', title='توزيع جدول الكميات حسب الفئة' ) st.plotly_chart(fig, use_container_width=True) def _render_cost_analysis_report(self): """عرض نموذج تقرير تحليل التكاليف""" st.markdown("### تقرير تحليل التكاليف") st.markdown("**تاريخ التقرير:** " + time.strftime("%Y-%m-%d")) st.markdown("**اسم المشروع:** مشروع إنشاء مبنى إداري") st.markdown("**رقم المناقصة:** T-2024-001") st.markdown("#### تحليل التكاليف") # عرض تحليل التكاليف cost_df = pd.DataFrame(st.session_state.cost_analysis) st.dataframe(cost_df, use_container_width=True, hide_index=True) # عرض إجمالي التكاليف total_cost = sum(item['amount'] for item in st.session_state.cost_analysis) st.metric("إجمالي التكاليف", f"{total_cost:,.2f} ريال") # عرض توزيع التكاليف حسب الفئة 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'] category_df = pd.DataFrame({ 'الفئة': list(category_totals.keys()), 'المبلغ': list(category_totals.values()) }) fig = px.pie( category_df, values='المبلغ', names='الفئة', title='توزيع التكاليف حسب الفئة' ) st.plotly_chart(fig, use_container_width=True) # عرض توزيع التكاليف المباشرة st.markdown("#### توزيع التكاليف المباشرة") # تجميع البيانات حسب الفئة الفرعية للتكاليف المباشرة direct_subcategory_totals = {} for item in st.session_state.cost_analysis: if item['category'] == 'تكاليف مباشرة': subcategory = item['subcategory'] if subcategory in direct_subcategory_totals: direct_subcategory_totals[subcategory] += item['amount'] else: direct_subcategory_totals[subcategory] = item['amount'] direct_subcategory_df = pd.DataFrame({ 'الفئة الفرعية': list(direct_subcategory_totals.keys()), 'المبلغ': list(direct_subcategory_totals.values()) }) fig = px.bar( direct_subcategory_df, x='الفئة الفرعية', y='المبلغ', title='توزيع التكاليف المباشرة', color='الفئة الفرعية', text_auto='.2s' ) st.plotly_chart(fig, use_container_width=True) def _render_pricing_scenarios_report(self): """عرض نموذج تقرير سيناريوهات التسعير""" st.markdown("### تقرير سيناريوهات التسعير") st.markdown("**تاريخ التقرير:** " + time.strftime("%Y-%m-%d")) st.markdown("**اسم المشروع:** مشروع إنشاء مبنى إداري") st.markdown("**رقم المناقصة:** T-2024-001") st.markdown("#### سيناريوهات التسعير") # عرض سيناريوهات التسعير scenarios_df = pd.DataFrame(st.session_state.price_scenarios) st.dataframe(scenarios_df, use_container_width=True, hide_index=True) # عرض السيناريو النشط active_scenario = next((item for item in st.session_state.price_scenarios if item['is_active']), None) if active_scenario: st.markdown(f"**السيناريو النشط:** {active_scenario['name']}") st.markdown(f"**السعر الإجمالي:** {active_scenario['total_price']:,.2f} ريال") st.markdown(f"**هامش الربح:** {active_scenario['profit_margin']:.1f}%") # عرض مقارنة السيناريوهات st.markdown("#### مقارنة السيناريوهات") # إنشاء DataFrame للرسم البياني scenarios_comparison_df = pd.DataFrame({ 'السيناريو': [item['name'] for item in st.session_state.price_scenarios], 'التكلفة الإجمالية': [item['total_cost'] for item in st.session_state.price_scenarios], 'هامش الربح (%)': [item['profit_margin'] for item in st.session_state.price_scenarios], 'السعر الإجمالي': [item['total_price'] for item in st.session_state.price_scenarios], 'الحالة': ['نشط' if item['is_active'] else 'غير نشط' for item in st.session_state.price_scenarios] }) # إنشاء رسم بياني شريطي مزدوج fig = go.Figure() # إضافة شريط للتكلفة الإجمالية fig.add_trace(go.Bar( x=scenarios_comparison_df['السيناريو'], y=scenarios_comparison_df['التكلفة الإجمالية'], name='التكلفة الإجمالية', marker_color='indianred' )) # إضافة شريط للسعر الإجمالي fig.add_trace(go.Bar( x=scenarios_comparison_df['السيناريو'], y=scenarios_comparison_df['السعر الإجمالي'], name='السعر الإجمالي', marker_color='lightsalmon' )) # إضافة خط لهامش الربح fig.add_trace(go.Scatter( x=scenarios_comparison_df['السيناريو'], y=scenarios_comparison_df['هامش الربح (%)'] * 10000, # تكبير القيم لتظهر على الرسم البياني name='هامش الربح (%)', yaxis='y2', line=dict(color='royalblue', width=4) )) # تعديل تخطيط الرسم البياني fig.update_layout( title='مقارنة سيناريوهات التسعير', xaxis_title='السيناريو', yaxis_title='المبلغ (ريال)', yaxis2=dict( title='هامش الربح (%)', titlefont=dict(color='royalblue'), tickfont=dict(color='royalblue'), overlaying='y', side='right', range=[0, 20] ), barmode='group', legend=dict( x=0, y=1.2, orientation='h' ) ) # تعديل النص على الأشرطة fig.update_traces( texttemplate='%{y:,.0f}', textposition='outside' ) st.plotly_chart(fig, use_container_width=True) def _render_competitive_analysis_report(self): """عرض نموذج تقرير المقارنة التنافسية""" st.markdown("### تقرير المقارنة التنافسية") st.markdown("**تاريخ التقرير:** " + time.strftime("%Y-%m-%d")) st.markdown("**اسم المشروع:** مشروع إنشاء مبنى إداري") st.markdown("**رقم المناقصة:** T-2024-001") # بيانات افتراضية للمنافسين competitors_data = [ { 'name': 'شركتنا', 'price': 670000, 'quality': 4.5, 'delivery_time': 180, 'experience': 8, 'local_content': 85 }, { 'name': 'المنافس أ', 'price': 700000, 'quality': 4.2, 'delivery_time': 200, 'experience': 10, 'local_content': 75 }, { 'name': 'المنافس ب', 'price': 650000, 'quality': 3.8, 'delivery_time': 160, 'experience': 5, 'local_content': 90 }, { 'name': 'المنافس ج', 'price': 680000, 'quality': 4.0, 'delivery_time': 190, 'experience': 12, 'local_content': 80 } ] # عرض بيانات المنافسين st.markdown("#### بيانات المنافسين") competitors_df = pd.DataFrame(competitors_data) st.dataframe(competitors_df, use_container_width=True, hide_index=True) # مقارنة الأسعار st.markdown("#### مقارنة الأسعار") fig = px.bar( competitors_df, x='name', y='price', title='مقارنة الأسعار بين المنافسين', color='price', text_auto='.2s' ) fig.update_layout( xaxis_title='المنافس', yaxis_title='السعر (ريال)' ) st.plotly_chart(fig, use_container_width=True) # مقارنة متعددة الأبعاد st.markdown("#### مقارنة متعددة الأبعاد") # تحويل البيانات إلى تنسيق مناسب للرسم البياني الراداري categories = ['price', 'quality', 'delivery_time', 'experience', 'local_content'] # تطبيع البيانات (لجعل القيم بين 0 و 1) normalized_data = {} for category in categories: if category == 'price' or category == 'delivery_time': # للسعر ووقت التسليم، القيمة الأقل أفضل min_val = min(item[category] for item in competitors_data) max_val = max(item[category] for item in competitors_data) normalized_data[category] = [(max_val - item[category]) / (max_val - min_val) if max_val != min_val else 0.5 for item in competitors_data] else: # للجودة والخبرة والمحتوى المحلي، القيمة الأعلى أفضل min_val = min(item[category] for item in competitors_data) max_val = max(item[category] for item in competitors_data) normalized_data[category] = [(item[category] - min_val) / (max_val - min_val) if max_val != min_val else 0.5 for item in competitors_data] # إنشاء الرسم البياني الراداري fig = go.Figure() for i, competitor in enumerate(competitors_data): fig.add_trace(go.Scatterpolar( r=[normalized_data[category][i] for category in categories], theta=['السعر', 'الجودة', 'وقت التسليم', 'الخبرة', 'المحتوى المحلي'], fill='toself', name=competitor['name'] )) fig.update_layout( polar=dict( radialaxis=dict( visible=True, range=[0, 1] ) ), title='مقارنة متعددة الأبعاد بين المنافسين', showlegend=True ) st.plotly_chart(fig, use_container_width=True) def _render_pricing_summary_report(self): """عرض نموذج تقرير ملخص التسعير""" st.markdown("### تقرير ملخص التسعير") st.markdown("**تاريخ التقرير:** " + time.strftime("%Y-%m-%d")) st.markdown("**اسم المشروع:** مشروع إنشاء مبنى إداري") st.markdown("**رقم المناقصة:** T-2024-001") # عرض ملخص التسعير 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 # إنشاء جدول ملخص 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, use_container_width=True, hide_index=True) # عرض توزيع التكاليف st.markdown("#### توزيع التكاليف") # إنشاء DataFrame للرسم البياني cost_distribution_df = pd.DataFrame({ 'البند': ['التكاليف المباشرة', 'التكاليف غير المباشرة', 'هامش الربح'], 'المبلغ': [total_direct_cost, total_indirect_cost, total_profit] }) fig = px.pie( cost_distribution_df, values='المبلغ', names='البند', title='توزيع التكاليف والأرباح', color_discrete_sequence=px.colors.qualitative.Set3 ) st.plotly_chart(fig, use_container_width=True) # عرض ملخص السيناريو النشط st.markdown("#### السيناريو النشط") active_scenario = next((item for item in st.session_state.price_scenarios if item['is_active']), None) if active_scenario: st.markdown(f"**اسم السيناريو:** {active_scenario['name']}") st.markdown(f"**الوصف:** {active_scenario['description']}") st.markdown(f"**إجمالي التكلفة:** {active_scenario['total_cost']:,.2f} ريال") st.markdown(f"**هامش الربح:** {active_scenario['profit_margin']:.1f}%") st.markdown(f"**السعر الإجمالي:** {active_scenario['total_price']:,.2f} ريال") # عرض توصيات التسعير st.markdown("#### توصيات التسعير") st.markdown("- مراجعة هيكل التكاليف بشكل دوري للحفاظ على القدرة التنافسية.") st.markdown("- التأكيد على جودة الخدمات في العروض التسويقية.") st.markdown("- التأكيد على نسبة المحتوى المحلي العالية في العروض.") st.markdown("- مراقبة أسعار المنافسين وتعديل الاستراتيجية التسعيرية عند الحاجة.") st.markdown("- تحليل نقاط القوة والضعف بشكل مستمر لتحسين العروض المستقبلية.")