Finished migration of poe/scanner
This commit is contained in:
parent
d81515d4c5
commit
61661db807
@ -115,7 +115,6 @@
|
||||
|
||||
<ul uk-tab>
|
||||
<li><a href="#">Manual Transaction</a></li>
|
||||
<li><a href="#">Scan To Receipt</a></li>
|
||||
</ul>
|
||||
|
||||
<div class="uk-switcher">
|
||||
@ -201,47 +200,6 @@
|
||||
<button onclick="submitTransaction()" class="uk-button uk-button-primary">Submit</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="uk-grid-small" uk-grid>
|
||||
<div class="uk-width-1-1">
|
||||
<p class="uk-text-meta">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.
|
||||
</p>
|
||||
</div>
|
||||
<div class="uk-width-1-1" uk-grid>
|
||||
<div>
|
||||
<button id="receiptStart" onclick="startReceipt()" class="uk-button uk-button-default">Start Receipt</button>
|
||||
</div>
|
||||
<div>
|
||||
<button id="receiptComplete" onclick="completeReceipt()" class="uk-button uk-button-default uk-disabled">Complete Receipt</button>
|
||||
</div>
|
||||
<div>
|
||||
<button id="receiptClose" onclick="closeReceipt()" class="uk-button uk-button-default uk-disabled">Cancel Receipt</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="uk-width-1-1">
|
||||
<hr class="uk-divider-icon">
|
||||
</div>
|
||||
<div id="barcode-input" class="uk-width-1-1 uk-flex uk-flex-left uk-disabled" uk-grid>
|
||||
<div class="uk-width-1-3@m">
|
||||
<label class="uk-form-label" for="barcode-scan-receipt">Barcode</label>
|
||||
<input onkeydown="addToReceipt(event)" id="barcode-scan-receipt" class="uk-input uk-flex uk-flex-bottom" type="text">
|
||||
</div>
|
||||
</div>
|
||||
<div id="barcode-table" class="uk-width-1-1 uk-disabled">
|
||||
<table class="uk-table uk-table-striped uk-table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="uk-table-shrink">Type</th>
|
||||
<th class="uk-table-shrink">Barcode</th>
|
||||
<th>Name</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="scanReceiptTableBody">
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Modals -->
|
||||
|
||||
@ -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"})
|
||||
|
||||
|
||||
313
application/poe/poe_database.py
Normal file
313
application/poe/poe_database.py
Normal file
@ -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)
|
||||
103
application/poe/poe_processes.py
Normal file
103
application/poe/poe_processes.py
Normal file
@ -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!"}
|
||||
4
application/poe/sql/insertCostLayersTuple.sql
Normal file
4
application/poe/sql/insertCostLayersTuple.sql
Normal 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 *;
|
||||
5
application/poe/sql/insertTransactionsTuple.sql
Normal file
5
application/poe/sql/insertTransactionsTuple.sql
Normal 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 *;
|
||||
4
application/poe/sql/updateItemLocation.sql
Normal file
4
application/poe/sql/updateItemLocation.sql
Normal 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 *;
|
||||
734
application/poe/static/js/receiptsHandler.js
Normal file
734
application/poe/static/js/receiptsHandler.js
Normal file
@ -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 = `<a><span uk-pagination-previous></span></a>`;
|
||||
previousElement.classList.add('uk-disabled');
|
||||
}else {
|
||||
previousElement.innerHTML = `<a onclick="setPage(${pagination_current-1}, '${elementID}')"><span uk-pagination-previous></span></a>`;
|
||||
}
|
||||
paginationElement.append(previousElement)
|
||||
|
||||
//first
|
||||
let firstElement = document.createElement('li')
|
||||
if(pagination_current<=1){
|
||||
firstElement.innerHTML = `<a><strong>1</strong></a>`;
|
||||
firstElement.classList.add('uk-disabled');
|
||||
}else {
|
||||
firstElement.innerHTML = `<a onclick="setPage(1, '${elementID}')">1</a>`;
|
||||
}
|
||||
paginationElement.append(firstElement)
|
||||
|
||||
// ...
|
||||
if(pagination_current-2>1){
|
||||
let firstDotElement = document.createElement('li')
|
||||
firstDotElement.classList.add('uk-disabled')
|
||||
firstDotElement.innerHTML = `<span>…</span>`;
|
||||
paginationElement.append(firstDotElement)
|
||||
}
|
||||
// last
|
||||
if(pagination_current-2>0){
|
||||
let lastElement = document.createElement('li')
|
||||
lastElement.innerHTML = `<a onclick="setPage(${pagination_current-1}, '${elementID}')">${pagination_current-1}</a>`
|
||||
paginationElement.append(lastElement)
|
||||
}
|
||||
// current
|
||||
if(pagination_current!=1 && pagination_current != pagination_end){
|
||||
let currentElement = document.createElement('li')
|
||||
currentElement.innerHTML = `<li class="uk-active"><span aria-current="page"><strong>${pagination_current}</strong></span></li>`
|
||||
paginationElement.append(currentElement)
|
||||
}
|
||||
// next
|
||||
if(pagination_current+2<pagination_end+1){
|
||||
let nextElement = document.createElement('li')
|
||||
nextElement.innerHTML = `<a onclick="setPage(${pagination_current+1}, '${elementID}')">${pagination_current+1}</a>`
|
||||
paginationElement.append(nextElement)
|
||||
}
|
||||
// ...
|
||||
if(pagination_current+2<=pagination_end){
|
||||
let secondDotElement = document.createElement('li')
|
||||
secondDotElement.classList.add('uk-disabled')
|
||||
secondDotElement.innerHTML = `<span>…</span>`;
|
||||
paginationElement.append(secondDotElement)
|
||||
}
|
||||
//end
|
||||
let endElement = document.createElement('li')
|
||||
if(pagination_current>=pagination_end){
|
||||
endElement.innerHTML = `<a><strong>${pagination_end}</strong></a>`;
|
||||
endElement.classList.add('uk-disabled');
|
||||
}else {
|
||||
endElement.innerHTML = `<a onclick="setPage(${pagination_end}, '${elementID}')">${pagination_end}</a>`;
|
||||
}
|
||||
paginationElement.append(endElement)
|
||||
//next button
|
||||
let nextElement = document.createElement('li')
|
||||
if(pagination_current>=pagination_end){
|
||||
nextElement.innerHTML = `<a><span uk-pagination-next></span></a>`;
|
||||
nextElement.classList.add('uk-disabled');
|
||||
}else {
|
||||
nextElement.innerHTML = `<a onclick="setPage(${pagination_current+1}, '${elementID}')"><span uk-pagination-next></span></a>`;
|
||||
console.log(nextElement.innerHTML)
|
||||
}
|
||||
paginationElement.append(nextElement)
|
||||
}
|
||||
|
||||
var scannedItems = Array();
|
||||
const queueLimit = 5; // 49 should be default
|
||||
|
||||
async function addToQueue(event) {
|
||||
if (event.key == "Enter"){
|
||||
let data = await getItemBarcode(document.getElementById('barcode-scan').value)
|
||||
let scannedItem = data.item
|
||||
if(data.error){
|
||||
UIkit.notification({
|
||||
message: data.message,
|
||||
status: "danger",
|
||||
pos: 'top-right',
|
||||
timeout: 5000
|
||||
});
|
||||
}
|
||||
if(scannedItems.length > queueLimit){
|
||||
scannedItems.shift()
|
||||
}
|
||||
if(!Array.isArray(scannedItem) && !data.error){
|
||||
let status = await submitScanTransaction(scannedItem)
|
||||
scannedItems.push({'item': scannedItem, 'type': `${document.getElementById('scan_trans_type').value}`, 'error': status})
|
||||
document.getElementById('barcode-scan').value = ""
|
||||
}
|
||||
}
|
||||
await replenishScanTable()
|
||||
}
|
||||
|
||||
async function getItemBarcode(barcode) {
|
||||
console.log(`selected item: ${barcode}`)
|
||||
const url = new URL('/external/getItem/barcode', window.location.origin);
|
||||
url.searchParams.append('barcode', barcode);
|
||||
const response = await fetch(url);
|
||||
data = await response.json();
|
||||
return data;
|
||||
}
|
||||
|
||||
async function submitScanTransaction(scannedItem) {
|
||||
/// I need to find the location that matches the items auto issue location id
|
||||
|
||||
let trans_type = document.getElementById('scan_trans_type').value
|
||||
let scan_transaction_item_location_id = 0
|
||||
let comparator = 0
|
||||
|
||||
if (trans_type === "Adjust In"){
|
||||
comparator = scannedItem.logistics_info.primary_location.id
|
||||
} else if (trans_type === "Adjust Out"){
|
||||
comparator = scannedItem.logistics_info.auto_issue_location.id
|
||||
}
|
||||
|
||||
for (let i = 0; i < scannedItem.item_locations.length; i++){
|
||||
if (scannedItem.item_locations[i].location_id === comparator){
|
||||
scan_transaction_item_location_id = scannedItem.item_locations[i].id
|
||||
}
|
||||
}
|
||||
|
||||
const response = await fetch(`/external/postTransaction`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
item_id: scannedItem.id,
|
||||
logistics_info_id: scannedItem.logistics_info_id,
|
||||
barcode: scannedItem.barcode,
|
||||
item_name: scannedItem.item_name,
|
||||
transaction_type: document.getElementById('scan_trans_type').value,
|
||||
quantity: scannedItem.item_info.uom_quantity,
|
||||
description: "",
|
||||
cost: parseFloat(scannedItem.item_info.cost),
|
||||
vendor: 0,
|
||||
expires: null,
|
||||
location_id: scan_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
|
||||
});
|
||||
|
||||
return data.error
|
||||
|
||||
}
|
||||
|
||||
async function replenishScanTable() {
|
||||
let scanTableBody = document.getElementById("scanTableBody")
|
||||
scanTableBody.innerHTML = ""
|
||||
|
||||
let reversedScannedItems = scannedItems.slice().reverse()
|
||||
|
||||
for(let i = 0; i < reversedScannedItems.length; i++){
|
||||
let tableRow = document.createElement('tr')
|
||||
|
||||
let icon = `<span uk-icon="check"></span>`
|
||||
if(reversedScannedItems[i].error){
|
||||
icon = `<span uk-icon="warning"></span>`
|
||||
}
|
||||
|
||||
let statusCell = document.createElement('td')
|
||||
statusCell.innerHTML = icon
|
||||
let barcodeCell = document.createElement('td')
|
||||
barcodeCell.innerHTML = reversedScannedItems[i].item.barcode
|
||||
let nameCell = document.createElement('td')
|
||||
nameCell.innerHTML = reversedScannedItems[i].item.item_name
|
||||
let typeCell = document.createElement('td')
|
||||
typeCell.innerHTML = reversedScannedItems[i].type
|
||||
let locationCell = document.createElement('td')
|
||||
if (reversedScannedItems[i].type === "Adjust In"){
|
||||
locationCell.innerHTML = reversedScannedItems[i].item.logistics_info.primary_location.uuid
|
||||
} else {
|
||||
locationCell.innerHTML = reversedScannedItems[i].item.logistics_info.auto_issue_location.uuid
|
||||
}
|
||||
|
||||
tableRow.append(statusCell, barcodeCell, nameCell, typeCell, locationCell)
|
||||
scanTableBody.append(tableRow)
|
||||
}
|
||||
}
|
||||
|
||||
async function submitScanReceipt(items) {
|
||||
const response = await fetch(`/external/postReceipt`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
items: items
|
||||
}),
|
||||
});
|
||||
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
|
||||
});
|
||||
|
||||
return data.error
|
||||
}
|
||||
|
||||
var openedReceipt = false
|
||||
async function startReceipt() {
|
||||
openedReceipt = true
|
||||
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')
|
||||
|
||||
}
|
||||
|
||||
async function completeReceipt() {
|
||||
openedReceipt = false
|
||||
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')
|
||||
|
||||
await submitScanReceipt(scannedReceiptItems)
|
||||
let scanReceiptTableBody = document.getElementById("scanReceiptTableBody")
|
||||
scanReceiptTableBody.innerHTML = ""
|
||||
|
||||
scannedReceiptItems = Array()
|
||||
|
||||
}
|
||||
|
||||
async function closeReceipt(){
|
||||
openedReceipt = false
|
||||
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')
|
||||
|
||||
let scanReceiptTableBody = document.getElementById("scanReceiptTableBody")
|
||||
scanReceiptTableBody.innerHTML = ""
|
||||
|
||||
scannedReceiptItems = Array()
|
||||
}
|
||||
|
||||
var scannedReceiptItems = Array();
|
||||
async function addToReceipt(event) {
|
||||
if (event.key == "Enter"){
|
||||
let barcode = document.getElementById('barcode-scan-receipt').value
|
||||
let data = await getItemBarcode(barcode)
|
||||
let scannedItem = data.item
|
||||
if(scannedItem){
|
||||
let expires = scannedItem.food_info.expires
|
||||
console.log(expires)
|
||||
if(scannedItem.food_info.expires){
|
||||
let today = new Date();
|
||||
today.setDate(today.getDate() + Number(scannedItem.food_info.default_expiration))
|
||||
expires = today.toISOString().split('T')[0];
|
||||
}
|
||||
scannedReceiptItems.push({item: {
|
||||
barcode: scannedItem.barcode,
|
||||
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}
|
||||
}, type: 'sku'})
|
||||
document.getElementById('barcode-scan-receipt').value = ""
|
||||
} else {
|
||||
scannedReceiptItems.push({item: {
|
||||
barcode: `%${barcode}%`,
|
||||
item_name: "unknown",
|
||||
qty: 1,
|
||||
uom: 1,
|
||||
data: {'cost': 0.00, 'expires': false}
|
||||
}, type: 'new sku'})
|
||||
document.getElementById('barcode-scan-receipt').value = ""
|
||||
}
|
||||
}
|
||||
await replenishScannedReceiptTable(scannedReceiptItems)
|
||||
}
|
||||
|
||||
async function replenishScannedReceiptTable(items) {
|
||||
let scanReceiptTableBody = document.getElementById("scanReceiptTableBody")
|
||||
scanReceiptTableBody.innerHTML = ""
|
||||
|
||||
for(let i = 0; i < items.length; i++){
|
||||
let tableRow = document.createElement('tr')
|
||||
|
||||
let typeCell = document.createElement('td')
|
||||
typeCell.innerHTML = items[i].type
|
||||
let barcodeCell = document.createElement('td')
|
||||
barcodeCell.innerHTML = items[i].item.barcode
|
||||
let nameCell = document.createElement('td')
|
||||
nameCell.innerHTML = items[i].item.item_name
|
||||
|
||||
let operationsCell = document.createElement('td')
|
||||
|
||||
let editOp = document.createElement('a')
|
||||
editOp.style = "margin-right: 5px;"
|
||||
editOp.setAttribute('class', 'uk-button uk-button-small uk-button-default')
|
||||
editOp.setAttribute('uk-icon', 'icon: pencil')
|
||||
editOp.onclick = async function () {
|
||||
await openLineEditModal(i, items[i])
|
||||
}
|
||||
|
||||
let deleteOp = document.createElement('a')
|
||||
deleteOp.setAttribute('class', 'uk-button uk-button-small uk-button-default')
|
||||
deleteOp.setAttribute('uk-icon', 'icon: trash')
|
||||
deleteOp.onclick = async function() {
|
||||
scannedReceiptItems.splice(i, 1)
|
||||
await replenishScannedReceiptTable(scannedReceiptItems)
|
||||
}
|
||||
|
||||
operationsCell.append(editOp, deleteOp)
|
||||
|
||||
operationsCell.classList.add("uk-flex")
|
||||
operationsCell.classList.add("uk-flex-right")
|
||||
|
||||
tableRow.append(typeCell, barcodeCell, nameCell, operationsCell)
|
||||
scanReceiptTableBody.append(tableRow)
|
||||
}
|
||||
}
|
||||
|
||||
async function openLineEditModal(ind, line_data) {
|
||||
console.log(line_data)
|
||||
document.getElementById('lineName').value = line_data.item.item_name
|
||||
document.getElementById('lineQty').value = line_data.item.qty
|
||||
document.getElementById('lineUOM').value = line_data.item.uom
|
||||
document.getElementById('lineCost').value = line_data.item.data.cost
|
||||
document.getElementById('lineExpires').value = line_data.item.data.expires
|
||||
if(line_data.type === 'sku'){
|
||||
document.getElementById('lineUOM').classList.add('uk-disabled')
|
||||
} else {
|
||||
document.getElementById('lineUOM').classList.remove('uk-disabled')
|
||||
}
|
||||
|
||||
if(!line_data.item.data.expires){
|
||||
document.getElementById('lineExpires').classList.add('uk-disabled')
|
||||
} else {
|
||||
document.getElementById('lineExpires').classList.remove('uk-disabled')
|
||||
}
|
||||
|
||||
document.getElementById('saveLineButton').onclick = async function() {
|
||||
line_data.item.item_name = document.getElementById('lineName').value
|
||||
line_data.item.qty = document.getElementById('lineQty').value
|
||||
line_data.item.uom = document.getElementById('lineUOM').value
|
||||
line_data.item.data.cost = document.getElementById('lineCost').value
|
||||
if(line_data.item.data.expires){
|
||||
line_data.item.data.expires = document.getElementById('lineExpires').value
|
||||
}
|
||||
|
||||
scannedReceiptItems[ind] = line_data
|
||||
UIkit.modal(document.getElementById("lineEditModal")).hide();
|
||||
await replenishScannedReceiptTable(scannedReceiptItems)
|
||||
}
|
||||
|
||||
UIkit.modal(document.getElementById("lineEditModal")).show();
|
||||
}
|
||||
|
||||
var mode = false
|
||||
async function toggleDarkMode() {
|
||||
let darkMode = document.getElementById("dark-mode");
|
||||
darkMode.disabled = !darkMode.disabled;
|
||||
mode = !mode;
|
||||
if(mode){
|
||||
document.getElementById('modeToggle').innerHTML = "light_mode"
|
||||
document.getElementById('main_html').classList.add('uk-light')
|
||||
} else {
|
||||
document.getElementById('modeToggle').innerHTML = "dark_mode"
|
||||
document.getElementById('main_html').classList.remove('uk-light')
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -78,7 +78,6 @@ async function submitScanTransaction(scannedItem) {
|
||||
scan_transaction_item_location_id = scannedItem.item_locations[i].id
|
||||
}
|
||||
}
|
||||
|
||||
const response = await fetch(`/poe/postTransaction`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
|
||||
154
application/poe/templates/receipts.html
Normal file
154
application/poe/templates/receipts.html
Normal file
@ -0,0 +1,154 @@
|
||||
<!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>
|
||||
<!-- 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 rel="stylesheet" href="{{ url_for('static', filename='css/uikit.min.css') }}"/>
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/pantry.css') }}"/>
|
||||
|
||||
<link id="dark-mode" rel="stylesheet" href="{{ url_for('static', filename='css/dark-mode.css') }}" disabled/>
|
||||
|
||||
|
||||
|
||||
</head>
|
||||
<style>
|
||||
.custom_row:hover{
|
||||
background-color: rgb(230, 230, 230) !important;
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
<body>
|
||||
<div uk-sticky="sel-target: .uk-navbar-container; cls-active: uk-navbar-sticky">
|
||||
<!-- to color the navbar i have to stlye this element the nav element -->
|
||||
<nav id="navbar" class="uk-navbar-container">
|
||||
<div class="uk-container uk-container-expand">
|
||||
<div uk-navbar="dropbar: true">
|
||||
<div id="offcanvas-slide" uk-offcanvas="mode: slide; overlay: true">
|
||||
<div class="uk-offcanvas-bar uk-flex uk-flex-column">
|
||||
<ul class="uk-nav uk-nav-secondary">
|
||||
<img class="uk-align-center uk-border-circle" data-src="{{ url_for('static', filename='pictures/logo.jpg') }}" style="width: 150px; height: auto;" uk-img />
|
||||
<li class="uk-nav-header">Apps</li>
|
||||
<li><a href="/shopping-lists">Shopping Lists</a></li>
|
||||
<li><a href="/recipes">Recipes</a></li>
|
||||
<li class="uk-nav-header">Logistics</li>
|
||||
<li><a href="/items">Items</a></li>
|
||||
<li>
|
||||
<a href="/transaction">
|
||||
<div class="uk-active">Add Transaction<div class="uk-nav-subtitle" disabled>You are adding transactions...</div>
|
||||
</div></a>
|
||||
</li>
|
||||
<li><a href="/workshop">Workshop</a></li>
|
||||
<li><a href="/receipts">Receipts</a></li>
|
||||
<li class="uk-nav-header">System Management</li>
|
||||
<li><a><div>{{current_site}}<div class="uk-nav-subtitle">This is the current site you are viewing...</div></div></a>
|
||||
<div uk-dropdown="mode: click">
|
||||
<ul class="uk-nav uk-dropdown-nav">
|
||||
{% 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>
|
||||
{% if system_admin %}
|
||||
<li><a href="/admin">Administration</a></li>
|
||||
{% endif %}
|
||||
<li><a href="" class="">{{username}}</a></li>
|
||||
</ul>
|
||||
<button class="uk-button uk-margin-small uk-position-top-right" uk-icon="icon: close" href=""></button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="uk-navbar-left uk-margin-small">
|
||||
<a href="#offcanvas-slide" class="uk-button uk-button-default uk-button-small" uk-icon="icon: menu" uk-toggle> Menu</a>
|
||||
</div>
|
||||
<div class="uk-navbar-center uk-margin-small uk-visible@s">
|
||||
<ul class="uk-breadcrumb">
|
||||
<li style="cursor: default;"><span><strong>{{current_site}}</strong></span>
|
||||
<div uk-dropdown="mode: hover">
|
||||
<ul class="uk-nav uk-dropdown-nav">
|
||||
{% 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;"><span>Logistics</span></li>
|
||||
<li><a href="/items">Items</a></li>
|
||||
<li class="uk-disabled"><span>Add Transaction</span></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="uk-navbar-right">
|
||||
<div>
|
||||
<a onclick="toggleDarkMode()" class="uk-button uk-button-small"><span id="modeToggle" class="uk-flex material-symbols-outlined">dark_mode</span></a>
|
||||
</div>
|
||||
<div>
|
||||
<a href="" class="" uk-icon="icon: user" uk-toggle>{{username}}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
<div class="uk-container uk-section">
|
||||
<div class="uk-grid-small" uk-grid>
|
||||
<div class="uk-width-1-1">
|
||||
<p class="uk-text-meta">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.
|
||||
</p>
|
||||
</div>
|
||||
<div class="uk-width-1-1" uk-grid>
|
||||
<div>
|
||||
<button id="receiptStart" onclick="startReceipt()" class="uk-button uk-button-default">Start Receipt</button>
|
||||
</div>
|
||||
<div>
|
||||
<button id="receiptComplete" onclick="completeReceipt()" class="uk-button uk-button-default uk-disabled">Complete Receipt</button>
|
||||
</div>
|
||||
<div>
|
||||
<button id="receiptClose" onclick="closeReceipt()" class="uk-button uk-button-default uk-disabled">Cancel Receipt</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="uk-width-1-1">
|
||||
<hr class="uk-divider-icon">
|
||||
</div>
|
||||
<div id="barcode-input" class="uk-width-1-1 uk-flex uk-flex-left uk-disabled" uk-grid>
|
||||
<div class="uk-width-1-3@m">
|
||||
<label class="uk-form-label" for="barcode-scan-receipt">Barcode</label>
|
||||
<input onkeydown="addToReceipt(event)" id="barcode-scan-receipt" class="uk-input uk-flex uk-flex-bottom" type="text">
|
||||
</div>
|
||||
</div>
|
||||
<div id="barcode-table" class="uk-width-1-1 uk-disabled">
|
||||
<table class="uk-table uk-table-striped uk-table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="uk-table-shrink">Type</th>
|
||||
<th class="uk-table-shrink">Barcode</th>
|
||||
<th>Name</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="scanReceiptTableBody">
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
{% assets "js_all" %}
|
||||
<script type="text/javascript" src="{{ ASSET_URL }}"></script>
|
||||
{% endassets %}
|
||||
<script src="{{ url_for('poe.static', filename='js/receiptsHandler.js') }}"></script>
|
||||
</html>
|
||||
@ -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': <built-in function id>, 'update': {'conv_factor': 3}},
|
||||
sql='UPDATE test_itemlinks SET conv_factor = %s WHERE id=%s RETURNING *;')
|
||||
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;')
|
||||
Loading…
x
Reference in New Issue
Block a user