Implemeted first attempt at Recipe Receipting

This commit is contained in:
Jadowyne Ulve 2025-08-10 09:47:48 -05:00
parent da0c67b7f7
commit 1212473c48
14 changed files with 543 additions and 19 deletions

View File

@ -91,12 +91,18 @@ def getRecipes(site:str, payload:tuple, convert=True):
raise postsqldb.DatabaseError(error, payload, sql) raise postsqldb.DatabaseError(error, payload, sql)
return recordset, count return recordset, count
def getRecipe(site, payload:tuple, convert=True): def getRecipe(site, payload:tuple, convert=True, conn=None):
database_config = config.config() self_conn = False
record = ()
with open(f"application/recipes/sql/getRecipeByID.sql", "r+") as file: with open(f"application/recipes/sql/getRecipeByID.sql", "r+") as file:
sql = file.read().replace("%%site_name%%", site) sql = file.read().replace("%%site_name%%", site)
try: try:
with psycopg2.connect(**database_config) as conn: if not conn:
database_config = config.config()
conn = psycopg2.connect(**database_config)
conn.autocommit = True
self_conn = True
with conn.cursor() as cur: with conn.cursor() as cur:
cur.execute(sql, payload) cur.execute(sql, payload)
rows = cur.fetchone() rows = cur.fetchone()
@ -104,6 +110,10 @@ def getRecipe(site, payload:tuple, convert=True):
record = postsqldb.tupleDictionaryFactory(cur.description, rows) record = postsqldb.tupleDictionaryFactory(cur.description, rows)
if rows and not convert: if rows and not convert:
record = rows record = rows
if self_conn:
conn.close()
return record return record
except (Exception, psycopg2.DatabaseError) as error: except (Exception, psycopg2.DatabaseError) as error:
raise postsqldb.DatabaseError(error, payload, sql) raise postsqldb.DatabaseError(error, payload, sql)
@ -116,6 +126,164 @@ def getPicturePath(site:str, payload:tuple):
rows = cur.fetchone()[0] rows = cur.fetchone()[0]
return rows return rows
def selectItemLocationsTuple(site_name, payload, convert=True, conn=None):
item_locations = ()
self_conn = False
select_item_location_sql = f"SELECT * FROM {site_name}_item_locations WHERE part_id = %s AND location_id = %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(select_item_location_sql, payload)
rows = cur.fetchone()
if rows and convert:
item_locations = postsqldb.tupleDictionaryFactory(cur.description, rows)
elif rows and not convert:
item_locations = rows
if self_conn:
conn.commit()
conn.close()
return item_locations
except Exception as error:
return error
def selectCostLayersTuple(site_name, payload, convert=True):
cost_layers = ()
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;"
try:
with psycopg2.connect(**database_config) as conn:
with conn.cursor() as cur:
cur.execute(select_cost_layers_sql, payload)
rows = cur.fetchall()
if rows and convert:
cost_layers = rows
cost_layers = [postsqldb.tupleDictionaryFactory(cur.description, layer) for layer in rows]
elif rows and not convert:
cost_layers = rows
return cost_layers
except Exception as error:
return error
def selectLocationsTuple(site, payload, convert=True, conn=None):
selected = ()
self_conn = False
sql = f"SELECT * FROM {site}_locations WHERE id=%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.fetchone()
if rows and convert:
selected = postsqldb.tupleDictionaryFactory(cur.description, rows)
elif rows and not convert:
selected = rows
if self_conn:
conn.commit()
conn.close()
return selected
except Exception as error:
raise postsqldb.DatabaseError(error, payload, sql)
def selectItemTupleByUUID(site, payload, convert=True, conn=None):
selected = ()
self_conn = False
with open(f"application/recipes/sql/getItemTupleByUUID.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:
selected = postsqldb.tupleDictionaryFactory(cur.description, rows)
elif rows and not convert:
selected = rows
if self_conn:
conn.commit()
conn.close()
return selected
except Exception as error:
raise postsqldb.DatabaseError(error, payload, sql)
def insertCostLayersTuple(site, payload, convert=True, conn=None):
cost_layer = ()
self_conn = False
with open(f"application/recipes/sql/insertCostLayersTuple.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:
cost_layer = postsqldb.tupleDictionaryFactory(cur.description, rows)
elif rows and not convert:
cost_layer = rows
if self_conn:
conn.commit()
conn.close()
return cost_layer
except Exception as error:
raise postsqldb.DatabaseError(error, payload, sql)
def insertTransactionsTuple(site, payload, convert=True, conn=None):
# payload (tuple): (timestamp[timestamp], logistics_info_id[int], barcode[str], name[str],
transaction = ()
self_conn = False
with open(f"application/recipes/sql/insertTransactionsTuple.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:
transaction = postsqldb.tupleDictionaryFactory(cur.description, rows)
elif rows and not convert:
transaction = rows
if self_conn:
conn.commit()
conn.close()
except Exception as error:
raise postsqldb.DatabaseError(error, payload, sql)
return transaction
def postAddRecipe(site:str, payload:tuple, convert:bool=True): def postAddRecipe(site:str, payload:tuple, convert:bool=True):
database_config = config.config() database_config = config.config()
record = () record = ()
@ -200,6 +368,93 @@ def postDeleteRecipeItem(site:str, payload:tuple, convert:bool=True):
deleted = rows deleted = rows
return deleted return deleted
def updateCostLayersTuple(site, payload, convert=True, conn=None):
cost_layer = ()
self_conn = False
set_clause, values = postsqldb.updateStringFactory(payload['update'])
values.append(payload['id'])
sql = f"UPDATE {site}_cost_layers SET {set_clause} WHERE id=%s RETURNING *;"
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, values)
rows = cur.fetchone()
if rows and convert:
cost_layer = postsqldb.tupleDictionaryFactory(cur.description, rows)
elif rows and not convert:
cost_layer = rows
if self_conn:
conn.commit()
conn.close()
return cost_layer
except Exception as error:
return error
def updateItemLocation(site, payload, convert=True, conn=None):
item_location = ()
self_conn = False
with open(f"application/recipes/sql/updateItemLocation.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:
item_location = postsqldb.tupleDictionaryFactory(cur.description, rows)
elif rows and not convert:
item_location = rows
if self_conn:
conn.commit()
conn.close()
return item_location
except Exception as error:
return error
def deleteCostLayersTuple(site, payload, convert=True, conn=None):
deleted = ()
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;"
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 deleteRecipe(site:str, payload:tuple, convert:bool=True): def deleteRecipe(site:str, payload:tuple, convert:bool=True):
database_config = config.config() database_config = config.config()
deleted = () deleted = ()

View File

@ -0,0 +1,143 @@
import psycopg2
import datetime
from application import database_payloads, postsqldb
from application.recipes import database_recipes
import config
def postTransaction(site_name, user_id, data: dict, conn=None):
""" dict_keys(['item_id', 'logistics_info_id', 'barcode', 'item_name', 'transaction_type',
'quantity', 'description', 'cost', 'vendor', 'expires', 'location_id'])"""
def quantityFactory(quantity_on_hand:float, quantity:float, transaction_type:str):
if transaction_type == "Adjust In":
quantity_on_hand += quantity
return quantity_on_hand
if transaction_type == "Adjust Out":
quantity_on_hand -= quantity
return quantity_on_hand
raise Exception("The transaction type is wrong!")
self_conn = False
if not conn:
database_config = config.config()
conn = psycopg2.connect(**database_config)
conn.autocommit = False
self_conn = True
transaction_time = datetime.datetime.now()
cost_layer = postsqldb.CostLayerPayload(
aquisition_date=transaction_time,
quantity=float(data['quantity']),
cost=float(data['cost']),
currency_type="USD",
vendor=int(data['vendor']),
expires=data['expires']
)
transaction = postsqldb.TransactionPayload(
timestamp=transaction_time,
logistics_info_id=int(data['logistics_info_id']),
barcode=data['barcode'],
name=data['item_name'],
transaction_type=data['transaction_type'],
quantity=float(data['quantity']),
description=data['description'],
user_id=user_id,
)
location = database_recipes.selectItemLocationsTuple(site_name, (data['item_id'], data['location_id']), conn=conn)
site_location = database_recipes.selectLocationsTuple(site_name, (location['location_id'], ), conn=conn)
print(location)
cost_layers: list = location['cost_layers']
if data['transaction_type'] == "Adjust In":
cost_layer = database_recipes.insertCostLayersTuple(site_name, cost_layer.payload(), conn=conn)
cost_layers.append(cost_layer['id'])
if data['transaction_type'] == "Adjust Out":
if float(location['quantity_on_hand']) < float(data['quantity']):
raise Exception(f"The quantity on hand for {data['item_name']} in {site_location['uuid']} is not enough to satisfy your transaction!")
cost_layers = database_recipes.selectCostLayersTuple(site_name, payload=(location['id'], ))
new_cost_layers = []
qty = float(data['quantity'])
for layer in cost_layers:
if qty == 0.0:
new_cost_layers.append(layer['id'])
elif qty >= float(layer['quantity']):
qty -= float(layer['quantity'])
layer['quantity'] = 0.0
else:
layer['quantity'] -= qty
new_cost_layers.append(layer['id'])
database_recipes.updateCostLayersTuple(site_name, {'id': layer['id'], 'update': {'quantity': layer['quantity']}}, conn=conn)
qty = 0.0
if layer['quantity'] == 0.0:
database_recipes.deleteCostLayersTuple(site_name, (layer['id'],), conn=conn)
cost_layers = new_cost_layers
quantity_on_hand = quantityFactory(float(location['quantity_on_hand']), data['quantity'], data['transaction_type'])
updated_item_location_payload = (cost_layers, quantity_on_hand, data['item_id'], data['location_id'])
database_recipes.updateItemLocation(site_name, updated_item_location_payload, conn=conn)
#site_location = database_recipes.selectLocationsTuple(site_name, (location['location_id'], ), conn=conn)
transaction.data = {'location': site_location['uuid']}
database_recipes.insertTransactionsTuple(site_name, transaction.payload(), conn=conn)
if self_conn:
conn.commit()
conn.close()
return conn
return {"error": False, "message":f"Transaction Successful!"}
def process_recipe_receipt(site_name, user_id, data:dict, conn=None):
"""data={'recipe_id': recipe_id}"""
self_conn = False
if not conn:
database_config = config.config()
conn = psycopg2.connect(**database_config)
conn.autocommit = False
self_conn = True
recipe = database_recipes.getRecipe(site_name, (data['recipe_id'],), conn=conn)
sku_items = [rp_item for rp_item in recipe['recipe_items'] if rp_item['item_type'] == "sku"]
for item in sku_items:
""" dict_keys(['item_id', 'logistics_info_id', 'barcode', 'item_name', 'transaction_type',
'quantity', 'description', 'cost', 'vendor', 'expires', 'location_id'])"""
item_stuff = database_recipes.selectItemTupleByUUID(site_name, (item['item_uuid'],), conn=conn)
print(item_stuff)
payload = {
'item_id': item_stuff['item_id'],
'logistics_info_id': item_stuff['logistics_info_id'],
'barcode': "",
'item_name': item_stuff['item_name'],
'transaction_type': "Adjust Out",
'quantity': item['qty'],
'description': f"Recipe Receipt - {data['recipe_id']}",
'cost': 0.00,
'vendor': 0,
'expires': False,
'location_id': item_stuff['auto_issue_location']
}
print(payload)
try:
postTransaction(site_name, user_id, payload, conn=conn)
except Exception as error:
conn.rollback()
conn.close()
return False, str(error)
if self_conn:
conn.commit()
conn.close()
return True, ""

View File

@ -8,7 +8,7 @@ import math
import main import main
import webpush import webpush
from application.access_module import access_api from application.access_module import access_api
from application.recipes import database_recipes from application.recipes import database_recipes, recipe_processes
from application import postsqldb as db from application import postsqldb as db
recipes_api = Blueprint('recipes_api', __name__, template_folder="templates", static_folder="static") recipes_api = Blueprint('recipes_api', __name__, template_folder="templates", static_folder="static")
@ -199,3 +199,16 @@ def saveRecipeItem():
recipe = database_recipes.getRecipe(site_name, (int(updated_line['rp_id']), )) recipe = database_recipes.getRecipe(site_name, (int(updated_line['rp_id']), ))
return jsonify({'recipe': recipe, 'error': False, 'message': f'Recipe Item {updated_line['item_name']} was updated successful!'}) return jsonify({'recipe': recipe, 'error': False, 'message': f'Recipe Item {updated_line['item_name']} was updated successful!'})
return jsonify({'recipe': recipe, 'error': True, 'message': f'method {request.method} not allowed!'}) return jsonify({'recipe': recipe, 'error': True, 'message': f'method {request.method} not allowed!'})
@recipes_api.route('/api/receiptRecipe', methods=["POST"])
@access_api.login_required
def receiptRecipe():
if request.method == "POST":
site_name = session['selected_site']
user_id = session['user_id']
status, message = recipe_processes.process_recipe_receipt(site_name, user_id, request.get_json())
if not status:
return jsonify(status=400, message=message)
return jsonify(status=201, message="Recipe Transacted Successfully!")
return jsonify(status=405, message=f"{request.method} is not an allowed method on this endpoint!")

View File

@ -0,0 +1,6 @@
SELECT items.id AS item_id,
items.item_name as item_name,
items.logistics_info_id as logistics_info_id,
lginf.auto_issue_location as auto_issue_location
FROM %%site_name%%_items items
LEFT JOIN %%site_name%%_logistics_info lginf ON lginf.id = items.logistics_info_id WHERE item_uuid=%s;

View File

@ -0,0 +1,4 @@
INSERT INTO %%site_name%%_cost_layers
(aquisition_date, quantity, cost, currency_type, expires, vendor)
VALUES (%s, %s, %s, %s, %s, %s)
RETURNING *;

View File

@ -0,0 +1,5 @@
INSERT INTO %%site_name%%_transactions
(timestamp, logistics_info_id, barcode, name, transaction_type,
quantity, description, user_id, data)
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s)
RETURNING *;

View File

@ -0,0 +1,4 @@
UPDATE %%site_name%%_item_locations
SET cost_layers = %s, quantity_on_hand = %s
WHERE part_id=%s AND location_id=%s
RETURNING *;

View File

@ -21,6 +21,7 @@ async function replenishRecipe() {
await replenishIngrediantsTable() await replenishIngrediantsTable()
await replenishInstructions() await replenishInstructions()
await replenishTransactionsTable()
await getImage() await getImage()
@ -32,16 +33,21 @@ async function replenishIngrediantsTable() {
for(let i=0; i<recipe.recipe_items.length; i++){ for(let i=0; i<recipe.recipe_items.length; i++){
let qty_needed = recipe.recipe_items[i].qty
let quantity_on_hand = recipe.recipe_items[i].quantity_on_hand
let item_type = recipe.recipe_items[i].item_type
let tableRow = document.createElement('tr') let tableRow = document.createElement('tr')
let markerCell = document.createElement('td') let markerCell = document.createElement('td')
if (recipe.recipe_items[i].qty <= recipe.recipe_items[i].quantity_on_hand){ if (qty_needed <= quantity_on_hand && item_type === "sku"){
markerCell.innerHTML = `<span class="uk-label uk-label-success">Have</span>` markerCell.innerHTML = `<span class="uk-label uk-label-success">On Hand</span>`
} else { } else if (qty_needed > quantity_on_hand && item_type === "sku") {
markerCell.innerHTML = `<span class="uk-label uk-label-danger">Missing</span>` markerCell.innerHTML = `<span class="uk-label uk-label-danger">Missing</span>`
} else {
markerCell.innerHTML = ""
} }
let nameCell = document.createElement('td') let nameCell = document.createElement('td')
nameCell.innerHTML = `${recipe.recipe_items[i].item_name}` nameCell.innerHTML = `${recipe.recipe_items[i].item_name}`
@ -54,6 +60,27 @@ async function replenishIngrediantsTable() {
} }
async function replenishTransactionsTable() {
let receiptRecipeTableBody = document.getElementById('receiptRecipeTableBody')
receiptRecipeTableBody.innerHTML = ""
for(let i=0; i < recipe.recipe_items.length; i++){
if (recipe.recipe_items[i].item_type === "sku"){
let tableRow = document.createElement('tr')
let nameCell = document.createElement('td')
nameCell.innerHTML = `${recipe.recipe_items[i].item_name}`
let qtyUOMCell = document.createElement('td')
qtyUOMCell.innerHTML = `${recipe.recipe_items[i].qty}`
tableRow.append(nameCell, qtyUOMCell)
receiptRecipeTableBody.append(tableRow)
}
}
}
async function replenishInstructions() { async function replenishInstructions() {
let tileList = document.getElementById('tileList') let tileList = document.getElementById('tileList')
tileList.innerHTML = "" tileList.innerHTML = ""
@ -90,3 +117,27 @@ async function getImage(){
document.getElementById('recipeImage').src = imageURL; document.getElementById('recipeImage').src = imageURL;
}); });
} }
async function receiptRecipe(){
let recipe_id = recipe.id
const response = await fetch(`/recipes/api/receiptRecipe`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
recipe_id: recipe_id
}),
});
data = await response.json()
message_type = "primary"
if(data.error){
message_type = "danger"
}
UIkit.notification({
message: data.message,
status: message_type,
pos: 'top-right',
timeout: 5000
});
}

