EGYADMIN commited on
Commit
d4f2ca0
·
verified ·
1 Parent(s): 2d90917

Update modules/pricing/pricing_app.py

Browse files
Files changed (1) hide show
  1. modules/pricing/pricing_app.py +131 -1434
modules/pricing/pricing_app.py CHANGED
@@ -57,23 +57,73 @@ class PricingApp:
57
  },
58
  {
59
  'id': 4,
60
- 'code': 'B-001',
 
 
 
 
 
 
 
 
 
 
61
  'description': 'توريد وتركيب حديد تسليح',
62
  'unit': 'طن',
63
- 'quantity': 15,
64
- 'unit_price': 3500,
65
- 'total_price': 52500,
66
  'category': 'أعمال حديد'
67
  },
68
  {
69
- 'id': 5,
70
- 'code': 'C-001',
71
  'description': 'توريد وبناء طابوق',
72
  'unit': 'م2',
73
- 'quantity': 450,
74
- 'unit_price': 120,
75
  'total_price': 54000,
76
  'category': 'أعمال بناء'
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
77
  }
78
  ]
79
 
@@ -84,16 +134,16 @@ class PricingApp:
84
  'category': 'تكاليف مباشرة',
85
  'subcategory': 'مواد',
86
  'description': 'خرسانة',
87
- 'amount': 168500,
88
- 'percentage': 25.2
89
  },
90
  {
91
  'id': 2,
92
  'category': 'تكاليف مباشرة',
93
  'subcategory': 'مواد',
94
  'description': 'حديد تسليح',
95
- 'amount': 52500,
96
- 'percentage': 7.8
97
  },
98
  {
99
  'id': 3,
@@ -192,6 +242,11 @@ class PricingApp:
192
  }
193
  ]
194
 
 
 
 
 
 
195
  def render(self):
196
  """عرض واجهة وحدة التسعير"""
197
 
@@ -301,1460 +356,102 @@ class PricingApp:
301
  'المبلغ': list(direct_cost_subcategories.values())
302
  })
303
 
304
- # إنشاء رسم بياني شريطي
305
- fig = px.bar(
306
  direct_cost_df,
307
- x='الفئة الفرعية',
308
- y='المبلغ',
309
  title='توزيع التكاليف المباشرة',
310
- color='الفئة الفرعية',
311
- text_auto='.2s'
312
  )
313
 
314
  st.plotly_chart(fig, use_container_width=True)
315
 
316
- # عرض مقارنة سيناريوهات التسعير
317
- st.markdown("### مقارنة سيناريوهات التسعير")
318
 
319
- # إنشاء DataFrame للرسم البياني
320
- scenarios_df = pd.DataFrame({
321
- 'السيناريو': [item['name'] for item in st.session_state.price_scenarios],
322
- 'التكلفة الإجمالية': [item['total_cost'] for item in st.session_state.price_scenarios],
323
- 'هامش الربح (%)': [item['profit_margin'] for item in st.session_state.price_scenarios],
324
- 'السعر الإجمالي': [item['total_price'] for item in st.session_state.price_scenarios]
325
- })
326
-
327
- # إنشاء رسم بياني شريطي مزدوج
328
- fig = go.Figure()
329
 
330
- # إضافة شريط للتكلفة الإجمالية
331
- fig.add_trace(go.Bar(
332
- x=scenarios_df['السيناريو'],
333
- y=scenarios_df['التكلفة الإجمالية'],
334
- name='التكلفة الإجمالية',
335
- marker_color='indianred'
336
- ))
337
-
338
- # إضافة شريط للسعر الإجمالي
339
- fig.add_trace(go.Bar(
340
- x=scenarios_df['السيناريو'],
341
- y=scenarios_df['السعر الإجمالي'],
342
- name='السعر الإجمالي',
343
- marker_color='lightsalmon'
344
- ))
345
-
346
- # إضافة خط لهامش الربح
347
- fig.add_trace(go.Scatter(
348
- x=scenarios_df['السيناريو'],
349
- y=scenarios_df['هامش الربح (%)'] * 10000, # تكبير القيم لتظهر على الرسم البياني
350
- name='هامش الربح (%)',
351
- yaxis='y2',
352
- line=dict(color='royalblue', width=4)
353
- ))
354
 
355
- # تعديل تخطيط الرسم البياني
356
- fig.update_layout(
357
- title='مقارنة سيناريوهات التسعير',
358
- xaxis_title='السيناريو',
359
- yaxis_title='المبلغ (ريال)',
360
- yaxis2=dict(
361
- title='هامش الربح (%)',
362
- titlefont=dict(color='royalblue'),
363
- tickfont=dict(color='royalblue'),
364
- overlaying='y',
365
- side='right',
366
- range=[0, 20]
367
- ),
368
- barmode='group',
369
- legend=dict(
370
- x=0,
371
- y=1.2,
372
- orientation='h'
373
- )
374
- )
375
 
376
- # تعديل النص على الأشرطة
377
- fig.update_traces(
378
- texttemplate='%{y:,.0f}',
379
- textposition='outside'
 
 
 
380
  )
381
 
382
  st.plotly_chart(fig, use_container_width=True)
383
-
384
- # عرض مؤشرات الأداء الرئيسية
385
- st.markdown("### مؤشرات الأداء الرئيسية")
386
-
387
- col1, col2, col3 = st.columns(3)
388
-
389
- with col1:
390
- # حساب نسبة التكاليف المباشرة من إجمالي التكاليف
391
- direct_cost_percentage = (total_direct_cost / total_cost) * 100
392
- st.metric("نسبة التكاليف المباشرة", f"{direct_cost_percentage:.1f}%")
393
-
394
- with col2:
395
- # حساب نسبة التكاليف غير المباشرة من إجمالي التكاليف
396
- indirect_cost_percentage = (total_indirect_cost / total_cost) * 100
397
- st.metric("نسبة التكاليف غير المباشرة", f"{indirect_cost_percentage:.1f}%")
398
-
399
- with col3:
400
- # حساب نسبة هامش الربح من السعر الإجمالي
401
- profit_margin = (total_profit / total_price) * 100
402
- st.metric("هامش الربح", f"{profit_margin:.1f}%")
403
 
404
  def _render_bill_of_quantities_tab(self):
405
  """عرض تبويب جدول الكميات"""
406
 
407
  st.markdown("### جدول الكميات")
408
 
409
- # عرض جدول الكميات الحالي
410
- st.markdown("#### قائمة البنود")
411
-
412
- # تحويل قائمة البنود إلى DataFrame
413
  boq_df = pd.DataFrame(st.session_state.bill_of_quantities)
414
 
415
- # عرض البنود كجدول قابل للتعديل
416
- edited_df = st.data_editor(
417
- boq_df,
418
  column_config={
419
- "id": st.column_config.NumberColumn("الرقم", disabled=True),
420
- "code": st.column_config.TextColumn("الكود"),
421
- "description": st.column_config.TextColumn("الوصف"),
422
- "unit": st.column_config.SelectboxColumn(
423
- "الوحدة",
424
- options=["م3", "م2", "طن", "كجم", "عدد", "لتر", "متر"]
425
- ),
426
- "quantity": st.column_config.NumberColumn("الكمية", min_value=0),
427
- "unit_price": st.column_config.NumberColumn("سعر الوحدة (ريال)", min_value=0, format="%.2f"),
428
- "total_price": st.column_config.NumberColumn("السعر الإجمالي (ريال)", min_value=0, format="%.2f", disabled=True),
429
- "category": st.column_config.SelectboxColumn(
430
- "الفئة",
431
- options=["أعمال ترابية", "أعمال خرسانية", "أعمال حديد", "أعمال بناء", "أعمال تشطيب", "أعمال كهربائية", "أعمال ميكانيكية", "أعمال صحية", "أخرى"]
432
- )
433
  },
434
- use_container_width=True,
435
  hide_index=True,
436
- num_rows="dynamic"
437
  )
438
 
439
- # تحديث السعر الإجمالي لكل بند
440
- for i, row in edited_df.iterrows():
441
- edited_df.at[i, 'total_price'] = row['quantity'] * row['unit_price']
442
-
443
- # تحديث قائمة البنود
444
- if not edited_df.equals(boq_df):
445
- st.session_state.bill_of_quantities = edited_df.to_dict('records')
446
- st.success("تم تحديث جدول الكميات بنجاح!")
447
-
448
- # عرض إجمالي جدول الكميات
449
- total_boq = sum(item['total_price'] for item in st.session_state.bill_of_quantities)
450
- st.metric("إجمالي جدول الكميات", f"{total_boq:,.2f} ريال")
451
-
452
  # إضافة بند جديد
