Migrating Shoppinglist Module to new schema
This commit is contained in:
parent
1018414200
commit
ce8e63b596
@ -3,14 +3,6 @@ import config
|
|||||||
from application import postsqldb
|
from application import postsqldb
|
||||||
|
|
||||||
def request_receipt_id(conn, site_name):
|
def request_receipt_id(conn, site_name):
|
||||||
"""gets the next id for receipts_id, currently returns a 8 digit number
|
|
||||||
|
|
||||||
Args:
|
|
||||||
site (str): site to get the next id for
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
json: receipt_id, message, error keys
|
|
||||||
"""
|
|
||||||
next_receipt_id = None
|
next_receipt_id = None
|
||||||
sql = f"SELECT receipt_id FROM {site_name}_receipts ORDER BY id DESC LIMIT 1;"
|
sql = f"SELECT receipt_id FROM {site_name}_receipts ORDER BY id DESC LIMIT 1;"
|
||||||
try:
|
try:
|
||||||
@ -34,17 +26,6 @@ def request_receipt_id(conn, site_name):
|
|||||||
return next_receipt_id
|
return next_receipt_id
|
||||||
|
|
||||||
def selectItemLocationsTuple(site_name, payload, convert=True):
|
def selectItemLocationsTuple(site_name, payload, convert=True):
|
||||||
"""select a single tuple from ItemLocations table for site_name
|
|
||||||
|
|
||||||
Args:
|
|
||||||
conn (_T_connector@connect):
|
|
||||||
site_name (str):
|
|
||||||
payload (tuple): [item_id, location_id]
|
|
||||||
convert (bool): defaults to False, used to determine return of tuple/dict
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
tuple: the row that was returned from the table
|
|
||||||
"""
|
|
||||||
item_locations = ()
|
item_locations = ()
|
||||||
database_config = config.config()
|
database_config = config.config()
|
||||||
select_item_location_sql = f"SELECT * FROM {site_name}_item_locations WHERE part_id = %s AND location_id = %s;"
|
select_item_location_sql = f"SELECT * FROM {site_name}_item_locations WHERE part_id = %s AND location_id = %s;"
|
||||||
@ -62,17 +43,6 @@ def selectItemLocationsTuple(site_name, payload, convert=True):
|
|||||||
return error
|
return error
|
||||||
|
|
||||||
def selectCostLayersTuple(site_name, payload, convert=True):
|
def selectCostLayersTuple(site_name, payload, convert=True):
|
||||||
"""select a single or series of cost layers from the database for site_name
|
|
||||||
|
|
||||||
Args:
|
|
||||||
conn (_T_connector@connect):
|
|
||||||
site_name (str):
|
|
||||||
payload (tuple): (item_locations_id, )
|
|
||||||
convert (bool): defaults to False, used for determining return as tuple/dict
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
list: list of tuples/dict from the cost_layers table for site_name
|
|
||||||
"""
|
|
||||||
cost_layers = ()
|
cost_layers = ()
|
||||||
database_config = config.config()
|
database_config = config.config()
|
||||||
select_cost_layers_sql = f"SELECT cl.* FROM {site_name}_item_locations il JOIN {site_name}_cost_layers cl ON cl.id = ANY(il.cost_layers) where il.id=%s;"
|
select_cost_layers_sql = f"SELECT cl.* FROM {site_name}_item_locations il JOIN {site_name}_cost_layers cl ON cl.id = ANY(il.cost_layers) where il.id=%s;"
|
||||||
@ -118,17 +88,6 @@ def selectLocationsTuple(site, payload, convert=True, conn=None):
|
|||||||
raise postsqldb.DatabaseError(error, payload, sql)
|
raise postsqldb.DatabaseError(error, payload, sql)
|
||||||
|
|
||||||
def selectItemLocationsTuple(site_name, payload, convert=True, conn=None):
|
def selectItemLocationsTuple(site_name, payload, convert=True, conn=None):
|
||||||
"""select a single tuple from ItemLocations table for site_name
|
|
||||||
|
|
||||||
Args:
|
|
||||||
conn (_T_connector@connect):
|
|
||||||
site_name (str):
|
|
||||||
payload (tuple): [item_id, location_id]
|
|
||||||
convert (bool): defaults to False, used to determine return of tuple/dict
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
tuple: the row that was returned from the table
|
|
||||||
"""
|
|
||||||
item_locations = ()
|
item_locations = ()
|
||||||
self_conn = False
|
self_conn = False
|
||||||
select_item_location_sql = f"SELECT * FROM {site_name}_item_locations WHERE part_id = %s AND location_id = %s;"
|
select_item_location_sql = f"SELECT * FROM {site_name}_item_locations WHERE part_id = %s AND location_id = %s;"
|
||||||
@ -276,21 +235,7 @@ def insertCostLayersTuple(site, payload, convert=True, conn=None):
|
|||||||
raise postsqldb.DatabaseError(error, payload, sql)
|
raise postsqldb.DatabaseError(error, payload, sql)
|
||||||
|
|
||||||
def insertTransactionsTuple(site, payload, convert=True, conn=None):
|
def insertTransactionsTuple(site, payload, convert=True, conn=None):
|
||||||
"""insert payload into transactions table for site
|
# payload (tuple): (timestamp[timestamp], logistics_info_id[int], barcode[str], name[str],
|
||||||
|
|
||||||
Args:
|
|
||||||
conn (_T_connector@connect): Postgresql Connector
|
|
||||||
site (str):
|
|
||||||
payload (tuple): (timestamp[timestamp], logistics_info_id[int], barcode[str], name[str],
|
|
||||||
transaction_type[str], quantity[float], description[str], user_id[int], data[jsonb])
|
|
||||||
convert (bool, optional): Determines if to return tuple as dictionary. Defaults to False.
|
|
||||||
|
|
||||||
Raises:
|
|
||||||
DatabaseError:
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
tuple or dict: inserted tuple
|
|
||||||
"""
|
|
||||||
transaction = ()
|
transaction = ()
|
||||||
self_conn = False
|
self_conn = False
|
||||||
with open(f"application/poe/sql/insertTransactionsTuple.sql", "r+") as file:
|
with open(f"application/poe/sql/insertTransactionsTuple.sql", "r+") as file:
|
||||||
@ -319,20 +264,6 @@ def insertTransactionsTuple(site, payload, convert=True, conn=None):
|
|||||||
return transaction
|
return transaction
|
||||||
|
|
||||||
def insertReceiptsTuple(site, payload, convert=True, conn=None):
|
def insertReceiptsTuple(site, payload, convert=True, conn=None):
|
||||||
"""insert payload into receipt table of site
|
|
||||||
|
|
||||||
Args:
|
|
||||||
conn (_T_connector@connect): Postgresql Connector
|
|
||||||
site (str):
|
|
||||||
payload (tuple):
|
|
||||||
convert (bool, optional): Determines if to return tuple as dictionary. Defaults to False.
|
|
||||||
|
|
||||||
Raises:
|
|
||||||
DatabaseError:
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
tuple or dict: inserted tuple
|
|
||||||
"""
|
|
||||||
receipt = ()
|
receipt = ()
|
||||||
self_conn = False
|
self_conn = False
|
||||||
with open(f"application/poe/sql/insertReceiptsTuple.sql", "r+") as file:
|
with open(f"application/poe/sql/insertReceiptsTuple.sql", "r+") as file:
|
||||||
@ -362,21 +293,6 @@ def insertReceiptsTuple(site, payload, convert=True, conn=None):
|
|||||||
raise postsqldb.DatabaseError(error, payload, sql)
|
raise postsqldb.DatabaseError(error, payload, sql)
|
||||||
|
|
||||||
def insertReceiptItemsTuple(site, payload, convert=True, conn=None):
|
def insertReceiptItemsTuple(site, payload, convert=True, conn=None):
|
||||||
"""insert payload into receipt_items table of site
|
|
||||||
|
|
||||||
Args:
|
|
||||||
conn (_T_connector@connect): Postgresql Connector
|
|
||||||
site (str):
|
|
||||||
payload (tuple): (type[str], receipt_id[int], barcode[str], name[str],
|
|
||||||
qty[float], data[jsonb], status[str])
|
|
||||||
convert (bool, optional): Determines if to return tuple as dictionary. Defaults to False.
|
|
||||||
|
|
||||||
Raises:
|
|
||||||
DatabaseError:
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
tuple or dict: inserted tuple
|
|
||||||
"""
|
|
||||||
receipt_item = ()
|
receipt_item = ()
|
||||||
self_conn = False
|
self_conn = False
|
||||||
|
|
||||||
@ -407,16 +323,6 @@ def insertReceiptItemsTuple(site, payload, convert=True, conn=None):
|
|||||||
raise postsqldb.DatabaseError(error, payload, sql)
|
raise postsqldb.DatabaseError(error, payload, sql)
|
||||||
|
|
||||||
def updateCostLayersTuple(site, payload, convert=True, conn=None):
|
def updateCostLayersTuple(site, payload, convert=True, conn=None):
|
||||||
"""_summary_
|
|
||||||
|
|
||||||
Args:
|
|
||||||
conn (_type_): _description_
|
|
||||||
site (_type_): _description_
|
|
||||||
payload (_type_): {'id': cost_layer_id, 'update': {column: data...}}
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
_type_: _description_
|
|
||||||
"""
|
|
||||||
cost_layer = ()
|
cost_layer = ()
|
||||||
self_conn = False
|
self_conn = False
|
||||||
|
|
||||||
@ -478,21 +384,6 @@ def updateItemLocation(site, payload, convert=True, conn=None):
|
|||||||
|
|
||||||
|
|
||||||
def deleteCostLayersTuple(site, payload, convert=True, conn=None):
|
def deleteCostLayersTuple(site, payload, convert=True, conn=None):
|
||||||
"""This is a basic funtion to delete a tuple from a table in site with an id. All
|
|
||||||
tables in this database has id's associated with them.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
conn (_T_connector@connect): Postgresql Connector
|
|
||||||
site_name (str):
|
|
||||||
payload (tuple): (tuple_id,)
|
|
||||||
convert (bool, optional): Determines if to return tuple as dictionary. Defaults to True.
|
|
||||||
|
|
||||||
Raises:
|
|
||||||
DatabaseError:
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
tuple or dict: deleted tuple
|
|
||||||
"""
|
|
||||||
deleted = ()
|
deleted = ()
|
||||||
self_conn = False
|
self_conn = False
|
||||||
sql = f"WITH deleted_rows AS (DELETE FROM {site}_cost_layers WHERE id IN ({','.join(['%s'] * len(payload))}) RETURNING *) SELECT * FROM deleted_rows;"
|
sql = f"WITH deleted_rows AS (DELETE FROM {site}_cost_layers WHERE id IN ({','.join(['%s'] * len(payload))}) RETURNING *) SELECT * FROM deleted_rows;"
|
||||||
|
|||||||
0
application/shoppinglists/__init__.py
Normal file
0
application/shoppinglists/__init__.py
Normal file
@ -4,26 +4,32 @@ from config import config, sites_config
|
|||||||
from main import unfoldCostLayers
|
from main import unfoldCostLayers
|
||||||
from user_api import login_required
|
from user_api import login_required
|
||||||
import postsqldb
|
import postsqldb
|
||||||
|
from application.shoppinglists import shoplist_database
|
||||||
|
from application import database_payloads
|
||||||
|
|
||||||
shopping_list_api = Blueprint('shopping_list_API', __name__)
|
shopping_list_api = Blueprint('shopping_list_API', __name__, template_folder="templates", static_folder="static")
|
||||||
|
|
||||||
@shopping_list_api.route("/shopping-lists")
|
|
||||||
|
# ROOT TEMPLATE CALLS
|
||||||
|
@shopping_list_api.route("/")
|
||||||
@login_required
|
@login_required
|
||||||
def shopping_lists():
|
def shopping_lists():
|
||||||
sites = [site[1] for site in main.get_sites(session['user']['sites'])]
|
sites = [site[1] for site in main.get_sites(session['user']['sites'])]
|
||||||
return render_template("shopping-lists/index.html", current_site=session['selected_site'], sites=sites)
|
return render_template("lists.html", current_site=session['selected_site'], sites=sites)
|
||||||
|
|
||||||
@shopping_list_api.route("/shopping-list/<mode>/<id>")
|
@shopping_list_api.route("/<mode>/<id>")
|
||||||
@login_required
|
@login_required
|
||||||
def shopping_list(mode, id):
|
def shopping_list(mode, id):
|
||||||
sites = [site[1] for site in main.get_sites(session['user']['sites'])]
|
sites = [site[1] for site in main.get_sites(session['user']['sites'])]
|
||||||
if mode == "view":
|
if mode == "view":
|
||||||
return render_template("shopping-lists/view.html", id=id, current_site=session['selected_site'], sites=sites)
|
return render_template("view.html", id=id, current_site=session['selected_site'], sites=sites)
|
||||||
if mode == "edit":
|
if mode == "edit":
|
||||||
return render_template("shopping-lists/edit.html", id=id, current_site=session['selected_site'], sites=sites)
|
return render_template("edit.html", id=id, current_site=session['selected_site'], sites=sites)
|
||||||
return redirect("/")
|
return redirect("/")
|
||||||
|
|
||||||
@shopping_list_api.route('/shopping-lists/addList', methods=["POST"])
|
|
||||||
|
# API CALLS
|
||||||
|
@shopping_list_api.route('/api/addList', methods=["POST"])
|
||||||
def addList():
|
def addList():
|
||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
list_name = request.get_json()['list_name']
|
list_name = request.get_json()['list_name']
|
||||||
@ -43,7 +49,7 @@ def addList():
|
|||||||
return jsonify({'error': False, 'message': 'List added!!'})
|
return jsonify({'error': False, 'message': 'List added!!'})
|
||||||
return jsonify({'error': True, 'message': 'These was an error with adding the list!'})
|
return jsonify({'error': True, 'message': 'These was an error with adding the list!'})
|
||||||
|
|
||||||
@shopping_list_api.route('/shopping-lists/getLists', methods=["GET"])
|
@shopping_list_api.route('/api/getLists', methods=["GET"])
|
||||||
def getShoppingLists():
|
def getShoppingLists():
|
||||||
lists = []
|
lists = []
|
||||||
if request.method == "GET":
|
if request.method == "GET":
|
||||||
@ -77,7 +83,7 @@ def getShoppingLists():
|
|||||||
|
|
||||||
return jsonify({'shopping_lists': lists, 'end':math.ceil(count/limit), 'error': False, 'message': 'Lists queried successfully!'})
|
return jsonify({'shopping_lists': lists, 'end':math.ceil(count/limit), 'error': False, 'message': 'Lists queried successfully!'})
|
||||||
|
|
||||||
@shopping_list_api.route('/shopping-lists/getList', methods=["GET"])
|
@shopping_list_api.route('/api/getList', methods=["GET"])
|
||||||
def getShoppingList():
|
def getShoppingList():
|
||||||
if request.method == "GET":
|
if request.method == "GET":
|
||||||
sl_id = int(request.args.get('id', 1))
|
sl_id = int(request.args.get('id', 1))
|
||||||
@ -87,19 +93,19 @@ def getShoppingList():
|
|||||||
lists = database.getShoppingList(conn, site_name, (sl_id, ), convert=True)
|
lists = database.getShoppingList(conn, site_name, (sl_id, ), convert=True)
|
||||||
return jsonify({'shopping_list': lists, 'error': False, 'message': 'Lists queried successfully!'})
|
return jsonify({'shopping_list': lists, 'error': False, 'message': 'Lists queried successfully!'})
|
||||||
|
|
||||||
@shopping_list_api.route('/shopping-lists/getListItem', methods=["GET"])
|
# Added to Database
|
||||||
|
@shopping_list_api.route('/api/getListItem', methods=["GET"])
|
||||||
def getShoppingListItem():
|
def getShoppingListItem():
|
||||||
list_item = {}
|
list_item = {}
|
||||||
if request.method == "GET":
|
if request.method == "GET":
|
||||||
sli_id = int(request.args.get('sli_id', 1))
|
sli_id = int(request.args.get('sli_id', 1))
|
||||||
database_config = config()
|
|
||||||
site_name = session['selected_site']
|
site_name = session['selected_site']
|
||||||
with psycopg2.connect(**database_config) as conn:
|
list_item = shoplist_database.getShoppingListItem(site_name, (sli_id, ))
|
||||||
list_item = postsqldb.ShoppingListsTable.getItem(conn, site_name, (sli_id, ))
|
|
||||||
return jsonify({'list_item': list_item, 'error': False, 'message': 'Lists Items queried successfully!'})
|
return jsonify({'list_item': list_item, 'error': False, 'message': 'Lists Items queried successfully!'})
|
||||||
return jsonify({'list_item': list_item, 'error': True, 'message': 'List Items queried unsuccessfully!'})
|
return jsonify({'list_item': list_item, 'error': True, 'message': 'List Items queried unsuccessfully!'})
|
||||||
|
|
||||||
@shopping_list_api.route('/shopping-lists/getItems', methods=["GET"])
|
# Added to database
|
||||||
|
@shopping_list_api.route('/api/getItems', methods=["GET"])
|
||||||
def getItems():
|
def getItems():
|
||||||
recordset = []
|
recordset = []
|
||||||
count = {'count': 0}
|
count = {'count': 0}
|
||||||
@ -108,47 +114,44 @@ def getItems():
|
|||||||
limit = int(request.args.get('limit', 10))
|
limit = int(request.args.get('limit', 10))
|
||||||
search_string = request.args.get('search_string', 10)
|
search_string = request.args.get('search_string', 10)
|
||||||
site_name = session['selected_site']
|
site_name = session['selected_site']
|
||||||
offset = (page - 1) * limit
|
offset = (page - 1) * limit
|
||||||
database_config = config()
|
sort_order = "ID ASC"
|
||||||
with psycopg2.connect(**database_config) as conn:
|
payload = (search_string, limit, offset, sort_order)
|
||||||
payload = (search_string, limit, offset)
|
recordset, count = shoplist_database.getItemsWithQOH(site_name, payload, convert=True)
|
||||||
recordset, count = database.getItemsWithQOH(conn, site_name, payload, convert=True)
|
|
||||||
return jsonify({"items":recordset, "end":math.ceil(count['count']/limit), "error":False, "message":"items fetched succesfully!"})
|
return jsonify({"items":recordset, "end":math.ceil(count['count']/limit), "error":False, "message":"items fetched succesfully!"})
|
||||||
return jsonify({"items":recordset, "end":math.ceil(count['count']/limit), "error":True, "message":"There was an error with this GET statement"})
|
return jsonify({"items":recordset, "end":math.ceil(count['count']/limit), "error":True, "message":"There was an error with this GET statement"})
|
||||||
|
|
||||||
@shopping_list_api.route('/shopping-lists/postListItem', methods=["POST"])
|
# Added to database
|
||||||
|
@shopping_list_api.route('/api/postListItem', methods=["POST"])
|
||||||
def postListItem():
|
def postListItem():
|
||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
data = request.get_json()['data']
|
data = request.get_json()['data']
|
||||||
site_name = session['selected_site']
|
site_name = session['selected_site']
|
||||||
database_config = config()
|
sl_item = database_payloads.ShoppingListItemPayload(
|
||||||
with psycopg2.connect(**database_config) as conn:
|
uuid = data['uuid'],
|
||||||
sl_item = MyDataclasses.ShoppingListItemPayload(
|
sl_id = data['sl_id'],
|
||||||
uuid = data['uuid'],
|
item_type=data['item_type'],
|
||||||
sl_id = data['sl_id'],
|
item_name=data['item_name'],
|
||||||
item_type=data['item_type'],
|
uom=data['uom'],
|
||||||
item_name=data['item_name'],
|
qty=data['qty'],
|
||||||
uom=data['uom'],
|
item_id=data['item_id'],
|
||||||
qty=data['qty'],
|
links=data['links']
|
||||||
item_id=data['item_id'],
|
)
|
||||||
links=data['links']
|
shoplist_database.insertShoppingListItemsTuple(site_name, sl_item.payload())
|
||||||
)
|
|
||||||
database.insertShoppingListItemsTuple(conn, site_name, sl_item.payload())
|
|
||||||
return jsonify({"error":False, "message":"items fetched succesfully!"})
|
return jsonify({"error":False, "message":"items fetched succesfully!"})
|
||||||
return jsonify({"error":True, "message":"There was an error with this GET statement"})
|
return jsonify({"error":True, "message":"There was an error with this GET statement"})
|
||||||
|
|
||||||
@shopping_list_api.route('/shopping-lists/deleteListItem', methods=["POST"])
|
# Added to Database
|
||||||
|
@shopping_list_api.route('/api/deleteListItem', methods=["POST"])
|
||||||
def deleteListItem():
|
def deleteListItem():
|
||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
sli_id = request.get_json()['sli_id']
|
sli_id = request.get_json()['sli_id']
|
||||||
site_name = session['selected_site']
|
site_name = session['selected_site']
|
||||||
database_config = config()
|
shoplist_database.deleteShoppingListItemsTuple(site_name, (sli_id, ))
|
||||||
with psycopg2.connect(**database_config) as conn:
|
|
||||||
database.deleteShoppingListItemsTuple(conn, site_name, (sli_id, ))
|
|
||||||
return jsonify({"error":False, "message":"item deleted succesfully!"})
|
return jsonify({"error":False, "message":"item deleted succesfully!"})
|
||||||
return jsonify({"error":True, "message":"There was an error with this POST statement"})
|
return jsonify({"error":True, "message":"There was an error with this POST statement"})
|
||||||
|
|
||||||
@shopping_list_api.route('/shopping-lists/saveListItem', methods=["POST"])
|
@shopping_list_api.route('/api/saveListItem', methods=["POST"])
|
||||||
def saveListItem():
|
def saveListItem():
|
||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
sli_id = request.get_json()['sli_id']
|
sli_id = request.get_json()['sli_id']
|
||||||
@ -160,7 +163,7 @@ def saveListItem():
|
|||||||
return jsonify({"error":False, "message":"items fetched succesfully!"})
|
return jsonify({"error":False, "message":"items fetched succesfully!"})
|
||||||
return jsonify({"error":True, "message":"There was an error with this GET statement"})
|
return jsonify({"error":True, "message":"There was an error with this GET statement"})
|
||||||
|
|
||||||
@shopping_list_api.route('/shopping-lists/getSKUItemsFull', methods=["GET"])
|
@shopping_list_api.route('/api/getSKUItemsFull', methods=["GET"])
|
||||||
def getSKUItemsFull():
|
def getSKUItemsFull():
|
||||||
items = []
|
items = []
|
||||||
count = {'count': 0}
|
count = {'count': 0}
|
||||||
146
application/shoppinglists/shoplist_database.py
Normal file
146
application/shoppinglists/shoplist_database.py
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
|
||||||
|
import psycopg2
|
||||||
|
|
||||||
|
import config
|
||||||
|
from application import postsqldb
|
||||||
|
|
||||||
|
def getShoppingListItem(site, payload, convert=True, conn=None):
|
||||||
|
"""_summary_
|
||||||
|
|
||||||
|
Args:
|
||||||
|
conn (_type_): _description_
|
||||||
|
site (_type_): _description_
|
||||||
|
payload (_type_): (id, )
|
||||||
|
convert (bool, optional): _description_. Defaults to True.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
DatabaseError: _description_
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
_type_: _description_
|
||||||
|
"""
|
||||||
|
record = ()
|
||||||
|
self_conn = False
|
||||||
|
with open('application/shoppinglists/sql/selectShoppingListItem.sql', 'r') as file:
|
||||||
|
sql = file.read().replace("%%site_name%%", site)
|
||||||
|
try:
|
||||||
|
|
||||||
|
if not conn:
|
||||||
|
database_config = config.config()
|
||||||
|
conn = psycopg2.connect(**database_config)
|
||||||
|
conn.autocommit = True
|
||||||
|
self_conn = True
|
||||||
|
|
||||||
|
with conn.cursor() as cur:
|
||||||
|
cur.execute(sql, payload)
|
||||||
|
rows = cur.fetchone()
|
||||||
|
if rows and convert:
|
||||||
|
record = postsqldb.tupleDictionaryFactory(cur.description, rows)
|
||||||
|
elif rows and not convert:
|
||||||
|
record = rows
|
||||||
|
|
||||||
|
if self_conn:
|
||||||
|
conn.close()
|
||||||
|
|
||||||
|
return record
|
||||||
|
except Exception as error:
|
||||||
|
raise postsqldb.DatabaseError(error, payload, sql)
|
||||||
|
|
||||||
|
|
||||||
|
def getItemsWithQOH(site, payload, convert=True, conn=None):
|
||||||
|
recordset = []
|
||||||
|
count = 0
|
||||||
|
self_conn = False
|
||||||
|
|
||||||
|
with open(f"application/shoppinglists/sql/getItemsWithQOH.sql", "r+") as file:
|
||||||
|
sql = file.read().replace("%%site_name%%", site).replace("%%sort_order%%", payload[3])
|
||||||
|
|
||||||
|
payload = list(payload)
|
||||||
|
payload.pop(3)
|
||||||
|
try:
|
||||||
|
|
||||||
|
if not conn:
|
||||||
|
database_config = config.config()
|
||||||
|
conn = psycopg2.connect(**database_config)
|
||||||
|
conn.autocommit = True
|
||||||
|
self_conn = True
|
||||||
|
|
||||||
|
if convert:
|
||||||
|
with conn.cursor(cursor_factory=psycopg2.extras.RealDictCursor) as cur:
|
||||||
|
cur.execute(sql, payload)
|
||||||
|
recordset = cur.fetchall()
|
||||||
|
recordset = [dict(record) for record in recordset]
|
||||||
|
cur.execute(f"SELECT COUNT(*) FROM {site}_items WHERE search_string LIKE '%%' || %s || '%%';", (payload[0], ))
|
||||||
|
count = cur.fetchone()
|
||||||
|
else:
|
||||||
|
with conn.cursor() as cur:
|
||||||
|
cur.execute(sql, payload)
|
||||||
|
recordset = cur.fetchall()
|
||||||
|
cur.execute(f"SELECT COUNT(*) FROM {site}_items WHERE search_string LIKE '%%' || %s || '%%';", (payload[0], ))
|
||||||
|
count = cur.fetchone()
|
||||||
|
|
||||||
|
if self_conn:
|
||||||
|
conn.close()
|
||||||
|
|
||||||
|
return recordset, count
|
||||||
|
|
||||||
|
except Exception as error:
|
||||||
|
raise postsqldb.DatabaseError(error, payload, sql)
|
||||||
|
|
||||||
|
def deleteShoppingListItemsTuple(site_name, payload, convert=True, conn=None):
|
||||||
|
deleted = ()
|
||||||
|
self_conn = False
|
||||||
|
sql = f"WITH deleted_rows AS (DELETE FROM {site_name}_shopping_list_items WHERE id IN ({','.join(['%s'] * len(payload))}) RETURNING *) SELECT * FROM deleted_rows;"
|
||||||
|
try:
|
||||||
|
|
||||||
|
if not conn:
|
||||||
|
database_config = config.config()
|
||||||
|
conn = psycopg2.connect(**database_config)
|
||||||
|
conn.autocommit = True
|
||||||
|
self_conn = True
|
||||||
|
|
||||||
|
with conn.cursor() as cur:
|
||||||
|
cur.execute(sql, payload)
|
||||||
|
rows = cur.fetchall()
|
||||||
|
if rows and convert:
|
||||||
|
deleted = [postsqldb.tupleDictionaryFactory(cur.description, r) for r in rows]
|
||||||
|
elif rows and not convert:
|
||||||
|
deleted = rows
|
||||||
|
|
||||||
|
if self_conn:
|
||||||
|
conn.commit()
|
||||||
|
conn.close()
|
||||||
|
|
||||||
|
return deleted
|
||||||
|
except Exception as error:
|
||||||
|
raise postsqldb.DatabaseError(error, payload, sql)
|
||||||
|
|
||||||
|
def insertShoppingListItemsTuple(site, payload, convert=True, conn=None):
|
||||||
|
shopping_list_item = ()
|
||||||
|
self_conn = False
|
||||||
|
with open(f"application/shoppinglists/sql/insertShoppingListItemsTuple.sql", "r+") as file:
|
||||||
|
sql = file.read().replace("%%site_name%%", site)
|
||||||
|
try:
|
||||||
|
|
||||||
|
if not conn:
|
||||||
|
database_config = config.config()
|
||||||
|
conn = psycopg2.connect(**database_config)
|
||||||
|
conn.autocommit = True
|
||||||
|
self_conn = True
|
||||||
|
|
||||||
|
with conn.cursor() as cur:
|
||||||
|
cur.execute(sql, payload)
|
||||||
|
rows = cur.fetchone()
|
||||||
|
if rows and convert:
|
||||||
|
shopping_list_item = postsqldb.tupleDictionaryFactory(cur.description, rows)
|
||||||
|
elif rows and not convert:
|
||||||
|
shopping_list_item = rows
|
||||||
|
|
||||||
|
if self_conn:
|
||||||
|
conn.commit()
|
||||||
|
conn.close()
|
||||||
|
|
||||||
|
return shopping_list_item
|
||||||
|
|
||||||
|
except Exception as error:
|
||||||
|
raise postsqldb.DatabaseError(error, payload, sql)
|
||||||
0
application/shoppinglists/shoplist_processess.py
Normal file
0
application/shoppinglists/shoplist_processess.py
Normal file
18
application/shoppinglists/sql/getItemsWithQOH.sql
Normal file
18
application/shoppinglists/sql/getItemsWithQOH.sql
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
WITH sum_cte AS (
|
||||||
|
SELECT mi.id, SUM(mil.quantity_on_hand)::FLOAT8 AS total_sum
|
||||||
|
FROM %%site_name%%_item_locations mil
|
||||||
|
JOIN %%site_name%%_items mi ON mil.part_id = mi.id
|
||||||
|
GROUP BY mi.id
|
||||||
|
)
|
||||||
|
|
||||||
|
SELECT %%site_name%%_items.*,
|
||||||
|
row_to_json(%%site_name%%_item_info.*) as item_info,
|
||||||
|
sum_cte.total_sum as total_qoh,
|
||||||
|
(SELECT COALESCE(row_to_json(u), '{}') FROM units as u WHERE u.id=%%site_name%%_item_info.uom) as uom
|
||||||
|
FROM %%site_name%%_items
|
||||||
|
LEFT JOIN sum_cte ON %%site_name%%_items.id = sum_cte.id
|
||||||
|
LEFT JOIN %%site_name%%_item_info ON %%site_name%%_items.item_info_id = %%site_name%%_item_info.id
|
||||||
|
WHERE %%site_name%%_items.search_string LIKE '%%' || %s || '%%'
|
||||||
|
ORDER BY %%sort_order%%
|
||||||
|
LIMIT %s OFFSET %s;
|
||||||
|
|
||||||
@ -0,0 +1,4 @@
|
|||||||
|
INSERT INTO %%site_name%%_shopping_list_items
|
||||||
|
(uuid, sl_id, item_type, item_name, uom, qty, item_id, links)
|
||||||
|
VALUES (%s, %s, %s, %s, %s, %s, %s, %s)
|
||||||
|
RETURNING *;
|
||||||
4
application/shoppinglists/sql/selectShoppingListItem.sql
Normal file
4
application/shoppinglists/sql/selectShoppingListItem.sql
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
SELECT items.*,
|
||||||
|
(SELECT COALESCE(row_to_json(un), '{}') FROM units un WHERE un.id = items.uom LIMIT 1) AS uom
|
||||||
|
FROM %%site_name%%_shopping_list_items items
|
||||||
|
WHERE items.id = %s;
|
||||||
@ -88,7 +88,7 @@ async function replenishLineTable(sl_items){
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function fetchShoppingList() {
|
async function fetchShoppingList() {
|
||||||
const url = new URL('/shopping-lists/getList', window.location.origin);
|
const url = new URL('/shopping-lists/api/getList', window.location.origin);
|
||||||
url.searchParams.append('id', sl_id);
|
url.searchParams.append('id', sl_id);
|
||||||
const response = await fetch(url);
|
const response = await fetch(url);
|
||||||
data = await response.json();
|
data = await response.json();
|
||||||
@ -277,7 +277,7 @@ async function updateItemsPaginationElement() {
|
|||||||
|
|
||||||
let items_limit = 25;
|
let items_limit = 25;
|
||||||
async function fetchItems() {
|
async function fetchItems() {
|
||||||
const url = new URL('/shopping-lists/getItems', window.location.origin);
|
const url = new URL('/shopping-lists/api/getItems', window.location.origin);
|
||||||
url.searchParams.append('page', pagination_current);
|
url.searchParams.append('page', pagination_current);
|
||||||
url.searchParams.append('limit', items_limit);
|
url.searchParams.append('limit', items_limit);
|
||||||
url.searchParams.append('search_string', search_string);
|
url.searchParams.append('search_string', search_string);
|
||||||
@ -288,7 +288,7 @@ async function fetchItems() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function fetchSLItem(sli_id) {
|
async function fetchSLItem(sli_id) {
|
||||||
const url = new URL('/shopping-lists/getListItem', window.location.origin);
|
const url = new URL('/shopping-lists/api/getListItem', window.location.origin);
|
||||||
url.searchParams.append('sli_id', sli_id);
|
url.searchParams.append('sli_id', sli_id);
|
||||||
const response = await fetch(url);
|
const response = await fetch(url);
|
||||||
data = await response.json();
|
data = await response.json();
|
||||||
@ -320,7 +320,7 @@ async function addCustomItem() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function submitItemToList(newItem) {
|
async function submitItemToList(newItem) {
|
||||||
const response = await fetch(`/shopping-lists/postListItem`, {
|
const response = await fetch(`/shopping-lists/api/postListItem`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
@ -345,7 +345,7 @@ async function submitItemToList(newItem) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function deleteLineItem(sli_id) {
|
async function deleteLineItem(sli_id) {
|
||||||
const response = await fetch(`/shopping-lists/deleteListItem`, {
|
const response = await fetch(`/shopping-lists/api/deleteListItem`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
@ -374,7 +374,7 @@ async function deleteLineItem(sli_id) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function saveLineItem(sli_id, update) {
|
async function saveLineItem(sli_id, update) {
|
||||||
const response = await fetch(`/shopping-lists/saveListItem`, {
|
const response = await fetch(`/shopping-lists/api/saveListItem`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
@ -58,12 +58,12 @@ async function replenishShoppingListCards(lists) {
|
|||||||
editOp.setAttribute('class', 'uk-button uk-button-small uk-button-default')
|
editOp.setAttribute('class', 'uk-button uk-button-small uk-button-default')
|
||||||
editOp.innerHTML = '<span uk-icon="icon: pencil"></span> Edit'
|
editOp.innerHTML = '<span uk-icon="icon: pencil"></span> Edit'
|
||||||
editOp.style = "margin-right: 10px;"
|
editOp.style = "margin-right: 10px;"
|
||||||
editOp.href = `/shopping-list/edit/${lists[i].id}`
|
editOp.href = `/shopping-lists/edit/${lists[i].id}`
|
||||||
|
|
||||||
let viewOp = document.createElement('a')
|
let viewOp = document.createElement('a')
|
||||||
viewOp.setAttribute('class', 'uk-button uk-button-small uk-button-default')
|
viewOp.setAttribute('class', 'uk-button uk-button-small uk-button-default')
|
||||||
viewOp.innerHTML = '<span uk-icon="icon: eye"></span> View'
|
viewOp.innerHTML = '<span uk-icon="icon: eye"></span> View'
|
||||||
viewOp.href = `/shopping-list/view/${lists[i].id}`
|
viewOp.href = `/shopping-lists/view/${lists[i].id}`
|
||||||
//viewOp.style = "margin-right: 20px;"
|
//viewOp.style = "margin-right: 20px;"
|
||||||
|
|
||||||
|
|
||||||
@ -83,7 +83,7 @@ async function openAddListModal() {
|
|||||||
var listLimit = 5;
|
var listLimit = 5;
|
||||||
async function getShoppingLists(){
|
async function getShoppingLists(){
|
||||||
console.log(pagination_current)
|
console.log(pagination_current)
|
||||||
const url = new URL('/shopping-lists/getLists', window.location.origin);
|
const url = new URL('/shopping-lists/api/getLists', window.location.origin);
|
||||||
url.searchParams.append('page', pagination_current);
|
url.searchParams.append('page', pagination_current);
|
||||||
url.searchParams.append('limit', listLimit);
|
url.searchParams.append('limit', listLimit);
|
||||||
response = await fetch(url)
|
response = await fetch(url)
|
||||||
@ -98,7 +98,7 @@ async function addList() {
|
|||||||
let list_description = document.getElementById('addListDescription').value
|
let list_description = document.getElementById('addListDescription').value
|
||||||
let list_type = document.getElementById('list_type').value
|
let list_type = document.getElementById('list_type').value
|
||||||
|
|
||||||
const response = await fetch(`/shopping-lists/addList`, {
|
const response = await fetch(`/shopping-lists/api/addList`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
@ -304,6 +304,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
<script src="{{ url_for('static', filename='handlers/shoppingListEditHandler.js') }}"></script>
|
<script src="{{ url_for('shopping_list_API.static', filename='js/shoppingListEditHandler.js') }}"></script>
|
||||||
<script>const sl_id = {{id|tojson}}</script>
|
<script>const sl_id = {{id|tojson}}</script>
|
||||||
</html>
|
</html>
|
||||||
@ -156,5 +156,5 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
<script src="{{ url_for('static', filename='handlers/shoppingListsHandler.js') }}"></script>
|
<script src="{{ url_for('shopping_list_API.static', filename='js/shoppingListsHandler.js') }}"></script>
|
||||||
</html>
|
</html>
|
||||||
@ -116,6 +116,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
<script src="{{ url_for('static', filename='handlers/shoppingListViewHandler.js') }}"></script>
|
<script src="{{ url_for('shopping_list_API.static', filename='js/shoppingListViewHandler.js') }}"></script>
|
||||||
<script>const sl_id = {{id|tojson}}</script>
|
<script>const sl_id = {{id|tojson}}</script>
|
||||||
</html>
|
</html>
|
||||||
14
database.log
14
database.log
@ -1943,4 +1943,16 @@
|
|||||||
sql='INSERT INTO test_receipt_items(type, receipt_id, barcode, name, qty, uom, data, status) VALUES (%s, %s, %s, %s, %s, %s, %s, %s) RETURNING *;')
|
sql='INSERT INTO test_receipt_items(type, receipt_id, barcode, name, qty, uom, data, status) VALUES (%s, %s, %s, %s, %s, %s, %s, %s) RETURNING *;')
|
||||||
2025-07-04 08:19:34.889657 --- ERROR --- DatabaseError(message='invalid input syntax for type integer: "{"cost": 1.99, "expires": false}"LINE 3: ...41789001314%', 'Chicken Ramen Noodle Soup', 1, 5, '{"cost": ... ^',
|
2025-07-04 08:19:34.889657 --- ERROR --- DatabaseError(message='invalid input syntax for type integer: "{"cost": 1.99, "expires": false}"LINE 3: ...41789001314%', 'Chicken Ramen Noodle Soup', 1, 5, '{"cost": ... ^',
|
||||||
payload=('sku', 23, '%041789001314%', 'Chicken Ramen Noodle Soup', 1, 5, '{"cost": 1.99, "expires": false}', 'Unresolved'),
|
payload=('sku', 23, '%041789001314%', 'Chicken Ramen Noodle Soup', 1, 5, '{"cost": 1.99, "expires": false}', 'Unresolved'),
|
||||||
sql='INSERT INTO test_recipe_items(uuid, rp_id, item_type, item_name, uom, qty, item_id, links) VALUES (%s, %s, %s, %s, %s, %s, %s, %s) RETURNING *;')
|
sql='INSERT INTO test_recipe_items(uuid, rp_id, item_type, item_name, uom, qty, item_id, links) VALUES (%s, %s, %s, %s, %s, %s, %s, %s) RETURNING *;')
|
||||||
|
2025-07-12 07:48:13.460875 --- ERROR --- DatabaseError(message='invalid input syntax for type integer: "each"LINE 3: VALUES ('5g89bj2', '5', 'custom', 'test', 'each', 1, NULL, '... ^',
|
||||||
|
payload=('5g89bj2', '5', 'custom', 'test', 'each', 1, None, '{"main": ""}'),
|
||||||
|
sql='INSERT INTO test_shopping_list_items(uuid, sl_id, item_type, item_name, uom, qty, item_id, links) VALUES (%s, %s, %s, %s, %s, %s, %s, %s) RETURNING *;')
|
||||||
|
2025-07-12 07:57:23.925776 --- ERROR --- DatabaseError(message='syntax error at or near "ASC"LINE 16: ORDER BY ASC ^',
|
||||||
|
payload=['', 25, 0],
|
||||||
|
sql='WITH sum_cte AS ( SELECT mi.id, SUM(mil.quantity_on_hand)::FLOAT8 AS total_sum FROM test_item_locations mil JOIN test_items mi ON mil.part_id = mi.id GROUP BY mi.id )SELECT test_items.*, row_to_json(test_item_info.*) as item_info, sum_cte.total_sum as total_qoh, (SELECT COALESCE(row_to_json(u), '{}') FROM units as u WHERE u.id=test_item_info.uom) as uomFROM test_itemsLEFT JOIN sum_cte ON test_items.id = sum_cte.idLEFT JOIN test_item_info ON test_items.item_info_id = test_item_info.idWHERE test_items.search_string LIKE '%%' || %s || '%%'ORDER BY ASCLIMIT %s OFFSET %s;')
|
||||||
|
2025-07-12 07:58:21.551161 --- ERROR --- DatabaseError(message='syntax error at or near "ASC"LINE 16: ORDER BY ASC ^',
|
||||||
|
payload=['', 25, 0],
|
||||||
|
sql='WITH sum_cte AS ( SELECT mi.id, SUM(mil.quantity_on_hand)::FLOAT8 AS total_sum FROM test_item_locations mil JOIN test_items mi ON mil.part_id = mi.id GROUP BY mi.id )SELECT test_items.*, row_to_json(test_item_info.*) as item_info, sum_cte.total_sum as total_qoh, (SELECT COALESCE(row_to_json(u), '{}') FROM units as u WHERE u.id=test_item_info.uom) as uomFROM test_itemsLEFT JOIN sum_cte ON test_items.id = sum_cte.idLEFT JOIN test_item_info ON test_items.item_info_id = test_item_info.idWHERE test_items.search_string LIKE '%%' || %s || '%%' ORDER BY ASC LIMIT %s OFFSET %s;')
|
||||||
|
2025-07-12 08:43:43.017720 --- ERROR --- DatabaseError(message='invalid input syntax for type integer: " Pinch"LINE 1: ... = 'Acai-Blueberry-Pomegranate', qty = '1', uom = ' Pinch', ... ^',
|
||||||
|
payload={'id': 12, 'update': {'item_name': 'Acai-Blueberry-Pomegranate', 'qty': '1', 'uom': ' Pinch', 'links': {'main': 'test'}}},
|
||||||
|
sql='UPDATE test_shopping_list_items SET item_name = %s, qty = %s, uom = %s, links = %s WHERE id=%s RETURNING *;')
|
||||||
@ -1,7 +1,7 @@
|
|||||||
import celery.schedules
|
import celery.schedules
|
||||||
from flask import Flask, render_template, session, request, redirect, jsonify
|
from flask import Flask, render_template, session, request, redirect, jsonify
|
||||||
from flask_assets import Environment, Bundle
|
from flask_assets import Environment, Bundle
|
||||||
import api, config, user_api, psycopg2, main, api_admin, receipts_API, shopping_list_API, group_api
|
import api, config, user_api, psycopg2, main, api_admin, receipts_API, group_api
|
||||||
from user_api import login_required, update_session_user
|
from user_api import login_required, update_session_user
|
||||||
from workshop_api import workshop_api
|
from workshop_api import workshop_api
|
||||||
import database
|
import database
|
||||||
@ -10,6 +10,7 @@ from webpush import trigger_push_notifications_for_subscriptions
|
|||||||
from application.recipes import recipes_api
|
from application.recipes import recipes_api
|
||||||
from application.items import items_API
|
from application.items import items_API
|
||||||
from application.poe import poe_api
|
from application.poe import poe_api
|
||||||
|
from application.shoppinglists import shoplist_api
|
||||||
from flasgger import Swagger
|
from flasgger import Swagger
|
||||||
|
|
||||||
|
|
||||||
@ -31,7 +32,7 @@ app.register_blueprint(items_API.items_api, url_prefix='/items')
|
|||||||
app.register_blueprint(poe_api.point_of_ease, url_prefix='/poe')
|
app.register_blueprint(poe_api.point_of_ease, url_prefix='/poe')
|
||||||
app.register_blueprint(workshop_api)
|
app.register_blueprint(workshop_api)
|
||||||
app.register_blueprint(receipts_API.receipt_api)
|
app.register_blueprint(receipts_API.receipt_api)
|
||||||
app.register_blueprint(shopping_list_API.shopping_list_api)
|
app.register_blueprint(shoplist_api.shopping_list_api, url_prefix="/shopping-lists")
|
||||||
app.register_blueprint(group_api.groups_api)
|
app.register_blueprint(group_api.groups_api)
|
||||||
app.register_blueprint(recipes_api.recipes_api, url_prefix='/recipes')
|
app.register_blueprint(recipes_api.recipes_api, url_prefix='/recipes')
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user