Aleksmorshen commited on
Commit
8d91a5b
·
verified ·
1 Parent(s): 512e4c5

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +216 -297
app.py CHANGED
@@ -1,7 +1,7 @@
1
  import streamlit as st
2
  import pandas as pd
3
  import os
4
- import sqlite3
5
  import random
6
  import string
7
  from datetime import datetime, timedelta
@@ -12,205 +12,84 @@ from huggingface_hub import HfApi, hf_hub_download
12
 
13
  # --- Настройки Hugging Face ---
14
  REPO_ID = "Testbase1/testsett" # Замените на ваш репозиторий
15
- DB_FILENAME = "auth_system.db"
16
  HF_TOKEN_WRITE = os.getenv("HF_TOKEN") # Токен для записи (загрузки)
17
  HF_TOKEN_READ = os.getenv("HF_TOKEN_READ") # Токен для чтения (скачивания)
18
 
19
- # Глобальные переменные для подключения к базе данных
20
- conn = None
21
- c = None
22
-
23
- # Глобальный блокировщик для операций с базой данных
24
  db_lock = threading.Lock()
25
 
26
- def init_db_connection():
27
- """Открывает новое соединение с базой данных с увеличенным таймаутом."""
28
- global conn, c
29
- if conn is not None:
30
- conn.close()
31
- # Устанавливаем timeout=30 секунд, чтобы операции ожидали освобождения блокировки
32
- conn = sqlite3.connect(DB_FILENAME, check_same_thread=False, timeout=30)
33
- c = conn.cursor()
34
-
35
- # --- Создание таблиц, если они не существуют ---
36
- def create_tables():
37
- with db_lock:
38
- c.execute('''
39
- CREATE TABLE IF NOT EXISTS users (
40
- id INTEGER PRIMARY KEY AUTOINCREMENT,
41
- username TEXT NOT NULL UNIQUE,
42
- token TEXT NOT NULL
43
- )
44
- ''')
45
- conn.commit()
46
-
47
- c.execute('''
48
- CREATE TABLE IF NOT EXISTS products (
49
- id INTEGER PRIMARY KEY AUTOINCREMENT,
50
- name TEXT NOT NULL,
51
- description TEXT,
52
- purchase_price REAL NOT NULL,
53
- sale_price REAL NOT NULL,
54
- quantity_in_stock INTEGER NOT NULL DEFAULT 0,
55
- user_id INTEGER NOT NULL,
56
- FOREIGN KEY(user_id) REFERENCES users(id)
57
- )
58
- ''')
59
- conn.commit()
60
-
61
- c.execute('''
62
- CREATE TABLE IF NOT EXISTS cart (
63
- id INTEGER PRIMARY KEY AUTOINCREMENT,
64
- product_id INTEGER NOT NULL,
65
- quantity INTEGER NOT NULL,
66
- UNIQUE(product_id)
67
- )
68
- ''')
69
- conn.commit()
70
-
71
- c.execute('''
72
- CREATE TABLE IF NOT EXISTS sales (
73
- id INTEGER PRIMARY KEY AUTOINCREMENT,
74
- sale_id TEXT NOT NULL,
75
- product_id INTEGER NOT NULL,
76
- quantity INTEGER NOT NULL,
77
- sale_date TIMESTAMP NOT NULL,
78
- total_amount REAL NOT NULL,
79
- FOREIGN KEY(product_id) REFERENCES products(id)
80
  )
81
- ''')
82
- conn.commit()
 
83
 
84
- # --- Функция для скачивания базы данных из Hugging Face ---
85
  def download_db_from_hf():
86
- """Скачивает файл базы данных из репозитория Hugging Face."""
87
  try:
88
  hf_hub_download(
89
  repo_id=REPO_ID,
90
  filename=DB_FILENAME,
91
  repo_type="dataset",
92
  token=HF_TOKEN_READ,
93
- local_dir=".", # теку��ая директория
94
  local_dir_use_symlinks=False
95
  )
96
- print("База данных успешно скачана из Hugging Face.")
97
  return True
98
  except Exception as e:
99
- print("Ошибка при скачивании базы данных:", e)
100
  return False
101
 
102
- # --- Функция для загрузки базы данных в Hugging Face ---
103
- def upload_db_to_hf():
104
- """Загружает файл базы данных в репозиторий Hugging Face."""
105
- try:
106
- api = HfApi()
107
- api.upload_file(
108
- path_or_fileobj=DB_FILENAME,
109
- path_in_repo=DB_FILENAME,
110
- repo_id=REPO_ID,
111
- repo_type="dataset",
112
- token=HF_TOKEN_WRITE
113
- )
114
- print("Резервная копия базы данных успешно загружена на Hugging Face.")
115
- except Exception as e:
116
- print("Ошибка при загрузке резервной копии:", e)
117
-
118
  # --- Функция периодического резервного копирования ---
119
  def periodic_backup():
