EGYADMIN commited on
Commit
f5db5d9
·
verified ·
1 Parent(s): 5ae7941

Update modules/pricing/pricing_app.py

Browse files
Files changed (1) hide show
  1. modules/pricing/pricing_app.py +2 -777
modules/pricing/pricing_app.py CHANGED
@@ -12,9 +12,6 @@ from datetime import datetime
12
  class PricingApp:
13
  def __init__(self):
14
  """تهيئة التطبيق"""
15
- # تعيين عنوان الصفحة
16
- st.set_page_config(page_title="وحدة التسعير المتكاملة", layout="wide")
17
-
18
  # إضافة CSS للتنسيق
19
  self._add_custom_css()
20
 
@@ -437,777 +434,5 @@ class PricingApp:
437
  st.info("لا توجد بنود في جدول الكميات. قم بإضافة بنود جديدة.")
438
 
439
  def _render_item_price_analysis(self):
440
- """عرض تحليل سعر البند"""
441
- st.header("تحليل سعر البند")
442
-
443
- # الحصول على المشروع الحالي
444
- current_project = next((p for p in st.session_state.projects if p['id'] == st.session_state.current_project_id), None)
445
-
446
- if not current_project or not current_project['boq']:
447
- st.warning("الرجاء اختيار مشروع به بنود في جدول الكميات أولاً.")
448
- return
449
-
450
- # اختيار البند للتحليل
451
- item_options = [f"{item['description']}. الكود: {item['code']}" for item in current_project['boq']]
452
- selected_item_option = st.selectbox("اختر البند للتحليل", item_options, key="item_selector")
453
-
454
- if not selected_item_option:
455
- st.warning("الرجاء اختيار بند للتحليل.")
456
- return
457
-
458
- # استخراج كود البند من الخيار المحدد
459
- selected_item_code = selected_item_option.split("الكود: ")[1]
460
-
461
- # البحث عن البند في جدول الكميات
462
- selected_item = next((item for item in current_project['boq'] if item['code'] == selected_item_code), None)
463
- selected_item_id = selected_item['id']
464
-
465
- if not selected_item:
466
- st.error("لم يتم العثور على البند المحدد.")
467
- return
468
-
469
- # عرض تفاصيل البند
470
- col1, col2 = st.columns(2)
471
- with col1:
472
- st.write(f"**البند:** {selected_item['description']}")
473
- st.write(f"**الكود:** {selected_item['code']}")
474
- st.write(f"**الوحدة:** {selected_item['unit']}")
475
- with col2:
476
- st.write(f"**الكمية:** {selected_item['quantity']}")
477
- st.write(f"**سعر الوحدة:** {selected_item['unit_price']:,.2f} ريال")
478
- st.write(f"**السعر الإجمالي:** {selected_item['total_price']:,.2f} ريال")
479
-
480
- # إنشاء تحليل سعر البند
481
- item_analysis = self._generate_item_analysis(selected_item)
482
-
483
- # عرض تحليل المواد مع إمكانية التعديل
484
- self._render_materials_analysis_enhanced(item_analysis)
485
-
486
- # عرض تحليل المعدات مع إمكانية التعديل
487
- self._render_equipment_analysis_enhanced(item_analysis)
488
-
489
- # عرض تحليل العمالة مع إمكانية التعديل
490
- self._render_labor_analysis_enhanced(item_analysis)
491
-
492
- # عرض تحليل المقاولين من الباطن مع إمكانية التعديل
493
- self._render_subcontractors_analysis_enhanced(item_analysis)
494
-
495
- # عرض ملخص التكلفة
496
- self._render_cost_summary_enhanced(item_analysis)
497
-
498
- # زر حفظ التغييرات في جدول الكميات الرئيسي
499
- if st.button("حفظ جميع التغييرات في جدول الكميات", key="save_all_changes", type="primary"):
500
- # تحديث البند في جدول الكميات
501
- self._update_boq_item_from_analysis(selected_item_id, item_analysis)
502
- st.success("تم حفظ جميع التغييرات بنجاح في جدول الكميات الرئيسي")
503
- st.session_state.item_analysis_edited = False
504
- st.rerun()
505
-
506
- def _generate_item_analysis(self, item):
507
- """إنشاء تحليل سعر البند"""
508
- # في هذه النسخة التجريبية، سنقوم بإنشاء بيانات عشوائية للتحليل
509
- # في النسخة النهائية، يجب استخدام بيانات حقيقية من قاعدة البيانات
510
-
511
- unit_price = item['unit_price']
512
-
513
- # تقسيم سعر الوحدة إلى مكوناته
514
- materials_cost = unit_price * random.uniform(0.4, 0.6)
515
- equipment_cost = unit_price * random.uniform(0.1, 0.2)
516
- labor_cost = unit_price * random.uniform(0.1, 0.2)
517
- overhead_cost = unit_price * random.uniform(0.05, 0.1)
518
- profit = unit_price - (materials_cost + equipment_cost + labor_cost + overhead_cost)
519
-
520
- # إنشاء قائمة المواد
521
- materials = [
522
- {
523
- 'name': 'خرسانة جاهزة' if 'خرسانة' in item['description'].lower() else 'حديد تسليح' if 'حديد' in item['description'].lower() else 'رمل',
524
- 'unit': 'م3' if 'خرسانة' in item['description'].lower() else 'طن' if 'حديد' in item['description'].lower() else 'م3',
525
- 'quantity': random.uniform(0.5, 1.5),
526
- 'unit_price': materials_cost * 0.6,
527
- 'total_price': materials_cost * 0.6 * random.uniform(0.5, 1.5)
528
- },
529
- {
530
- 'name': 'إسمنت',
531
- 'unit': 'طن',
532
- 'quantity': random.uniform(0.2, 0.5),
533
- 'unit_price': materials_cost * 0.3,
534
- 'total_price': materials_cost * 0.3 * random.uniform(0.2, 0.5)
535
- },
536
- {
537
- 'name': 'مواد أخرى',
538
- 'unit': 'مجموعة',
539
- 'quantity': 1,
540
- 'unit_price': materials_cost * 0.1,
541
- 'total_price': materials_cost * 0.1
542
- }
543
- ]
544
-
545
- # إنشاء قائمة المعدات
546
- equipment = [
547
- {
548
- 'name': 'خلاطة خرسانة' if 'خرسانة' in item['description'].lower() else 'حفارة' if 'حفر' in item['description'].lower() else 'رافعة',
549
- 'unit': 'يوم',
550
- 'quantity': random.uniform(1, 3),
551
- 'unit_price': equipment_cost * 0.7,
552
- 'total_price': equipment_cost * 0.7 * random.uniform(1, 3)
553
- },
554
- {
555
- 'name': 'معدات أخرى',
556
- 'unit': 'يوم',
557
- 'quantity': random.uniform(1, 2),
558
- 'unit_price': equipment_cost * 0.3,
559
- 'total_price': equipment_cost * 0.3 * random.uniform(1, 2)
560
- }
561
- ]
562
-
563
- # إنشاء قائمة العمالة
564
- labor = [
565
- {
566
- 'name': 'عامل فني',
567
- 'unit': 'يوم',
568
- 'quantity': random.uniform(2, 5),
569
- 'unit_price': labor_cost * 0.6,
570
- 'total_price': labor_cost * 0.6 * random.uniform(2, 5)
571
- },
572
- {
573
- 'name': 'عامل عادي',
574
- 'unit': 'يوم',
575
- 'quantity': random.uniform(3, 8),
576
- 'unit_price': labor_cost * 0.4,
577
- 'total_price': labor_cost * 0.4 * random.uniform(3, 8)
578
- }
579
- ]
580
-
581
- # إنشاء قائمة المقاولين من الباطن
582
- subcontractors = [
583
- {
584
- 'name': 'مقاول أعمال خرسانية' if 'خرسانة' in item['description'].lower() else 'مقاول أعمال حفر' if 'حفر' in item['description'].lower() else 'مقاول عام',
585
- 'unit': 'عقد',
586
- 'quantity': 1,
587
- 'unit_price': unit_price * 0.15,
588
- 'total_price': unit_price * 0.15
589
- }
590
- ]
591
-
592
- # حساب إجمالي التكاليف
593
- total_materials_cost = sum(material['total_price'] for material in materials)
594
- total_equipment_cost = sum(equipment_item['total_price'] for equipment_item in equipment)
595
- total_labor_cost = sum(labor_item['total_price'] for labor_item in labor)
596
- total_subcontractors_cost = sum(subcontractor['total_price'] for subcontractor in subcontractors)
597
-
598
- # تعديل الربح ليكون الفرق بين سعر الوحدة وإجمالي التكاليف
599
- total_cost = total_materials_cost + total_equipment_cost + total_labor_cost + total_subcontractors_cost + overhead_cost
600
- profit = unit_price - total_cost
601
-
602
- return {
603
- 'item': item,
604
- 'materials': materials,
605
- 'equipment': equipment,
606
- 'labor': labor,
607
- 'subcontractors': subcontractors,
608
- 'total_materials_cost': total_materials_cost,
609
- 'total_equipment_cost': total_equipment_cost,
610
- 'total_labor_cost': total_labor_cost,
611
- 'total_subcontractors_cost': total_subcontractors_cost,
612
- 'overhead_cost': overhead_cost,
613
- 'profit': profit,
614
- 'unit_price': unit_price
615
- }
616
-
617
- def _render_materials_analysis_enhanced(self, item_analysis):
618
- """عرض تحليل المواد مع إمكانية التعديل (نسخة محسنة)"""
619
- st.subheader("تحليل المواد")
620
-
621
- if not item_analysis['materials']:
622
- st.info("لا توجد مواد في تحليل هذا البند.")
623
-
624
- # إضافة زر لإضافة مواد جديدة
625
- if st.button("إضافة مواد", key="add_first_material"):
626
- item_analysis['materials'] = [{
627
- 'name': 'مادة جديدة',
628
- 'unit': 'وحدة',
629
- 'quantity': 1.0,
630
- 'unit_price': 0.0,
631
- 'total_price': 0.0
632
- }]
633
- st.session_state.item_analysis_edited = True
634
- st.rerun()
635
- return
636
-
637
- # إنشاء DataFrame من قائمة المواد
638
- df = pd.DataFrame(item_analysis['materials'])
639
- df.columns = ['المادة', 'الوحدة', 'الكمية', 'سعر الوحدة', 'السعر الإجمالي']
640
-
641
- # إضافة عمود للإجراءات
642
- df['الإجراءات'] = '✏️ 🗑️'
643
-
644
- # تحويل الجدول إلى جدول قابل للتعديل
645
- edited_df = st.data_editor(
646
- df,
647
- use_container_width=True,
648
- key="edit_materials_table",
649
- column_config={
650
- "المادة": st.column_config.Column("المادة"),
651
- "الوحدة": st.column_config.Column("الوحدة"),
652
- "الكمية": st.column_config.NumberColumn(
653
- "الكمية",
654
- min_value=0.0,
655
- format="%.2f",
656
- step=0.1,
657
- on_change=self._update_total_price,
658
- ),
659
- "سعر الوحدة": st.column_config.NumberColumn(
660
- "سعر الوحدة",
661
- min_value=0.0,
662
- format="%.2f ريال",
663
- step=0.1,
664
- on_change=self._update_total_price,
665
- ),
666
- "السعر الإجمالي": st.column_config.NumberColumn(
667
- "السعر الإجمالي",
668
- format="%.2f ريال",
669
- disabled=True,
670
- ),
671
- "الإجراءات": st.column_config.Column(
672
- "الإجراءات",
673
- width="small",
674
- disabled=True,
675
- ),
676
- },
677
- num_rows="dynamic",
678
- hide_index=True,
679
- )
680
-
681
- # تحديث البيانات في item_analysis بناءً على التعديلات
682
- if edited_df is not None and not edited_df.equals(df):
683
- # حذف جميع المواد الحالية
684
- item_analysis['materials'] = []
685
-
686
- # إضافة المواد المعدلة
687
- for i, row in edited_df.iterrows():
688
- # حساب السعر الإجمالي
689
- total_price = row['الكمية'] * row['سعر الوحدة']
690
-
691
- # إضافة المادة
692
- item_analysis['materials'].append({
693
- 'name': row['المادة'],
694
- 'unit': row['الوحدة'],
695
- 'quantity': row['الكمية'],
696
- 'unit_price': row['سعر الوحدة'],
697
- 'total_price': total_price
698
- })
699
-
700
- # إعادة حساب إجمالي تكلفة المواد
701
- item_analysis['total_materials_cost'] = sum(material['total_price'] for material in item_analysis['materials'])
702
-
703
- # تعيين علامة التعديل
704
- st.session_state.item_analysis_edited = True
705
- st.rerun()
706
-
707
- # عرض إجمالي تكلفة المواد بتنسيق محسن
708
- st.markdown(f"""
709
- <div class="total-summary">
710
- <div class="total-label">إجمالي تكلفة المواد:</div>
711
- <div class="total-value">{item_analysis['total_materials_cost']:,.2f} ريال</div>
712
- </div>
713
- """, unsafe_allow_html=True)
714
-
715
- # أزرار التحكم
716
- col1, col2 = st.columns(2)
717
- with col1:
718
- if st.button("➕ إضافة مادة جديدة", key="add_material"):
719
- item_analysis['materials'].append({
720
- 'name': 'مادة جديدة',
721
- 'unit': 'وحدة',
722
- 'quantity': 1.0,
723
- 'unit_price': 0.0,
724
- 'total_price': 0.0
725
- })
726
- st.session_state.item_analysis_edited = True
727
- st.rerun()
728
-
729
- def _render_equipment_analysis_enhanced(self, item_analysis):
730
- """عرض تحليل المعدات مع إمكانية التعديل (نسخة محسنة)"""
731
- st.subheader("تحليل المعدات")
732
-
733
- if not item_analysis['equipment']:
734
- st.info("لا توجد معدات في تحليل هذا البند.")
735
-
736
- # إضافة زر لإضافة معدات جديدة
737
- if st.button("إضافة معدات", key="add_first_equipment"):
738
- item_analysis['equipment'] = [{
739
- 'name': 'معدة جديدة',
740
- 'unit': 'يوم',
741
- 'quantity': 1.0,
742
- 'unit_price': 0.0,
743
- 'total_price': 0.0
744
- }]
745
- st.session_state.item_analysis_edited = True
746
- st.rerun()
747
- return
748
-
749
- # إنشاء DataFrame من قائمة المعدات
750
- df = pd.DataFrame(item_analysis['equipment'])
751
- df.columns = ['المعدة', 'الوحدة', 'الكمية', 'سعر الوحدة', 'السعر الإجمالي']
752
-
753
- # إضافة عمود للإجراءات
754
- df['الإجراءات'] = '✏️ 🗑️'
755
-
756
- # تحويل الجدول إلى جدول قابل للتعديل
757
- edited_df = st.data_editor(
758
- df,
759
- use_container_width=True,
760
- key="edit_equipment_table",
761
- column_config={
762
- "المعدة": st.column_config.Column("المعدة"),
763
- "الوحدة": st.column_config.Column("الوحدة"),
764
- "الكمية": st.column_config.NumberColumn(
765
- "الكمية",
766
- min_value=0.0,
767
- format="%.2f",
768
- step=0.1,
769
- on_change=self._update_total_price,
770
- ),
771
- "سعر الوحدة": st.column_config.NumberColumn(
772
- "سعر الوحدة",
773
- min_value=0.0,
774
- format="%.2f ريال",
775
- step=0.1,
776
- on_change=self._update_total_price,
777
- ),
778
- "السعر الإجمالي": st.column_config.NumberColumn(
779
- "السعر الإجمالي",
780
- format="%.2f ريال",
781
- disabled=True,
782
- ),
783
- "الإجراءات": st.column_config.Column(
784
- "الإجراءات",
785
- width="small",
786
- disabled=True,
787
- ),
788
- },
789
- num_rows="dynamic",
790
- hide_index=True,
791
- )
792
-
793
- # تحديث البيانات في item_analysis بناءً على التعديلات
794
- if edited_df is not None and not edited_df.equals(df):
795
- # حذف جميع المعدات الحالية
796
- item_analysis['equipment'] = []
797
-
798
- # إضافة المعدات المعدلة
799
- for i, row in edited_df.iterrows():
800
- # حساب السعر الإجمالي
801
- total_price = row['الكمية'] * row['سعر الوحدة']
802
-
803
- # إضافة المعدة
804
- item_analysis['equipment'].append({
805
- 'name': row['المعدة'],
806
- 'unit': row['الوحدة'],
807
- 'quantity': row['الكمية'],
808
- 'unit_price': row['سعر الوحدة'],
809
- 'total_price': total_price
810
- })
811
-
812
- # إعادة حساب إجمالي تكلفة المعدات
813
- item_analysis['total_equipment_cost'] = sum(equipment_item['total_price'] for equipment_item in item_analysis['equipment'])
814
-
815
- # تعيين علامة التعديل
816
- st.session_state.item_analysis_edited = True
817
- st.rerun()
818
-
819
- # عرض إجمالي تكلفة المعدات بتنسيق محسن
820
- st.markdown(f"""
821
- <div class="total-summary">
822
- <div class="total-label">إجمالي تكلفة المعدات:</div>
823
- <div class="total-value">{item_analysis['total_equipment_cost']:,.2f} ريال</div>
824
- </div>
825
- """, unsafe_allow_html=True)
826
-
827
- # أزرار التحكم
828
- col1, col2 = st.columns(2)
829
- with col1:
830
- if st.button("➕ إضافة معدة جديدة", key="add_equipment"):
831
- item_analysis['equipment'].append({
832
- 'name': 'معدة جديدة',
833
- 'unit': 'يوم',
834
- 'quantity': 1.0,
835
- 'unit_price': 0.0,
836
- 'total_price': 0.0
837
- })
838
- st.session_state.item_analysis_edited = True
839
- st.rerun()
840
-
841
- def _render_labor_analysis_enhanced(self, item_analysis):
842
- """عرض تحليل العمالة مع إمكانية التعديل (نسخة محسنة)"""
843
- st.subheader("تحليل العمالة")
844
-
845
- if not item_analysis['labor']:
846
- st.info("لا توجد عمالة في تحليل هذا البند.")
847
-
848
- # إضافة زر لإضافة عمالة جديدة
849
- if st.button("إضافة عمالة", key="add_first_labor"):
850
- item_analysis['labor'] = [{
851
- 'name': 'عامل جديد',
852
- 'unit': 'يوم',
853
- 'quantity': 1.0,
854
- 'unit_price': 0.0,
855
- 'total_price': 0.0
856
- }]
857
- st.session_state.item_analysis_edited = True
858
- st.rerun()
859
- return
860
-
861
- # إنشاء DataFrame من قائمة العمالة
862
- df = pd.DataFrame(item_analysis['labor'])
863
- df.columns = ['العامل', 'الوحدة', 'الكمية', 'سعر الوحدة', 'السعر الإجمالي']
864
-
865
- # إضافة عمود للإجراءات
866
- df['الإجراءات'] = '✏️ 🗑️'
867
-
868
- # تحويل الجدول إلى جدول قابل للتعديل
869
- edited_df = st.data_editor(
870
- df,
871
- use_container_width=True,
872
- key="edit_labor_table",
873
- column_config={
874
- "العامل": st.column_config.Column("العامل"),
875
- "الوحدة": st.column_config.Column("الوحدة"),
876
- "الكمية": st.column_config.NumberColumn(
877
- "الكمية",
878
- min_value=0.0,
879
- format="%.2f",
880
- step=0.1,
881
- on_change=self._update_total_price,
882
- ),
883
- "سعر الوحدة": st.column_config.NumberColumn(
884
- "سعر الوحدة",
885
- min_value=0.0,
886
- format="%.2f ريال",
887
- step=0.1,
888
- on_change=self._update_total_price,
889
- ),
890
- "السعر الإجمالي": st.column_config.NumberColumn(
891
- "السعر الإجمالي",
892
- format="%.2f ريال",
893
- disabled=True,
894
- ),
895
- "الإجراءات": st.column_config.Column(
896
- "الإجراءات",
897
- width="small",
898
- disabled=True,
899
- ),
900
- },
901
- num_rows="dynamic",
902
- hide_index=True,
903
- )
904
-
905
- # تحديث البيانات في item_analysis بناءً على التعديلات
906
- if edited_df is not None and not edited_df.equals(df):
907
- # حذف جميع العمالة الحالية
908
- item_analysis['labor'] = []
909
-
910
- # إضافة العمالة المعدلة
911
- for i, row in edited_df.iterrows():
912
- # حساب السعر الإجمالي
913
- total_price = row['الكمية'] * row['سعر الوحدة']
914
-
915
- # إضافة العامل
916
- item_analysis['labor'].append({
917
- 'name': row['العامل'],
918
- 'unit': row['الوحدة'],
919
- 'quantity': row['الكمية'],
920
- 'unit_price': row['سعر الوحدة'],
921
- 'total_price': total_price
922
- })
923
-
924
- # إعادة حساب إجمالي تكلفة العمالة
925
- item_analysis['total_labor_cost'] = sum(labor_item['total_price'] for labor_item in item_analysis['labor'])
926
-
927
- # تعيين علامة التعديل
928
- st.session_state.item_analysis_edited = True
929
- st.rerun()
930
-
931
- # عرض إجمالي تكلفة العمالة بتنسيق محسن
932
- st.markdown(f"""
933
- <div class="total-summary">
934
- <div class="total-label">إجمالي تكلفة العمالة:</div>
935
- <div class="total-value">{item_analysis['total_labor_cost']:,.2f} ريال</div>
936
- </div>
937
- """, unsafe_allow_html=True)
938
-
939
- # أزرار التحكم
940
- col1, col2 = st.columns(2)
941
- with col1:
942
- if st.button("➕ إضافة عامل جديد", key="add_labor"):
943
- item_analysis['labor'].append({
944
- 'name': 'عامل جديد',
945
- 'unit': 'يوم',
946
- 'quantity': 1.0,
947
- 'unit_price': 0.0,
948
- 'total_price': 0.0
949
- })
950
- st.session_state.item_analysis_edited = True
951
- st.rerun()
952
-
953
- def _render_subcontractors_analysis_enhanced(self, item_analysis):
954
- """عرض تحليل المقاولين من الباطن مع إمكانية التعديل (نسخة محسنة)"""
955
- st.subheader("تحليل المقاولين من الباطن")
956
-
957
- if not item_analysis['subcontractors']:
958
- st.info("لا يوجد مقاولين من الباطن في تحليل هذا البند.")
959
-
960
- # إضافة زر لإضافة مقاول جديد
961
- if st.button("إضافة مقاول", key="add_first_subcontractor"):
962
- item_analysis['subcontractors'] = [{
963
- 'name': 'مقاول جديد',
964
- 'unit': 'عقد',
965
- 'quantity': 1.0,
966
- 'unit_price': 0.0,
967
- 'total_price': 0.0
968
- }]
969
- st.session_state.item_analysis_edited = True
970
- st.rerun()
971
- return
972
-
973
- # إنشاء DataFrame من قائمة المقاولين
974
- df = pd.DataFrame(item_analysis['subcontractors'])
975
- df.columns = ['المقاول', 'الوحدة', 'الكمية', 'سعر الوحدة', 'السعر الإجمالي']
976
-
977
- # إضافة عمود للإجراءات
978
- df['الإجراءات'] = '✏️ 🗑️'
979
-
980
- # تحويل الجدول إلى جدول قابل للتعديل
981
- edited_df = st.data_editor(
982
- df,
983
- use_container_width=True,
984
- key="edit_subcontractors_table",
985
- column_config={
986
- "المقاول": st.column_config.Column("المقاول"),
987
- "الوحدة": st.column_config.Column("الوحدة"),
988
- "الكمية": st.column_config.NumberColumn(
989
- "الكمية",
990
- min_value=0.0,
991
- format="%.2f",
992
- step=0.1,
993
- on_change=self._update_total_price,
994
- ),
995
- "سعر الوحدة": st.column_config.NumberColumn(
996
- "سعر الوحدة",
997
- min_value=0.0,
998
- format="%.2f ريال",
999
- step=0.1,
1000
- on_change=self._update_total_price,
1001
- ),
1002
- "السعر الإجمالي": st.column_config.NumberColumn(
1003
- "السعر الإجمالي",
1004
- format="%.2f ريال",
1005
- disabled=True,
1006
- ),
1007
- "الإجراءات": st.column_config.Column(
1008
- "الإجراءات",
1009
- width="small",
1010
- disabled=True,
1011
- ),
1012
- },
1013
- num_rows="dynamic",
1014
- hide_index=True,
1015
- )
1016
-
1017
- # تحديث البيانات في item_analysis بناءً على التعديلات
1018
- if edited_df is not None and not edited_df.equals(df):
1019
- # حذف جميع المقاولين الحاليين
1020
- item_analysis['subcontractors'] = []
1021
-
1022
- # إضافة المقاولين المعدلين
1023
- for i, row in edited_df.iterrows():
1024
- # حساب السعر الإجمالي
1025
- total_price = row['الكمية'] * row['سعر الوحدة']
1026
-
1027
- # إضافة المقاول
1028
- item_analysis['subcontractors'].append({
1029
- 'name': row['المقاول'],
1030
- 'unit': row['الوحدة'],
1031
- 'quantity': row['الكمية'],
1032
- 'unit_price': row['سعر الوحدة'],
1033
- 'total_price': total_price
1034
- })
1035
-
1036
- # إعادة حساب إجمالي تكلفة المقاولين
1037
- item_analysis['total_subcontractors_cost'] = sum(subcontractor['total_price'] for subcontractor in item_analysis['subcontractors'])
1038
-
1039
- # تعيين علامة التعديل
1040
- st.session_state.item_analysis_edited = True
1041
- st.rerun()
1042
-
1043
- # عرض إجمالي تكلفة المقاولين بتنسيق محسن
1044
- st.markdown(f"""
1045
- <div class="total-summary">
1046
- <div class="total-label">إجمالي تكلفة المقاولين من الباطن:</div>
1047
- <div class="total-value">{item_analysis['total_subcontractors_cost']:,.2f} ريال</div>
1048
- </div>
1049
- """, unsafe_allow_html=True)
1050
-
1051
- # أزرار التحكم
1052
- col1, col2 = st.columns(2)
1053
- with col1:
1054
- if st.button("➕ إضافة مقاول جديد", key="add_subcontractor"):
1055
- item_analysis['subcontractors'].append({
1056
- 'name': 'مقاول جديد',
1057
- 'unit': 'عقد',
1058
- 'quantity': 1.0,
1059
- 'unit_price': 0.0,
1060
- 'total_price': 0.0
1061
- })
1062
- st.session_state.item_analysis_edited = True
1063
- st.rerun()
1064
-
1065
- def _render_cost_summary_enhanced(self, item_analysis):
1066
- """عرض ملخص التكلفة (نسخة محسنة)"""
1067
- st.subheader("ملخص التكلفة")
1068
-
1069
- # حساب إجمالي التكلفة
1070
- total_cost = (
1071
- item_analysis['total_materials_cost'] +
1072
- item_analysis['total_equipment_cost'] +
1073
- item_analysis['total_labor_cost'] +
1074
- item_analysis.get('total_subcontractors_cost', 0) +
1075
- item_analysis['overhead_cost']
1076
- )
1077
-
1078
- # إنشاء بيانات ملخص التكلفة
1079
- cost_data = [
1080
- {"البند": "تكلفة المواد", "القيمة": item_analysis['total_materials_cost'], "النسبة": item_analysis['total_materials_cost'] / total_cost * 100 if total_cost > 0 else 0},
1081
- {"البند": "تكلفة المعدات", "القيمة": item_analysis['total_equipment_cost'], "النسبة": item_analysis['total_equipment_cost'] / total_cost * 100 if total_cost > 0 else 0},
1082
- {"البند": "تكلفة العمالة", "القيمة": item_analysis['total_labor_cost'], "النسبة": item_analysis['total_labor_cost'] / total_cost * 100 if total_cost > 0 else 0},
1083
- {"البند": "تكلفة المقاولين من الباطن", "القيمة": item_analysis.get('total_subcontractors_cost', 0), "النسبة": item_analysis.get('total_subcontractors_cost', 0) / total_cost * 100 if total_cost > 0 else 0},
1084
- {"البند": "تكاليف غير مباشرة", "القيمة": item_analysis['overhead_cost'], "النسبة": item_analysis['overhead_cost'] / total_cost * 100 if total_cost > 0 else 0},
1085
- {"البند": "إجمالي التكلفة", "القيمة": total_cost, "النسبة": 100.0},
1086
- ]
1087
-
1088
- # إنشاء DataFrame من بيانات ملخص التكلفة
1089
- df = pd.DataFrame(cost_data)
1090
-
1091
- # تنسيق الأعمدة
1092
- df["القيمة (ريال)"] = df["القيمة"].apply(lambda x: f"{x:,.2f}")
1093
- df["النسبة (%)"] = df["النسبة"].apply(lambda x: f"{x:.2f}%")
1094
-
1095
- # حذف الأعمدة الأصلية
1096
- df = df.drop(columns=["القيمة", "النسبة"])
1097
-
1098
- # عرض الجدول
1099
- st.dataframe(df, use_container_width=True, hide_index=True)
1100
-
1101
- # عرض الرسم البياني
1102
- self._render_cost_chart(cost_data[:-1]) # استثناء صف الإجمالي
1103
-
1104
- # عرض تفاصيل الربح
1105
- st.subheader("تفاصيل الربح")
1106
-
1107
- col1, col2 = st.columns(2)
1108
-
1109
- with col1:
1110
- st.write(f"**سعر الوحدة:** {item_analysis['unit_price']:,.2f} ريال")
1111
- st.write(f"**إجمالي التكلفة:** {total_cost:,.2f} ريال")
1112
- st.write(f"**الربح:** {item_analysis['profit']:,.2f} ريال")
1113
- st.write(f"**نسبة الربح:** {(item_analysis['profit'] / item_analysis['unit_price'] * 100) if item_analysis['unit_price'] > 0 else 0:.2f}%")
1114
-
1115
- # تحديث الربح إذا تم تعديل التكاليف
1116
- if st.session_state.item_analysis_edited:
1117
- item_analysis['profit'] = item_analysis['unit_price'] - total_cost
1118
- st.session_state.item_analysis_edited = True
1119
- st.rerun()
1120
-
1121
- with col2:
1122
- new_profit = st.number_input(
1123
- "الربح (ريال)",
1124
- min_value=float(-1000000.0),
1125
- value=float(item_analysis['profit']),
1126
- step=10.0,
1127
- format="%.2f",
1128
- key="edit_profit"
1129
- )
1130
-
1131
- if new_profit != item_analysis['profit']:
1132
- item_analysis['profit'] = new_profit
1133
-
1134
- # إعادة حساب سعر الوحدة
1135
- total_cost = (
1136
- item_analysis['total_materials_cost'] +
1137
- item_analysis['total_equipment_cost'] +
1138
- item_analysis['total_labor_cost'] +
1139
- item_analysis.get('total_subcontractors_cost', 0) +
1140
- item_analysis['overhead_cost']
1141
- )
1142
-
1143
- item_analysis['unit_price'] = total_cost + new_profit
1144
- st.session_state.item_analysis_edited = True
1145
- st.rerun()
1146
-
1147
- def _render_cost_chart(self, cost_data):
1148
- """عرض رسم بياني للتكاليف"""
1149
- # إنشاء بيانات الرسم البياني
1150
- labels = [item["البند"] for item in cost_data]
1151
- values = [item["القيمة"] for item in cost_data]
1152
-
1153
- # تحويل النص العربي ليعرض بشكل صحيح
1154
- labels_reshaped = [get_display(arabic_reshaper.reshape(label)) for label in labels]
1155
-
1156
- # إنشاء الرسم البياني
1157
- fig, ax = plt.subplots(figsize=(10, 6))
1158
- wedges, texts, autotexts = ax.pie(
1159
- values,
1160
- labels=labels_reshaped,
1161
- autopct='%1.1f%%',
1162
- startangle=90,
1163
- shadow=False,
1164
- )
1165
-
1166
- # تنسيق الرسم البياني
1167
- ax.axis('equal')
1168
- plt.title(get_display(arabic_reshaper.reshape('توزيع التكاليف')))
1169
-
1170
- # عرض الرسم البياني
1171
- st.pyplot(fig)
1172
-
1173
- def _update_boq_item_from_analysis(self, item_id, item_analysis):
1174
- """تحديث البند في جدول الكميات بناءً على التحليل"""
1175
- # الحصول على المشروع الحالي
1176
- current_project = next((p for p in st.session_state.projects if p['id'] == st.session_state.current_project_id), None)
1177
-
1178
- if not current_project:
1179
- return
1180
-
1181
- # البحث عن البند في جدول الكميات
1182
- for item in current_project['boq']:
1183
- if item['id'] == item_id:
1184
- # تحديث سعر الوحدة والسعر الإجمالي
1185
- item['unit_price'] = item_analysis['unit_price']
1186
- item['total_price'] = item['quantity'] * item['unit_price']
1187
- break
1188
-
1189
- def _render_cost_analysis(self):
1190
- """عرض تحليل التكلفة"""
1191
- st.header("تحليل التكلفة")
1192
- st.info("هذه الصفحة قيد التطوير.")
1193
-
1194
- def _render_profit_analysis(self):
1195
- """عرض تحليل الربحية"""
1196
- st.header("تحليل الربحية")
1197
- st.info("هذه الصفحة قيد التطوير.")
1198
-
1199
- def _render_pricing_strategies(self):
1200
- """عرض استراتيجيات التسعير"""
1201
- st.header("استراتيجيات التسعير")
1202
- st.info("هذه الصفحة قيد التطوير.")
1203
-
1204
- # تشغيل التطبيق
1205
- if __name__ == "__main__":
1206
- app = PricingApp()
1207
- app.run()
1208
-
1209
- def _update_total_price(self, widget_value, widget_id):
1210
- """تحديث السعر الإجمالي تلقائياً عند تغيير الكمية أو سعر الوحدة"""
1211
- # سيتم استدعاء هذه الدالة عند تغيير قيمة الكمية أو سعر الوحدة
1212
- # يتم تحديث البيانات في الجدول وإعادة حساب المجموع الكلي
1213
- st.session_state.item_analysis_edited = True
 
12
  class PricingApp:
13
  def __init__(self):
14
  """تهيئة التطبيق"""
 
 
 
15
  # إضافة CSS للتنسيق
16
  self._add_custom_css()
17
 
 
434
  st.info("لا توجد بنود في جدول الكميات. قم بإضافة بنود جديدة.")
435
 
436
  def _render_item_price_analysis(self):
437
+
438
+ (Content truncated due to size limit. Use line ranges to read in chunks)