Spaces:
Paused
Paused
| """ | |
| كتالوج بنود نموذجية للمقاولات | |
| يحتوي هذا الملف على قائمة كاملة من النماذج الجاهزة للبنود الشائعة في مشاريع المقاولات، مثل: | |
| - أعمال الخرسانة بأنواعها | |
| - المناهل وأنواع المواسير | |
| - التركيبات المختلفة | |
| - الطرق والأسفلت | |
| - وغيرها من أعمال المقاولات | |
| """ | |
| 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 |