453
- st.markdown("#### إضافة بند جديد")
454
-
455
- with st.form(key="add_boq_item_form"):
456
- col1, col2 = st.columns(2)
457
-
458
- with col1:
459
- new_code = st.text_input("الكود", key="new_boq_code")
460
- new_description = st.text_area("الوصف", key="new_boq_description")
461
- new_category = st.selectbox(
462
- "الفئة",
463
- ["أعمال ترابية", "أعمال خرسانية", "أعمال حديد", "أعمال بناء", "أعمال تشطيب", "أعمال كهربائية", "أعمال ميكانيكية", "أعمال صحية", "أخرى"],
464
- key="new_boq_category"
465
- )
466
-
467
- with col2:
468
- new_unit = st.selectbox(
469
- "الوحدة",
470
- ["م3", "م2", "طن", "كجم", "عدد", "لتر", "متر"],
471
- key="new_boq_unit"
472
- )
473
- new_quantity = st.number_input("الكمية", min_value=0.0, key="new_boq_quantity")
474
- new_unit_price = st.number_input("سعر الوحدة (ريال)", min_value=0.0, key="new_boq_unit_price")
475
-
476
- submit_button = st.form_submit_button("إضافة بند")
477
-
478
- if submit_button:
479
- if new_code and new_description:
480
- # إنشاء معرف جديد
481
- new_id = max([item['id'] for item in st.session_state.bill_of_quantities], default=0) + 1
482
-
483
- # حساب السعر الإجمالي
484
- new_total_price = new_quantity * new_unit_price
485
-
486
- # إضافة البند الجديد
487
- st.session_state.bill_of_quantities.append({
488
- 'id': new_id,
489
- 'code': new_code,
490
- 'description': new_description,
491
- 'unit': new_unit,
492
- 'quantity': new_quantity,
493
- 'unit_price': new_unit_price,
494
- 'total_price': new_total_price,
495
- 'category': new_category
496
- })
497
-
498
- st.success(f"تمت إضافة البند '{new_code}' بنجاح!")
499
- st.rerun()
500
- else:
501
- st.error("يرجى إدخال الكود والوصف.")
502
-
503
- # تحليل جدول الكميات
504
- st.markdown("#### تحليل جدول الكميات")
505
-
506
- col1, col2 = st.columns(2)
507
-
508
- with col1:
509
- # توزيع البنود حسب الفئة
510
- category_totals = {}
511
-
512
- for item in st.session_state.bill_of_quantities:
513
- category = item['category']
514
- if category in category_totals:
515
- category_totals[category] += item['total_price']
516
- else:
517
- category_totals[category] = item['total_price']
518
-
519
- category_df = pd.DataFrame({
520
- 'الفئة': list(category_totals.keys()),
521
- 'المبلغ': list(category_totals.values())
522
- })
523
-
524
- fig = px.pie(
525
- category_df,
526
- values='المبلغ',
527
- names='الفئة',
528
- title='توزيع جدول الكميات حسب الفئة'
529
- )
530
-
531
- st.plotly_chart(fig, use_container_width=True)
532
-
533
- with col2:
534
- # ترتيب البنود حسب القيمة
535
- top_items = sorted(st.session_state.bill_of_quantities, key=lambda x: x['total_price'], reverse=True)[:5]
536
-
537
- top_items_df = pd.DataFrame({
538
- 'البند': [item['code'] + ' - ' + item['description'][:20] + '...' for item in top_items],
539
- 'القيمة': [item['total_price'] for item in top_items]
540
- })
541
-
542
- fig = px.bar(
543
- top_items_df,
544
- x='البند',
545
- y='القيمة',
546
- title='أعلى 5 بنود من حيث القيمة',
547
- color='القيمة',
548
- text_auto='.2s'
549
- )
550
-
551
- st.plotly_chart(fig, use_container_width=True)
552
-
553
- # استيراد وتصدير جدول الكميات
554
- st.markdown("#### استيراد وتصدير جدول الكميات")
555
-
556
- col1, col2 = st.columns(2)
557
-
558
- with col1:
559
- if st.button("تصدير جدول الكميات إلى Excel", key="export_boq_button"):
560
- # محاكاة تصدير البيانات
561
- st.success("تم تصدير جدول الكميات إلى Excel بنجاح!")
562
-
563
- with col2:
564
- uploaded_file = st.file_uploader("استيراد جدول الكميات من Excel", type=["xlsx"], key="import_boq_file")
565
-
566
- if uploaded_file is not None:
567
- if st.button("استيراد البيانات", key="import_boq_button"):
568
- # محاكاة استيراد البيانات
569
- st.success("تم استيراد جدول الكميات بنجاح!")
570
-
571
- def _render_cost_analysis_tab(self):
572
- """عرض تبويب تحليل التكاليف"""
573
-
574
- st.markdown("### تحليل التكاليف")
575
-
576
- # عرض تحليل التكاليف الحالي
577
- st.markdown("#### قائمة التكاليف")
578
-
579
- # تحويل قائمة التكاليف إلى DataFrame
580
- cost_df = pd.DataFrame(st.session_state.cost_analysis)
581
-
582
- # عرض التكاليف كجدول قابل للتعديل
583
- edited_df = st.data_editor(
584
- cost_df,
585
- column_config={
586
- "id": st.column_config.NumberColumn("الرقم", disabled=True),
587
- "category": st.column_config.SelectboxColumn(
588
- "الفئة",
589
- options=["تكاليف مباشرة", "تكاليف غير مباشرة", "أرباح"]
590
- ),
591
- "subcategory": st.column_config.TextColumn("الفئة الفرعية"),
592
- "description": st.column_config.TextColumn("الوصف"),
593
- "amount": st.column_config.NumberColumn("المبلغ (ريال)", min_value=0, format="%.2f"),
594
- "percentage": st.column_config.NumberColumn("النسبة (%)", min_value=0, format="%.1f", disabled=True)
595
- },
596
- use_container_width=True,
597
- hide_index=True,
598
- num_rows="dynamic"
599
- )
600
-
601
- # حساب إجمالي التكاليف
602
- total_amount = sum(item['amount'] for item in st.session_state.cost_analysis)
603
-
604
- # تحديث النسبة المئوية لكل بند
605
- for i, row in edited_df.iterrows():
606
- edited_df.at[i, 'percentage'] = (row['amount'] / total_amount) * 100
607
-
608
- # تحديث قائمة التكاليف
609
- if not edited_df.equals(cost_df):
610
- st.session_state.cost_analysis = edited_df.to_dict('records')
611
- st.success("تم تحديث تحليل التكاليف بنجاح!")
612
-
613
- # عرض إجمالي التكاليف
614
- st.metric("إجمالي التكاليف", f"{total_amount:,.2f} ريال")
615
-
616
- # إضافة تكلفة جديدة
617
- st.markdown("#### إضافة تكلفة جديدة")
618
-
619
- with st.form(key="add_cost_item_form"):
620
- col1, col2 = st.columns(2)
621
-
622
- with col1:
623
- new_category = st.selectbox(
624
- "الفئة",
625
- ["تكاليف مباشرة", "تكاليف غير مباشرة", "أرباح"],
626
- key="new_cost_category"
627
- )
628
- new_subcategory = st.text_input("الفئة الفرعية", key="new_cost_subcategory")
629
-
630
- with col2:
631
- new_description = st.text_input("الوصف", key="new_cost_description")
632
- new_amount = st.number_input("المبلغ (ريال)", min_value=0.0, key="new_cost_amount")
633
-
634
- submit_button = st.form_submit_button("إضافة تكلفة")
635
-
636
- if submit_button:
637
- if new_description and new_subcategory:
638
- # إنشاء معرف جديد
639
- new_id = max([item['id'] for item in st.session_state.cost_analysis], default=0) + 1
640
-
641
- # حساب النسبة المئوية
642
- new_percentage = (new_amount / (total_amount + new_amount)) * 100
643
-
644
- # إضافة التكلفة الجديدة
645
- st.session_state.cost_analysis.append({
646
- 'id': new_id,
647
- 'category': new_category,
648
- 'subcategory': new_subcategory,
649
- 'description': new_description,
650
- 'amount': new_amount,
651
- 'percentage': new_percentage
652
- })
653
-
654
- # إعادة حساب النسب المئوية لجميع البنود
655
- new_total = total_amount + new_amount
656
- for item in st.session_state.cost_analysis:
657
- item['percentage'] = (item['amount'] / new_total) * 100
658
-
659
- st.success(f"تمت إضافة التكلفة '{new_description}' بنجاح!")
660
- st.rerun()
661
- else:
662
- st.error("يرجى إدخال الفئة الفرعية والوصف.")
663
-
664
- # تحليل التكاليف
665
- st.markdown("#### تحليل التكاليف")
666
-
667
- # تحليل التكاليف حسب الفئة
668
- st.markdown("##### توزيع التكاليف حسب الفئة")
669
-
670
- # تجميع البيانات حسب الفئة
671
- category_totals = {}
672
-
673
- for item in st.session_state.cost_analysis:
674
- category = item['category']
675
- if category in category_totals:
676
- category_totals[category] += item['amount']
677
- else:
678
- category_totals[category] = item['amount']
679
-
680
- category_df = pd.DataFrame({
681
- 'الفئة': list(category_totals.keys()),
682
- 'المبلغ': list(category_totals.values())
683
- })
684
-
685
- fig = px.pie(
686
- category_df,
687
- values='المبلغ',
688
- names='الفئة',
689
- title='توزيع التكاليف حسب الفئة',
690
- color_discrete_sequence=px.colors.qualitative.Set3
691
- )
692
-
693
- st.plotly_chart(fig, use_container_width=True)
694
-
695
- # تحليل التكاليف المباشرة
696
- st.markdown("##### تحليل التكاليف المباشرة")
697
 