View File

@ -140,9 +140,34 @@
</table> </table>
</div> </div>
<div id="tileList" class="uk-width-1-2@l uk-child-width-1-1 uk-grid-collapse"> <div id="tileList" class="uk-width-1-2@l uk-child-width-1-1 uk-grid-collapse">
</div>
<div class="uk-width-1-1">
<button class="uk-button uk-button-primary" type="button" uk-toggle="target: #receiptRecipeModal">Receipt Recipe</button>
</div>
</div>
</div>
</div>
</div> <!-- Receipt Recipe Modal -->
</div> <div id="receiptRecipeModal" uk-modal>
<div class="uk-modal-dialog uk-modal-body">
<h2 class="uk-modal-title">Recipe Receipt Transaction</h2>
<p>You are about to receipt these items from the system, please confirm before completing these Transaction as once they have been completed
this cannot be reversed.</p>
<table class="uk-table uk-table-striped">
<thead>
<tr>
<th>Item</th>
<th>Qty</th>
</tr>
</thead>
<tbody id="receiptRecipeTableBody">
</tbody>
</table>
<p class="uk-text-right">
<button class="uk-button uk-button-default uk-modal-close" type="button">Cancel</button>
<button onclick="receiptRecipe()" class="uk-button uk-button-primary" type="button">Complete</button>
</p>
</div> </div>
</div> </div>
</body> </body>

