EGYADMIN commited on
Commit
3694780
·
verified ·
1 Parent(s): f8067b9

Update modules/pricing/services/construction_cost_calculator.py

Browse files
modules/pricing/services/construction_cost_calculator.py CHANGED
@@ -1,1006 +1,751 @@
1
  """
2
- خدمة حاسبة تكاليف البناء
3
- تقوم هذه الخدمة بحساب تكاليف البناء بشكل تفصيلي بناءً على المكونات المختلفة:
4
  - المواد الخام
5
- - العمالة
6
  - المعدات
 
7
  - المصاريف الإدارية
8
  - هامش الربح
9
  """
10
 
 
11
  import pandas as pd
12
  import numpy as np
13
- from datetime import datetime
14
- import os
15
- import json
16
- import sys
17
- from typing import Dict, List, Optional, Union, Any
18
 
19
- # إضافة مسار النظام للوصول لملفات التكوين
20
- sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "../../../")))
21
- try:
22
- import config
23
- except ImportError:
24
- # إذا لم يتم العثور على ملف التكوين، نستخدم قيم افتراضية
25
- class DefaultConfig:
26
- DATA_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), "../../../data"))
27
- config = DefaultConfig
28
-
29
- # إنشاء مجلد البيانات إذا لم يكن موجودًا
30
- if not os.path.exists(config.DATA_DIR):
31
- os.makedirs(config.DATA_DIR)
32
 
33
- class ConstructionCostCalculator:
34
- """خدمة حاسبة تكاليف البناء"""
35
-
36
- def __init__(self):
37
- """تهيئة حاسبة تكاليف البناء"""
38
- # تحميل بيانات المواد والأسعار المرجعية
39
- self.material_rates = self._load_material_rates()
40
- self.labor_rates = self._load_labor_rates()
41
- self.equipment_rates = self._load_equipment_rates()
42
-
43
- # النسب الافتراضية للمصاريف الإدارية وهامش الربح
44
- self.default_admin_expenses_percentage = 0.05 # 5%
45
- self.default_profit_margin_percentage = 0.10 # 10%
46
-
47
- # معاملات التعديل الافتراضية
48
- self.default_adjustment_factors = {
49
- 'location_factor': 1.0, # معامل الموقع
50
- 'time_factor': 1.0, # معامل الوقت
51
- 'risk_factor': 1.0, # معامل المخاطر
52
- 'market_factor': 1.0 # معامل السوق
53
- }
54
 
55
- def _load_material_rates(self) -> Dict[str, Dict[str, Any]]:
56
- """تحميل أسعار المواد"""
57
- # محاكاة تحميل البيانات من مصدر بيانات
58
- material_rates = {
59
- # مواد الخرسانة
60
- 'خرسانة جاهزة': {
61
- 'وحدة': 'م3',
62
- 'سعر_الوحدة': 750.0,
63
- 'وصف': 'خرسانة جاهزة بقوة 350 كجم/سم2',
64
- 'فئة': 'أعمال خرسانية'
65
- },
66
- 'حديد تسليح': {
67
- 'وحدة': 'طن',
68
- 'سعر_الوحدة': 5500.0,
69
- 'وصف': 'حديد تسليح قطر 8-32 مم',
70
- 'فئة': 'أعمال خرسانية'
71
- },
72
- 'أسمنت': {
73
- 'وحدة': 'كيس',
74
- 'سعر_الوحدة': 30.0,
75
- 'وصف': 'أسمنت بورتلاندي عادي',
76
- 'فئة': 'أعمال خرسانية'
77
- },
78
- 'رمل': {
79
- 'وحدة': 'م3',
80
- 'سعر_الوحدة': 120.0,
81
- 'وصف': 'رمل خشن للخرسانة',
82
- 'فئة': 'أعمال خرسانية'
83
- },
84
- 'زلط': {
85
- 'وحدة': 'م3',
86
- 'سعر_الوحدة': 150.0,
87
- 'وصف': 'زلط مقاس 10-20 مم للخرسانة',
88
- 'فئة': 'أعمال خرسانية'
89
- },
90
-
91
- # مواد البناء
92
- 'طوب أحمر': {
93
- 'وحدة': '1000 قطعة',
94
- 'سعر_الوحدة': 900.0,
95
- 'وصف': 'طوب أحمر مقاس 25×12×6 سم',
96
- 'فئة': 'أعمال بناء'
97
- },
98
- 'طوب أسمنتي': {
99
- 'وحدة': 'قطعة',
100
- 'سعر_الوحدة': 4.5,
101
- 'وصف': 'بلوك أسمنتي مقاس 20×20×40 سم',
102
- 'فئة': 'أعمال بناء'
103
- },
104
- 'مونة بناء': {
105
- 'وحدة': 'م3',
106
- 'سعر_الوحدة': 350.0,
107
- 'وصف': 'مونة أسمنتية للبناء',
108
- 'فئة': 'أعمال بناء'
109
- },
110
-
111
- # مواد التشطيبات
112
- 'بلاط سيراميك': {
113
- 'وحدة': 'م2',
114
- 'سعر_الوحدة': 120.0,
115
- 'وصف': 'بلاط سيراميك للأرضيات مقاس 40×40 سم',
116
- 'فئة': 'تشطيبات'
117
- },
118
- 'بلاط بورسلين': {
119
- 'وحدة': 'م2',
120
- 'سعر_الوحدة': 180.0,
121
- 'وصف': 'بلاط بورسلين للأرضيات مقاس 60×60 سم',
122
- 'فئة': 'تشطيبات'
123
- },
124
- 'دهانات بلاستيك': {
125
- 'وحدة': 'لتر',
126
- 'سعر_الوحدة': 35.0,
127
- 'وصف': 'دهان بلاستيك أساس وتشطيب',
128
- 'فئة': 'تشطيبات'
129
- },
130
- 'جبس بورد': {
131
- 'وحدة': 'م2',
132
- 'سعر_الوحدة': 95.0,
133
- 'وصف': 'ألواح جبس بورد سمك 12 مم',
134
- 'فئة': 'تشطيبات'
135
- },
136
-
137
- # مواد العزل
138
- 'عزل مائي': {
139
- 'وحدة': 'م2',
140
- 'سعر_الوحدة': 45.0,
141
- 'وصف': 'عزل مائي من البيتومين المؤكسد',
142
- 'فئة': 'أعمال عزل'
143
- },
144
- 'عزل حراري': {
145
- 'وحدة': 'م2',
146
- 'سعر_الوحدة': 65.0,
147
- 'وصف': 'ألواح عزل حراري من البوليسترين سمك 5 سم',
148
- 'فئة': 'أعمال عزل'
149
- }
 
 
 
 
 
 
150
  }
 
 
 
 
 
 
151
 
152
- # محاولة تحميل البيانات من ملف إذا كان متاحًا
153
- try:
154
- file_path = os.path.join(config.DATA_DIR, 'material_rates.json')
155
- if os.path.exists(file_path):
156
- with open(file_path, 'r', encoding='utf-8') as f:
157
- loaded_data = json.load(f)
158
- material_rates.update(loaded_data)
159
- except Exception as e:
160
- print(f"خطأ في تحميل بيانات أسعار المواد: {str(e)}")
161
 
162
- return material_rates
163
-
164
- def _load_labor_rates(self) -> Dict[str, Dict[str, Any]]:
165
- """تحميل أسعار العمالة"""
166
- # محاكاة تحميل البيانات من مصدر بيانات
167
- labor_rates = {
168
- # عمالة الخرسانات
169
- 'نجار مسلح': {
170
- 'وحدة': 'يوم',
171
- 'سعر_الوحدة': 250.0,
172
- 'وصف': 'نجار مسلح لأعمال الشدات والفرم',
173
- 'فئة': 'أعمال خرسانية',
174
- 'إنتاجية_يومية': {
175
- 'شدة أساسات': 12, # متر مربع
176
- 'شدة أعمدة': 10, # متر مربع
177
- 'شدة أسقف': 12 # متر مربع
178
- }
179
- },
180
- 'حداد مسلح': {
181
- 'وحدة': 'يوم',
182
- 'سعر_الوحدة': 250.0,
183
- 'وصف': 'حداد مسلح لأعمال حديد التسليح',
184
- 'فئة': 'أعمال خرسانية',
185
- 'إنتاجية_يومية': {
186
- 'تجهيز وتركيب حديد أساسات': 700, # كجم
187
- 'تجهيز وتركيب حديد أعمدة': 600, # كجم
188
- 'تجهيز وتركيب حديد أسقف': 650 # كجم
189
- }
190
- },
191
- 'عامل خرسانة': {
192
- 'وحدة': 'يوم',
193
- 'سعر_الوحدة': 150.0,
194
- 'وصف': 'عامل لصب وتسوية الخرسانة',
195
- 'فئة': 'أعمال خرسانية',
196
- 'إنتاجية_يومية': {
197
- 'صب خرسانة': 15 # متر مكعب
198
- }
199
- },
200
-
201
- # عمالة البناء
202
- 'بناء': {
203
- 'وحدة': 'يوم',
204
- 'سعر_الوحدة': 200.0,
205
- 'وصف': 'عامل بناء للطوب والبلوك',
206
- 'فئة': 'أعمال بناء',
207
- 'إنتاجية_يومية': {
208
- 'بناء طوب أحمر': 500, # قطعة
209
- 'بناء بلوك أسمنتي': 80 # قطعة
210
- }
211
- },
212
- 'مساعد بناء': {
213
- 'وحدة': 'يوم',
214
- 'سعر_الوحدة': 120.0,
215
- 'وصف': 'مساعد عامل بناء',
216
- 'فئة': 'أعمال بناء',
217
- 'إنتاجية_يومية': {}
218
- },
219
-
220
- # عمالة التشطيبات
221
- 'مبلط': {
222
- 'وحدة': 'يوم',
223
- 'سعر_الوحدة': 250.0,
224
- 'وصف': 'عامل تركيب بلاط وسيراميك',
225
- 'فئة': 'تشطيبات',
226
- 'إنتاجية_يومية': {
227
- 'تركيب سيراميك أرضيات': 15, # متر مربع
228
- 'تركيب سيراميك حوائط': 12, # متر مربع
229
- 'تركيب بورسلين': 12 # متر مربع
230
- }
231
- },
232
- 'نقاش': {
233
- 'وحدة': 'يوم',
234
- 'سعر_الوحدة': 200.0,
235
- 'وصف': 'عامل دهانات',
236
- 'فئة': 'تشطيبات',
237
- 'إنتاجية_يومية': {
238
- 'دهانات بلاستيك': 35, # متر مربع
239
- 'دهانات زيتية': 25 # متر مربع
240
- }
241
- },
242
- 'كهربائي': {
243
- 'وحدة': 'يوم',
244
- 'سعر_الوحدة': 270.0,
245
- 'وصف': 'فني كهرباء',
246
- 'فئة': 'تشطيبات',
247
- 'إنتاجية_يومية': {
248
- 'تأسيس نقاط كهرباء': 15, # نقطة
249
- 'تركيب لوحات توزيع': 2 # لوحة
250
- }
251
- },
252
- 'سباك': {
253
- 'وحدة': 'يوم',
254
- 'سعر_الوحدة': 250.0,
255
- 'وصف': 'فني سباكة',
256
- 'فئة': 'تشطيبات',
257
- 'إنتاجية_يومية': {
258
- 'تأسيس نقاط صرف': 8, # نقطة
259
- 'تأسيس نقاط تغذية': 10, # نقطة
260
- 'تركيب أطقم حمامات': 2 # طقم
261
- }
262
- },
263
-
264
- # مراقبة وإشراف
265
- 'مهندس موقع': {
266
- 'وحدة': 'يوم',
267
- 'سعر_الوحدة': 500.0,
268
- 'وصف': 'مهندس إشراف موقع',
269
- 'فئة': 'إشراف'
270
- },
271
- 'مراقب فني': {
272
- 'وحدة': 'يوم',
273
- 'سعر_الوحدة': 300.0,
274
- 'وصف': 'مراقب فني للتنفيذ',
275
- 'فئة': 'إشراف'
276
- }
277
- }
278
 
