Added PLU DASH and fixed receipt scanner

This commit is contained in:
Jadowyne Ulve 2025-08-09 11:10:18 -05:00
parent 3aa5899f77
commit 29ba974704
16 changed files with 212 additions and 52 deletions

View File

@ -2,7 +2,8 @@ CREATE TABLE IF NOT EXISTS %%site_name%%_receipt_items (
id SERIAL PRIMARY KEY,
type VARCHAR(255) NOT NULL,
receipt_id INTEGER NOT NULL,
barcode VARCHAR(255) NOT NULL,
barcode VARCHAR(255),
item_uuid UUID,
name VARCHAR(255) NOT NULL,
qty FLOAT8 NOT NULL,
uom VARCHAR(32) NOT NULL,

View File

@ -265,6 +265,7 @@ class ReceiptItemPayload:
type: str
receipt_id: int
barcode: str
item_uuid: str
name: str
qty: float = 1.0
uom: str = "each"
@ -276,6 +277,7 @@ class ReceiptItemPayload:
self.type,
self.receipt_id,
self.barcode,
self.item_uuid,
self.name,
self.qty,
self.uom,

View File

@ -45,6 +45,21 @@ def getItemBarcode():
return jsonify({"item":record, "error":False, "message":"item fetched succesfully!"})
return jsonify({"item":record, "error":True, "message":"There was an error with this GET statement"})
@point_of_ease.route('/api/paginatePLUItems', methods=['GET'])
@access_api.login_required
def paginatePLUItems():
if request.method == 'GET':
page = int(request.args.get('page', 1))
limit = int(request.args.get('limit', 50))
site_name = session['selected_site']
offset = (page - 1) * limit
try:
items = poe_database.paginatePLUItems(site_name, (limit, offset))
return jsonify(items=items, status=201, message="Fetch Successful!")
except Exception as error:
return jsonify(items=[], status=400, message=str(error))
return jsonify(items=[], status=405, message=f"The method: {request.method} is not allowed on this endpoint!")
@point_of_ease.route('/postTransaction', methods=["POST"])
@access_api.login_required
def post_transaction():

View File

@ -205,37 +205,26 @@ def selectItemByBarcode(site, payload, convert=True, conn=None):
except (Exception, psycopg2.DatabaseError) as error:
raise postsqldb.DatabaseError(error, payload, selectItemByBarcode_sql)
def selectItemAllByBarcode(site, payload, convert=True, conn=None):
def paginatePLUItems(site, payload, convert=True, conn=None):
""" payload = (limit, offset) """
item = ()
self_conn = False
if convert:
item = {}
if not conn:
database_config = config.config()
conn = psycopg2.connect(**database_config)
conn.autocommit = True
self_conn = True
linked_item = selectLinkedItemByBarcode(site, (payload[0],))
if len(linked_item) > 1:
item = selectItemAllByID(site, payload=(linked_item['link'], ), convert=convert)
item['item_info']['uom_quantity'] = linked_item['conv_factor']
if self_conn:
conn.close()
return item
else:
with open(f"application/poe/sql/getItemAllByBarcode.sql", "r+") as file:
getItemAllByBarcode_sql = file.read().replace("%%site_name%%", site)
with open(f"application/poe/sql/receipts/getPLUItems.sql", "r+") as file:
getPLUItems_sql = file.read().replace("%%site_name%%", site)
try:
with conn.cursor() as cur:
cur.execute(getItemAllByBarcode_sql, payload)
rows = cur.fetchone()
cur.execute(getPLUItems_sql, payload)
rows = cur.fetchall()
if rows and convert:
item = postsqldb.tupleDictionaryFactory(cur.description, rows)
item = [postsqldb.tupleDictionaryFactory(cur.description, row) for row in rows]
if rows and not convert:
item = rows
@ -243,7 +232,7 @@ def selectItemAllByBarcode(site, payload, convert=True, conn=None):
conn.close()
return item
except (Exception, psycopg2.DatabaseError) as error:
raise postsqldb.DatabaseError(error, payload, getItemAllByBarcode_sql)
raise postsqldb.DatabaseError(error, payload, getPLUItems_sql)
def insertCostLayersTuple(site, payload, convert=True, conn=None):
cost_layer = ()

View File

@ -122,6 +122,7 @@ def post_receipt(site_name, user_id, data: dict, conn=None):
type=item['type'],
receipt_id=receipt['id'],
barcode=item['item']['barcode'],
item_uuid=item['item']['item_uuid'],
name=item['item']['item_name'],
qty=item['item']['qty'],
uom=item['item']['uom'],

View File

@ -1,4 +1,4 @@
INSERT INTO %%site_name%%_receipt_items
(type, receipt_id, barcode, name, qty, uom, data, status)
VALUES (%s, %s, %s, %s, %s, %s, %s, %s)
(type, receipt_id, barcode, item_uuid, name, qty, uom, data, status)
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s)
RETURNING *;

