|
""" |
|
تطبيق وحدة التسعير المتكاملة |
|
""" |
|
|
|
import streamlit as st |
|
import pandas as pd |
|
import numpy as np |
|
import matplotlib.pyplot as plt |
|
import plotly.express as px |
|
import plotly.graph_objects as go |
|
from datetime import datetime |
|
import random |
|
import os |
|
import time |
|
import io |
|
|
|
from modules.pricing.services.standard_pricing import StandardPricing |
|
from modules.pricing.services.unbalanced_pricing import UnbalancedPricing |
|
from modules.pricing.services.local_content_calculator import LocalContentCalculator |
|
from modules.pricing.services.price_prediction import PricePrediction |
|
from utils.excel_handler import export_to_excel |
|
from utils.helpers import format_number, format_currency |
|
|
|
|
|
class PricingApp: |
|
"""وحدة التسعير المتكاملة""" |
|
|
|
def __init__(self): |
|
"""تهيئة وحدة التسعير المتكاملة""" |
|
self.pricing_methods = [ |
|
"التسعير القياسي", |
|
"التسعير غير المتزن", |
|
"التسعير التنافسي", |
|
"التسعير الموجه بالربحية" |
|
] |
|
|
|
|
|
self.standard_pricing = StandardPricing() |
|
self.unbalanced_pricing = UnbalancedPricing() |
|
self.local_content = LocalContentCalculator() |
|
self.price_prediction = PricePrediction() |
|
|
|
def render(self): |
|
"""عرض واجهة وحدة التسعير""" |
|
|
|
st.markdown("<h1 class='module-title'>وحدة التسعير المتكاملة</h1>", unsafe_allow_html=True) |
|
|
|
tabs = st.tabs([ |
|
"إنشاء تسعير جديد", |
|
"تحليل سعر البند", |
|
"نموذج التسعير الشامل", |
|
"التسعير غير المتزن", |
|
"المحتوى المحلي" |
|
]) |
|
|
|
with tabs[0]: |
|
self._render_new_pricing_tab() |
|
|
|
with tabs[1]: |
|
self._render_item_analysis_tab() |
|
|
|
with tabs[2]: |
|
self._render_comprehensive_pricing_tab() |
|
|
|
with tabs[3]: |
|
self._render_unbalanced_pricing_tab() |
|
|
|
with tabs[4]: |
|
self._render_local_content_tab() |
|
|
|
def _render_item_analysis_tab(self): |
|
"""عرض تبويب تحليل سعر البند""" |
|
|
|
st.markdown("### تحليل سعر البند") |
|
|
|
|
|
if 'current_pricing' not in st.session_state or st.session_state.current_pricing is None: |
|
st.warning("ليس هناك تسعير حالي. يرجى إنشاء تسعير جديد أولاً.") |
|
return |
|
|
|
|
|
if 'current_pricing' in st.session_state and st.session_state.current_pricing is not None: |
|
items = st.session_state.current_pricing['items'] |
|
item_options = items['رقم البند'].tolist() |
|
selected_item = st.selectbox("اختر البند للتحليل", item_options) |
|
|
|
if selected_item: |
|
item_data = items[items['رقم البند'] == selected_item].iloc[0] |
|
|
|
st.markdown(f"### تحليل البند: {selected_item}") |
|
st.markdown(f"**وصف البند**: {item_data['وصف البند']}") |
|
st.markdown(f"**الوحدة**: {item_data['الوحدة']}") |
|
st.markdown(f"**الكمية**: {item_data['الكمية']}") |
|
st.markdown(f"**سعر الوحدة**: {item_data['سعر الوحدة']:,.2f} ريال") |
|
|
|
|
|
st.markdown("### تحليل مكونات السعر") |
|
|
|
|
|
cost_components = { |
|
'المواد': 0.6, |
|
'العمالة': 0.25, |
|
'المعدات': 0.1, |
|
'نفقات عامة': 0.05 |
|
} |
|
|
|
|
|
unit_price = item_data['سعر الوحدة'] |
|
component_values = {k: v * unit_price for k, v in cost_components.items()} |
|
|
|
|
|
components_df = pd.DataFrame({ |
|
'العنصر': component_values.keys(), |
|
'نسبة من التكلفة': [f"{v*100:.1f}%" for v in cost_components.values()], |
|
'القيمة (ريال)': [f"{v:,.2f}" for v in component_values.values()] |
|
}) |
|
|
|
st.table(components_df) |
|
|
|
|
|
fig = px.pie( |
|
names=list(component_values.keys()), |
|
values=list(component_values.values()), |
|
title='توزيع مكونات التكلفة' |
|
) |
|
|
|
st.plotly_chart(fig) |
|
|
|
|
|
st.markdown("### تحليل تاريخي للأسعار") |
|
|
|
|
|
historical_data = { |
|
'التاريخ': ['2020-01', '2020-07', '2021-01', '2021-07', '2022-01', '2022-07', '2023-01', '2023-07'], |
|
'السعر': [ |
|
unit_price * 0.7, |
|
unit_price * 0.75, |
|
unit_price * 0.8, |
|
unit_price * 0.85, |
|
unit_price * 0.9, |
|
unit_price * 0.95, |
|
unit_price, |
|
unit_price * 1.05 |
|
] |
|
} |
|
|
|
hist_df = pd.DataFrame(historical_data) |
|
|
|
|
|
fig = px.line( |
|
hist_df, |
|
x='التاريخ', |
|
y='السعر', |
|
title='تطور سعر الوحدة عبر الزمن', |
|
markers=True |
|
) |
|
|
|
st.plotly_chart(fig) |
|
|
|
|
|
st.markdown("### المقارنة مع الأسعار المرجعية") |
|
|
|
|
|
reference_data = { |
|
'المصدر': ['قاعدة البيانات الداخلية', 'دليل الأسعار الاسترشادي', 'متوسط أسعار السوق', 'أسعار المشاريع المماثلة'], |
|
'السعر المرجعي': [ |
|
unit_price * 0.95, |
|
unit_price * 1.05, |
|
unit_price * 1.1, |
|
unit_price * 0.9 |
|
] |
|
} |
|
|
|
ref_df = pd.DataFrame(reference_data) |
|
ref_df['الفرق عن السعر الحالي'] = ref_df['السعر المرجعي'] - unit_price |
|
ref_df['نسبة الفرق'] = (ref_df['الفرق عن السعر الحالي'] / unit_price * 100).round(2).astype(str) + '%' |
|
|
|
st.table(ref_df) |
|
|
|
def _render_new_pricing_tab(self): |
|
"""عرض تبويب إنشاء تسعير جديد""" |
|
|
|
st.markdown("### إنشاء تسعير جديد") |
|
|
|
col1, col2 = st.columns(2) |
|
|
|
with col1: |
|
tender_name = st.text_input("اسم المناقصة") |
|
client = st.text_input("الجهة المالكة") |
|
pricing_method = st.selectbox("طريقة التسعير", self.pricing_methods) |
|
|
|
with col2: |
|
tender_number = st.text_input("رقم المناقصة") |
|
location = st.text_input("الموقع") |
|
submission_date = st.date_input("تاريخ التقديم") |
|
|
|
|
|
st.markdown("### بيانات البنود") |
|
|
|
data_source = st.radio( |
|
"مصدر بيانات البنود", |
|
["إدخال يدوي", "استيراد من Excel", "استيراد من وحدة تحليل المستندات"] |
|
) |
|
|
|
if data_source == "إدخال يدوي": |
|
|
|
st.markdown(""" |
|
<style> |
|
input, .stTextArea textarea { |
|
direction: rtl; |
|
text-align: right; |
|
font-family: 'Arial', 'Tahoma', sans-serif !important; |
|
} |
|
.stTextInput > div > div > input { |
|
text-align: right; |
|
direction: rtl; |
|
} |
|
</style> |
|
""", unsafe_allow_html=True) |
|
|
|
|
|
unit_options = ["م3", "م2", "طن", "متر طولي", "قطعة", "كجم", "لتر"] |
|
|
|
|
|
if 'manual_items' not in st.session_state: |
|
|
|
manual_items = pd.DataFrame(columns=[ |
|
'رقم البند', 'وصف البند', 'الوحدة', 'الكمية', 'سعر الوحدة', 'الإجمالي' |
|
]) |
|
|
|
|
|
default_items = pd.DataFrame({ |
|
'رقم البند': ["A1", "A2", "A3", "A4", "A5"], |
|
'وصف البند': [ |
|
"توريد وتركيب أعمال الخرسانة المسلحة للأساسات", |
|
"توريد وتركيب حديد التسليح للأساسات", |
|
"أعمال العزل المائي للأساسات", |
|
"أعمال الردم والدك للأساسات", |
|
"توريد وتركيب أعمال الخرسانة المسلحة للأعمدة" |
|
], |
|
'الوحدة': ["م3", "طن", "م2", "م3", "م3"], |
|
'الكمية': [250.0, 25.0, 500.0, 300.0, 120.0], |
|
'سعر الوحدة': [0.0, 0.0, 0.0, 0.0, 0.0], |
|
'الإجمالي': [0.0, 0.0, 0.0, 0.0, 0.0] |
|
}) |
|
|
|
manual_items = pd.concat([manual_items, default_items]) |
|
st.session_state.manual_items = manual_items |
|
|
|
|
|
st.markdown("### إدخال تفاصيل البنود") |
|
|
|
|
|
use_simple_input = st.checkbox("استخدام طريقة الإدخال البسيطة", value=True) |
|
|
|
if use_simple_input: |
|
|
|
st.markdown("### جدول البنود الحالية") |
|
st.dataframe(st.session_state.manual_items, use_container_width=True, hide_index=True) |
|
|
|
|
|
st.markdown("### إضافة بند جديد") |
|
col1, col2 = st.columns(2) |
|
|
|
with col1: |
|
new_id = st.text_input("رقم البند", value=f"A{len(st.session_state.manual_items)+1}") |
|
new_desc = st.text_area("وصف البند", value="") |
|
|
|
with col2: |
|
new_unit = st.selectbox("الوحدة", options=unit_options) |
|
new_qty = st.number_input("الكمية", value=0.0, min_value=0.0, format="%.2f") |
|
new_price = st.number_input("سعر الوحدة", value=0.0, min_value=0.0, format="%.2f") |
|
|
|
new_total = new_qty * new_price |
|
st.info(f"إجمالي البند الجديد: {new_total:,.2f} ريال") |
|
|
|
if st.button("إضافة البند"): |
|
|
|
if new_id and new_desc and new_qty > 0: |
|
|
|
new_row = pd.DataFrame({ |
|
'رقم البند': [new_id], |
|
'وصف البند': [new_desc], |
|
'الوحدة': [new_unit], |
|
'الكمية': [float(new_qty)], |
|
'سعر الوحدة': [float(new_price)], |
|
'الإجمالي': [float(new_total)] |
|
}) |
|
|
|
|
|
st.session_state.manual_items = pd.concat([st.session_state.manual_items, new_row], ignore_index=True) |
|
st.success("تم إضافة البند بنجاح!") |
|
st.rerun() |
|
else: |
|
st.error("يرجى ملء جميع الحقول المطلوبة: رقم البند، الوصف، والكمية يجب أن تكون أكبر من صفر.") |
|
|
|
|
|
st.markdown("### تعديل البنود الحالية") |
|
|
|
|
|
item_to_edit = st.selectbox( |
|
"اختر البند للتعديل", |
|
options=st.session_state.manual_items['رقم البند'].tolist(), |
|
format_func=lambda x: f"{x}: {st.session_state.manual_items[st.session_state.manual_items['رقم البند'] == x]['وصف البند'].values[0][:30]}..." |
|
) |
|
|
|
if item_to_edit: |
|
|
|
idx = st.session_state.manual_items[st.session_state.manual_items['رقم البند'] == item_to_edit].index[0] |
|
row = st.session_state.manual_items.loc[idx] |
|
|
|
|
|
col1, col2 = st.columns(2) |
|
|
|
with col1: |
|
edited_id = st.text_input("رقم البند (تعديل)", value=row['رقم البند'], key="edit_id") |
|
edited_desc = st.text_area("وصف البند (تعديل)", value=row['وصف البند'], key="edit_desc") |
|
|
|
with col2: |
|
edited_unit = st.selectbox( |
|
"الوحدة (تعديل)", |
|
options=unit_options, |
|
index=unit_options.index(row['الوحدة']) if row['الوحدة'] in unit_options else 0, |
|
key="edit_unit" |
|
) |
|
edited_qty = st.number_input("الكمية (تعديل)", value=float(row['الكمية']), min_value=0.0, format="%.2f", key="edit_qty") |
|
edited_price = st.number_input("سعر الوحدة (تعديل)", value=float(row['سعر الوحدة']), min_value=0.0, format="%.2f", key="edit_price") |
|
|
|
edited_total = edited_qty * edited_price |
|
st.info(f"إجمالي البند بعد التعديل: {edited_total:,.2f} ريال") |
|
|
|
col1, col2 = st.columns(2) |
|
with col1: |
|
if st.button("حفظ التعديلات"): |
|
|
|
st.session_state.manual_items.at[idx, 'رقم البند'] = edited_id |
|
st.session_state.manual_items.at[idx, 'وصف البند'] = edited_desc |
|
st.session_state.manual_items.at[idx, 'الوحدة'] = edited_unit |
|
st.session_state.manual_items.at[idx, 'الكمية'] = edited_qty |
|
st.session_state.manual_items.at[idx, 'سعر الوحدة'] = edited_price |
|
st.session_state.manual_items.at[idx, 'الإجمالي'] = edited_total |
|
|
|
st.success("تم تحديث البند بنجاح!") |
|
st.rerun() |
|
|
|
with col2: |
|
if st.button("حذف هذا البند"): |
|
st.session_state.manual_items = st.session_state.manual_items.drop(idx).reset_index(drop=True) |
|
st.warning("تم حذف البند!") |
|
st.rerun() |
|
|
|
|
|
total = st.session_state.manual_items['الإجمالي'].sum() |
|
st.metric("المجموع الكلي", f"{total:,.2f} ريال") |
|
|
|
|
|
edited_items = st.session_state.manual_items.copy() |
|
|
|
else: |
|
|
|
st.warning("لتجنب مشاكل عدم التوافق في أنواع البيانات، يُفضل استخدام طريقة الإدخال البسيطة.") |
|
|
|
|
|
try: |
|
|
|
for col in st.session_state.manual_items.columns: |
|
if col in ['رقم البند', 'وصف البند', 'الوحدة']: |
|
st.session_state.manual_items[col] = st.session_state.manual_items[col].astype(str) |
|
|
|
|
|
st.dataframe(st.session_state.manual_items, use_container_width=True, hide_index=True) |
|
|
|
|
|
st.markdown("### تعديل أسعار الوحدات") |
|
|
|
for idx, row in st.session_state.manual_items.iterrows(): |
|
col1, col2 = st.columns([3, 1]) |
|
|
|
with col1: |
|
st.text(f"{row['رقم البند']}: {row['وصف البند'][:50]}") |
|
|
|
with col2: |
|
price = st.number_input( |
|
f"سعر الوحدة ({row['الوحدة']})", |
|
value=float(row['سعر الوحدة']), |
|
min_value=0.0, |
|
key=f"price_{idx}" |
|
) |
|
|
|
|
|
st.session_state.manual_items.at[idx, 'سعر الوحدة'] = price |
|
st.session_state.manual_items.at[idx, 'الإجمالي'] = price * row['الكمية'] |
|
|
|
|
|
total = st.session_state.manual_items['الإجمالي'].sum() |
|
st.metric("المجموع الكلي", f"{total:,.2f} ريال") |
|
|
|
|
|
edited_items = st.session_state.manual_items.copy() |
|
|
|
except Exception as e: |
|
st.error(f"حدث خطأ: {str(e)}") |
|
st.info("يرجى استخدام طريقة الإدخال البسيطة لتجنب هذه المشكلة.") |
|
|
|
elif data_source == "استيراد من Excel": |
|
uploaded_file = st.file_uploader("رفع ملف Excel", type=["xlsx", "xls"]) |
|
|
|
if uploaded_file is not None: |
|
st.success("تم رفع الملف بنجاح") |
|
|
|
st.markdown("### معاينة البيانات المستوردة") |
|
|
|
|
|
import_items = pd.DataFrame({ |
|
'رقم البند': ["A1", "A2", "A3", "A4", "A5", "A6", "A7"], |
|
'وصف البند': [ |
|
"توريد وتركيب أعمال الخرسانة المسلحة للأساسات", |
|
"توريد وتركيب حديد التسليح للأساسات", |
|
"أعمال العزل المائي للأساسات", |
|
"أعمال الردم والدك للأساسات", |
|
"توريد وتركيب أعمال الخرسانة المسلحة للأعمدة", |
|
"توريد وتركيب حديد التسليح للأعمدة", |
|
"أعمال البلوك للجدران" |
|
], |
|
'الوحدة': ["م3", "طن", "م2", "م3", "م3", "طن", "م2"], |
|
'الكمية': [250.0, 25.0, 500.0, 300.0, 120.0, 10.0, 400.0], |
|
'سعر الوحدة': [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], |
|
'الإجمالي': [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0] |
|
}) |
|
|
|
st.dataframe(import_items) |
|
|
|
if st.button("استيراد البيانات"): |
|
st.session_state.manual_items = import_items.copy() |
|
st.session_state.manual_items_modified = True |
|
st.success("تم استيراد البيانات بنجاح!") |
|
st.rerun() |
|
|
|
else: |
|
available_documents = [ |
|
"كراسة شروط مشروع توسعة مستشفى الملك فهد", |
|
"جدول كميات صيانة محطات المياه", |
|
"مخططات إنشاء مدرسة ثانوية" |
|
] |
|
|
|
selected_doc = st.selectbox("اختر المستند", available_documents) |
|
|
|
if st.button("استيراد البيانات من تحليل المستند"): |
|
|
|
with st.spinner("جاري استيراد البيانات..."): |
|
time.sleep(2) |
|
|
|
|
|
doc_items = pd.DataFrame({ |
|
'رقم البند': ["A1", "A2", "A3", "A4", "A5", "A6", "A7"], |
|
'وصف البند': [ |
|
"توريد وتركيب أعمال الخرسانة المسلحة للأساسات", |
|
"توريد وتركيب حديد التسليح للأساسات", |
|
"أعمال العزل المائي للأساسات", |
|
"أعمال الردم والدك للأساسات", |
|
"توريد وتركيب أعمال الخرسانة المسلحة للأعمدة", |
|
"توريد وتركيب حديد التسليح للأعمدة", |
|
"أعمال البلوك للجدران" |
|
], |
|
'الوحدة': ["م3", "طن", "م2", "م3", "م3", "طن", "م2"], |
|
'الكمية': [250.0, 25.0, 500.0, 300.0, 120.0, 10.0, 400.0], |
|
'سعر الوحدة': [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], |
|
'الإجمالي': [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0] |
|
}) |
|
|
|
st.session_state.manual_items = doc_items.copy() |
|
st.success("تم استيراد البيانات من تحليل المستند بنجاح!") |
|
st.dataframe(doc_items) |
|
|
|
|
|
if st.button("بدء التسعير"): |
|
|
|
if 'manual_items' in st.session_state and not st.session_state.manual_items.empty: |
|
|
|
st.session_state.manual_items['الإجمالي'] = st.session_state.manual_items['الكمية'] * st.session_state.manual_items['سعر الوحدة'] |
|
|
|
|
|
st.session_state.current_pricing = { |
|
'name': tender_name, |
|
'number': tender_number, |
|
'client': client, |
|
'location': location, |
|
'method': pricing_method, |
|
'submission_date': submission_date, |
|
'items': st.session_state.manual_items.copy(), |
|
'status': 'جديد', |
|
'created_at': datetime.now() |
|
} |
|
|
|
|
|
st.success("تم إنشاء التسعير بنجاح! يمكنك الانتقال إلى نموذج التسعير الشامل.") |
|
else: |
|
st.error("يرجى إدخال بيانات البنود أولاً.") |
|
|
|
def _render_comprehensive_pricing_tab(self): |
|
"""عرض تبويب نموذج التسعير الشامل""" |
|
|
|
st.markdown("### نموذج التسعير الشامل") |
|
|
|
|
|
if 'current_pricing' not in st.session_state or st.session_state.current_pricing is None: |
|
st.warning("ليس هناك تسعير حالي. يرجى إنشاء تسعير جديد أولاً.") |
|
return |
|
|
|
|
|
pricing = st.session_state.current_pricing |
|
|
|
col1, col2, col3 = st.columns(3) |
|
|
|
with col1: |
|
st.metric("اسم المناقصة", pricing['name']) |
|
st.metric("الجهة المالكة", pricing['client']) |
|
|
|
with col2: |
|
st.metric("رقم المناقصة", pricing['number']) |
|
st.metric("تاريخ التقديم", pricing['submission_date'].strftime("%Y-%m-%d")) |
|
|
|
with col3: |
|
st.metric("طريقة التسعير", pricing['method']) |
|
st.metric("الموقع", pricing['location']) |
|
|
|
|
|
st.markdown("### بنود التسعير") |
|
|
|
items = pricing['items'].copy() |
|
|
|
|
|
if 'سعر الوحدة' in items.columns and (items['سعر الوحدة'] == 0).all(): |
|
items['سعر الوحدة'] = [ |
|
round(random.uniform(1000, 3000), 2), |
|
round(random.uniform(5000, 7000), 2), |
|
round(random.uniform(100, 200), 2), |
|
round(random.uniform(50, 100), 2), |
|
round(random.uniform(1200, 3500), 2), |
|
] |
|
|
|
if len(items) > 5: |
|
for i in range(5, len(items)): |
|
items.at[i, 'سعر الوحدة'] = round(random.uniform(500, 5000), 2) |
|
|
|
|
|
items['الإجمالي'] = items['الكمية'] * items['سعر الوحدة'] |
|
|
|
|
|
st.dataframe(items, use_container_width=True, hide_index=True) |
|
|
|
|
|
|
|
with st.expander("🔍 توليد توصية ذكية باستخدام AI"): |
|
if st.button("🔍 توليد توصية ذكية باستخدام AI", use_container_width=True): |
|
import openai |
|
import os |
|
|
|
client = openai.OpenAI(api_key=os.environ.get("ai")) |
|
|
|
items_df = items.copy() |
|
prompt = f"""قم بتحليل الجدول التالي للبنود في مشروع إنشاء، وقدم توصية ذكية لتحسين التسعير وضمان التوازن المالي. الجدول يحتوي على البنود، الكميات، الأسعار، والإجماليات:\n\n{items_df.to_string(index=False)}\n\nالتوصية:\n""" |
|
|
|
try: |
|
with st.spinner("جاري توليد التوصية..."): |
|
response = client.chat.completions.create( |
|
model="gpt-4", |
|
messages=[ |
|
{"role": "system", "content": "أنت خبير في تسعير مشاريع البناء والبنية التحتية."}, |
|
{"role": "user", "content": prompt} |
|
], |
|
temperature=0.4, |
|
max_tokens=500 |
|
) |
|
|
|
recommendation = response.choices[0].message.content |
|
st.success("تم توليد التوصية بنجاح!") |
|
st.markdown("#### التوصية الذكية:") |
|
st.info(recommendation) |
|
|
|
except Exception as e: |
|
st.error(f"حدث خطأ أثناء الاتصال بنموذج OpenAI: {e}") |
|
st.info("يجب التأكد من تثبيت أحدث إصدار من مكتبة OpenAI: `pip install openai --upgrade`") |
|
|
|
|
|
st.markdown("### تعديل أسعار الوحدات") |
|
|
|
|
|
col1, col2 = st.columns(2) |
|
half = len(items) // 2 + len(items) % 2 |
|
|
|
with col1: |
|
for idx in range(half): |
|
if idx < len(items): |
|
row = items.iloc[idx] |
|
price = st.number_input( |
|
f"{row['رقم البند']}: {row['وصف البند'][:30]}... ({row['الوحدة']})", |
|
value=float(row['سعر الوحدة']), |
|
min_value=0.0, |
|
key=f"price1_{idx}" |
|
) |
|
items.at[idx, 'سعر الوحدة'] = price |
|
items.at[idx, 'الإجمالي'] = price * items.at[idx, 'الكمية'] |
|
|
|
with col2: |
|
for idx in range(half, len(items)): |
|
row = items.iloc[idx] |
|
price = st.number_input( |
|
f"{row['رقم البند']}: {row['وصف البند'][:30]}... ({row['الوحدة']})", |
|
value=float(row['سعر الوحدة']), |
|
min_value=0.0, |
|
key=f"price2_{idx}" |
|
) |
|
items.at[idx, 'سعر الوحدة'] = price |
|
items.at[idx, 'الإجمالي'] = price * items.at[idx, 'الكمية'] |
|
|
|
|
|
total_price = items['الإجمالي'].sum() |
|
|
|
st.markdown("### إجماليات التسعير") |
|
|
|
col1, col2, col3 = st.columns(3) |
|
|
|
with col1: |
|
st.metric("إجمالي التكاليف المباشرة", f"{total_price:,.2f} ريال") |
|
|
|
with col2: |
|
overhead_percentage = st.slider("نسبة المصاريف العامة والأرباح (%)", 5, 30, 15) |
|
overhead_value = total_price * overhead_percentage / 100 |
|
st.metric("المصاريف العامة والأرباح", f"{overhead_value:,.2f} ريال") |
|
|
|
with col3: |
|
grand_total = total_price + overhead_value |
|
st.metric("الإجمالي النهائي", f"{grand_total:,.2f} ريال") |
|
|
|
|
|
st.markdown("### تحليل التكاليف") |
|
|
|
|
|
pie_data = items.copy() |
|
pie_data['نسبة من إجمالي التكاليف'] = pie_data['الإجمالي'] / total_price * 100 |
|
|
|
fig = px.pie( |
|
pie_data, |
|
values='نسبة من إجمالي التكاليف', |
|
names='وصف البند', |
|
title='توزيع التكاليف حسب البنود', |
|
hole=0.4 |
|
) |
|
|
|
st.plotly_chart(fig, use_container_width=True) |
|
|
|
|
|
col1, col2, col3 = st.columns(3) |
|
|
|
with col1: |
|
if st.button("حفظ التسعير"): |
|
|
|
st.session_state.current_pricing['items'] = items.copy() |
|
st.success("تم حفظ التسعير بنجاح!") |
|
|
|
with col2: |
|
if st.button("تصدير إلى Excel"): |
|
st.success("تم تصدير التسعير إلى Excel بنجاح!") |
|
|
|
with col3: |
|
if st.button("تحليل المخاطر المالية"): |
|
st.success("تم إرسال الطلب إلى وحدة تحليل المخاطر!") |
|
|
|
def _render_unbalanced_pricing_tab(self): |
|
"""عرض تبويب التسعير غير المتزن""" |
|
|
|
st.markdown("### التسعير غير المتزن") |
|
|
|
|
|
if 'current_pricing' not in st.session_state or st.session_state.current_pricing is None: |
|
st.warning("ليس هناك تسعير حالي. يرجى إنشاء تسعير جديد أولاً.") |
|
return |
|
|
|
|
|
with st.expander("ما هو التسعير غير المتزن؟", expanded=False): |
|
st.markdown(""" |
|
**التسعير غير المتزن** هو استراتيجية تسعير تقوم على توزيع التكاليف بين بنود المناقصة بشكل غير متساوٍ، مع الحفاظ على إجمالي قيمة العطاء. |
|
|
|
### استراتيجيات التسعير غير المتزن: |
|
|
|
1. **التحميل الأمامي (Front Loading)**: زيادة أسعار البنود المبكرة في المشروع للحصول على تدفق نقدي أفضل في بداية المشروع. |
|
2. **التحميل الخلفي (Back Loading)**: زيادة أسعار البنود المتأخرة في المشروع. |
|
3. **تحميل البنود المؤكدة**: زيادة أسعار البنود التي من المؤكد تنفيذها بالكميات المحددة. |
|
4. **تخفيض أسعار البنود المحتملة**: تخفيض أسعار البنود التي قد تزيد كمياتها أثناء التنفيذ. |
|
|
|
### مزايا التسعير غير المتزن: |
|
|
|
- تحسين التدفق النقدي للمشروع. |
|
- تعظيم الربحية في حالة التغييرات والأوامر التغييرية. |
|
- زيادة فرص الفوز بالمناقصة. |
|
|
|
### مخاطر التسعير غير المتزن: |
|
|
|
- قد يتم رفض العطاء إذا كان عدم التوازن واضحاً. |
|
- قد تتأثر السمعة سلباً إذا تم استخدامه بشكل مفرط. |
|
- قد يؤدي إلى خسائر إذا لم يتم تنفيذ البنود ذات الأسعار العالية. |
|
""") |
|
|
|
|
|
items = st.session_state.current_pricing['items'].copy() |
|
|
|
|
|
if 'إستراتيجية التسعير' not in items.columns: |
|
items['إستراتيجية التسعير'] = 'متوازن' |
|
|
|
st.markdown("### إستراتيجية التسعير غير المتزن") |
|
|
|
|
|
strategy = st.selectbox( |
|
"اختر إستراتيجية التسعير", |
|
[ |
|
"تحميل أمامي (Front Loading)", |
|
"تحميل البنود المؤكدة", |
|
"تخفيض البنود المحتمل زيادتها", |
|
"إستراتيجية مخصصة" |
|
] |
|
) |
|
|
|
|
|
if strategy == "تحميل أمامي (Front Loading)": |
|
|
|
items_count = len(items) |
|
early_items = items.iloc[:items_count//3].index |
|
middle_items = items.iloc[items_count//3:2*items_count//3].index |
|
late_items = items.iloc[2*items_count//3:].index |
|
|
|
|
|
for idx in early_items: |
|
items.at[idx, 'سعر الوحدة'] = items.at[idx, 'سعر الوحدة'] * 1.3 |
|
items.at[idx, 'إستراتيجية التسعير'] = 'زيادة' |
|
|
|
for idx in middle_items: |
|
items.at[idx, 'إستراتيجية التسعير'] = 'متوازن' |
|
|
|
for idx in late_items: |
|
items.at[idx, 'سعر الوحدة'] = items.at[idx, 'سعر الوحدة'] * 0.7 |
|
items.at[idx, 'إستراتيجية التسعير'] = 'نقص' |
|
|
|
elif strategy == "تحميل البنود المؤكدة": |
|
|
|
confirmed_items = [0, 2, 4] |
|
variable_items = [idx for idx in range(len(items)) if idx not in confirmed_items] |
|
|
|
|
|
for idx in confirmed_items: |
|
if idx < len(items): |
|
items.at[idx, 'سعر الوحدة'] = items.at[idx, 'سعر الوحدة'] * 1.25 |
|
items.at[idx, 'إستراتيجية التسعير'] = 'زيادة' |
|
|
|
for idx in variable_items: |
|
if idx < len(items): |
|
items.at[idx, 'سعر الوحدة'] = items.at[idx, 'سعر الوحدة'] * 0.85 |
|
items.at[idx, 'إستراتيجية التسعير'] = 'نقص' |
|
|
|
elif strategy == "تخفيض البنود المحتمل زيادتها": |
|
|
|
variable_items = [1, 3] |
|
other_items = [idx for idx in range(len(items)) if idx not in variable_items] |
|
|
|
|
|
for idx in variable_items: |
|
if idx < len(items): |
|
items.at[idx, 'سعر الوحدة'] = items.at[idx, 'سعر الوحدة'] * 0.7 |
|
items.at[idx, 'إستراتيجية التسعير'] = 'نقص' |
|
|
|
for idx in other_items: |
|
if idx < len(items): |
|
items.at[idx, 'سعر الوحدة'] = items.at[idx, 'سعر الوحدة'] * 1.15 |
|
items.at[idx, 'إستراتيجية التسعير'] = 'زيادة' |
|
|
|
else: |
|
st.markdown("### تعديل أسعار البنود يدوياً") |
|
st.markdown("قم بتعديل أسعار البنود وإستراتيجية التسعير يدوياً.") |
|
|
|
|
|
items['الإجمالي'] = items['الكمية'] * items['سعر الوحدة'] |
|
|
|
|
|
def highlight_strategy(val): |
|
if val == 'زيادة': |
|
return 'background-color: #a8e6cf' |
|
elif val == 'نقص': |
|
return 'background-color: #ff9aa2' |
|
return '' |
|
|
|
|
|
st.markdown("### بنود التسعير غير المتزن") |
|
styled_items = items.style.applymap(highlight_strategy, subset=['إستراتيجية التسعير']) |
|
st.dataframe(styled_items, use_container_width=True) |
|
|
|
|
|
st.markdown("### مقارنة التسعير المتوازن وغير المتوازن") |
|
|
|
original_items = st.session_state.current_pricing['items'].copy() |
|
original_total = original_items['الإجمالي'].sum() |
|
unbalanced_total = items['الإجمالي'].sum() |
|
|
|
col1, col2, col3 = st.columns(3) |
|
|
|
with col1: |
|
st.metric("إجمالي التسعير المتوازن", f"{original_total:,.2f} ريال") |
|
|
|
with col2: |
|
st.metric("إجمالي التسعير غير المتوازن", f"{unbalanced_total:,.2f} ريال") |
|
|
|
with col3: |
|
diff = unbalanced_total - original_total |
|
st.metric("الفرق", f"{diff:,.2f} ريال", delta=f"{diff/original_total*100:.1f}%") |
|
|
|
|
|
if abs(diff) > 1: |
|
if st.button("معايرة الأسعار للحفاظ على إجمالي التسعير"): |
|
|
|
adjustment_factor = original_total / unbalanced_total |
|
items['سعر الوحدة'] = items['سعر الوحدة'] * adjustment_factor |
|
items['الإجمالي'] = items['الكمية'] * items['سعر الوحدة'] |
|
|
|
st.success(f"تم تعديل الأسعار للحفاظ على إجمالي التسعير الأصلي ({original_total:,.2f} ريال)") |
|
st.dataframe(items, use_container_width=True) |
|
|
|
|
|
st.markdown("### تحليل بصري للتسعير غير المتوازن") |
|
|
|
|
|
chart_data = pd.DataFrame({ |
|
'وصف البند': original_items['وصف البند'], |
|
'التسعير المتوازن': original_items['الإجمالي'], |
|
'التسعير غير المتوازن': items['الإجمالي'] |
|
}) |
|
|
|
|
|
fig = go.Figure() |
|
|
|
fig.add_trace(go.Bar( |
|
x=chart_data['وصف البند'], |
|
y=chart_data['التسعير المتوازن'], |
|
name='التسعير المتوازن', |
|
marker_color='rgb(55, 83, 109)' |
|
)) |
|
|
|
fig.add_trace(go.Bar( |
|
x=chart_data['وصف البند'], |
|
y=chart_data['التسعير غير المتوازن'], |
|
name='التسعير غير المتوازن', |
|
marker_color='rgb(26, 118, 255)' |
|
)) |
|
|
|
fig.update_layout( |
|
title='مقارنة بين التسعير المتوازن وغير المتوازن', |
|
xaxis_tickfont_size=14, |
|
yaxis=dict( |
|
title='الإجمالي (ريال)', |
|
titlefont_size=16, |
|
tickfont_size=14, |
|
), |
|
legend=dict( |
|
x=0, |
|
y=1.0, |
|
bgcolor='rgba(255, 255, 255, 0)', |
|
bordercolor='rgba(255, 255, 255, 0)' |
|
), |
|
barmode='group', |
|
bargap=0.15, |
|
bargroupgap=0.1 |
|
) |
|
|
|
st.plotly_chart(fig, use_container_width=True) |
|
|
|
|
|
if st.button("حفظ التسعير غير المتوازن"): |
|
st.session_state.current_pricing['items'] = items.copy() |
|
st.session_state.current_pricing['method'] = "التسعير غير المتزن" |
|
st.success("تم حفظ التسعير غير المتوازن بنجاح!") |
|
|
|
def _render_local_content_tab(self): |
|
"""عرض تبويب المحتوى المحلي""" |
|
|
|
st.markdown("### تحليل المحتوى المحلي") |
|
|
|
|
|
if 'current_pricing' not in st.session_state or st.session_state.current_pricing is None: |
|
st.warning("ليس هناك تسعير حالي. يرجى إنشاء تسعير جديد أولاً.") |
|
return |
|
|
|
|
|
with st.expander("ما هو المحتوى المحلي؟", expanded=False): |
|
st.markdown(""" |
|
**المحتوى المحلي** هو نسبة المنتجات والخدمات والقوى العاملة المحلية المستخدمة في المشروع. يهدف إلى زيادة مساهمة المنتجات والخدمات المحلية في المشاريع. |
|
|
|
### مكونات المحتوى المحلي: |
|
|
|
1. **المنتجات**: المنتجات والمواد المصنعة محلياً. |
|
2. **الخدمات**: الخدمات المقدمة من شركات محلية. |
|
3. **القوى العاملة**: العمالة والكوادر الفنية والإدارية المحلية. |
|
|
|
### أهمية المحتوى المحلي: |
|
|
|
- تعزيز الاقتصاد المحلي وخلق فرص عمل. |
|
- تحقيق أهداف رؤية 2030 في زيادة المحتوى المحلي. |
|
- التأهل للمشاريع الحكومية التي تتطلب نسبة محتوى محلي محددة. |
|
- الحصول على حوافز وأفضلية في المناقصات الحكومية. |
|
|
|
### متطلبات المحتوى المحلي: |
|
|
|
- نسبة المحتوى المحلي للقوى العاملة: 80% |
|
- نسبة المحتوى المحلي للمنتجات: 70% |
|
- نسبة المحتوى المحلي للخدمات: 60% |
|
""") |
|
|
|
|
|
st.markdown("### بيانات المحتوى المحلي") |
|
|
|
|
|
lc_tabs = st.tabs(["المنتجات", "الخدمات", "القوى العاملة", "التحليل"]) |
|
|
|
with lc_tabs[0]: |
|
st.markdown("#### بيانات المنتجات") |
|
|
|
|
|
if 'local_content_products' not in st.session_state: |
|
st.session_state.local_content_products = pd.DataFrame({ |
|
'المنتج': [ |
|
"خرسانة مسلحة", |
|
"حديد تسليح", |
|
"بلوك خرساني", |
|
"عزل مائي", |
|
"دهانات" |
|
], |
|
'الكمية': [250, 25, 400, 500, 600], |
|
'سعر_الوحدة': [1200, 6000, 200, 100, 50], |
|
'التكلفة_الإجمالية': [300000, 150000, 80000, 50000, 30000], |
|
'نسبة_المحتوى_المحلي': [0.95, 0.70, 0.98, 0.60, 0.80] |
|
}) |
|
|
|
|
|
st.session_state.local_content_products['التكلفة_الإجمالية'] = st.session_state.local_content_products['الكمية'] * st.session_state.local_content_products['سعر_الوحدة'] |
|
|
|
|
|
edited_products = st.data_editor( |
|
st.session_state.local_content_products, |
|
use_container_width=True, |
|
hide_index=True, |
|
num_rows="dynamic" |
|
) |
|
st.session_state.local_content_products = edited_products |
|
|
|
|
|
total_products_cost = edited_products['التكلفة_الإجمالية'].sum() |
|
avg_local_content = (edited_products['التكلفة_الإجمالية'] * edited_products['نسبة_المحتوى_المحلي']).sum() / total_products_cost if total_products_cost > 0 else 0 |
|
|
|
st.markdown(f""" |
|
**إجمالي تكلفة المنتجات**: {total_products_cost:,.2f} ريال |
|
|
|
**متوسط نسبة المحتوى المحلي للمنتجات**: {avg_local_content*100:.2f}% |
|
|
|
**المستهدف**: 70% |
|
|
|
**الحالة**: {"✅ ملتزم" if avg_local_content >= 0.7 else "❌ غير ملتزم"} |
|
""") |
|
|
|
with lc_tabs[1]: |
|
st.markdown("#### بيانات الخدمات") |
|
|
|
|
|
if 'local_content_services' not in st.session_state: |
|
st.session_state.local_content_services = pd.DataFrame({ |
|
'الخدمة': [ |
|
"تصميم معماري", |
|
"إشراف هندسي", |
|
"خدمات نقل", |
|
"خدمات أمن وسلامة", |
|
"صيانة ونظافة" |
|
], |
|
'التكلفة': [100000, 120000, 50000, 30000, 20000], |
|
'نسبة_المحتوى_المحلي': [0.90, 0.85, 0.90, 0.95, 0.95] |
|
}) |
|
|
|
|
|
edited_services = st.data_editor( |
|
st.session_state.local_content_services, |
|
use_container_width=True, |
|
hide_index=True, |
|
num_rows="dynamic" |
|
) |
|
st.session_state.local_content_services = edited_services |
|
|
|
|
|
total_services_cost = edited_services['التكلفة'].sum() |
|
avg_local_content = (edited_services['التكلفة'] * edited_services['نسبة_المحتوى_المحلي']).sum() / total_services_cost if total_services_cost > 0 else 0 |
|
|
|
st.markdown(f""" |
|
**إجمالي تكلفة الخدمات**: {total_services_cost:,.2f} ريال |
|
|
|
**متوسط نسبة المحتوى المحلي للخدمات**: {avg_local_content*100:.2f}% |
|
|
|
**المستهدف**: 60% |
|
|
|
**الحالة**: {"✅ ملتزم" if avg_local_content >= 0.6 else "❌ غير ملتزم"} |
|
""") |
|
|
|
with lc_tabs[2]: |
|
st.markdown("#### بيانات القوى العاملة") |
|
|
|
|
|
if 'local_content_labor' not in st.session_state: |
|
st.session_state.local_content_labor = pd.DataFrame({ |
|
'فئة_العمالة': [ |
|
"مهندسون", |
|
"فنيون", |
|
"عمال بناء", |
|
"إداريون", |
|
"مشرفون" |
|
], |
|
'العدد': [5, 10, 30, 3, 4], |
|
'الراتب_الشهري': [15000, 8000, 3000, 10000, 12000], |
|
'المدة_بالأشهر': [12, 12, 12, 12, 12], |
|
'نسبة_المحتوى_المحلي': [0.75, 0.65, 0.60, 0.90, 0.80] |
|
}) |
|
|
|
|
|
st.session_state.local_content_labor['التكلفة_الإجمالية'] = st.session_state.local_content_labor['العدد'] * st.session_state.local_content_labor['الراتب_الشهري'] * st.session_state.local_content_labor['المدة_بالأشهر'] |
|
|
|
|
|
edited_labor = st.data_editor( |
|
st.session_state.local_content_labor, |
|
use_container_width=True, |
|
hide_index=True, |
|
num_rows="dynamic" |
|
) |
|
|
|
|
|
edited_labor['التكلفة_الإجمالية'] = edited_labor['العدد'] * edited_labor['الراتب_الشهري'] * edited_labor['المدة_بالأشهر'] |
|
st.session_state.local_content_labor = edited_labor |
|
|
|
|
|
total_labor_cost = edited_labor['التكلفة_الإجمالية'].sum() |
|
avg_local_content = (edited_labor['التكلفة_الإجمالية'] * edited_labor['نسبة_المحتوى_المحلي']).sum() / total_labor_cost if total_labor_cost > 0 else 0 |
|
|
|
st.markdown(f""" |
|
**إجمالي تكلفة القوى العاملة**: {total_labor_cost:,.2f} ريال |
|
|
|
**متوسط نسبة المحتوى المحلي للقوى العاملة**: {avg_local_content*100:.2f}% |
|
|
|
**المستهدف**: 80% |
|
|
|
**الحالة**: {"✅ ملتزم" if avg_local_content >= 0.8 else "❌ غير ملتزم"} |
|
""") |
|
|
|
with lc_tabs[3]: |
|
st.markdown("#### تحليل المحتوى المحلي") |
|
|
|
|
|
try: |
|
|
|
products_cost = st.session_state.local_content_products['التكلفة_الإجمالية'].sum() |
|
products_local_content = (st.session_state.local_content_products['التكلفة_الإجمالية'] * st.session_state.local_content_products['نسبة_المحتوى_المحلي']).sum() / products_cost if products_cost > 0 else 0 |
|
|
|
services_cost = st.session_state.local_content_services['التكلفة'].sum() |
|
services_local_content = (st.session_state.local_content_services['التكلفة'] * st.session_state.local_content_services['نسبة_المحتوى_المحلي']).sum() / services_cost if services_cost > 0 else 0 |
|
|
|
labor_cost = st.session_state.local_content_labor['التكلفة_الإجمالية'].sum() |
|
labor_local_content = (st.session_state.local_content_labor['التكلفة_الإجمالية'] * st.session_state.local_content_labor['نسبة_المحتوى_المحلي']).sum() / labor_cost if labor_cost > 0 else 0 |
|
|
|
|
|
total_cost = products_cost + services_cost + labor_cost |
|
products_weight = products_cost / total_cost if total_cost > 0 else 0 |
|
services_weight = services_cost / total_cost if total_cost > 0 else 0 |
|
labor_weight = labor_cost / total_cost if total_cost > 0 else 0 |
|
|
|
|
|
total_local_content = (products_local_content * products_weight) + (services_local_content * services_weight) + (labor_local_content * labor_weight) |
|
|
|
|
|
st.markdown("### ملخص المحتوى المحلي") |
|
|
|
col1, col2, col3 = st.columns(3) |
|
|
|
with col1: |
|
st.metric("إجمالي التكاليف", f"{total_cost:,.2f} ريال") |
|
|
|
with col2: |
|
st.metric("نسبة المحتوى المحلي الإجمالية", f"{total_local_content*100:.2f}%") |
|
|
|
with col3: |
|
target_local_content = 0.7 |
|
st.metric("الحالة", "ملتزم" if total_local_content >= target_local_content else "غير ملتزم", delta=f"{(total_local_content - target_local_content)*100:.2f}%") |
|
|
|
|
|
st.markdown("### تحليل بصري للمحتوى المحلي") |
|
|
|
|
|
categories = ['المنتجات', 'الخدمات', 'القوى العاملة', 'الإجمالي'] |
|
actual_values = [products_local_content * 100, services_local_content * 100, labor_local_content * 100, total_local_content * 100] |
|
target_values = [70, 60, 80, 70] |
|
|
|
|
|
chart_data = pd.DataFrame({ |
|
'الفئة': categories, |
|
'النسبة الفعلية': actual_values, |
|
'النسبة المستهدفة': target_values |
|
}) |
|
|
|
|
|
fig = go.Figure() |
|
|
|
fig.add_trace(go.Bar( |
|
x=chart_data['الفئة'], |
|
y=chart_data['النسبة الفعلية'], |
|
name='النسبة الفعلية', |
|
marker_color='rgb(26, 118, 255)' |
|
)) |
|
|
|
fig.add_trace(go.Bar( |
|
x=chart_data['الفئة'], |
|
y=chart_data['النسبة المستهدفة'], |
|
name='النسبة المستهدفة', |
|
marker_color='rgb(55, 83, 109)' |
|
)) |
|
|
|
fig.update_layout( |
|
title='مقارنة بين النسب الفعلية والمستهدفة للمحتوى المحلي', |
|
xaxis_tickfont_size=14, |
|
yaxis=dict( |
|
title='النسبة %', |
|
titlefont_size=16, |
|
tickfont_size=14, |
|
), |
|
legend=dict( |
|
x=0, |
|
y=1.0, |
|
bgcolor='rgba(255, 255, 255, 0)', |
|
bordercolor='rgba(255, 255, 255, 0)' |
|
), |
|
barmode='group', |
|
bargap=0.15, |
|
bargroupgap=0.1 |
|
) |
|
|
|
st.plotly_chart(fig, use_container_width=True) |
|
|
|
|
|
st.markdown("### توصيات لتحسين نسبة المحتوى المحلي") |
|
|
|
recommendations = [] |
|
|
|
if products_local_content < 0.7: |
|
recommendations.append("- زيادة نسبة المحتوى المحلي للمنتجات من خلال:") |
|
recommendations.append(" - البحث عن موردين محليين للمنتجات ذات النسبة المنخفضة") |
|
recommendations.append(" - استبدال المنتجات المستوردة ببدائل محلية") |
|
recommendations.append(" - التعاون مع المصانع المحلية لتوطين صناعة المنتجات") |
|
|
|
if services_local_content < 0.6: |
|
recommendations.append("- زيادة نسبة المحتوى المحلي للخدمات من خلال:") |
|
recommendations.append(" - التعاقد مع شركات خدمات محلية") |
|
recommendations.append(" - تحويل الخدمات المستعان بها من الخارج إلى شركات محلية") |
|
recommendations.append(" - تأهيل الشركات المحلية لتقديم الخدمات المطلوبة") |
|
|
|
if labor_local_content < 0.8: |
|
recommendations.append("- زيادة نسبة المحتوى المحلي للقوى العاملة من خلال:") |
|
recommendations.append(" - زيادة توظيف الكوادر المحلية") |
|
recommendations.append(" - تدريب وتأهيل العمالة المحلية") |
|
recommendations.append(" - استبدال العمالة الأجنبية بكوادر محلية تدريجياً") |
|
|
|
if total_local_content < 0.7: |
|
recommendations.append("- زيادة نسبة المحتوى المحلي الإجمالية من خلال:") |
|
recommendations.append(" - إعادة توزيع الميزانية لصالح المكونات ذات النسبة العالية من المحتوى المحلي") |
|
recommendations.append(" - وضع خطة مرحلية لزيادة المحتوى المحلي") |
|
recommendations.append(" - التعاون مع اللجنة المحلية لزيادة المحتوى المحلي") |
|
|
|
if recommendations: |
|
for rec in recommendations: |
|
st.markdown(rec) |
|
else: |
|
st.success("تهانينا! نسبة المحتوى المحلي متوافقة مع المتطلبات.") |
|
|
|
|
|
st.markdown("### تأثير المحتوى المحلي على التسعير") |
|
|
|
|
|
price_adjustment_factor = 1.0 |
|
|
|
if total_local_content >= 0.9: |
|
price_adjustment_factor = 0.92 |
|
price_discount = "8%" |
|
elif total_local_content >= 0.8: |
|
price_adjustment_factor = 0.94 |
|
price_discount = "6%" |
|
elif total_local_content >= 0.7: |
|
price_adjustment_factor = 0.96 |
|
price_discount = "4%" |
|
elif total_local_content >= 0.6: |
|
price_adjustment_factor = 0.98 |
|
price_discount = "2%" |
|
else: |
|
price_adjustment_factor = 1.0 |
|
price_discount = "0%" |
|
|
|
|
|
original_total = st.session_state.current_pricing['items']['الإجمالي'].sum() |
|
adjusted_total = original_total * price_adjustment_factor |
|
discount_amount = original_total - adjusted_total |
|
|
|
col1, col2, col3 = st.columns(3) |
|
|
|
with col1: |
|
st.metric("إجمالي التسعير الأصلي", f"{original_total:,.2f} ريال") |
|
|
|
with col2: |
|
st.metric("نسبة الخصم بسبب المحتوى المحلي", price_discount) |
|
|
|
with col3: |
|
st.metric("إجمالي التسعير بعد الخصم", f"{adjusted_total:,.2f} ريال", delta=f"-{discount_amount:,.2f} ريال") |
|
|
|
|
|
col1, col2 = st.columns(2) |
|
|
|
with col1: |
|
if st.button("حفظ تحليل المحتوى المحلي"): |
|
|
|
st.session_state.current_pricing['local_content'] = { |
|
'products': st.session_state.local_content_products.copy(), |
|
'services': st.session_state.local_content_services.copy(), |
|
'labor': st.session_state.local_content_labor.copy(), |
|
'total_local_content': total_local_content, |
|
'price_adjustment_factor': price_adjustment_factor |
|
} |
|
|
|
st.success("تم حفظ تحليل المحتوى المحلي بنجاح!") |
|
|
|
with col2: |
|
if st.button("تصدير تقرير المحتوى المحلي"): |
|
st.success("تم تصدير تقرير المحتوى المحلي بنجاح!") |
|
|
|
except Exception as e: |
|
st.error(f"حدث خطأ أثناء تحليل المحتوى المحلي: {str(e)}") |
|
st.warning("تأكد من إدخال بيانات المحتوى المحلي بشكل صحيح في التبويبات السابقة.") |