View File

@ -122,3 +122,21 @@
2025-08-10 07:48:28.429239 --- ERROR --- DatabaseError(message='missing FROM-clause entry for table "item"LINE 24: LEFT JOIN sum_cte ON item.id = sum_cte.id ^', 2025-08-10 07:48:28.429239 --- ERROR --- DatabaseError(message='missing FROM-clause entry for table "item"LINE 24: LEFT JOIN sum_cte ON item.id = sum_cte.id ^',
payload=(1,), payload=(1,),
sql='WITH passed_id AS (SELECT %s AS passed_id), sum_cte AS ( SELECT mi.id, SUM(mil.quantity_on_hand)::FLOAT8 AS total_sum FROM main_item_locations mil JOIN main_items mi ON mil.part_id = mi.id GROUP BY mi.id ), cte_recipe_items AS ( SELECT items.*, /*COALESCE(main_items.barcode, items.uuid) AS uuid,*/ (SELECT COALESCE(row_to_json(units.*), '{}') FROM units WHERE units.id=main_item_info.uom) AS item_uom, COALESCE(main_items.item_name, items.item_name) AS item_name, COALESCE(main_items.links, items.links) AS links, row_to_json(units.*) as uom, (SELECT COALESCE(array_agg(jsonb_build_object('conversion', conv, 'unit', units)), '{}') FROM main_conversions conv LEFT JOIN units ON conv.uom_id = units.id WHERE conv.item_id = main_items.id) AS conversions, COALESCE(sum_cte.total_sum, 0) AS quantity_on_hand FROM main_recipe_items items LEFT JOIN main_items ON items.item_id = main_items.id LEFT JOIN main_item_info ON main_items.item_info_id = main_item_info.id LEFT JOIN units ON units.id = items.uom LEFT JOIN sum_cte ON item.id = sum_cte.id WHERE items.rp_id = (SELECT passed_id FROM passed_id) ORDER BY items.item_name ASC ) SELECT (SELECT passed_id FROM passed_id) AS passed_id, main_recipes.*, logins.username as author, (SELECT COALESCE(array_agg(row_to_json(ris)), '{}') FROM cte_recipe_items ris) AS recipe_itemsFROM main_recipesJOIN logins ON main_recipes.author = logins.idWHERE main_recipes.id=(SELECT passed_id FROM passed_id)') sql='WITH passed_id AS (SELECT %s AS passed_id), sum_cte AS ( SELECT mi.id, SUM(mil.quantity_on_hand)::FLOAT8 AS total_sum FROM main_item_locations mil JOIN main_items mi ON mil.part_id = mi.id GROUP BY mi.id ), cte_recipe_items AS ( SELECT items.*, /*COALESCE(main_items.barcode, items.uuid) AS uuid,*/ (SELECT COALESCE(row_to_json(units.*), '{}') FROM units WHERE units.id=main_item_info.uom) AS item_uom, COALESCE(main_items.item_name, items.item_name) AS item_name, COALESCE(main_items.links, items.links) AS links, row_to_json(units.*) as uom, (SELECT COALESCE(array_agg(jsonb_build_object('conversion', conv, 'unit', units)), '{}') FROM main_conversions conv LEFT JOIN units ON conv.uom_id = units.id WHERE conv.item_id = main_items.id) AS conversions, COALESCE(sum_cte.total_sum, 0) AS quantity_on_hand FROM main_recipe_items items LEFT JOIN main_items ON items.item_id = main_items.id LEFT JOIN main_item_info ON main_items.item_info_id = main_item_info.id LEFT JOIN units ON units.id = items.uom LEFT JOIN sum_cte ON item.id = sum_cte.id WHERE items.rp_id = (SELECT passed_id FROM passed_id) ORDER BY items.item_name ASC ) SELECT (SELECT passed_id FROM passed_id) AS passed_id, main_recipes.*, logins.username as author, (SELECT COALESCE(array_agg(row_to_json(ris)), '{}') FROM cte_recipe_items ris) AS recipe_itemsFROM main_recipesJOIN logins ON main_recipes.author = logins.idWHERE main_recipes.id=(SELECT passed_id FROM passed_id)')
2025-08-10 08:56:10.432791 --- ERROR --- DatabaseError(message=''int' object does not support indexing',
payload=4,
sql='WITH passed_id AS (SELECT %s AS passed_id), 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 ), cte_recipe_items AS ( SELECT items.*, /*COALESCE(test_items.barcode, items.uuid) AS uuid,*/ (SELECT COALESCE(row_to_json(units.*), '{}') FROM units WHERE units.id=test_item_info.uom) AS item_uom, COALESCE(test_items.item_name, items.item_name) AS item_name, COALESCE(test_items.links, items.links) AS links, row_to_json(units.*) as uom, (SELECT COALESCE(array_agg(jsonb_build_object('conversion', conv, 'unit', units)), '{}') FROM test_conversions conv LEFT JOIN units ON conv.uom_id = units.id WHERE conv.item_id = test_items.id) AS conversions, COALESCE(sum_cte.total_sum, 0.0) AS quantity_on_hand FROM test_recipe_items items LEFT JOIN test_items ON items.item_id = test_items.id LEFT JOIN test_item_info ON test_items.item_info_id = test_item_info.id LEFT JOIN units ON units.id = items.uom LEFT JOIN sum_cte ON test_items.id = sum_cte.id WHERE items.rp_id = (SELECT passed_id FROM passed_id) ORDER BY items.item_name ASC ) SELECT (SELECT passed_id FROM passed_id) AS passed_id, test_recipes.*, logins.username as author, (SELECT COALESCE(array_agg(row_to_json(ris)), '{}') FROM cte_recipe_items ris) AS recipe_itemsFROM test_recipesJOIN logins ON test_recipes.author = logins.idWHERE test_recipes.id=(SELECT passed_id FROM passed_id)')
2025-08-10 08:57:07.377357 --- ERROR --- DatabaseError(message=''int' object does not support indexing',
payload=4,
sql='WITH passed_id AS (SELECT %s AS passed_id), 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 ), cte_recipe_items AS ( SELECT items.*, /*COALESCE(test_items.barcode, items.uuid) AS uuid,*/ (SELECT COALESCE(row_to_json(units.*), '{}') FROM units WHERE units.id=test_item_info.uom) AS item_uom, COALESCE(test_items.item_name, items.item_name) AS item_name, COALESCE(test_items.links, items.links) AS links, row_to_json(units.*) as uom, (SELECT COALESCE(array_agg(jsonb_build_object('conversion', conv, 'unit', units)), '{}') FROM test_conversions conv LEFT JOIN units ON conv.uom_id = units.id WHERE conv.item_id = test_items.id) AS conversions, COALESCE(sum_cte.total_sum, 0.0) AS quantity_on_hand FROM test_recipe_items items LEFT JOIN test_items ON items.item_id = test_items.id LEFT JOIN test_item_info ON test_items.item_info_id = test_item_info.id LEFT JOIN units ON units.id = items.uom LEFT JOIN sum_cte ON test_items.id = sum_cte.id WHERE items.rp_id = (SELECT passed_id FROM passed_id) ORDER BY items.item_name ASC ) SELECT (SELECT passed_id FROM passed_id) AS passed_id, test_recipes.*, logins.username as author, (SELECT COALESCE(array_agg(row_to_json(ris)), '{}') FROM cte_recipe_items ris) AS recipe_itemsFROM test_recipesJOIN logins ON test_recipes.author = logins.idWHERE test_recipes.id=(SELECT passed_id FROM passed_id)')
2025-08-10 08:57:32.169483 --- ERROR --- DatabaseError(message=''int' object does not support indexing',
payload=4,
sql='WITH passed_id AS (SELECT %s AS passed_id), 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 ), cte_recipe_items AS ( SELECT items.*, /*COALESCE(test_items.barcode, items.uuid) AS uuid,*/ (SELECT COALESCE(row_to_json(units.*), '{}') FROM units WHERE units.id=test_item_info.uom) AS item_uom, COALESCE(test_items.item_name, items.item_name) AS item_name, COALESCE(test_items.links, items.links) AS links, row_to_json(units.*) as uom, (SELECT COALESCE(array_agg(jsonb_build_object('conversion', conv, 'unit', units)), '{}') FROM test_conversions conv LEFT JOIN units ON conv.uom_id = units.id WHERE conv.item_id = test_items.id) AS conversions, COALESCE(sum_cte.total_sum, 0.0) AS quantity_on_hand FROM test_recipe_items items LEFT JOIN test_items ON items.item_id = test_items.id LEFT JOIN test_item_info ON test_items.item_info_id = test_item_info.id LEFT JOIN units ON units.id = items.uom LEFT JOIN sum_cte ON test_items.id = sum_cte.id WHERE items.rp_id = (SELECT passed_id FROM passed_id) ORDER BY items.item_name ASC ) SELECT (SELECT passed_id FROM passed_id) AS passed_id, test_recipes.*, logins.username as author, (SELECT COALESCE(array_agg(row_to_json(ris)), '{}') FROM cte_recipe_items ris) AS recipe_itemsFROM test_recipesJOIN logins ON test_recipes.author = logins.idWHERE test_recipes.id=(SELECT passed_id FROM passed_id)')
2025-08-10 09:12:15.585887 --- ERROR --- DatabaseError(message='syntax error at or near "%"LINE 1: SELECT * FROM test_items items LEFT JOIN %site_name%_logisti... ^',
payload=('44c41878-e645-4e16-a402-e480936ac4aa',),
sql='SELECT * FROM test_items items LEFT JOIN %%site_name%%_logistics_info lginf ON lginf.id = items.logistics_info_id WHERE item_uuid=%s;')
2025-08-10 09:21:38.180067 --- ERROR --- DatabaseError(message='duplicate key value violates unique constraint "test_item_locations_part_id_location_id_key"DETAIL: Key (part_id, location_id)=(2016, 2) already exists.',
payload=(2016, 2, 0.0, '{}'),
sql='INSERT INTO test_item_locations(part_id, location_id, quantity_on_hand, cost_layers) VALUES (%s, %s, %s, %s)RETURNING *;')
2025-08-10 09:26:25.470903 --- ERROR --- DatabaseError(message='syntax error at or near "{"LINE 1: SELECT items.*, lginf.* AS logistics_info FROM {site}_items ... ^',
payload=('44c41878-e645-4e16-a402-e480936ac4aa',),
sql='SELECT items.*, lginf.* AS logistics_info FROM {site}_items items LEFT JOIN {site}_logistics_info lginf ON lginf.id = items.logistics_info_id WHERE item_uuid=%s;')