|
""" |
|
وحدة الموارد - التطبيق الرئيسي |
|
""" |
|
|
|
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, timedelta |
|
import time |
|
import io |
|
import os |
|
import json |
|
import base64 |
|
from pathlib import Path |
|
|
|
class ResourcesApp: |
|
"""وحدة الموارد""" |
|
|
|
def __init__(self): |
|
"""تهيئة وحدة الموارد""" |
|
|
|
|
|
if 'resources_data' not in st.session_state: |
|
st.session_state.resources_data = {} |
|
|
|
|
|
if 'saved_pricing' in st.session_state: |
|
self.saved_project = st.session_state.saved_pricing[-1] if st.session_state.saved_pricing else None |
|
else: |
|
self.saved_project = None |
|
|
|
np.random.seed(42) |
|
|
|
|
|
n_employees = 50 |
|
employee_ids = [f"EMP-{i+1:03d}" for i in range(n_employees)] |
|
employee_names = [ |
|
"أحمد محمد", "محمد علي", "علي إبراهيم", "إبراهيم خالد", "خالد عبدالله", |
|
"عبدالله سعد", "سعد فهد", "فهد ناصر", "ناصر سلطان", "سلطان عمر", |
|
"عمر يوسف", "يوسف عبدالرحمن", "عبدالرحمن حسن", "حسن أحمد", "أحمد عبدالعزيز", |
|
"عبدالعزيز سعود", "سعود فيصل", "فيصل تركي", "تركي بندر", "بندر سلمان", |
|
"سلمان محمد", "محمد عبدالله", "عبدالله فهد", "فهد سعد", "سعد خالد", |
|
"خالد علي", "علي عمر", "عمر سعيد", "سعيد ماجد", "ماجد فارس", |
|
"فارس نايف", "نايف سامي", "سامي راشد", "راشد وليد", "وليد هاني", |
|
"هاني زياد", "زياد طارق", "طارق عادل", "عادل فراس", "فراس باسم", |
|
"باسم جمال", "جمال كريم", "كريم نبيل", "نبيل هشام", "هشام عماد", |
|
"عماد أيمن", "أيمن رامي", "رامي سمير", "سمير وائل", "وائل مازن" |
|
] |
|
employee_departments = np.random.choice(["الهندسة", "المشتريات", "المالية", "الموارد البشرية", "تقنية المعلومات", "التسويق", "المبيعات"], n_employees) |
|
employee_positions = np.random.choice(["مدير", "مهندس", "محلل", "مطور", "مصمم", "مشرف", "منسق", "أخصائي", "مساعد"], n_employees) |
|
employee_salaries = np.random.randint(5000, 25000, n_employees) |
|
employee_experiences = np.random.randint(1, 20, n_employees) |
|
employee_availabilities = np.random.choice([True, False], n_employees, p=[0.7, 0.3]) |
|
|
|
|
|
employees_data = { |
|
"رقم الموظف": employee_ids, |
|
"الاسم": employee_names, |
|
"القسم": employee_departments, |
|
"المنصب": employee_positions, |
|
"التكلفة الشهرية": employee_salaries, |
|
"سنوات الخبرة": employee_experiences, |
|
"متاح": employee_availabilities |
|
} |
|
|
|
|
|
n_equipment = 30 |
|
equipment_ids = [f"EQP-{i+1:03d}" for i in range(n_equipment)] |
|
equipment_names = [ |
|
"حفارة", "جرافة", "شاحنة نقل", "رافعة", "خلاطة خرسانة", "مولد كهرباء", "ضاغط هواء", |
|
"مضخة مياه", "آلة حفر", "آلة تسوية", "آلة رصف", "آلة تشكيل", "آلة قطع", "آلة لحام", |
|
"آلة طلاء", "آلة تنظيف", "آلة تعبئة", "آلة تغليف", "آلة فرز", "آلة تجميع", |
|
"آلة تثقيب", "آلة تجليخ", "آلة تفريز", "آلة خراطة", "آلة تشكيل", "آلة تقطيع", |
|
"آلة تجفيف", "آلة تبريد", "آلة تسخين", "آلة تهوية" |
|
] |
|
equipment_types = np.random.choice(["حفر", "نقل", "رفع", "خلط", "توليد", "ضغط", "ضخ", "تشكيل", "قطع", "لحام", "طلاء", "تنظيف", "تعبئة", "تغليف", "فرز", "تجميع", "تثقيب", "تجليخ", "تفريز", "خراطة"], n_equipment) |
|
equipment_costs = np.random.randint(500, 5000, n_equipment) |
|
equipment_availabilities = np.random.choice([True, False], n_equipment, p=[0.8, 0.2]) |
|
equipment_conditions = np.random.choice(["ممتاز", "جيد", "متوسط", "سيء"], n_equipment, p=[0.4, 0.3, 0.2, 0.1]) |
|
equipment_locations = np.random.choice(["المستودع", "موقع العمل 1", "موقع العمل 2", "موقع العمل 3", "في الصيانة"], n_equipment) |
|
|
|
|
|
equipment_data = { |
|
"رقم المعدة": equipment_ids, |
|
"الاسم": equipment_names, |
|
"النوع": equipment_types, |
|
"التكلفة اليومية": equipment_costs, |
|
"متاحة": equipment_availabilities, |
|
"الحالة": equipment_conditions, |
|
"الموقع": equipment_locations |
|
} |
|
|
|
|
|
n_materials = 40 |
|
material_ids = [f"MAT-{i+1:03d}" for i in range(n_materials)] |
|
material_names = [ |
|
"خرسانة جاهزة", "حديد تسليح", "طابوق", "أسمنت", "رمل", "بحص", "خشب", "ألمنيوم", "زجاج", "دهان", |
|
"سيراميك", "رخام", "جبس", "عازل مائي", "عازل حراري", "أنابيب PVC", "أسلاك كهربائية", "مفاتيح كهربائية", |
|
"إنارة", "تكييف", "مصاعد", "أبواب خشبية", "أبواب حديدية", "نوافذ ألمنيوم", "نوافذ زجاجية", |
|
"أرضيات خشبية", "أرضيات بلاط", "أرضيات رخام", "أرضيات سيراميك", "أرضيات بورسلين", |
|
"دهان داخلي", "دهان خارجي", "مواد عزل", "مواد تشطيب", "مواد كهربائية", "مواد سباكة", |
|
"مواد تكييف", "مواد إضاءة", "مواد سلامة", "مواد متنوعة" |
|
] |
|
material_units = np.random.choice(["م3", "طن", "م2", "كجم", "لتر", "قطعة", "متر"], n_materials) |
|
material_quantities = np.random.randint(10, 1000, n_materials) |
|
material_costs = np.random.randint(50, 5000, n_materials) |
|
material_suppliers = np.random.choice(["المورد 1", "المورد 2", "المورد 3", "المورد 4", "المورد 5"], n_materials) |
|
material_lead_times = np.random.randint(1, 30, n_materials) |
|
|
|
|
|
materials_data = { |
|
"رقم المادة": material_ids, |
|
"اسم المادة": material_names, |
|
"الوحدة": material_units, |
|
"الكمية المتاحة": material_quantities, |
|
"تكلفة الوحدة": material_costs, |
|
"المورد": material_suppliers, |
|
"مدة التوريد (يوم)": material_lead_times |
|
} |
|
|
|
|
|
n_projects = 10 |
|
project_ids = [f"PRJ-{i+1:03d}" for i in range(n_projects)] |
|
project_names = [ |
|
"مشروع إنشاء مبنى إداري", "مشروع إنشاء مبنى سكني", "مشروع إنشاء مدرسة", |
|
"مشروع إنشاء مستشفى", "مشروع تطوير طرق", "مشروع إنشاء جسر", |
|
"مشروع بنية تحتية", "مشروع إنشاء مركز تجاري", "مشروع إنشاء فندق", |
|
"مشروع إنشاء مصنع" |
|
] |
|
project_locations = np.random.choice(["الرياض", "جدة", "الدمام", "مكة", "المدينة", "أبها", "تبوك"], n_projects) |
|
project_start_dates = [ |
|
(datetime.now() - timedelta(days=np.random.randint(0, 180))).strftime("%Y-%m-%d") |
|
for _ in range(n_projects) |
|
] |
|
project_end_dates = [ |
|
(datetime.strptime(start_date, "%Y-%m-%d") + timedelta(days=np.random.randint(180, 720))).strftime("%Y-%m-%d") |
|
for start_date in project_start_dates |
|
] |
|
project_budgets = np.random.randint(1000000, 50000000, n_projects) |
|
project_statuses = np.random.choice(["قيد التنفيذ", "مكتمل", "متوقف", "مخطط"], n_projects) |
|
|
|
|
|
projects_data = { |
|
"رقم المشروع": project_ids, |
|
"اسم المشروع": project_names, |
|
"الموقع": project_locations, |
|
"تاريخ البدء": project_start_dates, |
|
"تاريخ الانتهاء": project_end_dates, |
|
"الميزانية": project_budgets, |
|
"الحالة": project_statuses |
|
} |
|
|
|
|
|
n_allocations = 100 |
|
allocation_ids = [f"ALLOC-{i+1:03d}" for i in range(n_allocations)] |
|
allocation_projects = np.random.choice(project_ids, n_allocations) |
|
allocation_resource_types = np.random.choice(["موظف", "معدة", "مادة"], n_allocations) |
|
allocation_resource_ids = [] |
|
for res_type in allocation_resource_types: |
|
if res_type == "موظف": |
|
allocation_resource_ids.append(np.random.choice(employee_ids)) |
|
elif res_type == "معدة": |
|
allocation_resource_ids.append(np.random.choice(equipment_ids)) |
|
else: |
|
allocation_resource_ids.append(np.random.choice(material_ids)) |
|
|
|
allocation_start_dates = [ |
|
(datetime.now() - timedelta(days=np.random.randint(0, 90))).strftime("%Y-%m-%d") |
|
for _ in range(n_allocations) |
|
] |
|
allocation_end_dates = [ |
|
(datetime.strptime(start_date, "%Y-%m-%d") + timedelta(days=np.random.randint(30, 180))).strftime("%Y-%m-%d") |
|
for start_date in allocation_start_dates |
|
] |
|
allocation_quantities = np.random.randint(1, 10, n_allocations) |
|
allocation_costs = np.random.randint(5000, 50000, n_allocations) |
|
|
|
|
|
allocations_data = { |
|
"رقم التخصيص": allocation_ids, |
|
"رقم المشروع": allocation_projects, |
|
"نوع المورد": allocation_resource_types, |
|
"رقم المورد": allocation_resource_ids, |
|
"تاريخ البدء": allocation_start_dates, |
|
"تاريخ الانتهاء": allocation_end_dates, |
|
"الكمية": allocation_quantities, |
|
"التكلفة": allocation_costs |
|
} |
|
|
|
|
|
st.session_state.resources_data = { |
|
"employees": pd.DataFrame(employees_data), |
|
"equipment": pd.DataFrame(equipment_data), |
|
"materials": pd.DataFrame(materials_data), |
|
"projects": pd.DataFrame(projects_data), |
|
"allocations": pd.DataFrame(allocations_data) |
|
} |
|
|
|
def run(self): |
|
""" |
|
تشغيل وحدة الموارد |
|
|
|
هذه الدالة هي نقطة الدخول الرئيسية لوحدة الموارد وتقوم بتهيئة واجهة المستخدم |
|
وعرض جميع العناصر المطلوبة. |
|
""" |
|
|
|
self.render() |
|
|
|
def render(self): |
|
"""عرض واجهة وحدة الموارد""" |
|
|
|
st.markdown("<h1 class='module-title'>وحدة الموارد</h1>", unsafe_allow_html=True) |
|
|
|
|
|
if self.saved_project: |
|
with st.expander("تفاصيل المشروع المحفوظ", expanded=True): |
|
st.markdown(f"**اسم المشروع:** {self.saved_project.get('project_name', 'غير محدد')}") |
|
st.markdown(f"**إجمالي القيمة:** {self.saved_project.get('total_price', 0):,.2f} ريال") |
|
st.markdown(f"**عدد البنود:** {len(self.saved_project.get('items', []))}") |
|
|
|
|
|
if 'items' in self.saved_project: |
|
st.subheader("جدول الكميات") |
|
df = pd.DataFrame(self.saved_project['items']) |
|
st.dataframe(df) |
|
|
|
tabs = st.tabs([ |
|
"لوحة المعلومات", |
|
"الموارد البشرية", |
|
"المعدات", |
|
"المواد", |
|
"تخصيص الموارد", |
|
"تخطيط الموارد" |
|
]) |
|
|
|
with tabs[0]: |
|
self._render_dashboard_tab() |
|
|
|
with tabs[1]: |
|
self._render_human_resources_tab() |
|
|
|
with tabs[2]: |
|
self._render_equipment_tab() |
|
|
|
with tabs[3]: |
|
self._render_materials_tab() |
|
|
|
with tabs[4]: |
|
self._render_resource_allocation_tab() |
|
|
|
with tabs[5]: |
|
self._render_resource_planning_tab() |
|
|
|
def _render_dashboard_tab(self): |
|
"""عرض تبويب لوحة المعلومات""" |
|
|
|
st.markdown("### لوحة معلومات الموارد") |
|
|
|
|
|
employees_df = st.session_state.resources_data["employees"] |
|
equipment_df = st.session_state.resources_data["equipment"] |
|
materials_df = st.session_state.resources_data["materials"] |
|
projects_df = st.session_state.resources_data["projects"] |
|
allocations_df = st.session_state.resources_data["allocations"] |
|
|
|
|
|
st.markdown("#### مؤشرات الأداء الرئيسية") |
|
|
|
col1, col2, col3, col4 = st.columns(4) |
|
|
|
|
|
with col1: |
|
total_employees = len(employees_df) |
|
available_employees = len(employees_df[employees_df["متاح"] == True]) |
|
st.metric("الموظفون", f"{available_employees}/{total_employees}") |
|
|
|
|
|
with col2: |
|
total_equipment = len(equipment_df) |
|
available_equipment = len(equipment_df[equipment_df["متاحة"] == True]) |
|
st.metric("المعدات", f"{available_equipment}/{total_equipment}") |
|
|
|
|
|
with col3: |
|
if self.saved_project and 'items' in self.saved_project: |
|
total_items = len(self.saved_project['items']) |
|
total_value = sum(item.get('total_price', 0) for item in self.saved_project['items']) |
|
st.metric( |
|
"إجمالي البنود", |
|
f"{total_items} بند", |
|
f"{total_value:,.0f} ريال" |
|
) |
|
else: |
|
total_materials = len(materials_df) |
|
low_stock_materials = len(materials_df[materials_df["الكمية المتاحة"] < 50]) |
|
st.metric("المواد", f"{total_materials}", f"-{low_stock_materials} منخفضة المخزون") |
|
|
|
|
|
with col4: |
|
if self.saved_project and 'items' in self.saved_project: |
|
items_df = pd.DataFrame(self.saved_project['items']) |
|
if not items_df.empty: |
|
avg_unit_price = items_df['unit_price'].mean() if 'unit_price' in items_df.columns else 0 |
|
st.metric( |
|
"متوسط سعر الوحدة", |
|
f"{avg_unit_price:,.2f} ريال" |
|
) |
|
else: |
|
total_projects = len(projects_df) |
|
active_projects = len(projects_df[projects_df["الحالة"] == "قيد التنفيذ"]) |
|
st.metric("المشاريع النشطة", f"{active_projects}/{total_projects}") |
|
|
|
|
|
st.markdown("#### توزيع الموارد البشرية حسب القسم") |
|
|
|
dept_counts = employees_df["القسم"].value_counts().reset_index() |
|
dept_counts.columns = ["القسم", "العدد"] |
|
|
|
fig = px.pie( |
|
dept_counts, |
|
values="العدد", |
|
names="القسم", |
|
title="توزيع الموظفين حسب القسم", |
|
color="القسم" |
|
) |
|
|
|
st.plotly_chart(fig, use_container_width=True) |
|
|
|
|
|
st.markdown("#### توزيع المعدات حسب النوع") |
|
|
|
type_counts = equipment_df["النوع"].value_counts().reset_index() |
|
type_counts.columns = ["النوع", "العدد"] |
|
|
|
fig = px.bar( |
|
type_counts, |
|
x="النوع", |
|
y="العدد", |
|
title="توزيع المعدات حسب النوع", |
|
color="النوع", |
|
text_auto=True |
|
) |
|
|
|
st.plotly_chart(fig, use_container_width=True) |
|
|
|
|
|
st.markdown("#### توزيع المواد حسب المورد") |
|
|
|
supplier_counts = materials_df["المورد"].value_counts().reset_index() |
|
supplier_counts.columns = ["المورد", "العدد"] |
|
|
|
fig = px.pie( |
|
supplier_counts, |
|
values="العدد", |
|
names="المورد", |
|
title="توزيع المواد حسب المورد", |
|
color="المورد" |
|
) |
|
|
|
st.plotly_chart(fig, use_container_width=True) |
|
|
|
|
|
st.markdown("#### توزيع تكاليف الموارد") |
|
|
|
|
|
total_employee_cost = employees_df["التكلفة الشهرية"].sum() |
|
|
|
|
|
total_equipment_cost = equipment_df["التكلفة اليومية"].sum() * 30 |
|
|
|
|
|
total_material_cost = (materials_df["الكمية المتاحة"] * materials_df["تكلفة الوحدة"]).sum() |
|
|
|
|
|
cost_distribution = pd.DataFrame({ |
|
"نوع المورد": ["الموظفون", "المعدات", "المواد"], |
|
"التكلفة": [total_employee_cost, total_equipment_cost, total_material_cost] |
|
}) |
|
|
|
fig = px.pie( |
|
cost_distribution, |
|
values="التكلفة", |
|
names="نوع المورد", |
|
title="توزيع تكاليف الموارد", |
|
color="نوع المورد", |
|
color_discrete_map={ |
|
"الموظفون": "#3498db", |
|
"المعدات": "#2ecc71", |
|
"المواد": "#f39c12" |
|
} |
|
) |
|
|
|
st.plotly_chart(fig, use_container_width=True) |
|
|
|
|
|
st.markdown("#### تخصيص الموارد للمشاريع") |
|
|
|
|
|
project_allocations = allocations_df["رقم المشروع"].value_counts().reset_index() |
|
project_allocations.columns = ["رقم المشروع", "عدد الموارد المخصصة"] |
|
|
|
|
|
project_allocations = project_allocations.merge( |
|
projects_df[["رقم المشروع", "اسم المشروع", "الحالة"]], |
|
on="رقم المشروع", |
|
how="left" |
|
) |
|
|
|
fig = px.bar( |
|
project_allocations, |
|
x="اسم المشروع", |
|
y="عدد الموارد المخصصة", |
|
title="توزيع الموارد على المشاريع", |
|
text_auto=True |
|
) |
|
st.plotly_chart(fig, use_container_width=True) |
|
|
|
|
|
def _render_human_resources_tab(self): |
|
"""عرض تبويب الموارد البشرية""" |
|
st.markdown("### الموارد البشرية") |
|
|
|
|
|
with st.expander("إضافة موظف جديد"): |
|
employee_form = st.form("add_employee_form") |
|
with employee_form: |
|
employee_data = {} |
|
employee_data['name'] = st.text_input("الاسم") |
|
employee_data['department'] = st.selectbox("القسم", [ |
|
"الهندسة", |
|
"المشتريات", |
|
"المالية", |
|
"الموارد البشرية", |
|
"تقنية المعلومات", |
|
"التسويق", |
|
"المشاريع", |
|
"الجودة والسلامة", |
|
"المكتب الفني" |
|
], key="employee_department_selectbox") |
|
employee_data['position'] = st.selectbox("المنصب", [ |
|
"مدير مشروع", |
|
"مهندس مدني", |
|
"مهندس معماري", |
|
"مهندس ميكانيكي", |
|
"مهندس كهربائي", |
|
"مهندس مساحة", |
|
"فني مدني", |
|
"فني كهرباء", |
|
"فني مساحة", |
|
"مراقب", |
|
"سائق معدات", |
|
"عامل مهني", |
|
"عامل عادي" |
|
], key="employee_position_selectbox") |
|
|
|
cols = st.columns(3) |
|
with cols[0]: |
|
employee_data['monthly_salary'] = st.number_input("الراتب الشهري", min_value=0) |
|
with cols[1]: |
|
employee_data['experience_years'] = st.number_input("سنوات الخبرة", min_value=0) |
|
with cols[2]: |
|
employee_data['availability'] = st.selectbox("الحالة", ["متاح", "غير متاح"], key="employee_availability_selectbox") |
|
|
|
employee_data['skills'] = st.multiselect("المهارات", [ |
|
"إدارة مشاريع", |
|
"تصميم إنشائي", |
|
"تخطيط وجدولة", |
|
"حساب كميات", |
|
"AutoCAD", |
|
"Revit", |
|
"Primavera", |
|
"MS Project", |
|
"تقدير تكاليف", |
|
"مراقبة جودة", |
|
"إدارة سلامة" |
|
]) |
|
|
|
employee_data['certifications'] = st.multiselect("الشهادات", [ |
|
"PMP", |
|
"عضوية الهيئة السعودية للمهندسين", |
|
"OSHA", |
|
"NEBOSH", |
|
"AutoCAD Certified", |
|
"Revit Certified" |
|
]) |
|
|
|
if st.form_submit_button("إضافة"): |
|
employees_df = st.session_state.resources_data["employees"] |
|
new_id = f"EMP-{len(employees_df) + 1:03d}" |
|
employee_data['id'] = new_id |
|
employees_df.loc[len(employees_df)] = employee_data |
|
st.success("تمت إضافة الموظف بنجاح") |
|
|
|
|
|
employees_df = st.session_state.resources_data["employees"] |
|
|
|
|
|
col1, col2, col3 = st.columns(3) |
|
with col1: |
|
dept_filter = st.selectbox("تصفية حسب القسم", ["الكل"] + list(employees_df["القسم"].unique()), key="employee_department_filter") |
|
with col2: |
|
position_filter = st.selectbox("تصفية حسب المنصب", ["الكل"] + list(employees_df["المنصب"].unique()), key="employee_position_filter") |
|
with col3: |
|
availability_filter = st.selectbox("تصفية حسب التوفر", ["الكل", "متاح", "غير متاح"], key="employee_availability_filter") |
|
|
|
filtered_df = employees_df.copy() |
|
if dept_filter != "الكل": |
|
filtered_df = filtered_df[filtered_df["القسم"] == dept_filter] |
|
if position_filter != "الكل": |
|
filtered_df = filtered_df[filtered_df["المنصب"] == position_filter] |
|
if availability_filter != "الكل": |
|
filtered_df = filtered_df[filtered_df["متاح"] == (availability_filter == "متاح")] |
|
|
|
st.dataframe(filtered_df, use_container_width=True) |
|
|
|
def _render_equipment_tab(self): |
|
"""عرض تبويب المعدات""" |
|
st.markdown("### المعدات") |
|
|
|
|
|
with st.expander("إضافة معدة جديدة"): |
|
equipment_form = st.form("add_equipment_form") |
|
with equipment_form: |
|
equipment_data = {} |
|
equipment_data['name'] = st.text_input("اسم المعدة") |
|
equipment_data['category'] = st.selectbox("الفئة", [ |
|
"معدات الحفر والردم", |
|
"معدات النقل", |
|
"معدات الرفع", |
|
"معدات الخرسانة", |
|
"معدات الطرق", |
|
"معدات الصرف الصحي", |
|
"معدات السيول والكباري", |
|
"معدات الضغط والتثبيت", |
|
"معدات التوليد والطاقة", |
|
"معدات القياس والمساحة" |
|
], key="equipment_category_selectbox") |
|
equipment_data['brand'] = st.text_input("الماركة") |
|
equipment_data['model'] = st.text_input("الموديل") |
|
equipment_data['capacity'] = st.text_input("السعة/القدرة") |
|
equipment_data['production_rate'] = st.text_input("معدل الإنتاجية") |
|
|
|
cols = st.columns(4) |
|
with cols[0]: |
|
equipment_data['hourly_cost'] = st.number_input("التكلفة بالساعة", min_value=0) |
|
with cols[1]: |
|
equipment_data['daily_cost'] = st.number_input("التكلفة باليوم", min_value=0) |
|
with cols[2]: |
|
equipment_data['weekly_cost'] = st.number_input("التكلفة بالأسبوع", min_value=0) |
|
with cols[3]: |
|
equipment_data['monthly_cost'] = st.number_input("التكلفة بالشهر", min_value=0) |
|
|
|
equipment_data['fuel_consumption'] = st.text_input("استهلاك الوقود") |
|
equipment_data['maintenance_period'] = st.text_input("فترة الصيانة") |
|
equipment_data['maintenance_cost'] = st.number_input("تكلفة الصيانة", min_value=0) |
|
equipment_data['operator_required'] = st.checkbox("تتطلب مشغل") |
|
equipment_data['description'] = st.text_area("الوصف") |
|
|
|
if st.form_submit_button("إضافة"): |
|
equipment_df = st.session_state.resources_data["equipment"] |
|
new_id = f"EQP-{len(equipment_df) + 1:03d}" |
|
equipment_data['id'] = new_id |
|
equipment_df.loc[len(equipment_df)] = equipment_data |
|
st.success("تمت إضافة المعدة بنجاح") |
|
|
|
|
|
equipment_df = st.session_state.resources_data["equipment"] |
|
|
|
|
|
col1, col2 = st.columns(2) |
|
with col1: |
|
category_filter = st.selectbox("تصفية حسب الفئة", ["الكل"] + list(equipment_df["النوع"].unique()), key="equipment_category_filter") |
|
with col2: |
|
availability_filter = st.selectbox("تصفية حسب التوفر", ["الكل", "متاح", "غير متاح"], key="equipment_availability_filter") |
|
|
|
filtered_df = equipment_df.copy() |
|
if category_filter != "الكل": |
|
filtered_df = filtered_df[filtered_df["النوع"] == category_filter] |
|
if availability_filter != "الكل": |
|
filtered_df = filtered_df[filtered_df["متاحة"] == (availability_filter == "متاح")] |
|
|
|
st.dataframe(filtered_df, use_container_width=True) |
|
|
|
|
|
st.markdown("### إحصائيات المعدات") |
|
col1, col2, col3 = st.columns(3) |
|
|
|
with col1: |
|
total_equipment = len(equipment_df) |
|
available_equipment = len(equipment_df[equipment_df["متاحة"] == True]) |
|
st.metric("إجمالي المعدات", f"{available_equipment}/{total_equipment}") |
|
|
|
with col2: |
|
avg_daily_cost = equipment_df["التكلفة اليومية"].mean() |
|
st.metric("متوسط التكلفة اليومية", f"{avg_daily_cost:.2f} ريال") |
|
|
|
with col3: |
|
maintenance_equipment = len(equipment_df[equipment_df["الموقع"] == "في الصيانة"]) |
|
st.metric("المعدات في الصيانة", maintenance_equipment) |
|
|
|
def _render_materials_tab(self): |
|
"""عرض تبويب المواد""" |
|
st.markdown("### المواد") |
|
|
|
|
|
with st.expander("إضافة مادة جديدة"): |
|
material_form = st.form("add_material_form") |
|
with material_form: |
|
material_data = {} |
|
material_data['name'] = st.text_input("اسم المادة") |
|
material_data['category'] = st.selectbox("الفئة", [ |
|
"مواد الخرسانة", |
|
"مواد البناء", |
|
"مواد الطرق", |
|
"مواد الصرف الصحي", |
|
"مواد العزل", |
|
"مواد التشطيبات", |
|
"مواد كهربائية", |
|
"مواد ميكانيكية", |
|
"مواد الري والزراعة" |
|
], key="material_category_selectbox") |
|
|
|
material_data['unit'] = st.selectbox("وحدة القياس", [ |
|
"م3", "م2", "متر طولي", "طن", "كجم", "لتر", "قطعة" |
|
], key="material_unit_selectbox") |
|
|
|
cols = st.columns(4) |
|
with cols[0]: |
|
material_data['quantity'] = st.number_input("الكمية المتاحة", min_value=0) |
|
with cols[1]: |
|
material_data['unit_cost'] = st.number_input("تكلفة الوحدة", min_value=0) |
|
with cols[2]: |
|
material_data['supplier'] = st.text_input("المورد") |
|
with cols[3]: |
|
material_data['lead_time'] = st.number_input("مدة التوريد (يوم)", min_value=1) |
|
|
|
material_data['specifications'] = st.text_area("المواصفات") |
|
material_data['notes'] = st.text_area("ملاحظات") |
|
|
|
if st.form_submit_button("إضافة"): |
|
materials_df = st.session_state.resources_data["materials"] |
|
new_id = f"MAT-{len(materials_df) + 1:03d}" |
|
material_data['id'] = new_id |
|
materials_df.loc[len(materials_df)] = material_data |
|
st.success("تمت إضافة المادة بنجاح") |
|
|
|
|
|
materials_df = st.session_state.resources_data["materials"] |
|
|
|
|
|
col1, col2 = st.columns(2) |
|
with col1: |
|
category_filter = st.selectbox("تصفية حسب الفئة", ["الكل"] + list(materials_df["اسم المادة"].unique()), key="material_category_filter") |
|
with col2: |
|
supplier_filter = st.selectbox("تصفية حسب المورد", ["الكل"] + list(materials_df["المورد"].unique()), key="material_supplier_filter") |
|
|
|
filtered_df = materials_df.copy() |
|
if category_filter != "الكل": |
|
filtered_df = filtered_df[filtered_df["اسم المادة"] == category_filter] |
|
if supplier_filter != "الكل": |
|
filtered_df = filtered_df[filtered_df["المورد"] == supplier_filter] |
|
|
|
st.dataframe(filtered_df, use_container_width=True) |
|
|
|
|
|
st.markdown("### إحصائيات المواد") |
|
col1, col2, col3 = st.columns(3) |
|
|
|
with col1: |
|
total_materials = len(materials_df) |
|
low_stock_materials = len(materials_df[materials_df["الكمية المتاحة"] < 50]) |
|
st.metric("إجمالي المواد", total_materials, f"-{low_stock_materials} منخفضة المخزون") |
|
|
|
with col2: |
|
total_value = (materials_df["الكمية المتاحة"] * materials_df["تكلفة الوحدة"]).sum() |
|
st.metric("القيمة الإجمالية للمخزون", f"{total_value:,.2f} ريال") |
|
|
|
with col3: |
|
avg_lead_time = materials_df["مدة التوريد (يوم)"].mean() |
|
st.metric("متوسط مدة التوريد", f"{avg_lead_time:.1f} يوم") |
|
|
|
def _render_resource_allocation_tab(self): |
|
"""عرض تبويب تخصيص الموارد""" |
|
st.markdown("### تخصيص الموارد") |
|
|
|
pass |
|
|
|
def _render_resource_planning_tab(self): |
|
"""عرض تبويب تخطيط الموارد""" |
|
st.markdown("### تخطيط الموارد") |
|
|
|
pass |