View File

@ -0,0 +1,4 @@
SELECT items.item_uuid, items.item_name
FROM %%site_name%%_items items
WHERE items.item_type = 'FOOD_PLU' ORDER BY items.item_name ASC
LIMIT %s OFFSET %s;

View File

@ -11,17 +11,22 @@ SELECT barcodes.*,
item.id as item_id,
item.logistics_info_id as logistics_info_id,
item.item_name as item_name,
item.item_uuid as item_uuid,
primary_location.id as primary_location_id,
primary_location.uuid as primary_location_uuid,
auto_issue_location.id as auto_issue_location_id,
auto_issue_location.uuid as auto_issue_location_uuid,
item_info.cost as cost,
item_info.uom_quantity as uom_quantity,
item_info.uom as uom,
food_info.expires as expires,
food_info.default_expiration as default_expiration,
(SELECT COALESCE(array_agg(row_to_json(ils)), '{}') FROM cte_item_locations ils) AS item_locations
FROM %%site_name%%_barcodes barcodes
LEFT JOIN %%site_name%%_items item ON barcodes.item_uuid = item.item_uuid
LEFT JOIN %%site_name%%_item_info as item_info ON item_info.id = item.item_info_id
LEFT JOIN %%site_name%%_logistics_info logistics_info ON logistics_info.id = item.logistics_info_id
LEFT JOIN %%site_name%%_food_info food_info ON food_info.id = item.food_info_id
LEFT JOIN %%site_name%%_locations primary_location ON logistics_info.primary_location = primary_location.id
LEFT JOIN %%site_name%%_locations auto_issue_location ON logistics_info.auto_issue_location = auto_issue_location.id
WHERE barcodes.barcode = (SELECT passed_barcode FROM passed_id);

View File