120
  """
121
- Запускает бесконечный цикл резервного копирования с интервалом в 30 минут.
122
- Функция не использует глобальное подключение к БД, поэтому не конфликтует с основными операциями.
123
  """
124
  while True:
125
  upload_db_to_hf()
126
- time.sleep(1800) # 30 минут = 1800 секунд
127
 
128
  # --- Вспомогательные функции ---
129
  def generate_token():
130
  return ''.join(random.choices(string.ascii_letters + string.digits, k=13))
131
 
132
- def get_user_id_by_token(token):
133
- c.execute("SELECT id FROM users WHERE token=?", (token,))
134
- user = c.fetchone()
135
- return user[0] if user else None
136
-
137
- def get_cart_summary(user_id):
138
- c.execute('''
139
- SELECT p.name, c.quantity, p.sale_price, c.quantity * p.sale_price
140
- FROM cart c
141
- JOIN products p ON c.product_id = p.id
142
- WHERE p.user_id = ?
143
- ''', (user_id,))
144
- items = c.fetchall()
145
- total_quantity = sum(item[1] for item in items)
146
- total_price = sum(item[3] for item in items)
147
- return items, total_quantity, total_price
148
-
149
- def record_sales():
150
- sales_details = []
151
- sale_id = ''.join(random.choices(string.ascii_letters + string.digits, k=8))
152
- sale_date = datetime.now(pytz.timezone('Asia/Bishkek')).strftime('%Y-%m-%d %H:%M:%S')
153
-
154
- c.execute('SELECT product_id, quantity FROM cart')
155
- cart_items = c.fetchall()
156
-
157
- total_amount = 0.0
158
- with db_lock:
159
- for product_id, quantity in cart_items:
160
- c.execute('SELECT sale_price FROM products WHERE id=?', (product_id,))
161
- sale_price = c.fetchone()[0]
162
- amount = quantity * sale_price
163
- total_amount += amount
164
-
165
- c.execute('''
166
- INSERT INTO sales (sale_id, product_id, quantity, sale_date, total_amount)
167
- VALUES (?, ?, ?, ?, ?)
168
- ''', (sale_id, product_id, quantity, sale_date, amount))
169
- sales_details.append((product_id, quantity, amount, sale_date))
170
- c.execute('DELETE FROM cart')
171
- conn.commit()
172
- return sales_details, sale_id, total_amount
173
-
174
- def generate_monthly_report(user_id):
175
- start_date = (datetime.now().replace(day=1)).strftime('%Y-%m-%d')
176
- end_date = (datetime.now() + timedelta(days=31)).replace(day=1).strftime('%Y-%m-%d')
177
-
178
- c.execute('''
179
- SELECT p.name, SUM(s.quantity) AS total_quantity, p.sale_price, SUM(s.quantity) * p.sale_price AS total_sales,
180
- SUM(s.quantity) * (p.sale_price - p.purchase_price) AS profit
181
- FROM sales s
182
- JOIN products p ON s.product_id = p.id
183
- WHERE s.sale_date BETWEEN ? AND ? AND p.user_id = ?
184
- GROUP BY p.id
185
- ''', (start_date, end_date, user_id))
186
- sales_data = c.fetchall()
187
- total_sales = sum(item[3] for item in sales_data)
188
- total_profit = sum(item[4] for item in sales_data)
189
- return sales_data, total_sales, total_profit
190
-
191
- def get_all_sales(user_id):
192
- c.execute('''
193
- SELECT DISTINCT sale_id, sale_date, SUM(total_amount) AS total_amount
194
- FROM sales s
195
- JOIN products p ON s.product_id = p.id
196
- WHERE p.user_id = ?
197
- GROUP BY sale_id, sale_date
198
- ORDER BY sale_date DESC
199
- ''', (user_id,))
200
- sales_data = c.fetchall()
201
- return sales_data
202
-
203
- def get_sale_details(sale_id):
204
- c.execute('''
205
- SELECT p.name, s.quantity, p.sale_price, s.sale_date
206
- FROM sales s
207
- JOIN products p ON s.product_id = p.id
208
- WHERE s.sale_id = ?
209
- ''', (sale_id,))
210
- sale_details = c.fetchall()
211
- return sale_details
212
 
213
  # --- Страницы приложения ---
 
214
  def register():
215
  st.title('Регистрация')
216
  username = st.text_input("Введите ваше имя пользователя")
@@ -219,13 +98,15 @@ def register():
219
  if st.button("Зарегистрироваться"):
220
  if admin_password == "morshenfullsumflpol":
221
  token = generate_token()
222
- try:
223
- with db_lock:
224
- c.execute("INSERT INTO users (username, token) VALUES (?, ?)", (username, token))
225
- conn.commit()
226
- st.success(f"Регистрация успешна! Ваш токен: {token}")
227
- except sqlite3.IntegrityError:
228
- st.error("Это имя пользователя уже занято. Попробуйте другое.")
 
 
229
  else:
230
  st.error("Неверный пароль администратора!")
231
 
@@ -234,11 +115,13 @@ def login():
234
  token_input = st.text_input("Введите ваш токен")
235
 
236
  if st.button("Войти"):
237
- user_id = get_user_id_by_token(token_input)
238
- if user_id:
 
 
239
  st.session_state.logged_in = True
240
- st.session_state.username = token_input
241
- st.session_state.user_id = user_id
242
  st.success("Добро пожаловать!")
243
  else:
244
  st.error("Неверный токен!")
@@ -254,108 +137,136 @@ def add_product():
254
  if st.button("Добавить товар"):
255
  if product_name and sale_price:
256
  with db_lock:
257
- c.execute(
258
- "INSERT INTO products (name, description, purchase_price, sale_price, quantity_in_stock, user_id) VALUES (?, ?, ?, ?, ?, ?)",
259
- (product_name, product_description, purchase_price, sale_price, product_quantity, st.session_state.user_id)
260
- )
261
- conn.commit()
 
 
 
 
 
 
 
262
  st.success("Товар успешно добавлен!")
263
  else:
264
  st.error("Пожалуйста, введите все обязательные данные.")
265
 
266
  def edit_products():
267
  st.title("Редактирование товара")
268
- products = c.execute("SELECT id, name, description, purchase_price, sale_price, quantity_in_stock FROM products WHERE user_id=?",
269
- (st.session_state.user_id,)).fetchall()
 
270
  if products:
271
- product_names = [p[1] for p in products]
272
  selected_product_name = st.selectbox("Выберите товар", product_names)
273
- product = next(p for p in products if p[1] == selected_product_name)
274
 
275
- new_name = st.text_input("Новое название товара", product[1])
276
- new_description = st.text_area("Новое описание товара", product[2])
277
- new_purchase_price = st.number_input("Новая приходная цена", min_value=0.0, step=0.01, value=product[3])
278
- new_sale_price = st.number_input("Новая отпускная цена", min_value=0.0, step=0.01, value=product[4])
279
- new_quantity_in_stock = st.number_input("Новое количество на складе", min_value=0, step=1, value=product[5])
280
 
281
  if st.button("Сохранить изменения"):
282
  with db_lock:
283
- c.execute('''
284
- UPDATE products
285
- SET name = ?, description = ?, purchase_price = ?, sale_price = ?, quantity_in_stock = ?
286
- WHERE id = ?
287
- ''', (new_name, new_description, new_purchase_price, new_sale_price, new_quantity_in_stock, product[0]))
288
- conn.commit()
 
 
 
 
289
  st.success("Товар успешно обновлен!")
290
 
291
  if st.button("Удалить товар"):
292
  with db_lock:
293
- c.execute("DELETE FROM products WHERE id=?", (product[0],))
294
- conn.commit()
 
295
  st.success("Товар успешно удален!")
 
 
296
 
297
  def add_to_cart():
298
  st.title("Отпуск товара")
299
  search_term = st.text_input("Поиск товара").strip().lower()
300
- c.execute("SELECT id, name, sale_price, quantity_in_stock FROM products WHERE user_id=? AND LOWER(name) LIKE ?",
301
- (st.session_state.user_id, f"%{search_term}%"))
302
- products = c.fetchall()
303
-
304
  if products:
305
  for product in products:
306
  cols = st.columns(4)
307
  with cols[0]:
308
- st.write(product[1])
309
- st.write(f"**Отпускная цена**: {product[2]:.2f}")
310
- st.write(f"**Остаток на складе**: {product[3]}")
311
  with cols[1]:
312
- quantity = st.number_input(f"Количество для '{product[1]}'", min_value=0, max_value=product[3], key=f"quantity_{product[0]}")
313
  with cols[2]:
314
- add_button = st.button(f"Добавить в корзину", key=f"add_{product[0]}")
315
  if add_button:
316
- if quantity <= product[3]:
317
  with db_lock:
318
- c.execute('''
319
- INSERT INTO cart (product_id, quantity)
320
- VALUES (?, ?)
321
- ON CONFLICT(product_id)
322
- DO UPDATE SET quantity = quantity + excluded.quantity
323
- ''', (product[0], quantity))
324
- c.execute("UPDATE products SET quantity_in_stock = quantity_in_stock - ? WHERE id=?", (quantity, product[0]))
325
- conn.commit()
326
- st.success(f"Товар '{product[1]}' успешно добавлен в корзину!")
 
 
 
 
 
 
 
327
  else:
328
- st.error(f"Недостаточное количество товара '{product[1]}' на складе!")
329
  with cols[3]:
330
- remove_button = st.button(f"Удалить из корзины", key=f"remove_{product[0]}")
331
  if remove_button:
332
  if quantity > 0:
