Wahbi-AI / modules /pricing /pricing_app.py
EGYADMIN's picture
Upload 34 files
ae93751 verified
raw
history blame
78.1 kB
"""
وحدة التسعير - التطبيق الرئيسي
"""
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("<h1 class='module-title'>وحدة التسعير</h1>", 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("- تحليل نقاط القوة والضعف بشكل مستمر لتحسين العروض المستقبلية.")