698
  col1, col2 = st.columns(2)
699
 
700
  with col1:
701
- # تجميع البيانات حسب الفئة الفرعية للتكاليف المباشرة
702
- direct_subcategory_totals = {}
703
-
704
- for item in st.session_state.cost_analysis:
705
- if item['category'] == 'تكاليف مباشرة':
706
- subcategory = item['subcategory']
707
- if subcategory in direct_subcategory_totals:
708
- direct_subcategory_totals[subcategory] += item['amount']
709
- else:
710
- direct_subcategory_totals[subcategory] = item['amount']
711
-
712
- direct_subcategory_df = pd.DataFrame({
713
- 'الفئة الفرعية': list(direct_subcategory_totals.keys()),
714
- 'المبلغ': list(direct_subcategory_totals.values())
715
- })
716
-
717
- fig = px.pie(
718
- direct_subcategory_df,
719
- values='المبلغ',
720
- names='الفئة الفرعية',
721
- title='توزيع التكاليف المباشرة حسب الفئة الفرعية'
722
- )
723
-
724
- st.plotly_chart(fig, use_container_width=True)
725
 
726
  with col2:
727
- # تجميع البيانات حسب الوصف للتكاليف المباشرة
728
- direct_description_totals = {}
729
-
730
- for item in st.session_state.cost_analysis:
731
- if item['category'] == 'تكاليف مباشرة':
732
- description = item['description']
733
- if description in direct_description_totals:
734
- direct_description_totals[description] += item['amount']
735
- else:
736
- direct_description_totals[description] = item['amount']
737
-
738
- direct_description_df = pd.DataFrame({
739
- 'الوصف': list(direct_description_totals.keys()),
740
- 'المبلغ': list(direct_description_totals.values())
741
- })
742
-
743
- # ترتيب البيانات تنازلياً حسب المبلغ
744
- direct_description_df = direct_description_df.sort_values(by='المبلغ', ascending=False)
745
-
746
- fig = px.bar(
747
- direct_description_df,
748
- x='الوصف',
749
- y='المبلغ',
750
- title='توزيع التكاليف المباشرة حسب البند',
751
- color='المبلغ',
752
- text_auto='.2s'
753
- )
754
-
755
- st.plotly_chart(fig, use_container_width=True)
756
-
757
- # تحليل التكاليف غير المباشرة
758
- st.markdown("##### تحليل التكاليف غير المباشرة")
759
-
760
- col1, col2 = st.columns(2)
761
-
762
- with col1:
763
- # تجميع البيانات حسب الفئة الفرعية للتكاليف غير المباشرة
764
- indirect_subcategory_totals = {}
765
-
766
- for item in st.session_state.cost_analysis:
767
- if item['category'] == 'تكاليف غير مباشرة':
768
- subcategory = item['subcategory']
769
- if subcategory in indirect_subcategory_totals:
770
- indirect_subcategory_totals[subcategory] += item['amount']
771
- else:
772
- indirect_subcategory_totals[subcategory] = item['amount']
773
-
774
- indirect_subcategory_df = pd.DataFrame({
775
- 'الفئة الفرعية': list(indirect_subcategory_totals.keys()),
776
- 'المبلغ': list(indirect_subcategory_totals.values())
777
- })
778
-
779
- fig = px.pie(
780
- indirect_subcategory_df,
781
- values='المبلغ',
782
- names='الفئة الفرعية',
783
- title='توزيع التكاليف غير المباشرة حسب الفئة الفرعية'
784
- )
785
-
786
- st.plotly_chart(fig, use_container_width=True)
787
-
788
- with col2:
789
- # تجميع البيانات حسب الوصف للتكاليف غير المباشرة
790
- indirect_description_totals = {}
791
-
792
- for item in st.session_state.cost_analysis:
793
- if item['category'] == 'تكاليف غير مباشرة':
794
- description = item['description']
795
- if description in indirect_description_totals:
796
- indirect_description_totals[description] += item['amount']
797
- else:
798
- indirect_description_totals[description] = item['amount']
799
-
800
- indirect_description_df = pd.DataFrame({
801
- 'الوصف': list(indirect_description_totals.keys()),
802
- 'المبلغ': list(indirect_description_totals.values())
803
- })
804
-
805
- # ترتيب البيانات تنازلياً حسب المبلغ
806
- indirect_description_df = indirect_description_df.sort_values(by='المبلغ', ascending=False)
807
-
808
- fig = px.bar(
809
- indirect_description_df,
810
- x='الوصف',
811
- y='المبلغ',
812
- title='توزيع التكاليف غير المباشرة حسب البند',
813
- color='المبلغ',
814
- text_auto='.2s'
815
- )
816
-
817
- st.plotly_chart(fig, use_container_width=True)
818
-
819
- # استيراد وتصدير تحليل التكاليف
820
- st.markdown("#### استيراد وتصدير تحليل التكاليف")
821
-
822
- col1, col2 = st.columns(2)
823
-
824
- with col1:
825
- if st.button("تصدير تحليل التكاليف إلى Excel", key="export_cost_button"):
826
- # محاكاة تصدير البيانات
827
- st.success("تم تصدير تحليل التكاليف إلى Excel بنجاح!")
828
-
829
- with col2:
830
- uploaded_file = st.file_uploader("استيراد تحليل التكاليف من Excel", type=["xlsx"], key="import_cost_file")
831
-
832
- if uploaded_file is not None:
833
- if st.button("استيراد البيانات", key="import_cost_button"):
834
- # محاكاة استيراد البيانات
835
- st.success("تم استيراد تحليل التكاليف بنجاح!")
836
-
837
- def _render_pricing_scenarios_tab(self):
838
- """عرض تبويب سيناريوهات التسعير"""
839
-
840
- st.markdown("### سيناريوهات التسعير")
841
-
842
- # عرض سيناريوهات التسعير الحالية
843
- st.markdown("#### قائمة السيناريوهات")
844
-
845
- # تحويل قائمة السيناريوهات إلى DataFrame
846
- scenarios_df = pd.DataFrame(st.session_state.price_scenarios)
847
-
848
- # عرض السيناريوهات كجدول قابل للتعديل
849
- edited_df = st.data_editor(
850
- scenarios_df,
851
- column_config={
852
- "id": st.column_config.NumberColumn("الرقم", disabled=True),
853
- "name": st.column_config.TextColumn("اسم السيناريو"),
854
- "description": st.column_config.TextColumn("الوصف"),
855
- "total_cost": st.column_config.NumberColumn("إجمالي التكلفة (ريال)", min_value=0, format="%.2f"),
856
- "profit_margin": st.column_config.NumberColumn("هامش الربح (%)", min_value=0, format="%.1f"),
857
- "total_price": st.column_config.NumberColumn("السعر الإجمالي (ريال)", min_value=0, format="%.2f"),
858
- "is_active": st.column_config.CheckboxColumn("نشط")
859
- },
860
- use_container_width=True,
861
- hide_index=True,
862
- num_rows="dynamic"
863
- )
864
-
865
- # تحديث قائمة السيناريوهات
866
- if not edited_df.equals(scenarios_df):
867
- # التأكد من وجود سيناريو نشط واحد فقط
868
- active_count = sum(edited_df['is_active'])
869
- if active_count != 1:
870
- st.error("يجب أن يكون هناك سيناريو نشط واحد فقط.")
871
- else:
872
- st.session_state.price_scenarios = edited_df.to_dict('records')
873
- st.success("تم تحديث سيناريوهات التسعير بنجاح!")
874
-
875
- # إضافة سيناريو جديد
876
- st.markdown("#### إضافة سيناريو جديد")
877
-
878
- with st.form(key="add_scenario_form"):
879
- col1, col2 = st.columns(2)
880
-
881
- with col1:
882
- new_name = st.text_input("اسم السيناريو", key="new_scenario_name")
883
- new_description = st.text_area("الوصف", key="new_scenario_description")
884
-
885
- with col2:
886
- # حساب إجمالي التكاليف الحالي
887
- total_cost = sum(item['amount'] for item in st.session_state.cost_analysis if item['category'] != 'أرباح')
888
-
889
- st.number_input("إجمالي التكلفة (ريال)", min_value=0.0, value=total_cost, key="new_scenario_total_cost", disabled=True)
890
- new_profit_margin = st.number_input("هامش الربح (%)", min_value=0.0, max_value=100.0, value=10.0, key="new_scenario_profit_margin")
891
-
892
- # حساب السعر الإجمالي
893
- new_profit_amount = total_cost * (new_profit_margin / 100)
894
- new_total_price = total_cost + new_profit_amount
895
-
896
- st.number_input("السعر الإجمالي (ريال)", min_value=0.0, value=new_total_price, key="new_scenario_total_price", disabled=True)
897
- new_is_active = st.checkbox("نشط", key="new_scenario_is_active")
898
-
899
- submit_button = st.form_submit_button("إضافة سيناريو")
900
-
901
- if submit_button:
902
- if new_name:
903
- # التحقق من حالة التنشيط
904
- if new_is_active:
905
- # إلغاء تنشيط جميع السيناريوهات الأخرى
906
- for scenario in st.session_state.price_scenarios:
907
- scenario['is_active'] = False
908
-
909
- # إنشاء معرف جديد
910
- new_id = max([item['id'] for item in st.session_state.price_scenarios], default=0) + 1
911
-
912
- # إضافة السيناريو الجديد
913
- st.session_state.price_scenarios.append({
914
- 'id': new_id,
915
- 'name': new_name,
916
- 'description': new_description,
917
- 'total_cost': total_cost,
918
- 'profit_margin': new_profit_margin,
919
- 'total_price': new_total_price,
920
- 'is_active': new_is_active
921
- })
922
-
923
- st.success(f"تمت إضافة السيناريو '{new_name}' بنجاح!")
924
- st.rerun()
925
- else:
926
- st.error("يرجى إدخال اسم السيناريو.")
927
-
928
- # تحليل السيناريوهات
929
- st.markdown("#### تحليل السيناريوهات")
930
-
931
- # مقارنة السيناريوهات
932
- st.markdown("##### مقارنة السيناريوهات")
933
-
934
- # إنشاء DataFrame للرسم البياني
935
- scenarios_comparison_df = pd.DataFrame({
936
- 'السيناريو': [item['name'] for item in st.session_state.price_scenarios],
937
- 'التكلفة الإجمالية': [item['total_cost'] for item in st.session_state.price_scenarios],
938
- 'هامش الربح (%)': [item['profit_margin'] for item in st.session_state.price_scenarios],
939
- 'السعر الإجمالي': [item['total_price'] for item in st.session_state.price_scenarios],
940
- 'الحالة': ['نشط' if item['is_active'] else 'غير نشط' for item in st.session_state.price_scenarios]
941
- })
942
-
943
- # إنشاء رسم بياني شريطي مزدوج
944
- fig = go.Figure()
945
-
946
- # إضافة شريط للتكلفة الإجمالية
947
- fig.add_trace(go.Bar(
948
- x=scenarios_comparison_df['السيناريو'],
949
- y=scenarios_comparison_df['التكلفة الإجمالية'],
950
- name='التكلفة الإجمالية',
951
- marker_color='indianred'
952
- ))
953
-
954
- # إضافة شريط للسعر الإجمالي
955
- fig.add_trace(go.Bar(
956
- x=scenarios_comparison_df['السيناريو'],
957
- y=scenarios_comparison_df['السعر الإجمالي'],
958
- name='السعر الإجمالي',
959
- marker_color='lightsalmon'
960
- ))
961
-
962
- # إضافة خط لهامش الربح
963
- fig.add_trace(go.Scatter(
964
- x=scenarios_comparison_df['السيناريو'],
965
- y=scenarios_comparison_df['هامش الربح (%)'] * 10000, # تكبير القيم لتظهر على الرسم البياني
966
- name='هامش الربح (%)',
967
- yaxis='y2',
968
- line=dict(color='royalblue', width=4)
969
- ))
970
-
971
- # تعديل تخطيط الرسم البياني
972
- fig.update_layout(
973
- title='مقارنة سيناريوهات التسعير',
974
- xaxis_title='السيناريو',
975
- yaxis_title='المبلغ (ريال)',
976
- yaxis2=dict(
977
- title='هامش الربح (%)',
978
- titlefont=dict(color='royalblue'),
979
- tickfont=dict(color='royalblue'),
980
- overlaying='y',
981
- side='right',
982
- range=[0, 20]
983
- ),
984
- barmode='group',
985
- legend=dict(
986
- x=0,
987
- y=1.2,
988
- orientation='h'
989
  )
990
- )
991
-
992
- # تعديل النص على الأشرطة
993
- fig.update_traces(
994
- texttemplate='%{y:,.0f}',
995
- textposition='outside'
996
- )
997
-
998
- st.plotly_chart(fig, use_container_width=True)
999
-
1000
- # تحليل تأثير هامش الربح
1001
- st.markdown("##### تحليل تأثير هامش الربح")
1002
-
1003
- # إنشاء نطاق من هوامش الربح
1004
- profit_margins = list(range(0, 21, 2)) # من 0% إلى 20% بزيادة 2%
1005
-
1006
- # حساب السعر الإجمالي لكل هامش ربح
1007
- total_cost = st.session_state.price_scenarios[0]['total_cost'] # استخدام التكلفة الإجمالية من السيناريو الأول
1008
- total_prices = [total_cost * (1 + margin / 100) for margin in profit_margins]
1009
-
1010
- # إنشاء DataFrame للرسم البياني
1011
- profit_analysis_df = pd.DataFrame({
1012
- 'هامش الربح (%)': profit_margins,
1013
- 'السعر الإجمالي': total_prices
1014
- })
1015
-
1016
- # إنشاء رسم بياني خطي
1017
- fig = px.line(
1018
- profit_analysis_df,
1019
- x='هامش الربح (%)',
1020
- y='السعر الإجمالي',
1021
- title='تأثير هامش الربح على السعر الإجمالي',
1022
- markers=True
1023
- )
1024
-
1025
- # تعديل النص على النقاط
1026
- fig.update_traces(
1027
- texttemplate='%{y:,.0f}',
1028
- textposition='top center'
1029
- )
1030
-
1031
- st.plotly_chart(fig, use_container_width=True)
1032
-
1033
- # تحليل نقطة التعادل
1034
- st.markdown("##### تحليل نقطة التعادل")
1035
-
1036
- # افتراض تكاليف ثابتة ومتغيرة
1037
- fixed_costs = sum(item['amount'] for item in st.session_state.cost_analysis if item['category'] == 'تكاليف غير مباشرة')
1038
- variable_costs = sum(item['amount'] for item in st.session_state.cost_analysis if item['category'] == 'تكاليف مباشرة')
1039
-
1040
- # افتراض سعر البيع من السيناريو النشط
1041
- active_scenario = next((item for item in st.session_state.price_scenarios if item['is_active']), None)
1042
- if active_scenario:
1043
- selling_price = active_scenario['total_price']
1044
- else:
1045
- selling_price = st.session_state.price_scenarios[0]['total_price']
1046
-
1047
- # حساب نقطة التعادل
1048
- if selling_price > variable_costs:
1049
- breakeven_point = fixed_costs / (selling_price - variable_costs)
1050
- st.metric("نقطة التعادل", f"{breakeven_point:.2f} وحدة")
1051
-
1052
- # إنشاء رسم بياني لنقطة التعادل
1053
- units = list(range(0, int(breakeven_point * 2) + 1, max(1, int(breakeven_point / 10))))
1054
-
1055
- total_costs = [fixed_costs + variable_costs * unit for unit in units]
1056
- total_revenues = [selling_price * unit for unit in units]
1057
- profits = [revenue - cost for revenue, cost in zip(total_revenues, total_costs)]
1058
-
1059
- breakeven_df = pd.DataFrame({
1060
- 'الوحدات': units,
1061
- 'إجمالي التكاليف': total_costs,
1062
- 'إجمالي الإيرادات': total_revenues,
1063
- 'الربح': profits
1064
- })
1065
-
1066
- fig = go.Figure()
1067
-
1068
- fig.add_trace(go.Scatter(
1069
- x=breakeven_df['الوحدات'],
1070
- y=breakeven_df['إجمالي التكاليف'],
1071
- name='إجمالي التكاليف',
1072
- line=dict(color='red', width=2)
1073
- ))
1074
-
1075
- fig.add_trace(go.Scatter(
1076
- x=breakeven_df['الوحدات'],
1077
- y=breakeven_df['إجمالي الإيرادات'],
1078
- name='إجمالي الإيرادات',
1079
- line=dict(color='green', width=2)
1080
- ))
1081
-
1082
- fig.add_trace(go.Scatter(
1083
- x=breakeven_df['الوحدات'],
1084
- y=breakeven_df['الربح'],
1085
- name='الربح',
1086
- line=dict(color='blue', width=2)
1087
- ))
1088
-
1089
- # إضافة خط عمودي عند نقطة التعادل
1090
- fig.add_vline(
1091
- x=breakeven_point,
1092
- line_dash="dash",
1093
- line_color="black",
1094
- annotation_text=f"نقطة التعادل: {breakeven_point:.2f}",
1095
- annotation_position="top right"
1096
- )
1097
-
1098
- # إضافة خط أفقي عند الصفر
1099
- fig.add_hline(
1100
- y=0,
1101
- line_dash="dash",
1102
- line_color="gray"
1103
- )
1104
-
1105
- fig.update_layout(
1106
- title='تحليل نقطة التعادل',
1107
- xaxis_title='الوحدات',
1108
- yaxis_title='المبلغ (ريال)'
1109
- )
1110
-
1111
- st.plotly_chart(fig, use_container_width=True)
1112
- else:
1113
- st.warning("لا يمكن حساب نقطة التعادل لأن سعر البيع أقل من التكاليف المتغيرة.")
1114
-
1115
- def _render_competitive_analysis_tab(self):
1116
- """عرض تبويب المقارنة التنافسية"""
1117
-
1118
- st.markdown("### المقارنة التنافسية")
1119
-
1120
- # بيانات افتراضية للمنافسين
1121
- competitors_data = [
1122
- {
1123
- 'name': 'شركتنا',
1124
- 'price': 670000,
1125
- 'quality': 4.5,
1126
- 'delivery_time': 180,
1127
- 'experience': 8,
1128
- 'local_content': 85
1129
- },
1130
- {
1131
- 'name': 'المنافس أ',
1132
- 'price': 700000,
1133
- 'quality': 4.2,
1134
- 'delivery_time': 200,
1135
- 'experience': 10,
1136
- 'local_content': 75
1137
- },
1138
- {
1139
- 'name': 'المنافس ب',
1140
- 'price': 650000,
1141
- 'quality': 3.8,
1142
- 'delivery_time': 160,
1143
- 'experience': 5,
1144
- 'local_content': 90
1145
- },
1146
- {
1147
- 'name': 'المنافس ج',
1148
- 'price': 680000,
1149
- 'quality': 4.0,
1150
- 'delivery_time': 190,
1151
- 'experience': 12,
1152
- 'local_content': 80
1153
- }
1154
- ]
1155
-
1156
- # عرض بيانات المنافسين
1157
- st.markdown("#### بيانات المنافسين")
1158
-
1159
- competitors_df = pd.DataFrame(competitors_data)
1160
- st.dataframe(competitors_df, use_container_width=True, hide_index=True)
1161
-
1162
- # مقارنة الأسعار
1163
- st.markdown("#### مقارنة الأسعار")
1164
-
1165
- fig = px.bar(
1166
- competitors_df,
1167
- x='name',
1168
- y='price',
1169
- title='مقارنة الأسعار بين المنافسين',
1170
- color='price',
1171
- text_auto='.2s'
1172
- )
1173
-
1174
- fig.update_layout(
1175
- xaxis_title='المنافس',
1176
- yaxis_title='السعر (ريال)'
1177
- )
1178
-
1179
- st.plotly_chart(fig, use_container_width=True)
1180
-
1181
- # مقارنة متعددة الأبعاد
1182
- st.markdown("#### مقارنة متعددة الأبعاد")
1183
-
1184
- # تحويل البيانات إلى تنسيق مناسب للرسم البياني الراداري
1185
- categories = ['price', 'quality', 'delivery_time', 'experience', 'local_content']
1186
-
1187
- # تطبيع البيانات (لجعل القيم بين 0 و 1)
1188
- normalized_data = {}
1189
-
1190
- for category in categories:
1191
- if category == 'price' or category == 'delivery_time':
1192
- # للسعر ووقت التسليم، القيمة الأقل أفضل
1193
- min_val = min(item[category] for item in competitors_data)
1194
- max_val = max(item[category] for item in competitors_data)
1195
- normalized_data[category] = [(max_val - item[category]) / (max_val - min_val) if max_val != min_val else 0.5 for item in competitors_data]
1196
- else:
1197
- # للجودة والخبرة والمحتوى المحلي، القيمة الأعلى أفضل
1198
- min_val = min(item[category] for item in competitors_data)
1199
- max_val = max(item[category] for item in competitors_data)
1200
- normalized_data[category] = [(item[category] - min_val) / (max_val - min_val) if max_val != min_val else 0.5 for item in competitors_data]
1201
-
1202
- # إنشاء الرسم البياني الراداري
1203
- fig = go.Figure()
1204
-
1205
- for i, competitor in enumerate(competitors_data):
1206
- fig.add_trace(go.Scatterpolar(
1207
- r=[normalized_data[category][i] for category in categories],
1208
- theta=['السعر', 'الجودة', 'وقت التسليم', 'الخبرة', 'المحتوى المحلي'],
1209
- fill='toself',
1210
- name=competitor['name']
1211
- ))
1212
-
1213
- fig.update_layout(
1214
- polar=dict(
1215
- radialaxis=dict(
1216
- visible=True,
1217
- range=[0, 1]
1218
- )
1219
- ),
1220
- title='مقارنة متعددة الأبعاد بين المنافسين',
1221
- showlegend=True
1222
- )
1223
-
1224
- st.plotly_chart(fig, use_container_width=True)
1225
-
1226
- # تحليل نقاط القوة والضعف
1227
- st.markdown("#### تحليل نقاط القوة والضعف")
1228
-
1229
- # تحديد نقاط القوة والضعف لشركتنا
1230
- our_company = competitors_data[0]
1231
-
1232
- strengths = []
1233
- weaknesses = []
1234
-
1235
- # مقارنة السعر
1236
- other_prices = [comp['price'] for comp in competitors_data[1:]]
1237
- if our_company['price'] <= min(other_prices):
1238
- strengths.append("السعر تنافسي جداً")
1239
- elif our_company['price'] <= sum(other_prices) / len(other_prices):
1240
- strengths.append("السعر تنافسي")
1241
- else:
1242
- weaknesses.append("السعر أعلى من المتوسط")
1243
-
1244
- # مقارنة الجودة
1245
- other_qualities = [comp['quality'] for comp in competitors_data[1:]]
1246
- if our_company['quality'] >= max(other_qualities):
1247
- strengths.append("الجودة ممتازة")
1248
- elif our_company['quality'] >= sum(other_qualities) / len(other_qualities):
1249
- strengths.append("الجودة جيدة")
1250
- else:
1251
- weaknesses.append("الجودة أقل من المتوسط")
1252
-
1253
- # مقارنة وقت التسليم
1254
- other_delivery_times = [comp['delivery_time'] for comp in competitors_data[1:]]
1255
- if our_company['delivery_time'] <= min(other_delivery_times):
1256
- strengths.append("وقت التسليم سريع جداً")
1257
- elif our_company['delivery_time'] <= sum(other_delivery_times) / len(other_delivery_times):
1258
- strengths.append("وقت التسليم جيد")
1259
- else:
1260
- weaknesses.append("وقت التسليم أطول من المتوسط")
1261
-
1262
- # مقارنة الخبرة
1263
- other_experiences = [comp['experience'] for comp in competitors_data[1:]]
1264
- if our_company['experience'] >= max(other_experiences):
1265
- strengths.append("خبرة واسعة جداً")
1266
- elif our_company['experience'] >= sum(other_experiences) / len(other_experiences):
1267
- strengths.append("خبرة جيدة")
1268
- else:
1269
- weaknesses.append("خبرة أقل من المتوسط")
1270
-
1271
- # مقارنة المحتوى المحلي
1272
- other_local_contents = [comp['local_content'] for comp in competitors_data[1:]]
1273
- if our_company['local_content'] >= max(other_local_contents):
1274
- strengths.append("محتوى محلي ممتاز")
1275
- elif our_company['local_content'] >= sum(other_local_contents) / len(other_local_contents):
1276
- strengths.append("محتوى محلي جيد")
1277
- else:
1278
- weaknesses.append("محتوى محلي أقل من المتوسط")
1279
-
1280
- # عرض نقاط القوة والضعف
1281
- col1, col2 = st.columns(2)
1282
-
1283
- with col1:
1284
- st.markdown("##### نقاط القوة")
1285
- for strength in strengths:
1286
- st.markdown(f"- {strength}")
1287
-
1288
- with col2:
1289
- st.markdown("##### نقاط الضعف")
1290
- for weakness in weaknesses:
1291
- st.markdown(f"- {weakness}")
1292
-
1293
- # توصيات للتسعير
1294
- st.markdown("#### توصيات للتسعير")
1295
-
1296
- # تحديد التوصيات بناءً على المقارنة
1297
- recommendations = []
1298
-
1299
- # توصية بناءً على السعر
1300
- avg_price = sum(comp['price'] for comp in competitors_data) / len(competitors_data)
1301
- if our_company['price'] > avg_price:
1302
- recommendations.append("النظر في تخفيض السعر للمنافسة بشكل أفضل.")
1303
-
1304
- # توصية بناءً على الجودة
1305
- if our_company['quality'] > sum(comp['quality'] for comp in competitors_data[1:]) / len(competitors_data[1:]):
1306
- recommendations.append("التأكيد على جودة الخدمات في العروض التسويقية.")
1307
-
1308
- # توصية بناءً على وقت التسليم
1309
- if our_company['delivery_time'] < sum(comp['delivery_time'] for comp in competitors_data[1:]) / len(competitors_data[1:]):
1310
- recommendations.append("التأكيد على سرعة التسليم كميزة تنافسية.")
1311
-
1312
- # توصية بناءً على الخبرة
1313
- if our_company['experience'] < max(comp['experience'] for comp in competitors_data[1:]):
1314
- recommendations.append("تعزيز فريق العمل بخبرات إضافية.")
1315
-
1316
- # توصية بناءً على المحتوى المحلي
1317
- if our_company['local_content'] > sum(comp['local_content'] for comp in competitors_data[1:]) / len(competitors_data[1:]):
1318
- recommendations.append("التأكيد على نسبة المحتوى المحلي العالية في العروض.")
1319
-
1320
- # توصية عامة
1321
- recommendations.append("مراجعة هيكل التكاليف بشكل دوري للحفاظ على القدرة التنافسية.")
1322
-
1323
- # عرض التوصيات
1324
- for recommendation in recommendations:
1325
- st.markdown(f"- {recommendation}")
1326
-
1327
- def _render_reports_tab(self):
1328
- """عرض تبويب التقارير"""
1329
-
1330
- st.markdown("### التقارير")
1331
-
1332
- # قائمة التقارير المتاحة
1333
- reports = [
1334
- "تقرير جدول الكميات",
1335
- "تقرير تحليل التكاليف",
1336
- "تقرير سيناريوهات التسعير",
1337
- "تقرير المقارنة التنافسية",
1338
- "تقرير ملخص التسعير"
1339
- ]
1340
-
1341
- # اختيار التقرير
1342
- selected_report = st.selectbox("اختر التقرير", reports)
1343
-
1344
- # خيارات التصدير
1345
- export_format = st.radio("صيغة التصدير", ["PDF", "Excel", "Word"])
1346
-
1347
- # زر إنشاء التقرير
1348
- if st.button("إنشاء التقرير"):
1349
- st.success(f"تم إنشاء {selected_report} بصيغة {export_format} بنجاح!")
1350
-
1351
- # عرض نموذج للتقرير
1352
- st.markdown("#### نموذج التقرير")
1353
-
1354
- if selected_report == "تقرير جدول الكميات":
1355
- self._render_boq_report()
1356
- elif selected_report == "تقرير تحليل التكاليف":
1357
- self._render_cost_analysis_report()
1358
- elif selected_report == "تقرير سيناريوهات التسعير":
1359
- self._render_pricing_scenarios_report()
1360
- elif selected_report == "تقرير المقارنة التنافسية":
1361
- self._render_competitive_analysis_report()
1362
- elif selected_report == "تقرير ملخص التسعير":
1363
- self._render_pricing_summary_report()
1364
-
1365
- def _render_boq_report(self):
1366
- """عرض نموذج تقرير جدول الكميات"""
1367
-
1368
- st.markdown("### تقرير جدول الكميات")
1369
- st.markdown("**تاريخ التقرير:** " + time.strftime("%Y-%m-%d"))
1370
- st.markdown("**اسم المشروع:** مشروع إنشاء مبنى إداري")
1371
- st.markdown("**رقم المناقصة:** T-2024-001")
1372
-
1373
- st.markdown("#### جدول الكميات")
1374
-
1375
- # عرض جدول الكميات
1376
- boq_df = pd.DataFrame(st.session_state.bill_of_quantities)
1377
- st.dataframe(boq_df, use_container_width=True, hide_index=True)
1378
-
1379
- # عرض إجمالي جدول الكميات
1380
- total_boq = sum(item['total_price'] for item in st.session_state.bill_of_quantities)
1381
- st.metric("إجمالي جدول الكميات", f"{total_boq:,.2f} ريال")
1382
-
1383
- # عرض توزيع البنود حسب الفئة
1384
- st.markdown("#### توزيع البنود حسب الفئة")
1385
-
1386
- # تجميع البيانات حسب الفئة
1387
- category_totals = {}
1388
-
1389
- for item in st.session_state.bill_of_quantities:
1390
- category = item['category']
1391
- if category in category_totals:
1392
- category_totals[category] += item['total_price']
1393
- else:
1394
- category_totals[category] = item['total_price']
1395
-
1396
- category_df = pd.DataFrame({
1397
- 'الفئة': list(category_totals.keys()),
1398
- 'المبلغ': list(category_totals.values())
1399
- })
1400
-
1401
- fig = px.pie(
1402
- category_df,
1403
- values='المبلغ',
1404
- names='الفئة',
1405
- title='توزيع جدول الكميات حسب الفئة'
1406
- )
1407
-
1408
- st.plotly_chart(fig, use_container_width=True)
1409
-
1410
- def _render_cost_analysis_report(self):
1411
- """عرض نموذج تقرير تحليل التكاليف"""
1412
-
1413
- st.markdown("### تقرير تحليل التكاليف")
1414
- st.markdown("**تاريخ التقرير:** " + time.strftime("%Y-%m-%d"))
1415
- st.markdown("**اسم المشروع:** مشروع إنشاء مبنى إداري")
1416
- st.markdown("**رقم المناقصة:** T-2024-001")
1417
-
1418
- st.markdown("#### تحليل التكاليف")
1419
-
1420
- # عرض تحليل التكاليف
1421
- cost_df = pd.DataFrame(st.session_state.cost_analysis)
1422
- st.dataframe(cost_df, use_container_width=True, hide_index=True)
1423
-
1424
- # عرض إجمالي التكاليف
1425
- total_cost = sum(item['amount'] for item in st.session_state.cost_analysis)
1426
- st.metric("إجمالي التكاليف", f"{total_cost:,.2f} ريال")
1427
-
1428
- # عرض توزيع التكاليف حسب الفئة
1429
- st.markdown("#### توزيع التكاليف حسب الفئة")
1430
-
1431
- # تجميع البيانات حسب الفئة
1432
- category_totals = {}
1433
-
1434
- for item in st.session_state.cost_analysis:
1435
- category = item['category']
1436
- if category in category_totals:
1437
- category_totals[category] += item['amount']
1438
- else:
1439
- category_totals[category] = item['amount']
1440
-
1441
- category_df = pd.DataFrame({
1442
- 'الفئة': list(category_totals.keys()),
1443
- 'المبلغ': list(category_totals.values())
1444
- })
1445
-
1446
- fig = px.pie(
1447
- category_df,
1448
- values='المبلغ',
1449
- names='الفئة',
1450
- title='توزيع التكاليف حسب الفئة'
1451
- )
1452
-
1453
- st.plotly_chart(fig, use_container_width=True)
1454
-
1455
- # عرض توزيع التكاليف المباشرة
1456
- st.markdown("#### توزيع التكاليف المباشرة")
1457
-
1458
- # تجميع البيانات حسب الفئة الفرعية للتكاليف المباشرة
1459
- direct_subcategory_totals = {}
1460
-
1461
- for item in st.session_state.cost_analysis:
1462
- if item['category'] == 'تكاليف مباشرة':
1463
- subcategory = item['subcategory']
1464
- if subcategory in direct_subcategory_totals:
1465
- direct_subcategory_totals[subcategory] += item['amount']
1466
- else:
1467
- direct_subcategory_totals[subcategory] = item['amount']
1468
-
1469
- direct_subcategory_df = pd.DataFrame({
1470
- 'الفئة الفرعية': list(direct_subcategory_totals.keys()),
1471
- 'المبلغ': list(direct_subcategory_totals.values())
1472
- })
1473
-
1474
- fig = px.bar(
1475
- direct_subcategory_df,
1476
- x='الفئة الفرعية',
1477
- y='المبلغ',
1478
- title='توزيع التكاليف المباشرة',
1479
- color='الفئة الفرعية',
1480
- text_auto='.2s'
1481
- )
1482
-
1483
- st.plotly_chart(fig, use_container_width=True)
1484
-
1485
- def _render_pricing_scenarios_report(self):
1486
- """عرض نموذج تقرير سيناريوهات التسعير"""
1487
-
1488
- st.markdown("### تقرير سيناريوهات التسعير")
1489
- st.markdown("**تاريخ التقرير:** " + time.strftime("%Y-%m-%d"))
1490
- st.markdown("**اسم المشروع:** مشروع إنشاء مبنى إداري")
1491
- st.markdown("**رقم المناقصة:** T-2024-001")
1492
-
1493
- st.markdown("#### سيناريوهات التسعير")
1494
-
1495
- # عرض سيناريوهات التسعير
1496
- scenarios_df = pd.DataFrame(st.session_state.price_scenarios)
1497
- st.dataframe(scenarios_df, use_container_width=True, hide_index=True)
1498
-
1499
- # عرض السيناريو النشط
1500
- active_scenario = next((item for item in st.session_state.price_scenarios if item['is_active']), None)
1501
- if active_scenario:
1502
- st.markdown(f"**السيناريو النشط:** {active_scenario['name']}")
1503
- st.markdown(f"**السعر الإجمالي:** {active_scenario['total_price']:,.2f} ريال")
1504
- st.markdown(f"**هامش الربح:** {active_scenario['profit_margin']:.1f}%")
1505
-
1506
- # عرض مقارنة السيناريوهات
1507
- st.markdown("#### مقارنة السيناريوهات")
1508
-
1509
- # إنشاء DataFrame للرسم البياني
1510
- scenarios_comparison_df = pd.DataFrame({
1511
- 'السيناريو': [item['name'] for item in st.session_state.price_scenarios],
1512
- 'التكلفة الإجمالية': [item['total_cost'] for item in st.session_state.price_scenarios],
1513
- 'هامش الربح (%)': [item['profit_margin'] for item in st.session_state.price_scenarios],
1514
- 'السعر الإجمالي': [item['total_price'] for item in st.session_state.price_scenarios],
1515
- 'الحالة': ['نشط' if item['is_active'] else 'غير نشط' for item in st.session_state.price_scenarios]
1516
- })
1517
-
1518
- # إنشاء رسم بياني شريطي مزدوج
1519
- fig = go.Figure()
1520
-
1521
- # إضافة شريط للتكلفة الإجمالية
1522
- fig.add_trace(go.Bar(
1523
- x=scenarios_comparison_df['السيناريو'],
1524
- y=scenarios_comparison_df['التكلفة الإجمالية'],
1525
- name='التكلفة الإجمالية',
1526
- marker_color='indianred'
1527
- ))
1528
-
1529
- # إضافة شريط للسعر الإجمالي
1530
- fig.add_trace(go.Bar(
1531
- x=scenarios_comparison_df['السيناريو'],
1532
- y=scenarios_comparison_df['السعر الإجمالي'],
1533
- name='السعر الإجمالي',
1534
- marker_color='lightsalmon'
1535
- ))
1536
-
1537
- # إضافة خط لهامش الربح
1538
- fig.add_trace(go.Scatter(
1539
- x=scenarios_comparison_df['السيناريو'],
1540
- y=scenarios_comparison_df['هامش الربح (%)'] * 10000, # تكبير القيم لتظهر على الرسم البياني
1541
- name='هامش الربح (%)',
1542
- yaxis='y2',
1543
- line=dict(color='royalblue', width=4)
1544
- ))
1545
-
1546
- # تعديل تخطيط الرسم البياني
1547
- fig.update_layout(
1548
- title='مقارنة سيناريوهات التسعير',
1549
- xaxis_title='السيناريو',
1550
- yaxis_title='المبلغ (ريال)',
1551
- yaxis2=dict(
1552
- title='هامش الربح (%)',
1553
- titlefont=dict(color='royalblue'),
1554
- tickfont=dict(color='royalblue'),
1555
- overlaying='y',
1556
- side='right',
1557
- range=[0, 20]
1558
- ),
1559
- barmode='group',
1560
- legend=dict(
1561
- x=0,
1562
- y=1.2,
1563
- orientation='h'
1564
- )
1565
- )
1566
-
1567
- # تعديل النص على الأشرطة
1568
- fig.update_traces(
1569
- texttemplate='%{y:,.0f}',
1570
- textposition='outside'
1571
- )
1572
-
1573
- st.plotly_chart(fig, use_container_width=True)
1574
-
1575
- def _render_competitive_analysis_report(self):
1576
- """عرض نموذج تقرير المقارنة التنافسية"""
1577
-
1578
- st.markdown("### تقرير المقارنة التنافسية")
1579
- st.markdown("**تاريخ التقرير:** " + time.strftime("%Y-%m-%d"))
1580
- st.markdown("**اسم المشروع:** مشروع إنشاء مبنى إداري")
1581
- st.markdown("**رقم المناقصة:** T-2024-001")
1582
-
1583
- # بيانات افتراضية للمنافسين
1584
- competitors_data = [
1585
- {
1586
- 'name': 'شركتنا',
1587
- 'price': 670000,
1588
- 'quality': 4.5,
1589
- 'delivery_time': 180,
1590
- 'experience': 8,
1591
- 'local_content': 85
1592
- },
1593
- {
1594
- 'name': 'المنافس أ',
1595
- 'price': 700000,
1596
- 'quality': 4.2,
1597
- 'delivery_time': 200,
1598
- 'experience': 10,
1599
- 'local_content': 75
1600
- },
1601
- {
1602
- 'name': 'المنافس ب',
1603
- 'price': 650000,
1604
- 'quality': 3.8,
1605
- 'delivery_time': 160,
1606
- 'experience': 5,
1607
- 'local_content': 90
1608
- },
1609
- {
1610
- 'name': 'المنافس ج',
1611
- 'price': 680000,
1612
- 'quality': 4.0,
1613
- 'delivery_time': 190,
1614
- 'experience': 12,
1615
- 'local_content': 80
1616
- }
1617
- ]
1618
-
1619
- # عرض بيانات المنافسين
1620
- st.markdown("#### بيانات المنافسين")
1621
-
1622
- competitors_df = pd.DataFrame(competitors_data)
1623
- st.dataframe(competitors_df, use_container_width=True, hide_index=True)
1624
-
1625
- # مقارنة الأسعار
1626
- st.markdown("#### مقارنة الأسعار")
1627
-
1628
- fig = px.bar(
1629
- competitors_df,
1630
- x='name',
1631
- y='price',
1632
- title='مقارنة الأسعار بين المنافسين',
1633
- color='price',
1634
- text_auto='.2s'
1635
- )
1636
-
1637
- fig.update_layout(
1638
- xaxis_title='المنافس',
1639
- yaxis_title='السعر (ريال)'
1640
- )
1641
-
1642
- st.plotly_chart(fig, use_container_width=True)
1643
-
1644
- # مقارنة متعددة الأبعاد
1645
- st.markdown("#### مقارنة متعددة الأبعاد")
1646
-
1647
- # تحويل البيانات إلى تنسيق مناسب للرسم البياني الراداري
1648
- categories = ['price', 'quality', 'delivery_time', 'experience', 'local_content']
1649
-
1650
- # تطبيع البيانات (لجعل القيم بين 0 و 1)
1651
- normalized_data = {}
1652
-
1653
- for category in categories:
1654
- if category == 'price' or category == 'delivery_time':
1655
- # للسعر ووقت التسليم، القيمة الأقل أفضل
1656
- min_val = min(item[category] for item in competitors_data)
1657
- max_val = max(item[category] for item in competitors_data)
1658
- normalized_data[category] = [(max_val - item[category]) / (max_val - min_val) if max_val != min_val else 0.5 for item in competitors_data]
1659
- else:
1660
- # للجودة والخبرة والمحتوى المحلي، القيمة الأعلى أفضل
1661
- min_val = min(item[category] for item in competitors_data)
1662
- max_val = max(item[category] for item in competitors_data)
1663
- normalized_data[category] = [(item[category] - min_val) / (max_val - min_val) if max_val != min_val else 0.5 for item in competitors_data]
1664
-
1665
- # إنشاء الرسم البياني الراداري
1666
- fig = go.Figure()
1667
-
1668
- for i, competitor in enumerate(competitors_data):
1669
- fig.add_trace(go.Scatterpolar(
1670
- r=[normalized_data[category][i] for category in categories],
1671
- theta=['السعر', 'الجودة', 'وقت التسليم', 'الخبرة', 'المحتوى المحلي'],
1672
- fill='toself',
1673
- name=competitor['name']
1674
- ))
1675
-
1676
- fig.update_layout(
1677
- polar=dict(
1678
- radialaxis=dict(
1679
- visible=True,
1680
- range=[0, 1]
1681
- )
1682
- ),
1683
- title='مقارنة متعددة الأبعاد بين المنافسين',
1684
- showlegend=True
1685
- )
1686
-
1687
- st.plotly_chart(fig, use_container_width=True)
1688
-
1689
- def _render_pricing_summary_report(self):
1690
- """عرض نموذج تقرير ملخص التسعير"""
1691
-
1692
- st.markdown("### تقرير ملخص التسعير")
1693
- st.markdown("**تاريخ التقرير:** " + time.strftime("%Y-%m-%d"))
1694
- st.markdown("**اسم المشروع:** مشروع إنشاء مبنى إداري")
1695
- st.markdown("**رقم المناقصة:** T-2024-001")
1696
-
1697
- # عرض ملخص التسعير
1698
- st.markdown("#### ملخص التسعير")
1699
-
1700
- # حساب إجمالي التكاليف
1701
- total_direct_cost = sum(item['amount'] for item in st.session_state.cost_analysis if item['category'] == 'تكاليف مباشرة')
1702
- total_indirect_cost = sum(item['amount'] for item in st.session_state.cost_analysis if item['category'] == 'تكاليف غير مباشرة')
1703
- total_profit = sum(item['amount'] for item in st.session_state.cost_analysis if item['category'] == 'أرباح')
1704
- total_cost = total_direct_cost + total_indirect_cost
1705
- total_price = total_cost + total_profit
1706
-
1707
- # إنشاء جدول ملخص
1708
- summary_data = {
1709
- 'البند': ['التكاليف المباشرة', 'التكاليف غير المباشرة', 'إجمالي التكاليف', 'هامش الربح', 'السعر الإجمالي'],
1710
- 'المبلغ (ريال)': [total_direct_cost, total_indirect_cost, total_cost, total_profit, total_price],
1711
- 'النسبة (%)': [
1712
- (total_direct_cost / total_price) * 100,
1713
- (total_indirect_cost / total_price) * 100,
1714
- (total_cost / total_price) * 100,
1715
- (total_profit / total_price) * 100,
1716
- 100.0
1717
- ]
1718
- }
1719
-
1720
- summary_df = pd.DataFrame(summary_data)
1721
- st.dataframe(summary_df, use_container_width=True, hide_index=True)
1722
-
1723
- # عرض توزيع التكاليف
1724
- st.markdown("#### توزيع التكاليف")
1725
-
1726
- # إنشاء DataFrame للرسم البياني
1727
- cost_distribution_df = pd.DataFrame({
1728
- 'البند': ['التكاليف المباشرة', 'التكاليف غير المباشرة', 'هامش الربح'],
1729
- 'المبلغ': [total_direct_cost, total_indirect_cost, total_profit]
1730
- })
1731
-
1732
- fig = px.pie(
1733
- cost_distribution_df,
1734
- values='المبلغ',
1735
- names='البند',
1736
- title='توزيع التكاليف والأرباح',
1737
- color_discrete_sequence=px.colors.qualitative.Set3
1738
- )
1739
-
1740
- st.plotly_chart(fig, use_container_width=True)
1741
-
1742
- # عرض ملخص السيناريو النشط
1743
- st.markdown("#### السيناريو النشط")
1744
-
1745
- active_scenario = next((item for item in st.session_state.price_scenarios if item['is_active']), None)
1746
- if active_scenario:
1747
- st.markdown(f"**اسم السيناريو:** {active_scenario['name']}")
1748
- st.markdown(f"**الوصف:** {active_scenario['description']}")
1749
- st.markdown(f"**إجمالي التكلفة:** {active_scenario['total_cost']:,.2f} ريال")
1750
- st.markdown(f"**هامش الربح:** {active_scenario['profit_margin']:.1f}%")
1751
- st.markdown(f"**السعر الإجمالي:** {active_scenario['total_price']:,.2f} ريال")
1752
-
1753
- # عرض توصيات التسعير
1754
- st.markdown("#### توصيات التسعير")
1755
 
