|
""" |
|
حاسبة تكاليف البناء المتكاملة |
|
تتضمن العناصر التالية: |
|
- المواد الخام |
|
- المعدات |
|
- العمالة |
|
- المصاريف الإدارية |
|
- هامش الربح |
|
""" |
|
|
|
import streamlit as st |
|
import pandas as pd |
|
import numpy as np |
|
import plotly.express as px |
|
import plotly.graph_objects as go |
|
|
|
|
|
def render_construction_calculator(): |
|
""" |
|
عرض حاسبة تكاليف البناء المتكاملة |
|
""" |
|
|
|
if 'materials_cost' not in st.session_state: |
|
st.session_state.materials_cost = 0.0 |
|
if 'equipment_cost' not in st.session_state: |
|
st.session_state.equipment_cost = 0.0 |
|
if 'labor_cost' not in st.session_state: |
|
st.session_state.labor_cost = 0.0 |
|
if 'admin_cost' not in st.session_state: |
|
st.session_state.admin_cost = 0.0 |
|
if 'profit_margin' not in st.session_state: |
|
st.session_state.profit_margin = 15.0 |
|
|
|
st.markdown("<h2 class='module-title'>حاسبة تكاليف البناء المتكاملة</h2>", unsafe_allow_html=True) |
|
|
|
|
|
st.markdown("<h3>معلومات المشروع</h3>", unsafe_allow_html=True) |
|
|
|
col1, col2 = st.columns(2) |
|
|
|
with col1: |
|
project_name = st.text_input("اسم المشروع", "مشروع سكني") |
|
project_location = st.text_input("موقع المشروع", "الرياض - حي النرجس") |
|
|
|
with col2: |
|
project_area = st.number_input("المساحة الإجمالية (م²)", min_value=1, value=500) |
|
project_type = st.selectbox( |
|
"نوع المشروع", |
|
options=[ |
|
"سكني", "تجاري", "صناعي", "إداري", "صحي", "تعليمي", |
|
"بنية تحتية", "طرق", "جسور", "أخرى" |
|
] |
|
) |
|
|
|
|
|
tabs = st.tabs([ |
|
"المواد الخام", "المعدات", "العمالة", "المصاريف الإدارية", "هامش الربح", "التقرير النهائي" |
|
]) |
|
|
|
|
|
if "materials_cost" not in st.session_state: |
|
st.session_state.materials_cost = 0.0 |
|
if "equipment_cost" not in st.session_state: |
|
st.session_state.equipment_cost = 0.0 |
|
if "labor_cost" not in st.session_state: |
|
st.session_state.labor_cost = 0.0 |
|
if "admin_cost" not in st.session_state: |
|
st.session_state.admin_cost = 0.0 |
|
if "profit_margin" not in st.session_state: |
|
st.session_state.profit_margin = 10.0 |
|
if "materials" not in st.session_state: |
|
st.session_state.materials = [] |
|
if "equipment" not in st.session_state: |
|
st.session_state.equipment = [] |
|
if "labor" not in st.session_state: |
|
st.session_state.labor = [] |
|
if "admin_expenses" not in st.session_state: |
|
st.session_state.admin_expenses = [] |
|
|
|
|
|
with tabs[0]: |
|
render_materials_tab() |
|
|
|
|
|
with tabs[1]: |
|
render_equipment_tab() |
|
|
|
|
|
with tabs[2]: |
|
render_labor_tab() |
|
|
|
|
|
with tabs[3]: |
|
render_admin_tab() |
|
|
|
|
|
with tabs[4]: |
|
render_profit_tab() |
|
|
|
|
|
with tabs[5]: |
|
render_final_report(project_name, project_location, project_area, project_type) |
|
|
|
|
|
def render_materials_tab(): |
|
""" |
|
عرض تبويب المواد الخام |
|
""" |
|
st.markdown("<h3>تكاليف المواد الخام</h3>", unsafe_allow_html=True) |
|
|
|
|
|
st.markdown("<h4>إضافة مادة جديدة</h4>", unsafe_allow_html=True) |
|
|
|
col1, col2, col3, col4 = st.columns(4) |
|
|
|
with col1: |
|
material_name = st.text_input("اسم المادة", key="new_material_name") |
|
with col2: |
|
material_quantity = st.number_input("الكمية", min_value=0.0, step=0.1, key="new_material_quantity") |
|
with col3: |
|
material_unit = st.selectbox( |
|
"الوحدة", |
|
options=["م²", "م³", "طن", "كجم", "لتر", "قطعة", "لفة", "كيس", "أخرى"], |
|
key="new_material_unit" |
|
) |
|
with col4: |
|
material_price = st.number_input("السعر للوحدة (ريال)", min_value=0.0, step=0.01, key="new_material_price") |
|
|
|
if st.button("إضافة مادة", key="add_material_btn"): |
|
total_price = material_quantity * material_price |
|
new_material = { |
|
"name": material_name, |
|
"quantity": material_quantity, |
|
"unit": material_unit, |
|
"price": material_price, |
|
"total": total_price |
|
} |
|
st.session_state.materials.append(new_material) |
|
st.success(f"تمت إضافة {material_name} بنجاح!") |
|
|
|
|
|
if st.session_state.materials: |
|
st.markdown("<h4>قائمة المواد المضافة</h4>", unsafe_allow_html=True) |
|
|
|
materials_df = pd.DataFrame(st.session_state.materials) |
|
materials_df.columns = ["اسم المادة", "الكمية", "الوحدة", "السعر للوحدة", "التكلفة الإجمالية"] |
|
st.dataframe(materials_df) |
|
|
|
total_materials_cost = sum(item["total"] for item in st.session_state.materials) |
|
st.session_state.materials_cost = total_materials_cost |
|
|
|
st.markdown(f"<h4>إجمالي تكلفة المواد: <span style='color:var(--primary-color)'>{total_materials_cost:,.2f} ريال</span></h4>", unsafe_allow_html=True) |
|
|
|
|
|
if len(st.session_state.materials) > 1: |
|
st.markdown("<h4>توزيع تكاليف المواد</h4>", unsafe_allow_html=True) |
|
|
|
fig = px.pie( |
|
materials_df, |
|
values="التكلفة الإجمالية", |
|
names="اسم المادة", |
|
title="توزيع تكاليف المواد", |
|
color_discrete_sequence=px.colors.sequential.Teal, |
|
hole=0.4 |
|
) |
|
fig.update_layout( |
|
font=dict(family="Almarai, Arial", size=14), |
|
margin=dict(t=50, b=50, l=20, r=20) |
|
) |
|
st.plotly_chart(fig, use_container_width=True) |
|
|
|
st.markdown("---") |
|
|
|
|
|
st.markdown("<h4>استيراد بيانات المواد من ملف</h4>", unsafe_allow_html=True) |
|
uploaded_file = st.file_uploader("اختر ملف Excel أو CSV", type=["xlsx", "csv"], key="materials_upload") |
|
|
|
if uploaded_file is not None: |
|
if uploaded_file.name.endswith('.csv'): |
|
df = pd.read_csv(uploaded_file) |
|
else: |
|
df = pd.read_excel(uploaded_file) |
|
|
|
st.success("تم استيراد البيانات بنجاح!") |
|
st.dataframe(df) |
|
|
|
if st.button("إضافة المواد من الملف"): |
|
try: |
|
|
|
column_mapping = { |
|
"المادة": "name", |
|
"اسم المادة": "name", |
|
"الكمية": "quantity", |
|
"الوحدة": "unit", |
|
"السعر": "price", |
|
"سعر الوحدة": "price" |
|
} |
|
|
|
mapped_df = df.rename(columns=column_mapping) |
|
|
|
|
|
for _, row in mapped_df.iterrows(): |
|
total_price = row["quantity"] * row["price"] |
|
new_material = { |
|
"name": row["name"], |
|
"quantity": row["quantity"], |
|
"unit": row["unit"], |
|
"price": row["price"], |
|
"total": total_price |
|
} |
|
st.session_state.materials.append(new_material) |
|
|
|
st.success("تمت إضافة جميع المواد من الملف بنجاح!") |
|
|
|
except Exception as e: |
|
st.error(f"حدث خطأ: {str(e)}") |
|
st.error("تأكد من أن الملف يحتوي على الأعمدة المطلوبة: اسم المادة، الكمية، الوحدة، السعر للوحدة") |
|
|
|
|
|
def render_equipment_tab(): |
|
""" |
|
عرض تبويب المعدات |
|
""" |
|
st.markdown("<h3>تكاليف المعدات</h3>", unsafe_allow_html=True) |
|
|
|
|
|
st.markdown("<h4>إضافة معدة جديدة</h4>", unsafe_allow_html=True) |
|
|
|
col1, col2, col3 = st.columns(3) |
|
|
|
with col1: |
|
equipment_name = st.text_input("اسم المعدة", key="new_equipment_name") |
|
with col2: |
|
rental_type = st.selectbox( |
|
"نوع الإيجار", |
|
options=["يومي", "أسبوعي", "شهري", "سنوي", "مملوكة (استهلاك)"], |
|
key="rental_type" |
|
) |
|
with col3: |
|
usage_period = st.number_input(f"مدة الاستخدام ({rental_type})", min_value=1, value=1, key="usage_period") |
|
|
|
col4, col5, col6 = st.columns(3) |
|
|
|
with col4: |
|
equipment_rate = st.number_input(f"سعر الإيجار لكل ({rental_type}) (ريال)", min_value=0.0, step=0.01, key="equipment_rate") |
|
with col5: |
|
fuel_cost = st.number_input("تكلفة الوقود اليومية (ريال)", min_value=0.0, step=0.01, key="fuel_cost") |
|
with col6: |
|
operator_cost = st.number_input("تكلفة المشغل اليومية (ريال)", min_value=0.0, step=0.01, key="operator_cost") |
|
|
|
|
|
rental_days = { |
|
"يومي": 1, |
|
"أسبوعي": 7, |
|
"شهري": 30, |
|
"سنوي": 365, |
|
"مملوكة (استهلاك)": 1 |
|
} |
|
|
|
total_days = usage_period * rental_days[rental_type] |
|
total_equipment_cost = equipment_rate * usage_period |
|
total_fuel_cost = fuel_cost * total_days |
|
total_operator_cost = operator_cost * total_days |
|
total_cost = total_equipment_cost + total_fuel_cost + total_operator_cost |
|
|
|
if st.button("إضافة معدة", key="add_equipment_btn"): |
|
new_equipment = { |
|
"name": equipment_name, |
|
"rental_type": rental_type, |
|
"usage_period": usage_period, |
|
"equipment_rate": equipment_rate, |
|
"fuel_cost": fuel_cost, |
|
"operator_cost": operator_cost, |
|
"total": total_cost |
|
} |
|
st.session_state.equipment.append(new_equipment) |
|
st.success(f"تمت إضافة {equipment_name} بنجاح!") |
|
|
|
|
|
st.markdown("<div class='card' style='margin-top: 10px;'>", unsafe_allow_html=True) |
|
st.markdown(f"<p>عدد أيام الاستخدام الإجمالية: {total_days} يوم</p>", unsafe_allow_html=True) |
|
st.markdown(f"<p>تكلفة إيجار المعدة: {total_equipment_cost:,.2f} ريال</p>", unsafe_allow_html=True) |
|
st.markdown(f"<p>تكلفة الوقود: {total_fuel_cost:,.2f} ريال</p>", unsafe_allow_html=True) |
|
st.markdown(f"<p>تكلفة المشغل: {total_operator_cost:,.2f} ريال</p>", unsafe_allow_html=True) |
|
st.markdown(f"<h4>التكلفة الإجمالية للمعدة: {total_cost:,.2f} ريال</h4>", unsafe_allow_html=True) |
|
st.markdown("</div>", unsafe_allow_html=True) |
|
|
|
|
|
if st.session_state.equipment: |
|
st.markdown("<h4>قائمة المعدات المضافة</h4>", unsafe_allow_html=True) |
|
|
|
equipment_data = [] |
|
for item in st.session_state.equipment: |
|
equipment_data.append({ |
|
"اسم المعدة": item["name"], |
|
"نوع الإيجار": item["rental_type"], |
|
"مدة الاستخدام": item["usage_period"], |
|
"إيجار الوحدة": item["equipment_rate"], |
|
"تكلفة الوقود": item["fuel_cost"], |
|
"تكلفة المشغل": item["operator_cost"], |
|
"التكلفة الإجمالية": item["total"] |
|
}) |
|
|
|
equipment_df = pd.DataFrame(equipment_data) |
|
st.dataframe(equipment_df) |
|
|
|
total_equipment_cost = sum(item["total"] for item in st.session_state.equipment) |
|
st.session_state.equipment_cost = total_equipment_cost |
|
|
|
st.markdown(f"<h4>إجمالي تكلفة المعدات: <span style='color:var(--primary-color)'>{total_equipment_cost:,.2f} ريال</span></h4>", unsafe_allow_html=True) |
|
|
|
|
|
if len(st.session_state.equipment) > 1: |
|
st.markdown("<h4>توزيع تكاليف المعدات</h4>", unsafe_allow_html=True) |
|
|
|
fig = go.Figure() |
|
|
|
fig.add_trace(go.Bar( |
|
x=[item["اسم المعدة"] for item in equipment_data], |
|
y=[item["التكلفة الإجمالية"] for item in equipment_data], |
|
name="التكلفة الإجمالية", |
|
marker_color="teal" |
|
)) |
|
|
|
fig.update_layout( |
|
title="تكاليف المعدات", |
|
xaxis_title="المعدة", |
|
yaxis_title="التكلفة (ريال)", |
|
font=dict(family="Almarai, Arial", size=14), |
|
margin=dict(t=50, b=50, l=20, r=20) |
|
) |
|
|
|
st.plotly_chart(fig, use_container_width=True) |
|
|
|
|
|
def render_labor_tab(): |
|
""" |
|
عرض تبويب العمالة |
|
""" |
|
st.markdown("<h3>تكاليف العمالة</h3>", unsafe_allow_html=True) |
|
|
|
|
|
st.markdown("<h4>إضافة عمالة جديدة</h4>", unsafe_allow_html=True) |
|
|
|
col1, col2, col3 = st.columns(3) |
|
|
|
with col1: |
|
labor_type = st.text_input("نوع العمالة", key="new_labor_type") |
|
with col2: |
|
labor_count = st.number_input("العدد", min_value=1, value=1, key="new_labor_count") |
|
with col3: |
|
payment_type = st.selectbox( |
|
"نوع الدفع", |
|
options=["يومي", "أسبوعي", "شهري", "بالقطعة"], |
|
key="new_payment_type" |
|
) |
|
|
|
col4, col5, col6 = st.columns(3) |
|
|
|
with col4: |
|
wage_rate = st.number_input(f"الأجرة ({payment_type}) (ريال)", min_value=0.0, step=0.01, key="new_wage_rate") |
|
with col5: |
|
work_period = st.number_input(f"مدة العمل ({payment_type})", min_value=1, value=30, key="new_work_period") |
|
with col6: |
|
benefits_percent = st.slider("نسبة البدلات والتأمين (%)", min_value=0, max_value=50, value=15, key="new_benefits_percent") |
|
|
|
|
|
days_factor = { |
|
"يومي": 1, |
|
"أسبوعي": 7, |
|
"شهري": 30, |
|
"بالقطعة": 1 |
|
} |
|
|
|
monthly_days = work_period * days_factor[payment_type] / 30 |
|
|
|
if payment_type == "بالقطعة": |
|
total_labor_cost = labor_count * wage_rate * work_period |
|
else: |
|
|
|
monthly_wage = wage_rate * 30 / days_factor[payment_type] |
|
|
|
benefits_cost = monthly_wage * (benefits_percent / 100) |
|
|
|
monthly_total_cost = monthly_wage + benefits_cost |
|
|
|
total_labor_cost = labor_count * monthly_total_cost * monthly_days |
|
|
|
if st.button("إضافة عمالة"): |
|
new_labor = { |
|
"type": labor_type, |
|
"count": labor_count, |
|
"payment_type": payment_type, |
|
"wage_rate": wage_rate, |
|
"work_period": work_period, |
|
"benefits_percent": benefits_percent, |
|
"total": total_labor_cost |
|
} |
|
st.session_state.labor.append(new_labor) |
|
st.success(f"تمت إضافة {labor_type} بنجاح!") |
|
|
|
|
|
st.markdown("<div class='card' style='margin-top: 10px;'>", unsafe_allow_html=True) |
|
if payment_type != "بالقطعة": |
|
monthly_wage = wage_rate * 30 / days_factor[payment_type] |
|
benefits_cost = monthly_wage * (benefits_percent / 100) |
|
monthly_total_cost = monthly_wage + benefits_cost |
|
|
|
st.markdown(f"<p>الراتب الشهري للعامل: {monthly_wage:,.2f} ريال</p>", unsafe_allow_html=True) |
|
st.markdown(f"<p>تكلفة البدلات والتأمين الشهرية: {benefits_cost:,.2f} ريال</p>", unsafe_allow_html=True) |
|
st.markdown(f"<p>إجمالي التكلفة الشهرية للعامل: {monthly_total_cost:,.2f} ريال</p>", unsafe_allow_html=True) |
|
st.markdown(f"<p>مدة العمل بالشهور: {monthly_days:.2f} شهر</p>", unsafe_allow_html=True) |
|
else: |
|
st.markdown(f"<p>سعر القطعة: {wage_rate:,.2f} ريال</p>", unsafe_allow_html=True) |
|
st.markdown(f"<p>عدد القطع: {work_period}</p>", unsafe_allow_html=True) |
|
|
|
st.markdown(f"<p>عدد العمال: {labor_count}</p>", unsafe_allow_html=True) |
|
st.markdown(f"<h4>التكلفة الإجمالية للعمالة: {total_labor_cost:,.2f} ريال</h4>", unsafe_allow_html=True) |
|
st.markdown("</div>", unsafe_allow_html=True) |
|
|
|
|
|
if st.session_state.labor: |
|
st.markdown("<h4>قائمة العمالة المضافة</h4>", unsafe_allow_html=True) |
|
|
|
labor_data = [] |
|
for item in st.session_state.labor: |
|
labor_data.append({ |
|
"نوع العمالة": item["type"], |
|
"العدد": item["count"], |
|
"نوع الدفع": item["payment_type"], |
|
"معدل الأجرة": item["wage_rate"], |
|
"مدة العمل": item["work_period"], |
|
"نسبة البدلات": f"{item['benefits_percent']}%", |
|
"التكلفة الإجمالية": item["total"] |
|
}) |
|
|
|
labor_df = pd.DataFrame(labor_data) |
|
st.dataframe(labor_df) |
|
|
|
total_labor_cost = sum(item["total"] for item in st.session_state.labor) |
|
st.session_state.labor_cost = total_labor_cost |
|
|
|
st.markdown(f"<h4>إجمالي تكلفة العمالة: <span style='color:var(--primary-color)'>{total_labor_cost:,.2f} ريال</span></h4>", unsafe_allow_html=True) |
|
|
|
|
|
if len(st.session_state.labor) > 1: |
|
st.markdown("<h4>توزيع تكاليف العمالة</h4>", unsafe_allow_html=True) |
|
|
|
fig = px.bar( |
|
labor_df, |
|
x="نوع العمالة", |
|
y="التكلفة الإجمالية", |
|
color="العدد", |
|
title="توزيع تكاليف العمالة", |
|
color_continuous_scale=px.colors.sequential.Teal |
|
) |
|
fig.update_layout( |
|
font=dict(family="Almarai, Arial", size=14), |
|
margin=dict(t=50, b=50, l=20, r=20) |
|
) |
|
st.plotly_chart(fig, use_container_width=True) |
|
|
|
|
|
def render_admin_tab(): |
|
""" |
|
عرض تبويب المصاريف الإدارية |
|
""" |
|
st.markdown("<h3>المصاريف الإدارية والعمومية</h3>", unsafe_allow_html=True) |
|
|
|
|
|
st.markdown("<h4>إضافة مصروف جديد</h4>", unsafe_allow_html=True) |
|
|
|
col1, col2, col3 = st.columns(3) |
|
|
|
with col1: |
|
expense_name = st.text_input("اسم المصروف", key="new_expense_name") |
|
with col2: |
|
expense_type = st.selectbox( |
|
"نوع المصروف", |
|
options=[ |
|
"رواتب إدارية", "إيجارات", "مكتبية", "سفر", "تأمين", |
|
"استشارات", "رسوم حكومية", "منافع", "أخرى" |
|
], |
|
key="new_expense_type" |
|
) |
|
with col3: |
|
expense_amount = st.number_input("المبلغ (ريال)", min_value=0.0, step=100.0, key="new_expense_amount") |
|
|
|
if st.button("إضافة مصروف"): |
|
new_expense = { |
|
"name": expense_name, |
|
"type": expense_type, |
|
"amount": expense_amount |
|
} |
|
st.session_state.admin_expenses.append(new_expense) |
|
st.success(f"تمت إضافة {expense_name} بنجاح!") |
|
|
|
|
|
if st.session_state.admin_expenses: |
|
st.markdown("<h4>قائمة المصاريف الإدارية</h4>", unsafe_allow_html=True) |
|
|
|
admin_data = [] |
|
for item in st.session_state.admin_expenses: |
|
admin_data.append({ |
|
"اسم المصروف": item["name"], |
|
"نوع المصروف": item["type"], |
|
"المبلغ": item["amount"] |
|
}) |
|
|
|
admin_df = pd.DataFrame(admin_data) |
|
st.dataframe(admin_df) |
|
|
|
total_admin_cost = sum(item["amount"] for item in st.session_state.admin_expenses) |
|
st.session_state.admin_cost = total_admin_cost |
|
|
|
st.markdown(f"<h4>إجمالي المصاريف الإدارية: <span style='color:var(--primary-color)'>{total_admin_cost:,.2f} ريال</span></h4>", unsafe_allow_html=True) |
|
|
|
|
|
if len(st.session_state.admin_expenses) > 1: |
|
st.markdown("<h4>توزيع المصاريف الإدارية حسب النوع</h4>", unsafe_allow_html=True) |
|
|
|
|
|
expense_by_type = admin_df.groupby("نوع المصروف")["المبلغ"].sum().reset_index() |
|
|
|
fig = px.pie( |
|
expense_by_type, |
|
values="المبلغ", |
|
names="نوع المصروف", |
|
title="توزيع المصاريف الإدارية", |
|
color_discrete_sequence=px.colors.sequential.Teal, |
|
hole=0.4 |
|
) |
|
fig.update_layout( |
|
font=dict(family="Almarai, Arial", size=14), |
|
margin=dict(t=50, b=50, l=20, r=20) |
|
) |
|
st.plotly_chart(fig, use_container_width=True) |
|
|
|
|
|
st.markdown("<h4>احتساب المصاريف الإدارية بالنسبة المئوية</h4>", unsafe_allow_html=True) |
|
|
|
|
|
direct_costs = st.session_state.materials_cost + st.session_state.equipment_cost + st.session_state.labor_cost |
|
|
|
col1, col2 = st.columns(2) |
|
|
|
with col1: |
|
admin_percent = st.slider("نسبة المصاريف الإدارية من التكاليف المباشرة (%)", min_value=0, max_value=30, value=10, key="admin_percent") |
|
|
|
with col2: |
|
calculated_admin_cost = direct_costs * (admin_percent / 100) |
|
st.markdown(f"<div class='card'><h4>المصاريف الإدارية بالنسبة: <span style='color:var(--primary-color)'>{calculated_admin_cost:,.2f} ريال</span></h4></div>", unsafe_allow_html=True) |
|
|
|
if st.button("استخدام النسبة المئوية للمصاريف الإدارية"): |
|
st.session_state.admin_cost = calculated_admin_cost |
|
st.success("تم تحديث إجمالي المصاريف الإدارية بناء على النسبة المئوية!") |
|
|
|
|
|
def render_profit_tab(): |
|
""" |
|
عرض تبويب هامش الربح |
|
""" |
|
st.markdown("<h3>هامش الربح</h3>", unsafe_allow_html=True) |
|
|
|
|
|
direct_costs = st.session_state.materials_cost + st.session_state.equipment_cost + st.session_state.labor_cost |
|
total_costs = direct_costs + st.session_state.admin_cost |
|
|
|
|
|
st.markdown("<div class='card'>", unsafe_allow_html=True) |
|
st.markdown("<h4>ملخص التكاليف</h4>", unsafe_allow_html=True) |
|
st.markdown(f"<p>إجمالي تكلفة المواد: <span style='color:var(--text-medium)'>{st.session_state.materials_cost:,.2f} ريال</span></p>", unsafe_allow_html=True) |
|
st.markdown(f"<p>إجمالي تكلفة المعدات: <span style='color:var(--text-medium)'>{st.session_state.equipment_cost:,.2f} ريال</span></p>", unsafe_allow_html=True) |
|
st.markdown(f"<p>إجمالي تكلفة العمالة: <span style='color:var(--text-medium)'>{st.session_state.labor_cost:,.2f} ريال</span></p>", unsafe_allow_html=True) |
|
st.markdown(f"<p>إجمالي التكاليف المباشرة: <span style='color:var(--primary-color)'>{direct_costs:,.2f} ريال</span></p>", unsafe_allow_html=True) |
|
st.markdown(f"<p>إجمالي المصاريف الإدارية: <span style='color:var(--text-medium)'>{st.session_state.admin_cost:,.2f} ريال</span></p>", unsafe_allow_html=True) |
|
st.markdown(f"<h4>إجمالي التكاليف: <span style='color:var(--primary-color)'>{total_costs:,.2f} ريال</span></h4>", unsafe_allow_html=True) |
|
st.markdown("</div>", unsafe_allow_html=True) |
|
|
|
|
|
st.markdown("<h4>تحديد هامش الربح</h4>", unsafe_allow_html=True) |
|
|
|
col1, col2 = st.columns(2) |
|
|
|
with col1: |
|
profit_margin = st.slider("نسبة هامش الربح (%)", min_value=0, max_value=30, value=int(st.session_state.profit_margin), key="profit_margin_slider") |
|
st.session_state.profit_margin = profit_margin |
|
|
|
with col2: |
|
profit_amount = total_costs * (profit_margin / 100) |
|
st.markdown(f"<div class='card'><h4>قيمة هامش الربح: <span style='color:var(--primary-color)'>{profit_amount:,.2f} ريال</span></h4></div>", unsafe_allow_html=True) |
|
|
|
|
|
total_price = total_costs + profit_amount |
|
st.markdown("<div class='card' style='background: var(--primary-light);'>", unsafe_allow_html=True) |
|
st.markdown(f"<h3>إجمالي قيمة العرض: <span style='color:var(--primary-color)'>{total_price:,.2f} ريال</span></h3>", unsafe_allow_html=True) |
|
st.markdown("</div>", unsafe_allow_html=True) |
|
|
|
|
|
st.markdown("<h4>تحليل حساسية هامش الربح</h4>", unsafe_allow_html=True) |
|
|
|
sensitivity_data = [] |
|
for margin in range(5, 31, 5): |
|
profit = total_costs * (margin / 100) |
|
total = total_costs + profit |
|
sensitivity_data.append({ |
|
"نسبة الربح": f"{margin}%", |
|
"قيمة الربح": profit, |
|
"إجمالي العرض": total |
|
}) |
|
|
|
sensitivity_df = pd.DataFrame(sensitivity_data) |
|
|
|
|
|
fig = go.Figure() |
|
|
|
fig.add_trace(go.Bar( |
|
x=[item["نسبة الربح"] for item in sensitivity_data], |
|
y=[item["قيمة الربح"] for item in sensitivity_data], |
|
name="قيمة الربح", |
|
marker_color="rgba(14, 165, 165, 0.7)" |
|
)) |
|
|
|
fig.add_trace(go.Scatter( |
|
x=[item["نسبة الربح"] for item in sensitivity_data], |
|
y=[item["إجمالي العرض"] for item in sensitivity_data], |
|
name="إجمالي العرض", |
|
mode="lines+markers", |
|
marker=dict(size=8, color="rgba(255, 154, 60, 1.0)"), |
|
line=dict(width=3, color="rgba(255, 154, 60, 0.7)") |
|
)) |
|
|
|
fig.update_layout( |
|
title="تحليل حساسية هامش الربح", |
|
xaxis_title="نسبة الربح", |
|
yaxis_title="القيمة (ريال)", |
|
font=dict(family="Almarai, Arial", size=14), |
|
margin=dict(t=50, b=50, l=20, r=20), |
|
hovermode="x unified" |
|
) |
|
|
|
st.plotly_chart(fig, use_container_width=True) |
|
|
|
|
|
st.dataframe(sensitivity_df) |
|
|
|
|
|
def render_final_report(project_name, project_location, project_area, project_type): |
|
""" |
|
عرض التقرير النهائي للتكاليف |
|
""" |
|
st.markdown("<h3>التقرير النهائي لتكاليف المشروع</h3>", unsafe_allow_html=True) |
|
|
|
|
|
required_fields = { |
|
'materials_cost': 0.0, |
|
'equipment_cost': 0.0, |
|
'labor_cost': 0.0, |
|
'admin_cost': 0.0, |
|
'profit_margin': 15.0, |
|
'materials': [], |
|
'equipment': [], |
|
'labor': [], |
|
'admin_expenses': [] |
|
} |
|
|
|
|
|
for field, default_value in required_fields.items(): |
|
if field not in st.session_state: |
|
st.session_state[field] = default_value |
|
|
|
|
|
if field in ['materials_cost', 'equipment_cost', 'labor_cost', 'admin_cost', 'profit_margin']: |
|
|
|
if st.session_state[field] is None or pd.isna(st.session_state[field]): |
|
st.session_state[field] = default_value |
|
|
|
|
|
direct_costs = st.session_state.materials_cost + st.session_state.equipment_cost + st.session_state.labor_cost |
|
total_costs = direct_costs + st.session_state.admin_cost |
|
profit_amount = total_costs * (st.session_state.profit_margin / 100) |
|
total_price = total_costs + profit_amount |
|
|
|
|
|
st.markdown("<div class='card'>", unsafe_allow_html=True) |
|
st.markdown("<h4>معلومات المشروع</h4>", unsafe_allow_html=True) |
|
col1, col2 = st.columns(2) |
|
|
|
with col1: |
|
st.markdown(f"<p><strong>اسم المشروع:</strong> {project_name}</p>", unsafe_allow_html=True) |
|
st.markdown(f"<p><strong>نوع المشروع:</strong> {project_type}</p>", unsafe_allow_html=True) |
|
|
|
with col2: |
|
st.markdown(f"<p><strong>موقع المشروع:</strong> {project_location}</p>", unsafe_allow_html=True) |
|
st.markdown(f"<p><strong>المساحة الإجمالية:</strong> {project_area} م²</p>", unsafe_allow_html=True) |
|
|
|
st.markdown("</div>", unsafe_allow_html=True) |
|
|
|
|
|
st.markdown("<div class='card'>", unsafe_allow_html=True) |
|
st.markdown("<h4>ملخص التكاليف</h4>", unsafe_allow_html=True) |
|
|
|
col1, col2 = st.columns(2) |
|
|
|
with col1: |
|
st.markdown(f"<p><strong>تكلفة المواد:</strong> {st.session_state.materials_cost:,.2f} ريال</p>", unsafe_allow_html=True) |
|
st.markdown(f"<p><strong>تكلفة المعدات:</strong> {st.session_state.equipment_cost:,.2f} ريال</p>", unsafe_allow_html=True) |
|
st.markdown(f"<p><strong>تكلفة العمالة:</strong> {st.session_state.labor_cost:,.2f} ريال</p>", unsafe_allow_html=True) |
|
st.markdown(f"<p><strong>إجمالي التكاليف المباشرة:</strong> {direct_costs:,.2f} ريال</p>", unsafe_allow_html=True) |
|
|
|
with col2: |
|
st.markdown(f"<p><strong>المصاريف الإدارية:</strong> {st.session_state.admin_cost:,.2f} ريال</p>", unsafe_allow_html=True) |
|
st.markdown(f"<p><strong>إجمالي التكاليف:</strong> {total_costs:,.2f} ريال</p>", unsafe_allow_html=True) |
|
st.markdown(f"<p><strong>هامش الربح ({st.session_state.profit_margin}%):</strong> {profit_amount:,.2f} ريال</p>", unsafe_allow_html=True) |
|
st.markdown(f"<h4>إجمالي قيمة العرض: {total_price:,.2f} ريال</h4>", unsafe_allow_html=True) |
|
|
|
st.markdown("</div>", unsafe_allow_html=True) |
|
|
|
|
|
if project_area > 0: |
|
per_sqm_cost = total_price / project_area |
|
st.markdown("<div class='card'>", unsafe_allow_html=True) |
|
st.markdown("<h4>تكلفة المتر المربع</h4>", unsafe_allow_html=True) |
|
st.markdown(f"<p>تكلفة المتر المربع الإجمالية: <strong>{per_sqm_cost:,.2f} ريال/م²</strong></p>", unsafe_allow_html=True) |
|
st.markdown("</div>", unsafe_allow_html=True) |
|
else: |
|
st.markdown("<div class='card'>", unsafe_allow_html=True) |
|
st.markdown("<h4>تكلفة المتر المربع</h4>", unsafe_allow_html=True) |
|
st.markdown("<p>يرجى إدخال مساحة صحيحة للمشروع لحساب تكلفة المتر المربع</p>", unsafe_allow_html=True) |
|
st.markdown("</div>", unsafe_allow_html=True) |
|
|
|
|
|
st.markdown("<h4>توزيع التكاليف</h4>", unsafe_allow_html=True) |
|
|
|
|
|
if total_price > 0: |
|
cost_distribution = [ |
|
{"النوع": "المواد", "القيمة": st.session_state.materials_cost, "النسبة": st.session_state.materials_cost / total_price * 100}, |
|
{"النوع": "المعدات", "القيمة": st.session_state.equipment_cost, "النسبة": st.session_state.equipment_cost / total_price * 100}, |
|
{"النوع": "العمالة", "القيمة": st.session_state.labor_cost, "النسبة": st.session_state.labor_cost / total_price * 100}, |
|
{"النوع": "المصاريف الإدارية", "القيمة": st.session_state.admin_cost, "النسبة": st.session_state.admin_cost / total_price * 100}, |
|
{"النوع": "هامش الربح", "القيمة": profit_amount, "النسبة": profit_amount / total_price * 100} |
|
] |
|
else: |
|
|
|
cost_distribution = [ |
|
{"النوع": "المواد", "القيمة": st.session_state.materials_cost, "النسبة": 0}, |
|
{"النوع": "المعدات", "القيمة": st.session_state.equipment_cost, "النسبة": 0}, |
|
{"النوع": "العمالة", "القيمة": st.session_state.labor_cost, "النسبة": 0}, |
|
{"النوع": "المصاريف الإدارية", "القيمة": st.session_state.admin_cost, "النسبة": 0}, |
|
{"النوع": "هامش الربح", "القيمة": profit_amount, "النسبة": 0} |
|
] |
|
|
|
cost_df = pd.DataFrame(cost_distribution) |
|
|
|
fig = px.pie( |
|
cost_df, |
|
values="القيمة", |
|
names="النوع", |
|
title="توزيع التكاليف والأرباح", |
|
color_discrete_sequence=px.colors.sequential.Teal, |
|
hole=0.4 |
|
) |
|
|
|
fig.update_traces(textposition='inside', textinfo='percent+label') |
|
|
|
fig.update_layout( |
|
annotations=[dict(text=f"{total_price:,.0f} ريال", x=0.5, y=0.5, font_size=14, showarrow=False)], |
|
font=dict(family="Almarai, Arial", size=14), |
|
margin=dict(t=50, b=50, l=20, r=20) |
|
) |
|
|
|
st.plotly_chart(fig, use_container_width=True) |
|
|
|
|
|
st.dataframe(cost_df) |
|
|
|
|
|
col1, col2 = st.columns(2) |
|
|
|
with col1: |
|
if st.button("تصدير التقرير إلى PDF"): |
|
st.success("تم تصدير التقرير بنجاح!") |
|
|
|
with col2: |
|
if st.button("حفظ التقرير في قاعدة البيانات"): |
|
st.success("تم حفظ التقرير في قاعدة البيانات بنجاح!") |