EGYADMIN commited on
Commit
692c469
·
verified ·
1 Parent(s): e055149

Create pricing_app.py

Browse files
Files changed (1) hide show
  1. modules/pricing/pricing_app.py +1237 -0
modules/pricing/pricing_app.py ADDED
@@ -0,0 +1,1237 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ import pandas as pd
3
+ import numpy as np
4
+ import random
5
+ import matplotlib.pyplot as plt
6
+ import arabic_reshaper
7
+ from bidi.algorithm import get_display
8
+ import json
9
+ import os
10
+ from datetime import datetime
11
+
12
+ class PricingApp:
13
+ def __init__(self):
14
+ """تهيئة التطبيق"""
15
+ # إضافة CSS للتنسيق
16
+ self._add_custom_css()
17
+
18
+ # تهيئة حالة الجلسة
19
+ if 'projects' not in st.session_state:
20
+ st.session_state.projects = self._load_sample_projects()
21
+
22
+ if 'current_project_id' not in st.session_state:
23
+ st.session_state.current_project_id = None
24
+
25
+ if 'current_tab' not in st.session_state:
26
+ st.session_state.current_tab = "boq"
27
+
28
+ if 'item_analysis_edited' not in st.session_state:
29
+ st.session_state.item_analysis_edited = False
30
+
31
+ if 'new_item_created' not in st.session_state:
32
+ st.session_state.new_item_created = False
33
+
34
+ def _add_custom_css(self):
35
+ """إضافة CSS مخصص للتطبيق"""
36
+ st.markdown("""
37
+ <style>
38
+ .main {
39
+ direction: rtl;
40
+ }
41
+ .stButton button {
42
+ width: 100%;
43
+ }
44
+ .total-row {
45
+ background-color: #f0f0f0 !important;
46
+ font-weight: bold !important;
47
+ }
48
+ .action-button {
49
+ border: none;
50
+ background: none;
51
+ cursor: pointer;
52
+ padding: 5px;
53
+ margin: 0 2px;
54
+ }
55
+ .edit-button {
56
+ color: #1E88E5;
57
+ }
58
+ .delete-button {
59
+ color: #E53935;
60
+ }
61
+ .stDataFrame td:last-child {
62
+ text-align: center;
63
+ }
64
+ .highlight-total {
65
+ background-color: #e6f7ff;
66
+ font-weight: bold;
67
+ padding: 10px;
68
+ border-radius: 5px;
69
+ margin-top: 10px;
70
+ }
71
+ .total-summary {
72
+ background-color: #f8f9fa;
73
+ border: 1px solid #dee2e6;
74
+ border-radius: 5px;
75
+ padding: 15px;
76
+ margin-top: 20px;
77
+ box-shadow: 0 2px 4px rgba(0,0,0,0.05);
78
+ }
79
+ .total-value {
80
+ font-size: 18px;
81
+ font-weight: bold;
82
+ color: #0366d6;
83
+ }
84
+ .total-label {
85
+ font-weight: bold;
86
+ margin-bottom: 5px;
87
+ }
88
+ .delete-confirm {
89
+ background-color: #ffebee;
90
+ border: 1px solid #ffcdd2;
91
+ border-radius: 5px;
92
+ padding: 10px;
93
+ margin: 10px 0;
94
+ }
95
+ .main-total {
96
+ font-size: 22px;
97
+ font-weight: bold;
98
+ color: #1a73e8;
99
+ background-color: #f0f7ff;
100
+ padding: 15px;
101
+ border-radius: 8px;
102
+ margin-top: 25px;
103
+ text-align: center;
104
+ }
105
+ .card {
106
+ border: 1px solid #ddd;
107
+ border-radius: 8px;
108
+ padding: 15px;
109
+ margin-bottom: 15px;
110
+ background-color: #fff;
111
+ box-shadow: 0 2px 5px rgba(0,0,0,0.1);
112
+ }
113
+ .card-header {
114
+ border-bottom: 1px solid #eee;
115
+ padding-bottom: 10px;
116
+ margin-bottom: 10px;
117
+ font-weight: bold;
118
+ font-size: 16px;
119
+ color: #333;
120
+ }
121
+ .summary-container {
122
+ display: flex;
123
+ justify-content: space-between;
124
+ margin-top: 15px;
125
+ }
126
+ .summary-item {
127
+ text-align: center;
128
+ padding: 10px;
129
+ border-radius: 5px;
130
+ background-color: #f5f5f5;
131
+ width: 23%;
132
+ }
133
+ .summary-value {
134
+ font-size: 20px;
135
+ font-weight: bold;
136
+ color: #1a73e8;
137
+ }
138
+ .summary-label {
139
+ font-size: 14px;
140
+ color: #666;
141
+ }
142
+ .primary-button {
143
+ background-color: #1a73e8;
144
+ color: white;
145
+ border: none;
146
+ padding: 10px 15px;
147
+ border-radius: 5px;
148
+ font-weight: bold;
149
+ cursor: pointer;
150
+ }
151
+ </style>
152
+ """, unsafe_allow_html=True)
153
+
154
+ def _update_total_price(self):
155
+ """تحديث السعر الإجمالي تلقائياً عند تغيير الكمية أو سعر الوحدة"""
156
+ # سيتم استدعاء هذه الدالة عند تغيير قيمة الكمية أو سعر الوحدة
157
+ # يتم تحديث البيانات في الجدول وإعادة حساب المجموع الكلي
158
+ st.session_state.item_analysis_edited = True
159
+
160
+ def render(self):
161
+ """دالة العرض الرئيسية للتوافق مع واجهة التطبيق الرئيسي"""
162
+ # استدعاء دالة run لتنفيذ وظائف التطبيق
163
+ self.run()
164
+
165
+ def run(self):
166
+ """تشغيل التطبيق"""
167
+ st.title("وحدة التسعير المتكاملة")
168
+
169
+ # عرض الشريط العلوي
170
+ self._render_top_bar()
171
+
172
+ # إذا تم إنشاء بند جديد، انتقل مباشرة إلى تحليل السعر
173
+ if st.session_state.new_item_created:
174
+ st.session_state.current_tab = "item_analysis"
175
+ st.session_state.new_item_created = False
176
+
177
+ # عرض المحتوى حسب التبويب المحدد
178
+ if st.session_state.current_tab == "boq":
179
+ self._render_bill_of_quantities()
180
+ elif st.session_state.current_tab == "item_analysis":
181
+ self._render_item_price_analysis()
182
+
183
+ def _render_top_bar(self):
184
+ """عرض الشريط العلوي"""
185
+ cols = st.columns([1, 1, 1, 1, 1])
186
+
187
+ with cols[0]:
188
+ if st.button("جدول الكميات", use_container_width=True):
189
+ st.session_state.current_tab = "boq"
190
+ st.rerun()
191
+
192
+ with cols[1]:
193
+ if st.button("تحليل الأسعار", use_container_width=True):
194
+ if st.session_state.current_project_id is not None:
195
+ st.session_state.current_tab = "item_analysis"
196
+ st.rerun()
197
+ else:
198
+ st.warning("الرجاء اختيار مشروع أولاً")
199
+
200
+ with cols[4]:
201
+ if st.button("إنشاء مشروع جديد", use_container_width=True):
202
+ self._create_new_project()
203
+
204
+ def _create_new_project(self):
205
+ """إنشاء مشروع جديد"""
206
+ project_id = f"proj_{len(st.session_state.projects) + 1}"
207
+ new_project = {
208
+ "id": project_id,
209
+ "name": f"مشروع رقم {len(st.session_state.projects) + 1}",
210
+ "client": "عميل قياسي",
211
+ "date": datetime.now().strftime("%Y-%m-%d"),
212
+ "budget": 1000000.00,
213
+ "items": []
214
+ }
215
+ st.session_state.projects[project_id] = new_project
216
+ st.session_state.current_project_id = project_id
217
+ st.session_state.current_tab = "boq"
218
+ st.rerun()
219
+
220
+ def _render_bill_of_quantities(self):
221
+ """عرض جدول الكميات"""
222
+ st.header("جدول الكميات")
223
+
224
+ # اختيار المشروع
225
+ project_options = {proj["name"]: proj_id for proj_id, proj in st.session_state.projects.items()}
226
+ selected_project_name = st.selectbox(
227
+ "اختر المشروع",
228
+ options=list(project_options.keys()),
229
+ index=0 if st.session_state.current_project_id is None else list(project_options.keys()).index(st.session_state.projects[st.session_state.current_project_id]["name"])
230
+ )
231
+
232
+ st.session_state.current_project_id = project_options[selected_project_name]
233
+ current_project = st.session_state.projects[st.session_state.current_project_id]
234
+
235
+ # عرض معلومات المشروع
236
+ col1, col2, col3 = st.columns(3)
237
+ with col1:
238
+ st.write(f"**العميل:** {current_project['client']}")
239
+ with col2:
240
+ st.write(f"**التاريخ:** {current_project['date']}")
241
+ with col3:
242
+ st.write(f"**الميزانية:** {current_project['budget']:,.2f} ريال")
243
+
244
+ # إضافة بند جديد
245
+ with st.expander("إضافة بند جديد"):
246
+ with st.form("add_item_form"):
247
+ col1, col2 = st.columns(2)
248
+ with col1:
249
+ new_item_name = st.text_input("اسم البند")
250
+ new_item_unit = st.text_input("الوحدة")
251
+ with col2:
252
+ new_item_quantity = st.number_input("الكمية", min_value=0.0, step=0.1)
253
+ new_item_price = st.number_input("سعر الوحدة (ريال)", min_value=0.0, step=0.1)
254
+
255
+ submitted = st.form_submit_button("إضافة البند")
256
+ if submitted and new_item_name and new_item_unit:
257
+ new_item = {
258
+ "id": f"item_{len(current_project['items']) + 1}",
259
+ "name": new_item_name,
260
+ "unit": new_item_unit,
261
+ "quantity": float(new_item_quantity),
262
+ "unit_price": float(new_item_price),
263
+ "total_price": float(new_item_quantity) * float(new_item_price),
264
+ "materials": [],
265
+ "labor": [],
266
+ "equipment": [],
267
+ "subcontractors": []
268
+ }
269
+ current_project["items"].append(new_item)
270
+ st.session_state.current_item_index = len(current_project["items"]) - 1
271
+ st.session_state.new_item_created = True # فعّل الانتقال إلى تحل��ل السعر
272
+ st.success("تم حفظ التحليل بنجاح")
273
+ st.rerun()
274
+ with col2:
275
+ if st.button("العودة بدون حفظ", use_container_width=True):
276
+ st.session_state.current_tab = "boq"
277
+ st.session_state.item_analysis_edited = False
278
+ st.rerun()
279
+
280
+ # إضافة زر لتحديث سعر البند تلقائياً بناءً على التحليل
281
+ if st.button("احتساب السعر من التكلفة المباشرة + ربح", type="primary", use_container_width=True):
282
+ # إضافة نسبة ربح افتراضية (مثلاً 15٪)
283
+ profit_percentage = 15
284
+ # حساب السعر الجديد مع إضافة الربح
285
+ if current_item["quantity"] > 0:
286
+ profit_markup = direct_cost * profit_percentage / 100
287
+ new_total_price = direct_cost + profit_markup
288
+ new_unit_price = new_total_price / current_item["quantity"]
289
+
290
+ # تحديث سعر البند
291
+ current_item["unit_price"] = new_unit_price
292
+ current_item["total_price"] = new_total_price
293
+
294
+ # تحديث حالة التعديل
295
+ st.session_state.item_analysis_edited = True
296
+ st.success(f"تم احتساب السعر بنجاح مع إضافة ربح {profit_percentage}%")
297
+ st.rerun()
298
+ else:
299
+ st.warning("لا يمكن احتساب السعر لأن الكمية صفر.")
300
+
301
+ def _load_sample_projects(self):
302
+ """تحميل مشاريع نموذجية"""
303
+ return {
304
+ "proj_1": {
305
+ "id": "proj_1",
306
+ "name": "مشروع إنشاء مبنى سكني",
307
+ "client": "شركة الإعمار",
308
+ "date": "2025-03-24",
309
+ "budget": 1000000.00,
310
+ "items": [
311
+ {
312
+ "id": "item_1",
313
+ "name": "أعمال الحفر",
314
+ "unit": "م3",
315
+ "quantity": 1500.0,
316
+ "unit_price": 35.0,
317
+ "total_price": 52500.0,
318
+ "materials": [
319
+ {
320
+ "id": "material_1",
321
+ "name": "رمل",
322
+ "unit": "م3",
323
+ "quantity": 50.0,
324
+ "unit_price": 120.0,
325
+ "total_price": 6000.0
326
+ }
327
+ ],
328
+ "labor": [
329
+ {
330
+ "id": "labor_1",
331
+ "name": "عامل حفر",
332
+ "unit": "يوم",
333
+ "quantity": 30.0,
334
+ "unit_price": 150.0,
335
+ "total_price": 4500.0
336
+ }
337
+ ],
338
+ "equipment": [
339
+ {
340
+ "id": "equipment_1",
341
+ "name": "حفارة",
342
+ "unit": "يوم",
343
+ "quantity": 15.0,
344
+ "unit_price": 1200.0,
345
+ "total_price": 18000.0
346
+ }
347
+ ],
348
+ "subcontractors": []
349
+ },
350
+ {
351
+ "id": "item_2",
352
+ "name": "أعمال الخرسانة",
353
+ "unit": "م3",
354
+ "quantity": 800.0,
355
+ "unit_price": 450.0,
356
+ "total_price": 360000.0,
357
+ "materials": [
358
+ {
359
+ "id": "material_1",
360
+ "name": "أسمنت",
361
+ "unit": "طن",
362
+ "quantity": 120.0,
363
+ "unit_price": 600.0,
364
+ "total_price": 72000.0
365
+ },
366
+ {
367
+ "id": "material_2",
368
+ "name": "رمل",
369
+ "unit": "م3",
370
+ "quantity": 400.0,
371
+ "unit_price": 120.0,
372
+ "total_price": 48000.0
373
+ },
374
+ {
375
+ "id": "material_3",
376
+ "name": "حصى",
377
+ "unit": "م3",
378
+ "quantity": 600.0,
379
+ "unit_price": 150.0,
380
+ "total_price": 90000.0
381
+ }
382
+ ],
383
+ "labor": [
384
+ {
385
+ "id": "labor_1",
386
+ "name": "عامل خرسانة",
387
+ "unit": "يوم",
388
+ "quantity": 200.0,
389
+ "unit_price": 200.0,
390
+ "total_price": 40000.0
391
+ },
392
+ {
393
+ "id": "labor_2",
394
+ "name": "فني خرسانة",
395
+ "unit": "يوم",
396
+ "quantity": 50.0,
397
+ "unit_price": 350.0,
398
+ "total_price": 17500.0
399
+ }
400
+ ],
401
+ "equipment": [
402
+ {
403
+ "id": "equipment_1",
404
+ "name": "خلاطة خرسانة",
405
+ "unit": "يوم",
406
+ "quantity": 40.0,
407
+ "unit_price": 800.0,
408
+ "total_price": 32000.0
409
+ },
410
+ {
411
+ "id": "equipment_2",
412
+ "name": "مضخة خرسانة",
413
+ "unit": "يوم",
414
+ "quantity": 20.0,
415
+ "unit_price": 1500.0,
416
+ "total_price": 30000.0
417
+ }
418
+ ],
419
+ "subcontractors": []
420
+ },
421
+ {
422
+ "id": "item_3",
423
+ "name": "أعمال البناء",
424
+ "unit": "م2",
425
+ "quantity": 2000.0,
426
+ "unit_price": 220.0,
427
+ "total_price": 440000.0,
428
+ "materials": [],
429
+ "labor": [],
430
+ "equipment": [],
431
+ "subcontractors": [
432
+ {
433
+ "id": "sub_1",
434
+ "name": "مقاول بناء",
435
+ "work": "بناء جدران",
436
+ "quantity": 1,
437
+ "unit_price": 250000.0,
438
+ "total_price": 250000.0
439
+ }
440
+ ]
441
+ }
442
+ ]
443
+ }
444
+ }
445
+
446
+ # تشغيل التطبيق عند تنفيذ الملف مباشرة
447
+ if __name__ == "__main__":
448
+ app = PricingApp()
449
+ app.run()ت إضافة البند بنجاح")
450
+ st.rerun()
451
+
452
+ # عرض جدول الكميات
453
+ if current_project["items"]:
454
+ st.markdown("### قائمة البنود")
455
+
456
+ # عرض كل بند كبطاقة منفصلة
457
+ for i, item in enumerate(current_project["items"]):
458
+ with st.container():
459
+ st.markdown(f"""
460
+ <div class="card">
461
+ <div class="card-header">{i+1}. {item['name']}</div>
462
+ <div>
463
+ <strong>الوحدة:</strong> {item['unit']} |
464
+ <strong>الكمية:</strong> {item['quantity']} |
465
+ <strong>سعر الوحدة:</strong> {item['unit_price']:,.2f} ريال |
466
+ <strong>السعر الإجمالي:</strong> {item['total_price']:,.2f} ريال
467
+ </div>
468
+ </div>
469
+ """, unsafe_allow_html=True)
470
+
471
+ col1, col2, col3 = st.columns([1, 1, 1])
472
+ with col1:
473
+ if st.button(f"تحليل البند", key=f"analyze_btn_{i}", use_container_width=True):
474
+ st.session_state.current_item_index = i
475
+ st.session_state.current_tab = "item_analysis"
476
+ st.rerun()
477
+ with col2:
478
+ if st.button(f"تعديل البند", key=f"edit_btn_{i}", use_container_width=True):
479
+ st.session_state[f"edit_item_{i}"] = True
480
+ with col3:
481
+ if st.button(f"حذف البند", key=f"delete_btn_{i}", use_container_width=True):
482
+ st.session_state[f"delete_item_{i}"] = True
483
+
484
+ # نموذج التعديل
485
+ if st.session_state.get(f"edit_item_{i}", False):
486
+ with st.form(f"edit_item_form_{i}"):
487
+ col1, col2 = st.columns(2)
488
+ with col1:
489
+ item_name = st.text_input("اسم البند", value=item["name"])
490
+ item_unit = st.text_input("الوحدة", value=item["unit"])
491
+ with col2:
492
+ item_quantity = st.number_input("الكمية", min_value=0.0, step=0.1, value=item["quantity"])
493
+ item_price = st.number_input("سعر الوحدة (ريال)", min_value=0.0, step=0.1, value=item["unit_price"])
494
+
495
+ col1, col2 = st.columns(2)
496
+ with col1:
497
+ if st.form_submit_button("حفظ التعديلات"):
498
+ item["name"] = item_name
499
+ item["unit"] = item_unit
500
+ item["quantity"] = float(item_quantity)
501
+ item["unit_price"] = float(item_price)
502
+ item["total_price"] = item["quantity"] * item["unit_price"]
503
+ st.session_state[f"edit_item_{i}"] = False
504
+ st.rerun()
505
+ with col2:
506
+ if st.form_submit_button("إلغاء"):
507
+ st.session_state[f"edit_item_{i}"] = False
508
+ st.rerun()
509
+
510
+ # تأكيد الحذف
511
+ if st.session_state.get(f"delete_item_{i}", False):
512
+ st.warning(f"هل أنت متأكد من حذف البند: {item['name']}؟")
513
+ col1, col2 = st.columns(2)
514
+ with col1:
515
+ if st.button("نعم، حذف", key=f"confirm_delete_item_{i}"):
516
+ current_project["items"].pop(i)
517
+ st.session_state[f"delete_item_{i}"] = False
518
+ st.rerun()
519
+ with col2:
520
+ if st.button("إلغاء", key=f"cancel_delete_item_{i}"):
521
+ st.session_state[f"delete_item_{i}"] = False
522
+ st.rerun()
523
+
524
+ # عرض المجموع الكلي
525
+ total_price = sum(item["total_price"] for item in current_project["items"])
526
+ st.markdown(f"""
527
+ <div class="main-total">
528
+ المجموع الكلي: {total_price:,.2f} ريال
529
+ </div>
530
+ """, unsafe_allow_html=True)
531
+ else:
532
+ st.info("لا توجد بنود في جدول الكميات. قم بإضافة بنود جديدة.")
533
+
534
+ def _render_item_price_analysis(self):
535
+ """عرض تحليل سعر البند"""
536
+ if st.session_state.current_project_id is None or "current_item_index" not in st.session_state:
537
+ st.warning("الرجاء اختيار مشروع وبند أولاً")
538
+ return
539
+
540
+ current_project = st.session_state.projects[st.session_state.current_project_id]
541
+ current_item = current_project["items"][st.session_state.current_item_index]
542
+
543
+ st.header(f"تحليل سعر البند: {current_item['name']}")
544
+
545
+ # معلومات البند
546
+ col1, col2, col3, col4 = st.columns(4)
547
+ with col1:
548
+ st.write(f"**الوحدة:** {current_item['unit']}")
549
+ with col2:
550
+ st.write(f"**الكمية:** {current_item['quantity']}")
551
+ with col3:
552
+ st.write(f"**سعر الوحدة:** {current_item['unit_price']} ريال")
553
+ with col4:
554
+ st.write(f"**السعر الإجمالي:** {current_item['total_price']} ريال")
555
+
556
+ # القيم الإجمالية
557
+ materials_total = sum(material.get("total_price", 0) for material in current_item["materials"])
558
+ labor_total = sum(labor.get("total_price", 0) for labor in current_item["labor"])
559
+ equipment_total = sum(equipment.get("total_price", 0) for equipment in current_item["equipment"])
560
+ subcontractors_total = sum(sub.get("total_price", 0) for sub in current_item["subcontractors"])
561
+ direct_cost = materials_total + labor_total + equipment_total + subcontractors_total
562
+ overhead_profit = max(0, current_item["total_price"] - direct_cost)
563
+
564
+ # عرض ملخص البند
565
+ st.markdown("""
566
+ <div class="summary-container">
567
+ <div class="summary-item">
568
+ <div class="summary-value" id="materials-total"></div>
569
+ <div class="summary-label">تكلفة المواد</div>
570
+ </div>
571
+ <div class="summary-item">
572
+ <div class="summary-value" id="labor-total"></div>
573
+ <div class="summary-label">تكلفة العمالة</div>
574
+ </div>
575
+ <div class="summary-item">
576
+ <div class="summary-value" id="equipment-total"></div>
577
+ <div class="summary-label">تكلفة المعدات</div>
578
+ </div>
579
+ <div class="summary-item">
580
+ <div class="summary-value" id="subs-total"></div>
581
+ <div class="summary-label">المقاولين من الباطن</div>
582
+ </div>
583
+ </div>
584
+ """, unsafe_allow_html=True)
585
+
586
+ # تحديث القيم باستخدام JavaScript
587
+ st.markdown(f"""
588
+ <script>
589
+ document.getElementById('materials-total').innerText = '{materials_total:,.2f} ريال';
590
+ document.getElementById('labor-total').innerText = '{labor_total:,.2f} ريال';
591
+ document.getElementById('equipment-total').innerText = '{equipment_total:,.2f} ريال';
592
+ document.getElementById('subs-total').innerText = '{subcontractors_total:,.2f} ريال';
593
+ </script>
594
+ """, unsafe_allow_html=True)
595
+
596
+ # زر حفظ التغييرات
597
+ if st.session_state.item_analysis_edited:
598
+ if st.button("حفظ التغييرات وتحديث السعر", type="primary", use_container_width=True):
599
+ # تحديث السعر الإجمالي للبند
600
+ current_item["unit_price"] = direct_cost / current_item["quantity"] if current_item["quantity"] > 0 else 0
601
+ current_item["total_price"] = current_item["unit_price"] * current_item["quantity"]
602
+
603
+ st.session_state.item_analysis_edited = False
604
+ st.success("تم حفظ التغييرات وتحديث السعر بنجاح")
605
+ st.rerun()
606
+
607
+ # تبويبات لعناصر التكلفة
608
+ tab1, tab2, tab3, tab4 = st.tabs(["المواد", "العمالة", "المعدات", "المقاولين من الباطن"])
609
+
610
+ # تحليل المواد
611
+ with tab1:
612
+ st.subheader("تحليل المواد")
613
+
614
+ # إضافة مادة جديدة
615
+ with st.expander("إضافة مادة جديدة", expanded=True):
616
+ with st.form("add_material_form"):
617
+ col1, col2 = st.columns(2)
618
+ with col1:
619
+ new_material_name = st.text_input("اسم المادة")
620
+ new_material_unit = st.text_input("الوحدة")
621
+ with col2:
622
+ new_material_quantity = st.number_input("الكمية", min_value=0.0, step=0.1)
623
+ new_material_price = st.number_input("سعر الوحدة (ريال)", min_value=0.0, step=0.1)
624
+
625
+ submitted = st.form_submit_button("إضافة المادة")
626
+ if submitted and new_material_name and new_material_unit:
627
+ new_material = {
628
+ "id": f"material_{len(current_item['materials']) + 1}",
629
+ "name": new_material_name,
630
+ "unit": new_material_unit,
631
+ "quantity": float(new_material_quantity),
632
+ "unit_price": float(new_material_price),
633
+ "total_price": float(new_material_quantity) * float(new_material_price)
634
+ }
635
+ current_item["materials"].append(new_material)
636
+ st.session_state.item_analysis_edited = True
637
+ st.success("تمت إضافة المادة بنجاح")
638
+ st.rerun()
639
+
640
+ # عرض جدول المواد
641
+ if current_item["materials"]:
642
+ # تحويل البيانات إلى DataFrame
643
+ materials_data = []
644
+ for i, material in enumerate(current_item["materials"]):
645
+ materials_data.append({
646
+ "م": i + 1,
647
+ "اسم المادة": material["name"],
648
+ "الوحدة": material["unit"],
649
+ "الكمية": material["quantity"],
650
+ "سعر الوحدة (ريال)": material["unit_price"],
651
+ "السعر الإجمالي (ريال)": material["total_price"]
652
+ })
653
+
654
+ materials_df = pd.DataFrame(materials_data)
655
+
656
+ # إضافة صف المجموع
657
+ total_row = pd.DataFrame([{
658
+ "م": "",
659
+ "اسم المادة": "المجموع",
660
+ "الوحدة": "",
661
+ "الكمية": "",
662
+ "سعر الوحدة (ريال)": "",
663
+ "السعر الإجمالي (ريال)": materials_total
664
+ }])
665
+
666
+ # استخدام pd.concat بدلاً من append
667
+ materials_df = pd.concat([materials_df, total_row], ignore_index=True)
668
+
669
+ # عرض الجدول القابل للتعديل
670
+ edited_materials_df = st.data_editor(
671
+ materials_df,
672
+ column_config={
673
+ "م": st.column_config.NumberColumn("م", width="small"),
674
+ "اسم المادة": st.column_config.TextColumn("اسم المادة"),
675
+ "الوحدة": st.column_config.TextColumn("الوحدة", width="small"),
676
+ "الكمية": st.column_config.NumberColumn("الكمية", format="%.2f", width="small"),
677
+ "سعر الوحدة (ريال)": st.column_config.NumberColumn("سعر الوحدة (ريال)", format="%.2f"),
678
+ "السعر الإجمالي (ريال)": st.column_config.NumberColumn("السعر الإجمالي (ريال)", format="%.2f"),
679
+ },
680
+ hide_index=True,
681
+ key="materials_table",
682
+ on_change=self._update_total_price,
683
+ disabled=["م", "اسم المادة", "الوحدة", "السعر الإجمالي (ريال)"]
684
+ )
685
+
686
+ # تحديث البيانات بعد التعديل
687
+ for i, material in enumerate(current_item["materials"]):
688
+ if i < len(edited_materials_df) - 1: # تجاهل صف المجموع
689
+ material["quantity"] = edited_materials_df.iloc[i]["الكمية"]
690
+ material["unit_price"] = edited_materials_df.iloc[i]["سعر الوحدة (ريال)"]
691
+ material["total_price"] = material["quantity"] * material["unit_price"]
692
+
693
+ # معالجة أزرار التعديل والحذف
694
+ for i in range(len(current_item["materials"])):
695
+ col1, col2 = st.columns([1, 1])
696
+ with col1:
697
+ if st.button(f"تعديل المادة {i+1}", key=f"edit_material_btn_{i}"):
698
+ st.session_state[f"edit_material_{i}"] = True
699
+
700
+ with col2:
701
+ if st.button(f"حذف المادة {i+1}", key=f"delete_material_btn_{i}"):
702
+ st.session_state[f"delete_material_{i}"] = True
703
+
704
+ # نموذج التعديل
705
+ if st.session_state.get(f"edit_material_{i}", False):
706
+ with st.form(f"edit_material_form_{i}"):
707
+ material = current_item["materials"][i]
708
+ col1, col2 = st.columns(2)
709
+ with col1:
710
+ material_name = st.text_input("اسم المادة", value=material["name"])
711
+ material_unit = st.text_input("الوحدة", value=material["unit"])
712
+ with col2:
713
+ material_quantity = st.number_input("الكمية", min_value=0.0, step=0.1, value=material["quantity"])
714
+ material_price = st.number_input("سعر الوحدة (ريال)", min_value=0.0, step=0.1, value=material["unit_price"])
715
+
716
+ col1, col2 = st.columns(2)
717
+ with col1:
718
+ if st.form_submit_button("حفظ التعديلات"):
719
+ material["name"] = material_name
720
+ material["unit"] = material_unit
721
+ material["quantity"] = float(material_quantity)
722
+ material["unit_price"] = float(material_price)
723
+ material["total_price"] = material["quantity"] * material["unit_price"]
724
+ st.session_state.item_analysis_edited = True
725
+ st.session_state[f"edit_material_{i}"] = False
726
+ st.rerun()
727
+ with col2:
728
+ if st.form_submit_button("إلغاء"):
729
+ st.session_state[f"edit_material_{i}"] = False
730
+ st.rerun()
731
+
732
+ # تأكيد الحذف
733
+ if st.session_state.get(f"delete_material_{i}", False):
734
+ st.warning(f"هل أنت متأكد من حذف المادة: {current_item['materials'][i]['name']}؟")
735
+ col1, col2 = st.columns(2)
736
+ with col1:
737
+ if st.button("نعم، حذف", key=f"confirm_delete_material_{i}"):
738
+ current_item["materials"].pop(i)
739
+ st.session_state.item_analysis_edited = True
740
+ st.session_state[f"delete_material_{i}"] = False
741
+ st.rerun()
742
+ with col2:
743
+ if st.button("إلغاء", key=f"cancel_delete_material_{i}"):
744
+ st.session_state[f"delete_material_{i}"] = False
745
+ st.rerun()
746
+
747
+ # عرض المجموع الكلي للمواد
748
+ st.markdown(f"<div class='highlight-total'>إجمالي تكلفة المواد: <span class='total-value'>{materials_total:,.2f}</span> ريال</div>", unsafe_allow_html=True)
749
+ else:
750
+ st.info("لا توجد مواد مضافة لهذا البند")
751
+
752
+ # تحليل العمالة
753
+ with tab2:
754
+ st.subheader("تحليل العمالة")
755
+
756
+ # إضافة عمالة جديدة
757
+ with st.expander("إضافة عمالة جديدة", expanded=True):
758
+ with st.form("add_labor_form"):
759
+ col1, col2 = st.columns(2)
760
+ with col1:
761
+ new_labor_name = st.text_input("نوع العمالة")
762
+ new_labor_unit = st.text_input("الوحدة", value="يوم")
763
+ with col2:
764
+ new_labor_quantity = st.number_input("عدد الأيام", min_value=0.0, step=0.5)
765
+ new_labor_price = st.number_input("الأجر اليومي (ريال)", min_value=0.0, step=10.0)
766
+
767
+ submitted = st.form_submit_button("إضافة العمالة")
768
+ if submitted and new_labor_name:
769
+ new_labor = {
770
+ "id": f"labor_{len(current_item['labor']) + 1}",
771
+ "name": new_labor_name,
772
+ "unit": new_labor_unit,
773
+ "quantity": float(new_labor_quantity),
774
+ "unit_price": float(new_labor_price),
775
+ "total_price": float(new_labor_quantity) * float(new_labor_price)
776
+ }
777
+ current_item["labor"].append(new_labor)
778
+ st.session_state.item_analysis_edited = True
779
+ st.success("تمت إضافة العمالة بنجاح")
780
+ st.rerun()
781
+
782
+ # عرض جدول العمالة
783
+ if current_item["labor"]:
784
+ # تحويل البيانات إلى DataFrame
785
+ labor_data = []
786
+ for i, labor in enumerate(current_item["labor"]):
787
+ labor_data.append({
788
+ "م": i + 1,
789
+ "نوع العمالة": labor["name"],
790
+ "الوحدة": labor["unit"],
791
+ "عدد الأيام": labor["quantity"],
792
+ "الأجر اليومي (ريال)": labor["unit_price"],
793
+ "الإجمالي (ريال)": labor["total_price"]
794
+ })
795
+
796
+ labor_df = pd.DataFrame(labor_data)
797
+
798
+ # إضافة صف المجموع
799
+ total_row = pd.DataFrame([{
800
+ "م": "",
801
+ "نوع العمالة": "المجموع",
802
+ "الوحدة": "",
803
+ "عدد الأيام": "",
804
+ "الأجر اليومي (ريال)": "",
805
+ "الإجمالي (ريال)": labor_total
806
+ }])
807
+
808
+ # استخدام pd.concat بدلاً من append
809
+ labor_df = pd.concat([labor_df, total_row], ignore_index=True)
810
+
811
+ # عرض الجدول القابل للتعديل
812
+ edited_labor_df = st.data_editor(
813
+ labor_df,
814
+ column_config={
815
+ "م": st.column_config.NumberColumn("م", width="small"),
816
+ "نوع العمالة": st.column_config.TextColumn("نوع العمالة"),
817
+ "الوحدة": st.column_config.TextColumn("الوحدة", width="small"),
818
+ "عدد الأيام": st.column_config.NumberColumn("عدد الأيام", format="%.1f", width="small"),
819
+ "الأجر اليومي (ريال)": st.column_config.NumberColumn("الأجر اليومي (ريال)", format="%.2f"),
820
+ "الإجمالي (ريال)": st.column_config.NumberColumn("الإجمالي (ريال)", format="%.2f")
821
+ },
822
+ hide_index=True,
823
+ key="labor_table",
824
+ on_change=self._update_total_price,
825
+ disabled=["م", "نوع العمالة", "الوحدة", "الإجمالي (ريال)"]
826
+ )
827
+
828
+ # تحديث البيانات بعد التعديل
829
+ for i, labor in enumerate(current_item["labor"]):
830
+ if i < len(edited_labor_df) - 1: # تجاهل صف المجموع
831
+ labor["quantity"] = edited_labor_df.iloc[i]["عدد الأيام"]
832
+ labor["unit_price"] = edited_labor_df.iloc[i]["الأجر اليومي (ريال)"]
833
+ labor["total_price"] = labor["quantity"] * labor["unit_price"]
834
+
835
+ # معالجة أزرار التعديل والحذف
836
+ for i in range(len(current_item["labor"])):
837
+ col1, col2 = st.columns([1, 1])
838
+ with col1:
839
+ if st.button(f"تعديل العمالة {i+1}", key=f"edit_labor_btn_{i}"):
840
+ st.session_state[f"edit_labor_{i}"] = True
841
+
842
+ with col2:
843
+ if st.button(f"حذف العمالة {i+1}", key=f"delete_labor_btn_{i}"):
844
+ st.session_state[f"delete_labor_{i}"] = True
845
+
846
+ # نموذج التعديل
847
+ if st.session_state.get(f"edit_labor_{i}", False):
848
+ with st.form(f"edit_labor_form_{i}"):
849
+ labor = current_item["labor"][i]
850
+ col1, col2 = st.columns(2)
851
+ with col1:
852
+ labor_name = st.text_input("نوع العمالة", value=labor["name"])
853
+ labor_unit = st.text_input("الوحدة", value=labor["unit"])
854
+ with col2:
855
+ labor_quantity = st.number_input("عدد الأيام", min_value=0.0, step=0.5, value=labor["quantity"])
856
+ labor_price = st.number_input("الأجر اليومي (ريال)", min_value=0.0, step=10.0, value=labor["unit_price"])
857
+
858
+ col1, col2 = st.columns(2)
859
+ with col1:
860
+ if st.form_submit_button("حفظ التعديلات"):
861
+ labor["name"] = labor_name
862
+ labor["unit"] = labor_unit
863
+ labor["quantity"] = float(labor_quantity)
864
+ labor["unit_price"] = float(labor_price)
865
+ labor["total_price"] = labor["quantity"] * labor["unit_price"]
866
+ st.session_state.item_analysis_edited = True
867
+ st.session_state[f"edit_labor_{i}"] = False
868
+ st.rerun()
869
+ with col2:
870
+ if st.form_submit_button("إلغاء"):
871
+ st.session_state[f"edit_labor_{i}"] = False
872
+ st.rerun()
873
+
874
+ # تأكيد الحذف
875
+ if st.session_state.get(f"delete_labor_{i}", False):
876
+ st.warning(f"هل أنت متأكد من حذف العمالة: {current_item['labor'][i]['name']}؟")
877
+ col1, col2 = st.columns(2)
878
+ with col1:
879
+ if st.button("نعم، حذف", key=f"confirm_delete_labor_{i}"):
880
+ current_item["labor"].pop(i)
881
+ st.session_state.item_analysis_edited = True
882
+ st.session_state[f"delete_labor_{i}"] = False
883
+ st.rerun()
884
+ with col2:
885
+ if st.button("إلغاء", key=f"cancel_delete_labor_{i}"):
886
+ st.session_state[f"delete_labor_{i}"] = False
887
+ st.rerun()
888
+
889
+ # عرض المجموع الكلي للعمالة
890
+ st.markdown(f"<div class='highlight-total'>إجمالي تكلفة العمالة: <span class='total-value'>{labor_total:,.2f}</span> ريال</div>", unsafe_allow_html=True)
891
+ else:
892
+ st.info("لا توجد عمالة مضافة لهذا البند")
893
+
894
+ # تحليل المعدات
895
+ with tab3:
896
+ st.subheader("تحليل المعدات")
897
+
898
+ # إضافة معدات جديدة
899
+ with st.expander("إضافة معدات جديدة", expanded=True):
900
+ with st.form("add_equipment_form"):
901
+ col1, col2 = st.columns(2)
902
+ with col1:
903
+ new_equipment_name = st.text_input("نوع المعدات")
904
+ new_equipment_unit = st.text_input("الوحدة", value="يوم")
905
+ with col2:
906
+ new_equipment_quantity = st.number_input("عدد الأيام", min_value=0.0, step=0.5, key="new_equipment_quantity")
907
+ new_equipment_price = st.number_input("التكلفة اليومية (ريال)", min_value=0.0, step=10.0, key="new_equipment_price")
908
+
909
+ submitted = st.form_submit_button("إضافة المعدات")
910
+ if submitted and new_equipment_name:
911
+ new_equipment = {
912
+ "id": f"equipment_{len(current_item['equipment']) + 1}",
913
+ "name": new_equipment_name,
914
+ "unit": new_equipment_unit,
915
+ "quantity": float(new_equipment_quantity),
916
+ "unit_price": float(new_equipment_price),
917
+ "total_price": float(new_equipment_quantity) * float(new_equipment_price)
918
+ }
919
+ current_item["equipment"].append(new_equipment)
920
+ st.session_state.item_analysis_edited = True
921
+ st.success("تمت إضافة المعدات بنجاح")
922
+ st.rerun()
923
+
924
+ # عرض جدول المعدات
925
+ if current_item["equipment"]:
926
+ # تحويل البيانات إلى DataFrame
927
+ equipment_data = []
928
+ for i, equipment in enumerate(current_item["equipment"]):
929
+ equipment_data.append({
930
+ "م": i + 1,
931
+ "نوع المعدات": equipment["name"],
932
+ "الوحدة": equipment["unit"],
933
+ "عدد الأيام": equipment["quantity"],
934
+ "التكلفة اليومية (ريال)": equipment["unit_price"],
935
+ "الإجمالي (ريال)": equipment["total_price"]
936
+ })
937
+
938
+ equipment_df = pd.DataFrame(equipment_data)
939
+
940
+ # إضافة صف المجموع
941
+ total_row = pd.DataFrame([{
942
+ "م": "",
943
+ "نوع المعدات": "المجموع",
944
+ "الوحدة": "",
945
+ "عدد الأيام": "",
946
+ "التكلفة اليومية (ريال)": "",
947
+ "الإجمالي (ريال)": equipment_total
948
+ }])
949
+
950
+ # استخدام pd.concat بدلاً من append
951
+ equipment_df = pd.concat([equipment_df, total_row], ignore_index=True)
952
+
953
+ # عرض الجدول القابل للتعديل
954
+ edited_equipment_df = st.data_editor(
955
+ equipment_df,
956
+ column_config={
957
+ "م": st.column_config.NumberColumn("م", width="small"),
958
+ "نوع المعدات": st.column_config.TextColumn("نوع المعدات"),
959
+ "الوحدة": st.column_config.TextColumn("الوحدة", width="small"),
960
+ "عدد الأيام": st.column_config.NumberColumn("عدد الأيام", format="%.1f", width="small"),
961
+ "التكلفة اليومية (ريال)": st.column_config.NumberColumn("التكلفة اليومية (ريال)", format="%.2f"),
962
+ "الإجمالي (ريال)": st.column_config.NumberColumn("الإجمالي (ريال)", format="%.2f")
963
+ },
964
+ hide_index=True,
965
+ key="equipment_table",
966
+ on_change=self._update_total_price,
967
+ disabled=["م", "نوع المعدات", "الوحدة", "الإجمالي (ريال)"]
968
+ )
969
+
970
+ # تحديث البيانات بعد التعديل
971
+ for i, equipment in enumerate(current_item["equipment"]):
972
+ if i < len(edited_equipment_df) - 1: # تجاهل صف المجموع
973
+ equipment["quantity"] = edited_equipment_df.iloc[i]["عدد الأيام"]
974
+ equipment["unit_price"] = edited_equipment_df.iloc[i]["التكلفة اليومية (ريال)"]
975
+ equipment["total_price"] = equipment["quantity"] * equipment["unit_price"]
976
+
977
+ # معالجة أزرار التعديل والحذف
978
+ for i in range(len(current_item["equipment"])):
979
+ col1, col2 = st.columns([1, 1])
980
+ with col1:
981
+ if st.button(f"تعديل المعدات {i+1}", key=f"edit_equipment_btn_{i}"):
982
+ st.session_state[f"edit_equipment_{i}"] = True
983
+
984
+ with col2:
985
+ if st.button(f"حذف المعدات {i+1}", key=f"delete_equipment_btn_{i}"):
986
+ st.session_state[f"delete_equipment_{i}"] = True
987
+
988
+ # نموذج التعديل
989
+ if st.session_state.get(f"edit_equipment_{i}", False):
990
+ with st.form(f"edit_equipment_form_{i}"):
991
+ equipment = current_item["equipment"][i]
992
+ col1, col2 = st.columns(2)
993
+ with col1:
994
+ equipment_name = st.text_input("نوع المعدات", value=equipment["name"])
995
+ equipment_unit = st.text_input("الوحدة", value=equipment["unit"])
996
+ with col2:
997
+ equipment_quantity = st.number_input("عدد الأيام", min_value=0.0, step=0.5, value=equipment["quantity"])
998
+ equipment_price = st.number_input("التكلفة اليومية (ريال)", min_value=0.0, step=10.0, value=equipment["unit_price"])
999
+
1000
+ col1, col2 = st.columns(2)
1001
+ with col1:
1002
+ if st.form_submit_button("حفظ التعديلات"):
1003
+ equipment["name"] = equipment_name
1004
+ equipment["unit"] = equipment_unit
1005
+ equipment["quantity"] = float(equipment_quantity)
1006
+ equipment["unit_price"] = float(equipment_price)
1007
+ equipment["total_price"] = equipment["quantity"] * equipment["unit_price"]
1008
+ st.session_state.item_analysis_edited = True
1009
+ st.session_state[f"edit_equipment_{i}"] = False
1010
+ st.rerun()
1011
+ with col2:
1012
+ if st.form_submit_button("إلغاء"):
1013
+ st.session_state[f"edit_equipment_{i}"] = False
1014
+ st.rerun()
1015
+
1016
+ # تأكيد الحذف
1017
+ if st.session_state.get(f"delete_equipment_{i}", False):
1018
+ st.warning(f"هل أنت متأكد من حذف المعدات: {current_item['equipment'][i]['name']}؟")
1019
+ col1, col2 = st.columns(2)
1020
+ with col1:
1021
+ if st.button("نعم، حذف", key=f"confirm_delete_equipment_{i}"):
1022
+ current_item["equipment"].pop(i)
1023
+ st.session_state.item_analysis_edited = True
1024
+ st.session_state[f"delete_equipment_{i}"] = False
1025
+ st.rerun()
1026
+ with col2:
1027
+ if st.button("إلغاء", key=f"cancel_delete_equipment_{i}"):
1028
+ st.session_state[f"delete_equipment_{i}"] = False
1029
+ st.rerun()
1030
+
1031
+ # عرض المجموع الكلي للمعدات
1032
+ st.markdown(f"<div class='highlight-total'>إجمالي تكلفة المعدات: <span class='total-value'>{equipment_total:,.2f}</span> ريال</div>", unsafe_allow_html=True)
1033
+ else:
1034
+ st.info("لا توجد معدات مضافة لهذا البند")
1035
+
1036
+ # تحليل المقاولين من الباطن
1037
+ with tab4:
1038
+ st.subheader("تحليل المقاولين من الباطن")
1039
+
1040
+ # إضافة مقاول جديد
1041
+ with st.expander("إضافة مقاول من الباطن", expanded=True):
1042
+ with st.form("add_subcontractor_form"):
1043
+ col1, col2 = st.columns(2)
1044
+ with col1:
1045
+ new_sub_name = st.text_input("اسم المقاول")
1046
+ new_sub_work = st.text_input("نوع العمل")
1047
+ with col2:
1048
+ new_sub_price = st.number_input("التكلفة الإجمالية (ريال)", min_value=0.0, step=100.0)
1049
+
1050
+ submitted = st.form_submit_button("إضافة مقاول")
1051
+ if submitted and new_sub_name and new_sub_work:
1052
+ new_sub = {
1053
+ "id": f"sub_{len(current_item['subcontractors']) + 1}",
1054
+ "name": new_sub_name,
1055
+ "work": new_sub_work,
1056
+ "quantity": 1,
1057
+ "unit_price": float(new_sub_price),
1058
+ "total_price": float(new_sub_price)
1059
+ }
1060
+ current_item["subcontractors"].append(new_sub)
1061
+ st.session_state.item_analysis_edited = True
1062
+ st.success("تمت إضافة المقاول بنجاح")
1063
+ st.rerun()
1064
+
1065
+ # عرض جدول المقاولين
1066
+ if current_item["subcontractors"]:
1067
+ # تحويل البيانات إلى DataFrame
1068
+ subs_data = []
1069
+ for i, sub in enumerate(current_item["subcontractors"]):
1070
+ subs_data.append({
1071
+ "م": i + 1,
1072
+ "اسم المقاول": sub["name"],
1073
+ "نوع العمل": sub["work"],
1074
+ "التكلفة الإجمالية (ريال)": sub["total_price"]
1075
+ })
1076
+
1077
+ subs_df = pd.DataFrame(subs_data)
1078
+
1079
+ # إضافة صف المجموع
1080
+ total_row = pd.DataFrame([{
1081
+ "م": "",
1082
+ "اسم المقاول": "المجموع",
1083
+ "نوع العمل": "",
1084
+ "التكلفة الإجمالية (ريال)": subcontractors_total
1085
+ }])
1086
+
1087
+ # استخدام pd.concat بدلاً من append
1088
+ subs_df = pd.concat([subs_df, total_row], ignore_index=True)
1089
+
1090
+ # عرض الجدول القابل للتعديل
1091
+ edited_subs_df = st.data_editor(
1092
+ subs_df,
1093
+ column_config={
1094
+ "م": st.column_config.NumberColumn("م", width="small"),
1095
+ "اسم المقاول": st.column_config.TextColumn("اسم المقاول"),
1096
+ "نوع العمل": st.column_config.TextColumn("نوع العمل"),
1097
+ "التكلفة الإجمالية (ريال)": st.column_config.NumberColumn("التكلفة الإجمالية (ريال)", format="%.2f")
1098
+ },
1099
+ hide_index=True,
1100
+ key="subs_table",
1101
+ on_change=self._update_total_price,
1102
+ disabled=["م", "اسم المقاول", "نوع العمل"]
1103
+ )
1104
+
1105
+ # تحديث البيانات بعد التعديل
1106
+ for i, sub in enumerate(current_item["subcontractors"]):
1107
+ if i < len(edited_subs_df) - 1: # تجاهل صف المجموع
1108
+ sub["total_price"] = edited_subs_df.iloc[i]["التكلفة الإجمالية (ريال)"]
1109
+ sub["unit_price"] = sub["total_price"] # للمقاولين، سعر الوحدة = السعر الإجمالي
1110
+
1111
+ # معالجة أزرار التعديل والحذف
1112
+ for i in range(len(current_item["subcontractors"])):
1113
+ col1, col2 = st.columns([1, 1])
1114
+ with col1:
1115
+ if st.button(f"تعديل المقاول {i+1}", key=f"edit_sub_btn_{i}"):
1116
+ st.session_state[f"edit_sub_{i}"] = True
1117
+
1118
+ with col2:
1119
+ if st.button(f"حذف المقاول {i+1}", key=f"delete_sub_btn_{i}"):
1120
+ st.session_state[f"delete_sub_{i}"] = True
1121
+
1122
+ # نموذج التعديل
1123
+ if st.session_state.get(f"edit_sub_{i}", False):
1124
+ with st.form(f"edit_sub_form_{i}"):
1125
+ sub = current_item["subcontractors"][i]
1126
+ col1, col2 = st.columns(2)
1127
+ with col1:
1128
+ sub_name = st.text_input("اسم المقاول", value=sub["name"])
1129
+ sub_work = st.text_input("نوع العمل", value=sub["work"])
1130
+ with col2:
1131
+ sub_price = st.number_input("التكلفة الإجمالية (ريال)", min_value=0.0, step=100.0, value=sub["total_price"])
1132
+
1133
+ col1, col2 = st.columns(2)
1134
+ with col1:
1135
+ if st.form_submit_button("حفظ التعديلات"):
1136
+ sub["name"] = sub_name
1137
+ sub["work"] = sub_work
1138
+ sub["total_price"] = float(sub_price)
1139
+ sub["unit_price"] = sub["total_price"]
1140
+ st.session_state.item_analysis_edited = True
1141
+ st.session_state[f"edit_sub_{i}"] = False
1142
+ st.rerun()
1143
+ with col2:
1144
+ if st.form_submit_button("إلغاء"):
1145
+ st.session_state[f"edit_sub_{i}"] = False
1146
+ st.rerun()
1147
+
1148
+ # تأكيد الحذف
1149
+ if st.session_state.get(f"delete_sub_{i}", False):
1150
+ st.warning(f"هل أنت متأكد من حذف المقاول: {current_item['subcontractors'][i]['name']}؟")
1151
+ col1, col2 = st.columns(2)
1152
+ with col1:
1153
+ if st.button("نعم، حذف", key=f"confirm_delete_sub_{i}"):
1154
+ current_item["subcontractors"].pop(i)
1155
+ st.session_state.item_analysis_edited = True
1156
+ st.session_state[f"delete_sub_{i}"] = False
1157
+ st.rerun()
1158
+ with col2:
1159
+ if st.button("إلغاء", key=f"cancel_delete_sub_{i}"):
1160
+ st.session_state[f"delete_sub_{i}"] = False
1161
+ st.rerun()
1162
+
1163
+ # عرض المجموع الكلي للمقاولين
1164
+ st.markdown(f"<div class='highlight-total'>إجمالي تكلفة المقاولين من الباطن: <span class='total-value'>{subcontractors_total:,.2f}</span> ريال</div>", unsafe_allow_html=True)
1165
+ else:
1166
+ st.info("لا يوجد مقاولون من الباطن مضافون لهذا البند")
1167
+
1168
+ # ملخص التكلفة - عرض خارج التبويبات
1169
+ st.markdown("<hr>", unsafe_allow_html=True)
1170
+ st.subheader("ملخص التكلفة الإجمالية للبند")
1171
+
1172
+ # تنسيق ملخص التكلفة
1173
+ col1, col2 = st.columns([2, 3])
1174
+
1175
+ with col1:
1176
+ # عرض ملخص التكلفة كجدول
1177
+ summary_data = {
1178
+ "عنصر التكلفة": ["تكلفة المواد", "تكلفة العمالة", "تكلفة المعدات", "تكلفة المقاولين من الباطن", "إجمالي التكلفة المباشرة", "المصاريف العمومية والربح", "إجمالي سعر البند"],
1179
+ "القيمة (ريال)": [materials_total, labor_total, equipment_total, subcontractors_total, direct_cost, overhead_profit, current_item["total_price"]]
1180
+ }
1181
+
1182
+ summary_df = pd.DataFrame(summary_data)
1183
+
1184
+ # تنسيق الجدول
1185
+ st.dataframe(
1186
+ summary_df,
1187
+ column_config={
1188
+ "عنصر التكلفة": st.column_config.TextColumn("عنصر التكلفة"),
1189
+ "القيمة (ريال)": st.column_config.NumberColumn("القيمة (ريال)", format="%.2f")
1190
+ },
1191
+ hide_index=True
1192
+ )
1193
+
1194
+ with col2:
1195
+ # رسم بياني للتكاليف
1196
+ if direct_cost > 0:
1197
+ # إعداد البيانات للرسم البياني
1198
+ labels = ['المواد', 'العمالة', 'المعدات', 'المقاولين', 'الربح']
1199
+ sizes = [materials_total, labor_total, equipment_total, subcontractors_total, overhead_profit]
1200
+ colors = ['#ff9999', '#66b3ff', '#99ff99', '#ffcc99', '#c2c2f0']
1201
+
1202
+ # رسم الرسم البياني
1203
+ fig, ax = plt.subplots(figsize=(8, 6))
1204
+ wedges, texts, autotexts = ax.pie(
1205
+ sizes,
1206
+ labels=[get_display(arabic_reshaper.reshape(label)) for label in labels],
1207
+ autopct='%1.1f%%',
1208
+ startangle=90,
1209
+ colors=colors
1210
+ )
1211
+
1212
+ # تنسيق النص
1213
+ for text in texts:
1214
+ text.set_fontsize(12)
1215
+ for autotext in autotexts:
1216
+ autotext.set_fontsize(10)
1217
+ autotext.set_color('white')
1218
+
1219
+ ax.axis('equal')
1220
+ plt.title(get_display(arabic_reshaper.reshape('توزيع تكاليف البند')), fontsize=16)
1221
+
1222
+ st.pyplot(fig)
1223
+
1224
+ # أزرار الإجراءات
1225
+ col1, col2 = st.columns(2)
1226
+ with col1:
1227
+ if st.button("حفظ التحليل والعودة لجدول الكميات", type="primary", use_container_width=True):
1228
+ # تحديث السعر الإجمالي للبند بناءً على التكاليف المباشرة
1229
+ if current_item["quantity"] > 0:
1230
+ unit_cost = direct_cost / current_item["quantity"]
1231
+ current_item["unit_price"] = unit_cost
1232
+ current_item["total_price"] = current_item["quantity"] * unit_cost
1233
+
1234
+ st.session_state.current_tab = "boq"
1235
+ st.session_state.item_analysis_edited = False
1236
+ st.success("تم حفظ التحليل بنجاح")
1237
+ st.rerun()