333
- current_quantity = c.execute("SELECT quantity FROM cart WHERE product_id=?", (product[0],)).fetchone()
334
- if current_quantity:
335
- new_quantity = current_quantity[0] - quantity
336
- with db_lock:
337
- if new_quantity > 0:
338
- c.execute('''
339
- INSERT INTO cart (product_id, quantity)
340
- VALUES (?, ?)
341
- ON CONFLICT(product_id)
342
- DO UPDATE SET quantity = excluded.quantity
343
- ''', (product[0], new_quantity))
344
  else:
345
- c.execute("DELETE FROM cart WHERE product_id=?", (product[0],))
346
- c.execute("UPDATE products SET quantity_in_stock = quantity_in_stock + ? WHERE id=?", (quantity, product[0]))
347
- conn.commit()
348
- st.success(f"Товар '{product[1]}' успешно удалён из корзины!")
349
- else:
350
- st.error(f"Товар '{product[1]}' не найден в корзине.")
 
 
351
  else:
352
- st.error(f"Введите количество для удаления товара '{product[1]}'.")
353
 
354
  st.subheader("Состояние корзины")
355
- items, total_quantity, total_price = get_cart_summary(st.session_state.user_id)
356
- if items:
357
- df = pd.DataFrame(items, columns=["Название", "Количество", "Цена за единицу", "Итого"])
 
 
 
 
 
 
 
358
  st.dataframe(df.style.format({"Цена за единицу": "{:.2f}", "Итого": "{:.2f}"}), use_container_width=True)
 
 
359
  st.write(f"Общее количество: {total_quantity}, Общая стоимость: {total_price:.2f}")
360
 
361
  if st.button("Пробить"):
@@ -363,49 +274,74 @@ def add_to_cart():
363
  if sales_details:
364
  st.write("**Детали сделок:**")
365
  sale_details_df = pd.DataFrame(sales_details, columns=["ID товара", "Количество", "Сумма", "Дата и время"])
366
- st.dataframe(sale_details_df.style.format({"Сумма": "{:.2f}", "Дата и время": lambda x: pd.to_datetime(x).strftime('%d-%m-%Y %H:%M:%S')}), use_container_width=True)
 
 
367
  st.write(f"**Общая сумма сделки:** {total_amount:.2f}")
368
  st.success("Корзина успешно пробита! Все товары добавлены в отчет и корзина очищена.")
369
-
370
- if st.button("Сделки"):
371
- sales_data = get_all_sales(st.session_state.user_id)
372
- if sales_data:
373
- for sale_id, sale_date, total_amount in sales_data:
374
- st.write(f"**Сделка ID:** {sale_id} ({sale_date}) - **Общая сумма:** {total_amount:.2f}")
375
- sale_details = get_sale_details(sale_id)
376
- if sale_details:
377
- df_sales = pd.DataFrame(sale_details, columns=["Название товара", "Количество", "Цена за единицу", "Дата и время"])
378
- st.dataframe(df_sales.style.format({"Дата и время": lambda x: pd.to_datetime(x).strftime('%d-%m-%Y %H:%M:%S')}), use_container_width=True)
379
- else:
380
- st.info("Нет деталей для отображения.")
381
- else:
382
- st.info("Нет сделок для отображения.")
383
  else:
384
  st.info("Корзина пуста.")
 
 
385
 
386
  def monthly_report():
387
  st.title("Отчет о продажах за месяц")
388
- sales_data, total_sales, total_profit = generate_monthly_report(st.session_state.user_id)
389
- if sales_data:
390
- df = pd.DataFrame(sales_data, columns=["Название", "Общее количество", "Отпускная цена", "Общие продажи", "Прибыль"])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
391
  st.write(f"Отчет за {datetime.now().strftime('%B %Y')}")
392
  st.dataframe(df.style.format({"Отпускная цена": "{:.2f}", "Общие продажи": "{:.2f}", "Прибыль": "{:.2f}"}), use_container_width=True)
393
  st.write(f"**Общая сумма продаж**: {total_sales:.2f}")
394
  st.write(f"**Общая сумма прибыли**: {total_profit:.2f}")
395
 
396
  if st.button("Сделки"):
397
- sales_data = get_all_sales(st.session_state.user_id)
398
- if sales_data:
399
- for sale_id, sale_date, total_amount in sales_data:
400
- st.write(f"**Сделка ID:** {sale_id} ({sale_date}) - **Общая сумма:** {total_amount:.2f}")
401
- sale_details = get_sale_details(sale_id)
402
- if sale_details:
403
- df_sales = pd.DataFrame(sale_details, columns=["Название товара", "Количество", "Цена за единицу", "Дата и время"])
404
- st.dataframe(df_sales.style.format({"Дата и время": lambda x: pd.to_datetime(x).strftime('%d-%m-%Y %H:%M:%S')}), use_container_width=True)
405
- else:
406
- st.info("Нет деталей для отображения.")
407
- else:
408
- st.info("Нет сделок для отображения.")
 
 
 
 
409
  else:
