|
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 |
|
|
|
|
|
from pricing_system.modules.analysis import smart_price_analysis as analysis_utils |
|
from pricing_system.modules.catalogs import materials_catalog, equipment_catalog |
|
from pricing_system.modules.indirect_support import overheads |
|
from pricing_system.modules.pricing_strategies import balanced_pricing, profit_oriented |
|
|
|
class PricingApp: |
|
"""وحدة التسعير""" |
|
|
|
def __init__(self): |
|
"""تهيئة وحدة التسعير""" |
|
|
|
if 'bill_of_quantities' not in st.session_state: |
|
st.session_state.bill_of_quantities = [] |
|
|
|
if 'cost_analysis' not in st.session_state: |
|
st.session_state.cost_analysis = [] |
|
|
|
if 'price_scenarios' not in st.session_state: |
|
st.session_state.price_scenarios = [] |
|
|
|
if 'local_content_data' not in st.session_state: |
|
st.session_state.local_content_data = {'target_percent': 30, 'actual_percent': 0, 'items': []} |
|
|
|
def run(self): |
|
self.render() |
|
|
|
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_local_content_tab() |
|
with tabs[6]: |
|
self._render_unbalanced_pricing_tab() |
|
with tabs[7]: |
|
self._render_reports_tab() |
|
|
|
def _render_dashboard_tab(self): |
|
st.markdown("### لوحة تحكم التسعير") |
|
st.info("سيتم عرض بيانات ملخص التسعير والتحليلات هنا.") |
|
|
|
def _render_bill_of_quantities_tab(self): |
|
st.markdown("### جدول الكميات") |
|
|
|
boq_df = pd.DataFrame(st.session_state.bill_of_quantities) |
|
|
|
if not boq_df.empty: |
|
st.dataframe( |
|
boq_df[['code', 'description', 'unit', 'quantity', 'unit_price', 'total_price', 'category']], |
|
column_config={ |
|
'code': 'الكود', |
|
'description': 'الوصف', |
|
'unit': 'الوحدة', |
|
'quantity': 'الكمية', |
|
'unit_price': st.column_config.NumberColumn('سعر الوحدة', format='%d ريال'), |
|
'total_price': st.column_config.NumberColumn('السعر الإجمالي', format='%d ريال'), |
|
'category': 'الفئة' |
|
}, |
|
hide_index=True, |
|
use_container_width=True |
|
) |
|
else: |
|
st.warning("لا توجد بيانات في جدول الكميات.") |
|
|
|
st.markdown("### إضافة بند جديد") |
|
col1, col2 = st.columns(2) |
|
with col1: |
|
new_code = st.text_input("الكود", key="new_boq_code") |
|
new_description = st.text_input("الوصف", key="new_boq_description") |
|
new_unit = st.selectbox("الوحدة", ["م3", "م2", "طن", "عدد", "متر طولي"], key="new_boq_unit") |
|
with col2: |
|
new_quantity = st.number_input("الكمية", min_value=0.0, step=1.0, key="new_boq_quantity") |
|
new_unit_price = st.number_input("سعر الوحدة", min_value=0.0, step=10.0, key="new_boq_unit_price") |
|
new_category = st.selectbox("الفئة", [ |
|
"أعمال ترابية", |
|
"أعمال خرسانية", |
|
"أعمال حديد", |
|
"أعمال بناء", |
|
"أعمال تشطيبات", |
|
"أعمال نجارة", |
|
"أعمال ألمنيوم", |
|
"أعمال كهربائية", |
|
"أعمال ميكانيكية", |
|
"أعمال صحية" |
|
], key="new_boq_category") |
|
|
|
if st.button("إضافة البند", key="add_boq_item"): |
|
if new_code and new_description and new_quantity > 0 and new_unit_price > 0: |
|
new_total_price = new_quantity * new_unit_price |
|
new_id = max([item['id'] for item in st.session_state.bill_of_quantities], default=0) + 1 |
|
st.session_state.bill_of_quantities.append({ |
|
'id': new_id, |
|
'code': new_code, |
|
'description': new_description, |
|
'unit': new_unit, |
|
'quantity': new_quantity, |
|
'unit_price': new_unit_price, |
|
'total_price': new_total_price, |
|
'category': new_category |
|
}) |
|
st.success(f"تمت إضافة البند بنجاح: {new_description}") |
|
st.rerun() |
|
else: |
|
st.error("يرجى إدخال جميع البيانات المطلوبة بشكل صحيح") |
|
|
|
def _render_cost_analysis_tab(self): |
|
st.markdown("### تحليل التكاليف") |
|
analysis_utils.render_cost_breakdown() |
|
|
|
def _render_pricing_scenarios_tab(self): |
|
st.markdown("### سيناريوهات التسعير") |
|
balanced_pricing.render_balanced_strategy() |
|
|
|
def _render_competitive_analysis_tab(self): |
|
st.markdown("### المقارنة التنافسية") |
|
st.info("سيتم عرض مقارنة الأسعار والمنافسين هنا.") |
|
|
|
def _render_local_content_tab(self): |
|
st.markdown("### المحتوى المحلي") |
|
overheads.render_local_content_ui() |
|
|
|
def _render_unbalanced_pricing_tab(self): |
|
st.markdown("### التسعير غير المتزن") |
|
profit_oriented.render_profit_driven_strategy() |
|
|
|
def _render_reports_tab(self): |
|
st.markdown("### التقارير") |
|
st.info("يمكنك هنا تنزيل التقارير الخاصة بالتسعير وجدول الكميات.") |
|
|