|
import unittest |
|
import sys |
|
import os |
|
import pandas as pd |
|
import numpy as np |
|
import streamlit as st |
|
from unittest.mock import patch, MagicMock |
|
|
|
|
|
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) |
|
|
|
|
|
from pricing_system.integration_framework import IntegrationFramework |
|
from pricing_system.modules.catalogs.equipment_catalog import EquipmentCatalog |
|
from pricing_system.modules.catalogs.materials_catalog import MaterialsCatalog |
|
from pricing_system.modules.catalogs.labor_catalog import LaborCatalog |
|
from pricing_system.modules.catalogs.subcontractors_catalog import SubcontractorsCatalog |
|
from pricing_system.modules.analysis.smart_price_analysis import SmartPriceAnalysis |
|
from pricing_system.modules.indirect_support.indirect_support_management import IndirectSupportManagement |
|
from pricing_system.modules.pricing_strategies.pricing_strategies import PricingStrategies |
|
|
|
class TestIntegratedSystem(unittest.TestCase): |
|
""" |
|
اختبارات النظام المتكامل للتأكد من عمل جميع المكونات معًا بشكل صحيح |
|
""" |
|
|
|
def setUp(self): |
|
""" |
|
إعداد بيئة الاختبار |
|
""" |
|
|
|
st.session_state = {} |
|
|
|
|
|
self.equipment_catalog = EquipmentCatalog() |
|
self.materials_catalog = MaterialsCatalog() |
|
self.labor_catalog = LaborCatalog() |
|
self.subcontractors_catalog = SubcontractorsCatalog() |
|
self.smart_price_analysis = SmartPriceAnalysis() |
|
self.indirect_support = IndirectSupportManagement() |
|
self.pricing_strategies = PricingStrategies() |
|
|
|
|
|
self.integration_framework = IntegrationFramework() |
|
|
|
|
|
self.project_data = { |
|
'name': 'مشروع اختبار', |
|
'location': 'الرياض', |
|
'client': 'وزارة الإسكان', |
|
'start_date': '2025-01-01', |
|
'end_date': '2025-12-31', |
|
'budget': 10000000.0, |
|
'boq_items': [ |
|
{ |
|
'item_code': 'B001', |
|
'description': 'حفر وردم', |
|
'unit': 'م3', |
|
'quantity': 1000.0, |
|
'unit_price': 50.0, |
|
'total_price': 50000.0 |
|
}, |
|
{ |
|
'item_code': 'B002', |
|
'description': 'خرسانة مسلحة', |
|
'unit': 'م3', |
|
'quantity': 500.0, |
|
'unit_price': 1200.0, |
|
'total_price': 600000.0 |
|
}, |
|
{ |
|
'item_code': 'B003', |
|
'description': 'أعمال تشطيبات', |
|
'unit': 'م2', |
|
'quantity': 2000.0, |
|
'unit_price': 300.0, |
|
'total_price': 600000.0 |
|
} |
|
], |
|
'resources': [], |
|
'pricing_strategy': 'standard', |
|
'indirect_costs': {}, |
|
'profit_margin': 0.15, |
|
'local_content_target': 0.40 |
|
} |
|
|
|
|
|
self._add_test_data_to_catalogs() |
|
|
|
def _add_test_data_to_catalogs(self): |
|
""" |
|
إضافة بيانات اختبار إلى الكتالوجات |
|
""" |
|
|
|
equipment_data = [ |
|
{ |
|
'name': 'حفار', |
|
'type': 'حفار', |
|
'description': 'حفار هيدروليكي', |
|
'price_per_hour': 200.0, |
|
'price_per_day': 1600.0, |
|
'price_per_week': 8000.0, |
|
'price_per_month': 30000.0, |
|
'is_local': True |
|
}, |
|
{ |
|
'name': 'لودر', |
|
'type': 'لودر', |
|
'description': 'لودر متوسط الحجم', |
|
'price_per_hour': 150.0, |
|
'price_per_day': 1200.0, |
|
'price_per_week': 6000.0, |
|
'price_per_month': 22000.0, |
|
'is_local': False |
|
}, |
|
{ |
|
'name': 'شاحنة نقل', |
|
'type': 'شاحنة', |
|
'description': 'شاحنة نقل ثقيلة', |
|
'price_per_hour': 120.0, |
|
'price_per_day': 960.0, |
|
'price_per_week': 4800.0, |
|
'price_per_month': 18000.0, |
|
'is_local': True |
|
} |
|
] |
|
|
|
for equipment in equipment_data: |
|
self.equipment_catalog.add_equipment(equipment['name'], equipment) |
|
|
|
|
|
materials_data = [ |
|
{ |
|
'name': 'اسمنت', |
|
'description': 'اسمنت بورتلاندي', |
|
'unit': 'طن', |
|
'price': 600.0, |
|
'is_local': True |
|
}, |
|
{ |
|
'name': 'حديد تسليح', |
|
'description': 'حديد تسليح قطر 16 مم', |
|
'unit': 'طن', |
|
'price': 3500.0, |
|
'is_local': True |
|
}, |
|
{ |
|
'name': 'رمل', |
|
'description': 'رمل ناعم للخرسانة', |
|
'unit': 'م3', |
|
'price': 80.0, |
|
'is_local': True |
|
}, |
|
{ |
|
'name': 'بلاط سيراميك', |
|
'description': 'بلاط سيراميك للأرضيات', |
|
'unit': 'م2', |
|
'price': 120.0, |
|
'is_local': False |
|
} |
|
] |
|
|
|
for material in materials_data: |
|
self.materials_catalog.add_material(material['name'], material) |
|
|
|
|
|
labor_data = [ |
|
{ |
|
'name': 'مهندس مدني', |
|
'type': 'مهندس مدني', |
|
'description': 'مهندس مدني خبرة 5 سنوات', |
|
'price_per_hour': 100.0, |
|
'price_per_day': 800.0, |
|
'price_per_week': 4000.0, |
|
'price_per_month': 15000.0, |
|
'is_local': True |
|
}, |
|
{ |
|
'name': 'عامل بناء', |
|
'type': 'عامل بناء', |
|
'description': 'عامل بناء ماهر', |
|
'price_per_hour': 20.0, |
|
'price_per_day': 160.0, |
|
'price_per_week': 800.0, |
|
'price_per_month': 3000.0, |
|
'is_local': False |
|
}, |
|
{ |
|
'name': 'فني كهرباء', |
|
'type': 'كهربائي', |
|
'description': 'فني كهرباء خبرة 3 سنوات', |
|
'price_per_hour': 30.0, |
|
'price_per_day': 240.0, |
|
'price_per_week': 1200.0, |
|
'price_per_month': 4500.0, |
|
'is_local': True |
|
} |
|
] |
|
|
|
for labor in labor_data: |
|
self.labor_catalog.add_labor(labor['name'], labor) |
|
|
|
|
|
subcontractors_data = [ |
|
{ |
|
'name': 'شركة الأعمال الكهربائية', |
|
'specialization': 'أعمال كهربائية', |
|
'description': 'شركة متخصصة في الأعمال الكهربائية', |
|
'contact_info': '[email protected]', |
|
'is_local': True |
|
}, |
|
{ |
|
'name': 'مؤسسة أنظمة التكييف', |
|
'specialization': 'أعمال تكييف', |
|
'description': 'مؤسسة متخصصة في أنظمة التكييف', |
|
'contact_info': '[email protected]', |
|
'is_local': True |
|
}, |
|
{ |
|
'name': 'شركة أنظمة المراقبة', |
|
'specialization': 'أعمال CCTV', |
|
'description': 'شركة متخصصة في أنظمة المراقبة والكاميرات', |
|
'contact_info': '[email protected]', |
|
'is_local': False |
|
} |
|
] |
|
|
|
for subcontractor in subcontractors_data: |
|
self.subcontractors_catalog.add_subcontractor(subcontractor['name'], subcontractor) |
|
|
|
|
|
departments_data = [ |
|
{ |
|
'name': 'إدارة المشاريع', |
|
'description': 'إدارة متابعة وتنفيذ المشاريع', |
|
'monthly_cost': 100000.0, |
|
'employees_count': 10, |
|
'allocation_percentage': 20.0 |
|
}, |
|
{ |
|
'name': 'إدارة المشتريات', |
|
'description': 'إدارة المشتريات والتوريدات', |
|
'monthly_cost': 80000.0, |
|
'employees_count': 8, |
|
'allocation_percentage': 15.0 |
|
}, |
|
{ |
|
'name': 'الإدارة المالية', |
|
'description': 'الإدارة المالية والمحاسبة', |
|
'monthly_cost': 70000.0, |
|
'employees_count': 7, |
|
'allocation_percentage': 10.0 |
|
} |
|
] |
|
|
|
for department in departments_data: |
|
self.indirect_support.add_department(department['name'], department) |
|
|
|
def test_equipment_catalog_integration(self): |
|
""" |
|
اختبار تكامل كتالوج المعدات |
|
""" |
|
|
|
equipment_list = self.equipment_catalog.get_equipment_list() |
|
self.assertEqual(len(equipment_list), 3) |
|
self.assertIn('حفار', equipment_list) |
|
self.assertIn('لودر', equipment_list) |
|
self.assertIn('شاحنة نقل', equipment_list) |
|
|
|
|
|
حفار_details = self.equipment_catalog.get_equipment_details('حفار') |
|
self.assertEqual(حفار_details['price_per_day'], 1600.0) |
|
self.assertTrue(حفار_details['is_local']) |
|
|
|
لودر_details = self.equipment_catalog.get_equipment_details('لودر') |
|
self.assertEqual(لودر_details['price_per_day'], 1200.0) |
|
self.assertFalse(لودر_details['is_local']) |
|
|
|
def test_materials_catalog_integration(self): |
|
""" |
|
اختبار تكامل كتالوج المواد |
|
""" |
|
|
|
materials_list = self.materials_catalog.get_materials_list() |
|
self.assertEqual(len(materials_list), 4) |
|
self.assertIn('اسمنت', materials_list) |
|
self.assertIn('حديد تسليح', materials_list) |
|
self.assertIn('رمل', materials_list) |
|
self.assertIn('بلاط سيراميك', materials_list) |
|
|
|
|
|
اسمنت_details = self.materials_catalog.get_material_details('اسمنت') |
|
self.assertEqual(اسمنت_details['price'], 600.0) |
|
self.assertEqual(اسمنت_details['unit'], 'طن') |
|
self.assertTrue(اسمنت_details['is_local']) |
|
|
|
بلاط_details = self.materials_catalog.get_material_details('بلاط سيراميك') |
|
self.assertEqual(بلاط_details['price'], 120.0) |
|
self.assertEqual(بلاط_details['unit'], 'م2') |
|
self.assertFalse(بلاط_details['is_local']) |
|
|
|
def test_labor_catalog_integration(self): |
|
""" |
|
اختبار تكامل كتالوج العمالة |
|
""" |
|
|
|
labor_list = self.labor_catalog.get_labor_list() |
|
self.assertEqual(len(labor_list), 3) |
|
self.assertIn('مهندس مدني', labor_list) |
|
self.assertIn('عامل بناء', labor_list) |
|
self.assertIn('فني كهرباء', labor_list) |
|
|
|
|
|
مهندس_details = self.labor_catalog.get_labor_details('مهندس مدني') |
|
self.assertEqual(مهندس_details['price_per_month'], 15000.0) |
|
self.assertTrue(مهندس_details['is_local']) |
|
|
|
عامل_details = self.labor_catalog.get_labor_details('عامل بناء') |
|
self.assertEqual(عامل_details['price_per_day'], 160.0) |
|
self.assertFalse(عامل_details['is_local']) |
|
|
|
def test_subcontractors_catalog_integration(self): |
|
""" |
|
اختبار تكامل كتالوج مقاولي الباطن |
|
""" |
|
|
|
subcontractors_list = self.subcontractors_catalog.get_subcontractors_list() |
|
self.assertEqual(len(subcontractors_list), 3) |
|
self.assertIn('شركة الأعمال الكهربائية', subcontractors_list) |
|
self.assertIn('مؤسسة أنظمة التكييف', subcontractors_list) |
|
self.assertIn('شركة أنظمة المراقبة', subcontractors_list) |
|
|
|
|
|
كهرباء_details = self.subcontractors_catalog.get_subcontractor_details('شركة الأعمال الكهربائية') |
|
self.assertEqual(كهرباء_details['specialization'], 'أعمال كهربائية') |
|
self.assertTrue(كهرباء_details['is_local']) |
|
|
|
مراقبة_details = self.subcontractors_catalog.get_subcontractor_details('شركة أنظمة المراقبة') |
|
self.assertEqual(مراقبة_details['specialization'], 'أعمال CCTV') |
|
self.assertFalse(مراقبة_details['is_local']) |
|
|
|
def test_indirect_support_integration(self): |
|
""" |
|
اختبار تكامل إدارة الإدارات المساندة |
|
""" |
|
|
|
departments = self.indirect_support.get_departments() |
|
self.assertEqual(len(departments), 3) |
|
self.assertIn('إدارة المشاريع', departments) |
|
self.assertIn('إدارة المشتريات', departments) |
|
self.assertIn('الإدارة المالية', departments) |
|
|
|
|
|
مشاريع_details = self.indirect_support.get_department_details('إدارة المشاريع') |
|
self.assertEqual(مشاريع_details['monthly_cost'], 100000.0) |
|
self.assertEqual(مشاريع_details['allocation_percentage'], 20.0) |
|
|
|
|
|
total_monthly_cost = self.indirect_support.get_total_monthly_cost() |
|
self.assertEqual(total_monthly_cost, 250000.0) |
|
|
|
total_allocated_cost = self.indirect_support.get_total_allocated_cost() |
|
self.assertEqual(total_allocated_cost, 45000.0) |
|
|
|
def test_smart_price_analysis_integration(self): |
|
""" |
|
اختبار تكامل التحليل الذكي للأسعار |
|
""" |
|
|
|
for i, item in enumerate(self.project_data['boq_items']): |
|
analysis = { |
|
'direct_cost': item['total_price'] * 0.7, |
|
'indirect_cost': item['total_price'] * 0.15, |
|
'profit_margin': item['total_price'] * 0.15, |
|
'total_price': item['total_price'], |
|
'materials': [ |
|
{ |
|
'name': 'اسمنت', |
|
'quantity': 10, |
|
'unit': 'طن', |
|
'price': 600.0, |
|
'total': 6000.0, |
|
'is_local': True |
|
}, |
|
{ |
|
'name': 'حديد تسليح', |
|
'quantity': 5, |
|
'unit': 'طن', |
|
'price': 3500.0, |
|
'total': 17500.0, |
|
'is_local': True |
|
} |
|
], |
|
'equipment': [ |
|
{ |
|
'name': 'حفار', |
|
'duration': 5, |
|
'duration_unit': 'يوم', |
|
'price': 1600.0, |
|
'total': 8000.0, |
|
'is_local': True |
|
} |
|
], |
|
'labor': [ |
|
{ |
|
'name': 'عامل بناء', |
|
'duration': 20, |
|
'duration_unit': 'يوم', |
|
'price': 160.0, |
|
'total': 3200.0, |
|
'is_local': False |
|
} |
|
], |
|
'materials_cost': 23500.0, |
|
'equipment_cost': 8000.0, |
|
'labor_cost': 3200.0, |
|
'subcontractors_cost': 0.0 |
|
} |
|
|
|
self.smart_price_analysis.add_item_analysis(i, analysis) |
|
|
|
|
|
all_analyses = self.smart_price_analysis.get_all_items_analysis() |
|
self.assertEqual(len(all_analyses), 3) |
|
|
|
|
|
cost_analysis = self.smart_price_analysis.get_project_cost_analysis() |
|
self.assertEqual(cost_analysis['total_materials_cost'], 23500.0 * 3) |
|
self.assertEqual(cost_analysis['total_equipment_cost'], 8000.0 * 3) |
|
self.assertEqual(cost_analysis['total_labor_cost'], 3200.0 * 3) |
|
|
|
|
|
top_materials = self.smart_price_analysis.get_top_materials(limit=2) |
|
self.assertEqual(len(top_materials), 2) |
|
self.assertEqual(top_materials[0]['name'], 'حديد تسليح') |
|
self.assertEqual(top_materials[0]['total_cost'], 17500.0 * 3) |
|
|
|
def test_pricing_strategies_integration(self): |
|
""" |
|
اختبار تكامل استراتيجيات التسعير |
|
""" |
|
|
|
result = self.pricing_strategies.apply_strategy( |
|
'standard', |
|
self.project_data, |
|
self.smart_price_analysis |
|
) |
|
|
|
self.assertTrue(result['success']) |
|
self.assertEqual(len(result['items_result']), 3) |
|
|
|
|
|
total_cost = sum(item['cost'] for item in result['items_result']) |
|
total_price = sum(item['price'] for item in result['items_result']) |
|
profit_margin = total_price - total_cost |
|
|
|
self.assertEqual(result['total_cost'], total_cost) |
|
self.assertEqual(result['total_price'], total_price) |
|
self.assertEqual(result['profit_margin'], profit_margin) |
|
|
|
|
|
comparison_result = self.pricing_strategies.compare_strategies( |
|
self.project_data, |
|
self.smart_price_analysis |
|
) |
|
|
|
self.assertTrue(comparison_result['success']) |
|
self.assertEqual(len(comparison_result['strategies_result']), 6) |
|
|
|
def test_local_content_analysis_integration(self): |
|
""" |
|
اختبار تكامل تحليل المحتوى المحلي |
|
""" |
|
|
|
local_content_analysis = self.pricing_strategies.analyze_local_content( |
|
self.project_data, |
|
self.smart_price_analysis |
|
) |
|
|
|
self.assertTrue(local_content_analysis['success']) |
|
|
|
|
|
self.assertIn('local_content_percentage', local_content_analysis) |
|
self.assertIn('resources_local_content', local_content_analysis) |
|
|
|
|
|
resources_local_content = local_content_analysis['resources_local_content'] |
|
self.assertIn('materials', resources_local_content) |
|
self.assertIn('equipment', resources_local_content) |
|
self.assertIn('labor', resources_local_content) |
|
|
|
|
|
if local_content_analysis['local_content_percentage'] < self.project_data['local_content_target']: |
|
self.assertIn('recommendations', local_content_analysis) |
|
self.assertTrue(len(local_content_analysis['recommendations']) > 0) |
|
|
|
def test_integration_framework(self): |
|
""" |
|
اختبار إطار التكامل |
|
""" |
|
|
|
st.session_state = {} |
|
|
|
|
|
pricing_app_mock = MagicMock() |
|
pricing_app_mock.tabs = ["جدول الكميات", "تحليل التكاليف", "سيناريوهات التسعير", "التحليل التنافسي", "التقارير"] |
|
pricing_app_mock._render_bill_of_quantities_tab = MagicMock() |
|
pricing_app_mock._render_cost_analysis_tab = MagicMock() |
|
pricing_app_mock._render_pricing_scenarios_tab = MagicMock() |
|
pricing_app_mock._render_competitive_analysis_tab = MagicMock() |
|
pricing_app_mock._render_reports_tab = MagicMock() |
|
|
|
resources_app_mock = MagicMock() |
|
|
|
|
|
self.integration_framework.connect_pricing_app(pricing_app_mock) |
|
self.integration_framework.connect_resources_app(resources_app_mock) |
|
|
|
|
|
self.assertEqual(len(pricing_app_mock.tabs), 8) |
|
self.assertIn("كتالوجات الموارد", pricing_app_mock.tabs) |
|
self.assertIn("الإدارات المساندة", pricing_app_mock.tabs) |
|
self.assertIn("المحتوى المحلي", pricing_app_mock.tabs) |
|
|
|
|
|
self.assertTrue(hasattr(pricing_app_mock, '_render_resource_catalogs_tab')) |
|
self.assertTrue(hasattr(pricing_app_mock, '_render_indirect_support_tab')) |
|
self.assertTrue(hasattr(pricing_app_mock, '_render_local_content_tab')) |
|
|
|
if __name__ == '__main__': |
|
unittest.main() |
|
|