Finished migration of poe/scanner
This commit is contained in:
parent
d81515d4c5
commit
61661db807
@ -115,7 +115,6 @@
|
|||||||
|
|
||||||
<ul uk-tab>
|
<ul uk-tab>
|
||||||
<li><a href="#">Manual Transaction</a></li>
|
<li><a href="#">Manual Transaction</a></li>
|
||||||
<li><a href="#">Scan To Receipt</a></li>
|
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<div class="uk-switcher">
|
<div class="uk-switcher">
|
||||||
@ -201,47 +200,6 @@
|
|||||||
<button onclick="submitTransaction()" class="uk-button uk-button-primary">Submit</button>
|
<button onclick="submitTransaction()" class="uk-button uk-button-primary">Submit</button>
|
||||||
</div>
|
</div>
|
||||||
</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>
|
||||||
</div>
|
</div>
|
||||||
<!-- Modals -->
|
<!-- Modals -->
|
||||||
|
|||||||
@ -7,6 +7,7 @@ from queue import Queue
|
|||||||
import time, process
|
import time, process
|
||||||
from user_api import login_required
|
from user_api import login_required
|
||||||
import webpush
|
import webpush
|
||||||
|
from application.poe import poe_processes
|
||||||
|
|
||||||
point_of_ease = Blueprint('poe', __name__, template_folder="templates", static_folder="static")
|
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'],
|
return render_template('scanner.html', current_site=session['selected_site'],
|
||||||
sites=sites)
|
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"])
|
@point_of_ease.route('/getItemLocations', methods=["GET"])
|
||||||
def getItemLocations():
|
def getItemLocations():
|
||||||
@ -86,14 +91,18 @@ def getModalItems():
|
|||||||
@point_of_ease.route('/postTransaction', methods=["POST"])
|
@point_of_ease.route('/postTransaction', methods=["POST"])
|
||||||
def post_transaction():
|
def post_transaction():
|
||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
database_config = config()
|
print('test two')
|
||||||
with psycopg2.connect(**database_config) as conn:
|
result = poe_processes.postTransaction(
|
||||||
result = process.postTransaction(
|
site_name=session['selected_site'],
|
||||||
conn=conn,
|
user_id=session['user_id'],
|
||||||
site_name=session['selected_site'],
|
data=dict(request.json)
|
||||||
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(result)
|
||||||
return jsonify({"error":True, "message":"There was an error with this POST statement"})
|
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
|
scan_transaction_item_location_id = scannedItem.item_locations[i].id
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const response = await fetch(`/poe/postTransaction`, {
|
const response = await fetch(`/poe/postTransaction`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
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 *;')
|
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'',
|
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}},
|
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