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