Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
@@ -294,6 +294,314 @@ def add_to_cart():
|
|
294 |
st.success("Корзина успешно пробита! Все товары добавлены в отчет и корзина очищена.")
|
295 |
|
296 |
# Кнопка для отображения сделок
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
297 |
if st.button("Сделки"):
|
298 |
sales_data = get_all_sales()
|
299 |
if sales_data:
|
|
|
294 |
st.success("Корзина успешно пробита! Все товары добавлены в отчет и корзина очищена.")
|
295 |
|
296 |
# Кнопка для отображения сделок
|
297 |
+
import streamlit as st
|
298 |
+
import pandas as pd
|
299 |
+
import sqlite3
|
300 |
+
import random
|
301 |
+
import string
|
302 |
+
from datetime import datetime, timedelta
|
303 |
+
import pytz
|
304 |
+
|
305 |
+
# Подключение к базе данных SQLite3
|
306 |
+
conn = sqlite3.connect('auth_system.db', check_same_thread=False)
|
307 |
+
c = conn.cursor()
|
308 |
+
|
309 |
+
# Создание таблиц, если они не существуют
|
310 |
+
c.execute('''
|
311 |
+
CREATE TABLE IF NOT EXISTS users (
|
312 |
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
313 |
+
username TEXT NOT NULL UNIQUE,
|
314 |
+
token TEXT NOT NULL
|
315 |
+
)
|
316 |
+
''')
|
317 |
+
conn.commit()
|
318 |
+
|
319 |
+
c.execute('''
|
320 |
+
CREATE TABLE IF NOT EXISTS products (
|
321 |
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
322 |
+
name TEXT NOT NULL,
|
323 |
+
description TEXT,
|
324 |
+
purchase_price REAL NOT NULL,
|
325 |
+
sale_price REAL NOT NULL,
|
326 |
+
quantity_in_stock INTEGER NOT NULL DEFAULT 0,
|
327 |
+
user_id INTEGER NOT NULL,
|
328 |
+
FOREIGN KEY(user_id) REFERENCES users(id)
|
329 |
+
)
|
330 |
+
''')
|
331 |
+
conn.commit()
|
332 |
+
|
333 |
+
c.execute('''
|
334 |
+
CREATE TABLE IF NOT EXISTS cart (
|
335 |
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
336 |
+
product_id INTEGER NOT NULL,
|
337 |
+
quantity INTEGER NOT NULL,
|
338 |
+
UNIQUE(product_id)
|
339 |
+
)
|
340 |
+
''')
|
341 |
+
conn.commit()
|
342 |
+
|
343 |
+
c.execute('''
|
344 |
+
CREATE TABLE IF NOT EXISTS sales (
|
345 |
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
346 |
+
product_id INTEGER NOT NULL,
|
347 |
+
quantity INTEGER NOT NULL,
|
348 |
+
sale_date TIMESTAMP NOT NULL,
|
349 |
+
FOREIGN KEY(product_id) REFERENCES products(id)
|
350 |
+
)
|
351 |
+
''')
|
352 |
+
conn.commit()
|
353 |
+
|
354 |
+
# Функция для генерации случайного 13-значного токена
|
355 |
+
def generate_token():
|
356 |
+
return ''.join(random.choices(string.ascii_letters + string.digits, k=13))
|
357 |
+
|
358 |
+
# Функция для получения идентификатора пользователя по токену
|
359 |
+
def get_user_id_by_token(token):
|
360 |
+
c.execute("SELECT id FROM users WHERE token=?", (token,))
|
361 |
+
user = c.fetchone()
|
362 |
+
return user[0] if user else None
|
363 |
+
|
364 |
+
# Функция для получения товаров из корзины
|
365 |
+
def get_cart_summary(user_id):
|
366 |
+
c.execute('''
|
367 |
+
SELECT p.name, c.quantity, p.sale_price, c.quantity * p.sale_price
|
368 |
+
FROM cart c
|
369 |
+
JOIN products p ON c.product_id = p.id
|
370 |
+
WHERE p.user_id = ?
|
371 |
+
''', (user_id,))
|
372 |
+
items = c.fetchall()
|
373 |
+
total_quantity = sum(item[1] for item in items)
|
374 |
+
total_price = sum(item[3] for item in items)
|
375 |
+
return items, total_quantity, total_price
|
376 |
+
|
377 |
+
# Функция для добавления продажи в отчет
|
378 |
+
def record_sales():
|
379 |
+
sales_details = []
|
380 |
+
c.execute('SELECT product_id, quantity FROM cart')
|
381 |
+
cart_items = c.fetchall()
|
382 |
+
for product_id, quantity in cart_items:
|
383 |
+
sale_date = datetime.now(pytz.timezone('Asia/Bishkek')).strftime('%Y-%m-%d %H:%M:%S')
|
384 |
+
c.execute('''
|
385 |
+
INSERT INTO sales (product_id, quantity, sale_date)
|
386 |
+
VALUES (?, ?, ?)
|
387 |
+
''', (product_id, quantity, sale_date))
|
388 |
+
sales_details.append((product_id, quantity, sale_date))
|
389 |
+
|
390 |
+
c.execute('DELETE FROM cart')
|
391 |
+
conn.commit()
|
392 |
+
return sales_details
|
393 |
+
|
394 |
+
# Функция для генерации отчета за месяц
|
395 |
+
def generate_monthly_report():
|
396 |
+
start_date = (datetime.now().replace(day=1)).strftime('%Y-%m-%d')
|
397 |
+
end_date = (datetime.now() + timedelta(days=31)).replace(day=1).strftime('%Y-%m-%d')
|
398 |
+
|
399 |
+
c.execute('''
|
400 |
+
SELECT p.name, SUM(s.quantity) AS total_quantity, p.sale_price, SUM(s.quantity) * p.sale_price AS total_sales,
|
401 |
+
SUM(s.quantity) * (p.sale_price - p.purchase_price) AS profit
|
402 |
+
FROM sales s
|
403 |
+
JOIN products p ON s.product_id = p.id
|
404 |
+
WHERE s.sale_date BETWEEN ? AND ?
|
405 |
+
GROUP BY p.id
|
406 |
+
''', (start_date, end_date))
|
407 |
+
sales_data = c.fetchall()
|
408 |
+
|
409 |
+
# Расчёт общей суммы продаж и прибыли
|
410 |
+
total_sales = sum(item[3] for item in sales_data)
|
411 |
+
total_profit = sum(item[4] for item in sales_data)
|
412 |
+
|
413 |
+
return sales_data, total_sales, total_profit
|
414 |
+
|
415 |
+
# Функция для получения всех сделок
|
416 |
+
def get_all_sales():
|
417 |
+
c.execute('''
|
418 |
+
SELECT s.id, p.name, s.quantity, s.sale_date
|
419 |
+
FROM sales s
|
420 |
+
JOIN products p ON s.product_id = p.id
|
421 |
+
ORDER BY s.sale_date DESC
|
422 |
+
''')
|
423 |
+
sales_data = c.fetchall()
|
424 |
+
return sales_data
|
425 |
+
|
426 |
+
# Функция для получения всех подробностей сделок
|
427 |
+
def get_sale_details(sale_id):
|
428 |
+
c.execute('''
|
429 |
+
SELECT p.name, s.quantity, p.sale_price, s.sale_date
|
430 |
+
FROM sales s
|
431 |
+
JOIN products p ON s.product_id = p.id
|
432 |
+
WHERE s.id = ?
|
433 |
+
''', (sale_id,))
|
434 |
+
sale_details = c.fetchone()
|
435 |
+
return sale_details
|
436 |
+
|
437 |
+
# Страница регистрации
|
438 |
+
def register():
|
439 |
+
st.title('Регистрация')
|
440 |
+
username = st.text_input("Введите ваше имя пользователя")
|
441 |
+
admin_password = st.text_input("Введите пароль администратора", type="password")
|
442 |
+
|
443 |
+
if st.button("Зарегистрироваться"):
|
444 |
+
if admin_password == "1234":
|
445 |
+
token = generate_token()
|
446 |
+
try:
|
447 |
+
c.execute("INSERT INTO users (username, token) VALUES (?, ?)", (username, token))
|
448 |
+
conn.commit()
|
449 |
+
st.success(f"Регистрация успешна! Ваш токен: {token}")
|
450 |
+
except sqlite3.IntegrityError:
|
451 |
+
st.error("Это имя пользователя уже занято. Попробуйте другое.")
|
452 |
+
else:
|
453 |
+
st.error("Неверный пароль администратора!")
|
454 |
+
|
455 |
+
# Страница авторизации
|
456 |
+
def login():
|
457 |
+
st.title('Авторизация')
|
458 |
+
token_input = st.text_input("Введите ваш токен")
|
459 |
+
|
460 |
+
if st.button("Войти"):
|
461 |
+
user_id = get_user_id_by_token(token_input)
|
462 |
+
if user_id:
|
463 |
+
st.session_state.logged_in = True # Устанавливаем состояние авторизации
|
464 |
+
st.session_state.username = token_input
|
465 |
+
st.session_state.user_id = user_id
|
466 |
+
st.success("Добро пожаловать!")
|
467 |
+
else:
|
468 |
+
st.error("Неверный токен!")
|
469 |
+
|
470 |
+
# Форма добавления товара
|
471 |
+
def add_product():
|
472 |
+
st.title("Добавление товара")
|
473 |
+
product_name = st.text_input("Название товара").strip().lower() # Преобразование в нижний регистр
|
474 |
+
product_description = st.text_area("Описание товара")
|
475 |
+
purchase_price = st.number_input("Приходная цена", min_value=0.0, step=0.01) # Запрос приходной цены
|
476 |
+
sale_price = st.number_input("Отпускная цена", min_value=0.0, step=0.01) # Запрос отпускной цены
|
477 |
+
product_quantity = st.number_input("Количество на складе", min_value=0, step=1)
|
478 |
+
|
479 |
+
if st.button("Добавить товар"):
|
480 |
+
if product_name and sale_price:
|
481 |
+
c.execute("INSERT INTO products (name, description, purchase_price, sale_price, quantity_in_stock, user_id) VALUES (?, ?, ?, ?, ?, ?)",
|
482 |
+
(product_name, product_description, purchase_price, sale_price, product_quantity, st.session_state.user_id))
|
483 |
+
conn.commit()
|
484 |
+
st.success("Товар успешно добавлен!")
|
485 |
+
else:
|
486 |
+
st.error("Пожалуйста, введите все обязательные данные.")
|
487 |
+
|
488 |
+
# Форма редактирования и удаления товара
|
489 |
+
def edit_products():
|
490 |
+
st.title("Редактирование товара")
|
491 |
+
products = c.execute("SELECT id, name, description, purchase_price, sale_price, quantity_in_stock FROM products WHERE user_id=?",
|
492 |
+
(st.session_state.user_id,)).fetchall()
|
493 |
+
|
494 |
+
if products:
|
495 |
+
product_names = [p[1] for p in products]
|
496 |
+
product_id = st.selectbox("Выберите товар", product_names)
|
497 |
+
product = next(p for p in products if p[1] == product_id)
|
498 |
+
|
499 |
+
new_name = st.text_input("Новое название товара", product[1])
|
500 |
+
new_description = st.text_area("Новое описание товара", product[2])
|
501 |
+
new_purchase_price = st.number_input("Новая приходная цена", min_value=0.0, step=0.01, value=product[3])
|
502 |
+
new_sale_price = st.number_input("Новая отпускная цена", min_value=0.0, step=0.01, value=product[4])
|
503 |
+
new_quantity_in_stock = st.number_input("Новое количество на складе", min_value=0, step=1, value=product[5])
|
504 |
+
|
505 |
+
if st.button("Сохранить изменения"):
|
506 |
+
c.execute("UPDATE products SET name=?, description=?, purchase_price=?, sale_price=?, quantity_in_stock=? WHERE id=?",
|
507 |
+
(new_name, new_description, new_purchase_price, new_sale_price, new_quantity_in_stock, product[0]))
|
508 |
+
conn.commit()
|
509 |
+
st.success("Изменения успешно сохранены!")
|
510 |
+
|
511 |
+
if st.button("Удалить товар"):
|
512 |
+
c.execute("DELETE FROM products WHERE id=?", (product[0],))
|
513 |
+
c.execute("DELETE FROM cart WHERE product_id=?", (product[0],)) # Удаляем товар из корзины, если он там есть
|
514 |
+
conn.commit()
|
515 |
+
st.success("Товар успешно удалён!")
|
516 |
+
else:
|
517 |
+
st.info("Нет товаров для редактирования.")
|
518 |
+
|
519 |
+
# Форма для добавления и удаления товара в корзину с поиском по названию
|
520 |
+
def add_to_cart():
|
521 |
+
st.title("Отпуск товара")
|
522 |
+
products = c.execute("SELECT id, name, sale_price, quantity_in_stock FROM products WHERE user_id=?",
|
523 |
+
(st.session_state.user_id,)).fetchall()
|
524 |
+
|
525 |
+
if products:
|
526 |
+
for product in products:
|
527 |
+
cols = st.columns(4)
|
528 |
+
with cols[0]:
|
529 |
+
st.write(product[1]) # Название товара
|
530 |
+
|
531 |
+
with cols[1]:
|
532 |
+
quantity = st.number_input(f"Количество для '{product[1]}'", min_value=1, max_value=product[3], key=f"quantity_{product[0]}")
|
533 |
+
|
534 |
+
with cols[2]:
|
535 |
+
add_button = st.button(f"Добавить в корзину", key=f"add_{product[0]}")
|
536 |
+
|
537 |
+
if add_button:
|
538 |
+
if quantity <= product[3]:
|
539 |
+
# Обработка добавления товара в корзину
|
540 |
+
c.execute('''
|
541 |
+
INSERT INTO cart (product_id, quantity)
|
542 |
+
VALUES (?, ?)
|
543 |
+
ON CONFLICT(product_id)
|
544 |
+
DO UPDATE SET quantity = quantity + excluded.quantity
|
545 |
+
''', (product[0], quantity))
|
546 |
+
|
547 |
+
# Уменьшаем количество на складе
|
548 |
+
c.execute("UPDATE products SET quantity_in_stock = quantity_in_stock - ? WHERE id=?",
|
549 |
+
(quantity, product[0]))
|
550 |
+
conn.commit()
|
551 |
+
st.success(f"Товар '{product[1]}' успешно добавлен в корзину!")
|
552 |
+
else:
|
553 |
+
st.error(f"Недостаточное количество товара '{product[1]}' на складе!")
|
554 |
+
|
555 |
+
with cols[3]:
|
556 |
+
remove_button = st.button(f"Удалить из корзины", key=f"remove_{product[0]}")
|
557 |
+
|
558 |
+
if remove_button:
|
559 |
+
if quantity > 0:
|
560 |
+
# Обработка удаления товара из корзины
|
561 |
+
current_quantity = c.execute("SELECT quantity FROM cart WHERE product_id=?", (product[0],)).fetchone()
|
562 |
+
if current_quantity:
|
563 |
+
new_quantity = current_quantity[0] - quantity
|
564 |
+
if new_quantity > 0:
|
565 |
+
c.execute('''
|
566 |
+
INSERT INTO cart (product_id, quantity)
|
567 |
+
VALUES (?, ?)
|
568 |
+
ON CONFLICT(product_id)
|
569 |
+
DO UPDATE SET quantity = excluded.quantity
|
570 |
+
''', (product[0], new_quantity))
|
571 |
+
else:
|
572 |
+
c.execute("DELETE FROM cart WHERE product_id=?", (product[0],))
|
573 |
+
|
574 |
+
# Увеличиваем количество на складе
|
575 |
+
c.execute("UPDATE products SET quantity_in_stock = quantity_in_stock + ? WHERE id=?",
|
576 |
+
(quantity, product[0]))
|
577 |
+
conn.commit()
|
578 |
+
st.success(f"Товар '{product[1]}' успешно удалён из корзины!")
|
579 |
+
else:
|
580 |
+
st.error(f"Товар '{product[1]}' не найден в корзине.")
|
581 |
+
else:
|
582 |
+
st.error(f"Введите количество для удаления товара '{product[1]}'.")
|
583 |
+
|
584 |
+
# Отображение состояния корзины
|
585 |
+
st.subheader("Состояние корзины")
|
586 |
+
items, total_quantity, total_price = get_cart_summary(st.session_state.user_id)
|
587 |
+
|
588 |
+
if items:
|
589 |
+
# Создание DataFrame для таблицы
|
590 |
+
df = pd.DataFrame(items, columns=["Название", "Количество", "Цена за единицу", "Итого"])
|
591 |
+
st.dataframe(df.style.format({"Цена за единицу": "{:.2f}", "Итого": "{:.2f}"}), use_container_width=True)
|
592 |
+
|
593 |
+
st.write(f"Общее количество: {total_quantity}, Общая стоимость: {total_price:.2f}")
|
594 |
+
|
595 |
+
# Добавляем кнопку "Пробить" для оформления продажи
|
596 |
+
if st.button("Пробить"):
|
597 |
+
sales_details = record_sales()
|
598 |
+
if sales_details:
|
599 |
+
st.write("**Детали сделок:**")
|
600 |
+
sale_details_df = pd.DataFrame(sales_details, columns=["ID товара", "Количество", "Дата и время"])
|
601 |
+
st.dataframe(sale_details_df.style.format({"Дата и время": lambda x: pd.to_datetime(x).strftime('%d-%m-%Y %H:%M:%S')}), use_container_width=True)
|
602 |
+
st.success("Корзина успешно пробита! Все товары добавлены в отчет и корзина очищена.")
|
603 |
+
|
604 |
+
# Кнопка для отображения сделок
|
605 |
if st.button("Сделки"):
|
606 |
sales_data = get_all_sales()
|
607 |
if sales_data:
|