279
- # محاولة تحميل البيانات من ملف إذا كان متاحًا
280
- try:
281
- file_path = os.path.join(config.DATA_DIR, 'labor_rates.json')
282
- if os.path.exists(file_path):
283
- with open(file_path, 'r', encoding='utf-8') as f:
284
- loaded_data = json.load(f)
285
- labor_rates.update(loaded_data)
286
- except Exception as e:
287
- print(f"خطأ في تحميل بيانات أسعار العمالة: {str(e)}")
288
 
289
- return labor_rates
290
-
291
- def _load_equipment_rates(self) -> Dict[str, Dict[str, Any]]:
292
- """تحميل أسعار المعدات"""
293
- # محاكاة تحميل البيانات من مصدر بيانات
294
- equipment_rates = {
295
- # معدات الحفر والتسوية
296
- 'حفار صغير': {
297
- 'وحدة': 'يوم',
298
- 'سعر_الوحدة': 1200.0,
299
- 'وصف': 'حفار صغير (بوبكات) بقدرة 70 حصان',
300
- 'فئة': 'معدات حفر',
301
- 'إنتاجية_يومية': {
302
- 'حفر في تربة عادية': 60 # متر مكعب
303
- }
304
- },
305
- 'حفار متوسط': {
306
- 'وحدة': 'يوم',
307
- 'سعر_الوحدة': 2500.0,
308
- 'وصف': 'حفار متوسط الحجم بقدرة 150 حصان',
309
- 'فئة': 'معدات حفر',
310
- 'إنتاجية_يومية': {
311
- 'حفر في تربة عادية': 200 # متر مكعب
312
- }
313
- },
314
- 'لودر': {
315
- 'وحدة': 'يوم',
316
- 'سعر_الوحدة': 2000.0,
317
- 'وصف': 'لودر أمامي لنقل التربة',
318
- 'فئة': 'معدات حفر',
319
- 'إنتاجية_يومية': {
320
- 'تحميل تربة': 300, # متر مكعب
321
- 'تسوية موقع': 1500 # متر مربع
322
- }
323
- },
324
- 'جريدر': {
325
- 'وحدة': 'يوم',
326
- 'سعر_الوحدة': 2200.0,
327
- 'وصف': 'جريدر لتسوية الموقع',
328
- 'فئة': 'معدات حفر',
329
- 'إنتاجية_يومية': {
330
- 'تسوية طرق': 3000 # متر مربع
331
- }
332
- },
333
-
334
- # معدات الخرسانة
335
- 'خلاطة خرسانة': {
336
- 'وحدة': 'يوم',
337
- 'سعر_الوحدة': 350.0,
338
- 'وصف': 'خلاطة خرسانة بسعة 0.5 متر مكعب',
339
- 'فئة': 'معدات خرسانة',
340
- 'إنتاجية_يومية': {
341
- 'خلط خرسانة': 15 # متر مكعب
342
- }
343
- },
344
- 'هزاز خرسانة': {
345
- 'وحدة': 'يوم',
346
- 'سعر_الوحد��': 150.0,
347
- 'وصف': 'هزاز خرسانة كهربائي',
348
- 'فئة': 'معدات خرسانة',
349
- 'إنتاجية_يومية': {
350
- 'دمك خرسانة': 40 # متر مكعب
351
- }
352
- },
353
- 'شاحنة خرسانة جاهزة': {
354
- 'وحدة': 'يوم',
355
- 'سعر_الوحدة': 3000.0,
356
- 'وصف': 'شاحنة خرسانة جاهزة (مكسر) سعة 8 متر مكعب',
357
- 'فئة': 'معدات خرسانة',
358
- 'إنتاجية_يومية': {
359
- 'نقل وصب خرسانة': 50 # متر مكعب
360
- }
361
- },
362
- 'مضخة خرسانة': {
363
- 'وحدة': 'يوم',
364
- 'سعر_الوحدة': 5000.0,
365
- 'وصف': 'مضخة خرسانة بذراع 42 متر',
366
- 'فئة': 'معدات خرسانة',
367
- 'إنتاجية_يومية': {
368
- 'ضخ خرسانة': 120 # متر مكعب
369
- }
370
- },
371
 
372
- # معدات رفع ونقل
373
- 'رافعة برجية': {
374
- 'وحدة': 'شهر',
375
- 'سعر_الوحدة': 35000.0,
376
- 'وصف': 'رافعة برجية بارتفاع 40 متر',
377
- 'فئة': 'معدات رفع',
378
- },
379
- 'ونش شوكة': {
380
- 'وحدة': 'يوم',
381
- 'سعر_الوحدة': 1500.0,
382
- 'وصف': 'ونش شوكة لرفع مواد البناء',
383
- 'فئة': 'معدات رفع',
384
- 'إنتاجية_يومية': {
385
- 'رفع ونقل مواد': 100 # طن
386
- }
387
- },
388
- 'شاحنة نقل': {
389
- 'وحدة': 'يوم',
390
- 'سعر_الوحدة': 1200.0,
391
- 'وصف': 'شاحنة نقل حمولة 20 طن',
392
- 'فئة': 'معدات نقل',
393
- 'إنتاجية_يومية': {
394
- 'نقل مواد': 80 # طن
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
395
  }
396
- }
397
- }
398
-
399
- # محاولة تحميل البيانات من ملف إذا كان متاحًا
400
- try:
401
- file_path = os.path.join(config.DATA_DIR, 'equipment_rates.json')
402
- if os.path.exists(file_path):
403
- with open(file_path, 'r', encoding='utf-8') as f:
404
- loaded_data = json.load(f)
405
- equipment_rates.update(loaded_data)
406
- except Exception as e:
407
- print(f"خطأ في تحميل بيانات أسعار المعدات: {str(e)}")
408
-
409
- return equipment_rates
 
 
 
 
 
 
 
 
 
 
 
 
 
410
 
411
- def calculate_item_cost(self, item_data: Dict[str, Any]) -> Dict[str, Any]:
412
- """
413
- حساب تكلفة بند محدد بكافة مكوناته
414
-
415
- المعلمات:
416
- item_data (dict): بيانات البند، تتضمن:
417
- - وصف_البند (str): وصف البند
418
- - الكمية (float): كمية البند
419
- - الوحدة (str): وحدة القياس
420
- - المواد (list): قائمة المواد المستخدمة وكمياتها
421
- - العمالة (list): قائمة العمالة المستخدمة وعددها
422
- - المعدات (list): قائمة المعدات المستخدمة وساعات عملها
423
- - المصاريف_الإدارية (float, optional): نسبة المصاريف الإدارية (افتراضياً 5%)
424
- - هامش_الربح (float, optional): نسبة هامش الربح (افتراضياً 10%)
425
- - عوامل_التعديل (dict, optional): عوامل تعديل التكلفة
426
-
427
- العوائد:
428
- dict: تفاصيل تكلفة البند بكافة عناصرها
429
- """
430
- # استخراج البيانات الأساسية للبند
431
- item_description = item_data.get('وصف_البند', 'بند غير محدد')
432
- quantity = item_data.get('الكمية', 0.0)
433
- unit = item_data.get('الوحدة', 'وحدة')
434
-
435
- # حساب تكلفة المواد
436
- materials_cost = self._calculate_materials_cost(item_data.get('المواد', []))
437
-
438
- # حساب تكلفة العمالة
439
- labor_cost = self._calculate_labor_cost(item_data.get('العمالة', []))
440
-
441
- # حساب تكلفة المعدات
442
- equipment_cost = self._calculate_equipment_cost(item_data.get('المعدات', []))
443
-
444
- # حساب التكلفة المباشرة الإجمالية
445
- direct_cost = materials_cost['الإجمالي'] + labor_cost['الإجمالي'] + equipment_cost['الإجمالي']
446
-
447
- # حساب المصاريف الإدارية
448
- admin_percentage = item_data.get('المصاريف_الإدارية', self.default_admin_expenses_percentage)
449
- admin_cost = direct_cost * admin_percentage
450
-
451
- # حساب هامش الربح
452
- profit_percentage = item_data.get('هامش_الربح', self.default_profit_margin_percentage)
453
- profit_margin = (direct_cost + admin_cost) * profit_percentage
454
-
455
- # حساب التكلفة الإجمالية
456
- total_cost = direct_cost + admin_cost + profit_margin
457
-
458
- # حساب سعر الوحدة
459
- unit_price = total_cost / quantity if quantity > 0 else 0.0
460
-
461
- # تطبيق عوامل التعديل إذا وجدت
462
- adjustment_factors = item_data.get('عوامل_التعديل', self.default_adjustment_factors)
463
- adjustment_factor = self._calculate_adjustment_factor(adjustment_factors)
464
-
465
- adjusted_unit_price = unit_price * adjustment_factor
466
- adjusted_total_cost = total_cost * adjustment_factor
467
-
468
- # إعداد النتائج
469
- result = {
470
- 'وصف_البند': item_description,
471
- 'الكمية': quantity,
472
- 'الوحدة': unit,
473
- 'تكاليف_مباشرة': {
474
- 'المواد': materials_cost,
475
- 'العمالة': labor_cost,
476
- 'المعدات': equipment_cost,
477
- 'إجمالي_تكاليف_مباشرة': direct_cost
478
- },
479
- 'مصاريف_إدارية': {
480
- 'نسبة': admin_percentage * 100,
481
- 'قيمة': admin_cost
482
- },
483
- 'هامش_ربح': {
484
- 'نسبة': profit_percentage * 100,
485
- 'قيمة': profit_margin
486
- },
487
- 'التكلفة_الإجمالية': total_cost,
488
- 'سعر_الوحدة': unit_price,
489
- 'عوامل_التعديل': {
490
- 'المعامل_الإجمالي': adjustment_factor,
491
- 'التفاصيل': adjustment_factors
492
- },
493
- 'السعر_المعدل': {
494
- 'سعر_الوحدة': adjusted_unit_price,
495
- 'إجمالي': adjusted_total_cost
496
- }
497
- }
498
-
499
- return result
500
 