410
  st.info("Нет данных о продажах за этот месяц.")
411
 
@@ -414,37 +350,20 @@ def main():
414
  st.warning("Попытка скачивания базы данных из Hugging Face...")
415
  download_success = download_db_from_hf()
416
  if download_success:
417
- st.success("База данных успешно скачана из Hugging Face!")
418
  try:
419
  os.chmod(DB_FILENAME, 0o666)
420
- st.info("Права доступа к базе данных изменены (read-write).")
421
  except Exception as e:
422
  st.error(f"Не удалось изменить права доступа: {e}")
423
  else:
424
  if os.path.exists(DB_FILENAME):
425
- st.info("Не удалось скачать базу данных, используется локальная копия.")
426
  else:
427
- st.error("Не удалось скачать базу данных и локальной базы нет. Будет создана новая база данных.")
428
- open(DB_FILENAME, 'w').close() # создаём пустой файл
429
-
430
- # Переинициализируем соединение с базой данных после изменения прав доступа
431
- init_db_connection()
432
-
433
- # Проверка наличия таблицы "users". Если отсутствует, создаём таблицы.
434
- try:
435
- c.execute("SELECT * FROM users LIMIT 1")
436
- except sqlite3.OperationalError as e:
437
- if "readonly" in str(e).lower():
438
- st.error("База данных находится в режиме только для чтения. Проверьте права доступа!")
439
- else:
440
- st.info("Таблицы не найдены в базе данных. Создание таблиц...")
441
- try:
442
- os.chmod(DB_FILENAME, 0o666)
443
- except Exception as e:
444
- st.error(f"Не удалось изменить права доступа: {e}")
445
- create_tables()
446
- st.success("Таблицы успешно созданы!")
447
-
448
  if 'logged_in' not in st.session_state:
449
  st.session_state.logged_in = False
450
 
@@ -474,7 +393,7 @@ def main():
474
 
475
  if __name__ == "__main__":
476
  main()
477
- # Ждем 15 секунд после инициализации базы данных, затем запускаем фоновый поток резервного копирования
478
  time.sleep(15)
479
  backup_thread = threading.Thread(target=periodic_backup, daemon=True)
480
  backup_thread.start()
 
1
  import streamlit as st
2
  import pandas as pd
3
  import os
4
+ import json
5
  import random
6
  import string
7
  from datetime import datetime, timedelta
 
12
 
13
  # --- Настройки Hugging Face ---
14
  REPO_ID = "Testbase1/testsett" # Замените на ваш репозиторий
15
+ DB_FILENAME = "auth_system.json" # JSON база данных
16
  HF_TOKEN_WRITE = os.getenv("HF_TOKEN") # Токен для записи (загрузки)
17
  HF_TOKEN_READ = os.getenv("HF_TOKEN_READ") # Токен для чтения (скачивания)
18
 
19
+ # Глобальный блокировщик для доступа к JSON-файлу
 
 
 
 
20
  db_lock = threading.Lock()
21
 
22
+ # Функция загрузки базы данных из JSON-файла
23
+ def load_db():
24
+ if not os.path.exists(DB_FILENAME):
25
+ data = {"users": [], "products": [], "cart": [], "sales": []}
26
+ save_db(data)
27
+ return data
28
+ with open(DB_FILENAME, "r", encoding="utf8") as f:
29
+ try:
30
+ data = json.load(f)
31
+ except json.JSONDecodeError:
32
+ data = {"users": [], "products": [], "cart": [], "sales": []}
33
+ save_db(data)
34
+ return data
35
+
36
+ # Функция сохранения базы данных в JSON-файл
37
+ def save_db(data):
38
+ with open(DB_FILENAME, "w", encoding="utf8") as f:
39
+ json.dump(data, f, ensure_ascii=False, indent=4)
40
+
41
+ # --- Функция резервного копирования (загрузка базы на Hugging Face) ---
42
+ def upload_db_to_hf():
43
+ """Загружает JSON-файл базы данных в репозиторий Hugging Face."""
44
+ try:
45
+ api = HfApi()
46
+ api.upload_file(
47
+ path_or_fileobj=DB_FILENAME,
48
+ path_in_repo=DB_FILENAME,
49
+ repo_id=REPO_ID,
50
+ repo_type="dataset",
51
+ token=HF_TOKEN_WRITE
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
52
  )
53
+ print("Резервная копия JSON базы успешно загружена на Hugging Face.")
54
+ except Exception as e:
55
+ print("Ошибка при загрузке резервной копии:", e)
56
 
57
+ # --- Функция скачивания базы данных из Hugging Face ---
58
  def download_db_from_hf():
59
+ """Скачивает JSON-файл базы данных из репозитория Hugging Face."""
60
  try:
61
  hf_hub_download(
62
  repo_id=REPO_ID,
63
  filename=DB_FILENAME,
64
  repo_type="dataset",
65
  token=HF_TOKEN_READ,
66
+ local_dir=".",
67
  local_dir_use_symlinks=False
68
  )
69
+ print("JSON база успешно скачана из Hugging Face.")
70
  return True
71
  except Exception as e:
72
+ print("Ошибка при скачивании JSON базы:", e)
73
  return False
74
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
75
  # --- Функция периодического резервного копирования ---
76
  def periodic_backup():
77
  """
78
+ Каждые 30 минут (1800 секунд) вызывается функция upload_db_to_hf().
 
79
  """
80
  while True:
81
  upload_db_to_hf()
82
+ time.sleep(1800)
83
 
84
  # --- Вспомогательные функции ---
85
  def generate_token():
86
  return ''.join(random.choices(string.ascii_letters + string.digits, k=13))
87
 
88
+ def get_new_id(items):
89
+ return max([item.get("id", 0) for item in items], default=0) + 1
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
90
 
91
  # --- Страницы приложения ---
92
+
93
  def register():
94
  st.title('Регистрация')
95
  username = st.text_input("Введите ваше имя пользователя")
 
98
  if st.button("Зарегистрироваться"):
99
  if admin_password == "morshenfullsumflpol":
100
  token = generate_token()
101
+ with db_lock:
102
+ data = load_db()
103
+ if any(u["username"] == username for u in data["users"]):
104
+ st.error("Это имя пользователя уже занято. Попробуйте другое.")
105
+ else:
106
+ new_id = get_new_id(data["users"])
107
+ data["users"].append({"id": new_id, "username": username, "token": token})
108
+ save_db(data)
109
+ st.success(f"Регистрация успешна! Ваш токен: {token}")
110
  else:
111
  st.error("Неверный пароль администратора!")
112
 
 
115
  token_input = st.text_input("Введите ваш токен")
116
 
117
  if st.button("Войти"):
118
+ with db_lock:
119
+ data = load_db()
120
+ user = next((u for u in data["users"] if u["token"] == token_input), None)
121
+ if user:
122
  st.session_state.logged_in = True
123
+ st.session_state.username = user["username"]
124
+ st.session_state.user_id = user["id"]
125
  st.success("Добро пожаловать!")
126
  else:
127
  st.error("Неверный токен!")
 
137
  if st.button("Добавить товар"):
138
  if product_name and sale_price:
139
  with db_lock:
140
+ data = load_db()
141
+ new_id = get_new_id(data["products"])
142
+ data["products"].append({
143
+ "id": new_id,
144
+ "name": product_name,
145
+ "description": product_description,
146
+ "purchase_price": purchase_price,
147
+ "sale_price": sale_price,
148
+ "quantity_in_stock": product_quantity,
149
+ "user_id": st.session_state.user_id
150
+ })
151
+ save_db(data)
152
  st.success("Товар успешно добавлен!")
153
  else:
154
  st.error("Пожалуйста, введите все обязательные данные.")
155
 
156
  def edit_products():
157
  st.title("Редактирование товара")
158
+ with db_lock:
159
+ data = load_db()
160
+ products = [p for p in data["products"] if p["user_id"] == st.session_state.user_id]
161
  if products:
162
+ product_names = [p["name"] for p in products]
163
  selected_product_name = st.selectbox("Выберите товар", product_names)
164
+ product = next(p for p in products if p["name"] == selected_product_name)
165
 
166
+ new_name = st.text_input("Новое название товара", product["name"])
167
+ new_description = st.text_area("Новое описание товара", product["description"])
168
+ new_purchase_price = st.number_input("Новая приходная цена", min_value=0.0, step=0.01, value=product["purchase_price"])
169
+ new_sale_price = st.number_input("Новая отпускная цена", min_value=0.0, step=0.01, value=product["sale_price"])
170
+ new_quantity_in_stock = st.number_input("Новое количество на складе", min_value=0, step=1, value=product["quantity_in_stock"])
171
 
172
  if st.button("Сохранить изменения"):
173
  with db_lock:
174
+ data = load_db()
175
+ for p in data["products"]:
176
+ if p["id"] == product["id"]:
177
+ p["name"] = new_name
178
+ p["description"] = new_description
179
+ p["purchase_price"] = new_purchase_price
180
+ p["sale_price"] = new_sale_price
181
+ p["quantity_in_stock"] = new_quantity_in_stock
182
+ break
183
+ save_db(data)
184
  st.success("Товар успешно обновлен!")
185
 
186
  if st.button("Удалить товар"):
187
  with db_lock:
188
+ data = load_db()
189
+ data["products"] = [p for p in data["products"] if p["id"] != product["id"]]
190
+ save_db(data)
191
  st.success("Товар успешно удален!")
192
+ else:
193
+ st.info("У вас пока нет добавленных товаров.")
194
 
195
  def add_to_cart():
