Working on new Generation of shopping lists
This commit is contained in:
parent
7003836890
commit
a22faeb7a8
3
.gitignore
vendored
3
.gitignore
vendored
@ -7,4 +7,5 @@ static/css/uikit.min.css
|
|||||||
instance/application.cfg.py
|
instance/application.cfg.py
|
||||||
test.py
|
test.py
|
||||||
.VScodeCounter
|
.VScodeCounter
|
||||||
celerybeat-schedule
|
celerybeat-schedule
|
||||||
|
instance/application.cfg.py
|
||||||
|
|||||||
@ -3,6 +3,7 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" charset="utf-8" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" charset="utf-8" />
|
||||||
<title id="title">Login</title>
|
<title id="title">Login</title>
|
||||||
|
<link rel="icon" href="{{ url_for('static', filename='favicon.ico') }}">
|
||||||
|
|
||||||
|
|
||||||
<!-- Material Icons -->
|
<!-- Material Icons -->
|
||||||
@ -15,19 +16,20 @@
|
|||||||
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Sharp" rel="stylesheet" />
|
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Sharp" rel="stylesheet" />
|
||||||
|
|
||||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/uikit.min.css') }}"/>
|
<link rel="stylesheet" href="{{ url_for('static', filename='css/uikit.min.css') }}"/>
|
||||||
<link id="dark-mode" rel="stylesheet" href="{{ url_for('access_api.static', filename='css/logins-dark.css') }}"/>
|
<!--link id="dark-mode" rel="stylesheet" href="{{ url_for('access_api.static', filename='css/logins-dark.css') }}"/-->
|
||||||
|
|
||||||
|
|
||||||
<script src="{{ url_for('static', filename='js/uikit.min.js') }}"></script>
|
<script src="{{ url_for('static', filename='js/uikit.min.js') }}"></script>
|
||||||
<script src="{{ url_for('static', filename='js/uikit-icons.min.js') }}"></script>
|
<script src="{{ url_for('static', filename='js/uikit-icons.min.js') }}"></script>
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
<body class="uk-light">
|
<body>
|
||||||
<div class="uk-container">
|
<div class="uk-container">
|
||||||
<div class="uk-section">
|
<div class="uk-section">
|
||||||
<div class="uk-flex-center" uk-grid>
|
<div class="uk-flex-center" uk-grid>
|
||||||
<div class="uk-card uk-card-default uk-card-body uk-width-1-2@m uk-flex-center">
|
<div class="uk-card uk-card-default uk-card-body uk-width-1-2@m uk-flex-center">
|
||||||
<img class="uk-align-center" src="{{ url_for('static', filename='pictures/logo.jpg') }}" style="width: 200px; border-radius: 50%;">
|
<img class="uk-align-center" src="{{ url_for('static', filename='pictures/logo.png') }}" style="width: 200px;">
|
||||||
|
<h1 class="uk-text-center">PantryTrack</h1>
|
||||||
<ul uk-tab>
|
<ul uk-tab>
|
||||||
<li><a href="#">Login</a></li>
|
<li><a href="#">Login</a></li>
|
||||||
{% if instance_settings['signup_enabled'] %}
|
{% if instance_settings['signup_enabled'] %}
|
||||||
|
|||||||
@ -3,6 +3,7 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" charset="utf-8" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" charset="utf-8" />
|
||||||
<title id="title"></title>
|
<title id="title"></title>
|
||||||
|
<link rel="icon" href="{{ url_for('static', filename='favicon.ico') }}">
|
||||||
<!-- Material Icons -->
|
<!-- Material Icons -->
|
||||||
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet" />
|
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet" />
|
||||||
<!-- Material Symbols - Outlined Set -->
|
<!-- Material Symbols - Outlined Set -->
|
||||||
|
|||||||
@ -3,6 +3,7 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" charset="utf-8" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" charset="utf-8" />
|
||||||
<title id="title"></title>
|
<title id="title"></title>
|
||||||
|
<link rel="icon" href="{{ url_for('static', filename='favicon.ico') }}">
|
||||||
<!-- Material Icons -->
|
<!-- Material Icons -->
|
||||||
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet" />
|
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet" />
|
||||||
<!-- Material Symbols - Outlined Set -->
|
<!-- Material Symbols - Outlined Set -->
|
||||||
|
|||||||
@ -3,6 +3,7 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" charset="utf-8" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" charset="utf-8" />
|
||||||
<title>My Pantry - Setup</title>
|
<title>My Pantry - Setup</title>
|
||||||
|
<link rel="icon" href="{{ url_for('static', filename='favicon.ico') }}">
|
||||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@materializecss/materialize@2.0.3-alpha/dist/css/materialize.min.css" />
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@materializecss/materialize@2.0.3-alpha/dist/css/materialize.min.css" />
|
||||||
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet" />
|
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet" />
|
||||||
<script src="https://cdn.jsdelivr.net/npm/@materializecss/materialize@2.0.3-alpha/dist/js/materialize.min.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/@materializecss/materialize@2.0.3-alpha/dist/js/materialize.min.js"></script>
|
||||||
|
|||||||
@ -3,6 +3,7 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" charset="utf-8" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" charset="utf-8" />
|
||||||
<title id="title"></title>
|
<title id="title"></title>
|
||||||
|
<link rel="icon" href="{{ url_for('static', filename='favicon.ico') }}">
|
||||||
<!-- Material Icons -->
|
<!-- Material Icons -->
|
||||||
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet" />
|
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet" />
|
||||||
<!-- Material Symbols - Outlined Set -->
|
<!-- Material Symbols - Outlined Set -->
|
||||||
|
|||||||
@ -3,6 +3,7 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" charset="utf-8" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" charset="utf-8" />
|
||||||
<title id="title">User</title>
|
<title id="title">User</title>
|
||||||
|
<link rel="icon" href="{{ url_for('static', filename='favicon.ico') }}">
|
||||||
<!-- Material Icons -->
|
<!-- Material Icons -->
|
||||||
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet" />
|
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet" />
|
||||||
<!-- Material Symbols - Outlined Set -->
|
<!-- Material Symbols - Outlined Set -->
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" charset="utf-8" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" charset="utf-8" />
|
||||||
<title id="title"></title>
|
<title id="title"></title>
|
||||||
|
<link rel="icon" href="{{ url_for('static', filename='favicon.ico') }}">
|
||||||
|
|
||||||
<!-- Material Icons -->
|
<!-- Material Icons -->
|
||||||
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet" />
|
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet" />
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" charset="utf-8" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" charset="utf-8" />
|
||||||
<title id="title"></title>
|
<title id="title"></title>
|
||||||
|
<link rel="icon" href="{{ url_for('static', filename='favicon.ico') }}">
|
||||||
|
|
||||||
<!-- Material Icons -->
|
<!-- Material Icons -->
|
||||||
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet" />
|
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet" />
|
||||||
|
|||||||
@ -3,6 +3,7 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" charset="utf-8" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" charset="utf-8" />
|
||||||
<title id="title"></title>
|
<title id="title"></title>
|
||||||
|
<link rel="icon" href="{{ url_for('static', filename='favicon.ico') }}">
|
||||||
<!-- Material Icons -->
|
<!-- Material Icons -->
|
||||||
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet" />
|
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet" />
|
||||||
<!-- Material Symbols - Outlined Set -->
|
<!-- Material Symbols - Outlined Set -->
|
||||||
|
|||||||
@ -3,6 +3,7 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" charset="utf-8" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" charset="utf-8" />
|
||||||
<title id="title"></title>
|
<title id="title"></title>
|
||||||
|
<link rel="icon" href="{{ url_for('static', filename='favicon.ico') }}">
|
||||||
<!-- Material Icons -->
|
<!-- Material Icons -->
|
||||||
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet" />
|
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet" />
|
||||||
<!-- Material Symbols - Outlined Set -->
|
<!-- Material Symbols - Outlined Set -->
|
||||||
|
|||||||
@ -3,6 +3,7 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" charset="utf-8" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" charset="utf-8" />
|
||||||
<title id="title"></title>
|
<title id="title"></title>
|
||||||
|
<link rel="icon" href="{{ url_for('static', filename='favicon.ico') }}">
|
||||||
|
|
||||||
|
|
||||||
<!-- Material Icons -->
|
<!-- Material Icons -->
|
||||||
|
|||||||
@ -3,6 +3,7 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" charset="utf-8" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" charset="utf-8" />
|
||||||
<title id="title"></title>
|
<title id="title"></title>
|
||||||
|
<link rel="icon" href="{{ url_for('static', filename='favicon.ico') }}">
|
||||||
<!-- Material Icons -->
|
<!-- Material Icons -->
|
||||||
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet" />
|
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet" />
|
||||||
<!-- Material Symbols - Outlined Set -->
|
<!-- Material Symbols - Outlined Set -->
|
||||||
|
|||||||
@ -3,6 +3,7 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" charset="utf-8" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" charset="utf-8" />
|
||||||
<title id="title"></title>
|
<title id="title"></title>
|
||||||
|
<link rel="icon" href="{{ url_for('static', filename='favicon.ico') }}">
|
||||||
<!-- Material Icons -->
|
<!-- Material Icons -->
|
||||||
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet" />
|
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet" />
|
||||||
<!-- Material Symbols - Outlined Set -->
|
<!-- Material Symbols - Outlined Set -->
|
||||||
|
|||||||
@ -3,6 +3,7 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" charset="utf-8" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" charset="utf-8" />
|
||||||
<title id="title"></title>
|
<title id="title"></title>
|
||||||
|
<link rel="icon" href="{{ url_for('static', filename='favicon.ico') }}">
|
||||||
<!-- Material Icons -->
|
<!-- Material Icons -->
|
||||||
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet" />
|
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet" />
|
||||||
<!-- Material Symbols - Outlined Set -->
|
<!-- Material Symbols - Outlined Set -->
|
||||||
|
|||||||
Binary file not shown.
@ -123,7 +123,7 @@ def addSKULine():
|
|||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
item_id = int(request.get_json()['item_id'])
|
item_id = int(request.get_json()['item_id'])
|
||||||
receipt_id = int(request.get_json()['receipt_id'])
|
receipt_id = int(request.get_json()['receipt_id'])
|
||||||
|
print(item_id, receipt_id)
|
||||||
site_name = session['selected_site']
|
site_name = session['selected_site']
|
||||||
item = receipts_database.getItemAllByID(site_name, (item_id, ))
|
item = receipts_database.getItemAllByID(site_name, (item_id, ))
|
||||||
data = {
|
data = {
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
WITH passed_id AS (SELECT %s AS passed_id),
|
WITH passed_id AS (SELECT id AS passed_id, item_uuid as passed_uuid FROM %%site_name%%_items WHERE id=%s),
|
||||||
logistics_id AS (SELECT logistics_info_id FROM %%site_name%%_items WHERE id=(SELECT passed_id FROM passed_id)),
|
logistics_id AS (SELECT logistics_info_id FROM %%site_name%%_items WHERE id=(SELECT passed_id FROM passed_id)),
|
||||||
info_id AS (SELECT item_info_id FROM %%site_name%%_items WHERE id=(SELECT passed_id FROM passed_id)),
|
info_id AS (SELECT item_info_id FROM %%site_name%%_items WHERE id=(SELECT passed_id FROM passed_id)),
|
||||||
cte_conversions AS (
|
cte_conversions AS (
|
||||||
@ -33,12 +33,11 @@ WITH passed_id AS (SELECT %s AS passed_id),
|
|||||||
cte_shopping_lists AS (
|
cte_shopping_lists AS (
|
||||||
SELECT
|
SELECT
|
||||||
%%site_name%%_shopping_lists.*,
|
%%site_name%%_shopping_lists.*,
|
||||||
%%site_name%%_shopping_list_items.uuid,
|
|
||||||
%%site_name%%_shopping_list_items.item_type,
|
%%site_name%%_shopping_list_items.item_type,
|
||||||
%%site_name%%_shopping_list_items.qty
|
%%site_name%%_shopping_list_items.qty
|
||||||
FROM %%site_name%%_shopping_lists
|
FROM %%site_name%%_shopping_lists
|
||||||
JOIN %%site_name%%_shopping_list_items ON %%site_name%%_shopping_lists.id = %%site_name%%_shopping_list_items.sl_id
|
JOIN %%site_name%%_shopping_list_items ON %%site_name%%_shopping_lists.list_uuid = %%site_name%%_shopping_list_items.list_uuid
|
||||||
WHERE %%site_name%%_shopping_list_items.item_id = (SELECT passed_id FROM passed_id)
|
WHERE %%site_name%%_shopping_list_items.item_uuid = (SELECT passed_uuid FROM passed_id)
|
||||||
),
|
),
|
||||||
cte_itemlinks AS (
|
cte_itemlinks AS (
|
||||||
SELECT * FROM %%site_name%%_itemlinks WHERE link=(SELECT passed_id FROM passed_id)
|
SELECT * FROM %%site_name%%_itemlinks WHERE link=(SELECT passed_id FROM passed_id)
|
||||||
|
|||||||
@ -3,6 +3,7 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" charset="utf-8" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" charset="utf-8" />
|
||||||
<title id="title"></title>
|
<title id="title"></title>
|
||||||
|
<link rel="icon" href="{{ url_for('static', filename='favicon.ico') }}">
|
||||||
|
|
||||||
|
|
||||||
<!-- Material Icons -->
|
<!-- Material Icons -->
|
||||||
|
|||||||
@ -3,6 +3,7 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" charset="utf-8" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" charset="utf-8" />
|
||||||
<title id="title"></title>
|
<title id="title"></title>
|
||||||
|
<link rel="icon" href="{{ url_for('static', filename='favicon.ico') }}">
|
||||||
|
|
||||||
|
|
||||||
<!-- Material Icons -->
|
<!-- Material Icons -->
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
@ -309,6 +309,8 @@ def selectItemTupleByUUID(site, payload, convert=True, conn=None):
|
|||||||
def selectConversionTuple(site, payload, convert=True, conn=None):
|
def selectConversionTuple(site, payload, convert=True, conn=None):
|
||||||
"""payload=(item_id, uom_id)"""
|
"""payload=(item_id, uom_id)"""
|
||||||
selected = ()
|
selected = ()
|
||||||
|
if convert:
|
||||||
|
selected = {}
|
||||||
self_conn = False
|
self_conn = False
|
||||||
sql = f"SELECT conversions.conv_factor FROM {site}_conversions conversions WHERE item_id = %s AND uom_id = %s;"
|
sql = f"SELECT conversions.conv_factor FROM {site}_conversions conversions WHERE item_id = %s AND uom_id = %s;"
|
||||||
try:
|
try:
|
||||||
|
|||||||
@ -112,7 +112,9 @@ def process_recipe_receipt(site_name, user_id, data:dict, conn=None):
|
|||||||
rp_item_uom = item['uom']['id']
|
rp_item_uom = item['uom']['id']
|
||||||
item_stuff = database_recipes.selectItemTupleByUUID(site_name, (item['item_uuid'],), conn=conn)
|
item_stuff = database_recipes.selectItemTupleByUUID(site_name, (item['item_uuid'],), conn=conn)
|
||||||
conv_factor = database_recipes.selectConversionTuple(site_name, (item_stuff['item_id'], rp_item_uom))
|
conv_factor = database_recipes.selectConversionTuple(site_name, (item_stuff['item_id'], rp_item_uom))
|
||||||
qty = float(item['qty']) / float(conv_factor['conv_factor'])
|
print(conv_factor)
|
||||||
|
conversion = conv_factor.get('conv_factor', 1)
|
||||||
|
qty = float(item['qty']) / float(conversion)
|
||||||
payload = {
|
payload = {
|
||||||
'item_id': item_stuff['item_id'],
|
'item_id': item_stuff['item_id'],
|
||||||
'logistics_info_id': item_stuff['logistics_info_id'],
|
'logistics_info_id': item_stuff['logistics_info_id'],
|
||||||
@ -164,7 +166,7 @@ def postNewSkuFromRecipe(site_name: str, user_id: int, data: dict, conn=None):
|
|||||||
)
|
)
|
||||||
|
|
||||||
# create item info
|
# create item info
|
||||||
item_info = database_payloads.ItemInfoPayload(barcode=None)
|
item_info = database_payloads.ItemInfoPayload(barcode=None, uom=data['uom_id'], cost=data['cost'])
|
||||||
|
|
||||||
# create Food Info
|
# create Food Info
|
||||||
food_info = database_payloads.FoodInfoPayload()
|
food_info = database_payloads.FoodInfoPayload()
|
||||||
@ -184,6 +186,7 @@ def postNewSkuFromRecipe(site_name: str, user_id: int, data: dict, conn=None):
|
|||||||
links = {'main': data['main_link']}
|
links = {'main': data['main_link']}
|
||||||
search_string = f"&&{name}&&"
|
search_string = f"&&{name}&&"
|
||||||
|
|
||||||
|
print(item_info)
|
||||||
|
|
||||||
item = database_payloads.ItemsPayload(
|
item = database_payloads.ItemsPayload(
|
||||||
barcode=None,
|
barcode=None,
|
||||||
|
|||||||
@ -369,14 +369,14 @@ async function deleteInstruction(index){
|
|||||||
|
|
||||||
|
|
||||||
async function openNewSKUModal() {
|
async function openNewSKUModal() {
|
||||||
let itemsModal = document.getElementById('addNewSKUItem')
|
let addNewSKUItem = document.getElementById('addNewSKUItem')
|
||||||
document.getElementById('newSKUName').value = ""
|
document.getElementById('newSKUName').value = ""
|
||||||
document.getElementById('newSKUSubtype').value = "FOOD"
|
document.getElementById('newSKUSubtype').value = "FOOD"
|
||||||
document.getElementById('newSKUQty').value = 1
|
document.getElementById('newSKUQty').value = 1
|
||||||
document.getElementById('newSKUUOM').value = "1"
|
document.getElementById('newSKUUOM').value = "1"
|
||||||
document.getElementById('newWeblink').value = ""
|
document.getElementById('newWeblink').value = ""
|
||||||
document.getElementById('newSKUCost').value = 0.00
|
document.getElementById('newSKUCost').value = 0.00
|
||||||
UIkit.modal(itemsModal).show();
|
UIkit.modal(addNewSKUItem).show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -3,6 +3,7 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" charset="utf-8" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" charset="utf-8" />
|
||||||
<title id="title">Recipes</title>
|
<title id="title">Recipes</title>
|
||||||
|
<link rel="icon" href="{{ url_for('static', filename='favicon.ico') }}">
|
||||||
|
|
||||||
|
|
||||||
<!-- Material Icons -->
|
<!-- Material Icons -->
|
||||||
|
|||||||
@ -3,6 +3,7 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" charset="utf-8" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" charset="utf-8" />
|
||||||
<title id="title">Recipes</title>
|
<title id="title">Recipes</title>
|
||||||
|
<link rel="icon" href="{{ url_for('static', filename='favicon.ico') }}">
|
||||||
|
|
||||||
|
|
||||||
<!-- Material Icons -->
|
<!-- Material Icons -->
|
||||||
|
|||||||
@ -3,6 +3,7 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" charset="utf-8" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" charset="utf-8" />
|
||||||
<title id="title">Recipes</title>
|
<title id="title">Recipes</title>
|
||||||
|
<link rel="icon" href="{{ url_for('static', filename='favicon.ico') }}">
|
||||||
|
|
||||||
|
|
||||||
<!-- Material Icons -->
|
<!-- Material Icons -->
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
@ -29,6 +29,13 @@ def shopping_list(mode, list_uuid):
|
|||||||
return render_template("edit.html", list_uuid=list_uuid, current_site=session['selected_site'], sites=sites)
|
return render_template("edit.html", list_uuid=list_uuid, current_site=session['selected_site'], sites=sites)
|
||||||
return redirect("/")
|
return redirect("/")
|
||||||
|
|
||||||
|
@shopping_list_api.route("/generate")
|
||||||
|
@access_api.login_required
|
||||||
|
def generateList():
|
||||||
|
sites = [site[1] for site in postsqldb.get_sites(session['user']['sites'])]
|
||||||
|
units = postsqldb.get_units_of_measure()
|
||||||
|
return render_template("generate.html", current_site=session['selected_site'], sites=sites, units=units)
|
||||||
|
|
||||||
# API CALLS
|
# API CALLS
|
||||||
# Added to Database
|
# Added to Database
|
||||||
@shopping_list_api.route('/api/addList', methods=["POST"])
|
@shopping_list_api.route('/api/addList', methods=["POST"])
|
||||||
@ -107,7 +114,7 @@ def getShoppingListItem():
|
|||||||
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!'})
|
||||||
|
|
||||||
# Added to database
|
# Added to database
|
||||||
@shopping_list_api.route('/api/getItems', methods=["GET"])
|
@shopping_list_api.route('/api/getItemstwo', methods=["GET"])
|
||||||
@access_api.login_required
|
@access_api.login_required
|
||||||
def getItems():
|
def getItems():
|
||||||
recordset = []
|
recordset = []
|
||||||
@ -141,6 +148,40 @@ def getRecipesModal():
|
|||||||
return jsonify(status=201, recipes=recordsets, end=math.ceil(count/limit), message=f"Recipes fetched successfully!")
|
return jsonify(status=201, recipes=recordsets, end=math.ceil(count/limit), message=f"Recipes fetched successfully!")
|
||||||
return jsonify(status=405, recipes=recordsets, end=math.ceil(count/limit), message=f"{request.method} is not an accepted method on this endpoint!")
|
return jsonify(status=405, recipes=recordsets, end=math.ceil(count/limit), message=f"{request.method} is not an accepted method on this endpoint!")
|
||||||
|
|
||||||
|
@shopping_list_api.route('/api/getListsModal', methods=["GET"])
|
||||||
|
@access_api.login_required
|
||||||
|
def getListsModal():
|
||||||
|
recordsets = []
|
||||||
|
count = 0
|
||||||
|
if request.method == "GET":
|
||||||
|
page = int(request.args.get('page', 1))
|
||||||
|
limit = int(request.args.get('limit', 10))
|
||||||
|
search_string = request.args.get('search_string', 10)
|
||||||
|
site_name = session['selected_site']
|
||||||
|
offset = (page - 1) * limit
|
||||||
|
|
||||||
|
payload = (search_string, limit, offset)
|
||||||
|
recordsets, count = shoplist_database.getListsModal(site_name, payload)
|
||||||
|
return jsonify(status=201, lists=recordsets, end=math.ceil(count/limit), message=f"Recipes fetched successfully!")
|
||||||
|
return jsonify(status=405, lists=recordsets, end=math.ceil(count/limit), message=f"{request.method} is not an accepted method on this endpoint!")
|
||||||
|
|
||||||
|
|
||||||
|
@shopping_list_api.route("/api/getItems", methods=["GET"])
|
||||||
|
@access_api.login_required
|
||||||
|
def getItemsModal():
|
||||||
|
items = []
|
||||||
|
count = 0
|
||||||
|
if request.method == "GET":
|
||||||
|
page = int(request.args.get('page', 1))
|
||||||
|
limit = int(request.args.get('limit', 10))
|
||||||
|
search_string = request.args.get('search_string', 10)
|
||||||
|
site_name = session['selected_site']
|
||||||
|
offset = (page - 1) * limit
|
||||||
|
payload = (search_string, limit, offset)
|
||||||
|
items, count = shoplist_database.getItemsModal(site_name, payload)
|
||||||
|
return jsonify(status=201, items=items, end=math.ceil(count/limit), message=f"Items fetched successfully!")
|
||||||
|
return jsonify(status=405, items=items, end=math.ceil(count/limit), message=f"{request.method} is not an accepted method on this endpoint!")
|
||||||
|
|
||||||
|
|
||||||
# Added to database
|
# Added to database
|
||||||
@shopping_list_api.route('/api/postListItem', methods=["POST"])
|
@shopping_list_api.route('/api/postListItem', methods=["POST"])
|
||||||
@ -231,8 +272,24 @@ def getSKUItemsFull():
|
|||||||
'qty': float(float(item['item_info']['safety_stock']) - float(item['total_sum'])),
|
'qty': float(float(item['item_info']['safety_stock']) - float(item['total_sum'])),
|
||||||
'item_id': item['id'],
|
'item_id': item['id'],
|
||||||
'links': item['links'],
|
'links': item['links'],
|
||||||
'uom_fullname': item['uom_fullname']
|
'uom_fullname': item['uom_fullname'],
|
||||||
|
'list_item_state': False
|
||||||
}
|
}
|
||||||
items.append(new_item)
|
items.append(new_item)
|
||||||
return jsonify({"list_items":items, "error":False, "message":"items fetched succesfully!"})
|
return jsonify({"list_items":items, "error":False, "message":"items fetched succesfully!"})
|
||||||
return jsonify({"list_items":items, "error":True, "message":"There was an error with this GET statement"})
|
return jsonify({"list_items":items, "error":True, "message":"There was an error with this GET statement"})
|
||||||
|
|
||||||
|
# Added to Database
|
||||||
|
@shopping_list_api.route('/api/setListItemState', methods=["POST"])
|
||||||
|
@access_api.login_required
|
||||||
|
def setListItemState():
|
||||||
|
items = []
|
||||||
|
count = {'count': 0}
|
||||||
|
if request.method == "POST":
|
||||||
|
site_name = session['selected_site']
|
||||||
|
print(request.get_json())
|
||||||
|
|
||||||
|
shoplist_database.updateShoppingListItemsTuple(site_name, {'uuid': request.get_json()['list_item_uuid'], 'update': {'list_item_state': request.get_json()['list_item_state']}})
|
||||||
|
|
||||||
|
return jsonify({"list_items":items, "error":False, "message":"items fetched succesfully!"})
|
||||||
|
return jsonify({"list_items":items, "error":True, "message":"There was an error with this GET statement"})
|
||||||
|
|||||||
@ -227,6 +227,77 @@ def getRecipesModal(site, payload, convert=True, conn=None):
|
|||||||
except Exception as error:
|
except Exception as error:
|
||||||
raise postsqldb.DatabaseError(error, payload, sql)
|
raise postsqldb.DatabaseError(error, payload, sql)
|
||||||
|
|
||||||
|
def getListsModal(site, payload, convert=True, conn=None):
|
||||||
|
recordsets = []
|
||||||
|
count = 0
|
||||||
|
self_conn = False
|
||||||
|
|
||||||
|
|
||||||
|
sql = f"SELECT lists.list_uuid, lists.name FROM {site}_shopping_lists lists WHERE lists.name LIKE '%%' || %s || '%%' LIMIT %s OFFSET %s;"
|
||||||
|
sql_count = f"SELECT COUNT(*) FROM {site}_shopping_lists lists WHERE lists.name LIKE '%%' || %s || '%%';"
|
||||||
|
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:
|
||||||
|
recordsets = [postsqldb.tupleDictionaryFactory(cur.description, row) for row in rows]
|
||||||
|
if rows and not convert:
|
||||||
|
recordsets = rows
|
||||||
|
|
||||||
|
|
||||||
|
cur.execute(sql_count, (payload[0], ))
|
||||||
|
count = cur.fetchone()[0]
|
||||||
|
|
||||||
|
if self_conn:
|
||||||
|
conn.close()
|
||||||
|
|
||||||
|
return recordsets, count
|
||||||
|
|
||||||
|
except Exception as error:
|
||||||
|
raise postsqldb.DatabaseError(error, payload, sql)
|
||||||
|
|
||||||
|
|
||||||
|
def getItemsModal(site, payload, convert=True, conn=None):
|
||||||
|
recordsets = []
|
||||||
|
count = 0
|
||||||
|
self_conn = False
|
||||||
|
with open(f"application/shoppinglists/sql/getItemsForModal.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.fetchall()
|
||||||
|
if rows and convert:
|
||||||
|
recordsets = [postsqldb.tupleDictionaryFactory(cur.description, row) for row in rows]
|
||||||
|
if rows and not convert:
|
||||||
|
recordsets = rows
|
||||||
|
|
||||||
|
cur.execute(f"SELECT COUNT(*) FROM {site}_items WHERE search_string LIKE '%%' || %s || '%%';", (payload[0], ))
|
||||||
|
count = cur.fetchone()[0]
|
||||||
|
|
||||||
|
if self_conn:
|
||||||
|
conn.close()
|
||||||
|
|
||||||
|
return recordsets, count
|
||||||
|
|
||||||
|
except Exception as error:
|
||||||
|
raise postsqldb.DatabaseError(error, payload, sql)
|
||||||
|
|
||||||
def deleteShoppingListItemsTuple(site_name, payload, convert=True, conn=None):
|
def deleteShoppingListItemsTuple(site_name, payload, convert=True, conn=None):
|
||||||
deleted = ()
|
deleted = ()
|
||||||
self_conn = False
|
self_conn = False
|
||||||
|
|||||||
11
application/shoppinglists/sql/getItemsForModal.sql
Normal file
11
application/shoppinglists/sql/getItemsForModal.sql
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
SELECT items.item_uuid as item_uuid,
|
||||||
|
items.item_name as item_name,
|
||||||
|
units.fullname AS fullname,
|
||||||
|
units.id AS unit_id,
|
||||||
|
items.links AS links
|
||||||
|
FROM %%site_name%%_items items
|
||||||
|
LEFT JOIN %%site_name%%_item_info item_info ON item_info.id = items.item_info_id
|
||||||
|
LEFT JOIN units ON item_info.uom = units.id
|
||||||
|
WHERE items.search_string LIKE '%%' || %s || '%%'
|
||||||
|
ORDER BY items.item_name
|
||||||
|
LIMIT %s OFFSET %s;
|
||||||
1077
application/shoppinglists/static/js/shoppingListGeneratorHandler.js
Normal file
1077
application/shoppinglists/static/js/shoppingListGeneratorHandler.js
Normal file
File diff suppressed because it is too large
Load Diff
@ -21,13 +21,17 @@ async function replenishForm(shopping_list){
|
|||||||
async function replenishLineTable(sl_items){
|
async function replenishLineTable(sl_items){
|
||||||
let listItemsTableBody = document.getElementById('listItemsTableBody')
|
let listItemsTableBody = document.getElementById('listItemsTableBody')
|
||||||
listItemsTableBody.innerHTML = ""
|
listItemsTableBody.innerHTML = ""
|
||||||
|
console.log(sl_items)
|
||||||
|
|
||||||
for(let i = 0; i < sl_items.length; i++){
|
for(let i = 0; i < sl_items.length; i++){
|
||||||
let tableRow = document.createElement('tr')
|
let tableRow = document.createElement('tr')
|
||||||
|
|
||||||
let checkboxCell = document.createElement('td')
|
let checkboxCell = document.createElement('td')
|
||||||
checkboxCell.innerHTML = `<label><input class="uk-checkbox" type="checkbox"></label>`
|
checkboxCell.innerHTML = `<label><input class="uk-checkbox" type="checkbox" ${sl_items[i].list_item_state ? 'checked' : ''}></label>`
|
||||||
|
checkboxCell.onclick = async function (event) {
|
||||||
|
await updateListItemState(sl_items[i].list_item_uuid, event.target.checked)
|
||||||
|
}
|
||||||
|
|
||||||
namefield = sl_items[i].item_name
|
namefield = sl_items[i].item_name
|
||||||
if(sl_items[i].links.hasOwnProperty('main')){
|
if(sl_items[i].links.hasOwnProperty('main')){
|
||||||
namefield = `<a href=${sl_items[i].links.main} target='_blank'>${sl_items[i].item_name}</a>`
|
namefield = `<a href=${sl_items[i].links.main} target='_blank'>${sl_items[i].item_name}</a>`
|
||||||
@ -37,8 +41,9 @@ async function replenishLineTable(sl_items){
|
|||||||
nameCell.innerHTML = namefield
|
nameCell.innerHTML = namefield
|
||||||
|
|
||||||
let qtyuomCell = document.createElement('td')
|
let qtyuomCell = document.createElement('td')
|
||||||
qtyuomCell.innerHTML = `${sl_items[i].qty} ${sl_items[i].uom_fullname}`
|
qtyuomCell.innerHTML = `${sl_items[i].qty} ${sl_items[i].uom.fullname}`
|
||||||
|
|
||||||
|
checkboxCell.checked = sl_items[i].list_item_state
|
||||||
tableRow.append(checkboxCell, nameCell, qtyuomCell)
|
tableRow.append(checkboxCell, nameCell, qtyuomCell)
|
||||||
listItemsTableBody.append(tableRow)
|
listItemsTableBody.append(tableRow)
|
||||||
}
|
}
|
||||||
@ -65,4 +70,18 @@ async function fetchItemsFullCalculated() {
|
|||||||
const response = await fetch(url);
|
const response = await fetch(url);
|
||||||
data = await response.json();
|
data = await response.json();
|
||||||
return data.list_items;
|
return data.list_items;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function updateListItemState(list_item_uuid, state){
|
||||||
|
console.log(list_item_uuid, state)
|
||||||
|
const response = await fetch(`/shopping-lists/api/setListItemState`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
list_item_uuid: list_item_uuid,
|
||||||
|
list_item_state: state
|
||||||
|
}),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
10
application/shoppinglists/static/quick-lists/MRP.json
Normal file
10
application/shoppinglists/static/quick-lists/MRP.json
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"name": "MRP - %DATE%",
|
||||||
|
"description": "This is a shopping list generated using Full Calculated SKUs Operator Only! Generated on %DATE%",
|
||||||
|
"custom_items": [],
|
||||||
|
"uncalculated_items": [],
|
||||||
|
"calculated_items": [],
|
||||||
|
"recipes": [],
|
||||||
|
"planner": [],
|
||||||
|
"full_sku_enabled": true
|
||||||
|
}
|
||||||
@ -3,7 +3,8 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" charset="utf-8" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" charset="utf-8" />
|
||||||
<title id="title"></title>
|
<title id="title"></title>
|
||||||
|
<link rel="icon" href="{{ url_for('static', filename='favicon.ico') }}">
|
||||||
|
|
||||||
|
|
||||||
<!-- Material Icons -->
|
<!-- Material Icons -->
|
||||||
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet" />
|
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet" />
|
||||||
|
|||||||
660
application/shoppinglists/templates/generate.html
Normal file
660
application/shoppinglists/templates/generate.html
Normal file
@ -0,0 +1,660 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en" dir="ltr" id="main_html">
|
||||||
|
<head>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" charset="utf-8" />
|
||||||
|
<title id="title"></title>
|
||||||
|
<link rel="icon" href="{{ url_for('static', filename='favicon.ico') }}">
|
||||||
|
|
||||||
|
<!-- Material Icons -->
|
||||||
|
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet" />
|
||||||
|
<!-- Material Symbols - Outlined Set -->
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined" rel="stylesheet" />
|
||||||
|
<!-- Material Symbols - Rounded Set -->
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Rounded" rel="stylesheet" />
|
||||||
|
<!-- Material Symbols - Sharp Set -->
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Sharp" rel="stylesheet" />
|
||||||
|
|
||||||
|
<link rel="stylesheet" href="{{ url_for('static', filename='css/uikit.min.css') }}"/>
|
||||||
|
{% if session['user']['flags']['darkmode'] %}
|
||||||
|
<link id="dark-mode" rel="stylesheet" href="{{ url_for('static', filename='css/dark-mode.css') }}"/>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
|
||||||
|
<script src="{{ url_for('static', filename='js/uikit.min.js') }}"></script>
|
||||||
|
<script src="{{ url_for('static', filename='js/uikit-icons.min.js') }}"></script>
|
||||||
|
|
||||||
|
</head>
|
||||||
|
<style>
|
||||||
|
:root {
|
||||||
|
--primary-color: {{ session['user']['flags']['styles']['primary_color']}};
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
{% if session['user']['flags']['darkmode'] %}
|
||||||
|
<body class="uk-light">
|
||||||
|
{% else %}
|
||||||
|
<body>
|
||||||
|
{% endif %}
|
||||||
|
<nav class="uk-navbar-container">
|
||||||
|
<div class="uk-container uk-container-expand">
|
||||||
|
<div class="uk-navbar uk-navbar-primary">
|
||||||
|
<!-- Application Navigation-->
|
||||||
|
<div class="uk-navbar-left">
|
||||||
|
<ul class="uk-navbar-nav">
|
||||||
|
<li>
|
||||||
|
<a href>Apps</a>
|
||||||
|
<div class="uk-navbar-dropdown" uk-drop="mode: click; multi:false">
|
||||||
|
<ul class="uk-nav uk-navbar-dropdown-nav">
|
||||||
|
<li><a href="/planner">Planner</a></li>
|
||||||
|
<li><a href="/recipes">Recipes</a></li>
|
||||||
|
<li><a href="/shopping-lists">Shopping Lists</a></li>
|
||||||
|
<li class="uk-nav-header">Logistics</li>
|
||||||
|
<li><a href="/items">Items</a></li>
|
||||||
|
<li><a href="/items/transaction">Transaction</a></li>
|
||||||
|
<li><a href="/receipts">Receipts</a></li>
|
||||||
|
<li class="uk-nav-header">Points of Ease</li>
|
||||||
|
<li><a href="/poe/scanner">Transaction Scanner</a></li>
|
||||||
|
<li><a href="/poe/receipts">Receipts Scanner</a></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<!-- Breadcrumbs Navigation -->
|
||||||
|
<div class="uk-navbar-center uk-visible@m">
|
||||||
|
<ul class="uk-breadcrumb uk-margin-remove">
|
||||||
|
<li class="uk-disabled" style="cursor: pointer;"><span><strong>{{current_site}}</strong></span>
|
||||||
|
<div uk-dropdown="mode: hover">
|
||||||
|
<ul class="uk-nav uk-dropdown-nav">
|
||||||
|
<li class="uk-nav-header">Select Site</li>
|
||||||
|
<li class="uk-nav-divider"></li>
|
||||||
|
{% for site in sites %}
|
||||||
|
{% if site == current_site %}
|
||||||
|
<li><a class="uk-disabled" href="#">{{site}}</a></li>
|
||||||
|
{% else %}
|
||||||
|
<li><a onclick="changeSite('{{site}}')">{{site}}</a></li>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
<li style="cursor: default; user-select: none;" class="uk-disabled"><span>Modules</span></li>
|
||||||
|
<li class="uk-disabled"><span>Shopping Lists</span></li>
|
||||||
|
<li class="uk-disabled"><span>Editing Shopping List</span></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<!-- Profile/Management Navigation-->
|
||||||
|
<div class="uk-navbar-right">
|
||||||
|
<ul class="uk-navbar-nav">
|
||||||
|
<li>
|
||||||
|
<a href="#">
|
||||||
|
<img src="{{session['user']['profile_pic_url']}}" alt="Profile Picture" class="profile-pic uk-visible@m" style="width: 40px; height: 40px; border-radius: 50%; margin-right: 5px;">
|
||||||
|
{{username}}
|
||||||
|
</a>
|
||||||
|
<div class="uk-navbar-dropdown" uk-drop="mode: click; multi:false">
|
||||||
|
<ul class="uk-nav uk-navbar-dropdown-nav">
|
||||||
|
<li><a href="/profile">Profile</a></li>
|
||||||
|
<li><a onclick="toggleDarkMode()">Dark Mode</a></li>
|
||||||
|
<li><a href="/site-management">Site Management</a></li>
|
||||||
|
<li><a href="/administration">System Management</a></li>
|
||||||
|
<li><a href="/access/logout">Logout</a></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
<div class="uk-container">
|
||||||
|
<div class="uk-section">
|
||||||
|
<div class="uk-grid-small" uk-grid>
|
||||||
|
<div class="uk-width-1-1">
|
||||||
|
<h1 class="uk-heading-medium uk-heading-divider">Generate Shopping List</h1>
|
||||||
|
</div>
|
||||||
|
<div class="uk-width-1-1">
|
||||||
|
<p class="uk-text-small">Generating a shopping list is as powerful as you wish to make it. In this form you will make the choices of what and when you want to
|
||||||
|
generate the list for. You will be adding specific items in the system, even can add all the safety stock items or specific ones, recipes, even other shopping lists
|
||||||
|
can be added to this one list. This is a snapshot of a moment though and any calculated safety stocks become static and can change if there is a long wait.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<!-- type section -->
|
||||||
|
<div class="uk-width-1-1">
|
||||||
|
<h1 class="uk-heading-xsmall uk-heading-divider">Shopping List Type</h1>
|
||||||
|
</div>
|
||||||
|
<div class="uk-width-1-1">
|
||||||
|
<p class="uk-text-small">Choosing the shopping list type informs the system of your intent when actually using the list when viewed. It will inform the system if it should
|
||||||
|
completely delete the list (known as a "Temporary" list) or if it should just change the states of all items to new (known as a "Permanent" list). Permanent lists would be used
|
||||||
|
for reoccuring lists that never change and can be reused. Temporary lists will generally be used when generating a huge one time list, or something small that you just want to
|
||||||
|
track quickly.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="uk-width-1-1">
|
||||||
|
<label class="uk-form-label" for="generated_list_type">Select List Type</label>
|
||||||
|
<select id="generated_list_type" class="uk-select" aria-label="Select">
|
||||||
|
<option value="temporary" selected>Temporary</option>
|
||||||
|
<option value="permanent">Permanent</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<!-- basic info section -->
|
||||||
|
<div class="uk-width-1-1">
|
||||||
|
<h1 class="uk-heading-xsmall uk-heading-divider">Shopping List Type</h1>
|
||||||
|
</div>
|
||||||
|
<div class="uk-width-1-1">
|
||||||
|
<p class="uk-text-small">Fill out the basic info asked for here, the description could be helpful to remind yourself and others what the list was generated for.
|
||||||
|
The shopping list name must be unique!
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="uk-width-1-1">
|
||||||
|
<label class="uk-form-label" for="generated_list_name">Shopping List Name</label>
|
||||||
|
<div class="uk-form-controls">
|
||||||
|
<input class="uk-input" id="generated_list_name" type="text" placeholder="Enter list name here...">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="uk-width-1-1">
|
||||||
|
<label class="uk-form-label" for="generated_list_name">Shopping List Description</label>
|
||||||
|
<div class="uk-form-controls">
|
||||||
|
<textarea id="generated_list_name" class="uk-textarea" rows="5" placeholder="Enter list description here..." aria-label="Textarea"></textarea>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- Part section -->
|
||||||
|
<div class="uk-width-1-1">
|
||||||
|
<h1 class="uk-heading-xsmall uk-heading-divider">Shopping List Operators</h1>
|
||||||
|
</div>
|
||||||
|
<div class="uk-width-1-1">
|
||||||
|
<p class="uk-text-small"> Operators are what drives the engine that is a shopping list. You can configure each on specifically for the list you
|
||||||
|
want to generate and each will operate in their own manner behind the scenes.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="uk-width-1-1">
|
||||||
|
<button class="uk-button uk-button-primary" type="button">Add Operator</button>
|
||||||
|
<div uk-dropdown>
|
||||||
|
<ul class="uk-nav uk-dropdown-nav">
|
||||||
|
<li class="uk-nav-header">Active Operators</li>
|
||||||
|
<li class="uk-nav-divider"></li>
|
||||||
|
<li><a onclick="addCustomItemsCard()" uk-tooltip="title: Create Custom items to add into the generated list.; pos: right">Custom Items</a></li>
|
||||||
|
<li><a onclick="addUncalculatedItemsCard()" uk-tooltip="title: Add items from the system that take a static quantity into the generated list.; pos: right">Non-Calculated System Items</a></li>
|
||||||
|
<li><a onclick="addCalculatedItemsCard()" uk-tooltip="title: Add items from the system that calculate quantity using quantity on hand and a set safety stocks into the generated list.; pos: right">Calculated System Items</a></li>
|
||||||
|
<li><a onclick="addRecipesCard()" uk-tooltip="title: Add Recipes that will take all the ingrediants and add them into the generated list.; pos: right">System Recipes</a></li>
|
||||||
|
<li><a onclick="addFullSKUCard()" uk-tooltip="title: Takes a full safety stock count from the system and adds any quantities below their safety stocks into the generated list.; pos: right">Full System Calculated</a></li>
|
||||||
|
<li><a class="uk-disabled" uk-tooltip="title: Uses a date range and selected planners for each to add any planned recipes into the generated list.; pos: right">Site Planners</a></li>
|
||||||
|
<li><a onclick="addListsCard()" uk-tooltip="title: Combine already made lists into this one; pos: right">Shopping Lists</a></li>
|
||||||
|
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="uk-width-1-1">
|
||||||
|
<div id="operators_deck" class="uk-grid-small uk-child-width-1-1" uk-grid>
|
||||||
|
<!-- Items Custom -->
|
||||||
|
<div id="customItemsCard" hidden>
|
||||||
|
<div class="uk-card uk-card-default uk-card-small uk-card-body">
|
||||||
|
<h3>Custom Items Operator
|
||||||
|
<span class="">
|
||||||
|
<button onclick="changeCustomZoneState()" class="uk-button uk-button-small" title="Show/Hide the card body." uk-tooltip>Show/Hide</button>
|
||||||
|
</span>
|
||||||
|
<span class="uk-align-right">
|
||||||
|
<button onclick="removeCustomItemsCard()" class="uk-button uk-button-small" title="Will remove the custom items card and data from the list." uk-tooltip>Remove</button>
|
||||||
|
</span>
|
||||||
|
</h3>
|
||||||
|
<p class="uk-text-meta">A custom Item is an item that does not exist in your system but you would like to show up on your shopping list. This is useful for items
|
||||||
|
you dont often have to buy or do not want to keep track of but give the users the ability to see it.
|
||||||
|
</p>
|
||||||
|
<div id="customCardZone">
|
||||||
|
<table class="uk-table uk-table-striped">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Name</th>
|
||||||
|
<th>Qty/UOM</th>
|
||||||
|
<th>Operations</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody id="customItemsTableBody"></tbody>
|
||||||
|
</table>
|
||||||
|
<button onclick="openCustomItemsModal()" class="uk-button uk-button-secondary uk-width-1-1">Add Item</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- Items Uncalculated -->
|
||||||
|
<div id="uncalcedItemsCard" hidden>
|
||||||
|
<div class="uk-card uk-card-default uk-card-small uk-card-body">
|
||||||
|
<h3>Un-Calculated System Item Operator
|
||||||
|
<span class="">
|
||||||
|
<button onclick="changeUncalculatedZoneState()" class="uk-button uk-button-small" title="Show/Hide the card body." uk-tooltip>Show/Hide</button>
|
||||||
|
</span>
|
||||||
|
<span class="uk-align-right">
|
||||||
|
<button onclick="removeUncalcedItemsCard()" class="uk-button uk-button-small" title="Will remove the uncalculated system items card and data from the list." uk-tooltip >Remove</button>
|
||||||
|
</span>
|
||||||
|
</h3>
|
||||||
|
<p class="uk-text-meta"> An Uncalulated System Item is an item choosen from the configured SKUs in your
|
||||||
|
system. The uncalculated part means the quantity asked for in the list will always remain to be what
|
||||||
|
you set it to during configuration of the list or while editing.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div id="uncalculatedCardZone">
|
||||||
|
<table class="uk-table uk-table-striped">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Name</th>
|
||||||
|
<th>Qty/UOM</th>
|
||||||
|
<th>Operations</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody id="uncalculatedItemsTableBody"></tbody>
|
||||||
|
</table>
|
||||||
|
<button onclick="openUncalculatedItemsModal()" class="uk-button uk-button-secondary uk-width-1-1">Add Item</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- Items Calculated -->
|
||||||
|
<div id="calculatedItemsCard" hidden>
|
||||||
|
<div class="uk-card uk-card-default uk-card-small uk-card-body">
|
||||||
|
<h3>Calculated System Item Operator
|
||||||
|
<span class="">
|
||||||
|
<button onclick="changeCalculatedZoneState()" class="uk-button uk-button-small" title="Show/Hide the card body." uk-tooltip>Show/Hide</button>
|
||||||
|
</span>
|
||||||
|
<span class="uk-align-right">
|
||||||
|
<button onclick="removeCalculatedItemsCard()" class="uk-button uk-button-small" title="Will remove the calculated system items card and data from the list." uk-tooltip >Remove</button>
|
||||||
|
</span>
|
||||||
|
</h3>
|
||||||
|
<p class="uk-text-meta"> A Calculated System Item is an item choosen from the configured SKUs in your
|
||||||
|
system. The calculated part means the quantity asked for in the list will be calculated at the time
|
||||||
|
that the list is generated based on set safety stock and quantity on hand.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div id="calculatedCardZone">
|
||||||
|
<table class="uk-table uk-table-striped">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Name</th>
|
||||||
|
<th>Operations</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody id="calculatedItemsTableBody"></tbody>
|
||||||
|
</table>
|
||||||
|
<button onclick="openCalculatedItemsModal()" class="uk-button uk-button-secondary uk-width-1-1">Add Item</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- Recipes -->
|
||||||
|
<div id="recipesCard" hidden>
|
||||||
|
<div class="uk-card uk-card-default uk-card-small uk-card-body">
|
||||||
|
<h3>Recipes Operator
|
||||||
|
<span class="">
|
||||||
|
<button onclick="changeRecipesZoneState()" class="uk-button uk-button-small" title="Show/Hide the card body." uk-tooltip>Show/Hide</button>
|
||||||
|
</span>
|
||||||
|
<span class="uk-align-right">
|
||||||
|
<button onclick="removeRecipesItemsCard()" class="uk-button uk-button-small" title="Will remove the Recipes card and data from the list." uk-tooltip >Remove</button>
|
||||||
|
</span>
|
||||||
|
</h3>
|
||||||
|
<p class="uk-text-meta">Recipes can be built into the system and when added through this operator each ingrediant will be added to the list at the recipes quantity and UOM.</p>
|
||||||
|
|
||||||
|
<div id="recipesCardZone">
|
||||||
|
<table class="uk-table uk-table-striped">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Name</th>
|
||||||
|
<th>Operations</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody id="recipesTableBody"></tbody>
|
||||||
|
</table>
|
||||||
|
<button onclick="openRecipesModal()" class="uk-button uk-button-secondary uk-width-1-1">Add Item</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- Shopping List -->
|
||||||
|
<div id="listsCard" hidden>
|
||||||
|
<div class="uk-card uk-card-default uk-card-small uk-card-body">
|
||||||
|
<h3>Shopping Lists Operator
|
||||||
|
<span class="">
|
||||||
|
<button onclick="changeListsZoneState()" class="uk-button uk-button-small" title="Show/Hide the card body." uk-tooltip>Show/Hide</button>
|
||||||
|
</span>
|
||||||
|
<span class="uk-align-right">
|
||||||
|
<button onclick="removeListsItemsCard()" class="uk-button uk-button-small" title="Will remove the Recipes card and data from the list." uk-tooltip >Remove</button>
|
||||||
|
</span>
|
||||||
|
</h3>
|
||||||
|
<p class="uk-text-meta">This operator allows you to select other shopping lists that already exist to have their
|
||||||
|
items added onto this lists items.
|
||||||
|
</p>
|
||||||
|
<div id="listsCardZone">
|
||||||
|
<table class="uk-table uk-table-striped">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Name</th>
|
||||||
|
<th>Operations</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody id="listsTableBody"></tbody>
|
||||||
|
</table>
|
||||||
|
<button onclick="openListsModal()" class="uk-button uk-button-secondary uk-width-1-1">Add Item</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- Full Calculated SKU -->
|
||||||
|
<div id="fullSKUCard" hidden>
|
||||||
|
<div class="uk-card uk-card-default uk-card-small uk-card-body">
|
||||||
|
<h3>Full Calculation Operator
|
||||||
|
<span class="uk-align-right">
|
||||||
|
<button onclick="removeFullSKUCard()" class="uk-button uk-button-small" title="Will remove the Recipes card and data from the list." uk-tooltip >Remove</button>
|
||||||
|
</span>
|
||||||
|
</h3>
|
||||||
|
<p class="uk-text-meta">Full SKU calculations will go through your whole system and grab all items that have safety stocks.
|
||||||
|
It will then use the safety stocks and quantity on hand to determine if you need to purchase more to add to the list. With
|
||||||
|
this in mind you CANNOT have full SKU calculation enabled and also have the Calculated Items Operator.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div id="fullSKUZone" uk-grid>
|
||||||
|
<div class="uk-width-1-1">
|
||||||
|
<label><input autocomplete="off" id="fullSKUCheckbox" onclick="fullSKUEnabledChange(event)" class="uk-checkbox" type="checkbox"> Enabled</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="modals_deck">
|
||||||
|
<!-- Custom Item Modal -->
|
||||||
|
<div id="CustomItemModal" class="uk-modal">
|
||||||
|
<div class="uk-modal-dialog uk-modal-body">
|
||||||
|
<h2 class="uk-modal-title">Add Custom Line...</h2>
|
||||||
|
<p class="uk-text-small">A custom Item is an item that does not exist in your system but you would like to show up on your shopping list. This is useful for items
|
||||||
|
you dont often have to buy or do not want to keep track of but give the users the ability to see it.
|
||||||
|
</p>
|
||||||
|
<table class="uk-table uk-table-responsive uk-table-striped">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th></th>
|
||||||
|
<th></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>Name</td>
|
||||||
|
<td><input id="customName" class="uk-input" type="text"></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>QTY</td>
|
||||||
|
<td><input id="customQty" class="uk-input" type="number"></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>UOM</td>
|
||||||
|
<td>
|
||||||
|
<select id="customUnit" class="uk-select" aria-label="Select">
|
||||||
|
{% for unit in units %}
|
||||||
|
<option value={{unit['id']}} data-fullname={{unit['fullname']}}>{{unit['fullname']}}</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Main Link</td>
|
||||||
|
<td><input id="customLink" class="uk-input" type="text"></td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<p class="uk-text-right">
|
||||||
|
<button class="uk-button uk-button-default uk-modal-close" type="button">Cancel</button>
|
||||||
|
<button id="customItemModalButton" onclick="addCustomItem()" class="uk-button uk-button-primary" type="button">Add Line</button>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- Uncalculated Item Modals -->
|
||||||
|
<div id="uncalculatedItemModal" class="uk-flex-top" uk-modal>
|
||||||
|
<div class="uk-modal-dialog">
|
||||||
|
<button class="uk-modal-close-default" type="button" uk-close></button>
|
||||||
|
<div class="uk-modal-header">
|
||||||
|
<h2 class="uk-modal-title">System Item Search</h2>
|
||||||
|
</div>
|
||||||
|
<div id="paginationModalBody" class="uk-modal-body">
|
||||||
|
<div class="uk-grid-small" uk-grid>
|
||||||
|
<div class="uk-width-1-1">
|
||||||
|
<p class="uk-text-small"> An Uncalulated System Item is an item choosen from the configured SKUs in your
|
||||||
|
system. The uncalculated part means the quantity asked for in the list will always remain to be what
|
||||||
|
you set it to during configuration of the list or while editing.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="uk-width-1-1">
|
||||||
|
<div id="searchItemsForm" onkeydown="searchItemTable(event)" class="uk-search uk-search-default uk-align-center">
|
||||||
|
<input id="searchItemsInput" class="uk-border-pill uk-search-input" type="search" placeholder="" aria-label="">
|
||||||
|
<span class="uk-search-icon-flip" uk-search-icon></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="uk-width-1-1">
|
||||||
|
<nav aria-label="Pagination">
|
||||||
|
<ul id="itemsPage" class="uk-pagination uk-flex-center" uk-margin>
|
||||||
|
<li><a href="#"><span uk-pagination-previous></span></a></li>
|
||||||
|
<li><a href="#">1</a></li>
|
||||||
|
<li class="uk-disabled"><span>…</span></li>
|
||||||
|
<li><a href="#">5</a></li>
|
||||||
|
<li><a href="#">6</a></li>
|
||||||
|
<li class="uk-active"><span aria-current="page">7</span></li>
|
||||||
|
<li><a href="#">8</a></li>
|
||||||
|
<li><a href="#"><span uk-pagination-next></span></a></li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
<div class="uk-width-1-1">
|
||||||
|
<caption>Select an Item from the system...</caption>
|
||||||
|
<table class="uk-table uk-table-striped">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Name</th>
|
||||||
|
<th>Qty</th>
|
||||||
|
<th>UOM</th>
|
||||||
|
<th>Operations</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody id="uncalculatedItemsModalTableBody">
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="uncalculatedItemModalEdit" class="uk-modal">
|
||||||
|
<div class="uk-modal-dialog uk-modal-body">
|
||||||
|
<h2 class="uk-modal-title">Edit Uncalulated Item...</h2>
|
||||||
|
<p class="uk-text-small"> An Uncalulated System Item is an item choosen from the configured SKUs in your
|
||||||
|
system. The uncalculated part means the quantity asked for in the list will always remain to be what
|
||||||
|
you set it to during configuration of the list or while editing.
|
||||||
|
</p>
|
||||||
|
<div class="uk-alert uk-alert-warning">While editing System Items the <strong>Name</strong> and <strong>UOM</strong> inputs will be Disabled!</div>
|
||||||
|
<table class="uk-table uk-table-responsive uk-table-striped">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th></th>
|
||||||
|
<th></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>Name</td>
|
||||||
|
<td><input id="uncalculatedItemName" class="uk-input uk-disabled" type="text"></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>QTY</td>
|
||||||
|
<td><input id="uncalculatedItemQty" class="uk-input" type="number"></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>UOM</td>
|
||||||
|
<td>
|
||||||
|
<select id="uncalculatedItemUnit" class="uk-select uk-disabled" aria-label="Select">
|
||||||
|
{% for unit in units %}
|
||||||
|
<option value={{unit['id']}} data-fullname={{unit['fullname']}}>{{unit['fullname']}}</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Main Link</td>
|
||||||
|
<td><input id="uncalculatedItemLink" class="uk-input" type="text"></td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<p class="uk-text-right">
|
||||||
|
<button class="uk-button uk-button-default uk-modal-close" type="button">Cancel</button>
|
||||||
|
<button id="uncalculatedItemModalEditButton" class="uk-button uk-button-primary" type="button">Add Line</button>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- Calculated Item Modals -->
|
||||||
|
<div id="calculatedItemModal" class="uk-flex-top" uk-modal>
|
||||||
|
<div class="uk-modal-dialog">
|
||||||
|
<button class="uk-modal-close-default" type="button" uk-close></button>
|
||||||
|
<div class="uk-modal-header">
|
||||||
|
<h2 class="uk-modal-title">System Item Search</h2>
|
||||||
|
</div>
|
||||||
|
<div class="uk-modal-body">
|
||||||
|
<div class="uk-grid-small" uk-grid>
|
||||||
|
<div class="uk-width-1-1">
|
||||||
|
<p class="uk-text-small"> A Calculated System Item is an item choosen from the configured SKUs in your
|
||||||
|
system. The calculated part means the quantity asked for in the list will be calculated at the time
|
||||||
|
that the list is generated based on set safety stock and quantity on hand.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="uk-width-1-1">
|
||||||
|
<div id="searcCalculatedItemsForm" onkeydown="searchCalculatedItemTable(event)" class="uk-search uk-search-default uk-align-center">
|
||||||
|
<input id="searchCalculatedItemsInput" class="uk-border-pill uk-search-input" type="search" placeholder="" aria-label="">
|
||||||
|
<span class="uk-search-icon-flip" uk-search-icon></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="uk-width-1-1">
|
||||||
|
<nav aria-label="Pagination">
|
||||||
|
<ul id="itemsCalculatedPage" class="uk-pagination uk-flex-center" uk-margin>
|
||||||
|
<li><a href="#"><span uk-pagination-previous></span></a></li>
|
||||||
|
<li><a href="#">1</a></li>
|
||||||
|
<li class="uk-disabled"><span>…</span></li>
|
||||||
|
<li><a href="#">5</a></li>
|
||||||
|
<li><a href="#">6</a></li>
|
||||||
|
<li class="uk-active"><span aria-current="page">7</span></li>
|
||||||
|
<li><a href="#">8</a></li>
|
||||||
|
<li><a href="#"><span uk-pagination-next></span></a></li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
<div class="uk-width-1-1">
|
||||||
|
<caption>Select an Item from the system...</caption>
|
||||||
|
<table class="uk-table uk-table-striped">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Name</th>
|
||||||
|
<th>Operations</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody id="calculatedItemsModalTableBody">
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- Recipes Modals -->
|
||||||
|
<div id="recipesModal" class="uk-flex-top" uk-modal>
|
||||||
|
<div class="uk-modal-dialog">
|
||||||
|
<button class="uk-modal-close-default" type="button" uk-close></button>
|
||||||
|
<div class="uk-modal-header">
|
||||||
|
<h2 class="uk-modal-title">System Recipes Search</h2>
|
||||||
|
</div>
|
||||||
|
<div class="uk-modal-body">
|
||||||
|
<div class="uk-grid-small" uk-grid>
|
||||||
|
<div class="uk-width-1-1">
|
||||||
|
<p class="uk-text-small">Recipes can be built into the system and when added through this operator each ingrediant will be added to the list at the recipes quantity and UOM.</p>
|
||||||
|
</div>
|
||||||
|
<div class="uk-width-1-1">
|
||||||
|
<div id="searchRecipesForm" onkeydown="searchRecipesTable(event)" class="uk-search uk-search-default uk-align-center">
|
||||||
|
<input id="searchRecipesInput" class="uk-border-pill uk-search-input" type="search" placeholder="" aria-label="">
|
||||||
|
<span class="uk-search-icon-flip" uk-search-icon></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="uk-width-1-1">
|
||||||
|
<nav aria-label="Pagination">
|
||||||
|
<ul id="recipesPage" class="uk-pagination uk-flex-center" uk-margin>
|
||||||
|
<li><a href="#"><span uk-pagination-previous></span></a></li>
|
||||||
|
<li><a href="#">1</a></li>
|
||||||
|
<li class="uk-disabled"><span>…</span></li>
|
||||||
|
<li><a href="#">5</a></li>
|
||||||
|
<li><a href="#">6</a></li>
|
||||||
|
<li class="uk-active"><span aria-current="page">7</span></li>
|
||||||
|
<li><a href="#">8</a></li>
|
||||||
|
<li><a href="#"><span uk-pagination-next></span></a></li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
<div class="uk-width-1-1">
|
||||||
|
<caption>Select a Recipe from the system...</caption>
|
||||||
|
<table class="uk-table uk-table-striped">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Name</th>
|
||||||
|
<th>Operations</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody id="recipesModalTableBody">
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- Shopping Lists Modals -->
|
||||||
|
<div id="listsModal" class="uk-flex-top" uk-modal>
|
||||||
|
<div class="uk-modal-dialog">
|
||||||
|
<button class="uk-modal-close-default" type="button" uk-close></button>
|
||||||
|
<div class="uk-modal-header">
|
||||||
|
<h2 class="uk-modal-title">System Shopping Lists Search</h2>
|
||||||
|
</div>
|
||||||
|
<div class="uk-modal-body">
|
||||||
|
<div class="uk-grid-small" uk-grid>
|
||||||
|
<div class="uk-width-1-1">
|
||||||
|
<p class="uk-text-small">This operator allows you to select other shopping lists that already exist to have their
|
||||||
|
items added onto this lists items.</p>
|
||||||
|
</div>
|
||||||
|
<div class="uk-width-1-1">
|
||||||
|
<div id="searchListsForm" onkeydown="searchListsTable(event)" class="uk-search uk-search-default uk-align-center">
|
||||||
|
<input id="searchListsInput" class="uk-border-pill uk-search-input" type="search" placeholder="" aria-label="">
|
||||||
|
<span class="uk-search-icon-flip" uk-search-icon></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="uk-width-1-1">
|
||||||
|
<nav aria-label="Pagination">
|
||||||
|
<ul id="listsPage" class="uk-pagination uk-flex-center" uk-margin>
|
||||||
|
<li><a href="#"><span uk-pagination-previous></span></a></li>
|
||||||
|
<li><a href="#">1</a></li>
|
||||||
|
<li class="uk-disabled"><span>…</span></li>
|
||||||
|
<li><a href="#">5</a></li>
|
||||||
|
<li><a href="#">6</a></li>
|
||||||
|
<li class="uk-active"><span aria-current="page">7</span></li>
|
||||||
|
<li><a href="#">8</a></li>
|
||||||
|
<li><a href="#"><span uk-pagination-next></span></a></li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
<div class="uk-width-1-1">
|
||||||
|
<caption>Select a List from the system...</caption>
|
||||||
|
<table class="uk-table uk-table-striped">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Name</th>
|
||||||
|
<th>Operations</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody id="listsModalTableBody">
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
<script>const units = {{units|tojson}}</script>
|
||||||
|
<script src="{{ url_for('shopping_list_API.static', filename='js/shoppingListGeneratorHandler.js') }}"></script>
|
||||||
|
</html>
|
||||||
@ -3,6 +3,8 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" charset="utf-8" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" charset="utf-8" />
|
||||||
<title id="title"></title>
|
<title id="title"></title>
|
||||||
|
<link rel="icon" href="{{ url_for('static', filename='favicon.ico') }}">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<!-- Material Icons -->
|
<!-- Material Icons -->
|
||||||
@ -111,6 +113,7 @@
|
|||||||
<div class="uk-width-1-2@m">
|
<div class="uk-width-1-2@m">
|
||||||
<ul class="uk-iconnav uk-flex-center uk-flex-left@m">
|
<ul class="uk-iconnav uk-flex-center uk-flex-left@m">
|
||||||
<li><a onclick="openAddListModal()" uk-icon="icon: plus">Add List</a></li>
|
<li><a onclick="openAddListModal()" uk-icon="icon: plus">Add List</a></li>
|
||||||
|
<li><a href="/shopping-lists/generate" uk-icon="icon: plus">Generate List</a></li>
|
||||||
<li><a href="#" uk-icon="icon: cloud-download">download</a></li>
|
<li><a href="#" uk-icon="icon: cloud-download">download</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -3,6 +3,7 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" charset="utf-8" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" charset="utf-8" />
|
||||||
<title id="title"></title>
|
<title id="title"></title>
|
||||||
|
<link rel="icon" href="{{ url_for('static', filename='favicon.ico') }}">
|
||||||
|
|
||||||
|
|
||||||
<!-- Material Icons -->
|
<!-- Material Icons -->
|
||||||
|
|||||||
@ -3,6 +3,7 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" charset="utf-8" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" charset="utf-8" />
|
||||||
<title id="title"></title>
|
<title id="title"></title>
|
||||||
|
<link rel="icon" href="{{ url_for('static', filename='favicon.ico') }}">
|
||||||
<!-- Material Icons -->
|
<!-- Material Icons -->
|
||||||
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet" />
|
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet" />
|
||||||
<!-- Material Symbols - Outlined Set -->
|
<!-- Material Symbols - Outlined Set -->
|
||||||
|
|||||||
Binary file not shown.
@ -538,4 +538,28 @@
|
|||||||
sql='WITH sum_cte AS ( SELECT mi.id, SUM(mil.quantity_on_hand)::FLOAT8 AS total_sum FROM tet_item_locations mil JOIN tet_items mi ON mil.part_id = mi.id GROUP BY mi.id )SELECT item.id, item.description, item.item_name, sum_cte.total_sum as total_qoh, u.fullnameFROM tet_items itemLEFT JOIN sum_cte ON item.id = sum_cte.idLEFT JOIN tet_item_info item_info ON item.item_info_id = item_info.idLEFT JOIN units u ON item_info.uom = u.idWHERE item.search_string LIKE '%%' || %s || '%%'ORDER BY item.id ASCLIMIT %s OFFSET %s;')
|
sql='WITH sum_cte AS ( SELECT mi.id, SUM(mil.quantity_on_hand)::FLOAT8 AS total_sum FROM tet_item_locations mil JOIN tet_items mi ON mil.part_id = mi.id GROUP BY mi.id )SELECT item.id, item.description, item.item_name, sum_cte.total_sum as total_qoh, u.fullnameFROM tet_items itemLEFT JOIN sum_cte ON item.id = sum_cte.idLEFT JOIN tet_item_info item_info ON item.item_info_id = item_info.idLEFT JOIN units u ON item_info.uom = u.idWHERE item.search_string LIKE '%%' || %s || '%%'ORDER BY item.id ASCLIMIT %s OFFSET %s;')
|
||||||
2025-08-14 21:02:40.167097 --- ERROR --- DatabaseError(message='invalid input syntax for type uuid: "target='_blank'"',
|
2025-08-14 21:02:40.167097 --- ERROR --- DatabaseError(message='invalid input syntax for type uuid: "target='_blank'"',
|
||||||
payload=("target='_blank'",),
|
payload=("target='_blank'",),
|
||||||
sql='WITH passed_uuid AS (SELECT %s AS passed_uuid), cte_sl_items AS ( SELECT items.*, (SELECT COALESCE(row_to_json(un), '{}') FROM units un WHERE un.id = items.uom LIMIT 1) AS uom FROM test2_shopping_list_items items WHERE items.list_uuid = (SELECT passed_uuid::uuid FROM passed_uuid) )SELECT (SELECT passed_uuid FROM passed_uuid) AS passed_uuid, test2_shopping_lists.*, logins.username as author, (SELECT COALESCE(array_agg(row_to_json(slis)), '{}') FROM cte_sl_items slis) AS sl_items FROM test2_shopping_listsJOIN logins ON test2_shopping_lists.author = logins.idWHERE test2_shopping_lists.list_uuid=(SELECT passed_uuid::uuid FROM passed_uuid)')
|
sql='WITH passed_uuid AS (SELECT %s AS passed_uuid), cte_sl_items AS ( SELECT items.*, (SELECT COALESCE(row_to_json(un), '{}') FROM units un WHERE un.id = items.uom LIMIT 1) AS uom FROM test2_shopping_list_items items WHERE items.list_uuid = (SELECT passed_uuid::uuid FROM passed_uuid) )SELECT (SELECT passed_uuid FROM passed_uuid) AS passed_uuid, test2_shopping_lists.*, logins.username as author, (SELECT COALESCE(array_agg(row_to_json(slis)), '{}') FROM cte_sl_items slis) AS sl_items FROM test2_shopping_listsJOIN logins ON test2_shopping_lists.author = logins.idWHERE test2_shopping_lists.list_uuid=(SELECT passed_uuid::uuid FROM passed_uuid)')
|
||||||
|
2025-08-15 06:32:31.728417 --- ERROR --- DatabaseError(message='column "list_item_state" of relation "main_shopping_list_items" does not existLINE 1: UPDATE main_shopping_list_items SET list_item_state = true W... ^',
|
||||||
|
payload={'uuid': '9280626f-c100-44bd-a50d-35ee5d43d855', 'update': {'list_item_state': True}},
|
||||||
|
sql='UPDATE main_shopping_list_items SET list_item_state = %s WHERE list_item_uuid=%s::uuid RETURNING *;')
|
||||||
|
2025-08-16 11:06:43.660699 --- ERROR --- DatabaseError(message='column main_shopping_list_items.sl_id does not existLINE 40: ...n_shopping_list_items ON main_shopping_lists.id = main_shopp... ^',
|
||||||
|
payload=(196,),
|
||||||
|
sql='WITH passed_id AS (SELECT %s AS passed_id), logistics_id AS (SELECT logistics_info_id FROM main_items WHERE id=(SELECT passed_id FROM passed_id)), info_id AS (SELECT item_info_id FROM main_items WHERE id=(SELECT passed_id FROM passed_id)), cte_conversions AS ( SELECT main_conversions.id as conv_id, main_conversions.conv_factor as conv_factor, units.* as uom FROM main_conversions LEFT JOIN units ON main_conversions.uom_id = units.id WHERE main_conversions.item_id = (SELECT passed_id FROM passed_id) ), cte_item_info AS ( SELECT main_item_info.*, row_to_json(units.*) as uom, COALESCE((SELECT json_agg(convs) FROM cte_conversions convs), '[]'::json) AS conversions, COALESCE((SELECT json_agg(p.*) FROM main_sku_prefix as p WHERE p.id = ANY(main_item_info.prefixes)), '[]'::json) as prefixes FROM main_item_info LEFT JOIN units ON main_item_info.uom = units.id WHERE main_item_info.id = (SELECT item_info_id FROM info_id) ), cte_groups AS ( SELECT main_groups.*, main_group_items.uuid, main_group_items.item_type, main_group_items.qty FROM main_groups JOIN main_group_items ON main_groups.id = main_group_items.gr_id WHERE main_group_items.item_id = (SELECT passed_id FROM passed_id) ), cte_shopping_lists AS ( SELECT main_shopping_lists.*, main_shopping_list_items.uuid, main_shopping_list_items.item_type, main_shopping_list_items.qty FROM main_shopping_lists JOIN main_shopping_list_items ON main_shopping_lists.id = main_shopping_list_items.sl_id WHERE main_shopping_list_items.item_id = (SELECT passed_id FROM passed_id) ), cte_itemlinks AS ( SELECT * FROM main_itemlinks WHERE link=(SELECT passed_id FROM passed_id) ), cte_item_locations AS ( SELECT * FROM main_item_locations LEFT JOIN main_locations ON main_locations.id = main_item_locations.location_id WHERE part_id = (SELECT passed_id FROM passed_id) ), cte_logistics_info AS ( SELECT li.*, row_to_json(pl) AS primary_location, row_to_json(ail) AS auto_issue_location, row_to_json(pz) AS primary_zone, row_to_json(aiz) AS auto_issue_zone FROM main_logistics_info AS li LEFT JOIN main_locations AS pl ON li.primary_location = pl.id LEFT JOIN main_locations AS ail ON li.auto_issue_location = ail.id LEFT JOIN main_zones AS pz ON li.primary_zone = pz.id LEFT JOIN main_zones AS aiz ON li.auto_issue_zone = aiz.id WHERE li.id=(SELECT logistics_info_id FROM logistics_id) )SELECT (SELECT passed_id FROM passed_id) AS passed_id, main_items.*, (SELECT COALESCE(row_to_json(logis), '{}') FROM cte_logistics_info logis) AS logistics_info, row_to_json(main_food_info.*) as food_info, row_to_json(main_brands.*) as brand, (SELECT COALESCE(row_to_json(ii), '{}') FROM cte_item_info ii) AS item_info, (SELECT COALESCE(array_agg(row_to_json(g)), '{}') FROM cte_groups g) AS item_groups, (SELECT COALESCE(array_agg(row_to_json(sl)), '{}') FROM cte_shopping_lists sl) AS item_shopping_lists, (SELECT COALESCE(array_agg(row_to_json(il)), '{}') FROM cte_itemlinks il) AS linked_items, (SELECT COALESCE(array_agg(row_to_json(ils)), '{}') FROM cte_item_locations ils) AS item_locationsFROM main_items LEFT JOIN main_item_info ON main_items.item_info_id = main_item_info.id LEFT JOIN main_food_info ON main_items.food_info_id = main_food_info.id LEFT JOIN main_brands ON main_items.brand = main_brands.id LEFT JOIN units ON main_item_info.uom = units.id LEFT JOIN cte_groups ON main_items.id = cte_groups.id LEFT JOIN cte_shopping_lists ON main_items.id = cte_shopping_lists.idWHERE main_items.id=(SELECT passed_id FROM passed_id)GROUP BY main_items.id, main_item_info.id, main_food_info.id, main_brands.id;')
|
||||||
|
2025-08-16 11:07:04.722280 --- ERROR --- DatabaseError(message='column main_shopping_list_items.sl_id does not existLINE 40: ...n_shopping_list_items ON main_shopping_lists.id = main_shopp... ^',
|
||||||
|
payload=(196,),
|
||||||
|
sql='WITH passed_id AS (SELECT %s AS passed_id), logistics_id AS (SELECT logistics_info_id FROM main_items WHERE id=(SELECT passed_id FROM passed_id)), info_id AS (SELECT item_info_id FROM main_items WHERE id=(SELECT passed_id FROM passed_id)), cte_conversions AS ( SELECT main_conversions.id as conv_id, main_conversions.conv_factor as conv_factor, units.* as uom FROM main_conversions LEFT JOIN units ON main_conversions.uom_id = units.id WHERE main_conversions.item_id = (SELECT passed_id FROM passed_id) ), cte_item_info AS ( SELECT main_item_info.*, row_to_json(units.*) as uom, COALESCE((SELECT json_agg(convs) FROM cte_conversions convs), '[]'::json) AS conversions, COALESCE((SELECT json_agg(p.*) FROM main_sku_prefix as p WHERE p.id = ANY(main_item_info.prefixes)), '[]'::json) as prefixes FROM main_item_info LEFT JOIN units ON main_item_info.uom = units.id WHERE main_item_info.id = (SELECT item_info_id FROM info_id) ), cte_groups AS ( SELECT main_groups.*, main_group_items.uuid, main_group_items.item_type, main_group_items.qty FROM main_groups JOIN main_group_items ON main_groups.id = main_group_items.gr_id WHERE main_group_items.item_id = (SELECT passed_id FROM passed_id) ), cte_shopping_lists AS ( SELECT main_shopping_lists.*, main_shopping_list_items.uuid, main_shopping_list_items.item_type, main_shopping_list_items.qty FROM main_shopping_lists JOIN main_shopping_list_items ON main_shopping_lists.id = main_shopping_list_items.sl_id WHERE main_shopping_list_items.item_id = (SELECT passed_id FROM passed_id) ), cte_itemlinks AS ( SELECT * FROM main_itemlinks WHERE link=(SELECT passed_id FROM passed_id) ), cte_item_locations AS ( SELECT * FROM main_item_locations LEFT JOIN main_locations ON main_locations.id = main_item_locations.location_id WHERE part_id = (SELECT passed_id FROM passed_id) ), cte_logistics_info AS ( SELECT li.*, row_to_json(pl) AS primary_location, row_to_json(ail) AS auto_issue_location, row_to_json(pz) AS primary_zone, row_to_json(aiz) AS auto_issue_zone FROM main_logistics_info AS li LEFT JOIN main_locations AS pl ON li.primary_location = pl.id LEFT JOIN main_locations AS ail ON li.auto_issue_location = ail.id LEFT JOIN main_zones AS pz ON li.primary_zone = pz.id LEFT JOIN main_zones AS aiz ON li.auto_issue_zone = aiz.id WHERE li.id=(SELECT logistics_info_id FROM logistics_id) )SELECT (SELECT passed_id FROM passed_id) AS passed_id, main_items.*, (SELECT COALESCE(row_to_json(logis), '{}') FROM cte_logistics_info logis) AS logistics_info, row_to_json(main_food_info.*) as food_info, row_to_json(main_brands.*) as brand, (SELECT COALESCE(row_to_json(ii), '{}') FROM cte_item_info ii) AS item_info, (SELECT COALESCE(array_agg(row_to_json(g)), '{}') FROM cte_groups g) AS item_groups, (SELECT COALESCE(array_agg(row_to_json(sl)), '{}') FROM cte_shopping_lists sl) AS item_shopping_lists, (SELECT COALESCE(array_agg(row_to_json(il)), '{}') FROM cte_itemlinks il) AS linked_items, (SELECT COALESCE(array_agg(row_to_json(ils)), '{}') FROM cte_item_locations ils) AS item_locationsFROM main_items LEFT JOIN main_item_info ON main_items.item_info_id = main_item_info.id LEFT JOIN main_food_info ON main_items.food_info_id = main_food_info.id LEFT JOIN main_brands ON main_items.brand = main_brands.id LEFT JOIN units ON main_item_info.uom = units.id LEFT JOIN cte_groups ON main_items.id = cte_groups.id LEFT JOIN cte_shopping_lists ON main_items.id = cte_shopping_lists.idWHERE main_items.id=(SELECT passed_id FROM passed_id)GROUP BY main_items.id, main_item_info.id, main_food_info.id, main_brands.id;')
|
||||||
|
2025-08-16 11:23:59.868804 --- ERROR --- DatabaseError(message='column main_shopping_list_items.sl_id does not existLINE 40: ...n_shopping_list_items ON main_shopping_lists.id = main_shopp... ^',
|
||||||
|
payload=(196,),
|
||||||
|
sql='WITH passed_id AS (SELECT %s AS passed_id), logistics_id AS (SELECT logistics_info_id FROM main_items WHERE id=(SELECT passed_id FROM passed_id)), info_id AS (SELECT item_info_id FROM main_items WHERE id=(SELECT passed_id FROM passed_id)), cte_conversions AS ( SELECT main_conversions.id as conv_id, main_conversions.conv_factor as conv_factor, units.* as uom FROM main_conversions LEFT JOIN units ON main_conversions.uom_id = units.id WHERE main_conversions.item_id = (SELECT passed_id FROM passed_id) ), cte_item_info AS ( SELECT main_item_info.*, row_to_json(units.*) as uom, COALESCE((SELECT json_agg(convs) FROM cte_conversions convs), '[]'::json) AS conversions, COALESCE((SELECT json_agg(p.*) FROM main_sku_prefix as p WHERE p.id = ANY(main_item_info.prefixes)), '[]'::json) as prefixes FROM main_item_info LEFT JOIN units ON main_item_info.uom = units.id WHERE main_item_info.id = (SELECT item_info_id FROM info_id) ), cte_groups AS ( SELECT main_groups.*, main_group_items.uuid, main_group_items.item_type, main_group_items.qty FROM main_groups JOIN main_group_items ON main_groups.id = main_group_items.gr_id WHERE main_group_items.item_id = (SELECT passed_id FROM passed_id) ), cte_shopping_lists AS ( SELECT main_shopping_lists.*, main_shopping_list_items.uuid, main_shopping_list_items.item_type, main_shopping_list_items.qty FROM main_shopping_lists JOIN main_shopping_list_items ON main_shopping_lists.id = main_shopping_list_items.sl_id WHERE main_shopping_list_items.item_id = (SELECT passed_id FROM passed_id) ), cte_itemlinks AS ( SELECT * FROM main_itemlinks WHERE link=(SELECT passed_id FROM passed_id) ), cte_item_locations AS ( SELECT * FROM main_item_locations LEFT JOIN main_locations ON main_locations.id = main_item_locations.location_id WHERE part_id = (SELECT passed_id FROM passed_id) ), cte_logistics_info AS ( SELECT li.*, row_to_json(pl) AS primary_location, row_to_json(ail) AS auto_issue_location, row_to_json(pz) AS primary_zone, row_to_json(aiz) AS auto_issue_zone FROM main_logistics_info AS li LEFT JOIN main_locations AS pl ON li.primary_location = pl.id LEFT JOIN main_locations AS ail ON li.auto_issue_location = ail.id LEFT JOIN main_zones AS pz ON li.primary_zone = pz.id LEFT JOIN main_zones AS aiz ON li.auto_issue_zone = aiz.id WHERE li.id=(SELECT logistics_info_id FROM logistics_id) )SELECT (SELECT passed_id FROM passed_id) AS passed_id, main_items.*, (SELECT COALESCE(row_to_json(logis), '{}') FROM cte_logistics_info logis) AS logistics_info, row_to_json(main_food_info.*) as food_info, row_to_json(main_brands.*) as brand, (SELECT COALESCE(row_to_json(ii), '{}') FROM cte_item_info ii) AS item_info, (SELECT COALESCE(array_agg(row_to_json(g)), '{}') FROM cte_groups g) AS item_groups, (SELECT COALESCE(array_agg(row_to_json(sl)), '{}') FROM cte_shopping_lists sl) AS item_shopping_lists, (SELECT COALESCE(array_agg(row_to_json(il)), '{}') FROM cte_itemlinks il) AS linked_items, (SELECT COALESCE(array_agg(row_to_json(ils)), '{}') FROM cte_item_locations ils) AS item_locationsFROM main_items LEFT JOIN main_item_info ON main_items.item_info_id = main_item_info.id LEFT JOIN main_food_info ON main_items.food_info_id = main_food_info.id LEFT JOIN main_brands ON main_items.brand = main_brands.id LEFT JOIN units ON main_item_info.uom = units.id LEFT JOIN cte_groups ON main_items.id = cte_groups.id LEFT JOIN cte_shopping_lists ON main_items.id = cte_shopping_lists.idWHERE main_items.id=(SELECT passed_id FROM passed_id)GROUP BY main_items.id, main_item_info.id, main_food_info.id, main_brands.id;')
|
||||||
|
2025-08-16 17:04:26.272090 --- ERROR --- DatabaseError(message='invalid reference to FROM-clause entry for table "main_items"LINE 7: WHERE main_items.search_string LIKE '%' || '' || '%' ^HINT: Perhaps you meant to reference the table alias "items".',
|
||||||
|
payload=('', 50, 0),
|
||||||
|
sql='SELECT items.item_uuid, items.item_name, units.fullnameFROM main_items itemsLEFT JOIN main_item_info item_info ON item_info.id = items.item_info_idLEFT JOIN units ON item_info.uom = units.idWHERE main_items.search_string LIKE '%%' || %s || '%%' ORDER BY items.item_name LIMIT %s OFFSET %s;')
|
||||||
|
2025-08-17 19:21:27.673759 --- ERROR --- DatabaseError(message='duplicate key value violates unique constraint "main_logistics_info_barcode_key"DETAIL: Key (barcode)=(%%) already exists.',
|
||||||
|
payload=('%%', 1, 1, 1, 1),
|
||||||
|
sql='INSERT INTO main_logistics_info(barcode, primary_location, primary_zone, auto_issue_location, auto_issue_zone) VALUES (%s, %s, %s, %s, %s) RETURNING *;')
|
||||||
|
2025-08-17 19:21:30.048853 --- ERROR --- DatabaseError(message='duplicate key value violates unique constraint "main_logistics_info_barcode_key"DETAIL: Key (barcode)=(%%) already exists.',
|
||||||
|
payload=('%%', 1, 1, 1, 1),
|
||||||
|
sql='INSERT INTO main_logistics_info(barcode, primary_location, primary_zone, auto_issue_location, auto_issue_zone) VALUES (%s, %s, %s, %s, %s) RETURNING *;')
|
||||||
|
2025-08-17 19:22:32.824380 --- ERROR --- DatabaseError(message='duplicate key value violates unique constraint "main_item_info_barcode_key"DETAIL: Key (barcode)=(%%) already exists.',
|
||||||
|
payload=('%%', '', 1.0, 1, 0.0, 0.0, 0.0, False, '{}'),
|
||||||
|
sql='INSERT INTO main_item_info(barcode, packaging, uom_quantity, uom, cost, safety_stock, lead_time_days, ai_pick, prefixes) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s) RETURNING *;')
|
||||||
BIN
static/pictures/logo.png
Normal file
BIN
static/pictures/logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 395 KiB |
|
Before Width: | Height: | Size: 89 KiB After Width: | Height: | Size: 89 KiB |
@ -1,4 +1,4 @@
|
|||||||
from flask import Flask, render_template, session, request, redirect, jsonify
|
from flask import Flask, render_template, session, request, redirect, jsonify, send_from_directory
|
||||||
from flask_assets import Environment, Bundle
|
from flask_assets import Environment, Bundle
|
||||||
from authlib.integrations.flask_client import OAuth
|
from authlib.integrations.flask_client import OAuth
|
||||||
import config, psycopg2, main
|
import config, psycopg2, main
|
||||||
@ -105,6 +105,12 @@ def create_push_subscription():
|
|||||||
def subscribe():
|
def subscribe():
|
||||||
return render_template("subscribe.html")
|
return render_template("subscribe.html")
|
||||||
|
|
||||||
|
@app.route('/favicon.ico')
|
||||||
|
def favicon():
|
||||||
|
return send_from_directory(
|
||||||
|
app.static_folder, 'pictures/favicon.ico', mimetype='image/vnd.microsoft.icon'
|
||||||
|
)
|
||||||
|
|
||||||
@app.route("/")
|
@app.route("/")
|
||||||
@access_api.login_required
|
@access_api.login_required
|
||||||
def home():
|
def home():
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user