Spaces:
Sleeping
Sleeping
import streamlit as st | |
import pandas as pd | |
import numpy as np | |
from datetime import datetime | |
import time | |
class PriceAnalysisComponent: | |
"""مكون تحليل الأسعار للبنود""" | |
def __init__(self): | |
"""تهيئة مكون تحليل الأسعار""" | |
# تهيئة قائمة الوحدات المتاحة | |
self.unit_options = ["م3", "م2", "طن", "متر طولي", "قطعة", "كجم", "لتر"] | |
# تهيئة فئات التكاليف | |
self.cost_categories = [ | |
"مواد", | |
"عمالة", | |
"معدات", | |
"مقاولي الباطن", | |
"مصاريف عامة", | |
"أرباح" | |
] | |
# تهيئة قائمة البنود وتحليل أسعارها | |
if 'items_price_analysis' not in st.session_state: | |
st.session_state.items_price_analysis = {} | |
def render(self): | |
"""عرض واجهة تحليل الأسعار""" | |
st.markdown("<h2 class='module-title'>تحليل أسعار البنود</h2>", unsafe_allow_html=True) | |
# التحقق من وجود بنود في التسعير الحالي | |
if 'current_pricing' not in st.session_state or 'items' not in st.session_state.current_pricing: | |
st.warning("ليس هناك بنود للتحليل. يرجى إنشاء تسعير أولاً.") | |
return | |
# الحصول على البنود من التسعير الحالي | |
items = st.session_state.current_pricing['items'].copy() | |
# عرض قائمة البنود | |
st.markdown("### قائمة البنود") | |
st.dataframe(items[['رقم البند', 'وصف البند', 'الوحدة', 'الكمية', 'سعر الوحدة', 'الإجمالي']], | |
use_container_width=True, hide_index=True) | |
# اختيار البند لتحليل السعر | |
selected_item_id = st.selectbox( | |
"اختر البند لتحليل السعر", | |
options=items['رقم البند'].tolist(), | |
format_func=lambda x: f"{x}: {items[items['رقم البند'] == x]['وصف البند'].values[0][:50]}..." | |
) | |
if selected_item_id: | |
# الحصول على البند المحدد | |
selected_item = items[items['رقم البند'] == selected_item_id].iloc[0] | |
# عرض تفاصيل البند المختار | |
col1, col2, col3 = st.columns(3) | |
with col1: | |
st.metric("رقم البند", selected_item['رقم البند']) | |
with col2: | |
st.metric("الكمية", f"{selected_item['الكمية']} {selected_item['الوحدة']}") | |
with col3: | |
st.metric("سعر الوحدة", f"{selected_item['سعر الوحدة']:,.2f} ريال") | |
st.markdown(f"**وصف البند**: {selected_item['وصف البند']}") | |
# إنشاء أو تحديث تحليل السعر للبند المحدد | |
if selected_item_id not in st.session_state.items_price_analysis: | |
# إنشاء تحليل سعر افتراضي | |
self._create_default_price_analysis(selected_item_id, selected_item) | |
# عرض وتحرير تحليل السعر | |
self._render_price_analysis_editor(selected_item_id, selected_item) | |
def _create_default_price_analysis(self, item_id, item): | |
"""إنشاء تحليل سعر افتراضي للبند""" | |
# إنشاء قائمة مكونات تحليل السعر | |
components = pd.DataFrame(columns=[ | |
'نوع التكلفة', 'الوصف', 'الكمية', 'الوحدة', 'سعر الوحدة', 'الإجمالي' | |
]) | |
# إضافة مكونات افتراضية بناءً على نوع البند | |
is_concrete = 'خرسان' in item['وصف البند'] | |
is_steel = 'حديد' in item['وصف البند'] or 'تسليح' in item['وصف البند'] | |
is_bricks = 'بلوك' in item['وصف البند'] or 'طوب' in item['وصف البند'] | |
is_paint = 'دهان' in item['وصف البند'] or 'طلاء' in item['وصف البند'] | |
is_insulation = 'عزل' in item['وصف البند'] | |
# إضافة المكونات بناءً على نوع البند | |
if is_concrete: | |
# مكونات الخرسانة | |
default_components = pd.DataFrame({ | |
'نوع التكلفة': ['مواد', 'مواد', 'مواد', 'عمالة', 'معدات', 'مصاريف عامة', 'أرباح'], | |
'الوصف': ['أسمنت', 'رمل', 'حصى', 'عمال وفنيين', 'خلاطات ومعدات صب', 'مصاريف عامة', 'أرباح'], | |
'الكمية': [350, 0.4, 0.8, 8, 1, 1, 1], | |
'الوحدة': ['كجم', 'م3', 'م3', 'ساعة', 'يوم', 'وحدة', 'وحدة'], | |
'سعر الوحدة': [0.5, 100, 120, 50, 500, 100, 150], | |
'الإجمالي': [175, 40, 96, 400, 500, 100, 150] | |
}) | |
components = pd.concat([components, default_components], ignore_index=True) | |
elif is_steel: | |
# مكونات الحديد | |
default_components = pd.DataFrame({ | |
'نوع التكلفة': ['مواد', 'عمالة', 'معدات', 'مصاريف عامة', 'أرباح'], | |
'الوصف': ['حديد التسليح', 'عمال وفنيين', 'معدات ثني وتجهيز الحديد', 'مصاريف عامة', 'أرباح'], | |
'الكمية': [1000, 10, 1, 1, 1], | |
'الوحدة': ['كجم', 'ساعة', 'يوم', 'وحدة', 'وحدة'], | |
'سعر الوحدة': [4.5, 50, 300, 200, 300], | |
'الإجمالي': [4500, 500, 300, 200, 300] | |
}) | |
components = pd.concat([components, default_components], ignore_index=True) | |
elif is_bricks: | |
# مكونات البلوك | |
default_components = pd.DataFrame({ | |
'نوع التكلفة': ['مواد', 'مواد', 'عمالة', 'مصاريف عامة', 'أرباح'], | |
'الوصف': ['بلوك خرساني', 'مونة', 'عمالة بناء', 'مصاريف عامة', 'أرباح'], | |
'الكمية': [12.5, 0.02, 1, 1, 1], | |
'الوحدة': ['قطعة', 'م3', 'م2', 'وحدة', 'وحدة'], | |
'سعر الوحدة': [8, 500, 80, 15, 20], | |
'الإجمالي': [100, 10, 80, 15, 20] | |
}) | |
components = pd.concat([components, default_components], ignore_index=True) | |
elif is_paint: | |
# مكونات الدهانات | |
default_components = pd.DataFrame({ | |
'نوع التكلفة': ['مواد', 'مواد', 'عمالة', 'مصاريف عامة', 'أرباح'], | |
'الوصف': ['دهان', 'مواد تجهيز', 'عمالة دهان', 'مصاريف عامة', 'أرباح'], | |
'الكمية': [0.4, 0.1, 1, 1, 1], | |
'الوحدة': ['لتر', 'وحدة', 'م2', 'وحدة', 'وحدة'], | |
'سعر الوحدة': [80, 20, 35, 5, 10], | |
'الإجمالي': [32, 2, 35, 5, 10] | |
}) | |
components = pd.concat([components, default_components], ignore_index=True) | |
elif is_insulation: | |
# مكونات العزل | |
default_components = pd.DataFrame({ | |
'نوع التكلفة': ['مواد', 'مواد', 'عمالة', 'مصاريف عامة', 'أرباح'], | |
'الوصف': ['مواد عازلة', 'مواد لاصقة', 'عمالة تركيب', 'مصاريف عامة', 'أرباح'], | |
'الكمية': [1.1, 0.2, 1, 1, 1], | |
'الوحدة': ['م2', 'كجم', 'م2', 'وحدة', 'وحدة'], | |
'سعر الوحدة': [60, 30, 25, 10, 15], | |
'الإجمالي': [66, 6, 25, 10, 15] | |
}) | |
components = pd.concat([components, default_components], ignore_index=True) | |
else: | |
# مكونات عامة افتراضية | |
default_components = pd.DataFrame({ | |
'نوع التكلفة': ['مواد', 'عمالة', 'معدات', 'مصاريف عامة', 'أرباح'], | |
'الوصف': ['مواد أساسية', 'عمالة', 'معدات ومعد مساعدة', 'مصاريف عامة', 'أرباح'], | |
'الكمية': [1, 1, 1, 1, 1], | |
'الوحدة': [item['الوحدة'], 'وحدة', 'وحدة', 'وحدة', 'وحدة'], | |
'سعر الوحدة': [ | |
item['سعر الوحدة'] * 0.6, | |
item['سعر الوحدة'] * 0.2, | |
item['سعر الوحدة'] * 0.1, | |
item['سعر الوحدة'] * 0.05, | |
item['سعر الوحدة'] * 0.05 | |
], | |
'الإجمالي': [ | |
item['سعر الوحدة'] * 0.6, | |
item['سعر الوحدة'] * 0.2, | |
item['سعر الوحدة'] * 0.1, | |
item['سعر الوحدة'] * 0.05, | |
item['سعر الوحدة'] * 0.05 | |
] | |
}) | |
components = pd.concat([components, default_components], ignore_index=True) | |
# حفظ تحليل السعر للبند | |
st.session_state.items_price_analysis[item_id] = components | |
def _render_price_analysis_editor(self, item_id, item): | |
"""عرض محرر تحليل السعر للبند""" | |
st.markdown("### تحليل السعر") | |
# الحصول على مكونات تحليل السعر | |
components = st.session_state.items_price_analysis[item_id] | |
# عرض تحليل السعر في محرر بيانات | |
st.markdown("#### مكونات السعر") | |
edited_components = st.data_editor( | |
components, | |
use_container_width=True, | |
hide_index=True, | |
num_rows="dynamic", | |
column_config={ | |
'نوع التكلفة': st.column_config.SelectboxColumn( | |
'نوع التكلفة', | |
help='فئة التكلفة', | |
options=self.cost_categories | |
), | |
'الوحدة': st.column_config.SelectboxColumn( | |
'الوحدة', | |
help='وحدة القياس', | |
options=self.unit_options + ["وحدة", "ساعة", "يوم"] | |
), | |
'الكمية': st.column_config.NumberColumn( | |
'الكمية', | |
help='الكمية', | |
min_value=0.0, | |
format="%.2f" | |
), | |
'سعر الوحدة': st.column_config.NumberColumn( | |
'سعر الوحدة', | |
help='سعر الوحدة', | |
min_value=0.0, | |
format="%.2f" | |
), | |
'الإجمالي': st.column_config.NumberColumn( | |
'الإجمالي', | |
help='الإجمالي', | |
min_value=0.0, | |
format="%.2f" | |
) | |
} | |
) | |
# إعادة حساب الإجمالي لكل مكون | |
edited_components['الإجمالي'] = edited_components['الكمية'] * edited_components['سعر الوحدة'] | |
# حفظ التعديلات | |
st.session_state.items_price_analysis[item_id] = edited_components | |
# حساب إجمالي تحليل السعر | |
total_analysis_price = edited_components['الإجمالي'].sum() | |
unit_price_from_analysis = total_analysis_price / item['الكمية'] if item['الكمية'] > 0 else 0 | |
# عرض ملخص تحليل السعر | |
st.markdown("#### ملخص تحليل السعر") | |
col1, col2, col3 = st.columns(3) | |
with col1: | |
st.metric("إجمالي تكلفة البند من التحليل", f"{total_analysis_price:,.2f} ريال") | |
with col2: | |
st.metric("سعر الوحدة من التحليل", f"{unit_price_from_analysis:,.2f} ريال") | |
with col3: | |
# المقارنة مع السعر الأصلي | |
diff = unit_price_from_analysis - item['سعر الوحدة'] | |
st.metric( | |
"الفرق عن السعر الأصلي", | |
f"{diff:,.2f} ريال", | |
delta=f"{(diff/item['سعر الوحدة']*100) if item['سعر الوحدة'] > 0 else 0:.1f}%" | |
) | |
# تحليل توزيع التكاليف حسب الفئة | |
cost_by_category = edited_components.groupby('نوع التكلفة')['الإجمالي'].sum().reset_index() | |
# عرض مخطط توزيع التكاليف | |
st.markdown("#### توزيع التكاليف حسب الفئة") | |
# عرض توزيع التكاليف في جدول | |
distribution_df = pd.DataFrame({ | |
'نوع التكلفة': cost_by_category['نوع التكلفة'], | |
'القيمة': cost_by_category['الإجمالي'], | |
'النسبة المئوية': (cost_by_category['الإجمالي'] / total_analysis_price * 100).round(2) | |
}) | |
st.dataframe( | |
distribution_df, | |
use_container_width=True, | |
hide_index=True, | |
column_config={ | |
'القيمة': st.column_config.NumberColumn( | |
'القيمة', | |
help='القيمة', | |
format="%.2f" | |
), | |
'النسبة المئوية': st.column_config.ProgressColumn( | |
'النسبة المئوية', | |
help='النسبة المئوية', | |
format="%.2f%%", | |
min_value=0, | |
max_value=100 | |
) | |
} | |
) | |
# أزرار الإجراءات | |
col1, col2, col3 = st.columns(3) | |
with col1: | |
if st.button("تحديث سعر البند", use_container_width=True): | |
# تحديث سعر البند بناءً على تحليل السعر | |
items = st.session_state.current_pricing['items'].copy() | |
item_index = items[items['رقم البند'] == item_id].index[0] | |
# تحديث سعر الوحدة والإجمالي | |
items.at[item_index, 'سعر الوحدة'] = unit_price_from_analysis | |
items.at[item_index, 'الإجمالي'] = unit_price_from_analysis * items.at[item_index, 'الكمية'] | |
# حفظ التعديلات في التسعير الحالي | |
st.session_state.current_pricing['items'] = items | |
st.success(f"تم تحديث سعر البند بناءً على تحليل السعر: {unit_price_from_analysis:,.2f} ريال") | |
time.sleep(0.5) | |
st.rerun() | |
with col2: | |
if st.button("تصدير تحليل السعر", use_container_width=True): | |
st.success("تم إرسال تحليل السعر للتصدير بنجاح!") | |
with col3: | |
if st.button("مسح تحليل السعر", use_container_width=True): | |
# حذف تحليل السعر للبند | |
if item_id in st.session_state.items_price_analysis: | |
del st.session_state.items_price_analysis[item_id] | |
st.warning("تم مسح تحليل السعر للبند") | |
time.sleep(0.5) | |
st.rerun() | |
def add_to_pricing_app(self, pricing_app): | |
"""إضافة مكون تحليل الأسعار إلى تطبيق التسعير""" | |
# إضافة تبويب جديد | |
if not hasattr(pricing_app, 'tabs'): | |
pricing_app.tabs = [] | |
if len(pricing_app.tabs) == 4: # إذا كان هناك 4 تبويبات فقط | |
pricing_app.tabs.append("تحليل أسعار البنود") | |
# إضافة دالة العرض | |
pricing_app._render_price_analysis_tab = self.render | |
def render_integrated_item_input(): | |
"""عرض واجهة إدخال البنود مع تحليل السعر المتكامل""" | |
# ضبط CSS لتحسين ظهور الواجهة العربية | |
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; | |
} | |
.pricing-analysis-container { | |
border: 1px solid #e0e0e0; | |
border-radius: 10px; | |
padding: 10px; | |
margin-top: 10px; | |
background-color: #f9f9f9; | |
} | |
</style> | |
""", unsafe_allow_html=True) | |
# تهيئة قائمة الوحدات المتاحة | |
unit_options = ["م3", "م2", "طن", "متر طولي", "قطعة", "كجم", "لتر"] | |
# تهيئة فئات التكاليف | |
cost_categories = [ | |
"مواد", | |
"عمالة", | |
"معدات", | |
"مقاولي الباطن", | |
"مصاريف عامة", | |
"أرباح" | |
] | |
# إنشاء جدول البنود اذا لم يكن موجوداً | |
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 | |
# إنشاء جدول تحليل الأسعار اذا لم يكن موجوداً | |
if 'items_price_analysis' not in st.session_state: | |
st.session_state.items_price_analysis = {} | |
# عرض واجهة إدخال البنود | |
st.markdown("### إدخال تفاصيل البنود مع تحليل الأسعار") | |
# عرض البنود الحالية كجدول للعرض | |
st.markdown("### جدول البنود الحالية") | |
st.dataframe(st.session_state.manual_items, use_container_width=True, hide_index=True) | |
# التبويبات لإضافة بند جديد أو تعديل بند | |
tabs = st.tabs(["إضافة بند جديد", "تعديل بند حالي"]) | |
with tabs[0]: # إضافة بند جديد | |
st.markdown("### إضافة بند جديد مع تحليل السعر") | |
col1, col2 = st.columns(2) | |
with col1: | |
new_id = st.text_input("رقم البند", value=f"A{len(st.session_state.manual_items)+1}", key="new_id") | |
new_desc = st.text_area("وصف البند", value="", key="new_desc") | |
with col2: | |
new_unit = st.selectbox("الوحدة", options=unit_options, key="new_unit") | |
new_qty = st.number_input("الكمية", value=0.0, min_value=0.0, format="%.2f", key="new_qty") | |
# إنشاء تحليل السعر للبند الجديد | |
st.markdown('<div class="pricing-analysis-container">', unsafe_allow_html=True) | |
st.markdown("#### تحليل سعر البند") | |
# التعرف التلقائي على نوع البند من الوصف | |
is_concrete = False | |
is_steel = False | |
is_bricks = False | |
is_paint = False | |
is_insulation = False | |
if new_desc: | |
is_concrete = 'خرسان' in new_desc | |
is_steel = 'حديد' in new_desc or 'تسليح' in new_desc | |
is_bricks = 'بلوك' in new_desc or 'طوب' in new_desc | |
is_paint = 'دهان' in new_desc or 'طلاء' in new_desc | |
is_insulation = 'عزل' in new_desc | |
# تلميح للمستخدم عن التعرف التلقائي | |
if any([is_concrete, is_steel, is_bricks, is_paint, is_insulation]): | |
detected_type = "" | |
if is_concrete: | |
detected_type = "أعمال خرسانة" | |
elif is_steel: | |
detected_type = "أعمال حديد" | |
elif is_bricks: | |
detected_type = "أعمال بلوك" | |
elif is_paint: | |
detected_type = "أعمال دهانات" | |
elif is_insulation: | |
detected_type = "أعمال عزل" | |
st.info(f"تم التعرف تلقائياً على نوع البند: {detected_type}") | |
# إنشاء مصفوفة فارغة لمكونات البند | |
if 'new_components' not in st.session_state: | |
# إنشاء DataFrame فارغ | |
new_components = pd.DataFrame(columns=[ | |
'نوع التكلفة', 'الوصف', 'الكمية', 'الوحدة', 'سعر الوحدة', 'الإجمالي' | |
]) | |
# إضافة مكونات افتراضية بناءً على نوع البند | |
if is_concrete: | |
# مكونات الخرسانة | |
default_components = pd.DataFrame({ | |
'نوع التكلفة': ['مواد', 'مواد', 'مواد', 'عمالة', 'معدات', 'مصاريف عامة', 'أرباح'], | |
'الوصف': ['أسمنت', 'رمل', 'حصى', 'عمال وفنيين', 'خلاطات ومعدات صب', 'مصاريف عامة', 'أرباح'], | |
'الكمية': [350, 0.4, 0.8, 8, 1, 1, 1], | |
'الوحدة': ['كجم', 'م3', 'م3', 'ساعة', 'يوم', 'وحدة', 'وحدة'], | |
'سعر الوحدة': [0.5, 100, 120, 50, 500, 100, 150], | |
'الإجمالي': [175, 40, 96, 400, 500, 100, 150] | |
}) | |
new_components = pd.concat([new_components, default_components], ignore_index=True) | |
elif is_steel: | |
# مكونات الحديد | |
default_components = pd.DataFrame({ | |
'نوع التكلفة': ['مواد', 'عمالة', 'معدات', 'مصاريف عامة', 'أرباح'], | |
'الوصف': ['حديد التسليح', 'عمال وفنيين', 'معدات ثني وتجهيز الحديد', 'مصاريف عامة', 'أرباح'], | |
'الكمية': [1000, 10, 1, 1, 1], | |
'الوحدة': ['كجم', 'ساعة', 'يوم', 'وحدة', 'وحدة'], | |
'سعر الوحدة': [4.5, 50, 300, 200, 300], | |
'الإجمالي': [4500, 500, 300, 200, 300] | |
}) | |
new_components = pd.concat([new_components, default_components], ignore_index=True) | |
elif is_bricks: | |
# مكونات البلوك | |
default_components = pd.DataFrame({ | |
'نوع التكلفة': ['مواد', 'مواد', 'عمالة', 'مصاريف عامة', 'أرباح'], | |
'الوصف': ['بلوك خرساني', 'مونة', 'عمالة بناء', 'مصاريف عامة', 'أرباح'], | |
'الكمية': [12.5, 0.02, 1, 1, 1], | |
'الوحدة': ['قطعة', 'م3', 'م2', 'وحدة', 'وحدة'], | |
'سعر الوحدة': [8, 500, 80, 15, 20], | |
'الإجمالي': [100, 10, 80, 15, 20] | |
}) | |
new_components = pd.concat([new_components, default_components], ignore_index=True) | |
elif is_paint: | |
# مكونات الدهانات | |
default_components = pd.DataFrame({ | |
'نوع التكلفة': ['مواد', 'مواد', 'عمالة', 'مصاريف عامة', 'أرباح'], | |
'الوصف': ['دهان', 'مواد تجهيز', 'عمالة دهان', 'مصاريف عامة', 'أرباح'], | |
'الكمية': [0.4, 0.1, 1, 1, 1], | |
'الوحدة': ['لتر', 'وحدة', 'م2', 'وحدة', 'وحدة'], | |
'سعر الوحدة': [80, 20, 35, 5, 10], | |
'الإجمالي': [32, 2, 35, 5, 10] | |
}) | |
new_components = pd.concat([new_components, default_components], ignore_index=True) | |
elif is_insulation: | |
# مكونات العزل | |
default_components = pd.DataFrame({ | |
'نوع التكلفة': ['مواد', 'مواد', 'عمالة', 'مصاريف عامة', 'أرباح'], | |
'الوصف': ['مواد عازلة', 'مواد لاصقة', 'عمالة تركيب', 'مصاريف عامة', 'أرباح'], | |
'الكمية': [1.1, 0.2, 1, 1, 1], | |
'الوحدة': ['م2', 'كجم', 'م2', 'وحدة', 'وحدة'], | |
'سعر الوحدة': [60, 30, 25, 10, 15], | |
'الإجمالي': [66, 6, 25, 10, 15] | |
}) | |
new_components = pd.concat([new_components, default_components], ignore_index=True) | |
else: | |
# مكونات عامة افتراضية | |
default_components = pd.DataFrame({ | |
'نوع التكلفة': ['مواد', 'عمالة', 'معدات', 'مصاريف عامة', 'أرباح'], | |
'الوصف': ['مواد أساسية', 'عمالة', 'معدات', 'مصاريف عامة', 'أرباح'], | |
'الكمية': [1, 1, 1, 1, 1], | |
'الوحدة': [new_unit if new_unit else 'وحدة', 'وحدة', 'وحدة', 'وحدة', 'وحدة'], | |
'سعر الوحدة': [100, 50, 30, 20, 20], | |
'الإجمالي': [100, 50, 30, 20, 20] | |
}) | |
new_components = pd.concat([new_components, default_components], ignore_index=True) | |
st.session_state.new_components = new_components | |
# عرض وتحرير مكونات تحليل السعر | |
edited_components = st.data_editor( | |
st.session_state.new_components, | |
use_container_width=True, | |
hide_index=True, | |
num_rows="dynamic", | |
column_config={ | |
'نوع التكلفة': st.column_config.SelectboxColumn( | |
'نوع التكلفة', | |
help='فئة التكلفة', | |
options=cost_categories | |
), | |
'الوحدة': st.column_config.SelectboxColumn( | |
'الوحدة', | |
help='وحدة القياس', | |
options=unit_options + ["وحدة", "ساعة", "يوم"] | |
), | |
'الكمية': st.column_config.NumberColumn( | |
'الكمية', | |
help='الكمية', | |
min_value=0.0, | |
format="%.2f" | |
), | |
'سعر الوحدة': st.column_config.NumberColumn( | |
'سعر الوحدة', | |
help='سعر الوحدة', | |
min_value=0.0, | |
format="%.2f" | |
), | |
'الإجمالي': st.column_config.NumberColumn( | |
'الإجمالي', | |
help='الإجمالي', | |
min_value=0.0, | |
format="%.2f" | |
) | |
} | |
) | |
# إعادة حساب الإجمالي لكل مكون | |
edited_components['الإجمالي'] = edited_components['الكمية'] * edited_components['سعر الوحدة'] | |
# حفظ التعديلات | |
st.session_state.new_components = edited_components | |
# حساب إجمالي تحليل السعر | |
total_analysis_price = edited_components['الإجمالي'].sum() | |
unit_price_from_analysis = total_analysis_price / new_qty if new_qty > 0 else 0 | |
# عرض ملخص تحليل السعر | |
st.markdown("#### ملخص تحليل السعر") | |
col1, col2 = st.columns(2) | |
with col1: | |
st.metric("إجمالي تكلفة البند من التحليل", f"{total_analysis_price:,.2f} ريال") | |
with col2: | |
st.metric("سعر الوحدة المحسوب", f"{unit_price_from_analysis:,.2f} ريال") | |
st.markdown('</div>', unsafe_allow_html=True) | |
# استخدام السعر المحسوب | |
use_calculated_price = st.checkbox("استخدام السعر المحسوب من التحليل", value=True) | |
# تحديد سعر الوحدة النهائي | |
if use_calculated_price and new_qty > 0: | |
new_unit_price = unit_price_from_analysis | |
else: | |
new_unit_price = st.number_input |