1756
- st.markdown("- مراجعة هيكل التكاليف بشكل دوري للحفاظ على القدرة التنافسية.")
1757
- st.markdown("- التأكيد على جودة الخدمات في العروض التسويقية.")
1758
- st.markdown("- التأكيد على نسبة المحتوى المحلي العالية في العروض.")
1759
- st.markdown("- مراقبة أسعار المنافسين وتعديل الاستراتيجية التسعيرية عند الحاجة.")
1760
- st.markdown("- تحليل نقاط القوة والضعف بشكل مستمر لتحسين العروض المستقبلية.")
 
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
 
 
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,
 
242
  }
243
  ]
244
 
245
+ def run(self):
246
+ """تشغيل وحدة التسعير"""
247
+ # استدعاء دالة العرض
248
+ self.render()
249
+
250
  def render(self):
251
  """عرض واجهة وحدة التسعير"""
252
 
 
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
+ "أعمال حديد",
444
+ "أعمال بناء",
445
+ "أعمال تشطيبات",
446
+ "أعمال نجارة",
447
+ "أعمال ألمنيوم",
448
+ "أعمال كهربائية",
449
+ "أعمال ميكانيكية",
450
+ "أعمال صحية"
451
+ ],
452
+ key="new_boq_category"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
453
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
454
 
455
+ if st.button("إضافة البند", key="add_boq_item"):
456
+ if new_code and ne
457
+ (Content truncated due to size limit. Use line ranges to read in chunks)