@ -30,9 +30,6 @@ async function changeSite(site){
location.reload(true)
}
async function getItemBarcode(barcode) {
console.log(`selected item: ${barcode}`)
const url = new URL('/poe/getItem/barcode', window.location.origin);
@ -74,9 +71,9 @@ async function startReceipt() {
document.getElementById('barcode-input').classList.remove('uk-disabled')
document.getElementById('barcode-table').classList.remove('uk-disabled')
document.getElementById('receiptStart').classList.add('uk-disabled')
document.getElementById('receiptComplete').classList.remove('uk-disabled')
document.getElementById('receiptClose').classList.remove('uk-disabled')
document.getElementById('receiptStart').setAttribute('class', 'uk-button uk-button-default uk-disabled')
document.getElementById('receiptComplete').setAttribute('class', 'uk-button uk-button-primary')
document.getElementById('receiptClose').setAttribute('class', 'uk-button uk-button-danger')
}
@ -85,9 +82,9 @@ async function completeReceipt() {
document.getElementById('barcode-input').classList.add('uk-disabled')
document.getElementById('barcode-table').classList.add('uk-disabled')
document.getElementById('receiptStart').classList.remove('uk-disabled')
document.getElementById('receiptComplete').classList.add('uk-disabled')
document.getElementById('receiptClose').classList.add('uk-disabled')
document.getElementById('receiptStart').setAttribute('class', 'uk-button uk-button-primary')
document.getElementById('receiptComplete').setAttribute('class', 'uk-button uk-button-default uk-disabled')
document.getElementById('receiptClose').setAttribute('class', 'uk-button uk-button-default uk-disabled')
await submitScanReceipt(scannedReceiptItems)
let scanReceiptTableBody = document.getElementById("scanReceiptTableBody")
@ -101,9 +98,9 @@ async function closeReceipt(){
document.getElementById('barcode-input').classList.add('uk-disabled')
document.getElementById('barcode-table').classList.add('uk-disabled')
document.getElementById('receiptStart').classList.remove('uk-disabled')
document.getElementById('receiptComplete').classList.add('uk-disabled')
document.getElementById('receiptClose').classList.add('uk-disabled')
document.getElementById('receiptStart').setAttribute('class', 'uk-button uk-button-primary')
document.getElementById('receiptComplete').setAttribute('class', 'uk-button uk-button-default uk-disabled')
document.getElementById('receiptClose').setAttribute('class', 'uk-button uk-button-default uk-disabled')
let scanReceiptTableBody = document.getElementById("scanReceiptTableBody")
scanReceiptTableBody.innerHTML = ""
@ -117,24 +114,27 @@ async function addToReceipt(event) {
let barcode = document.getElementById('barcode-scan-receipt').value
let data = await getItemBarcode(barcode)
let scannedItem = data.item
console.log(scannedItem)
if(scannedItem){
let expires = scannedItem.food_info.expires
if(scannedItem.food_info.expires){
let expires = scannedItem.expires
if(scannedItem.expires){
let today = new Date();
today.setDate(today.getDate() + Number(scannedItem.food_info.default_expiration))
today.setDate(today.getDate() + Number(scannedItem.default_expiration))
expires = today.toISOString().split('T')[0];
}
scannedReceiptItems.push({item: {
barcode: scannedItem.barcode,
item_uuid: scannedItem.item_uuid,
item_name: scannedItem.item_name,
qty: scannedItem.item_info.uom_quantity,
uom: scannedItem.item_info.uom.id,
data: {cost: scannedItem.item_info.cost, expires: expires}
qty: scannedItem.uom_quantity,
uom: scannedItem.uom,
data: {cost: scannedItem.cost, expires: expires}
}, type: 'sku'})
document.getElementById('barcode-scan-receipt').value = ""
} else {
scannedReceiptItems.push({item: {
barcode: `%${barcode}%`,
item_uuid: null,
item_name: "unknown",
qty: 1,
uom: 1,
@ -224,6 +224,89 @@ async function openLineEditModal(ind, line_data) {
UIkit.modal(document.getElementById("lineEditModal")).show();
}
// PLU Modal Controls
async function openPLUModal() {
let items = await getPLUItems()
await generatePLUCards(items)
UIkit.modal(document.getElementById("PLUDASHModal")).show();
}
plu_current_page = 1
plu_limit = 50
async function getPLUItems() {
const url = new URL('/poe/api/paginatePLUItems', window.location.origin);
url.searchParams.append('page', plu_current_page);
url.searchParams.append('limit', plu_limit);
const response = await fetch(url);
data = await response.json();
return data.items;
}
async function generatePLUCards(plu_items) {
let PLUCardsBody = document.getElementById('PLUCardsBody')
PLUCardsBody.innerHTML = ""
for (let i = 0; i < plu_items.length; i++){
let container_div = document.createElement('div')
let card_div = document.createElement('div')
card_div.setAttribute('class','uk-card uk-card-default uk-card-small uk-card-hover uk-text-center')
// need to check for key, use placeholder
let image_div = document.createElement('div')
image_div.setAttribute('class', 'uk-card-media-top uk-flex uk-flex-center uk-padding-small')
let item_image = document.createElement('img')
//item_image.src = "https://cdn-icons-png.flaticon.com/128/2756/2756716.png"
item_image.width = "60"
image_div.append(item_image)
let card_body_div = document.createElement('div')
card_body_div.setAttribute('class', 'uk-card-body uk-padding-small')
let item_header = document.createElement('h5')
item_header.setAttribute('class', 'uk-card-title')
item_header.style = "margin-bottom: 4px;"
item_header.innerHTML = plu_items[i].item_name
let id_text = document.createElement('div')
id_text.style = "font-size: 0.8em; margin-bottom: 7px;"
id_text.innerHTML = `ID: ${plu_items[i].item_uuid}`
let add_button = document.createElement('button')
add_button.setAttribute('class', 'uk-button uk-button-primary uk-button-small')
add_button.onclick = async function(){await addPLUToReceipt(plu_items[i])}
add_button.innerHTML = "Add"
card_body_div.append(item_header, id_text, add_button)
card_div.append(image_div, card_body_div)
container_div.append(card_div)
PLUCardsBody.append(container_div)
}
}
async function addPLUToReceipt(item) {
scannedReceiptItems.push({item: {
barcode: null,
item_uuid: item.item_uuid ,
item_name: item.item_name,
qty: 1,
uom: 1,
data: {'cost': 0.00, 'expires': false}
}, type: 'PLU SKU'})
await replenishScannedReceiptTable(scannedReceiptItems)
}
var mode = false
async function toggleDarkMode() {
let darkMode = document.getElementById("dark-mode");

View File

@ -105,7 +105,7 @@
</div>
<div class="uk-width-1-1" uk-grid>
<div>
<button id="receiptStart" onclick="startReceipt()" class="uk-button uk-button-default">Start Receipt</button>
<button id="receiptStart" onclick="startReceipt()" class="uk-button uk-button-primary">Start Receipt</button>
</div>
<div>
<button id="receiptComplete" onclick="completeReceipt()" class="uk-button uk-button-default uk-disabled">Complete Receipt</button>
@ -113,6 +113,9 @@
<div>
<button id="receiptClose" onclick="closeReceipt()" class="uk-button uk-button-default uk-disabled">Cancel Receipt</button>
</div>
<div>
<button onclick="openPLUModal()" class="uk-button uk-button-default">PLU Dash</button>
</div>
</div>
<div class="uk-width-1-1">
<hr class="uk-divider-icon">
@ -152,7 +155,7 @@
</thead>
<tbody>
<tr>
<td>QTY</td>
<td>Item Name</td>
<td><input id="lineName" class="uk-input" type="text"></td>
</tr>
<tr>
@ -185,6 +188,60 @@
</p>
</div>
</div>
<!-- PLU Modal USED FOR RECEIPTS-->
<div id="PLUDASHModal" class="uk-modal-full" uk-modal>
<div class="uk-modal-dialog uk-modal-body">
<h2 class="uk-modal-title">Add PLU Line...</h2>
<div id="PLUCardsBody" class="uk-grid-small uk-child-width-1-3@s uk-child-width-1-5@m uk-child-width-1-6@l" uk-grid="masonry: pack">
<!-- need to be built in javascript -->
<div>
<div class="uk-card uk-card-default uk-card-small uk-card-hover uk-text-center">
<div class="uk-card-media-top uk-flex uk-flex-center uk-padding-small">
<img src="https://upload.wikimedia.org/wikipedia/commons/1/15/Red_Apple.jpg" alt="Apple" width="60">
</div>
<div class="uk-card-body uk-padding-small">
<h5 class="uk-card-title" style="margin-bottom: 4px;">Apple</h5>
<div style="font-size: 0.8em; margin-bottom: 7px;">PLU: 4017</div>
<button class="uk-button uk-button-primary uk-button-small" onclick="addToReceipt('Apple', '4017')">Add</button>
</div>
</div>
</div>
<div>
<div class="uk-card uk-card-default uk-card-small uk-card-body">
<h3 class="uk-card-title">Small</h3>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>
</div>
</div>
<div>
<div class="uk-card uk-card-default uk-card-small uk-card-body">
<h3 class="uk-card-title">Small</h3>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>
</div>
</div>
<div>
<div class="uk-card uk-card-default uk-card-small uk-card-body">
<h3 class="uk-card-title">Small</h3>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>
</div>
</div>
<div>
<div class="uk-card uk-card-default uk-card-small uk-card-body">
<h3 class="uk-card-title">Small</h3>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>
</div>
</div>
<div>
<div class="uk-card uk-card-default uk-card-small uk-card-body">
<h3 class="uk-card-title">Small</h3>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>
</div>
</div>
</div>
</div>
<div class="uk-modal-footer uk-text-right">
<button class="uk-button uk-button-default uk-modal-close" type="button">Cancel</button>
</div>
</div>
</body>
{% assets "js_all" %}
<script type="text/javascript" src="{{ ASSET_URL }}"></script>

View File

@ -105,7 +105,7 @@
<div class="uk-width-1-1 uk-flex uk-flex-left" uk-grid>
<div class="uk-width-1-3@m">
<label class="uk-form-label" for="barcode-scan">Barcode</label>
<input onkeydown="addToQueue(event)" on id="barcode-scan" class="uk-input uk-flex uk-flex-bottom" type="text">
<input onkeydown="addToQueue(event)" id="barcode-scan" class="uk-input uk-flex uk-flex-bottom" type="text">
</div>
<div class="uk-width-1-3@m uk-flex uk-flex-bottom uk-flex-right@m">
<select id="scan_trans_type" class="uk-select" aria-label="Select">

View File

@ -44,3 +44,6 @@
2025-08-09 08:08:51.302903 --- ERROR --- DatabaseError(message='can't adapt type 'dict'',
payload={'barcode': {'barcode': 'tsath', 'descriptor': 'test', 'in_exchange': 1, 'out_exchange': 1}, 'update': {'in_exchange': '1', 'out_exchange': '1', 'descriptor': 'test ggh'}},
sql='UPDATE test_barcodes SET in_exchange = %s, out_exchange = %s, descriptor = %s WHERE barcode=%s RETURNING *;')
2025-08-09 11:07:03.004000 --- ERROR --- DatabaseError(message='not all arguments converted during string formatting',
payload=('PLU SKU', 30, None, '8cac7ce9-dfd1-4da8-8856-57f5bdbc752b', 'Bubble Bath Lavender', 1, 1, '{"cost": 0, "expires": false}', 'Unresolved'),
sql='INSERT INTO test_receipt_items(type, receipt_id, barcode, name, qty, uom, data, status) VALUES (%s, %s, %s, %s, %s, %s, %s, %s) RETURNING *;')