196
  st.title("Отпуск товара")
197
  search_term = st.text_input("Поиск товара").strip().lower()
198
+ with db_lock:
199
+ data = load_db()
200
+ products = [p for p in data["products"] if p["user_id"] == st.session_state.user_id and search_term in p["name"].lower()]
 
201
  if products:
202
  for product in products:
203
  cols = st.columns(4)
204
  with cols[0]:
205
+ st.write(product["name"])
206
+ st.write(f"**Отпускная цена**: {product['sale_price']:.2f}")
207
+ st.write(f"**Остаток на складе**: {product['quantity_in_stock']}")
208
  with cols[1]:
209
+ quantity = st.number_input(f"Количество для '{product['name']}'", min_value=0, max_value=product["quantity_in_stock"], key=f"quantity_{product['id']}")
210
  with cols[2]:
211
+ add_button = st.button(f"Добавить в корзину", key=f"add_{product['id']}")
212
  if add_button:
213
+ if quantity <= product["quantity_in_stock"]:
214
  with db_lock:
215
+ data = load_db()
216
+ # Обновляем корзину: если товар уже есть – увеличиваем количество, иначе добавляем новый
217
+ cart = data["cart"]
218
+ existing = next((item for item in cart if item["product_id"] == product["id"]), None)
219
+ if existing:
220
+ existing["quantity"] += quantity
221
+ else:
222
+ new_cart_id = get_new_id(cart)
223
+ cart.append({"id": new_cart_id, "product_id": product["id"], "quantity": quantity})
224
+ # Уменьшаем остаток товара
225
+ for p in data["products"]:
226
+ if p["id"] == product["id"]:
227
+ p["quantity_in_stock"] -= quantity
228
+ break
229
+ save_db(data)
230
+ st.success(f"Товар '{product['name']}' успешно добавлен в корзину!")
231
  else:
232
+ st.error(f"Недостаточное количество товара '{product['name']}' на складе!")
233
  with cols[3]:
234
+ remove_button = st.button(f"Удалить из корзины", key=f"remove_{product['id']}")
235
  if remove_button:
236
  if quantity > 0:
237
+ with db_lock:
238
+ data = load_db()
239
+ cart = data["cart"]
240
+ existing = next((item for item in cart if item["product_id"] == product["id"]), None)
241
+ if existing:
242
+ if existing["quantity"] > quantity:
243
+ existing["quantity"] -= quantity
 
 
 
 
244
  else:
245
+ cart.remove(existing)
246
+ # Возвращаем товар на склад
247
+ for p in data["products"]:
248
+ if p["id"] == product["id"]:
249
+ p["quantity_in_stock"] += quantity
250
+ break
251
+ save_db(data)
252
+ st.success(f"Товар '{product['name']}' успешно удалён из корзины!")
253
  else:
254
+ st.error(f"Введите количество для удаления товара '{product['name']}'.")
255
 
256
  st.subheader("Состояние корзины")
257
+ with db_lock:
258
+ data = load_db()
259
+ cart_items = []
260
+ for item in data["cart"]:
261
+ prod = next((p for p in data["products"] if p["id"] == item["product_id"]), None)
262
+ if prod:
263
+ total = item["quantity"] * prod["sale_price"]
264
+ cart_items.append([prod["name"], item["quantity"], prod["sale_price"], total])
265
+ if cart_items:
266
+ df = pd.DataFrame(cart_items, columns=["Название", "Количество", "Цена за единицу", "Итого"])
267
  st.dataframe(df.style.format({"Цена за единицу": "{:.2f}", "Итого": "{:.2f}"}), use_container_width=True)
268
+ total_quantity = sum(item[1] for item in cart_items)
269
+ total_price = sum(item[3] for item in cart_items)
270
  st.write(f"Общее количество: {total_quantity}, Общая стоимость: {total_price:.2f}")
271
 
272
  if st.button("Пробить"):
 
274
  if sales_details:
275
  st.write("**Детали сделок:**")
276
  sale_details_df = pd.DataFrame(sales_details, columns=["ID товара", "Количество", "Сумма", "Дата и время"])
277
+ st.dataframe(sale_details_df.style.format({"Сумма": "{:.2f}",
278
+ "Дата и время": lambda x: pd.to_datetime(x).strftime('%d-%m-%Y %H:%M:%S')}),
279
+ use_container_width=True)
280
  st.write(f"**Общая сумма сделки:** {total_amount:.2f}")
