EGYADMIN commited on
Commit
4a4508c
·
verified ·
1 Parent(s): bc73e0b

Update modules/pricing/pricing_app.py

Browse files
Files changed (1) hide show
  1. modules/pricing/pricing_app.py +107 -457
modules/pricing/pricing_app.py CHANGED
@@ -1,7 +1,3 @@
1
- """
2
- وحدة التسعير - التطبيق الرئيسي
3
- """
4
-
5
  import streamlit as st
6
  import pandas as pd
7
  import numpy as np
@@ -18,240 +14,25 @@ from pathlib import Path
18
 
19
  class PricingApp:
20
  """وحدة التسعير"""
21
-
22
  def __init__(self):
23
  """تهيئة وحدة التسعير"""
24
-
25
- # تهيئة حالة الجلسة
26
  if 'bill_of_quantities' not in st.session_state:
27
- st.session_state.bill_of_quantities = [
28
- {
29
- 'id': 1,
30
- 'code': 'A-001',
31
- 'description': 'أعمال الحفر والردم',
32
- 'unit': 'م3',
33
- 'quantity': 1500,
34
- 'unit_price': 45,
35
- 'total_price': 67500,
36
- 'category': 'أعمال ترابية'
37
- },
38
- {
39
- 'id': 2,
40
- 'code': 'A-002',
41
- 'description': 'توريد وصب خرسانة عادية',
42
- 'unit': 'م3',
43
- 'quantity': 250,
44
- 'unit_price': 350,
45
- 'total_price': 87500,
46
- 'category': 'أعمال خرسانية'
47
- },
48
- {
49
- 'id': 3,
50
- 'code': 'A-003',
51
- 'description': 'توريد وصب خرسانة مسلحة للأساسات',
52
- 'unit': 'م3',
53
- 'quantity': 180,
54
- 'unit_price': 450,
55
- 'total_price': 81000,
56
- 'category': 'أعمال خرسانية'
57
- },
58
- {
59
- 'id': 4,
60
- 'code': 'A-004',
61
- 'description': 'توريد وصب خرسانة مسلحة للأعمدة',
62
- 'unit': 'م3',
63
- 'quantity': 120,
64
- 'unit_price': 500,
65
- 'total_price': 60000,
66
- 'category': 'أعمال خرسانية'
67
- },
68
- {
69
- 'id': 5,
70
- 'code': 'A-005',
71
- 'description': 'توريد وتركيب حديد تسليح',
72
- 'unit': 'طن',
73
- 'quantity': 45,
74
- 'unit_price': 3000,
75
- 'total_price': 135000,
76
- 'category': 'أعمال حديد'
77
- },
78
- {
79
- 'id': 6,
80
- 'code': 'A-006',
81
- 'description': 'توريد وبناء طابوق',
82
- 'unit': 'م2',
83
- 'quantity': 1200,
84
- 'unit_price': 45,
85
- 'total_price': 54000,
86
- 'category': 'أعمال بناء'
87
- },
88
- {
89
- 'id': 7,
90
- 'code': 'A-007',
91
- 'description': 'أعمال اللياسة والتشطيبات',
92
- 'unit': 'م2',
93
- 'quantity': 2400,
94
- 'unit_price': 35,
95
- 'total_price': 84000,
96
- 'category': 'أعمال تشطيبات'
97
- },
98
- {
99
- 'id': 8,
100
- 'code': 'A-008',
101
- 'description': 'أعمال الدهانات',
102
- 'unit': 'م2',
103
- 'quantity': 2400,
104
- 'unit_price': 25,
105
- 'total_price': 60000,
106
- 'category': 'أعمال تشطيبات'
107
- },
108
- {
109
- 'id': 9,
110
- 'code': 'A-009',
111
- 'description': 'توريد وتركيب أبواب خشبية',
112
- 'unit': 'عدد',
113
- 'quantity': 24,
114
- 'unit_price': 750,
115
- 'total_price': 18000,
116
- 'category': 'أعمال نجارة'
117
- },
118
- {
119
- 'id': 10,
120
- 'code': 'A-010',
121
- 'description': 'توريد وتركيب نوافذ ألمنيوم',
122
- 'unit': 'م2',
123
- 'quantity': 120,
124
- 'unit_price': 350,
125
- 'total_price': 42000,
126
- 'category': 'أعمال ألمنيوم'
127
- }
128
- ]
129
-
130
  if 'cost_analysis' not in st.session_state:
131
- st.session_state.cost_analysis = [
132
- {
133
- 'id': 1,
134
- 'category': 'تكاليف مباشرة',
135
- 'subcategory': 'مواد',
136
- 'description': 'خرسانة',
137
- 'amount': 120000,
138
- 'percentage': 17.9
139
- },
140
- {
141
- 'id': 2,
142
- 'category': 'تكاليف مباشرة',
143
- 'subcategory': 'مواد',
144
- 'description': 'حديد تسليح',
145
- 'amount': 135000,
146
- 'percentage': 20.1
147
- },
148
- {
149
- 'id': 3,
150
- 'category': 'تكاليف مباشرة',
151
- 'subcategory': 'مواد',
152
- 'description': 'طابوق',
153
- 'amount': 54000,
154
- 'percentage': 8.1
155
- },
156
- {
157
- 'id': 4,
158
- 'category': 'تكاليف مباشرة',
159
- 'subcategory': 'عمالة',
160
- 'description': 'عمالة تنفيذ',
161
- 'amount': 120000,
162
- 'percentage': 17.9
163
- },
164
- {
165
- 'id': 5,
166
- 'category': 'تكاليف مباشرة',
167
- 'subcategory': 'معدات',
168
- 'description': 'معدات إنشائية',
169
- 'amount': 85000,
170
- 'percentage': 12.7
171
- },
172
- {
173
- 'id': 6,
174
- 'category': 'تكاليف غير مباشرة',
175
- 'subcategory': 'إدارة',
176
- 'description': 'إدارة المشروع',
177
- 'amount': 45000,
178
- 'percentage': 6.7
179
- },
180
- {
181
- 'id': 7,
182
- 'category': 'تكاليف غير مباشرة',
183
- 'subcategory': 'إدارة',
184
- 'description': 'إشراف هندسي',
185
- 'amount': 35000,
186
- 'percentage': 5.2
187
- },
188
- {
189
- 'id': 8,
190
- 'category': 'تكاليف غير مباشرة',
191
- 'subcategory': 'عامة',
192
- 'description': 'تأمينات وضمانات',
193
- 'amount': 25000,
194
- 'percentage': 3.7
195
- },
196
- {
197
- 'id': 9,
198
- 'category': 'تكاليف غير مباشرة',
199
- 'subcategory': 'عامة',
200
- 'description': 'مصاريف إدارية',
201
- 'amount': 30000,
202
- 'percentage': 4.5
203
- },
204
- {
205
- 'id': 10,
206
- 'category': 'أرباح',
207
- 'subcategory': 'أرباح',
208
- 'description': 'هامش الربح',
209
- 'amount': 55000,
210
- 'percentage': 8.2
211
- }
212
- ]
213
-
214
  if 'price_scenarios' not in st.session_state:
215
- st.session_state.price_scenarios = [
216
- {
217
- 'id': 1,
218
- 'name': 'السيناريو الأساسي',
219
- 'description': 'التسعير الأساسي مع هامش ربح 8%',
220
- 'total_cost': 615000,
221
- 'profit_margin': 8.2,
222
- 'total_price': 670000,
223
- 'is_active': True
224
- },
225
- {
226
- 'id': 2,
227
- 'name': 'سيناريو تنافسي',
228
- 'description': 'تخفيض هامش الربح للمنافسة',
229
- 'total_cost': 615000,
230
- 'profit_margin': 5.0,
231
- 'total_price': 650000,
232
- 'is_active': False
233
- },
234
- {
235
- 'id': 3,
236
- 'name': 'سيناريو مرتفع',
237
- 'description': 'زيادة هامش الربح للمشاريع ذات المخاطر العالية',
238
- 'total_cost': 615000,
239
- 'profit_margin': 12.0,
240
- 'total_price': 700000,
241
- 'is_active': False
242
- }
243
- ]
244
-
245
  def run(self):
246
- """تشغيل وحدة التسعير"""
247
- # استدعاء دالة العرض
248
  self.render()
249
-
250
  def render(self):
251
- """عرض واجهة وحدة التسعير"""
252
-
253
  st.markdown("<h1 class='module-title'>وحدة التسعير</h1>", unsafe_allow_html=True)
