simikkk commited on
Commit
81f31cc
·
verified ·
1 Parent(s): b166784

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +343 -0
app.py ADDED
@@ -0,0 +1,343 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ import stripe
3
+ import pandas as pd
4
+ import time
5
+ import random
6
+ import traceback
7
+
8
+ # -----------------------------------------------------------------------------
9
+ # KONFIGURACE A INICIALIZACE STAVU
10
+ # -----------------------------------------------------------------------------
11
+ VERSION = "1.0.0-PRODUKČNÍ"
12
+
13
+ st.set_page_config(
14
+ page_title="TradeFlow AI — Systém pro řemeslníky",
15
+ page_icon="🏆",
16
+ layout="wide",
17
+ initial_sidebar_state="expanded"
18
+ )
19
+
20
+ # Bezpečné založení session state parametrů pro uchování dat při klikání v UI
21
+ if "dev_mode" not in st.session_state:
22
+ st.session_state.dev_mode = True
23
+ if "user_subscribed" not in st.session_state:
24
+ st.session_state.user_subscribed = False
25
+ if "quotes_count" not in st.session_state:
26
+ st.session_state.quotes_count = 1
27
+ if "stripe_mode" not in st.session_state:
28
+ st.session_state.stripe_mode = "Sandbox (Simulace)"
29
+ if "stripe_api_key" not in st.session_state:
30
+ st.session_state.stripe_api_key = ""
31
+ if "error_logs" not in st.session_state:
32
+ st.session_state.error_logs = []
33
+
34
+ if "db_customers" not in st.session_state:
35
+ st.session_state.db_customers = [
36
+ {"id": 1, "jmeno": "Jan Novák", "firma": "Stavebniny s.r.o.", "telefon": "+420 777 123 456"},
37
+ {"id": 2, "jmeno": "Marie Podlahová", "firma": "Bytové družstvo", "telefon": "+420 602 987 654"}
38
+ ]
39
+ if "db_quotes" not in st.session_state:
40
+ st.session_state.db_quotes = []
41
+ if "db_invoices" not in st.session_state:
42
+ st.session_state.db_invoices = []
43
+
44
+ def log_system_error(err_msg, trace_str):
45
+ st.session_state.error_logs.append({
46
+ "time": time.strftime("%H:%M:%S"),
47
+ "msg": err_msg,
48
+ "trace": trace_str
49
+ })
50
+
51
+ # -----------------------------------------------------------------------------
52
+ # BACKEND: STRIPE PLATEBNÍ PROCESOR
53
+ # -----------------------------------------------------------------------------
54
+ def execute_stripe_checkout(card_number, exp_month, exp_year, cvc, amount=29.00):
55
+ try:
56
+ if st.session_state.stripe_mode == "Stripe Live API":
57
+ if not st.session_state.stripe_api_key:
58
+ raise ValueError("Chybí Stripe Secret API klíč pro Live režim.")
59
+ stripe.api_key = st.session_state.stripe_api_key
60
+
61
+ token = stripe.Token.create(
62
+ card={"number": card_number, "exp_month": int(exp_month), "exp_year": int(exp_year), "cvc": cvc}
63
+ )
64
+ charge = stripe.Charge.create(
65
+ amount=int(amount * 100), currency="eur", source=token.id, description="TradeFlow AI Pro Tarif"
66
+ )
67
+ if charge.status == "succeeded":
68
+ st.session_state.user_subscribed = True
69
+ return True, "Platba úspěšně zpracována přes Stripe Live API!"
70
+ return False, f"Stripe zamítl platbu: {charge.status}"
71
+ else:
72
+ # Sandbox testovací režim
73
+ time.sleep(1.0)
74
+ if not card_number or len(card_number.replace(" ", "")) < 16:
75
+ return False, "Chyba karty: Neplatná délka čísla karty."
76
+ if cvc == "000":
77
+ return False, "Karta zamítnuta (Sandbox: Kód 51 - Nedostatečný zůstatek)."
78
+ st.session_state.user_subscribed = True
79
+ return True, "Sandbox úspěch: Licence aktivována!"
80
+ except Exception as e:
81
+ err_msg = f"Selhání Stripe subsystému: {str(e)}"
82
+ log_system_error(err_msg, traceback.format_exc())
83
+ return False, err_msg
84
+
85
+ # -----------------------------------------------------------------------------
86
+ # BACKEND: AI CORE & LOGIKA GENERÁTORU
87
+ # -----------------------------------------------------------------------------
88
+ def mock_ai_generate_quote(customer_name, text_zadani, hodinova_sazba):
89
+ try:
90
+ if st.session_state.quotes_count >= 3 and not st.session_state.user_subscribed:
91
+ raise PermissionError("Překročen limit Free tieru (max 3 nabídky).")
92
+
93
+ time.sleep(1.2)
94
+
95
+ # Algoritmus pro analýzu klíčových slov v zadání
96
+ odhad_hodin = 8
97
+ if "koupeln" in text_zadani.lower() or "rekonstrukce" in text_zadani.lower():
98
+ odhad_hodin = 24
99
+ elif "oprava" in text_zadani.lower():
100
+ odhad_hodin = 4
101
+
102
+ cena_prace = odhad_hodin * hodinova_sazba
103
+ cena_materialu = round(cena_prace * 0.65, 2)
104
+ celkova_cena = cena_prace + cena_materialu
105
+
106
+ quote_id = len(st.session_state.db_quotes) + 1
107
+ quote_data = {
108
+ "id": quote_id,
109
+ "zakaznik": customer_name,
110
+ "popis": text_zadani,
111
+ "hodin": odhad_hodin,
112
+ "cena_prace": cena_prace,
113
+ "cena_mat": cena_materialu,
114
+ "celkem": celkova_cena,
115
+ "datum": time.strftime("%Y-%m-%d")
116
+ }
117
+
118
+ st.session_state.db_quotes.append(quote_data)
119
+ st.session_state.quotes_count += 1
120
+ return quote_data
121
+ except Exception as e:
122
+ log_system_error(f"Chyba AI generátoru: {str(e)}", traceback.format_exc())
123
+ return None
124
+
125
+ # -----------------------------------------------------------------------------
126
+ # UI STRUKTURA (STREAMLIT INTERFACE)
127
+ # -----------------------------------------------------------------------------
128
+ st.title("🏆 TradeFlow AI")
129
+ st.caption(f"Chytrý operační systém pro novou generaci řemeslníků a živnostníků. Běží na verzi: {VERSION}")
130
+
131
+ # Globální chybová konzole viditelná ihned při selhání kódu
132
+ if st.session_state.error_logs:
133
+ with st.expander("⚠️ SYSTÉMOVÝ DEBBUG LOG (Přepošli vývojáři k opravě)", expanded=True):
134
+ for log in st.session_state.error_logs:
135
+ st.error(f"**[{log['time']}]** {log['msg']}")
136
+ st.code(log['trace'], language="python")
137
+ if st.button("Vyčistit systémové logy"):
138
+ st.session_state.error_logs = []
139
+ st.rerun()
140
+
141
+ # BOČNÍ PANEL: SPRÁVA CHOVÁNÍ APLIKACE A EMULACE
142
+ with st.sidebar:
143
+ st.header("🛠️ Dev Panel pro Testování")
144
+ st.session_state.dev_mode = st.checkbox("Aktivovat testovací nástroje", value=st.session_state.dev_mode)
145
+
146
+ if st.session_state.dev_mode:
147
+ st.markdown("---")
148
+ st.subheader("Stav účtu a Limity")
149
+ st.session_state.user_subscribed = st.toggle("Uživatel má zakoupen PRO tarif", value=st.session_state.user_subscribed)
150
+ st.session_state.quotes_count = st.number_input("Počet vygenerovaných nabídek v tomto měsíci", min_value=0, max_value=10, value=st.session_state.quotes_count)
151
+
152
+ st.markdown("---")
153
+ st.subheader("Stripe Gateway Config")
154
+ st.session_state.stripe_mode = st.radio("Režim Stripe", ["Sandbox (Simulace)", "Stripe Live API"])
155
+ if st.session_state.stripe_mode == "Stripe Live API":
156
+ st.session_state.stripe_api_key = st.text_input("Vlož sk_live_...", type="password", value=st.session_state.stripe_api_key)
157
+
158
+ st.markdown("---")
159
+ if st.button("Resetovat aplikaci do výchozího stavu"):
160
+ st.session_state.clear()
161
+ st.rerun()
162
+
163
+ # Kontrola překročení limitů bez předplatného
164
+ is_locked = st.session_state.quotes_count >= 3 and not st.session_state.user_subscribed
165
+
166
+ # Rozdělení rozhraní na logické sekce pomocí záložek
167
+ tab_dashboard, tab_generator, tab_crm, tab_fakturace = st.tabs([
168
+ "📈 Přehled & Metriky",
169
+ "📝 AI Generátor Nabídek",
170
+ "👥 Adresář (CRM Lite)",
171
+ "🧾 Fakturace jedním klikem"
172
+ ])
173
+
174
+ # ZÁLOŽKA 1: AKTUÁLNÍ PODNIKATELSKÉ VÝSLEDKY
175
+ with tab_dashboard:
176
+ st.header("Podnikání pod kontrolou")
177
+ try:
178
+ total_revenue = sum(inv["castka"] for inv in st.session_state.db_invoices if inv["stav"] == "Zaplaceno")
179
+ pending_revenue = sum(inv["castka"] for inv in st.session_state.db_invoices if inv["stav"] == "Odesláno")
180
+ potencial = sum(q["celkem"] for q in st.session_state.db_quotes)
181
+
182
+ c1, c2, c3, c4 = st.columns(4)
183
+ c1.metric("Skutečný zisk (Zaplaceno)", f"{total_revenue:,.2f} EUR")
184
+ c2.metric("Čekající peníze (Ve fakturách)", f"{pending_revenue:,.2f} EUR")
185
+ c3.metric("Otevřené AI Nabídky (Potenciál)", f"{potencial:,.2f} EUR")
186
+ c4.metric("Využití Free limitu", f"{st.session_state.quotes_count} / 3", delta="⚠️ LIMIT DOSAŽEN" if is_locked else "V normě")
187
+
188
+ st.subheader("Přehled posledních aktivit")
189
+ if not st.session_state.db_quotes:
190
+ st.info("Zatím jste nevygenerovali žádnou cenovou nabídku. Začněte v záložce AI Generátor.")
191
+ else:
192
+ df_overview = pd.DataFrame(st.session_state.db_quotes)
193
+ st.dataframe(df_overview[["datum", "zakaznik", "popis", "celkem"]], use_container_width=True)
194
+ except Exception as e:
195
+ log_system_error(f"Chyba vykreslení dashboardu: {str(e)}", traceback.format_exc())
196
+
197
+ # ZÁLOŽKA 2: GENEROVÁNÍ NABÍDEK A STRIPE PAYWALL
198
+ with tab_generator:
199
+ st.header("Vytvoření nabídky pomocí AI")
200
+
201
+ if is_locked:
202
+ st.error("🔒 Dosáhli jste limitu 3 bezplatných nabídek pro tento měsíc. Pro neomezené generování aktivujte tarif PRO níže.")
203
+
204
+ col_pay_form, col_pay_info = st.columns([1.5, 1])
205
+ with col_pay_form:
206
+ with st.form("stripe_paywall_form"):
207
+ st.subheader("Aktivace TradeFlow PRO")
208
+ st.write("**Cena:** €29.00 / měsíčně (Bez závazků)")
209
+ cc_num = st.text_input("Číslo karty", value="4242 4242 4242 4242")
210
+ cc_m = st.selectbox("Měsíc", [f"{i:02d}" for i in range(1, 13)], index=5)
211
+ cc_y = st.selectbox("Rok", [str(i) for i in range(2026, 2035)], index=0)
212
+ cc_cvc = st.text_input("CVC", value="123")
213
+
214
+ pay_submit = st.form_submit_button("Bezpečně zaplatit €29.00", use_container_width=True)
215
+ if pay_submit:
216
+ with st.spinner("Zpracovávám platbu..."):
217
+ success, msg = execute_stripe_checkout(cc_num, cc_m, cc_y, cc_cvc)
218
+ if success:
219
+ st.success(msg)
220
+ time.sleep(0.5)
221
+ st.rerun()
222
+ else:
223
+ st.error(msg)
224
+ with col_pay_info:
225
+ st.markdown("""
226
+ ### Co získáte s tarifem PRO:
227
+ * **Neomezené** AI generování komplexních kalkulací.
228
+ * **Fakturace 1 kliknutím** přímo z vygenerované nabídky.
229
+ * Možnost stahovat dokumenty pro zákazníky v čistém formátu.
230
+ """)
231
+ else:
232
+ try:
233
+ with st.form("ai_generator_form"):
234
+ cust_names = [c["jmeno"] for c in st.session_state.db_customers]
235
+ selected_cust = st.selectbox("Vyberte zákazníka z adresáře", cust_names)
236
+ input_task = st.text_area("Popište práci vlastními slovy",
237
+ value="Kompletní rekonstrukce koupelny v panelovém bytě. Vybourání starého jádra, nové obklady, montáž sprchového koutu, instalace baterie a napojení pračky.")
238
+ hourly_rate = st.number_input("Vaše hodinová sazba za práci (EUR/hod)", min_value=10, max_value=250, value=35)
239
+
240
+ submit_ai = st.form_submit_button("⚡ Vygenerovat profesionální rozpočet pomocí AI")
241
+
242
+ if submit_ai:
243
+ with st.spinner("AI TradeFlow přepočítává normohodiny a sestavuje položkový rozpočet..."):
244
+ new_quote = mock_ai_generate_quote(selected_cust, input_task, hourly_rate)
245
+ if new_quote:
246
+ st.success("Cenová nabídka byla úspěšně vygenerována a uložena!")
247
+ st.markdown(f"### 📄 CENOVÁ NABÍDKA PRO: {new_quote['zakaznik'].upper()}")
248
+ st.markdown(f"**Datum vystavení:** {new_quote['datum']} | **Status:** Návrh")
249
+ st.markdown("---")
250
+ st.write(f"**Specifikace díla:** {new_quote['popis']}")
251
+
252
+ df_items = pd.DataFrame([
253
+ {"Položka": "Odborné řemeslné práce", "Množství": f"{new_quote['hodin']} hod", "Sazba": f"{hourly_rate} EUR/hod", "Cena celkem": f"{new_quote['cena_prace']:.2f} EUR"},
254
+ {"Položka": "Spotřební a stavební materiál", "Množství": "Paušál", "Sazba": "Dle projektu", "Cena celkem": f"{new_quote['cena_mat']:.2f} EUR"}
255
+ ])
256
+ st.table(df_items)
257
+ st.markdown(f"### **KONEČNÁ CENA K ÚHRADĚ: {new_quote['celkem']:.2f} EUR**")
258
+ except Exception as e:
259
+ log_system_error(f"Chyba formuláře generátoru: {str(e)}", traceback.format_exc())
260
+
261
+ # ZÁLOŽKA 3: ADRESÁŘ ZÁKAZNÍKŮ (CRM LITE)
262
+ with tab_crm:
263
+ st.header("Adresář zákazníků")
264
+ try:
265
+ col_crm_list, col_crm_add = st.columns([2, 1])
266
+
267
+ with col_crm_list:
268
+ st.subheader("Aktuální klienti")
269
+ if not st.session_state.db_customers:
270
+ st.info("Adresář je prázdný.")
271
+ else:
272
+ df_cust = pd.DataFrame(st.session_state.db_customers)
273
+ st.dataframe(df_cust[["jmeno", "firma", "telefon"]], use_container_width=True)
274
+
275
+ with col_crm_add:
276
+ st.subheader("Nový zákazník")
277
+ with st.form("add_customer_form"):
278
+ new_name = st.text_input("Jméno a příjmení")
279
+ new_company = st.text_input("Firma / Název společnosti (Volitelné)")
280
+ new_phone = st.text_input("Telefonní číslo")
281
+ crm_submit = st.form_submit_button("Uložit klienta")
282
+
283
+ if crm_submit:
284
+ if not new_name:
285
+ st.error("Jméno klienta je povinné.")
286
+ else:
287
+ st.session_state.db_customers.append({
288
+ "id": len(st.session_state.db_customers) + 1,
289
+ "jmeno": new_name,
290
+ "firma": new_company if new_company else "Fyzická osoba",
291
+ "telefon": new_phone
292
+ })
293
+ st.success("Klient byl úspěšně přidán do CRM.")
294
+ st.rerun()
295
+ except Exception as e:
296
+ log_system_error(f"Chyba v CRM subsystému: {str(e)}", traceback.format_exc())
297
+
298
+ # ZÁLOŽKA 4: KONVERZE NABÍDKY NA FAKTURY
299
+ with tab_fakturace:
300
+ st.header("Fakturace schválených zakázek")
301
+ try:
302
+ if not st.session_state.db_quotes:
303
+ st.info("Nemáte žádné vygenerované cenové nabídky, ze kterých by bylo možné vytvořit fakturu.")
304
+ else:
305
+ st.subheader("Převést schválenou nabídku na fakturu (1 kliknutí)")
306
+ quote_options = {f"ID: {q['id']} | {q['zakaznik']} — {q['celkem']:.2f} EUR": q for q in st.session_state.db_quotes}
307
+ selected_quote_label = st.selectbox("Zvolte cenovou nabídku pro fakturaci", list(quote_options.keys()))
308
+ chosen_quote = quote_options[selected_quote_label]
309
+
310
+ if st.button("🧾 VYSTAVIT FAKTURU JEDNÍM KLIKNUTÍM", type="primary", use_container_width=True):
311
+ invoice_id = len(st.session_state.db_invoices) + 1
312
+ new_invoice = {
313
+ "id": invoice_id,
314
+ "cislo": f"2026{invoice_id:04d}",
315
+ "zakaznik": chosen_quote["zakaznik"],
316
+ "castka": chosen_quote["celkem"],
317
+ "stav": "Odesláno",
318
+ "datum": time.strftime("%Y-%m-%d")
319
+ }
320
+ st.session_state.db_invoices.append(new_invoice)
321
+ st.success(f"Faktura číslo {new_invoice['cislo']} byla úspěšně vystavena a odeslána na e-mail klienta.")
322
+
323
+ st.markdown("---")
324
+ st.subheader("Přehled vystavených faktur")
325
+ if not st.session_state.db_invoices:
326
+ st.caption("Zatím nebyly vystaveny žádné faktury.")
327
+ else:
328
+ for idx, inv in enumerate(st.session_state.db_invoices):
329
+ c_inv1, c_inv2, c_inv3, c_inv4 = st.columns([1, 2, 1, 1])
330
+ c_inv1.write(f"**FA {inv['cislo']}**")
331
+ c_inv2.write(f"Klient: {inv['zakaznik']} | Částka: **{inv['castka']:.2f} EUR**")
332
+ stav_barvy = "🟢" if inv["stav"] == "Zaplaceno" else "🔵"
333
+ c_inv3.write(f"{stav_barvy} {inv['stav']}")
334
+
335
+ if inv["stav"] == "Odesláno":
336
+ if c_inv4.button("Označit jako zaplacené", key=f"pay_btn_{idx}"):
337
+ inv["stav"] = "Zaplaceno"
338
+ st.rerun()
339
+ else:
340
+ c_inv4.write("✅ Vyřízeno")
341
+
342
+ except Exception as e:
343
+ log_system_error(f"Chyba ve fakturačním modulu: {str(e)}", traceback.format_exc())