501
- def _calculate_materials_cost(self, materials: List[Dict[str, Any]]) -> Dict[str, Any]:
502
- """
503
- حساب تكلفة المواد
504
-
505
- المعلمات:
506
- materials (list): قائمة المواد المستخدمة وكمياتها
507
- - الاسم (str): اسم المادة
508
- - الكمية (float): الكمية المستخدمة
509
- - الوحدة (str, optional): وحدة القياس
510
- - سعر_الوحدة (float, optional): سعر الوحدة (يستخدم السعر من البيانات المرجعية إذا لم يتم تحديده)
511
-
512
- العوائد:
513
- dict: تفاصيل تكلفة المواد
514
- """
515
- materials_details = []
516
- total_cost = 0.0
517
-
518
- for material in materials:
519
- material_name = material.get('الاسم', '')
520
- quantity = material.get('الكمية', 0.0)
521
-
522
- # البحث عن سعر المادة من البيانات المرجعية إذا لم يتم تحديده
523
- if 'سعر_الوحدة' in material:
524
- unit_price = material.get('سعر_الوحدة', 0.0)
525
- unit = material.get('الوحدة', 'وحدة')
526
- elif material_name in self.material_rates:
527
- ref_material = self.material_rates[material_name]
528
- unit_price = ref_material.get('سعر_الوحدة', 0.0)
529
- unit = ref_material.get('وحدة', 'وحدة')
530
- else:
531
- unit_price = 0.0
532
- unit = material.get('الوحدة', 'وحدة')
533
-
534
- # حساب التكلفة
535
- cost = quantity * unit_price
536
- total_cost += cost
537
-
538
- # إضافة التفاصيل
539
- materials_details.append({
540
- 'الاسم': material_name,
541
- 'الكمية': quantity,
542
- 'الوحدة': unit,
543
- 'سعر_الوحدة': unit_price,
544
- 'التكلفة': cost
545
- })
546
-
547
- return {
548
- 'التفاصيل': materials_details,
549
- 'الإجمالي': total_cost
550
  }
 
 
551
 
552
- def _calculate_labor_cost(self, labor: List[Dict[str, Any]]) -> Dict[str, Any]:
553
- """
554
- حساب تكلفة العمالة
555
-
556
- المعلمات:
557
- labor (list): قائمة العمالة المستخدمة وعددها
558
- - النوع (str): ن��ع العامل
559
- - العدد (int): عدد العمال
560
- - المدة (float): مدة العمل بالأيام
561
- - سعر_اليوم (float, optional): أجر اليوم (يستخدم السعر من البيانات المرجعية إذا لم يتم تحديده)
562
-
563
- العوائد:
564
- dict: تفاصيل تكلفة العمالة
565
- """
566
- labor_details = []
567
- total_cost = 0.0
568
-
569
- for worker in labor:
570
- worker_type = worker.get('النوع', '')
571
- count = worker.get('العدد', 0)
572
- duration = worker.get('المدة', 0.0)
573
-
574
- # البحث عن سعر العامل من البيانات المرجعية إذا لم يتم تحديده
575
- if 'سعر_اليوم' in worker:
576
- daily_rate = worker.get('سعر_اليوم', 0.0)
577
- elif worker_type in self.labor_rates:
578
- daily_rate = self.labor_rates[worker_type].get('سعر_الوحدة', 0.0)
579
- else:
580
- daily_rate = 0.0
581
-
582
- # حساب التكلفة
583
- cost = count * duration * daily_rate
584
- total_cost += cost
585
-
586
- # إضافة التفاصيل
587
- labor_details.append({
588
- 'النوع': worker_type,
589
- 'العدد': count,
590
- 'المدة': duration,
591
- 'سعر_اليوم': daily_rate,
592
- 'التكلفة': cost
593
  })
594
 
595
- return {
596
- 'التفاصيل': labor_details,
597
- 'الإجمالي': total_cost
598
- }
599
-
600
- def _calculate_equipment_cost(self, equipment: List[Dict[str, Any]]) -> Dict[str, Any]:
601
- """
602
- حساب تكلفة المعدات
603
 
604
- المعلمات:
605
- equipment (list): قائمة المعدات المستخدمة وساعات عملها
606
- - النوع (str): نوع المعدة
607
- - العدد (int): عدد المعدات
608
- - المدة (float): مدة الاستخدام بالأيام
609
- - سعر_اليوم (float, optional): أجر اليوم (يستخدم السعر من البيانات المرجعية إذا لم يتم تحديده)
610
 
611
- العوائد:
612
- dict: تفاصيل تكلفة المعدات
613
- """
614
- equipment_details = []
615
- total_cost = 0.0
616
 
617
- for equip in equipment:
618
- equip_type = equip.get('النوع', '')
619
- count = equip.get('العدد', 0)
620
- duration = equip.get('المدة', 0.0)
621
 
622
- # البحث عن سعر المعدة من البيانات المرجعية إذا لم يتم تحديده
623
- if 'سعر_اليوم' in equip:
624
- daily_rate = equip.get('سعر_اليوم', 0.0)
625
- elif equip_type in self.equipment_rates:
626
- daily_rate = self.equipment_rates[equip_type].get('سعر_الوحدة', 0.0)
627
- else:
628
- daily_rate = 0.0
629
 
630
- # حساب التكلفة
631
- cost = count * duration * daily_rate
632
- total_cost += cost
 
 
 
633
 