254
-
255
  tabs = st.tabs([
256
  "لوحة التحكم",
257
  "جدول الكميات",
@@ -260,184 +41,57 @@ class PricingApp:
260
  "المقارنة التنافسية",
261
  "التقارير"
262
  ])
263
-
264
  with tabs[0]:
265
  self._render_dashboard_tab()
266
-
267
  with tabs[1]:
268
  self._render_bill_of_quantities_tab()
269
-
270
  with tabs[2]:
271
  self._render_cost_analysis_tab()
272
-
273
  with tabs[3]:
274
  self._render_pricing_scenarios_tab()
275
-
276
  with tabs[4]:
277
  self._render_competitive_analysis_tab()
278
-
279
  with tabs[5]:
280
  self._render_reports_tab()
281
-
282
  def _render_dashboard_tab(self):
283
- """عرض تبويب لوحة التحكم"""
284
-
285
  st.markdown("### لوحة تحكم التسعير")
286
-
287
- # عرض ملخص التسعير
288
- col1, col2, col3, col4 = st.columns(4)
289
-
290
- # حساب إجمالي التكاليف
291
- total_direct_cost = sum(item['amount'] for item in st.session_state.cost_analysis if item['category'] == 'تكاليف مباشرة')
292
- total_indirect_cost = sum(item['amount'] for item in st.session_state.cost_analysis if item['category'] == 'تكاليف غير مباشرة')
293
- total_profit = sum(item['amount'] for item in st.session_state.cost_analysis if item['category'] == 'أرباح')
294
- total_cost = total_direct_cost + total_indirect_cost
295
- total_price = total_cost + total_profit
296
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
297
  with col1:
298
- st.metric("إجمالي التكاليف المباشرة", f"{total_direct_cost:,.0f} ريال")
299
-
 
300
  with col2:
301
- st.metric("إجمالي التكاليف غير المباشرة", f"{total_indirect_cost:,.0f} ريال")
302
-
303
- with col3:
304
- st.metric("إجمالي التكاليف", f"{total_cost:,.0f} ريال")
305
-
306
- with col4:
307
- st.metric("السعر الإجمالي", f"{total_price:,.0f} ريال")
308
-
309
- # عرض توزيع التكاليف
310
- st.markdown("### توزيع التكاليف")
311
-
312
- # تجميع البيانات حسب الفئة
313
- cost_categories = {}
314
-
315
- for item in st.session_state.cost_analysis:
316
- category = item['category']
317
- if category in cost_categories:
318
- cost_categories[category] += item['amount']
319
- else:
320
- cost_categories[category] = item['amount']
321
-
322
- # إنشاء DataFrame للرسم البياني
323
- cost_df = pd.DataFrame({
324
- 'الفئة': list(cost_categories.keys()),
325
- 'المبلغ': list(cost_categories.values())
326
- })
327
-
328
- # إنشاء رسم بياني دائري
329
- fig = px.pie(
330
- cost_df,
331
- values='المبلغ',
332
- names='الفئة',
333
- title='توزيع التكاليف حسب الفئة',
334
- color_discrete_sequence=px.colors.qualitative.Set3
335
- )
336
-
337
- st.plotly_chart(fig, use_container_width=True)
338
-
339
- # عرض توزيع التكاليف المباشرة
340
- st.markdown("### توزيع التكاليف المباشرة")
341
-
342
- # تجميع البيانات حسب الفئة الفرعية للتكاليف المباشرة
343
- direct_cost_subcategories = {}
344
-
345
- for item in st.session_state.cost_analysis:
346
- if item['category'] == 'تكاليف مباشرة':
347
- subcategory = item['subcategory']
348
- if subcategory in direct_cost_subcategories:
349
- direct_cost_subcategories[subcategory] += item['amount']
350
- else:
351
- direct_cost_subcategories[subcategory] = item['amount']
352
-
353
- # إنشاء DataFrame للرسم البياني
354
- direct_cost_df = pd.DataFrame({
355
- 'الفئة الفرعية': list(direct_cost_subcategories.keys()),
356
- 'المبلغ': list(direct_cost_subcategories.values())
357
- })
358
-
359
- # إنشاء رسم بياني دائري
360
- fig = px.pie(
361
- direct_cost_df,
362
- values='المبلغ',
363
- names='الفئة الفرعية',
364
- title='توزيع التكاليف المباشرة',
365
- color_discrete_sequence=px.colors.qualitative.Pastel
366
- )
367
-
368
- st.plotly_chart(fig, use_container_width=True)
369
-
370
- # عرض توزيع التكاليف غير المباشرة
371
- st.markdown("### توزيع التكاليف غير المباشرة")
372
-
373
- # تجميع البيانات حسب الفئة الفرعية للتكاليف غير المباشرة
374
- indirect_cost_subcategories = {}
375
-
376
- for item in st.session_state.cost_analysis:
377
- if item['category'] == 'تكاليف غير مباشرة':
378
- subcategory = item['subcategory']
379
- if subcategory in indirect_cost_subcategories:
380
- indirect_cost_subcategories[subcategory] += item['amount']
381
- else:
382
- indirect_cost_subcategories[subcategory] = item['amount']
383
-
384
- # إنشاء DataFrame للرسم البياني
385
- indirect_cost_df = pd.DataFrame({
386
- 'الفئة الفرعية': list(indirect_cost_subcategories.keys()),
387
- 'المبلغ': list(indirect_cost_subcategories.values())
388
- })
389
-
390
- # إنشاء رسم بياني دائري
391
- fig = px.pie(
392
- indirect_cost_df,
393
- values='المبلغ',
394
- names='الفئة الفرعية',
395
- title='توزيع التكاليف غير المباشرة',
396
- color_discrete_sequence=px.colors.qualitative.Pastel1
397
- )
398
-
399
- st.plotly_chart(fig, use_container_width=True)
400
-
401
- def _render_bill_of_quantities_tab(self):
402
- """عرض تبويب جدول الكميات"""
403
-
404
- st.markdown("### جدول الكميات")
405
-
406
- # إنشاء DataFrame من بيانات جدول الكميات
407
- boq_df = pd.DataFrame(st.session_state.bill_of_quantities)
408
-
409
- # عرض جدول الكميات
410
- st.dataframe(
411
- boq_df[['code', 'description', 'unit', 'quantity', 'unit_price', 'total_price', 'category']],
412
- column_config={
413
- 'code': 'الكود',
414
- 'description': 'الوصف',
415
- 'unit': 'الوحدة',
416
- 'quantity': 'الكمية',
417
- 'unit_price': st.column_config.NumberColumn('سعر الوحدة', format='%d ريال'),
418
- 'total_price': st.column_config.NumberColumn('السعر الإجمالي', format='%d ريال'),
419
- 'category': 'الفئة'
420
- },
421
- hide_index=True,
422
- use_container_width=True
423
- )
424
-
425
- # إضافة بند جديد
426
- st.markdown("### إضافة بند جديد")
427
-
428
- col1, col2 = st.columns(2)
429
-
430
- with col1:
431
- new_code = st.text_input("الكود", key="new_boq_code")
432
- new_description = st.text_input("الوصف", key="new_boq_description")
433
- new_unit = st.selectbox("الوحدة", ["م3", "م2", "طن", "عدد", "متر طولي"], key="new_boq_unit")
434
-
435
- with col2:
436
- new_quantity = st.number_input("الكمية", min_value=0.0, step=1.0, key="new_boq_quantity")
437
- new_unit_price = st.number_input("سعر الوحدة", min_value=0.0, step=10.0, key="new_boq_unit_price")
438
- new_category = st.selectbox(
439
- "الفئة",
440
- [
441
  "أعمال ترابية",
442
  "أعمال خرسانية",
443
  "أعمال حديد",
@@ -448,70 +102,66 @@ def _render_bill_of_quantities_tab(self):
448
  "أعمال كهربائية",
449
  "أعمال ميكانيكية",
450
  "أعمال صحية"
451
- ],
452
- key="new_boq_category"
453
- )
454
-
455
- if st.button("إضافة البند", key="add_boq_item"):
456
- if new_code and new_description and new_quantity > 0 and new_unit_price > 0:
457
- # حساب السعر الإجمالي
458
- new_total_price = new_quantity * new_unit_price
459
-
460
- # إضافة بند جديد
461
- new_id = max([item['id'] for item in st.session_state.bill_of_quantities]) + 1
462
-
463
- st.session_state.bill_of_quantities.append({
464
- 'id': new_id,
465
- 'code': new_code,
466
- 'description': new_description,
467
- 'unit': new_unit,
468
- 'quantity': new_quantity,
469
- 'unit_price': new_unit_price,
470
- 'total_price': new_total_price,
471
- 'category': new_category
472
- })
473
-
474
- st.success(f"تمت إضافة البند بنجاح: {new_description}")
475
-
476
- # تحديث الصفحة لعرض البند الجديد
477
- st.rerun()
478
- else:
479
- st.error("يرجى إدخال جميع البيانات المطلوبة بشكل صحيح")
480
-
481
- # عرض ملخص جدول الكميات (إزالة التكرار)
482
- st.markdown("### ملخص جدول الكميات")
483
-
484
- # تجميع البيانات حسب الفئة
485
- category_totals = {}
486
- for item in st.session_state.bill_of_quantities:
487
- category = item['category']
488
- if category in category_totals:
489
- category_totals[category] += item['total_price']
 
 
 
 
 
 
490
  else:
491
- category_totals[category] = item['total_price']
492
-
493
- # إنشاء DataFrame للرسم البياني
494
- category_df = pd.DataFrame({
495
- 'الفئة': list(category_totals.keys()),
496
- 'المبلغ': list(category_totals.values())
497
- })
498
-
499
- # ترتيب البيانات تنازليًا حسب المبلغ
500
- category_df = category_df.sort_values('المبلغ', ascending=False)
501
-
502
- # إنشاء رسم بياني شريطي
503
- fig = px.bar(
504
- category_df,
505
- x='الفئة',
506
- y='المبلغ',
507
- title='إجمالي تكلفة البنود حسب الفئة',
508
- color='الفئة',
509
- text_auto=True
510
- )
511
-
512
- st.plotly_chart(fig, use_container_width=True)
513
-
514
- # حساب إجمالي جدول الكميات
515
- total_boq = sum(item['total_price'] for item in st.session_state.bill_of_quantities)
516
-
517
- st.markdown(f"### إجمالي جدول الكميات: **{total_boq:,.0f} ريال**")
 
 
 
 
 
1
  import streamlit as st
2
  import pandas as pd
3
  import numpy as np
 
14
 
15
  class PricingApp:
16
  """وحدة التسعير"""
17
+
18
  def __init__(self):
19
  """تهيئة وحدة التسعير"""
20
+
 
21
  if 'bill_of_quantities' not in st.session_state:
22
+ st.session_state.bill_of_quantities = []
23
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
24
  if 'cost_analysis' not in st.session_state:
25
+ st.session_state.cost_analysis = []
26
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
27
  if 'price_scenarios' not in st.session_state:
28
+ st.session_state.price_scenarios = []
29
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
30
  def run(self):
 
 
31
  self.render()
32
+
33
  def render(self):
 
 
34
  st.markdown("<h1 class='module-title'>وحدة التسعير</h1>", unsafe_allow_html=True)
35
+
36
  tabs = st.tabs([
37
  "لوحة التحكم",
38
  "جدول الكميات",
 
41
  "المقارنة التنافسية",
42
  "التقارير"
43
  ])
44
+
45
  with tabs[0]:
46
  self._render_dashboard_tab()
 
47
  with tabs[1]:
48
  self._render_bill_of_quantities_tab()
 
49
  with tabs[2]:
50
  self._render_cost_analysis_tab()
 
51
  with tabs[3]:
52
  self._render_pricing_scenarios_tab()
 
53
  with tabs[4]:
54
  self._render_competitive_analysis_tab()
 
55
  with tabs[5]:
56
  self._render_reports_tab()
57
+
58
  def _render_dashboard_tab(self):
 
 
59
  st.markdown("### لوحة تحكم التسعير")
60
+ st.info("سيتم عرض بيانات ملخص التسعير والتحليلات هنا.")
61
+
62
+ def _render_bill_of_quantities_tab(self):
63
+ st.markdown("### جدول الكميات")
64
+
65
+ boq_df = pd.DataFrame(st.session_state.bill_of_quantities)
66
+
67
+ if not boq_df.empty:
68
+ st.dataframe(
69
+ boq_df[['code', 'description', 'unit', 'quantity', 'unit_price', 'total_price', 'category']],
70
+ column_config={
71
+ 'code': 'الكود',
72
+ 'description': 'الوصف',
73
+ 'unit': 'الوحدة',
74
+ 'quantity': 'الكمية',
75
+ 'unit_price': st.column_config.NumberColumn('سعر الوحدة', format='%d ريال'),
76
+ 'total_price': st.column_config.NumberColumn('السعر الإجمالي', format='%d ريال'),
77
+ 'category': 'الفئة'
78
+ },
79
+ hide_index=True,
80
+ use_container_width=True
81
+ )
82
+ else:
83
+ st.warning("لا توجد بيانات في جدول الكميات.")
84
+
85
+ st.markdown("### إضافة بند جديد")
86
+ col1, col2 = st.columns(2)
87
  with col1:
88
+ new_code = st.text_input("الكود", key="new_boq_code")
89
+ new_description = st.text_input("الوصف", key="new_boq_description")
90
+ new_unit = st.selectbox("الوحدة", ["م3", "م2", "طن", "عدد", "متر طولي"], key="new_boq_unit")
91
  with col2:
92
+ new_quantity = st.number_input("الكمية", min_value=0.0, step=1.0, key="new_boq_quantity")
93
+ new_unit_price = st.number_input("سعر الوحدة", min_value=0.0, step=10.0, key="new_boq_unit_price")
94
+ new_category = st.selectbox("الفئة", [
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
95
  "أعمال ترابية",
96
  "أعمال خرسانية",
97
  "أعمال حديد",
 
102
  "أعمال كهربائية",
103
  "أعمال ميكانيكية",
104
  "أعمال صحية"
105
+ ], key="new_boq_category")
106
+
107
+ if st.button("إضافة البند", key="add_boq_item"):
108
+ if new_code and new_description and new_quantity > 0 and new_unit_price > 0:
109
+ new_total_price = new_quantity * new_unit_price
110
+ new_id = max([item['id'] for item in st.session_state.bill_of_quantities], default=0) + 1
111
+ st.session_state.bill_of_quantities.append({
112
+ 'id': new_id,
113
+ 'code': new_code,
114
+ 'description': new_description,
115
+ 'unit': new_unit,
116
+ 'quantity': new_quantity,
117
+ 'unit_price': new_unit_price,
118
+ 'total_price': new_total_price,
119
+ 'category': new_category
120
+ })
121
+ st.success(f"تمت إضافة البند بنجاح: {new_description}")
122
+ st.rerun()
123
+ else:
124
+ st.error("يرجى إدخال جميع البيانات المطلوبة بشكل صحيح")
125
+
126
+ st.markdown("### ملخص جدول الكميات")
127
+ category_totals = {}
128
+ for item in st.session_state.bill_of_quantities:
129
+ category = item['category']
130
+ category_totals[category] = category_totals.get(category, 0) + item['total_price']
131
+
132
+ if category_totals:
133
+ category_df = pd.DataFrame({
134
+ 'الفئة': list(category_totals.keys()),
135
+ 'المبلغ': list(category_totals.values())
136
+ }).sort_values('المبلغ', ascending=False)
137
+
138
+ fig = px.bar(
139
+ category_df,
140
+ x='الفئة',
141
+ y='المبلغ',
142
+ title='إجمالي تكلفة البنود حسب الفئة',
143
+ color='الفئة',
144
+ text_auto=True
145
+ )
146
+ st.plotly_chart(fig, use_container_width=True)
147
+
148
+ total_boq = sum(item['total_price'] for item in st.session_state.bill_of_quantities)
149
+ st.markdown(f"### إجمالي جدول الكميات: **{total_boq:,.0f} ريال**")
150
  else:
151
+ st.info("لم يتم إدخال بنود بعد.")
152
+
153
+ def _render_cost_analysis_tab(self):
154
+ st.markdown("### تحليل التكاليف")
155
+ st.info("سيتم عرض تحليلات التكاليف هنا.")
156
+
157
+ def _render_pricing_scenarios_tab(self):
158
+ st.markdown("### سيناريوهات التسعير")
159
+ st.info("سيتم عرض سيناريوهات التسعير هنا.")
160
+
161
+ def _render_competitive_analysis_tab(self):
162
+ st.markdown("### المقارنة التنافسية")
163
+ st.info("سيتم عرض مقارنة الأسعار والمنافسين هنا.")
164
+
165
+ def _render_reports_tab(self):
166
+ st.markdown("### التقارير")
167
+ st.info("يمكنك هنا تنزيل التقارير الخاصة بالتسعير وجدول الكميات.")