Using this method of entering receipts does so by adding each barcode to a list and once the receipt has been built the
- the system will then add the receipt to the system. Its important that you have the Barcode input focused and use a scanner that places the
- characters into the field before it finishes up with a press of the ENTER key.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Type
-
Barcode
-
Name
-
-
-
-
-
-
-
diff --git a/application/poe/poe_api.py b/application/poe/poe_api.py
index 46ad57c..3cdfc8d 100644
--- a/application/poe/poe_api.py
+++ b/application/poe/poe_api.py
@@ -7,6 +7,7 @@ from queue import Queue
import time, process
from user_api import login_required
import webpush
+from application.poe import poe_processes
point_of_ease = Blueprint('poe', __name__, template_folder="templates", static_folder="static")
@@ -17,7 +18,11 @@ def scannerEndpoint():
return render_template('scanner.html', current_site=session['selected_site'],
sites=sites)
-
+@point_of_ease.route('/receipts', methods=["GET"])
+def receiptsEndpoint():
+ sites = [site[1] for site in main.get_sites(session['user']['sites'])]
+ return render_template('receipts.html', current_site=session['selected_site'],
+ sites=sites)
@point_of_ease.route('/getItemLocations', methods=["GET"])
def getItemLocations():
@@ -86,14 +91,18 @@ def getModalItems():
@point_of_ease.route('/postTransaction', methods=["POST"])
def post_transaction():
if request.method == "POST":
- database_config = config()
- with psycopg2.connect(**database_config) as conn:
- result = process.postTransaction(
- conn=conn,
- site_name=session['selected_site'],
- user_id=session['user_id'],
- data=dict(request.json)
- )
+ print('test two')
+ result = poe_processes.postTransaction(
+ site_name=session['selected_site'],
+ user_id=session['user_id'],
+ data=dict(request.json)
+ )
+ #result = process.postTransaction(
+ # conn=conn,
+ # site_name=session['selected_site'],
+ # user_id=session['user_id'],
+ # data=dict(request.json)
+ #)
return jsonify(result)
return jsonify({"error":True, "message":"There was an error with this POST statement"})
diff --git a/application/poe/poe_database.py b/application/poe/poe_database.py
new file mode 100644
index 0000000..3b93697
--- /dev/null
+++ b/application/poe/poe_database.py
@@ -0,0 +1,313 @@
+import psycopg2
+import config
+from application import postsqldb
+
+
+def selectItemLocationsTuple(site_name, payload, convert=True):
+ """select a single tuple from ItemLocations table for site_name
+
+ Args:
+ conn (_T_connector@connect):
+ site_name (str):
+ payload (tuple): [item_id, location_id]
+ convert (bool): defaults to False, used to determine return of tuple/dict
+
+ Returns:
+ tuple: the row that was returned from the table
+ """
+ item_locations = ()
+ database_config = config.config()
+ select_item_location_sql = f"SELECT * FROM {site_name}_item_locations WHERE part_id = %s AND location_id = %s;"
+ try:
+ with psycopg2.connect(**database_config) as conn:
+ 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
+ return item_locations
+ except Exception as error:
+ return error
+
+def selectCostLayersTuple(site_name, payload, convert=True):
+ """select a single or series of cost layers from the database for site_name
+
+ Args:
+ conn (_T_connector@connect):
+ site_name (str):
+ payload (tuple): (item_locations_id, )
+ convert (bool): defaults to False, used for determining return as tuple/dict
+
+ Returns:
+ list: list of tuples/dict from the cost_layers table for site_name
+ """
+ cost_layers = ()
+ 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 selectItemLocationsTuple(site_name, payload, convert=True, conn=None):
+ """select a single tuple from ItemLocations table for site_name
+
+ Args:
+ conn (_T_connector@connect):
+ site_name (str):
+ payload (tuple): [item_id, location_id]
+ convert (bool): defaults to False, used to determine return of tuple/dict
+
+ Returns:
+ tuple: the row that was returned from the table
+ """
+ item_locations = ()
+ 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 insertCostLayersTuple(site, payload, convert=True, conn=None):
+ cost_layer = ()
+ self_conn = False
+
+ with open(f"application/poe/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):
+ """insert payload into transactions table for site
+
+ Args:
+ conn (_T_connector@connect): Postgresql Connector
+ site (str):
+ payload (tuple): (timestamp[timestamp], logistics_info_id[int], barcode[str], name[str],
+ transaction_type[str], quantity[float], description[str], user_id[int], data[jsonb])
+ convert (bool, optional): Determines if to return tuple as dictionary. Defaults to False.
+
+ Raises:
+ DatabaseError:
+
+ Returns:
+ tuple or dict: inserted tuple
+ """
+ transaction = ()
+ self_conn = False
+ with open(f"application/poe/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 updateCostLayersTuple(site, payload, convert=True, conn=None):
+ """_summary_
+
+ Args:
+ conn (_type_): _description_
+ site (_type_): _description_
+ payload (_type_): {'id': cost_layer_id, 'update': {column: data...}}
+
+ Returns:
+ _type_: _description_
+ """
+ cost_layer = ()
+ 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/poe/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):
+ """This is a basic funtion to delete a tuple from a table in site with an id. All
+ tables in this database has id's associated with them.
+
+ Args:
+ conn (_T_connector@connect): Postgresql Connector
+ site_name (str):
+ payload (tuple): (tuple_id,)
+ convert (bool, optional): Determines if to return tuple as dictionary. Defaults to True.
+
+ Raises:
+ DatabaseError:
+
+ Returns:
+ tuple or dict: deleted tuple
+ """
+ deleted = ()
+ 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)
diff --git a/application/poe/poe_processes.py b/application/poe/poe_processes.py
new file mode 100644
index 0000000..dcfc930
--- /dev/null
+++ b/application/poe/poe_processes.py
@@ -0,0 +1,103 @@
+from application import postsqldb
+from application.poe import poe_database
+
+import datetime
+import psycopg2
+
+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.selectItemLocationsTuple(conn, site_name, payload=(data['item_id'], data['location_id']), convert=True)
+ location = poe_database.selectItemLocationsTuple(site_name, payload=(data['item_id'], data['location_id']), conn=conn)
+ cost_layers: list = location['cost_layers']
+ if data['transaction_type'] == "Adjust In":
+ cost_layer = poe_database.insertCostLayersTuple(site_name, cost_layer.payload(), conn=conn)
+ #cost_layer = database.insertCostLayersTuple(conn, site_name, cost_layer.payload(), convert=True)
+ cost_layers.append(cost_layer['id'])
+
+ if data['transaction_type'] == "Adjust Out":
+ if float(location['quantity_on_hand']) < float(data['quantity']):
+ return {"error":True, "message":f"The quantity on hand in the chosen location is not enough to satisfy your transaction!"}
+ #cost_layers = database.selectCostLayersTuple(conn, site_name, (location['id'], ), convert=True)
+ cost_layers = poe_database.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'])
+ poe_database.updateCostLayersTuple(site_name, {'id': layer['id'], 'update': {'quantity': layer['quantity']}}, conn=conn)
+ #database.__updateTuple(conn, site_name, f"{site_name}_cost_layers", {'id': layer['id'], 'update': {'quantity': layer['quantity']}})
+ qty = 0.0
+
+ if layer['quantity'] == 0.0:
+ poe_database.deleteCostLayersTuple(site_name, (layer['id'],), conn=conn)
+ #database.deleteCostLayersTuple(conn, site_name, (layer['id'], ))
+
+ 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'])
+ poe_database.updateItemLocation(site_name, updated_item_location_payload, conn=conn)
+ #database.updateItemLocation(conn, site_name, updated_item_location_payload)
+
+ site_location = poe_database.selectLocationsTuple(site_name, (location['location_id'], ), conn=conn)
+ #site_location = database.__selectTuple(conn, site_name, f"{site_name}_locations", (location['location_id'], ), convert=True)
+
+ transaction.data = {'location': site_location['uuid']}
+
+ poe_database.insertTransactionsTuple(site_name, transaction.payload(), conn=conn)
+ #database.insertTransactionsTuple(conn, site_name, transaction.payload())
+
+ if self_conn:
+ conn.rollback()
+ conn.close()
+
+ return {"error": False, "message":f"Transaction Successful!"}
\ No newline at end of file
diff --git a/application/poe/sql/insertCostLayersTuple.sql b/application/poe/sql/insertCostLayersTuple.sql
new file mode 100644
index 0000000..c3d381f
--- /dev/null
+++ b/application/poe/sql/insertCostLayersTuple.sql
@@ -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 *;
\ No newline at end of file
diff --git a/application/poe/sql/insertTransactionsTuple.sql b/application/poe/sql/insertTransactionsTuple.sql
new file mode 100644
index 0000000..d8ee48d
--- /dev/null
+++ b/application/poe/sql/insertTransactionsTuple.sql
@@ -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 *;
\ No newline at end of file
diff --git a/application/poe/sql/updateItemLocation.sql b/application/poe/sql/updateItemLocation.sql
new file mode 100644
index 0000000..7316d94
--- /dev/null
+++ b/application/poe/sql/updateItemLocation.sql
@@ -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 *;
\ No newline at end of file
diff --git a/application/poe/static/js/receiptsHandler.js b/application/poe/static/js/receiptsHandler.js
new file mode 100644
index 0000000..0ecb98b
--- /dev/null
+++ b/application/poe/static/js/receiptsHandler.js
@@ -0,0 +1,734 @@
+var pagination_current = 1;
+var search_string = '';
+var defaqult_limit = 2;
+var pagination_end = 1;
+var item;
+
+async function changeSite(site){
+ console.log(site)
+ const response = await fetch(`/changeSite`, {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify({
+ site: site,
+ }),
+ });
+ data = await response.json();
+ transaction_status = "success"
+ if (data.error){
+ transaction_status = "danger"
+ }
+
+ UIkit.notification({
+ message: data.message,
+ status: transaction_status,
+ pos: 'top-right',
+ timeout: 5000
+ });
+ location.reload(true)
+}
+
+async function replenishItemsTable(items) {
+ let itemsTableBody = document.getElementById("itemsTableBody")
+ itemsTableBody.innerHTML = ""
+
+ for(let i = 0; i < items.length; i++){
+ let tableRow = document.createElement('tr')
+
+
+ let idCell = document.createElement('td')
+ idCell.innerHTML = items[i].id
+ let barcodeCell = document.createElement('td')
+ barcodeCell.innerHTML = items[i].barcode
+ let nameCell = document.createElement('td')
+ nameCell.innerHTML = items[i].item_name
+
+ tableRow.append(idCell)
+ tableRow.append(barcodeCell)
+ tableRow.append(nameCell)
+
+ tableRow.onclick = function(){
+ selectItem(items[i].id)
+ }
+
+ itemsTableBody.append(tableRow)
+ }
+}
+
+async function populateForm() {
+ if (item){
+ console.log(item)
+ document.getElementById('database_id').value = item.id
+ document.getElementById('barcode').value = item.barcode
+ document.getElementById('name').value = item.item_name
+ document.getElementById('transaction_cost').value = parseFloat(item.item_info.cost)
+
+ await selectLocation(
+ item.logistics_info.primary_zone.id,
+ item.logistics_info.primary_location.id,
+ item.logistics_info.primary_zone.name,
+ item.logistics_info.primary_location.name
+ )
+
+
+ let quantity_on_hand = 0
+ let locations = await getItemLocations()
+ for(let i = 0; i < locations.length; i++){
+ quantity_on_hand = quantity_on_hand + locations[i].quantity_on_hand
+ }
+ document.getElementById('QOH').value = quantity_on_hand
+ document.getElementById('UOM').value = item.item_info.uom.fullname
+
+ await replenishItemLocationsTable(locations)
+
+ }
+}
+
+async function selectItem(id) {
+ UIkit.modal(document.getElementById("itemsModal")).hide();
+ item = await getItem(id)
+ await populateForm()
+}
+
+var transaction_zone_id = 0
+var transaction_item_location_id = 0
+async function selectLocation(zone_id, location_id, zone_name, location_name) {
+ document.getElementById('zone').value = zone_name
+ document.getElementById('location').value = location_name
+ transaction_zone_id = zone_id
+ transaction_item_location_id = location_id
+}
+
+async function openItemsModal(elementID){
+ UIkit.modal(document.getElementById("itemsModal")).show();
+ pagination_current = 1
+ search_string = ''
+ let items = await getItems()
+ await replenishItemsTable(items)
+ await updatePaginationElement(elementID)
+ setFormButtonsEnabled(true)
+}
+
+async function setFormButtonsEnabled(state) {
+ let item_location_button = document.getElementById("itemLocations")
+
+ if(state){
+ item_location_button.classList.remove("uk-disabled")
+ } else {
+ item_location_button.classList.add("uk-disabled")
+ }
+}
+
+async function setTransactionTypeAdjustments() {
+ let trans_type = document.getElementById('trans_type').value
+
+ if(trans_type=="Adjust Out"){
+ document.getElementById('transaction_cost').classList.add('uk-disabled')
+ }
+ if(trans_type=="Adjust In"){
+ document.getElementById('transaction_cost').classList.remove('uk-disabled')
+ }
+
+}
+
+async function replenishItemLocationsTable(locations) {
+ let itemLocationTableBody = document.getElementById('itemLocationTableBody')
+ itemLocationTableBody.innerHTML = ""
+ for(let i = 0; i < locations.length; i++){
+ let tableRow = document.createElement('tr')
+
+ let loca = locations[i].uuid.split('@')
+
+ let zoneCell = document.createElement('td')
+ zoneCell.innerHTML = loca[0]
+
+ let locationCell = document.createElement('td')
+ locationCell.innerHTML = loca[1]
+
+ let qohCell = document.createElement('td')
+ qohCell.innerHTML = parseFloat(locations[i].quantity_on_hand)
+
+ tableRow.append(zoneCell, locationCell, qohCell)
+ tableRow.onclick = async function(){
+ await selectLocation(
+ locations[i].zone_id,
+ locations[i].id,
+ loca[0],
+ loca[1]
+ )
+ }
+ itemLocationTableBody.append(tableRow)
+ }
+}
+
+let locations_limit = 10;
+async function getItemLocations() {
+ console.log("getting Locations")
+ const url = new URL('/external/getItemLocations', window.location.origin);
+ url.searchParams.append('page', pagination_current);
+ url.searchParams.append('limit', locations_limit);
+ url.searchParams.append('id', item.id);
+ const response = await fetch(url);
+ data = await response.json();
+ pagination_end = data.end
+ let locations = data.locations;
+ console.log(locations)
+ return locations;
+}
+
+
+let items_limit = 50;
+async function getItems() {
+ console.log("getting items")
+ const url = new URL('/external/getModalItems', window.location.origin);
+ url.searchParams.append('page', pagination_current);
+ url.searchParams.append('limit', items_limit);
+ url.searchParams.append('search_string', search_string)
+ const response = await fetch(url);
+ data = await response.json();
+ pagination_end = data.end
+ let items = data.items;
+ return items;
+}
+
+async function getItem(id) {
+ console.log(`selected item: ${id}`)
+ const url = new URL('/external/getItem', window.location.origin);
+ url.searchParams.append('id', id);
+ const response = await fetch(url);
+ data = await response.json();
+ item = data.item;
+ return item;
+}
+
+async function validateTransaction() {
+ let database_id = document.getElementById("database_id")
+ let transaction_type = document.getElementById("trans_type")
+ let transaction_zone = document.getElementById("zone")
+ let transaction_location = document.getElementById("location")
+ let transaction_quantity = document.getElementById("transaction_quantity")
+ let transaction_cost = document.getElementById("transaction_cost")
+
+
+ let error_count = 0
+ if(database_id.value === ""){
+ error_count = error_count + 1
+ database_id.classList.add("uk-form-danger")
+ } else {
+ database_id.classList.remove("uk-form-danger")
+ }
+ if(transaction_type.value === "0"){
+ error_count = error_count + 1
+ transaction_type.classList.add("uk-form-danger")
+ } else {
+ transaction_type.classList.remove("uk-form-danger")
+ }
+
+ if (transaction_zone.value === ""){
+ error_count = error_count + 1
+ transaction_zone.classList.add("uk-form-danger")
+ } else {
+ transaction_zone.classList.remove("uk-form-danger")
+ }
+
+ if (transaction_location.value === ""){
+ error_count = error_count + 1
+ transaction_location.classList.add("uk-form-danger")
+ } else {
+ transaction_location.classList.remove("uk-form-danger")
+ }
+
+ let transaction_quantity_int = parseFloat(transaction_quantity.value)
+ if (transaction_quantity_int === 0.00 || transaction_quantity_int < 0.00){
+ error_count = error_count + 1
+ transaction_quantity.classList.add("uk-form-danger")
+ } else {
+ transaction_quantity.classList.remove("uk-form-danger")
+ }
+
+ let transaction_cost_int = parseFloat(transaction_cost.value)
+ if (transaction_cost_int == 0.00 && transaction_type.value == "Adjust In"){
+ error_count = error_count + 1
+ transaction_cost.classList.add("uk-form-danger")
+ } else {
+ transaction_cost.classList.remove("uk-form-danger")
+ }
+
+ if(error_count > 0){
+ return false
+ }
+
+ return true
+}
+
+async function submitTransaction() {
+ let validated = await validateTransaction()
+ if (validated){
+ let cost = parseFloat(document.getElementById('transaction_cost').value.replace(/[^0-9.-]+/g, ""));
+ const response = await fetch(`/external/postTransaction`, {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify({
+ item_id: item.id,
+ logistics_info_id: item.logistics_info_id,
+ barcode: item.barcode,
+ item_name: item.item_name,
+ transaction_type: document.getElementById('trans_type').value,
+ quantity: parseFloat(document.getElementById('transaction_quantity').value),
+ description: document.getElementById('transaction_description').value,
+ cost: cost,
+ vendor: 0,
+ expires: null,
+ location_id: transaction_item_location_id
+ }),
+ });
+ data = await response.json();
+ transaction_status = "success"
+ if (data.error){
+ transaction_status = "danger"
+ }
+
+ UIkit.notification({
+ message: data.message,
+ status: transaction_status,
+ pos: 'top-right',
+ timeout: 5000
+ });
+
+ item = await getItem(item.id)
+ await populateForm()
+ document.getElementById('transaction_quantity').value = '0.00'
+
+ } else {
+ UIkit.notification({
+ message: 'Please verify your transaction receipt.',
+ status: 'warning',
+ pos: 'top-right',
+ timeout: 5000
+ })
+}
+}
+
+async function searchTable(event, logis, elementID) {
+ if(event.key==='Enter' && logis==='items'){
+ search_string = event.srcElement.value
+ let items = await getItems()
+ await replenishItemsTable(items)
+ }
+ await updatePaginationElement(elementID)
+}
+
+async function setPage(pageNumber, elementID){
+ pagination_current = pageNumber;
+
+ if(elementID=="itemsPage"){
+ let items = await getItems()
+ await replenishItemsTable(items)
+ }
+ await updatePaginationElement(elementID)
+}
+
+async function updatePaginationElement(elementID) {
+ let paginationElement = document.getElementById(elementID);
+ paginationElement.innerHTML = "";
+ // previous
+ let previousElement = document.createElement('li')
+ if(pagination_current<=1){
+ previousElement.innerHTML = ``;
+ previousElement.classList.add('uk-disabled');
+ }else {
+ previousElement.innerHTML = ``;
+ }
+ paginationElement.append(previousElement)
+
+ //first
+ let firstElement = document.createElement('li')
+ if(pagination_current<=1){
+ firstElement.innerHTML = `1`;
+ firstElement.classList.add('uk-disabled');
+ }else {
+ firstElement.innerHTML = `1`;
+ }
+ paginationElement.append(firstElement)
+
+ // ...
+ if(pagination_current-2>1){
+ let firstDotElement = document.createElement('li')
+ firstDotElement.classList.add('uk-disabled')
+ firstDotElement.innerHTML = `…`;
+ paginationElement.append(firstDotElement)
+ }
+ // last
+ if(pagination_current-2>0){
+ let lastElement = document.createElement('li')
+ lastElement.innerHTML = `${pagination_current-1}`
+ paginationElement.append(lastElement)
+ }
+ // current
+ if(pagination_current!=1 && pagination_current != pagination_end){
+ let currentElement = document.createElement('li')
+ currentElement.innerHTML = `
Using this method of entering receipts does so by adding each barcode to a list and once the receipt has been built the
+ the system will then add the receipt to the system. Its important that you have the Barcode input focused and use a scanner that places the
+ characters into the field before it finishes up with a press of the ENTER key.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Type
+
Barcode
+
Name
+
+
+
+
+
+
+
+
+
+ {% assets "js_all" %}
+
+ {% endassets %}
+
+
\ No newline at end of file
diff --git a/database.log b/database.log
index abc5896..fca24ac 100644
--- a/database.log
+++ b/database.log
@@ -1934,4 +1934,7 @@
sql='UPDATE test_items SET brand = %s, item_type = %s WHERE id=%s RETURNING *;')
2025-04-28 06:46:35.145654 --- ERROR --- DatabaseError(message='can't adapt type 'builtin_function_or_method'',
payload={'id': , 'update': {'conv_factor': 3}},
- sql='UPDATE test_itemlinks SET conv_factor = %s WHERE id=%s RETURNING *;')
\ No newline at end of file
+ sql='UPDATE test_itemlinks SET conv_factor = %s WHERE id=%s RETURNING *;')
+2025-07-02 18:04:47.600077 --- ERROR --- DatabaseError(message='not all arguments converted during string formatting',
+ payload=(1, 2),
+ sql='SELECT * FROM main_locations WHERE id=%s;')
\ No newline at end of file