634
- # إضافة التفاصيل
635
- equipment_details.append({
636
- 'النوع': equip_type,
637
- 'العدد': count,
638
- 'المدة': duration,
639
- 'سعر_اليوم': daily_rate,
640
- 'التكلفة': cost
641
- })
642
-
643
- return {
644
- 'التفاصيل': equipment_details,
645
- 'الإجمالي': total_cost
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
646
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
647
 
648
- def _calculate_adjustment_factor(self, factors: Dict[str, float]) -> float:
649
- """
650
- حساب المعامل الإجمالي لتعديل التكلفة
 
 
 
 
 
 
 
 
 
 
 
 
651
 
652
- المعلمات:
653
- factors (dict): عوامل التعديل
654
 
655
- العوائد:
656
- float: المعامل الإجمالي
657
- """
658
- # دمج العوامل المحددة مع العوامل الافتراضية
659
- effective_factors = self.default_adjustment_factors.copy()
660
- effective_factors.update(factors)
661
 
662
- # حساب المعامل الإجمالي
663
- total_factor = 1.0
664
- for factor in effective_factors.values():
665
- total_factor *= factor
666
 
667
- return total_factor
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
668
 
669
- def calculate_project_cost(self, project_data: Dict[str, Any]) -> Dict[str, Any]:
670
- """
671
- حساب التكلفة الإجمالية لمشروع بناء كامل
672
-
673
- المعلمات:
674
- project_data (dict): بيانات المشروع، تتضمن:
675
- - اسم_المشروع (str): اسم المشروع
676
- - وصف_المشروع (str): وصف المشروع
677
- - البنود (list): قائمة بنود المشروع
678
- - المصاريف_الإدارية (float, optional): نسبة المصاريف الإدارية الإجمالية (افتراضياً 5%)
679
- - هامش_الربح (float, optional): نسبة هامش الربح الإجمالي (افتراضياً 10%)
680
- - عوامل_التعديل (dict, optional): عوامل تعديل التكلفة للمشروع
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
681
 
682
- العوائد:
683
- dict: تفاصيل تكلفة المشروع بكافة عناصرها
684
- """
685
- # استخراج البيانات الأساسية للمشروع
686
- project_name = project_data.get('اسم_المشروع', 'مشروع غير محدد')
687
- project_description = project_data.get('وصف_المشروع', '')
688
- items = project_data.get('البنود', [])
689
 
690
- # استخراج النسب الإجمالية
691
- admin_percentage = project_data.get('المصاريف_الإدارية', self.default_admin_expenses_percentage)
692
- profit_percentage = project_data.get('هامش_الربح', self.default_profit_margin_percentage)
693
 
694
- # حساب تكلفة كل بند
695
- items_costs = []
696
- total_direct_cost = 0.0
697
- total_materials_cost = 0.0
698
- total_labor_cost = 0.0
699
- total_equipment_cost = 0.0
700
 
701
- for item_data in items:
702
- # تحديث نسب المصاريف والربح للبند إذا لم تكن محددة
703
- if 'المصاريف_الإدارية' not in item_data:
704
- item_data['المصاريف_الإدارية'] = admin_percentage
705
 
706
- if 'هامش_الربح' not in item_data:
707
- item_data['هامش_الربح'] = profit_percentage
708
 
709
- # حساب تكلفة البند
710
- item_cost = self.calculate_item_cost(item_data)
711
- items_costs.append(item_cost)
712
-
713
- # تحديث الإجماليات
714
- total_materials_cost += item_cost['تكاليف_مباشرة']['المواد']['الإجمالي']
715
- total_labor_cost += item_cost['تكاليف_مباشرة']['العمالة']['الإجمالي']
716
- total_equipment_cost += item_cost['تكاليف_مباشرة']['المعدات']['الإجمالي']
717
- total_direct_cost += item_cost['تكاليف_مباشرة']['إجمالي_تكاليف_مباشرة']
718
-
719
- # حساب المصاريف الإدارية
720
- admin_cost = total_direct_cost * admin_percentage
721
-
722
- # حساب هامش الربح
723
- profit_margin = (total_direct_cost + admin_cost) * profit_percentage
724
-
725
- # حساب التكلفة الإجمالية
726
- total_cost = total_direct_cost + admin_cost + profit_margin
727
-
728
- # تطبيق عوامل التعديل إذا وجدت
729
- adjustment_factors = project_data.get('عوامل_التعديل', self.default_adjustment_factors)
730
- adjustment_factor = self._calculate_adjustment_factor(adjustment_factors)
731
-
732
- adjusted_total_cost = total_cost * adjustment_factor
733
-
734
- # إعداد النتائج
735
- result = {
736
- 'اسم_المشروع': project_name,
737
- 'وصف_المشروع': project_description,
738
- 'تكاليف_مباشرة': {
739
- 'المواد': {
740
- 'الإجمالي': total_materials_cost,
741
- 'النسبة_المئوية': (total_materials_cost / total_direct_cost * 100) if total_direct_cost > 0 else 0
742
- },
743
- 'العمالة': {
744
- 'الإجمالي': total_labor_cost,
745
- 'النسبة_المئوية': (total_labor_cost / total_direct_cost * 100) if total_direct_cost > 0 else 0
746
- },
747
- 'المعدات': {
748
- 'الإجمالي': total_equipment_cost,
749
- 'النسبة_المئوية': (total_equipment_cost / total_direct_cost * 100) if total_direct_cost > 0 else 0
750
- },
751
- 'إجمالي_تكاليف_مباشرة': total_direct_cost
752
- },
753
- 'مصاريف_إدارية': {
754
- 'نسبة': admin_percentage * 100,
755
- 'قيمة': admin_cost
756
- },
757
- 'هامش_ربح': {
758
- 'نسبة': profit_percentage * 100,
759
- 'قيمة': profit_margin
760
- },
761
- 'التكلفة_الإجمالية': total_cost,
762
- 'عوامل_التعديل': {
763
- 'المعامل_الإجمالي': adjustment_factor,
764
- 'التفاصيل': adjustment_factors
765
- },
766
- 'التكلفة_النهائية_المعدلة': adjusted_total_cost,
767
- 'تفاصيل_البنود': items_costs,
768
- 'عدد_البنود': len(items)
769
- }
770
-
771
- return result
772
 
773
- def get_rate_info(self, item_type: str, item_name: str) -> Dict[str, Any]:
774
- """
775
- الحصول على معلومات تفصيلية عن معدل وسعر عنصر محدد (مادة، عمالة، معدة)
776
-
777
- المعلمات:
778
- item_type (str): نوع العنصر - 'مادة'، 'عمالة'، 'معدة'
779
- item_name (str): اسم العنصر
780
-
781
- العوائد:
782
- dict: معلومات تفصيلية عن العنصر
783
- """
784
- # تحديد القاموس المناسب حسب نوع العنصر
785
- if item_type == 'مادة':
786
- rates_dict = self.material_rates
787
- elif item_type == 'عمالة':
788
- rates_dict = self.labor_rates
789
- elif item_type == 'معدة':
790
- rates_dict = self.equipment_rates
791
- else:
792
- return {'خطأ': 'نوع العنصر غير صحيح'}
793
-
794
- # البحث عن العنصر في القاموس
795
- if item_name in rates_dict:
796
- return rates_dict[item_name]
797
- else:
798
- return {'خطأ': 'العنصر غير موجود'}
799
 
800
- def get_all_rates(self, item_type: str = None, category: str = None) -> Dict[str, Any]:
801
- """
802
- الحصول على قوائم معدلات الأسعار (لجميع المواد أو العمالة أو المعدات)
803
-
804
- المعلمات:
805
- item_type (str, optional): نوع العنصر - 'مادة'، 'عمالة'، 'معدة'، أو None لجميع الأنواع
806
- category (str, optional): فئة محددة للتصفية
807
-
808
- العوائد:
809
- dict: قوائم معدلات الأسعار
810
- """
811
- result = {}
812
-
813
- # جمع المواد حسب الفئة
814
- if item_type is None or item_type == 'مادة':
815
- materials = {}
816
- for name, info in self.material_rates.items():
817
- if category is None or info.get('فئة') == category:
818
- materials[name] = info
819
- result['المواد'] = materials
820
-
821
- # جمع العمالة حسب الفئة
822
- if item_type is None or item_type == 'عمالة':
823
- labor = {}
824
- for name, info in self.labor_rates.items():
825
- if category is None or info.get('فئة') == category:
826
- labor[name] = info
827
- result['العمالة'] = labor
828
-
829
- # جمع المعدات حسب الفئة
830
- if item_type is None or item_type == 'معدة':
831
- equipment = {}
832
- for name, info in self.equipment_rates.items():
833
- if category is None or info.get('فئة') == category:
834
- equipment[name] = info
835
- result['المعدات'] = equipment
836
-
837
- return result
838
 
839
- def generate_sample_project_data(self) -> Dict[str, Any]:
840
- """
841
- توليد بيانات نموذجية لمشروع بناء صغير للاختبار
842
-
843
- العوائد:
844
- dict: بيانات المشروع النموذجية
845
- """
846
- # إنشاء بيانات المشروع
847
- project_data = {
848
- 'اسم_المشروع': 'مبنى سكني صغير',
849
- 'وصف_المشروع': 'مبنى سكني مكون من دور أرضي بمساحة 250 متر مربع',
850
- 'المصاريف_الإدارية': 0.05, # 5%
851
- 'هامش_الربح': 0.10, # 10%
852
- 'عوامل_التعديل': {
853
- 'location_factor': 1.2, # معامل الموقع (منطقة مرتفعة التكلفة)
854
- 'time_factor': 1.0, # معامل الوقت
855
- 'risk_factor': 1.05, # معامل المخاطر
856
- 'market_factor': 1.0 # معامل السوق
857
- },
858
- 'البنود': [
859
- # الأساسات
860
- {
861
- 'وصف_البند': 'حفر الأساسات بعمق 2 متر',
862
- 'الكمية': 150.0,
863
- 'الوحدة': 'م3',
864
- 'المواد': [],
865
- 'العمالة': [
866
- {'النوع': 'عامل خرسانة', 'العدد': 4, 'المدة': 3}
867
- ],
868
- 'المعدات': [
869
- {'النوع': 'حفار متوسط', 'العدد': 1, 'المدة': 2}
870
- ]
871
- },
872
- {
873
- 'وصف_البند': 'توريد وصب خرسانة عادية للأساسات',
874
- 'الكمية': 25.0,
875
- 'الوحدة': 'م3',
876
- 'المواد': [
877
- {'الاسم': 'خرسانة جاهزة', 'الكمية': 25.0}
878
- ],
879
- 'العمالة': [
880
- {'النوع': 'عامل خرسانة', 'العدد': 6, 'المدة': 1}
881
- ],
882
- 'المعدات': [
883
- {'النوع': 'مضخة خرسانة', 'العدد': 1, 'المدة': 0.5}
884
- ]
885
- },
886
- {
887
- 'وصف_البند': 'توريد وتركيب حديد تسليح للأساسات',
888
- 'الكمية': 3.5,
889
- 'الوحدة': 'طن',
890
- 'المواد': [
891
- {'الاسم': 'حديد تسليح', 'الكمية': 3.5}
892
- ],
893
- 'العمالة': [
894
- {'النوع': 'حداد مسلح', 'العدد': 4, 'المدة': 3}
895
- ],
896
- 'المعدات': []
897
- },
898
- {
899
- 'وصف_البند': 'نجارة وفك شدة الأساسات',
900
- 'الكمية': 120.0,
901
- 'الوحدة': 'م2',
902
- 'المواد': [],
903
- 'العمالة': [
904
- {'النوع': 'نجار مسلح', 'العدد': 4, 'المدة': 3}
905
- ],
906
- 'المعدات': []
907
- },
908
- {
909
- 'وصف_البند': 'توريد وصب خرسانة مسلحة للأساسات',
910
- 'الكمية': 30.0,
911
- 'الوحدة': 'م3',
912
- 'المواد': [
913
- {'الاسم': 'خرسانة جاهزة', 'الكمية': 30.0}
914
- ],
915
- 'العمالة': [
916
- {'النوع': 'عامل خرسانة', 'العدد': 6, 'المدة': 1}
917
- ],
918
- 'المعدات': [
919
- {'النوع': 'مضخة خرسانة', 'العدد': 1, 'المدة': 0.5},
920
- {'النوع': 'هزاز خرسانة', 'العدد': 2, 'المدة': 1}
921
- ]
922
- },
923
-
924
- # الأعمدة والأسقف
925
- {
926
- 'وصف_البند': 'توريد وتركيب حديد تسليح للأعمدة',
927
- 'الكمية': 2.8,
928
- 'الوحدة': 'طن',
929
- 'المواد': [
930
- {'الاسم': 'حديد تسليح', 'الكمية': 2.8}
931
- ],
932
- 'العمالة': [
933
- {'النوع': 'حداد مسلح', 'العدد': 4, 'المدة': 3}
934
- ],
935
- 'المعدات': []
936
- },
937
- {
938
- 'وصف_البند': 'نجارة وفك شدة الأعمدة',
939
- 'الكمية': 85.0,
940
- 'الوحدة': 'م2',
941
- 'المواد': [],
942
- 'العمالة': [
943
- {'النوع': 'نجار مسلح', 'العدد': 3, 'المدة': 3}
944
- ],
945
- 'المعدات': []
946
- },
947
- {
948
- 'وصف_البند': 'توريد وصب خرسانة مسلحة للأعمدة',
949
- 'الكمية': 12.0,
950
- 'الوحدة': 'م3',
951
- 'المواد': [
952
- {'الاسم': 'خرسانة جاهزة', 'الكمية': 12.0}
953
- ],
954
- 'العمالة': [
955
- {'النوع': 'عامل خرسانة', 'العدد': 4, 'المدة': 1}
956
- ],
957
- 'المعدات': [
958
- {'النوع': 'مضخة خرسانة', 'العدد': 1, 'المدة': 0.5},
959
- {'النوع': 'هزاز خرسانة', 'العدد': 2, 'المدة': 1}
960
- ]
961
- },
962
-
963
- # أعمال البناء
964
- {
965
- 'وصف_البند': 'توريد وبناء حوائط من الطوب الأحمر',
966
- 'الكمية': 220.0,
967
- 'الوحدة': 'م2',
968
- 'المواد': [
969
- {'الاسم': 'طوب أحمر', 'الكمية': 16.5} # بالألف
970
- ],
971
- 'العمالة': [
972
- {'النوع': 'بناء', 'العدد': 4, 'المدة': 8},
973
- {'النوع': 'مساعد بناء', 'العدد': 4, 'المدة': 8}
974
- ],
975
- 'المعدات': []
976
- },
977
-
978
- # أعمال التشطيبات
979
- {
980
- 'وصف_البند': 'توريد وتركيب بلاط سيراميك للأرضيات',
981
- 'الكمية': 250.0,
982
- 'الوحدة': 'م2',
983
- 'المواد': [
984
- {'الاسم': 'بلاط سيراميك', 'الكمية': 250.0}
985
- ],
986
- 'العمالة': [
987
- {'النوع': 'مبلط', 'العدد': 4, 'المدة': 7}
988
- ],
989
- 'المعدات': []
990
- },
991
- {
992
- 'وصف_البند': 'توريد وتنفيذ دهانات للحوائط',
993
- 'الكمية': 450.0,
994
- 'الوحدة': 'م2',
995
- 'المواد': [
996
- {'الاسم': 'دهانات بلاستيك', 'الكمية': 90.0} # بالتر
997
- ],
998
- 'العمالة': [
999
- {'النوع': 'نقاش', 'العدد': 3, 'المدة': 8}
1000
- ],
1001
- 'المعدات': []
1002
- }
1003
- ]
1004
- }
1005
-
1006
- return project_data
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  """
2
+ حاسبة تكاليف البناء المتكاملة
3
+ تتضمن العناصر التالية:
4
  - المواد الخام
 
5
  - المعدات
6
+ - العمالة
7
  - المصاريف الإدارية
8
  - هامش الربح
9
  """
10
 
11
+ import streamlit as st
12
  import pandas as pd
13
  import numpy as np
14
+ import plotly.express as px
15
+ import plotly.graph_objects as go
 
 
 
16
 
 
 
 
 
 
 
 
 
 
 
 
 
 
17
 
18
+ def render_construction_calculator():
19
+ """
20
+ عرض حاسبة تكاليف البناء المتكاملة
21
+ """
22
+ st.markdown("<h2 class='module-title'>حاسبة تكاليف البناء المتكاملة</h2>", unsafe_allow_html=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
23
 
24
+ # معلومات المشروع
25
+ st.markdown("<h3>معلومات المشروع</h3>", unsafe_allow_html=True)
26
+
27
+ col1, col2 = st.columns(2)
28
+
29
+ with col1:
30
+ project_name = st.text_input("اسم المشروع", "مشروع سكني")
31
+ project_location = st.text_input("موقع المشروع", "الرياض - حي النرجس")
32
+
33
+ with col2:
34
+ project_area = st.number_input("المساحة الإجمالية (م²)", min_value=1, value=500)
35
+ project_type = st.selectbox(
36
+ "نوع المشروع",
37
+ options=[
38
+ "سكني", "تجاري", "صناعي", "إداري", "صحي", "تعليمي",
39
+ "بنية تحتية", "طرق", "جسور", "أخرى"
40
+ ]
41
+ )
42
+
43
+ # التبويبات الرئيسية للحاسبة
44
+ tabs = st.tabs([
45
+ "المواد الخام", "المعدات", "العمالة", "المصاريف الإدارية", "هامش الربح", "التقرير النهائي"
46
+ ])
47
+
48
+ # تعريف المتغيرات العامة
49
+ if "materials_cost" not in st.session_state:
50
+ st.session_state.materials_cost = 0.0
51
+ if "equipment_cost" not in st.session_state:
52
+ st.session_state.equipment_cost = 0.0
53
+ if "labor_cost" not in st.session_state:
54
+ st.session_state.labor_cost = 0.0
55
+ if "admin_cost" not in st.session_state:
56
+ st.session_state.admin_cost = 0.0
57
+ if "profit_margin" not in st.session_state:
58
+ st.session_state.profit_margin = 10.0
59
+ if "materials" not in st.session_state:
60
+ st.session_state.materials = []
61
+ if "equipment" not in st.session_state:
62
+ st.session_state.equipment = []
63
+ if "labor" not in st.session_state:
64
+ st.session_state.labor = []
65
+ if "admin_expenses" not in st.session_state:
66
+ st.session_state.admin_expenses = []
67
+
68
+ # تبويب المواد الخام
69
+ with tabs[0]:
70
+ render_materials_tab()
71
+
72
+ # تبويب المعدات
73
+ with tabs[1]:
74
+ render_equipment_tab()
75
+
76
+ # تبويب العمالة
77
+ with tabs[2]:
78
+ render_labor_tab()
79
+
80
+ # تبويب المصاريف الإدارية
81
+ with tabs[3]:
82
+ render_admin_tab()
83
+
84
+ # تبويب هامش الربح
85
+ with tabs[4]:
86
+ render_profit_tab()
87
+
88
+ # تبويب التقرير النهائي
89
+ with tabs[5]:
90
+ render_final_report(project_name, project_location, project_area, project_type)
91
+
92
+
93
+ def render_materials_tab():
94
+ """
95
+ عرض تبويب المواد الخام
96
+ """
97
+ st.markdown("<h3>تكاليف المواد الخام</h3>", unsafe_allow_html=True)
98
+
99
+ # إضافة مادة جديدة
100
+ st.markdown("<h4>إضافة مادة جديدة</h4>", unsafe_allow_html=True)
101
+
102
+ col1, col2, col3, col4 = st.columns(4)
103
+
104
+ with col1:
105
+ material_name = st.text_input("اسم المادة", key="new_material_name")
106
+ with col2:
107
+ material_quantity = st.number_input("الكمية", min_value=0.0, step=0.1, key="new_material_quantity")
108
+ with col3:
109
+ material_unit = st.selectbox(
110
+ "الوحدة",
111
+ options=["م²", "م³", "طن", "كجم", "لتر", "قطعة", "لفة", "كيس", "أخرى"],
112
+ key="new_material_unit"
113
+ )
114
+ with col4:
115
+ material_price = st.number_input("السعر للوحدة (ريال)", min_value=0.0, step=0.01, key="new_material_price")
116
+
117
+ if st.button("إضافة مادة", key="add_material_btn"):
118
+ total_price = material_quantity * material_price
119
+ new_material = {
120
+ "name": material_name,
121
+ "quantity": material_quantity,
122
+ "unit": material_unit,
123
+ "price": material_price,
124
+ "total": total_price
125
  }
126
+ st.session_state.materials.append(new_material)
127
+ st.success(f"تمت إضافة {material_name} بنجاح!")
128
+
129
+ # عرض قائمة المواد المضافة
130
+ if st.session_state.materials:
131
+ st.markdown("<h4>قائمة المواد المضافة</h4>", unsafe_allow_html=True)
132
 
133
+ materials_df = pd.DataFrame(st.session_state.materials)
134
+ materials_df.columns = ["اسم المادة", "الكمية", "الوحدة", "السعر للوحدة", "التكلفة الإجمالية"]
135
+ st.dataframe(materials_df)
 
 
 
 
 
 
136
 
137
+ total_materials_cost = sum(item["total"] for item in st.session_state.materials)
138
+ st.session_state.materials_cost = total_materials_cost
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
139
 
140
+ st.markdown(f"<h4>إجمالي تكلفة المواد: <span style='color:var(--primary-color)'>{total_materials_cost:,.2f} ريال</span></h4>", unsafe_allow_html=True)
 
 
 
 
 
 
 
 
141
 
142
+ # رسم بياني للمواد حسب التكلفة
143
+ if len(st.session_state.materials) > 1:
144
+ st.markdown("<h4>توزيع تكاليف المواد</h4>", unsafe_allow_html=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
145
 
146
+ fig = px.pie(
147
+ materials_df,
148
+ values="التكلفة الإجمالية",
149
+ names="اسم المادة",
150
+ title="توزيع تكاليف المواد",
151
+ color_discrete_sequence=px.colors.sequential.Teal,
152
+ hole=0.4
153
+ )
154
+ fig.update_layout(
155
+ font=dict(family="Almarai, Arial", size=14),
156
+ margin=dict(t=50, b=50, l=20, r=20)
157
+ )
158
+ st.plotly_chart(fig, use_container_width=True)
159
+
160
+ st.markdown("---")
161
+
162
+ # استيراد بيانات المواد من ملف
163
+ st.markdown("<h4>استيراد بيانات المواد من ملف</h4>", unsafe_allow_html=True)
164
+ uploaded_file = st.file_uploader("اختر ملف Excel أو CSV", type=["xlsx", "csv"], key="materials_upload")
165
+
166
+ if uploaded_file is not None:
167
+ if uploaded_file.name.endswith('.csv'):
168
+ df = pd.read_csv(uploaded_file)
169
+ else:
170
+ df = pd.read_excel(uploaded_file)
171
+
172
+ st.success("تم استيراد البيانات بنجاح!")
173
+ st.dataframe(df)
174
+
175
+ if st.button("إضافة المواد من الملف"):
176
+ try:
177
+ # تحويل أسماء الأعمدة للمطابقة مع النظام
178
+ column_mapping = {
179
+ "المادة": "name",
180
+ "اسم المادة": "name",
181
+ "الكمية": "quantity",
182
+ "الوحدة": "unit",
183
+ "السعر": "price",
184
+ "سعر الوحدة": "price"
185
  }
186
+
187
+ mapped_df = df.rename(columns=column_mapping)
188
+
189
+ # حساب التكلفة الإجمالية لكل مادة
190
+ for _, row in mapped_df.iterrows():
191
+ total_price = row["quantity"] * row["price"]
192
+ new_material = {
193
+ "name": row["name"],
194
+ "quantity": row["quantity"],
195
+ "unit": row["unit"],
196
+ "price": row["price"],
197
+ "total": total_price
198
+ }
199
+ st.session_state.materials.append(new_material)
200
+
201
+ st.success("تمت إضافة جميع المواد من الملف بنجاح!")
202
+
203
+ except Exception as e:
204
+ st.error(f"حدث خطأ: {str(e)}")
205
+ st.error("تأكد من أن الملف يحتوي على الأعمدة المطلوبة: اسم المادة، الكمية، الوحدة، السعر للوحدة")
206
+
207
+
208
+ def render_equipment_tab():
209
+ """
210
+ عرض تبويب المعدات
211
+ """
212
+ st.markdown("<h3>تكاليف المعدات</h3>", unsafe_allow_html=True)
213
 
214
+ # إضافة معدة جديدة
215
+ st.markdown("<h4>إضافة معدة جديدة</h4>", unsafe_allow_html=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
216
 
217
+ col1, col2, col3 = st.columns(3)
218
+
219
+ with col1:
220
+ equipment_name = st.text_input("اسم المعدة", key="new_equipment_name")
221
+ with col2:
222
+ rental_type = st.selectbox(
223
+ "نوع الإيجار",
224
+ options=["يومي", "أسبوعي", "شهري", "سنوي", "مملوكة (استهلاك)"],
225
+ key="rental_type"
226
+ )
227
+ with col3:
228
+ usage_period = st.number_input(f"مدة الاستخدام ({rental_type})", min_value=1, value=1, key="usage_period")
229
+
230
+ col4, col5, col6 = st.columns(3)
231
+
232
+ with col4:
233
+ equipment_rate = st.number_input(f"سعر الإيجار لكل ({rental_type}) (ريال)", min_value=0.0, step=0.01, key="equipment_rate")
234
+ with col5:
235
+ fuel_cost = st.number_input("تكلفة الوقود اليومية (ريال)", min_value=0.0, step=0.01, key="fuel_cost")
236
+ with col6:
237
+ operator_cost = st.number_input("تكلفة المشغل اليومية (ريال)", min_value=0.0, step=0.01, key="operator_cost")
238
+
239
+ # حساب إجمالي التكلفة
240
+ rental_days = {
241
+ "يومي": 1,
242
+ "أسبوعي": 7,
243
+ "شهري": 30,
244
+ "سنوي": 365,
245
+ "مملوكة (استهلاك)": 1
246
+ }
247
+
248
+ total_days = usage_period * rental_days[rental_type]
249
+ total_equipment_cost = equipment_rate * usage_period
250
+ total_fuel_cost = fuel_cost * total_days
251
+ total_operator_cost = operator_cost * total_days
252
+ total_cost = total_equipment_cost + total_fuel_cost + total_operator_cost
253
+
254
+ if st.button("إضافة معدة", key="add_equipment_btn"):
255
+ new_equipment = {
256
+ "name": equipment_name,
257
+ "rental_type": rental_type,
258
+ "usage_period": usage_period,
259
+ "equipment_rate": equipment_rate,
260
+ "fuel_cost": fuel_cost,
261
+ "operator_cost": operator_cost,
262
+ "total": total_cost
 
 
 
263
  }
264
+ st.session_state.equipment.append(new_equipment)
265
+ st.success(f"تمت إضافة {equipment_name} بنجاح!")
266
 
267
+ # عرض تفاصيل الحساب
268
+ st.markdown("<div class='card' style='margin-top: 10px;'>", unsafe_allow_html=True)
269
+ st.markdown(f"<p>عدد أيام الاستخدام الإجمالية: {total_days} يوم</p>", unsafe_allow_html=True)
270
+ st.markdown(f"<p>تكلفة إيجار المعدة: {total_equipment_cost:,.2f} ريال</p>", unsafe_allow_html=True)
271
+ st.markdown(f"<p>تكلفة الوقود: {total_fuel_cost:,.2f} ريال</p>", unsafe_allow_html=True)
272
+ st.markdown(f"<p>تكلفة المشغل: {total_operator_cost:,.2f} ريال</p>", unsafe_allow_html=True)
273
+ st.markdown(f"<h4>التكلفة الإجمالية للمعدة: {total_cost:,.2f} ريال</h4>", unsafe_allow_html=True)
274
+ st.markdown("</div>", unsafe_allow_html=True)
275
+
276
+ # عرض قائمة المعدات المضافة
277
+ if st.session_state.equipment:
278
+ st.markdown("<h4>قائمة المعدات المضافة</h4>", unsafe_allow_html=True)
279
+
280
+ equipment_data = []
281
+ for item in st.session_state.equipment:
282
+ equipment_data.append({
283
+ "اسم المعدة": item["name"],
284
+ "نوع الإيجار": item["rental_type"],
285
+ "مدة الاستخدام": item["usage_period"],
286
+ "إيجار الوحدة": item["equipment_rate"],
287
+ "تكلفة الوقود": item["fuel_cost"],
288
+ "تكلفة المشغل": item["operator_cost"],
289
+ "التكلفة الإجمالية": item["total"]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
290
  })
291
 
292
+ equipment_df = pd.DataFrame(equipment_data)
293
+ st.dataframe(equipment_df)
 
 
 
 
 
 
294
 
295
+ total_equipment_cost = sum(item["total"] for item in st.session_state.equipment)
296
+ st.session_state.equipment_cost = total_equipment_cost
 
 
 
 
297
 
298
+ st.markdown(f"<h4>إجمالي تكلفة المعدات: <span style='color:var(--primary-color)'>{total_equipment_cost:,.2f} ريال</span></h4>", unsafe_allow_html=True)
 
 
 
 
299
 
300
+ # رسم بياني للمعدات حسب التكلفة
301
+ if len(st.session_state.equipment) > 1:
302
+ st.markdown("<h4>توزيع تكاليف المعدات</h4>", unsafe_allow_html=True)
 
303
 
304
+ fig = go.Figure()
 
 
 
 
 
 
305
 
306
+ fig.add_trace(go.Bar(
307
+ x=[item["اسم المعدة"] for item in equipment_data],
308
+ y=[item["التكلفة الإجمالية"] for item in equipment_data],
309
+ name="التكلفة الإجمالية",
310
+ marker_color="teal"
311
+ ))
312
 
313
+ fig.update_layout(
314
+ title="تكاليف المعدات",
315
+ xaxis_title="المعدة",
316
+ yaxis_title="التكلفة (ريال)",
317
+ font=dict(family="Almarai, Arial", size=14),
318
+ margin=dict(t=50, b=50, l=20, r=20)
319
+ )
320
+
321
+ st.plotly_chart(fig, use_container_width=True)
322
+
323
+
324
+ def render_labor_tab():
325
+ """
326
+ عرض تبويب العمالة
327
+ """
328
+ st.markdown("<h3>تكاليف العمالة</h3>", unsafe_allow_html=True)
329
+
330
+ # إضافة عامل أو مجموعة عمال
331
+ st.markdown("<h4>إضافة عمالة جديدة</h4>", unsafe_allow_html=True)
332
+
333
+ col1, col2, col3 = st.columns(3)
334
+
335
+ with col1:
336
+ labor_type = st.text_input("نوع العمالة", key="new_labor_type")
337
+ with col2:
338
+ labor_count = st.number_input("العدد", min_value=1, value=1, key="new_labor_count")
339
+ with col3:
340
+ payment_type = st.selectbox(
341
+ "نوع الدفع",
342
+ options=["يومي", "أسبوعي", "شهري", "بالقطعة"],
343
+ key="new_payment_type"
344
+ )
345
+
346
+ col4, col5, col6 = st.columns(3)
347
+
348
+ with col4:
349
+ wage_rate = st.number_input(f"الأجرة ({payment_type}) (ريال)", min_value=0.0, step=0.01, key="new_wage_rate")
350
+ with col5:
351
+ work_period = st.number_input(f"مدة العمل ({payment_type})", min_value=1, value=30, key="new_work_period")
352
+ with col6:
353
+ benefits_percent = st.slider("نسبة البدلات والتأمين (%)", min_value=0, max_value=50, value=15, key="new_benefits_percent")
354
+
355
+ # حساب إجمالي التكلفة
356
+ days_factor = {
357
+ "يومي": 1,
358
+ "أسبوعي": 7,
359
+ "شهري": 30,
360
+ "بالقطعة": 1
361
+ }
362
+
363
+ monthly_days = work_period * days_factor[payment_type] / 30 # تحويل الأيام إلى شهور
364
+
365
+ if payment_type == "بالقطعة":
366
+ total_labor_cost = labor_count * wage_rate * work_period
367
+ else:
368
+ # حساب الراتب الشهري
369
+ monthly_wage = wage_rate * 30 / days_factor[payment_type]
370
+ # حساب تكلفة البدلات والتأمين
371
+ benefits_cost = monthly_wage * (benefits_percent / 100)
372
+ # إجمالي التكلفة الشهرية
373
+ monthly_total_cost = monthly_wage + benefits_cost
374
+ # إجمالي التكلفة
375
+ total_labor_cost = labor_count * monthly_total_cost * monthly_days
376
+
377
+ if st.button("إضافة عمالة"):
378
+ new_labor = {
379
+ "type": labor_type,
380
+ "count": labor_count,
381
+ "payment_type": payment_type,
382
+ "wage_rate": wage_rate,
383
+ "work_period": work_period,
384
+ "benefits_percent": benefits_percent,
385
+ "total": total_labor_cost
386
  }
387
+ st.session_state.labor.append(new_labor)
388
+ st.success(f"تمت إضافة {labor_type} بنجاح!")
389
+
390
+ # عرض تفاصيل الحساب
391
+ st.markdown("<div class='card' style='margin-top: 10px;'>", unsafe_allow_html=True)
392
+ if payment_type != "بالقطعة":
393
+ monthly_wage = wage_rate * 30 / days_factor[payment_type]
394
+ benefits_cost = monthly_wage * (benefits_percent / 100)
395
+ monthly_total_cost = monthly_wage + benefits_cost
396
+
397
+ st.markdown(f"<p>الراتب الشهري للعامل: {monthly_wage:,.2f} ريال</p>", unsafe_allow_html=True)
398
+ st.markdown(f"<p>تكلفة البدلات والتأمين الشهرية: {benefits_cost:,.2f} ريال</p>", unsafe_allow_html=True)
399
+ st.markdown(f"<p>إجمالي التكلفة الشهرية للعامل: {monthly_total_cost:,.2f} ريال</p>", unsafe_allow_html=True)
400
+ st.markdown(f"<p>مدة العمل بالشهور: {monthly_days:.2f} شهر</p>", unsafe_allow_html=True)
401
+ else:
402
+ st.markdown(f"<p>سعر القطعة: {wage_rate:,.2f} ريال</p>", unsafe_allow_html=True)
403
+ st.markdown(f"<p>عدد القطع: {work_period}</p>", unsafe_allow_html=True)
404
+
405
+ st.markdown(f"<p>عدد العمال: {labor_count}</p>", unsafe_allow_html=True)
406
+ st.markdown(f"<h4>التكلفة الإجمالية للعمالة: {total_labor_cost:,.2f} ريال</h4>", unsafe_allow_html=True)
407
+ st.markdown("</div>", unsafe_allow_html=True)
408
 
409
+ # عرض قائمة العمالة المضافة
410
+ if st.session_state.labor:
411
+ st.markdown("<h4>قائمة العمالة المضافة</h4>", unsafe_allow_html=True)
412
+
413
+ labor_data = []
414
+ for item in st.session_state.labor:
415
+ labor_data.append({
416
+ "نوع العمالة": item["type"],
417
+ "العدد": item["count"],
418
+ "نوع الدفع": item["payment_type"],
419
+ "معدل الأجرة": item["wage_rate"],
420
+ "مدة العمل": item["work_period"],
421
+ "نسبة البدلات": f"{item['benefits_percent']}%",
422
+ "التكلفة الإجمالية": item["total"]
423
+ })
424
 
425
+ labor_df = pd.DataFrame(labor_data)
426
+ st.dataframe(labor_df)
427
 
428
+ total_labor_cost = sum(item["total"] for item in st.session_state.labor)
429
+ st.session_state.labor_cost = total_labor_cost
 
 
 
 
430
 
431
+ st.markdown(f"<h4>إجمالي تكلفة العمالة: <span style='color:var(--primary-color)'>{total_labor_cost:,.2f} ريال</span></h4>", unsafe_allow_html=True)
 
 
 
432
 
433
+ # رسم بياني للعمالة حسب التكلفة
434
+ if len(st.session_state.labor) > 1:
435
+ st.markdown("<h4>توزيع تكاليف العمالة</h4>", unsafe_allow_html=True)
436
+
437
+ fig = px.bar(
438
+ labor_df,
439
+ x="نوع العمالة",
440
+ y="التكلفة الإجمالية",
441
+ color="العدد",
442
+ title="توزيع تكاليف العمالة",
443
+ color_continuous_scale=px.colors.sequential.Teal
444
+ )
445
+ fig.update_layout(
446
+ font=dict(family="Almarai, Arial", size=14),
447
+ margin=dict(t=50, b=50, l=20, r=20)
448
+ )
449
+ st.plotly_chart(fig, use_container_width=True)
450
+
451
+
452
+ def render_admin_tab():
453
+ """
454
+ عرض تبويب المصاريف الإدارية
455
+ """
456
+ st.markdown("<h3>المصاريف الإدارية والعمومية</h3>", unsafe_allow_html=True)
457
 
458
+ # إضافة مصروف جديد
459
+ st.markdown("<h4>إضافة مصروف جديد</h4>", unsafe_allow_html=True)
460
+
461
+ col1, col2, col3 = st.columns(3)
462
+
463
+ with col1:
464
+ expense_name = st.text_input("اسم المصروف", key="new_expense_name")
465
+ with col2:
466
+ expense_type = st.selectbox(
467
+ "نوع المصروف",
468
+ options=[
469
+ "رواتب إدارية", "إيجارات", "مكتبية", "سفر", "تأمين",
470
+ "استشارات", "رسوم حكومية", "منافع", "أخرى"
471
+ ],
472
+ key="new_expense_type"
473
+ )
474
+ with col3:
475
+ expense_amount = st.number_input("المبلغ (ريال)", min_value=0.0, step=100.0, key="new_expense_amount")
476
+
477
+ if st.button("إضافة مصروف"):
478
+ new_expense = {
479
+ "name": expense_name,
480
+ "type": expense_type,
481
+ "amount": expense_amount
482
+ }
483
+ st.session_state.admin_expenses.append(new_expense)
484
+ st.success(f"تمت إضافة {expense_name} بنجاح!")
485
+
486
+ # عرض قائمة المصاريف المضافة
487
+ if st.session_state.admin_expenses:
488
+ st.markdown("<h4>قائمة المصاريف الإدارية</h4>", unsafe_allow_html=True)
489
+
490
+ admin_data = []
491
+ for item in st.session_state.admin_expenses:
492
+ admin_data.append({
493
+ "اسم المصروف": item["name"],
494
+ "نوع المصروف": item["type"],
495
+ "المبلغ": item["amount"]
496
+ })
497
 
498
+ admin_df = pd.DataFrame(admin_data)
499
+ st.dataframe(admin_df)
 
 
 
 
 
500
 
501
+ total_admin_cost = sum(item["amount"] for item in st.session_state.admin_expenses)
502
+ st.session_state.admin_cost = total_admin_cost
 
503
 
504
+ st.markdown(f"<h4>إجمالي المصاريف الإدارية: <span style='color:var(--primary-color)'>{total_admin_cost:,.2f} ريال</span></h4>", unsafe_allow_html=True)
 
 
 
 
 
505
 
506
+ # رسم بياني للمصاريف حسب النوع
507
+ if len(st.session_state.admin_expenses) > 1:
508
+ st.markdown("<h4>توزيع المصاريف الإدارية حسب النوع</h4>", unsafe_allow_html=True)
 
509
 
510
+ # تجميع المصاريف حسب النوع
511
+ expense_by_type = admin_df.groupby("نوع المصروف")["المبلغ"].sum().reset_index()
512
 
513
+ fig = px.pie(
514
+ expense_by_type,
515
+ values="المبلغ",
516
+ names="نوع المصروف",
517
+ title="توزيع المصاريف الإدارية",
518
+ color_discrete_sequence=px.colors.sequential.Teal,
519
+ hole=0.4
520
+ )
521
+ fig.update_layout(
522
+ font=dict(family="Almarai, Arial", size=14),
523
+ margin=dict(t=50, b=50, l=20, r=20)
524
+ )
525
+ st.plotly_chart(fig, use_container_width=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
526
 
527
+ # نسبة المصاريف الإدارية
528
+ st.markdown("<h4>احتساب المصاريف الإدارية بالنسبة المئوية</h4>", unsafe_allow_html=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
529
 
530
+ # حساب التكاليف المباشرة
531
+ direct_costs = st.session_state.materials_cost + st.session_state.equipment_cost + st.session_state.labor_cost
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
532
 
533
+ col1, col2 = st.columns(2)
534
+
535
+ with col1:
536
+ admin_percent = st.slider("نسبة المصاريف الإدارية من التكاليف المباشرة (%)", min_value=0, max_value=30, value=10, key="admin_percent")
537
+
538
+ with col2:
539
+ calculated_admin_cost = direct_costs * (admin_percent / 100)
540
+ st.markdown(f"<div class='card'><h4>المصاريف الإدارية بالنسبة: <span style='color:var(--primary-color)'>{calculated_admin_cost:,.2f} ريال</span></h4></div>", unsafe_allow_html=True)
541
+
542
+ if st.button("استخدام النسبة المئوية للمصاريف الإدارية"):
543
+ st.session_state.admin_cost = calculated_admin_cost
544
+ st.success("تم تحديث إجمالي المصاريف الإدارية بناء على النسبة المئوية!")
545
+
546
+
547
+ def render_profit_tab():
548
+ """
549
+ عرض تبويب هامش الربح
550
+ """
551
+ st.markdown("<h3>هامش الربح</h3>", unsafe_allow_html=True)
552
+
553
+ # حساب التكاليف المباشرة والإجمالية
554
+ direct_costs = st.session_state.materials_cost + st.session_state.equipment_cost + st.session_state.labor_cost
555
+ total_costs = direct_costs + st.session_state.admin_cost
556
+
557
+ # عرض ملخص التكاليف
558
+ st.markdown("<div class='card'>", unsafe_allow_html=True)
559
+ st.markdown("<h4>ملخص التكاليف</h4>", unsafe_allow_html=True)
560
+ st.markdown(f"<p>إجمالي تكلفة المواد: <span style='color:var(--text-medium)'>{st.session_state.materials_cost:,.2f} ريال</span></p>", unsafe_allow_html=True)
561
+ st.markdown(f"<p>إجمالي تكلفة المعدات: <span style='color:var(--text-medium)'>{st.session_state.equipment_cost:,.2f} ريال</span></p>", unsafe_allow_html=True)
562
+ st.markdown(f"<p>إجمالي تكلفة العمالة: <span style='color:var(--text-medium)'>{st.session_state.labor_cost:,.2f} ريال</span></p>", unsafe_allow_html=True)
563
+ st.markdown(f"<p>إجمالي التكاليف المباشرة: <span style='color:var(--primary-color)'>{direct_costs:,.2f} ريال</span></p>", unsafe_allow_html=True)
564
+ st.markdown(f"<p>إجمالي المصاريف الإدارية: <span style='color:var(--text-medium)'>{st.session_state.admin_cost:,.2f} ريال</span></p>", unsafe_allow_html=True)
565
+ st.markdown(f"<h4>إجمالي التكاليف: <span style='color:var(--primary-color)'>{total_costs:,.2f} ريال</span></h4>", unsafe_allow_html=True)
566
+ st.markdown("</div>", unsafe_allow_html=True)
567
+
568
+ # تحديد هامش الربح
569
+ st.markdown("<h4>تحديد هامش الربح</h4>", unsafe_allow_html=True)
570
+
571
+ col1, col2 = st.columns(2)
572
+
573
+ with col1:
574
+ profit_margin = st.slider("نسبة هامش الربح (%)", min_value=0, max_value=30, value=int(st.session_state.profit_margin), key="profit_margin_slider")
575
+ st.session_state.profit_margin = profit_margin
576
+
577
+ with col2:
578
+ profit_amount = total_costs * (profit_margin / 100)
579
+ st.markdown(f"<div class='card'><h4>قيمة هامش الربح: <span style='color:var(--primary-color)'>{profit_amount:,.2f} ريال</span></h4></div>", unsafe_allow_html=True)
580
+
581
+ # إجمالي قيمة العرض
582
+ total_price = total_costs + profit_amount
583
+ st.markdown("<div class='card' style='background: var(--primary-light);'>", unsafe_allow_html=True)
584
+ st.markdown(f"<h3>إجمالي قيمة العرض: <span style='color:var(--primary-color)'>{total_price:,.2f} ريال</span></h3>", unsafe_allow_html=True)
585
+ st.markdown("</div>", unsafe_allow_html=True)
586
+
587
+ # تحليل الحساسية لهامش الربح
588
+ st.markdown("<h4>تحليل حساسية هامش الربح</h4>", unsafe_allow_html=True)
589
+
590
+ sensitivity_data = []
591
+ for margin in range(5, 31, 5):
592
+ profit = total_costs * (margin / 100)
593
+ total = total_costs + profit
594
+ sensitivity_data.append({
595
+ "نسبة الربح": f"{margin}%",
596
+ "قيمة الربح": profit,
597
+ "إجمالي العرض": total
598
+ })
599
+
600
+ sensitivity_df = pd.DataFrame(sensitivity_data)
601
+
602
+ # رسم بياني لتحليل الحساسية
603
+ fig = go.Figure()
604
+
605
+ fig.add_trace(go.Bar(
606
+ x=[item["نسبة الربح"] for item in sensitivity_data],
607
+ y=[item["قيمة الربح"] for item in sensitivity_data],
608
+ name="قيمة الربح",
609
+ marker_color="rgba(14, 165, 165, 0.7)"
610
+ ))
611
+
612
+ fig.add_trace(go.Scatter(
613
+ x=[item["نسبة الربح"] for item in sensitivity_data],
614
+ y=[item["إجمالي العرض"] for item in sensitivity_data],
615
+ name="إجمالي العرض",
616
+ mode="lines+markers",
617
+ marker=dict(size=8, color="rgba(255, 154, 60, 1.0)"),
618
+ line=dict(width=3, color="rgba(255, 154, 60, 0.7)")
619
+ ))
620
+
621
+ fig.update_layout(
622
+ title="تحليل حساسية هامش الربح",
623
+ xaxis_title="نسبة الربح",
624
+ yaxis_title="القيمة (ريال)",
625
+ font=dict(family="Almarai, Arial", size=14),
626
+ margin=dict(t=50, b=50, l=20, r=20),
627
+ hovermode="x unified"
628
+ )
629
+
630
+ st.plotly_chart(fig, use_container_width=True)
631
+
632
+ # جدول تحليل الحساسية
633
+ st.dataframe(sensitivity_df)
634
+
635
+
636
+ def render_final_report(project_name, project_location, project_area, project_type):
637
+ """
638
+ عرض التقرير النهائي للتكاليف
639
+ """
640
+ st.markdown("<h3>التقرير النهائي لتكاليف المشروع</h3>", unsafe_allow_html=True)
641
+
642
+ # حساب التكاليف المباشرة والإجمالية
643
+ direct_costs = st.session_state.materials_cost + st.session_state.equipment_cost + st.session_state.labor_cost
644
+ total_costs = direct_costs + st.session_state.admin_cost
645
+ profit_amount = total_costs * (st.session_state.profit_margin / 100)
646
+ total_price = total_costs + profit_amount
647
+
648
+ # معلومات المشروع
649
+ st.markdown("<div class='card'>", unsafe_allow_html=True)
650
+ st.markdown("<h4>معلومات المشروع</h4>", unsafe_allow_html=True)
651
+ col1, col2 = st.columns(2)
652
+
653
+ with col1:
654
+ st.markdown(f"<p><strong>اسم المشروع:</strong> {project_name}</p>", unsafe_allow_html=True)
655
+ st.markdown(f"<p><strong>نوع المشروع:</strong> {project_type}</p>", unsafe_allow_html=True)
656
+
657
+ with col2:
658
+ st.markdown(f"<p><strong>موقع المشروع:</strong> {project_location}</p>", unsafe_allow_html=True)
659
+ st.markdown(f"<p><strong>المساحة الإجمالية:</strong> {project_area} م²</p>", unsafe_allow_html=True)
660
+
661
+ st.markdown("</div>", unsafe_allow_html=True)
662
+
663
+ # ملخص التكاليف
664
+ st.markdown("<div class='card'>", unsafe_allow_html=True)
665
+ st.markdown("<h4>ملخص التكاليف</h4>", unsafe_allow_html=True)
666
+
667
+ col1, col2 = st.columns(2)
668
+
669
+ with col1:
670
+ st.markdown(f"<p><strong>تكلفة المواد:</strong> {st.session_state.materials_cost:,.2f} ��يال</p>", unsafe_allow_html=True)
671
+ st.markdown(f"<p><strong>تكلفة المعدات:</strong> {st.session_state.equipment_cost:,.2f} ريال</p>", unsafe_allow_html=True)
672
+ st.markdown(f"<p><strong>تكلفة العمالة:</strong> {st.session_state.labor_cost:,.2f} ريال</p>", unsafe_allow_html=True)
673
+ st.markdown(f"<p><strong>إجمالي التكاليف المباشرة:</strong> {direct_costs:,.2f} ريال</p>", unsafe_allow_html=True)
674
+
675
+ with col2:
676
+ st.markdown(f"<p><strong>المصاريف الإدارية:</strong> {st.session_state.admin_cost:,.2f} ريال</p>", unsafe_allow_html=True)
677
+ st.markdown(f"<p><strong>إجمالي التكاليف:</strong> {total_costs:,.2f} ريال</p>", unsafe_allow_html=True)
678
+ st.markdown(f"<p><strong>هامش الربح ({st.session_state.profit_margin}%):</strong> {profit_amount:,.2f} ريال</p>", unsafe_allow_html=True)
679
+ st.markdown(f"<h4>إجمالي قيمة العرض: {total_price:,.2f} ريال</h4>", unsafe_allow_html=True)
680
+
681
+ st.markdown("</div>", unsafe_allow_html=True)
682
+
683
+ # عرض التفاصيل بالمتر المربع
684
+ if project_area > 0:
685
+ per_sqm_cost = total_price / project_area
686
+ st.markdown("<div class='card'>", unsafe_allow_html=True)
687
+ st.markdown("<h4>تكلفة المتر المربع</h4>", unsafe_allow_html=True)
688
+ st.markdown(f"<p>تكلفة المتر المربع الإجمالية: <strong>{per_sqm_cost:,.2f} ريال/م²</strong></p>", unsafe_allow_html=True)
689
+ st.markdown("</div>", unsafe_allow_html=True)
690
+ else:
691
+ st.markdown("<div class='card'>", unsafe_allow_html=True)
692
+ st.markdown("<h4>تكلفة المتر المربع</h4>", unsafe_allow_html=True)
693
+ st.markdown("<p>يرجى إدخال مساحة صحيحة للمشروع لحساب تكلفة المتر المربع</p>", unsafe_allow_html=True)
694
+ st.markdown("</div>", unsafe_allow_html=True)
695
+
696
+ # رسم بياني لتوزيع التكاليف
697
+ st.markdown("<h4>توزيع التكاليف</h4>", unsafe_allow_html=True)
698
+
699
+ # تجنب القسمة على صفر
700
+ if total_price > 0:
701
+ cost_distribution = [
702
+ {"النوع": "المواد", "القيمة": st.session_state.materials_cost, "النسبة": st.session_state.materials_cost / total_price * 100},
703
+ {"النوع": "المعدات", "القيمة": st.session_state.equipment_cost, "النسبة": st.session_state.equipment_cost / total_price * 100},
704
+ {"النوع": "العمالة", "القيمة": st.session_state.labor_cost, "النسبة": st.session_state.labor_cost / total_price * 100},
705
+ {"النوع": "المصاريف الإدارية", "القيمة": st.session_state.admin_cost, "النسبة": st.session_state.admin_cost / total_price * 100},
706
+ {"النوع": "هامش الربح", "القيمة": profit_amount, "النسبة": profit_amount / total_price * 100}
707
+ ]
708
+ else:
709
+ # إذا كان المجموع صفر، اجعل جميع النسب المئوية صفر
710
+ cost_distribution = [
711
+ {"النوع": "المواد", "القيمة": st.session_state.materials_cost, "النسبة": 0},
712
+ {"النوع": "المعدات", "القيمة": st.session_state.equipment_cost, "النسبة": 0},
713
+ {"النوع": "العمالة", "القيمة": st.session_state.labor_cost, "النسبة": 0},
714
+ {"النوع": "المصاريف الإدارية", "القيمة": st.session_state.admin_cost, "النسبة": 0},
715
+ {"النوع": "هامش الربح", "القيمة": profit_amount, "النسبة": 0}
716
+ ]
717
+
718
+ cost_df = pd.DataFrame(cost_distribution)
719
+
720
+ fig = px.pie(
721
+ cost_df,
722
+ values="القيمة",
723
+ names="النوع",
724
+ title="توزيع التكاليف والأرباح",
725
+ color_discrete_sequence=px.colors.sequential.Teal,
726
+ hole=0.4
727
+ )
728
+
729
+ fig.update_traces(textposition='inside', textinfo='percent+label')
730
+
731
+ fig.update_layout(
732
+ annotations=[dict(text=f"{total_price:,.0f} ريال", x=0.5, y=0.5, font_size=14, showarrow=False)],
733
+ font=dict(family="Almarai, Arial", size=14),
734
+ margin=dict(t=50, b=50, l=20, r=20)
735
+ )
736
+
737
+ st.plotly_chart(fig, use_container_width=True)
738
+
739
+ # جدول توزيع التكاليف
740
+ st.dataframe(cost_df)
741
+
742
+ # زر لإنشاء تقرير PDF
743
+ col1, col2 = st.columns(2)
744
+
745
+ with col1:
746
+ if st.button("تصدير التقرير إلى PDF"):
747
+ st.success("تم تصدير التقرير بنجاح!")
748
+
749
+ with col2:
750
+ if st.button("حفظ التقرير في قاعدة البيانات"):
751
+ st.success("تم حفظ التقرير في قاعدة البيانات بنجاح!")