diff --git a/application/__pycache__/database_payloads.cpython-313.pyc b/application/__pycache__/database_payloads.cpython-313.pyc index 7c9c46e..134fe43 100644 Binary files a/application/__pycache__/database_payloads.cpython-313.pyc and b/application/__pycache__/database_payloads.cpython-313.pyc differ diff --git a/application/administration/sql/CREATE/receipt_items.sql b/application/administration/sql/CREATE/receipt_items.sql index cfb9eb6..2e04a88 100644 --- a/application/administration/sql/CREATE/receipt_items.sql +++ b/application/administration/sql/CREATE/receipt_items.sql @@ -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, diff --git a/application/database_payloads.py b/application/database_payloads.py index 0f692d0..84cd7d8 100644 --- a/application/database_payloads.py +++ b/application/database_payloads.py @@ -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, diff --git a/application/poe/__pycache__/poe_api.cpython-313.pyc b/application/poe/__pycache__/poe_api.cpython-313.pyc index 18a2a52..51c9bc9 100644 Binary files a/application/poe/__pycache__/poe_api.cpython-313.pyc and b/application/poe/__pycache__/poe_api.cpython-313.pyc differ diff --git a/application/poe/__pycache__/poe_database.cpython-313.pyc b/application/poe/__pycache__/poe_database.cpython-313.pyc index b98ba08..042b6f8 100644 Binary files a/application/poe/__pycache__/poe_database.cpython-313.pyc and b/application/poe/__pycache__/poe_database.cpython-313.pyc differ diff --git a/application/poe/__pycache__/poe_processes.cpython-313.pyc b/application/poe/__pycache__/poe_processes.cpython-313.pyc index 9fd9d8a..e02cea0 100644 Binary files a/application/poe/__pycache__/poe_processes.cpython-313.pyc and b/application/poe/__pycache__/poe_processes.cpython-313.pyc differ diff --git a/application/poe/poe_api.py b/application/poe/poe_api.py index 71fdbb9..39bf225 100644 --- a/application/poe/poe_api.py +++ b/application/poe/poe_api.py @@ -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(): diff --git a/application/poe/poe_database.py b/application/poe/poe_database.py index 07544ac..dd0d6df 100644 --- a/application/poe/poe_database.py +++ b/application/poe/poe_database.py @@ -205,45 +205,34 @@ 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],)) + 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(getPLUItems_sql, payload) + rows = cur.fetchall() + if rows and convert: + item = [postsqldb.tupleDictionaryFactory(cur.description, row) for row in rows] + if rows and not convert: + item = rows - 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) - try: - - with conn.cursor() as cur: - cur.execute(getItemAllByBarcode_sql, payload) - rows = cur.fetchone() - if rows and convert: - item = postsqldb.tupleDictionaryFactory(cur.description, rows) - if rows and not convert: - item = rows - - if self_conn: - conn.close() - return item - except (Exception, psycopg2.DatabaseError) as error: - raise postsqldb.DatabaseError(error, payload, getItemAllByBarcode_sql) + except (Exception, psycopg2.DatabaseError) as error: + raise postsqldb.DatabaseError(error, payload, getPLUItems_sql) def insertCostLayersTuple(site, payload, convert=True, conn=None): cost_layer = () diff --git a/application/poe/poe_processes.py b/application/poe/poe_processes.py index 6d187f6..685e8fe 100644 --- a/application/poe/poe_processes.py +++ b/application/poe/poe_processes.py @@ -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'], diff --git a/application/poe/sql/insertReceiptItemsTuple.sql b/application/poe/sql/insertReceiptItemsTuple.sql index 6aeb85f..8f06d55 100644 --- a/application/poe/sql/insertReceiptItemsTuple.sql +++ b/application/poe/sql/insertReceiptItemsTuple.sql @@ -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 *; \ No newline at end of file diff --git a/application/poe/sql/receipts/getPLUItems.sql b/application/poe/sql/receipts/getPLUItems.sql new file mode 100644 index 0000000..96538fa --- /dev/null +++ b/application/poe/sql/receipts/getPLUItems.sql @@ -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; \ No newline at end of file diff --git a/application/poe/sql/scanner/selectItemByBarcode.sql b/application/poe/sql/scanner/selectItemByBarcode.sql index cf22e74..98084ef 100644 --- a/application/poe/sql/scanner/selectItemByBarcode.sql +++ b/application/poe/sql/scanner/selectItemByBarcode.sql @@ -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); \ No newline at end of file diff --git a/application/poe/static/js/receiptsHandler.js b/application/poe/static/js/receiptsHandler.js index 67b9c34..bd5bba1 100644 --- a/application/poe/static/js/receiptsHandler.js +++ b/application/poe/static/js/receiptsHandler.js @@ -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}%`, + 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"); diff --git a/application/poe/templates/receipts.html b/application/poe/templates/receipts.html index fa4889f..cc8bc10 100644 --- a/application/poe/templates/receipts.html +++ b/application/poe/templates/receipts.html @@ -105,7 +105,7 @@
- +
@@ -113,6 +113,9 @@
+
+ +

@@ -152,7 +155,7 @@ - QTY + Item Name @@ -185,6 +188,60 @@

+ +
+
+

Add PLU Line...

+
+ +
+
+
+ Apple +
+
+
Apple
+
PLU: 4017
+ +
+
+
+
+
+

Small

+

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.

+
+
+
+
+

Small

+

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.

+
+
+
+
+

Small

+

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.

+
+
+
+
+

Small

+

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.

+
+
+
+
+

Small

+

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.

+
+
+
+
+ +
{% assets "js_all" %} diff --git a/application/poe/templates/scanner.html b/application/poe/templates/scanner.html index 163e05e..ab2145b 100644 --- a/application/poe/templates/scanner.html +++ b/application/poe/templates/scanner.html @@ -105,7 +105,7 @@
- +