diff --git a/App/Auth/__init__.py b/App/Auth/__init__.py index 3e426da..402607f 100644 --- a/App/Auth/__init__.py +++ b/App/Auth/__init__.py @@ -12,6 +12,7 @@ def auth(): auth_data = auth_model(data) if auth_data.status: session.update({ + 'user_id': auth_data.result[0]['user_ID'], 'login': auth_data.result[0]['login'], 'access_user': data['access'], 'role': auth_data.result[0]['user_role'], diff --git a/App/Auth/sql/auth.sql b/App/Auth/sql/auth.sql index 4fb90bf..56c956a 100644 --- a/App/Auth/sql/auth.sql +++ b/App/Auth/sql/auth.sql @@ -1,4 +1,4 @@ -SELECT login, user_role FROM $access +SELECT user_ID, login, user_role FROM $access WHERE login = '$login' AND password = '$password' LIMIT 1; \ No newline at end of file diff --git a/App/Auth/templates/auth.html b/App/Auth/templates/auth.html index 986bdaa..f5f4cb8 100644 --- a/App/Auth/templates/auth.html +++ b/App/Auth/templates/auth.html @@ -1,5 +1,5 @@ - +
diff --git a/App/Waybill/__init__.py b/App/Waybill/__init__.py index 66fdc1b..55276bc 100644 --- a/App/Waybill/__init__.py +++ b/App/Waybill/__init__.py @@ -1,11 +1,47 @@ -from flask import request, Blueprint, render_template, session, current_app, redirect, url_for +from flask import request, Blueprint, render_template, session, redirect, url_for from checker import check_auth -waybill_bp = Blueprint('waybill_bp', __name__, template_folder='templates') +from .model import index_waybill, form_waybill, clear, button_click, transaction_order_model +from datetime import date -@waybill_bp.route('/', methods=['GET', 'POST']) +waybill_bp = Blueprint('waybill_bp', __name__, template_folder='templates', static_folder='static') + +@waybill_bp.route('/', methods=['GET']) @check_auth -def waybill(): - if request.method == 'GET': - return render_template('waybill.html') - print(request.form) - return 'OK' \ No newline at end of file +def index(): + lst = index_waybill() + if lst is not None: + waybill = form_waybill() + return render_template('waybill.html', items=lst, waybill=waybill) + else: + return render_template('error.html', error_message="Ошибка в подключении к СУБД") + +@waybill_bp.route('/', methods=['POST']) +@check_auth +def waybill_main(): + status = button_click(request) + if status: + return redirect(url_for('waybill_bp.index')) + else: + return render_template("error.html", error_message="Товар не был добавлен в корзину") + +@waybill_bp.route('/clear', methods=['GET']) +@check_auth +def clear_waybill(): + clear() + return redirect(url_for('waybill_bp.index')) + +@waybill_bp.route('/save_order') +@check_auth +def save_order(): + if not session.get('waybill',{}): + return redirect(url_for('waybill_bp.index')) + + user_id = session.get('user_id',"") + current_date = date.today().strftime("%Y-%m-%d") + ' ' + date.today().strftime("%H:%M:%S") + + result = transaction_order_model(user_id, current_date) + if result.status: + print("Order success") + return render_template("order_finish.html", order_id = result.result[0]) + else: + return render_template("error.html", error_message=result.error_message) \ No newline at end of file diff --git a/App/Waybill/cache/__init__.py b/App/Waybill/cache/__init__.py new file mode 100644 index 0000000..d902592 --- /dev/null +++ b/App/Waybill/cache/__init__.py @@ -0,0 +1,42 @@ +import json +from redis import Redis, ConnectionError, DataError + +class RedisCache: + def __init__(self, config: dict): + self.config = config + self.conn = self._connect() + + def _connect(self): + try: + conn = Redis(**self.config) + return conn + except DataError as err: + print(err) + return None + + def set_value(self, name: str, value_dict: dict, ttl: int): + try: + value_js = json.dumps(value_dict) + self.conn.set(name=name, value=value_js) + if ttl > 0: + self.conn.expire(name, ttl) + return True + except ConnectionError as err: + print(err) + return False + + def get_value(self, name: str): + try: + value_js = self.conn.get(name) + except: + return None + value_js = self.conn.get(name) + if value_js: + value_dict = json.loads(value_js) + return value_dict + else: + return None + + def __exit__(self, exc_type, exc_val, exc_tb): + self.conn.close() + return True \ No newline at end of file diff --git a/App/Waybill/cache/wrapper.py b/App/Waybill/cache/wrapper.py new file mode 100644 index 0000000..ff38803 --- /dev/null +++ b/App/Waybill/cache/wrapper.py @@ -0,0 +1,26 @@ +from functools import wraps +from Waybill.cache import RedisCache + +def fetch_from_cache(cache_name: str, cache_config: dict): + cache_conn = RedisCache(cache_config['redis']) + ttl = cache_config['ttl'] + def decorator(f): + # как оно работает + # лезем в кэш и смотрим, есть ли там что-то + # если есть, то возвращаем кэшированную информацию + # если нет, то запускаем декорируемую функцию + # достаём оттуда информацию + # заносим её в кэш + # возвращаем кэшированную информацию + @wraps(f) + def wrapper(*args, **kwargs): + cached_value = cache_conn.get_value(cache_name) + print("cached_value=", cached_value) + if cached_value: + return cached_value + response = f(*args, **kwargs) + print("response=", response) + cache_conn.set_value(cache_name,response,ttl) + return response + return wrapper + return decorator \ No newline at end of file diff --git a/App/Waybill/db/DBconnect.py b/App/Waybill/db/DBconnect.py new file mode 100644 index 0000000..b335654 --- /dev/null +++ b/App/Waybill/db/DBconnect.py @@ -0,0 +1,34 @@ +import pymysql +from pymysql.err import * +class DBContextManager: + def __init__(self, db_config : dict): + self.db_config = db_config + self.connection = None + self.cursor = None + + def __enter__(self): + try: + self.connection = pymysql.connect( + host=self.db_config['host'], + port=self.db_config['port'], + user=self.db_config['user'], + password=self.db_config['password'], + db=self.db_config['db'] + ) + self.cursor = self.connection.cursor() + return self.cursor + except (OperationalError, KeyError) as err: + print(err.args) + return None + + def __exit__(self, exc_type, exc_val, exc_tb): + if self.connection and self.cursor: + if exc_type: + print(exc_type, '\n', exc_val) + self.connection.rollback() + else: + self.connection.commit() + self.cursor.close() + self.connection.close() + return True + \ No newline at end of file diff --git a/App/Waybill/db/__init__.py b/App/Waybill/db/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/App/Waybill/db/sql_provider.py b/App/Waybill/db/sql_provider.py new file mode 100644 index 0000000..a3a1855 --- /dev/null +++ b/App/Waybill/db/sql_provider.py @@ -0,0 +1,14 @@ +import os +from string import Template + +class SQLProvider: + def __init__(self, file_path): + self.scripts = {} + for file in os.listdir(file_path): + _sql = open(f'{file_path}/{file}').read() + self.scripts[file] = Template(_sql) + + def get(self, name, params) -> dict: + if name not in self.scripts: + raise ValueError(f'SQL template {name} not found') + return self.scripts[name].substitute(**params) \ No newline at end of file diff --git a/App/Waybill/db/work.py b/App/Waybill/db/work.py new file mode 100644 index 0000000..5a7f039 --- /dev/null +++ b/App/Waybill/db/work.py @@ -0,0 +1,16 @@ +from .DBconnect import DBContextManager + +def select_list(db_config, sql) -> list: + with DBContextManager(db_config) as cursor: + if cursor is None: + raise ValueError("Cursor not created") + else: + cursor.execute(sql) + result = cursor.fetchall() + schema = [item[0] for item in cursor.description] + lst = [dict(zip(schema, row)) for row in result] + return lst + +def transaction(cursor, sql): + cursor.execute(sql) + return True \ No newline at end of file diff --git a/App/Waybill/model.py b/App/Waybill/model.py new file mode 100644 index 0000000..01162b1 --- /dev/null +++ b/App/Waybill/model.py @@ -0,0 +1,124 @@ +from .db.sql_provider import SQLProvider +from .db.work import select_list, transaction +from .db.DBconnect import DBContextManager + +from flask import current_app, session +from dataclasses import dataclass +from datetime import date +from .cache.wrapper import fetch_from_cache +import os + +@dataclass +class InfoRespronse: + result: tuple + error_message: str + status: bool + +sql_provider = SQLProvider(os.path.join(os.path.dirname(__file__), 'sql')) + +def clear(): + if session.get('waybill',{}): + session.pop('waybill') + +def form_waybill() -> list: + current_waybill = session.get('waybill',{}) + waybill = [] + for k,v in current_waybill.items(): + _sql = sql_provider.get('one_good.sql', dict(work_id=k)) + product = select_list(current_app.config['db_config'], _sql)[0] + product['amount'] = v + waybill.append(product) + return waybill + +def index_waybill() -> list: + db_config = current_app.config['db_config'] + cache_config = current_app.config['cache_config'] + + cache_select = fetch_from_cache('items_cached', cache_config)(select_list) + _sql = sql_provider.get('goods.sql', {}) + products = cache_select(db_config, _sql) + + return products + +def button_click(request): + db_config = current_app.config['db_config'] + data = dict(work_id=int(request.form['product_display'])) + + _sql = sql_provider.get('one_good.sql', data) + result = select_list(db_config, _sql) + if result == None: + return False + + product = result[0] + + if request.form.get('add'): + if 'waybill' not in session: + session['waybill'] = dict() + session['total'] = '0' + + if str(product['work_id']) in session['waybill']: + pr_id = product['work_id'] + price = product['price'] + amount = int(session['waybill'][str(pr_id)]) + session['waybill'][str(pr_id)] = str(amount+1) + session['total'] = str(int(session['total']) + price) + session.modified = True + else: + print("NEW WORKPIECE") + pr_id = product['work_id'] + price = product['price'] + session['waybill'][str(pr_id)] = '1' + session['total'] = str(int(session['total']) + price) + print(session['waybill']) + session.modified = True + + # elif request.form.get('product_display_plus'): + # # increasing count in waybill + + # amount = int(session['waybill'][str(product['work_id'])]) + # session['waybill'][str(product['work_id'])] = str(amount + 1) + # session.modified = True + + elif request.form.get('product_display_minus'): + # decreasing count in waybill + + amount = int(session['waybill'][str(product['work_id'])]) + if amount == 1: + session['waybill'].pop(str(product['work_id'])) + else: + session['waybill'][str(product['work_id'])] = str(amount-1) + session['total'] = str(int(session['total']) - product['price']) + session.modified = True + return True + +def transaction_order_model(user_id: int, current_date: date): + + db_config = current_app.config['db_config'] + waybill = session.get('waybill',{}) + total = session.get('total', 0) + + # Чтобы всё это шло как одна транзакция + with DBContextManager(db_config) as cursor: + + data = dict(e_user_id=user_id, e_order_date=current_date, e_total=total) + + _sql = sql_provider.get('create_order.sql', data) + try: + cursor.execute(_sql) + except: + return InfoRespronse((), error_message="Заказ не был создан", status=False) + + order_id = cursor.lastrowid + for key, value in waybill.items(): + _sql = sql_provider.get('insert_order_line.sql', + dict(e_order_id = order_id, + e_prod_id = int(key), + e_amount = int(value))) + try: + cursor.execute(_sql) + except: + return InfoRespronse((), error_message="Заказ не был создан", status=False) + + result = tuple([order_id]) + clear() + return InfoRespronse(result, error_message="", status=True) \ No newline at end of file diff --git a/App/Waybill/sql/create_order.sql b/App/Waybill/sql/create_order.sql new file mode 100644 index 0000000..0b5e11c --- /dev/null +++ b/App/Waybill/sql/create_order.sql @@ -0,0 +1 @@ +INSERT INTO `waybill` (`user_id`,`waybill_date`, `total`) VALUES ($e_user_id, '$e_order_date', $e_total); \ No newline at end of file diff --git a/App/Waybill/sql/goods.sql b/App/Waybill/sql/goods.sql new file mode 100644 index 0000000..9a18e9b --- /dev/null +++ b/App/Waybill/sql/goods.sql @@ -0,0 +1 @@ +SELECT work_id, name, price, material, count FROM workpiece \ No newline at end of file diff --git a/App/Waybill/sql/insert_order_line.sql b/App/Waybill/sql/insert_order_line.sql new file mode 100644 index 0000000..26091a8 --- /dev/null +++ b/App/Waybill/sql/insert_order_line.sql @@ -0,0 +1,2 @@ +INSERT INTO `waybill_lines` VALUES ($e_order_id, $e_prod_id, +(SELECT price FROM workpiece WHERE work_id = $e_prod_id), $e_amount); \ No newline at end of file diff --git a/App/Waybill/sql/one_good.sql b/App/Waybill/sql/one_good.sql new file mode 100644 index 0000000..d81e194 --- /dev/null +++ b/App/Waybill/sql/one_good.sql @@ -0,0 +1,3 @@ +SELECT `work_id`, `name`, `price`, `weight` +FROM `workpiece` +WHERE `work_id` = $work_id; \ No newline at end of file diff --git a/App/Waybill/static/css/waybill.css b/App/Waybill/static/css/waybill.css new file mode 100644 index 0000000..b7d39da --- /dev/null +++ b/App/Waybill/static/css/waybill.css @@ -0,0 +1,10 @@ +.card { + background: #fff; + border: 1px solid #ddd; + color: black; + border-radius: 10px; + width: 250px; + padding: 15px; + margin-bottom: 15px; + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); +} \ No newline at end of file diff --git a/App/Waybill/templates/card.html b/App/Waybill/templates/card.html new file mode 100644 index 0000000..b6248d6 --- /dev/null +++ b/App/Waybill/templates/card.html @@ -0,0 +1,24 @@ +{% macro render_item(item, show_amount = False, show_form = False) %} + +Цена: {{ item['price'] }} ₽
+ {% if show_amount %} + Количество: {{item['amount']}}