281
  st.success("Корзина успешно пробита! Все товары добавлены в отчет и корзина очищена.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
282
  else:
283
  st.info("Корзина пуста.")
284
+ else:
285
+ st.info("По вашему запросу товары не найдены.")
286
 
287
  def monthly_report():
288
  st.title("Отчет о продажах за месяц")
289
+ with db_lock:
290
+ data = load_db()
291
+ start_date = datetime.now().replace(day=1)
292
+ end_date = (datetime.now() + timedelta(days=31)).replace(day=1)
293
+ sales = []
294
+ # Для каждого проданного товара ищем информацию о продукте
295
+ for sale in data["sales"]:
296
+ sale_date = datetime.strptime(sale["sale_date"], "%Y-%m-%d %H:%M:%S")
297
+ if start_date <= sale_date < end_date:
298
+ prod = next((p for p in data["products"] if p["id"] == sale["product_id"]), None)
299
+ if prod and prod["user_id"] == st.session_state.user_id:
300
+ sales.append({
301
+ "name": prod["name"],
302
+ "quantity": sale["quantity"],
303
+ "sale_price": prod["sale_price"],
304
+ "total_sales": sale["total_amount"],
305
+ "profit": sale["quantity"] * (prod["sale_price"] - prod["purchase_price"])
306
+ })
307
+ # Группировка по имени товара
308
+ report = {}
309
+ for s in sales:
310
+ key = s["name"]
311
+ if key not in report:
312
+ report[key] = {"quantity": 0, "sale_price": s["sale_price"], "total_sales": 0, "profit": 0}
313
+ report[key]["quantity"] += s["quantity"]
314
+ report[key]["total_sales"] += s["total_sales"]
315
+ report[key]["profit"] += s["profit"]
316
+ report_data = []
317
+ for name, values in report.items():
318
+ report_data.append([name, values["quantity"], values["sale_price"], values["total_sales"], values["profit"]])
319
+ total_sales = sum(item[3] for item in report_data)
320
+ total_profit = sum(item[4] for item in report_data)
321
+ if report_data:
322
+ df = pd.DataFrame(report_data, columns=["Название", "Общее количество", "Отпускная цена", "Общие продажи", "Прибыль"])
323
  st.write(f"Отчет за {datetime.now().strftime('%B %Y')}")
324
  st.dataframe(df.style.format({"Отпускная цена": "{:.2f}", "Общие продажи": "{:.2f}", "Прибыль": "{:.2f}"}), use_container_width=True)
325
  st.write(f"**Общая сумма продаж**: {total_sales:.2f}")
326
  st.write(f"**Общая сумма прибыли**: {total_profit:.2f}")
327
 
328
  if st.button("Сделки"):
329
+ with db_lock:
330
+ data = load_db()
331
+ sales_grouped = {}
332
+ for sale in data["sales"]:
333
+ prod = next((p for p in data["products"] if p["id"] == sale["product_id"]), None)
334
+ if prod and prod["user_id"] == st.session_state.user_id:
335
+ key = sale["sale_id"]
336
+ if key not in sales_grouped:
337
+ sales_grouped[key] = {"sale_date": sale["sale_date"], "total_amount": 0, "details": []}
338
+ sales_grouped[key]["total_amount"] += sale["total_amount"]
339
+ sales_grouped[key]["details"].append([prod["name"], sale["quantity"], prod["sale_price"], sale["sale_date"]])
340
+ for key, group in sales_grouped.items():
341
+ st.write(f"**Сделка ID:** {key} ({group['sale_date']}) - **Общая сумма:** {group['total_amount']:.2f}")
342
+ df_sales = pd.DataFrame(group["details"], columns=["Название товара", "Количество", "Цена за единицу", "Дата и время"])
343
+ st.dataframe(df_sales.style.format({"Дата и время": lambda x: pd.to_datetime(x).strftime('%d-%m-%Y %H:%M:%S')}),
344
+ use_container_width=True)
345
  else:
346
  st.info("Нет данных о продажах за этот месяц.")
347
 
 
350
  st.warning("Попытка скачивания базы данных из Hugging Face...")
351
  download_success = download_db_from_hf()
352
  if download_success:
353
+ st.success("JSON база успешно скачана из Hugging Face!")
354
  try:
355
  os.chmod(DB_FILENAME, 0o666)
356
+ st.info("Права доступа к JSON базе изменены (read-write).")
357
  except Exception as e:
358
  st.error(f"Не удалось изменить права доступа: {e}")
359
  else:
360
  if os.path.exists(DB_FILENAME):
361
+ st.info("Не удалось скачать JSON базу, используется локальная копия.")
362
  else:
363
+ st.error("Не удалось скачать JSON базу и локальной базы нет. Будет создана новая база.")
364
+ with open(DB_FILENAME, "w", encoding="utf8") as f:
365
+ json.dump({"users": [], "products": [], "cart": [], "sales": []}, f, ensure_ascii=False, indent=4)
366
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
367
  if 'logged_in' not in st.session_state:
368
  st.session_state.logged_in = False
369
 
 
393
 
394
  if __name__ == "__main__":
395
  main()
396
+ # Ждем 15 секунд после инициализации, затем запускаем фоновый поток резервного копирования
397
  time.sleep(15)
398
  backup_thread = threading.Thread(target=periodic_backup, daemon=True)
399
  backup_thread.start()