Spaces:
Sleeping
Sleeping
Create app.py
Browse files
app.py
ADDED
@@ -0,0 +1,361 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import streamlit as st
|
2 |
+
import pandas as pd
|
3 |
+
import sqlite3
|
4 |
+
import random
|
5 |
+
import string
|
6 |
+
from datetime import datetime, timedelta
|
7 |
+
import pytz
|
8 |
+
|
9 |
+
# Подключение к базе данных SQLite3
|
10 |
+
conn = sqlite3.connect('auth_system.db', check_same_thread=False)
|
11 |
+
c = conn.cursor()
|
12 |
+
|
13 |
+
# Создание таблиц, если они не существуют
|
14 |
+
c.execute('''
|
15 |
+
CREATE TABLE IF NOT EXISTS users (
|
16 |
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
17 |
+
username TEXT NOT NULL UNIQUE,
|
18 |
+
token TEXT NOT NULL
|
19 |
+
)
|
20 |
+
''')
|
21 |
+
conn.commit()
|
22 |
+
|
23 |
+
c.execute('''
|
24 |
+
CREATE TABLE IF NOT EXISTS products (
|
25 |
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
26 |
+
name TEXT NOT NULL,
|
27 |
+
description TEXT,
|
28 |
+
purchase_price REAL NOT NULL,
|
29 |
+
sale_price REAL NOT NULL,
|
30 |
+
quantity_in_stock INTEGER NOT NULL DEFAULT 0,
|
31 |
+
user_id INTEGER NOT NULL,
|
32 |
+
FOREIGN KEY(user_id) REFERENCES users(id)
|
33 |
+
)
|
34 |
+
''')
|
35 |
+
conn.commit()
|
36 |
+
|
37 |
+
c.execute('''
|
38 |
+
CREATE TABLE IF NOT EXISTS cart (
|
39 |
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
40 |
+
product_id INTEGER NOT NULL,
|
41 |
+
quantity INTEGER NOT NULL,
|
42 |
+
UNIQUE(product_id)
|
43 |
+
)
|
44 |
+
''')
|
45 |
+
conn.commit()
|
46 |
+
|
47 |
+
c.execute('''
|
48 |
+
CREATE TABLE IF NOT EXISTS sales (
|
49 |
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
50 |
+
product_id INTEGER NOT NULL,
|
51 |
+
quantity INTEGER NOT NULL,
|
52 |
+
sale_date DATE NOT NULL,
|
53 |
+
FOREIGN KEY(product_id) REFERENCES products(id)
|
54 |
+
)
|
55 |
+
''')
|
56 |
+
conn.commit()
|
57 |
+
|
58 |
+
# Функция для генерации случайного 13-значного токена
|
59 |
+
def generate_token():
|
60 |
+
return ''.join(random.choices(string.ascii_letters + string.digits, k=13))
|
61 |
+
|
62 |
+
# Функция для получения идентификатора пользователя по токену
|
63 |
+
def get_user_id_by_token(token):
|
64 |
+
c.execute("SELECT id FROM users WHERE token=?", (token,))
|
65 |
+
user = c.fetchone()
|
66 |
+
return user[0] if user else None
|
67 |
+
|
68 |
+
# Функция для получения товаров из корзины
|
69 |
+
def get_cart_summary(user_id):
|
70 |
+
c.execute('''
|
71 |
+
SELECT p.name, c.quantity, p.sale_price, c.quantity * p.sale_price
|
72 |
+
FROM cart c
|
73 |
+
JOIN products p ON c.product_id = p.id
|
74 |
+
WHERE p.user_id = ?
|
75 |
+
''', (user_id,))
|
76 |
+
items = c.fetchall()
|
77 |
+
total_quantity = sum(item[1] for item in items)
|
78 |
+
total_price = sum(item[3] for item in items)
|
79 |
+
return items, total_quantity, total_price
|
80 |
+
|
81 |
+
# Функция для добавления продажи в отчет
|
82 |
+
def record_sales():
|
83 |
+
c.execute('''
|
84 |
+
SELECT product_id, quantity FROM cart
|
85 |
+
''')
|
86 |
+
cart_items = c.fetchall()
|
87 |
+
for product_id, quantity in cart_items:
|
88 |
+
c.execute('''
|
89 |
+
INSERT INTO sales (product_id, quantity, sale_date)
|
90 |
+
VALUES (?, ?, ?)
|
91 |
+
''', (product_id, quantity, datetime.now(pytz.timezone('Asia/Bishkek')).strftime('%Y-%m-%d %H:%M:%S')))
|
92 |
+
c.execute('DELETE FROM cart')
|
93 |
+
conn.commit()
|
94 |
+
|
95 |
+
# Функция для генерации отчета за месяц
|
96 |
+
def generate_monthly_report():
|
97 |
+
start_date = (datetime.now().replace(day=1)).strftime('%Y-%m-%d')
|
98 |
+
end_date = (datetime.now() + timedelta(days=31)).replace(day=1).strftime('%Y-%m-%d')
|
99 |
+
|
100 |
+
c.execute('''
|
101 |
+
SELECT p.name, SUM(s.quantity) AS total_quantity, p.sale_price, SUM(s.quantity) * p.sale_price AS total_sales,
|
102 |
+
SUM(s.quantity) * (p.sale_price - p.purchase_price) AS profit
|
103 |
+
FROM sales s
|
104 |
+
JOIN products p ON s.product_id = p.id
|
105 |
+
WHERE s.sale_date BETWEEN ? AND ?
|
106 |
+
GROUP BY p.id
|
107 |
+
''', (start_date, end_date))
|
108 |
+
sales_data = c.fetchall()
|
109 |
+
|
110 |
+
# Расчёт общей суммы продаж и прибыли
|
111 |
+
total_sales = sum(item[3] for item in sales_data)
|
112 |
+
total_profit = sum(item[4] for item in sales_data)
|
113 |
+
|
114 |
+
return sales_data, total_sales, total_profit
|
115 |
+
|
116 |
+
# Функция для получения всех сделок
|
117 |
+
def get_all_sales():
|
118 |
+
c.execute('''
|
119 |
+
SELECT s.id, p.name, s.quantity, s.sale_date
|
120 |
+
FROM sales s
|
121 |
+
JOIN products p ON s.product_id = p.id
|
122 |
+
ORDER BY s.sale_date DESC
|
123 |
+
''')
|
124 |
+
sales_data = c.fetchall()
|
125 |
+
return sales_data
|
126 |
+
|
127 |
+
# Страница регистрации
|
128 |
+
def register():
|
129 |
+
st.title('Регистрация')
|
130 |
+
username = st.text_input("Введите ваше имя пользователя")
|
131 |
+
admin_password = st.text_input("Введите пароль администратора", type="password")
|
132 |
+
|
133 |
+
if st.button("Зарегистрироваться"):
|
134 |
+
if admin_password == "1234":
|
135 |
+
token = generate_token()
|
136 |
+
try:
|
137 |
+
c.execute("INSERT INTO users (username, token) VALUES (?, ?)", (username, token))
|
138 |
+
conn.commit()
|
139 |
+
st.success(f"Регистрация успешна! Ваш токен: {token}")
|
140 |
+
except sqlite3.IntegrityError:
|
141 |
+
st.error("Это имя пользователя уже занято. Попробуйте другое.")
|
142 |
+
else:
|
143 |
+
st.error("Неверный пароль администратора!")
|
144 |
+
|
145 |
+
# Страница авторизации
|
146 |
+
def login():
|
147 |
+
st.title('Авторизация')
|
148 |
+
token_input = st.text_input("Введите ваш токен")
|
149 |
+
|
150 |
+
if st.button("Войти"):
|
151 |
+
user_id = get_user_id_by_token(token_input)
|
152 |
+
if user_id:
|
153 |
+
st.session_state.logged_in = True # Устанавливаем состояние авторизации
|
154 |
+
st.session_state.username = token_input
|
155 |
+
st.session_state.user_id = user_id
|
156 |
+
st.success("Добро пожаловать!")
|
157 |
+
else:
|
158 |
+
st.error("Неверный токен!")
|
159 |
+
|
160 |
+
# Форма добавления товара
|
161 |
+
def add_product():
|
162 |
+
st.title("Добавление товара")
|
163 |
+
product_name = st.text_input("Название товара").strip().lower() # Преобразование в нижний регистр
|
164 |
+
product_description = st.text_area("Описание товара")
|
165 |
+
purchase_price = st.number_input("Приходная цена", min_value=0.0, step=0.01) # Запрос приходной цены
|
166 |
+
sale_price = st.number_input("Отпускная цена", min_value=0.0, step=0.01) # Запрос отпускной цены
|
167 |
+
product_quantity = st.number_input("Количество на складе", min_value=0, step=1)
|
168 |
+
|
169 |
+
if st.button("Добавить товар"):
|
170 |
+
if product_name and sale_price:
|
171 |
+
c.execute("INSERT INTO products (name, description, purchase_price, sale_price, quantity_in_stock, user_id) VALUES (?, ?, ?, ?, ?, ?)",
|
172 |
+
(product_name, product_description, purchase_price, sale_price, product_quantity, st.session_state.user_id))
|
173 |
+
conn.commit()
|
174 |
+
st.success("Товар успешно добавлен!")
|
175 |
+
else:
|
176 |
+
st.error("Пожалуйста, введите все обязательные данные.")
|
177 |
+
|
178 |
+
# Форма редактирования и удаления товара
|
179 |
+
def edit_products():
|
180 |
+
st.title("Редактирование товара")
|
181 |
+
products = c.execute("SELECT id, name, description, purchase_price, sale_price, quantity_in_stock FROM products WHERE user_id=?",
|
182 |
+
(st.session_state.user_id,)).fetchall()
|
183 |
+
|
184 |
+
if products:
|
185 |
+
product_names = [p[1] for p in products]
|
186 |
+
product_id = st.selectbox("Выберите товар", product_names)
|
187 |
+
product = next(p for p in products if p[1] == product_id)
|
188 |
+
|
189 |
+
new_name = st.text_input("Новое название товара", product[1])
|
190 |
+
new_description = st.text_area("Новое описание товара", product[2])
|
191 |
+
new_purchase_price = st.number_input("Новая приходная цена", min_value=0.0, step=0.01, value=product[3])
|
192 |
+
new_sale_price = st.number_input("Новая отпускная цена", min_value=0.0, step=0.01, value=product[4])
|
193 |
+
new_quantity_in_stock = st.number_input("Новое количество на складе", min_value=0, step=1, value=product[5])
|
194 |
+
|
195 |
+
if st.button("Сохранить изменения"):
|
196 |
+
c.execute("UPDATE products SET name=?, description=?, purchase_price=?, sale_price=?, quantity_in_stock=? WHERE id=?",
|
197 |
+
(new_name, new_description, new_purchase_price, new_sale_price, new_quantity_in_stock, product[0]))
|
198 |
+
conn.commit()
|
199 |
+
st.success("Изменения успешно сохранены!")
|
200 |
+
|
201 |
+
if st.button("Удалить товар"):
|
202 |
+
c.execute("DELETE FROM products WHERE id=?", (product[0],))
|
203 |
+
c.execute("DELETE FROM cart WHERE product_id=?", (product[0],)) # Удаляем товар из корзины, если он там есть
|
204 |
+
conn.commit()
|
205 |
+
st.success("Товар успешно удалён!")
|
206 |
+
else:
|
207 |
+
st.info("Нет товаров для редактирования.")
|
208 |
+
|
209 |
+
# Форма для добавления и удаления товара в корзину с поиском
|
210 |
+
def add_to_cart():
|
211 |
+
st.title("Отпуск товара")
|
212 |
+
|
213 |
+
search_term = st.text_input("Поиск товара").strip().lower() # Преобразуем ввод в нижний регистр
|
214 |
+
|
215 |
+
# Поиск товаров по ключевому слову (без учета регистра)
|
216 |
+
c.execute("SELECT id, name, sale_price, quantity_in_stock FROM products WHERE user_id=? AND LOWER(name) LIKE ?",
|
217 |
+
(st.session_state.user_id, f"%{search_term}%"))
|
218 |
+
products = c.fetchall()
|
219 |
+
|
220 |
+
if products:
|
221 |
+
product_list = []
|
222 |
+
for product in products:
|
223 |
+
cols = st.columns(4)
|
224 |
+
with cols[0]:
|
225 |
+
st.write(product[1]) # Название товара
|
226 |
+
|
227 |
+
with cols[1]:
|
228 |
+
quantity = st.number_input(f"Количество для '{product[1]}'", min_value=1, max_value=product[3], key=f"quantity_{product[0]}")
|
229 |
+
|
230 |
+
with cols[2]:
|
231 |
+
add_button = st.button(f"Добавить в корзину", key=f"add_{product[0]}")
|
232 |
+
|
233 |
+
if add_button:
|
234 |
+
if quantity <= product[3]:
|
235 |
+
# Обработка добавления товара в корзину
|
236 |
+
c.execute('''
|
237 |
+
INSERT INTO cart (product_id, quantity)
|
238 |
+
VALUES (?, ?)
|
239 |
+
ON CONFLICT(product_id)
|
240 |
+
DO UPDATE SET quantity = quantity + excluded.quantity
|
241 |
+
''', (product[0], quantity))
|
242 |
+
|
243 |
+
# Уменьшаем количество на складе
|
244 |
+
c.execute("UPDATE products SET quantity_in_stock = quantity_in_stock - ? WHERE id=?",
|
245 |
+
(quantity, product[0]))
|
246 |
+
conn.commit()
|
247 |
+
st.success(f"Товар '{product[1]}' успешно добавлен в корзину!")
|
248 |
+
else:
|
249 |
+
st.error(f"Недостаточное количество товара '{product[1]}' на складе!")
|
250 |
+
|
251 |
+
with cols[3]:
|
252 |
+
remove_button = st.button(f"Удалить из корзины", key=f"remove_{product[0]}")
|
253 |
+
|
254 |
+
if remove_button:
|
255 |
+
if quantity > 0:
|
256 |
+
# Обработка удаления товара из корзины
|
257 |
+
current_quantity = c.execute("SELECT quantity FROM cart WHERE product_id=?", (product[0],)).fetchone()
|
258 |
+
if current_quantity:
|
259 |
+
new_quantity = current_quantity[0] - quantity
|
260 |
+
if new_quantity > 0:
|
261 |
+
c.execute('''
|
262 |
+
INSERT INTO cart (product_id, quantity)
|
263 |
+
VALUES (?, ?)
|
264 |
+
ON CONFLICT(product_id)
|
265 |
+
DO UPDATE SET quantity = excluded.quantity
|
266 |
+
''', (product[0], new_quantity))
|
267 |
+
else:
|
268 |
+
c.execute("DELETE FROM cart WHERE product_id=?", (product[0],))
|
269 |
+
|
270 |
+
# Увеличиваем количество на складе
|
271 |
+
c.execute("UPDATE products SET quantity_in_stock = quantity_in_stock + ? WHERE id=?",
|
272 |
+
(quantity, product[0]))
|
273 |
+
conn.commit()
|
274 |
+
st.success(f"Товар '{product[1]}' успешно удалён из корзины!")
|
275 |
+
else:
|
276 |
+
st.error(f"Товар '{product[1]}' не найден в корзине.")
|
277 |
+
else:
|
278 |
+
st.error(f"Введите количество для удаления товара '{product[1]}'.")
|
279 |
+
|
280 |
+
# Отображение состояния корзины
|
281 |
+
st.subheader("Состояние корзины")
|
282 |
+
items, total_quantity, total_price = get_cart_summary(st.session_state.user_id)
|
283 |
+
|
284 |
+
if items:
|
285 |
+
# Создание DataFrame для таблицы
|
286 |
+
df = pd.DataFrame(items, columns=["Название", "Количество", "Цена за единицу", "Итого"])
|
287 |
+
st.dataframe(df.style.format({"Цена за единицу": "{:.2f}", "Итого": "{:.2f}"}), use_container_width=True)
|
288 |
+
|
289 |
+
st.write(f"Общее количество: {total_quantity}, Общая стоимость: {total_price:.2f}")
|
290 |
+
|
291 |
+
# Добавляем кнопку "Пробить" для оформления продажи
|
292 |
+
if st.button("Пробить"):
|
293 |
+
record_sales()
|
294 |
+
st.success("Корзина успешно пробита! Все товары добавлены в отчет и корзина очищена.")
|
295 |
+
|
296 |
+
# Кнопка для отображения сделок
|
297 |
+
if st.button("Сделки"):
|
298 |
+
sales_data = get_all_sales()
|
299 |
+
if sales_data:
|
300 |
+
df_sales = pd.DataFrame(sales_data, columns=["ID", "Название товара", "Количество", "Дата и время"])
|
301 |
+
st.dataframe(df_sales.style.format({"Дата и время": lambda x: pd.to_datetime(x).strftime('%d-%m-%Y %H:%M:%S')}), use_container_width=True)
|
302 |
+
else:
|
303 |
+
st.info("Нет сделок для отображения.")
|
304 |
+
else:
|
305 |
+
st.info("Корзина пуста.")
|
306 |
+
|
307 |
+
# Страница отчета о продажах за месяц
|
308 |
+
def monthly_report():
|
309 |
+
st.title("Отчет о продажах за месяц")
|
310 |
+
sales_data, total_sales, total_profit = generate_monthly_report()
|
311 |
+
if sales_data:
|
312 |
+
# Создание DataFrame ��ля отчета
|
313 |
+
df = pd.DataFrame(sales_data, columns=["Название", "Общее количество", "Отпускная цена", "Общие продажи", "Прибыль"])
|
314 |
+
st.write(f"**Отчет за {datetime.now().strftime('%B %Y')}**")
|
315 |
+
st.dataframe(df.style.format({"Отпускная цена": "{:.2f}", "Общие продажи": "{:.2f}", "Прибыль": "{:.2f}"}), use_container_width=True)
|
316 |
+
|
317 |
+
st.write(f"**Общая сумма продаж**: {total_sales:.2f}")
|
318 |
+
st.write(f"**Общая сумма прибыли**: {total_profit:.2f}")
|
319 |
+
|
320 |
+
# Кнопка для отображения сделок
|
321 |
+
if st.button("Сделки"):
|
322 |
+
sales_data = get_all_sales()
|
323 |
+
if sales_data:
|
324 |
+
df_sales = pd.DataFrame(sales_data, columns=["ID", "Название товара", "Количество", "Дата и время"])
|
325 |
+
st.dataframe(df_sales.style.format({"Дата и время": lambda x: pd.to_datetime(x).strftime('%d-%m-%Y %H:%M:%S')}), use_container_width=True)
|
326 |
+
else:
|
327 |
+
st.info("Нет сделок для отображения.")
|
328 |
+
else:
|
329 |
+
st.info("Нет данных о продажах за этот месяц.")
|
330 |
+
|
331 |
+
# Главная функция приложения
|
332 |
+
def main():
|
333 |
+
if 'logged_in' not in st.session_state:
|
334 |
+
st.session_state.logged_in = False
|
335 |
+
|
336 |
+
if st.session_state.logged_in:
|
337 |
+
st.sidebar.title(f"Привет, {st.session_state.username}!")
|
338 |
+
option = st.sidebar.selectbox("Выберите действие", ["Добавить товар", "Отпуск товара", "Редактировать товары", "Отчет за месяц", "Выйти"])
|
339 |
+
|
340 |
+
if option == "Добавить товар":
|
341 |
+
add_product()
|
342 |
+
elif option == "Отпуск товара":
|
343 |
+
add_to_cart()
|
344 |
+
elif option == "Редактировать товары":
|
345 |
+
edit_products()
|
346 |
+
elif option == "Отчет за месяц":
|
347 |
+
monthly_report()
|
348 |
+
elif option == "Выйти":
|
349 |
+
st.session_state.logged_in = False
|
350 |
+
st.session_state.username = None
|
351 |
+
st.session_state.user_id = None
|
352 |
+
st.success("Вы вышли из системы!")
|
353 |
+
else:
|
354 |
+
page = st.sidebar.selectbox("Выберите страницу", ["Регистрация", "Авторизация"])
|
355 |
+
if page == "Регистрация":
|
356 |
+
register()
|
357 |
+
elif page == "Авторизация":
|
358 |
+
login()
|
359 |
+
|
360 |
+
if __name__ == "__main__":
|
361 |
+
main()
|