Spaces:
Paused
Paused
| """ | |
| مخطط المشتريات | |
| يقوم بإعداد خطة المشتريات الأمثل بناءً على متطلبات المشروع وقاعدة بيانات الموردين | |
| """ | |
| import logging | |
| import datetime | |
| from typing import Dict, List, Any, Tuple, Optional, Union | |
| from collections import defaultdict | |
| logger = logging.getLogger(__name__) | |
| class ProcurementPlanner: | |
| """ | |
| مخطط المشتريات | |
| """ | |
| def __init__(self, suppliers_db, config=None): | |
| """ | |
| تهيئة مخطط المشتريات | |
| المعاملات: | |
| ---------- | |
| suppliers_db : SuppliersDatabase | |
| قاعدة بيانات الموردين | |
| config : Dict, optional | |
| إعدادات مخطط المشتريات | |
| """ | |
| self.config = config or {} | |
| self.suppliers_db = suppliers_db | |
| logger.info("تم تهيئة مخطط المشتريات") | |
| def generate_plan(self, local_content_data: Dict[str, Any], | |
| suppliers_data: Dict[str, Any]) -> Dict[str, Any]: | |
| """ | |
| إعداد خطة المشتريات | |
| المعاملات: | |
| ---------- | |
| local_content_data : Dict[str, Any] | |
| بيانات المحتوى المحلي | |
| suppliers_data : Dict[str, Any] | |
| بيانات الموردين | |
| المخرجات: | |
| -------- | |
| Dict[str, Any] | |
| خطة المشتريات | |
| """ | |
| try: | |
| logger.info("بدء إعداد خطة المشتريات") | |
| # استخراج المواد المطلوبة | |
| required_materials = local_content_data.get("required_materials", []) | |
| # استخراج الموردين المحليين | |
| local_suppliers = suppliers_data.get("local_suppliers", []) | |
| # إعداد خطة المشتريات | |
| procurement_plan = self._prepare_procurement_plan(required_materials, local_suppliers) | |
| # حساب المؤشرات الرئيسية | |
| stats = self._calculate_procurement_stats(procurement_plan) | |
| # إضافة استراتيجية المشتريات | |
| procurement_strategy = self._generate_procurement_strategy( | |
| procurement_plan, | |
| local_content_data.get("estimated_local_content", 0), | |
| local_content_data.get("required_local_content", 0) | |
| ) | |
| # إعداد النتائج | |
| results = { | |
| "items": procurement_plan, | |
| "stats": stats, | |
| "procurement_strategy": procurement_strategy | |
| } | |
| logger.info(f"اكتملت خطة المشتريات: {len(procurement_plan)} عنصر") | |
| return results | |
| except Exception as e: | |
| logger.error(f"فشل في إعداد خطة المشتريات: {str(e)}") | |
| return { | |
| "items": [], | |
| "stats": {}, | |
| "procurement_strategy": {}, | |
| "error": str(e) | |
| } | |
| def _prepare_procurement_plan(self, required_materials: List[Dict[str, Any]], | |
| local_suppliers: List[Dict[str, Any]]) -> List[Dict[str, Any]]: | |
| """ | |
| إعداد خطة المشتريات التفصيلية | |
| المعاملات: | |
| ---------- | |
| required_materials : List[Dict[str, Any]] | |
| المواد المطلوبة | |
| local_suppliers : List[Dict[str, Any]] | |
| الموردين المحليين | |
| المخرجات: | |
| -------- | |
| List[Dict[str, Any]] | |
| خطة المشتريات | |
| """ | |
| procurement_items = [] | |
| # تاريخ البدء الافتراضي (اليوم) | |
| start_date = datetime.datetime.now() | |
| # معالجة كل مادة | |
| for material in required_materials: | |
| material_name = material.get("name", "") | |
| if not material_name: | |
| continue | |
| # البحث عن الموردين لهذه المادة | |
| material_suppliers = [] | |
| for supplier in local_suppliers: | |
| if material_name in supplier.get("materials", []): | |
| material_suppliers.append(supplier) | |
| # تحديد مصدر التوريد | |
| if material_suppliers: | |
| # المورد المحلي الأكثر موثوقية | |
| supplier = max(material_suppliers, key=lambda s: s.get("reliability", 0)) | |
| source_type = "local" | |
| source_name = supplier.get("name", "") | |
| source_region = supplier.get("region", "") | |
| lead_time = self._estimate_lead_time(source_region) | |
| estimated_cost = self._estimate_cost(material, source_type) | |
| else: | |
| # توريد من الخارج | |
| source_type = "import" | |
| source_name = "مورد خارجي" | |
| source_region = "دولي" | |
| lead_time = 60 # تقدير افتراضي: 60 يوم | |
| estimated_cost = self._estimate_cost(material, source_type) | |
| # تحديد تاريخ التوريد | |
| delivery_date = start_date + datetime.timedelta(days=lead_time) | |
| # إعداد عنصر المشتريات | |
| item = { | |
| "material": material_name, | |
| "quantity": material.get("quantity", 0), | |
| "unit": material.get("unit", ""), | |
| "source_type": source_type, | |
| "supplier": source_name, | |
| "region": source_region, | |
| "lead_time": lead_time, | |
| "delivery_date": delivery_date.strftime("%Y-%m-%d"), | |
| "estimated_cost": estimated_cost, | |
| "local_content_contribution": 100 if source_type == "local" else 0, | |
| "status": "planned" | |
| } | |
| procurement_items.append(item) | |
| # ترتيب العناصر حسب تاريخ التوريد | |
| procurement_items.sort(key=lambda x: x.get("delivery_date", "")) | |
| return procurement_items | |
| def _estimate_lead_time(self, region: str) -> int: | |
| """ | |
| تقدير وقت التوريد بناءً على المنطقة | |
| المعاملات: | |
| ---------- | |
| region : str | |
| المنطقة | |
| المخرجات: | |
| -------- | |
| int | |
| وقت التوريد المقدر (بالأيام) | |
| """ | |
| # قيم افتراضية للمناطق المختلفة | |
| lead_times = { | |
| "الرياض": 7, | |
| "جدة": 10, | |
| "الدمام": 10, | |
| "مكة المكرمة": 12, | |
| "المدينة المنورة": 14, | |
| "القصيم": 14, | |
| "حائل": 15, | |
| "تبوك": 16, | |
| "عسير": 16, | |
| "الباحة": 18, | |
| "جازان": 18, | |
| "نجران": 20 | |
| } | |
| return lead_times.get(region, 15) # قيمة افتراضية: 15 يوم | |
| def _estimate_cost(self, material: Dict[str, Any], source_type: str) -> float: | |
| """ | |
| تقدير تكلفة المادة | |
| المعاملات: | |
| ---------- | |
| material : Dict[str, Any] | |
| معلومات المادة | |
| source_type : str | |
| نوع المصدر (محلي أو استيراد) | |
| المخرجات: | |
| -------- | |
| float | |
| التكلفة المقدرة | |
| """ | |
| # قيم افتراضية | |
| base_cost = 1000.0 | |
| # تعديل التكلفة بناءً على الكمية | |
| quantity = float(material.get("quantity", 1)) | |
| if quantity <= 0: | |
| quantity = 1 | |
| cost = base_cost * quantity | |
| # تعديل التكلفة بناءً على المصدر | |
| if source_type == "import": | |
| # زيادة التكلفة بنسبة 30% للاستيراد | |
| cost *= 1.3 | |
| return cost | |
| def _calculate_procurement_stats(self, procurement_plan: List[Dict[str, Any]]) -> Dict[str, Any]: | |
| """ | |
| حساب إحصائيات خطة المشتريات | |
| المعاملات: | |
| ---------- | |
| procurement_plan : List[Dict[str, Any]] | |
| خطة المشتريات | |
| المخرجات: | |
| -------- | |
| Dict[str, Any] | |
| إحصائيات خطة المشتريات | |
| """ | |
| stats = {} | |
| # إجمالي العناصر | |
| stats["total_items"] = len(procurement_plan) | |
| # العناصر المحلية والمستوردة | |
| local_items = [item for item in procurement_plan if item.get("source_type") == "local"] | |
| import_items = [item for item in procurement_plan if item.get("source_type") == "import"] | |
| stats["local_items_count"] = len(local_items) | |
| stats["import_items_count"] = len(import_items) | |
| # نسبة المحتوى المحلي | |
| if procurement_plan: | |
| stats["local_content_percentage"] = (len(local_items) / len(procurement_plan)) * 100 | |
| else: | |
| stats["local_content_percentage"] = 0 | |
| # إجمالي التكلفة | |
| total_cost = sum(item.get("estimated_cost", 0) for item in procurement_plan) | |
| local_cost = sum(item.get("estimated_cost", 0) for item in local_items) | |
| import_cost = sum(item.get("estimated_cost", 0) for item in import_items) | |
| stats["total_cost"] = total_cost | |
| stats["local_cost"] = local_cost | |
| stats["import_cost"] = import_cost | |
| # نسبة تكلفة المحتوى المحلي | |
| if total_cost > 0: | |
| stats["local_cost_percentage"] = (local_cost / total_cost) * 100 | |
| else: | |
| stats["local_cost_percentage"] = 0 | |
| # متوسط وقت التوريد | |
| if procurement_plan: | |
| avg_lead_time = sum(item.get("lead_time", 0) for item in procurement_plan) / len(procurement_plan) | |
| stats["average_lead_time"] = avg_lead_time | |
| else: | |
| stats["average_lead_time"] = 0 | |
| # توزيع الموردين حسب المنطقة | |
| region_counts = defaultdict(int) | |
| for item in procurement_plan: | |
| if item.get("source_type") == "local": | |
| region = item.get("region", "غير محدد") | |
| region_counts[region] += 1 | |
| stats["region_distribution"] = dict(region_counts) | |
| return stats | |
| def _generate_procurement_strategy(self, procurement_plan: List[Dict[str, Any]], | |
| estimated_local_content: float, | |
| required_local_content: float) -> Dict[str, Any]: | |
| """ | |
| إعداد استراتيجية المشتريات | |
| المعاملات: | |
| ---------- | |
| procurement_plan : List[Dict[str, Any]] | |
| خطة المشتريات | |
| estimated_local_content : float | |
| نسبة المحتوى المحلي المقدرة | |
| required_local_content : float | |
| نسبة المحتوى المحلي المطلوبة | |
| المخرجات: | |
| -------- | |
| Dict[str, Any] | |
| استراتيجية المشتريات | |
| """ | |
| strategy = {} | |
| # تحديد النهج العام | |
| if estimated_local_content >= required_local_content: | |
| strategy["approach"] = "استراتيجية المشتريات المحلية" | |
| strategy["description"] = "التركيز على الموردين المحليين لتعزيز المحتوى المحلي والحفاظ على الميزة التنافسية." | |
| else: | |
| strategy["approach"] = "استراتيجية تعزيز المحتوى المحلي" | |
| strategy["description"] = "زيادة الاعتماد على الموردين المحليين واستبدال العناصر المستوردة تدريجياً بمنتجات محلية." | |
| # تحديد الجدول الزمني | |
| if procurement_plan: | |
| earliest_date = min(item.get("delivery_date", "2099-12-31") for item in procurement_plan) | |
| latest_date = max(item.get("delivery_date", "2000-01-01") for item in procurement_plan) | |
| strategy["timeline"] = { | |
| "start_date": earliest_date, | |
| "end_date": latest_date, | |
| "critical_path": self._identify_critical_path(procurement_plan) | |
| } | |
| else: | |
| strategy["timeline"] = { | |
| "start_date": "غير محدد", | |
| "end_date": "غير محدد", | |
| "critical_path": [] | |
| } | |
| # الاعتبارات الرئيسية | |
| strategy["key_considerations"] = [ | |
| "ضمان الامتثال لمتطلبات المحتوى المحلي", | |
| "تقليل مخاطر سلسلة الإمداد وتنويع مصادر التوريد", | |
| "تحسين كفاءة التكلفة والجودة", | |
| "بناء علاقات طويلة الأمد مع الموردين الرئيسيين" | |
| ] | |
| # خطة عمل محددة | |
| strategy["action_plan"] = [] | |
| # التعامل مع العناصر المستوردة | |
| import_items = [item for item in procurement_plan if item.get("source_type") == "import"] | |
| if import_items: | |
| strategy["action_plan"].append( | |
| "البحث عن موردين محليين بديلين للعناصر المستوردة (" + | |
| ", ".join([item.get("material", "") for item in import_items[:3]]) + | |
| (f" وغيرها من {len(import_items) - 3} عناصر" if len(import_items) > 3 else "") + | |
| ")" | |
| ) | |
| # تحسين أوقات التوريد الطويلة | |
| long_lead_items = [item for item in procurement_plan if item.get("lead_time", 0) > 30] | |
| if long_lead_items: | |
| strategy["action_plan"].append( | |
| "تقليل أوقات التوريد الطويلة للعناصر الحرجة (" + | |
| ", ".join([item.get("material", "") for item in long_lead_items[:3]]) + | |
| (f" وغيرها من {len(long_lead_items) - 3} عناصر" if len(long_lead_items) > 3 else "") + | |
| ")" | |
| ) | |
| # التعامل مع العناصر عالية التكلفة | |
| if procurement_plan: | |
| avg_cost = sum(item.get("estimated_cost", 0) for item in procurement_plan) / len(procurement_plan) | |
| high_cost_items = [item for item in procurement_plan if item.get("estimated_cost", 0) > avg_cost * 2] | |
| if high_cost_items: | |
| strategy["action_plan"].append( | |
| "تحليل بدائل خفض التكلفة للعناصر عالية التكلفة (" + | |
| ", ".join([item.get("material", "") for item in high_cost_items[:3]]) + | |
| (f" وغيرها من {len(high_cost_items) - 3} عناصر" if len(high_cost_items) > 3 else "") + | |
| ")" | |
| ) | |
| # إضافة إجراءات عامة | |
| strategy["action_plan"].extend([ | |
| "توحيد الطلبات من نفس المورد لتحقيق وفورات الحجم", | |
| "إعداد خطة طوارئ للموردين الرئيسيين", | |
| "تطوير نظام لتقييم أداء الموردين", | |
| "توثيق عمليات الشراء وإنشاء قاعدة بيانات سعرية" | |
| ]) | |
| return strategy | |
| def _identify_critical_path(self, procurement_plan: List[Dict[str, Any]]) -> List[Dict[str, Any]]: | |
| """ | |
| تحديد المسار الحرج في خطة المشتريات | |
| المعاملات: | |
| ---------- | |
| procurement_plan : List[Dict[str, Any]] | |
| خطة المشتريات | |
| المخرجات: | |
| -------- | |
| List[Dict[str, Any]] | |
| المسار الحرج | |
| """ | |
| # تحديد العناصر الحرجة (أطول وقت توريد، أعلى تكلفة، عناصر مستوردة) | |
| if not procurement_plan: | |
| return [] | |
| # ترتيب العناصر حسب وقت التوريد | |
| lead_time_sorted = sorted(procurement_plan, key=lambda x: x.get("lead_time", 0), reverse=True) | |
| # أخذ أطول 3 عناصر من حيث وقت التوريد | |
| critical_items = lead_time_sorted[:3] | |
| # ترتيب العناصر حسب التكلفة | |
| cost_sorted = sorted(procurement_plan, key=lambda x: x.get("estimated_cost", 0), reverse=True) | |
| # إضافة أعلى 2 عناصر من حيث التكلفة إذا لم تكن موجودة بالفعل | |
| for item in cost_sorted[:2]: | |
| if not any(critical.get("material") == item.get("material") for critical in critical_items): | |
| critical_items.append(item) | |
| return [ | |
| { | |
| "material": item.get("material", ""), | |
| "supplier": item.get("supplier", ""), | |
| "lead_time": item.get("lead_time", 0), | |
| "delivery_date": item.get("delivery_date", ""), | |
| "reason": "وقت توريد طويل" if item == lead_time_sorted[0] else | |
| "تكلفة عالية" if item == cost_sorted[0] else | |
| "عنصر حرج" | |
| } | |
| for item in critical_items | |
| ] | |