""" كتالوج بنود نموذجية للمقاولات يحتوي هذا الملف على قائمة كاملة من النماذج الجاهزة للبنود الشائعة في مشاريع المقاولات، مثل: - أعمال الخرسانة بأنواعها - المناهل وأنواع المواسير - التركيبات المختلفة - الطرق والأسفلت - وغيرها من أعمال المقاولات """ import os import json import sys from typing import Dict, List, Any, Optional from datetime import datetime # إضافة مسار النظام للوصول لملفات التكوين sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "../../../"))) try: import config except ImportError: # إذا لم يتم العثور على ملف التكوين، نستخدم قيم افتراضية class DefaultConfig: DATA_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), "../../../data")) config = DefaultConfig # إنشاء مجلد البيانات إذا لم يكن موجودًا if not os.path.exists(config.DATA_DIR): os.makedirs(config.DATA_DIR) class ConstructionTemplates: """كتالوج بنود نموذجية للمقاولات""" def __init__(self): """تهيئة كتالوج البنود النموذجية""" self.templates_file = os.path.join(config.DATA_DIR, 'construction_templates.json') self.market_prices_file = os.path.join(config.DATA_DIR, 'saudi_market_prices.json') # تحميل قوالب البنود النموذجية self.templates = self._load_templates() # تحميل أسعار السوق السعودي self.market_prices = self._load_market_prices() def _load_templates(self) -> Dict[str, Dict[str, Any]]: """تحميل قوالب البنود النموذجية من الملف""" if os.path.exists(self.templates_file): try: with open(self.templates_file, 'r', encoding='utf-8') as f: return json.load(f) except Exception as e: print(f"خطأ في تحميل قوالب البنود النموذجية: {str(e)}") # إنشاء بيانات افتراضية إذا لم يتم العثور على الملف default_templates = self._create_default_templates() # حفظ البيانات الافتراضية self._save_templates(default_templates) return default_templates def _save_templates(self, templates: Dict[str, Dict[str, Any]]) -> None: """حفظ قوالب البنود النموذجية إلى الملف""" try: with open(self.templates_file, 'w', encoding='utf-8') as f: json.dump(templates, f, ensure_ascii=False, indent=4) except Exception as e: print(f"خطأ في حفظ قوالب البنود النموذجية: {str(e)}") def _load_market_prices(self) -> Dict[str, Dict[str, Any]]: """تحميل أسعار السوق السعودي من الملف""" if os.path.exists(self.market_prices_file): try: with open(self.market_prices_file, 'r', encoding='utf-8') as f: return json.load(f) except Exception as e: print(f"خطأ في تحميل أسعار السوق السعودي: {str(e)}") # إنشاء بيانات افتراضية إذا لم يتم العثور على الملف default_prices = self._create_default_market_prices() # حفظ البيانات الافتراضية self._save_market_prices(default_prices) return default_prices def _save_market_prices(self, prices: Dict[str, Dict[str, Any]]) -> None: """حفظ أسعار السوق السعودي إلى الملف""" try: with open(self.market_prices_file, 'w', encoding='utf-8') as f: json.dump(prices, f, ensure_ascii=False, indent=4) except Exception as e: print(f"خطأ في حفظ أسعار السوق السعودي: {str(e)}") def _create_default_templates(self) -> Dict[str, Dict[str, Any]]: """إنشاء قوالب افتراضية للبنود النموذجية""" templates = { "categories": { "أعمال_خرسانية": { "name": "أعمال خرسانية", "description": "بنود أعمال الخرسانة المسلحة والعادية", "icon": "building" }, "أعمال_صحية": { "name": "أعمال صحية", "description": "بنود أعمال المناهل والمواسير والتركيبات الصحية", "icon": "pipe" }, "أعمال_طرق": { "name": "أعمال طرق", "description": "بنود أعمال الطرق والأسفلت والرصف", "icon": "road" }, "أعمال_كهربائية": { "name": "أعمال كهربائية", "description": "بنود أعمال الكهرباء والإنارة", "icon": "zap" }, "أعمال_ميكانيكية": { "name": "أعمال ميكانيكية", "description": "بنود أعمال التكييف والتهوية والتبريد", "icon": "thermometer" } }, "templates": { # نماذج أعمال خرسانية "خرسانة_مسلحة_أساسات": { "category": "أعمال_خرسانية", "name": "خرسانة مسلحة للأساسات", "description": "توريد وصب خرسانة مسلحة للأساسات بقوة لا تقل عن 300 كجم/سم2", "unit": "م3", "components": { "materials": [ {"الاسم": "خرسانة جاهزة", "الكمية": 1.0, "الوحدة": "م3", "سعر_الوحدة": 750.0}, {"الاسم": "حديد تسليح", "الكمية": 0.12, "الوحدة": "طن", "سعر_الوحدة": 5500.0} ], "labor": [ {"النوع": "عامل خرسانة", "العدد": 4, "المدة": 0.3, "سعر_اليوم": 150.0}, {"النوع": "نجار مسلح", "العدد": 2, "المدة": 0.5, "سعر_اليوم": 250.0}, {"النوع": "حداد مسلح", "العدد": 2, "المدة": 0.5, "سعر_اليوم": 250.0} ], "equipment": [ {"النوع": "هزاز خرسانة", "العدد": 1, "المدة": 0.3, "سعر_اليوم": 150.0} ] }, "admin_expenses": 0.05, "profit_margin": 0.10, "tags": ["خرسانة", "أساسات", "مسلحة"] }, "خرسانة_مسلحة_أعمدة": { "category": "أعمال_خرسانية", "name": "خرسانة مسلحة للأعمدة", "description": "توريد وصب خرسانة مسلحة للأعمدة بقوة لا تقل عن 350 كجم/سم2", "unit": "م3", "components": { "materials": [ {"الاسم": "خرسانة جاهزة", "الكمية": 1.0, "الوحدة": "م3", "سعر_الوحدة": 750.0}, {"الاسم": "حديد تسليح", "الكمية": 0.18, "الوحدة": "طن", "سعر_الوحدة": 5500.0} ], "labor": [ {"النوع": "عامل خرسانة", "العدد": 4, "المدة": 0.4, "سعر_اليوم": 150.0}, {"النوع": "نجار مسلح", "العدد": 2, "المدة": 0.7, "سعر_اليوم": 250.0}, {"النوع": "حداد مسلح", "العدد": 2, "المدة": 0.7, "سعر_اليوم": 250.0} ], "equipment": [ {"النوع": "هزاز خرسانة", "العدد": 2, "المدة": 0.4, "سعر_اليوم": 150.0} ] }, "admin_expenses": 0.05, "profit_margin": 0.10, "tags": ["خرسانة", "أعمدة", "مسلحة"] }, "خرسانة_مسلحة_أسقف": { "category": "أعمال_خرسانية", "name": "خرسانة مسلحة للأسقف", "description": "توريد وصب خرسانة مسلحة للأسقف والبلاطات بقوة لا تقل عن 350 كجم/سم2", "unit": "م3", "components": { "materials": [ {"الاسم": "خرسانة جاهزة", "الكمية": 1.0, "الوحدة": "م3", "سعر_الوحدة": 750.0}, {"الاسم": "حديد تسليح", "الكمية": 0.16, "الوحدة": "طن", "سعر_الوحدة": 5500.0} ], "labor": [ {"النوع": "عامل خرسانة", "العدد": 5, "المدة": 0.5, "سعر_اليوم": 150.0}, {"النوع": "نجار مسلح", "العدد": 3, "المدة": 0.8, "سعر_اليوم": 250.0}, {"النوع": "حداد مسلح", "العدد": 3, "المدة": 0.8, "سعر_اليوم": 250.0} ], "equipment": [ {"النوع": "هزاز خرسانة", "العدد": 2, "المدة": 0.5, "سعر_اليوم": 150.0} ] }, "admin_expenses": 0.05, "profit_margin": 0.10, "tags": ["خرسانة", "أسقف", "بلاطات", "مسلحة"] }, # نماذج أعمال صحية "منهل_تفتيش_خرساني": { "category": "أعمال_صحية", "name": "منهل تفتيش خرساني", "description": "توريد وتركيب منهل تفتيش خرساني قطر 1 متر وعمق 2 متر", "unit": "عدد", "components": { "materials": [ {"الاسم": "خرسانة جاهزة", "الكمية": 1.5, "الوحدة": "م3", "سعر_الوحدة": 750.0}, {"الاسم": "حديد تسليح", "الكمية": 0.15, "الوحدة": "طن", "سعر_الوحدة": 5500.0}, {"الاسم": "غطاء منهل حديد", "الكمية": 1, "الوحدة": "عدد", "سعر_الوحدة": 1500.0} ], "labor": [ {"النوع": "عامل خرسانة", "العدد": 3, "المدة": 1, "سعر_اليوم": 150.0}, {"النوع": "نجار مسلح", "العدد": 2, "المدة": 1, "سعر_اليوم": 250.0}, {"النوع": "حداد مسلح", "العدد": 1, "المدة": 1, "سعر_اليوم": 250.0}, {"النوع": "سباك", "العدد": 2, "المدة": 1, "سعر_اليوم": 250.0} ], "equipment": [ {"النوع": "حفار صغير", "العدد": 1, "المدة": 0.5, "سعر_اليوم": 1200.0} ] }, "admin_expenses": 0.05, "profit_margin": 0.12, "tags": ["صرف صحي", "منهل", "تفتيش"] }, "مواسير_بلاستيك_قطر_200_مم": { "category": "أعمال_صحية", "name": "مواسير بلاستيك قطر 200 مم", "description": "توريد وتركيب مواسير بلاستيك UPVC قطر 200 مم لشبكات الصرف الصحي", "unit": "م.ط", "components": { "materials": [ {"الاسم": "مواسير بلاستيك UPVC قطر 200 مم", "الكمية": 1.05, "الوحدة": "م.ط", "سعر_الوحدة": 180.0}, {"الاسم": "وصلات ومثبتات", "الكمية": 1, "الوحدة": "مجموعة", "سعر_الوحدة": 35.0}, {"الاسم": "مواد لاصقة", "الكمية": 0.1, "الوحدة": "لتر", "سعر_الوحدة": 120.0} ], "labor": [ {"النوع": "سباك", "العدد": 2, "المدة": 0.2, "سعر_اليوم": 250.0}, {"النوع": "مساعد سباك", "العدد": 2, "المدة": 0.2, "سعر_اليوم": 120.0} ], "equipment": [ {"النوع": "حفار صغير", "العدد": 1, "المدة": 0.1, "سعر_اليوم": 1200.0} ] }, "admin_expenses": 0.05, "profit_margin": 0.12, "tags": ["صرف صحي", "مواسير", "بلاستيك"] }, # نماذج أعمال طرق "طبقة_أساس_للطرق": { "category": "أعمال_طرق", "name": "طبقة أساس للطرق", "description": "توريد وفرد ودمك طبقة أساس للطرق سمك 20 سم، درجة دمك 98%", "unit": "م3", "components": { "materials": [ {"الاسم": "مواد طبقة أساس", "الكمية": 1.25, "الوحدة": "م3", "سعر_الوحدة": 90.0}, {"الاسم": "مياه للدمك", "الكمية": 0.2, "الوحدة": "م3", "سعر_الوحدة": 10.0} ], "labor": [ {"النوع": "عامل طرق", "العدد": 4, "المدة": 0.05, "سعر_اليوم": 150.0}, {"النوع": "مراقب فني", "العدد": 1, "المدة": 0.05, "سعر_اليوم": 300.0} ], "equipment": [ {"النوع": "جريدر", "العدد": 1, "المدة": 0.05, "سعر_اليوم": 2200.0}, {"النوع": "رصاصة دمك", "العدد": 1, "المدة": 0.05, "سعر_اليوم": 1800.0}, {"النوع": "شاحنة نقل", "العدد": 2, "المدة": 0.05, "سعر_اليوم": 1200.0} ] }, "admin_expenses": 0.05, "profit_margin": 0.12, "tags": ["طرق", "أساس", "دمك"] }, "طبقة_إسفلت_سطحية": { "category": "أعمال_طرق", "name": "طبقة إسفلت سطحية", "description": "توريد وفرد ودمك طبقة إسفلت سطحية سمك 5 سم", "unit": "م2", "components": { "materials": [ {"الاسم": "خلطة إسفلتية ساخنة", "الكمية": 0.125, "الوحدة": "طن", "سعر_الوحدة": 400.0}, {"الاسم": "مواد رش تأسيسي", "الكمية": 0.5, "الوحدة": "لتر", "سعر_الوحدة": 8.0} ], "labor": [ {"النوع": "عامل طرق", "العدد": 6, "المدة": 0.01, "سعر_اليوم": 150.0}, {"النوع": "مراقب فني", "العدد": 1, "المدة": 0.01, "سعر_اليوم": 300.0} ], "equipment": [ {"النوع": "فرادة إسفلت", "العدد": 1, "المدة": 0.01, "سعر_اليوم": 4000.0}, {"النوع": "رصاصة دمك", "العدد": 2, "المدة": 0.01, "سعر_اليوم": 1800.0}, {"النوع": "سيارة رش إسفلت", "العدد": 1, "المدة": 0.01, "سعر_اليوم": 2000.0}, {"النوع": "شاحنة نقل", "العدد": 4, "المدة": 0.01, "سعر_اليوم": 1200.0} ] }, "admin_expenses": 0.05, "profit_margin": 0.12, "tags": ["طرق", "إسفلت", "سطحية"] }, # نماذج أعمال كهربائية "عمود_إنارة_10_متر": { "category": "أعمال_كهربائية", "name": "عمود إنارة 10 متر", "description": "توريد وتركيب عمود إنارة جلفانيزي بارتفاع 10 متر مع ذراع مفردة وكشاف LED بقدرة 150 واط", "unit": "عدد", "components": { "materials": [ {"الاسم": "عمود إنارة جلفانيزي 10 متر", "الكمية": 1, "الوحدة": "عدد", "سعر_الوحدة": 3500.0}, {"الاسم": "ذراع إنارة مفردة", "الكمية": 1, "الوحدة": "عدد", "سعر_الوحدة": 450.0}, {"الاسم": "كشاف LED 150 واط", "الكمية": 1, "الوحدة": "عدد", "سعر_الوحدة": 850.0}, {"الاسم": "كابل كهرباء 3×4 مم²", "الكمية": 15, "الوحدة": "م.ط", "سعر_الوحدة": 32.0}, {"الاسم": "قاعدة خرسانية مسلحة", "الكمية": 0.25, "الوحدة": "م3", "سعر_الوحدة": 750.0} ], "labor": [ {"النوع": "كهربائي", "العدد": 2, "المدة": 1, "سعر_اليوم": 270.0}, {"النوع": "مساعد كهربائي", "العدد": 2, "المدة": 1, "سعر_اليوم": 120.0}, {"النوع": "عامل خرسانة", "العدد": 2, "المدة": 0.5, "سعر_اليوم": 150.0} ], "equipment": [ {"النوع": "ونش شوكة", "العدد": 1, "المدة": 0.5, "سعر_اليوم": 1500.0}, {"النوع": "حفار صغير", "العدد": 1, "المدة": 0.2, "سعر_اليوم": 1200.0} ] }, "admin_expenses": 0.05, "profit_margin": 0.12, "tags": ["كهرباء", "إنارة", "LED"] } } } return templates def _create_default_market_prices(self) -> Dict[str, Dict[str, Any]]: """إنشاء بيانات افتراضية لأسعار السوق السعودي""" current_date = datetime.now().strftime("%Y-%m-%d") prices = { "metadata": { "last_update": current_date, "source": "أسعار السوق السعودي الافتراضية", "disclaimer": "هذه الأسعار تقريبية وقد تختلف حسب المنطقة والكميات والموردين" }, "materials": { # مواد الخرسانة "خرسانة_جاهزة": { "name": "خرسانة جاهزة", "unit": "م3", "current_price": 750.0, "previous_price": 730.0, "price_trend": "up", "category": "أعمال خرسانية", "specifications": "خرسانة جاهزة بقوة 350 كجم/سم2", "note": "السعر يشمل توريد فقط، الضخ بتكلفة إضافية", "price_history": [ {"date": "2023-06-01", "price": 700.0}, {"date": "2023-09-01", "price": 715.0}, {"date": "2023-12-01", "price": 730.0}, {"date": current_date, "price": 750.0} ] }, "حديد_تسليح": { "name": "حديد تسليح", "unit": "طن", "current_price": 5500.0, "previous_price": 5200.0, "price_trend": "up", "category": "أعمال خرسانية", "specifications": "حديد تسليح قطر 8-32 مم، انتاج سابك", "note": "السعر يتغير بشكل دوري حسب أسعار الحديد العالمية", "price_history": [ {"date": "2023-06-01", "price": 4800.0}, {"date": "2023-09-01", "price": 5000.0}, {"date": "2023-12-01", "price": 5200.0}, {"date": current_date, "price": 5500.0} ] }, "أسمنت": { "name": "أسمنت", "unit": "كيس", "current_price": 30.0, "previous_price": 28.0, "price_trend": "up", "category": "أعمال خرسانية", "specifications": "أسمنت بورتلاندي عادي، كيس 50 كجم", "note": "السعر للكميات الكبيرة", "price_history": [ {"date": "2023-06-01", "price": 25.0}, {"date": "2023-09-01", "price": 27.0}, {"date": "2023-12-01", "price": 28.0}, {"date": current_date, "price": 30.0} ] }, # مواد الطرق والإسفلت "خلطة_إسفلتية_ساخنة": { "name": "خلطة إسفلتية ساخنة", "unit": "طن", "current_price": 400.0, "previous_price": 380.0, "price_trend": "up", "category": "أعمال طرق", "specifications": "خلطة إسفلتية ساخنة للطبقة السطحية", "note": "السعر يشمل التوريد من المصنع، النقل بتكلفة إضافية", "price_history": [ {"date": "2023-06-01", "price": 350.0}, {"date": "2023-09-01", "price": 370.0}, {"date": "2023-12-01", "price": 380.0}, {"date": current_date, "price": 400.0} ] }, # مواد صحية "مواسير_بلاستيك_UPVC": { "name": "مواسير بلاستيك UPVC قطر 200 مم", "unit": "م.ط", "current_price": 180.0, "previous_price": 165.0, "price_trend": "up", "category": "أعمال صحية", "specifications": "مواسير بلاستيك UPVC قطر 200 مم لشبكات الصرف الصحي", "note": "السعر للكميات الكبيرة", "price_history": [ {"date": "2023-06-01", "price": 150.0}, {"date": "2023-09-01", "price": 160.0}, {"date": "2023-12-01", "price": 165.0}, {"date": current_date, "price": 180.0} ] }, # مواد كهربائية "كشاف_LED": { "name": "كشاف LED 150 واط", "unit": "عدد", "current_price": 850.0, "previous_price": 820.0, "price_trend": "up", "category": "أعمال كهربائية", "specifications": "كشاف إنارة LED بقدرة 150 واط للاستخدام الخارجي، IP65", "note": "السعر شامل الضريبة", "price_history": [ {"date": "2023-06-01", "price": 780.0}, {"date": "2023-09-01", "price": 800.0}, {"date": "2023-12-01", "price": 820.0}, {"date": current_date, "price": 850.0} ] } }, "labor": { "عامل_خرسانة": { "name": "عامل خرسانة", "unit": "يوم", "current_price": 150.0, "previous_price": 140.0, "price_trend": "up", "category": "عمالة", "specifications": "عامل لصب وتسوية الخرسانة", "note": "أجرة اليوم الواحد لا تشمل السكن والمواصلات", "price_history": [ {"date": "2023-06-01", "price": 130.0}, {"date": "2023-09-01", "price": 135.0}, {"date": "2023-12-01", "price": 140.0}, {"date": current_date, "price": 150.0} ] }, "مهندس_موقع": { "name": "مهندس موقع", "unit": "يوم", "current_price": 500.0, "previous_price": 480.0, "price_trend": "up", "category": "إشراف", "specifications": "مهندس إشراف موقع", "note": "أجرة اليوم الواحد لا تشمل السكن والمواصلات", "price_history": [ {"date": "2023-06-01", "price": 450.0}, {"date": "2023-09-01", "price": 470.0}, {"date": "2023-12-01", "price": 480.0}, {"date": current_date, "price": 500.0} ] } }, "equipment": { "حفار_صغير": { "name": "حفار صغير", "unit": "يوم", "current_price": 1200.0, "previous_price": 1150.0, "price_trend": "up", "category": "معدات حفر", "specifications": "حفار صغير (بوبكات) بقدرة 70 حصان", "note": "السعر يشمل المشغل والوقود", "price_history": [ {"date": "2023-06-01", "price": 1100.0}, {"date": "2023-09-01", "price": 1120.0}, {"date": "2023-12-01", "price": 1150.0}, {"date": current_date, "price": 1200.0} ] }, "فرادة_إسفلت": { "name": "فرادة إسفلت", "unit": "يوم", "current_price": 4000.0, "previous_price": 3800.0, "price_trend": "up", "category": "معدات طرق", "specifications": "فرادة إسفلت بعرض 3 متر", "note": "السعر يشمل المشغل والوقود", "price_history": [ {"date": "2023-06-01", "price": 3500.0}, {"date": "2023-09-01", "price": 3650.0}, {"date": "2023-12-01", "price": 3800.0}, {"date": current_date, "price": 4000.0} ] } } } return prices def get_all_templates(self) -> Dict[str, Dict[str, Any]]: """الحصول على جميع القوالب النموذجية""" return self.templates def get_templates_by_category(self, category_id: str) -> List[Dict[str, Any]]: """الحصول على القوالب النموذجية حسب الفئة""" result = [] # التحقق من وجود الفئة if category_id not in self.templates["categories"]: return result # جمع القوالب التي تنتمي إلى الفئة المحددة for template_id, template in self.templates["templates"].items(): if template["category"] == category_id: template_copy = template.copy() template_copy["id"] = template_id result.append(template_copy) return result def get_template_by_id(self, template_id: str) -> Optional[Dict[str, Any]]: """الحصول على قالب نموذجي بواسطة المعرف""" if template_id in self.templates["templates"]: template = self.templates["templates"][template_id].copy() template["id"] = template_id return template return None def add_template(self, template_data: Dict[str, Any]) -> str: """إضافة قالب نموذجي جديد""" # إنشاء معرف فريد للقالب template_name = template_data.get("name", "").strip() if not template_name: raise ValueError("يجب تحديد اسم القالب") # تحويل الاسم إلى معرف (باستبدال المسافات بالشرطات السفلية وإزالة الأحرف الخاصة) import re template_id = re.sub(r'[^\w\s]', '', template_name) template_id = template_id.replace(" ", "_") # إضافة رقم عشوائي لتجنب التكرار import random if template_id in self.templates["templates"]: template_id = f"{template_id}_{random.randint(1000, 9999)}" # إضافة القالب إلى القائمة self.templates["templates"][template_id] = template_data # حفظ التغييرات self._save_templates(self.templates) return template_id def update_template(self, template_id: str, template_data: Dict[str, Any]) -> bool: """تحديث قالب نموذجي موجود""" if template_id not in self.templates["templates"]: return False # تحديث القالب self.templates["templates"][template_id] = template_data # حفظ التغييرات self._save_templates(self.templates) return True def delete_template(self, template_id: str) -> bool: """حذف قالب نموذجي""" if template_id not in self.templates["templates"]: return False # حذف القالب del self.templates["templates"][template_id] # حفظ التغييرات self._save_templates(self.templates) return True def get_market_prices(self, category: Optional[str] = None, item_type: Optional[str] = None) -> Dict[str, Any]: """الحصول على أسعار السوق السعودي""" result = { "metadata": self.market_prices["metadata"] } # تحديد نوع العناصر المطلوبة sections = [] if item_type: if item_type in ["materials", "المواد"]: sections = ["materials"] elif item_type in ["labor", "العمالة"]: sections = ["labor"] elif item_type in ["equipment", "المعدات"]: sections = ["equipment"] else: sections = ["materials", "labor", "equipment"] # جمع العناصر for section in sections: result[section] = {} for item_id, item_data in self.market_prices[section].items(): if not category or (item_data.get("category", "") == category): result[section][item_id] = item_data return result def update_market_price(self, item_type: str, item_id: str, new_price: float) -> bool: """تحديث سعر في قائمة أسعار السوق""" section = "" if item_type in ["materials", "المواد"]: section = "materials" elif item_type in ["labor", "العمالة"]: section = "labor" elif item_type in ["equipment", "المعدات"]: section = "equipment" else: return False if item_id not in self.market_prices[section]: return False # تحديث السعر current_price = self.market_prices[section][item_id]["current_price"] self.market_prices[section][item_id]["previous_price"] = current_price self.market_prices[section][item_id]["current_price"] = new_price # تحديد اتجاه السعر if new_price > current_price: self.market_prices[section][item_id]["price_trend"] = "up" elif new_price < current_price: self.market_prices[section][item_id]["price_trend"] = "down" else: self.market_prices[section][item_id]["price_trend"] = "stable" # إضافة السعر الجديد إلى تاريخ الأسعار current_date = datetime.now().strftime("%Y-%m-%d") self.market_prices[section][item_id]["price_history"].append({ "date": current_date, "price": new_price }) # تحديث تاريخ آخر تحديث self.market_prices["metadata"]["last_update"] = current_date # حفظ التغييرات self._save_market_prices(self.market_prices) return True def add_market_price_item(self, item_type: str, item_data: Dict[str, Any]) -> str: """إضافة عنصر جديد إلى قائمة أسعار السوق""" section = "" if item_type in ["materials", "المواد"]: section = "materials" elif item_type in ["labor", "العمالة"]: section = "labor" elif item_type in ["equipment", "المعدات"]: section = "equipment" else: raise ValueError("نوع العنصر غير صحيح") # التحقق من البيانات الأساسية if "name" not in item_data or "current_price" not in item_data or "unit" not in item_data: raise ValueError("يجب تحديد الاسم والسعر الحالي والوحدة") # إنشاء معرف فريد للعنصر item_name = item_data["name"].strip() import re item_id = re.sub(r'[^\w\s]', '', item_name) item_id = item_id.replace(" ", "_") # إضافة رقم عشوائي لتجنب التكرار import random if item_id in self.market_prices[section]: item_id = f"{item_id}_{random.randint(1000, 9999)}" # إعداد بيانات العنصر current_date = datetime.now().strftime("%Y-%m-%d") new_item = { "name": item_name, "unit": item_data["unit"], "current_price": item_data["current_price"], "previous_price": item_data.get("previous_price", item_data["current_price"]), "price_trend": "stable", "category": item_data.get("category", ""), "specifications": item_data.get("specifications", ""), "note": item_data.get("note", ""), "price_history": [ {"date": current_date, "price": item_data["current_price"]} ] } # إضافة العنصر إلى القائمة self.market_prices[section][item_id] = new_item # تحديث تاريخ آخر تحديث self.market_prices["metadata"]["last_update"] = current_date # حفظ التغييرات self._save_market_prices(self.market_prices) return item_id def convert_template_to_item(self, template_id: str) -> Dict[str, Any]: """تحويل قالب نموذجي إلى بند للاستخدام في حاسبة تكاليف البناء""" template = self.get_template_by_id(template_id) if not template: raise ValueError("القالب غير موجود") # تحويل القالب إلى صيغة بند item = { "وصف_البند": template["description"], "الكمية": 1.0, "الوحدة": template["unit"], "المواد": template["components"]["materials"], "العمالة": template["components"]["labor"], "المعدات": template["components"]["equipment"], "المصاريف_الإدارية": template["admin_expenses"], "هامش_الربح": template["profit_margin"], "عوامل_التعديل": { "location_factor": 1.0, "time_factor": 1.0, "risk_factor": 1.0, "market_factor": 1.0 } } return item