Mass Update

This commit is contained in:
Jadowyne Ulve 2024-12-08 19:27:55 -06:00
parent 05a6fbddb6
commit f1cc51f378
102 changed files with 2680 additions and 174 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

456
api.py
View File

@ -1,6 +1,7 @@
from flask import Blueprint, request, render_template, redirect, session, url_for, send_file, jsonify, Response from flask import Blueprint, request, render_template, redirect, session, url_for, send_file, jsonify, Response
import psycopg2, math, json, datetime, main, copy import psycopg2, math, json, datetime, main, copy, requests
from config import config, sites_config from config import config, sites_config
from main import unfoldCostLayers
database_api= Blueprint('database_api', __name__) database_api= Blueprint('database_api', __name__)
@ -126,6 +127,32 @@ def paginate_groups():
return jsonify({'groups': new_groups, "end": math.ceil(count/limit)}) return jsonify({'groups': new_groups, "end": math.ceil(count/limit)})
@database_api.route("/getReceipts")
def pagninate_receipts():
page = int(request.args.get('page', 1))
limit = int(request.args.get('limit', 10))
site_name = session['selected_site']
offset = (page - 1) * limit
receipts = []
count = 0
database_config = config()
with psycopg2.connect(**database_config) as conn:
try:
with conn.cursor() as cur:
sql = f"SELECT * FROM {site_name}_receipts LIMIT {limit} OFFSET {offset};"
count = f"SELECT COUNT(*) FROM {site_name}_receipts;"
cur.execute(sql)
receipts = cur.fetchall()
cur.execute(count)
count = cur.fetchone()[0]
except (Exception, psycopg2.DatabaseError) as error:
print(error)
return jsonify({'receipts': receipts, "end": math.ceil(count/limit)})
@database_api.route("/getItems") @database_api.route("/getItems")
def pagninate_items(): def pagninate_items():
page = int(request.args.get('page', 1)) page = int(request.args.get('page', 1))
@ -184,6 +211,22 @@ def pagninate_transactions():
return jsonify({'transactions': transactions, "end": math.ceil(count/limit)}) return jsonify({'transactions': transactions, "end": math.ceil(count/limit)})
@database_api.route("/getVendors")
def get_vendors():
database_config = config()
site_name = session['selected_site']
vendors = []
with psycopg2.connect(**database_config) as conn:
try:
with conn.cursor() as cur:
sql = f"SELECT * FROM {site_name}_vendors;"
cur.execute(sql)
vendors = cur.fetchall()
except (Exception, psycopg2.DatabaseError) as error:
print(error)
return jsonify(vendors=vendors)
@database_api.route("/getTransaction") @database_api.route("/getTransaction")
def get_transaction(): def get_transaction():
id = int(request.args.get('id', 1)) id = int(request.args.get('id', 1))
@ -200,7 +243,6 @@ def get_transaction():
except (Exception, psycopg2.DatabaseError) as error: except (Exception, psycopg2.DatabaseError) as error:
print(error) print(error)
print(transaction)
return jsonify(transaction=transaction) return jsonify(transaction=transaction)
@database_api.route("/getLocations") @database_api.route("/getLocations")
@ -222,7 +264,6 @@ def get_locations():
except (Exception, psycopg2.DatabaseError) as error: except (Exception, psycopg2.DatabaseError) as error:
print(error) print(error)
print(locations)
return jsonify(locations=locations) return jsonify(locations=locations)
@database_api.route("/getZones") @database_api.route("/getZones")
@ -241,13 +282,243 @@ def get_zones():
print(zones) print(zones)
return jsonify(zones=zones) return jsonify(zones=zones)
def checkReceiptState(index, site):
database_config = config()
with psycopg2.connect(**database_config) as conn:
try:
with conn.cursor() as cur:
sql = f"SELECT id, status FROM {site}_receipt_items WHERE receipt_id=%s;"
cur.execute(sql, (index, ))
items = cur.fetchall()
number_unresolved = 0
for item in items:
if item[1] == "Unresolved":
number_unresolved += 1
if number_unresolved == 0:
sql = f"UPDATE {site}_receipts SET receipt_status = 'Resolved' WHERE id=%s;"
cur.execute(sql, (index, ))
except (Exception, psycopg2.DatabaseError) as error:
print(error)
@database_api.route("/deleteReceiptItem", methods=["POST"])
def deleteReceiptItem():
database_config = config()
site_name = session['selected_site']
if request.method == "POST":
index = request.json['index']
with psycopg2.connect(**database_config) as conn:
try:
with conn.cursor() as cur:
sql = f"DELETE FROM {site_name}_receipt_items WHERE id=%s;"
cur.execute(sql, (index, ))
except (Exception, psycopg2.DatabaseError) as error:
print(error)
return jsonify({})
@database_api.route("/saveReceipt", methods=["POST"])
def saveReceipt():
database_config = config()
site_name = session['selected_site']
if request.method == "POST":
receipt_index = request.json['receipt_index']
vendor_index = request.json['vendor_index']
with psycopg2.connect(**database_config) as conn:
try:
with conn.cursor() as cur:
sql = f"UPDATE {site_name}_receipts SET vendor_id=%s WHERE id=%s;"
cur.execute(sql, (vendor_index, receipt_index))
except (Exception, psycopg2.DatabaseError) as error:
print(error)
return jsonify({})
@database_api.route("/saveReceiptItem", methods=["POST"])
def saveReceiptItem():
database_config = config()
site_name = session['selected_site']
if request.method == "POST":
index = request.json['index']
cost= request.json['cost']
qty = request.json['qty']
barcode = request.json['barcode']
with psycopg2.connect(**database_config) as conn:
try:
with conn.cursor() as cur:
sql = f"SELECT * FROM {site_name}_receipt_items WHERE id=%s;"
cur.execute(sql, (index, ))
receipt_item = list(cur.fetchone())
_new_type = receipt_item[1]
_new_name = receipt_item[4]
_new_item = receipt_item[6]
_new_cost = cost
if barcode != receipt_item[3]:
# grab the new barcode data...
sql = f"SELECT {site_name}_items.barcode FROM {site_name}_itemlinks LEFT JOIN {site_name}_items ON {site_name}_itemlinks.link = {site_name}_items.id WHERE {site_name}_itemlinks.barcode = %s;"
cur.execute(sql, (barcode, ))
x = cur.fetchone()
if x != None:
barcode = x[0]
# 078742013718
with open(f"sites/{site_name}/sql/unique/select_item_all_barcode.sql", "r+") as file:
sql = file.read()
cur.execute(sql, (barcode, ))
item = list(cur.fetchone())
if not item:
return jsonify({})
#TODO: implement the api code, this will be a big function in external that will do all the parsing and stuff in the system.
print(item)
_new_type = 'Pantry'
_new_name = item[2]
_new_cost = item[28]
_new_item = item
_new_item[28] = _new_cost
sql = f"UPDATE {site_name}_receipt_items SET type = %s, barcode = %s, name = %s, qty = %s, data = %s WHERE id=%s;"
cur.execute(sql, (_new_type, barcode, _new_name, qty, json.dumps(_new_item), index))
except (Exception, psycopg2.DatabaseError) as error:
print(error)
return jsonify({})
@database_api.route("/voidReceiptItem", methods=["POST"])
def voidReceiptItem():
database_config = config()
site_name = session['selected_site']
if request.method == "POST":
index = request.json['index']
with psycopg2.connect(**database_config) as conn:
try:
with conn.cursor() as cur:
sql = f"UPDATE {site_name}_receipt_items SET status = 'Voided' WHERE id=%s RETURNING receipt_id;"
cur.execute(sql, (index, ))
receipt_id = cur.fetchone()[0]
except (Exception, psycopg2.DatabaseError) as error:
print(error)
checkReceiptState(receipt_id, site_name)
return jsonify({})
@database_api.route("/resolveReceiptItem", methods=["POST"])
def resolveReceiptItem():
database_config = config()
site_name = session['selected_site']
if request.method == "POST":
index = request.json['index']
with psycopg2.connect(**database_config) as conn:
try:
with conn.cursor() as cur:
sql = f"SELECT * FROM {site_name}_receipt_items WHERE id=%s;"
cur.execute(sql, (index, ))
receipt_item = cur.fetchone()
sql = f"SELECT receipt_id FROM {site_name}_receipts WHERE id=%s;"
cur.execute(sql, (receipt_item[2], ))
receipt_id = cur.fetchone()[0]
payload = [
datetime.datetime.now(),
receipt_item[6][8],
receipt_item[3],
receipt_item[4],
"Receipt",
receipt_item[5],
f"{receipt_id}",
1,
json.dumps({'location': receipt_item[6][15], 'cost': receipt_item[6][28]})
]
print(payload)
main.addTransaction(
conn=conn,
site_name=site_name,
payload=payload,
location=receipt_item[6][15],
logistics_info_id=receipt_item[6][8],
item_id=receipt_item[6][0],
qty=receipt_item[5],
cost=receipt_item[6][28]
)
sql = f"UPDATE {site_name}_receipt_items SET status = 'Resolved' WHERE id=%s RETURNING receipt_id;"
cur.execute(sql, (index, ))
receipt_id = cur.fetchone()[0]
except (Exception, psycopg2.DatabaseError) as error:
print(error)
checkReceiptState(receipt_id, site_name)
return jsonify({})
@database_api.route("/getReceiptItem")
def get_receipt_item():
id = int(request.args.get('index', 1))
database_config = config()
site_name = session['selected_site']
receipt_item = []
with psycopg2.connect(**database_config) as conn:
try:
with conn.cursor() as cur:
sql = f"SELECT * FROM {site_name}_receipt_items WHERE id=%s;"
cur.execute(sql, (id, ))
receipt_item = list(cur.fetchone())
except (Exception, psycopg2.DatabaseError) as error:
print(error)
return jsonify({"receipt_item": receipt_item})
@database_api.route("/getReceipt")
def get_receipt():
id = int(request.args.get('id', 1))
database_config = config()
site_name = session['selected_site']
receipt = []
with psycopg2.connect(**database_config) as conn:
try:
with conn.cursor() as cur:
sql = f"SELECT * FROM {site_name}_receipts LEFT JOIN {site_name}_vendors ON {site_name}_receipts.vendor_id = {site_name}_vendors.id WHERE {site_name}_receipts.id=%s;"
cur.execute(sql, (id, ))
receipt = list(cur.fetchone())
sql = f"SELECT * FROM {site_name}_receipt_items WHERE receipt_id=%s;"
cur.execute(sql, (id, ))
receipt_items = cur.fetchall()
except (Exception, psycopg2.DatabaseError) as error:
print(error)
return jsonify({"receipt": receipt, "receipt_items": receipt_items})
@database_api.route("/getLinkedItem")
def get_linked_item():
id = int(request.args.get('id', 1))
database_config = config()
site_name = session['selected_site']
with psycopg2.connect(**database_config) as conn:
try:
with conn.cursor() as cur:
sql = f"SELECT * FROM {site_name}_itemlinks WHERE id=%s;"
cur.execute(sql, (id, ))
linked_item = cur.fetchone()
except (Exception, psycopg2.DatabaseError) as error:
print(error)
return jsonify(linked_item=linked_item)
@database_api.route("/getItem") @database_api.route("/getItem")
def get_item(): def get_item():
id = int(request.args.get('id', 1)) id = int(request.args.get('id', 1))
database_config = config() database_config = config()
site_name = session['selected_site'] site_name = session['selected_site']
sites = sites_config()
item = [] item = []
with psycopg2.connect(**database_config) as conn: with psycopg2.connect(**database_config) as conn:
@ -263,11 +534,25 @@ def get_item():
SQL_shopping_lists = f"SELECT * FROM {site_name}_shopping_lists WHERE pantry_items @> ARRAY[%s];" SQL_shopping_lists = f"SELECT * FROM {site_name}_shopping_lists WHERE pantry_items @> ARRAY[%s];"
cur.execute(SQL_shopping_lists, (item[0], )) cur.execute(SQL_shopping_lists, (item[0], ))
item[23] = list(cur.fetchall()) item[23] = list(cur.fetchall())
print(item) sql_location_data = f"SELECT {site_name}_locations.uuid, {site_name}_item_locations.quantity_on_hand, {site_name}_item_locations.cost_layers FROM {site_name}_item_locations LEFT JOIN {site_name}_locations ON {site_name}_item_locations.location_id = {site_name}_locations.id WHERE part_id=%s;"
cur.execute(sql_location_data, (item[0],))
# losing cost layers here by uniforming to the javascript, change to take a list?
columns = [desc[0] for desc in cur.description]
x = cur.fetchall()
qty_on_hand = sum([location[1] for location in x])
y = {location[0]: location[1] for location in x}
item[18] = y
item[19] = qty_on_hand
sql = f"SELECT * FROM {site_name}_itemlinks WHERE link=%s;"
cur.execute(sql, (item[0], ))
linked_items = cur.fetchall()
print(linked_items)
except (Exception, psycopg2.DatabaseError) as error: except (Exception, psycopg2.DatabaseError) as error:
print(error) print(error)
return jsonify(item=item) return jsonify(item=item, linked_items=linked_items)
@database_api.route("/addItem") @database_api.route("/addItem")
def addItem(): def addItem():
@ -303,6 +588,8 @@ def addItem():
return jsonify({'state': str(food_info_id)}) return jsonify({'state': str(food_info_id)})
sqltwo = f"INSERT INTO {site_name}_items(barcode, item_name, tags, links, item_info_id, logistics_info_id, food_info_id, row_type, item_type, search_string) VALUES('{barcode}', '{name}', '{tags}', '{links}', {item_info_id}, {logistics_info_id}, {food_info_id}, 'single', 'FOOD', '{barcode}%{name}') RETURNING *;" sqltwo = f"INSERT INTO {site_name}_items(barcode, item_name, tags, links, item_info_id, logistics_info_id, food_info_id, row_type, item_type, search_string) VALUES('{barcode}', '{name}', '{tags}', '{links}', {item_info_id}, {logistics_info_id}, {food_info_id}, 'single', 'FOOD', '{barcode}%{name}') RETURNING *;"
sqlthree = f"INSERT INTO {site_name}_item_locations(part_id, location_id, quantity_on_hand, cost_layers) VALUES (%s, %s, %s, %s);"
row = None row = None
try: try:
with conn.cursor() as cur: with conn.cursor() as cur:
@ -310,6 +597,9 @@ def addItem():
rows = cur.fetchone() rows = cur.fetchone()
if rows: if rows:
row = rows[:] row = rows[:]
cur.execute(f"SELECT id FROM {site_name}_locations WHERE uuid=%s;", (uuid, ))
location_id = cur.fetchone()
cur.execute(sqlthree, (row[0], location_id, 0.0, main.lst2pgarr([])))
except (Exception, psycopg2.DatabaseError) as error: except (Exception, psycopg2.DatabaseError) as error:
print(error) print(error)
conn.rollback() conn.rollback()
@ -341,20 +631,82 @@ def addItem():
payload=payload, payload=payload,
location=location, location=location,
logistics_info_id=logistics_info_id, logistics_info_id=logistics_info_id,
barcode=barcode, item_id=row[0],
qty=0.0) qty=0.0,
cost=0.0)
except (Exception, psycopg2.DatabaseError) as error: except (Exception, psycopg2.DatabaseError) as error:
print(error) print(error)
conn.rollback() conn.rollback()
return jsonify({'state': str(error)}) return jsonify({'state': str(error)})
return jsonify({'state': "SUCCESS"}) return jsonify({'state': "SUCCESS"})
@database_api.route("/transact", methods=['POST'])
def addTransaction():
if request.method == "POST":
if "site_name" in request.get_json().keys():
site_name = request.get_json()["site_name"]
print("passed")
elif "selected_site" in session.keys():
site_name = session['selected_site']
print(session)
else:
return jsonify({"message": "Failed", "error": "No site selected or sent along with request!"})
logistics_info_id = request.get_json()['logistics_info_id']
barcode = request.get_json()['barcode']
name = request.get_json()['name']
location = request.get_json()['location']
qty = request.get_json()['qty']
trans_type = request.get_json()['trans_type']
trans_cost = request.get_json()['trans_cost']
database_config = config()
actual_qty = qty
if trans_type == "Adjust Out":
actual_qty = -qty
with psycopg2.connect(**database_config) as conn:
try:
with conn.cursor() as cur:
cur.execute(f"SELECT id FROM {site_name}_items WHERE barcode=%s;", (barcode,))
item_id = cur.fetchone()
payload = [
datetime.datetime.now(),
logistics_info_id,
barcode,
name,
trans_type,
qty,
"",
1,
json.dumps({'location': location, 'cost': trans_cost})
]
print(payload)
main.addTransaction(
conn=conn,
site_name=site_name,
payload=payload,
location=location,
logistics_info_id=logistics_info_id,
item_id=item_id,
qty=actual_qty,
cost=trans_cost
)
except (Exception, psycopg2.DatabaseError) as error:
print(error)
conn.rollback()
return jsonify({'state': str(error)})
print("SUCCESS")
return jsonify({'state': str("SUCCESS")})
print("SUCCESS")
return jsonify({'state': str("FAILED")})
@database_api.route("/updateItem", methods=['POST']) @database_api.route("/updateItem", methods=['POST'])
def updateItem(): def updateItem():
def transformValues(values): def transformValues(values):
@ -443,8 +795,8 @@ def updateItem():
save_data[f"{k}_old"] = v; save_data[f"{k}_old"] = v;
cur.execute(sql, values) cur.execute(sql, values)
cur.execute(f"SELECT {site_name}_items.barcode, {site_name}_items.item_name, {site_name}_logistics_info.primary_location FROM {site_name}_items LEFT JOIN {site_name}_logistics_info ON {site_name}_items.logistics_info_id = {site_name}_logistics_info.id WHERE {site_name}_items.id={item_id};") cur.execute(f"SELECT {site_name}_items.id, {site_name}_items.barcode, {site_name}_items.item_name, {site_name}_logistics_info.primary_location FROM {site_name}_items LEFT JOIN {site_name}_logistics_info ON {site_name}_items.logistics_info_id = {site_name}_logistics_info.id WHERE {site_name}_items.id={item_id};")
barcode, name, primary_location = cur.fetchone() item_id, barcode, name, primary_location = cur.fetchone()
payload = [ payload = [
datetime.datetime.now(), datetime.datetime.now(),
logistics_info_id, logistics_info_id,
@ -463,8 +815,9 @@ def updateItem():
payload=payload, payload=payload,
location=primary_location, location=primary_location,
logistics_info_id=logistics_info_id, logistics_info_id=logistics_info_id,
barcode=barcode, item_id=item_id,
qty=0.0 qty=0.0,
cost=0.0
) )
except (Exception, psycopg2.DatabaseError) as error: except (Exception, psycopg2.DatabaseError) as error:
@ -475,6 +828,73 @@ def updateItem():
return jsonify({"status": "FAILED"}) return jsonify({"status": "FAILED"})
@database_api.route("/linkItem", methods=["POST"])
def linkItemToItem():
if request.method == "POST":
database_config = config()
site_name = session['selected_site']
master_index = request.json['master_index']
sub_index = request.json['sub_index']
print(master_index, sub_index)
with psycopg2.connect(**database_config) as conn:
try:
with conn.cursor() as cur:
with open(f"sites/{site_name}/sql/unique/select_item_all.sql", "r+") as file:
sql = file.read()
cur.execute(sql, (sub_index, ))
sub_item = cur.fetchone()
# grab all the location data and then get the qty on hand
sql_location_data = f"SELECT {site_name}_locations.uuid, {site_name}_item_locations.quantity_on_hand, {site_name}_item_locations.cost_layers FROM {site_name}_item_locations LEFT JOIN {site_name}_locations ON {site_name}_item_locations.location_id = {site_name}_locations.id WHERE part_id=%s;"
cur.execute(sql_location_data, (sub_item[0],))
x = cur.fetchall()
qty_on_hand = sum([location[1] for location in x])
# Delete sub_item from database and cascade through tables
sql = f"DELETE FROM {site_name}_items WHERE id=%s;"
cur.execute(sql, (sub_index,))
# insert sub_item into the links table
sql = f"INSERT INTO {site_name}_itemlinks (barcode, link, data, conv_factor) VALUES (%s, %s, %s, %s);"
cur.execute(sql, (sub_item[1], master_index, json.dumps(sub_item), 1.0))
# need to adjust the qty on hand into the master items
with open(f"sites/{site_name}/sql/unique/select_item_all.sql", "r+") as file:
sql = file.read()
cur.execute(sql, (master_index,))
master_item = cur.fetchone()
payload = [
datetime.datetime.now(),
master_item[8],
master_item[1],
master_item[2],
"Adjust In",
qty_on_hand,
f"COVERSION FROM {sub_item[1]}",
1,
json.dumps({'location': master_item[15], 'cost': sub_item[28]*qty_on_hand})
]
print(payload)
main.addTransaction(
conn=conn,
site_name=site_name,
payload=payload,
location=master_item[15],
logistics_info_id=master_item[8],
item_id=master_item[0],
qty=qty_on_hand,
cost=sub_item[28]*qty_on_hand
)
except (Exception, psycopg2.DatabaseError) as error:
print(error)
return jsonify({})
@database_api.route("/addGroup") @database_api.route("/addGroup")
def addGroup(): def addGroup():
name = str(request.args.get('name', "")) name = str(request.args.get('name', ""))
@ -641,7 +1061,7 @@ def paginate_lists():
list_length = len(custom_items) list_length = len(custom_items)
if shopping_list[10] == 'calculated': if shopping_list[10] == 'calculated':
item_sql = f"SELECT COUNT(*) FROM {site_name}_items LEFT JOIN {site_name}_logistics_info ON {site_name}_items.logistics_info_id = {site_name}_logistics_info.id LEFT JOIN {site_name}n_item_info ON {site_name}_items.item_info_id = {site_name}_item_info.id LEFT JOIN {site_name}_food_info ON {site_name}_items.food_info_id = {site_name}_food_info.id WHERE {site_name}_logistics_info.quantity_on_hand < {site_name}_item_info.safety_stock AND shopping_lists @> ARRAY[%s];" item_sql = f"SELECT COUNT(*) FROM {site_name}_items LEFT JOIN {site_name}_logistics_info ON {site_name}_items.logistics_info_id = {site_name}_logistics_info.id LEFT JOIN {site_name}_item_info ON {site_name}_items.item_info_id = {site_name}_item_info.id LEFT JOIN {site_name}_food_info ON {site_name}_items.food_info_id = {site_name}_food_info.id WHERE {site_name}_logistics_info.quantity_on_hand < {site_name}_item_info.safety_stock AND shopping_lists @> ARRAY[%s];"
cur.execute(item_sql, (shopping_list[0], )) cur.execute(item_sql, (shopping_list[0], ))
list_length += cur.fetchone()[0] list_length += cur.fetchone()[0]
else: else:

View File

@ -6,5 +6,5 @@ password = test
port = 5432 port = 5432
[manage] [manage]
sites = test,test2,main sites = ,test,main,Backpack

232
external_devices.py Normal file
View File

@ -0,0 +1,232 @@
from flask import Blueprint, request, render_template, redirect, session, url_for, send_file, jsonify, Response
import psycopg2, math, json, datetime, main, copy, openfoodfacts
from config import config, sites_config
from main import unfoldCostLayers
external_api= Blueprint('external_api', __name__)
open_food_api = openfoodfacts.API(user_agent="MyAwesomeApp/1.0")
open_food_enabled = False
def parseOpenFoodsData(data: dict):
print(data)
x = [
("brands_tags", list, []), # process into items.tags
("categories_tags", list, []), # process into items.tags
("countries_tags", list, []), # process into items.tags
("labels_hierarchy", list, []), # process into items.tags
("ingredients_text_en", str, ""), # process into a list of food_info.ingrediants
("nutriments", dict, {}), # process into food_info.nutrients
("product_name", str, ""), # #process into items.item_name
("serving_size", str, ""), # add to nutriments
("code", str, "") # process into items.barcode
]
dummy = {}
keys = data.keys()
for key in x:
if key[0] in keys and isinstance(data[key[0]], key[1]):
dummy[key[0]] = data[key[0]]
else:
dummy[key[0]] = key[2]
tags = dummy["brands_tags"] + dummy["categories_tags"] + dummy["countries_tags"] + dummy["labels_hierarchy"]
ingredients = str(dummy["ingredients_text_en"]).split(", ")
nutriments = dummy["nutriments"]
nutriments["serving_size"] = dummy["serving_size"]
payload = copy.deepcopy(main.payload_food_item)
payload["tags"] = tags
payload["product_name"] = dummy["product_name"]
payload["food_info"]["ingrediants"] = ingredients
payload["food_info"]["nutrients"] = nutriments
print(payload)
@external_api.route("/api/getLink/<site>/<barcode>")
def get_linked_item(site, barcode):
database_config = config()
with psycopg2.connect(**database_config) as conn:
try:
with conn.cursor() as cur:
cur.execute(f"SELECT * FROM {site}_itemlinks WHERE barcode=%s;", (barcode, ))
item = cur.fetchone()
if item:
return jsonify({"item": item}), 200
except (Exception, psycopg2.DatabaseError) as error:
print(error)
conn.rollback()
return jsonify({'state': str(error)}), 500
return jsonify({"item": []}), 500
@external_api.route("/api/getItem/<site>/<barcode>")
def get_item(site, barcode):
database_config = config()
with psycopg2.connect(**database_config) as conn:
try:
with conn.cursor() as cur:
with open(f"sites/{site}/sql/unique/select_item_all_barcode.sql", "r+") as file:
sql = file.read()
cur.execute(sql, (barcode, ))
item = cur.fetchone()
if item:
return jsonify({"item": item}), 200
except (Exception, psycopg2.DatabaseError) as error:
print(error)
conn.rollback()
return jsonify({'state': str(error)}), 500
return jsonify({"item": []}), 500
@external_api.route("/api/getOpenFacts/<site>/<barcode>")
def get_open_facts(site, barcode):
if open_food_enabled:
data = open_food_api.product.get(barcode)
if data != None:
return jsonify({"item": data}), 500
return jsonify({"item": []}), 500
@external_api.route("/api/addTransaction", methods=['POST'])
def add_transaction():
if request.method == "POST":
print(request.get_json())
site_name = request.get_json()["site_name"]
logistics_info_id = request.get_json()['logistics_info_id']
barcode = request.get_json()['barcode']
name = request.get_json()['name']
location = request.get_json()['location']
qty = float(request.get_json()['qty'])
trans_type = request.get_json()['trans_type']
trans_cost = request.get_json()['trans_cost']
database_config = config()
actual_qty = qty
if trans_type == "Adjust Out":
actual_qty = -qty
with psycopg2.connect(**database_config) as conn:
try:
with conn.cursor() as cur:
cur.execute(f"SELECT id FROM {site_name}_items WHERE barcode=%s;", (barcode,))
item_id = cur.fetchone()
payload = [
datetime.datetime.now(),
logistics_info_id,
barcode,
name,
trans_type,
qty,
"",
1,
json.dumps({'location': location, 'cost': trans_cost})
]
print(payload)
main.addTransaction(
conn=conn,
site_name=site_name,
payload=payload,
location=location,
logistics_info_id=logistics_info_id,
item_id=item_id,
qty=actual_qty,
cost=trans_cost
)
except (Exception, psycopg2.DatabaseError) as error:
print(error)
conn.rollback()
return jsonify({'state': str(error)})
print("SUCCESS")
return jsonify({'state': str("SUCCESS")})
print("SUCCESS")
return jsonify({'state': str("FAILED")})
@external_api.route("/api/requestReceiptId/<site>")
def request_receipt_id(site):
"""gets the next id for receipts_id, currently returns a 8 digit number
Args:
site (str): site to get the next id for
Returns:
json: receipt_id, message, error keys
"""
database_config = config()
with psycopg2.connect(**database_config) as conn:
try:
with conn.cursor() as cur:
cur.execute(f"SELECT receipt_id FROM {site}_receipts ORDER BY id DESC LIMIT 1;")
next_receipt_id = cur.fetchone()
print(next_receipt_id)
if next_receipt_id == None:
next_receipt_id = "00000001"
else:
next_receipt_id = next_receipt_id[0]
next_receipt_id = int(next_receipt_id.split("-")[1]) + 1
y = str(next_receipt_id)
len_str = len(y)
x = "".join(["0" for _ in range(8 - len_str)])
next_receipt_id = x + y
except (Exception, psycopg2.DatabaseError) as error:
print(error)
conn.rollback()
return jsonify({"message": "Failed", "error": str(error)})
return jsonify({"receipt_id": next_receipt_id, "message": "Success", "error": "None"}), 200
@external_api.route("/api/addReceipt", methods=["POST"])
def add_receipt():
"""Receives a payload and adds the receipt to the system for <site>
payload = {
receipt_id: str
receipt_status: str
date_submitted: timestamp
submitted_by: INT
vendor_id: INT
files: dict
items: list = (tuples)
(type, 0, barcode, name, qty, data, status),
site_name: str
}
Returns:
Success: dict with "error", "message" keys
"""
if request.method == "POST":
site_name = request.get_json()["site_name"]
receipt_id = request.get_json()["receipt_id"]
receipt_status = request.get_json()["receipt_status"]
date_submitted = request.get_json()['date_submitted']
submitted_by = request.get_json()["submitted_by"]
vendor_id = request.get_json()["vendor_id"]
files = request.get_json()["files"]
items = request.get_json()["items"]
payload = (receipt_id, receipt_status, date_submitted, submitted_by, vendor_id, json.dumps(files))
database_config = config()
with psycopg2.connect(**database_config) as conn:
try:
with conn.cursor() as cur:
insert_receipt = f"INSERT INTO {site_name}_receipts (receipt_id, receipt_status, date_submitted, submitted_by, vendor_id, files) VALUES (%s, %s, %s, %s, %s, %s) RETURNING id;"
cur.execute(insert_receipt, payload)
row_id = cur.fetchone()[0]
print(row_id)
insert_item = f"INSERT INTO {site_name}_receipt_items (type, receipt_id, barcode, name, qty, data, status) VALUES (%s, %s, %s, %s, %s, %s, %s);"
for item in items:
item = list(item)
item[1] = row_id
item[5] = json.dumps(item[5])
cur.execute(insert_item, item)
except (Exception, psycopg2.DatabaseError) as error:
print(error)
conn.rollback()
return jsonify({"message": "Failed", "error": str(error)})
return jsonify({"message": "Success", "error": "None"})
return jsonify({"message": "Failed", "error": "Must be a post method!"})

118
main.py
View File

@ -1,11 +1,15 @@
#!/usr/bin/python #!/usr/bin/python
import psycopg2 import psycopg2
from config import config from config import config
import json, datetime, copy, csv import json, datetime, copy, csv, ast
def lst2pgarr(alist): def lst2pgarr(alist):
return '{' + ','.join(alist) + '}' return '{' + ','.join(alist) + '}'
def unfoldCostLayers(cost_layers: str):
cost_layers:list = [ast.literal_eval(item) for item in ast.literal_eval(cost_layers.replace('{', '[').replace('}', ']'))]
return cost_layers
def update_item_primary(site_name, barcode, new_primary: str): def update_item_primary(site_name, barcode, new_primary: str):
zone, location = new_primary.split("@") zone, location = new_primary.split("@")
@ -182,11 +186,54 @@ def setLogisticsDataTransaction(conn, site_name, location, logistics_info_id, qt
quantity_on_hand = float(quantity_on_hand + qty) quantity_on_hand = float(quantity_on_hand + qty)
cur.execute(sql, (quantity_on_hand, json.dumps(location_data), logistics_info_id)) cur.execute(sql, (quantity_on_hand, json.dumps(location_data), logistics_info_id))
except Exception as error: except Exception as error:
conn.rollback()
return error return error
return "success" return "success"
def setLocationData(conn, site_name, location, barcode, qty):
def handleNegativeQuantityOnHand(qty, cost_layers):
cost_layers = [ast.literal_eval(item) for item in ast.literal_eval(cost_layers.replace('{', '[').replace('}', ']'))]
dummy_quantity = qty
while dummy_quantity > 0 and len(cost_layers) > 0:
layer: list = list(cost_layers[0])
if layer[0] < 0:
layer[0] = layer[0] + 1
dummy_quantity = dummy_quantity - 1
cost_layers[0] = tuple(layer)
if layer[0] == 0.0:
cost_layers.pop(0)
if dummy_quantity > 0 and len(cost_layers) > 0:
layer = list(cost_layers[0])
if dummy_quantity > 0 and len(cost_layers) == 0:
cost_layers.append((dummy_quantity, 0.0))
string_t = "ARRAY["
string_y = ', '.join([f"'{layer_tuple}'::cost_layer" for layer_tuple in cost_layers])
string_t += string_y + "]::cost_layer[]"
return string_t
def handleNegativeQuantity(qty, cost_layers):
cost_layers = [ast.literal_eval(item) for item in ast.literal_eval(cost_layers.replace('{', '[').replace('}', ']'))]
dummy_quantity = qty
while dummy_quantity < 0 and len(cost_layers) > 0:
layer: list = list(cost_layers[0])
layer[0] = layer[0] - 1
dummy_quantity = dummy_quantity + 1
cost_layers[0] = tuple(layer)
if layer[0] == 0.0:
cost_layers.pop(0)
if dummy_quantity < 0 and len(cost_layers) > 0:
layer = list(cost_layers[0])
if dummy_quantity < 0 and len(cost_layers) == 0:
cost_layers.append((dummy_quantity, 0.0))
string_t = "ARRAY["
string_y = ', '.join([f"'{layer_tuple}'::cost_layer" for layer_tuple in cost_layers])
string_t += string_y + "]::cost_layer[]"
return string_t
def setLocationData(conn, site_name, location, item_id, qty, cost):
"""Sets location data to include barcode: qty as k:v pair """Sets location data to include barcode: qty as k:v pair
Args: Args:
@ -198,16 +245,31 @@ def setLocationData(conn, site_name, location, barcode, qty):
Returns: Returns:
str: error/success str: error/success
""" """
with open(f"sites/{site_name}/sql/unique/set_location_data.sql", "r+") as file: #with open(f"sites/{site_name}/sql/unique/set_location_data.sql", "r+") as file:
sql = file.read() # sql = file.read()
sql = f"UPDATE %sitename%_locations SET quantity_on_hand = %s WHERE id = %s;"
try: try:
with conn.cursor() as cur: with conn.cursor() as cur:
cur.execute(f"SELECT id, items FROM {site_name}_locations WHERE uuid=%s;", (location, )) cur.execute(f"SELECT id FROM {site_name}_locations WHERE uuid=%s;", (location, ))
loc_id, items = cur.fetchone() loc_id = cur.fetchone()
items[barcode] = items.get(barcode, 0) + qty cur.execute(f"SELECT id, quantity_on_hand, cost_layers FROM {site_name}_item_locations WHERE part_id = %s AND location_id = %s;", (item_id, loc_id))
cur.execute(sql, (json.dumps(items), loc_id)) x = cur.fetchone()
# maybe a while loop that will pull the cost_layers out and then go
# through each 1 by 1 until qty is at 0...
qty_on_hand = float(x[1]) + float(qty)
if x[1] < 0 and qty > 0:
# do thing
cost_layers_string = handleNegativeQuantityOnHand(qty, x[2])
cur.execute(f"UPDATE {site_name}_item_locations SET quantity_on_hand = %s, cost_layers = {cost_layers_string} WHERE id = %s;", (qty_on_hand, x[0]))
elif qty < 0:
print("ding")
cost_layers_string = handleNegativeQuantity(qty, x[2])
print(cost_layers_string)
cur.execute(f"UPDATE {site_name}_item_locations SET quantity_on_hand = %s, cost_layers = {cost_layers_string} WHERE id = %s;", (qty_on_hand, x[0]))
else:
cur.execute(f"UPDATE {site_name}_item_locations SET quantity_on_hand = %s, cost_layers = cost_layers || ({qty}, {cost})::cost_layer WHERE id = %s;", (qty_on_hand, x[0]))
except Exception as error: except Exception as error:
conn.rollback() print(error)
return error return error
return "success" return "success"
@ -230,12 +292,11 @@ def insertTransaction(conn, site_name, payload):
with conn.cursor() as cur: with conn.cursor() as cur:
cur.execute(sql, payload) cur.execute(sql, payload)
except Exception as error: except Exception as error:
conn.rollback()
return error return error
return "success" return "success"
def addTransaction(*, conn, site_name, payload, location, logistics_info_id, barcode, qty): def addTransaction(*, conn, site_name, payload, location, logistics_info_id, item_id, qty, cost):
"""a complete function for adding a transaction to the system """a complete function for adding a transaction to the system
payload = [timestamp, logistics_info_id, barcode, name, transaction_type, payload = [timestamp, logistics_info_id, barcode, name, transaction_type,
@ -255,8 +316,9 @@ def addTransaction(*, conn, site_name, payload, location, logistics_info_id, bar
""" """
try: try:
insertTransaction(conn, site_name, payload) insertTransaction(conn, site_name, payload)
setLocationData(conn, site_name, location, barcode, qty) if qty != 0.0:
setLogisticsDataTransaction(conn, site_name, location, logistics_info_id, qty) setLocationData(conn, site_name, location, item_id, qty, cost)
#setLogisticsDataTransaction(conn, site_name, location, logistics_info_id, qty)
except (Exception, psycopg2.DatabaseError) as error: except (Exception, psycopg2.DatabaseError) as error:
print(error) print(error)
conn.rollback() conn.rollback()
@ -286,8 +348,18 @@ def add_food_item(site_name: str, barcode: str, name: str, payload: dict):
food_info_id = create_food_info(conn, site_name, payload["food_info"]) food_info_id = create_food_info(conn, site_name, payload["food_info"])
if not food_info_id: if not food_info_id:
return False return False
try:
with conn.cursor() as cur:
cur.execute(f"SELECT id FROM {site_name}_locations WHERE uuid=%s;", (uuid, ))
location_id = cur.fetchone()[0]
print(location_id)
except (Exception, psycopg2.DatabaseError) as error:
print(error)
conn.rollback()
return False
sqltwo = f"INSERT INTO {site_name}_items(barcode, item_name, tags, links, item_info_id, logistics_info_id, food_info_id, row_type, item_type, search_string) VALUES('{barcode}', '{name}', '{tags}', '{links}', {item_info_id}, {logistics_info_id}, {food_info_id}, 'single', 'FOOD', '{barcode}%{name}') RETURNING *;" sqltwo = f"INSERT INTO {site_name}_items(barcode, item_name, tags, links, item_info_id, logistics_info_id, food_info_id, row_type, item_type, search_string) VALUES('{barcode}', '{name}', '{tags}', '{links}', {item_info_id}, {logistics_info_id}, {food_info_id}, 'single', 'FOOD', '{barcode}%{name}') RETURNING *;"
sqlthree = f"INSERT INTO {site_name}_item_locations(part_id, location_id, quantity_on_hand, cost_layers) VALUES (%s, %s, %s, %s);"
row = None row = None
try: try:
with conn.cursor() as cur: with conn.cursor() as cur:
@ -295,6 +367,8 @@ def add_food_item(site_name: str, barcode: str, name: str, payload: dict):
rows = cur.fetchone() rows = cur.fetchone()
if rows: if rows:
row = rows[:] row = rows[:]
print(row)
cur.execute(sqlthree, (row[0], location_id, 0.0, lst2pgarr([])))
except (Exception, psycopg2.DatabaseError) as error: except (Exception, psycopg2.DatabaseError) as error:
print(error) print(error)
conn.rollback() conn.rollback()
@ -303,7 +377,7 @@ def add_food_item(site_name: str, barcode: str, name: str, payload: dict):
conn.commit() conn.commit()
payload = [datetime.datetime.now(), logistics_info_id, barcode, name, "SYSTEM", 0.0, "Item added to system!", 1, json.dumps({})] payload = [datetime.datetime.now(), logistics_info_id, barcode, name, "SYSTEM", 0.0, "Item added to system!", 1, json.dumps({})]
addTransaction(conn=conn, site_name=site_name,payload=payload, location=uuid, logistics_info_id=logistics_info_id,barcode=barcode, qty=0.0) addTransaction(conn=conn, site_name=site_name,payload=payload, location=uuid, logistics_info_id=logistics_info_id,item_id=row[0], qty=0.0, cost=0.0)
def drop_table(sql_file: str): def drop_table(sql_file: str):
database_config = config() database_config = config()
@ -339,6 +413,7 @@ def delete_site(site_name):
drop_table(f'sites/{site_name}/sql/drop/receipts.sql') drop_table(f'sites/{site_name}/sql/drop/receipts.sql')
drop_table(f'sites/{site_name}/sql/drop/recipes.sql') drop_table(f'sites/{site_name}/sql/drop/recipes.sql')
drop_table(f'sites/{site_name}/sql/drop/shopping_lists.sql') drop_table(f'sites/{site_name}/sql/drop/shopping_lists.sql')
drop_table(f'sites/{site_name}/sql/drop/item_locations.sql')
def create_site(site_name): def create_site(site_name):
@ -356,15 +431,16 @@ def create_site(site_name):
create_table(f'sites/{site_name}/sql/create/zones.sql') create_table(f'sites/{site_name}/sql/create/zones.sql')
create_table(f'sites/{site_name}/sql/create/locations.sql') create_table(f'sites/{site_name}/sql/create/locations.sql')
create_table(f'sites/{site_name}/sql/create/vendors.sql') create_table(f'sites/{site_name}/sql/create/vendors.sql')
create_table(f'sites/{site_name}/sql/create/receipt_items.sql')
create_table(f'sites/{site_name}/sql/create/receipts.sql') create_table(f'sites/{site_name}/sql/create/receipts.sql')
create_table(f'sites/{site_name}/sql/create/receipt_items.sql')
create_table(f'sites/{site_name}/sql/create/recipes.sql') create_table(f'sites/{site_name}/sql/create/recipes.sql')
create_table(f'sites/{site_name}/sql/create/shopping_lists.sql') create_table(f'sites/{site_name}/sql/create/shopping_lists.sql')
create_table(f'sites/{site_name}/sql/create/item_locations.sql')
sql = f"INSERT INTO {site_name}_zones(name) VALUES (%s) RETURNING id;" sql = f"INSERT INTO {site_name}_zones(name) VALUES (%s) RETURNING id;"
sqltwo = f"INSERT INTO {site_name}_locations(uuid, name, zone_id, items) VALUES (%s, %s, %s, %s);" sqltwo = f"INSERT INTO {site_name}_locations(uuid, name, zone_id, items) VALUES (%s, %s, %s, %s);"
sqlthree = f"INSERT INTO {site_name}_vendors(vendor_name, creation_date, created_by) VALUES (%s, %s, %s);"
database_config = config() database_config = config()
with psycopg2.connect(**database_config) as conn: with psycopg2.connect(**database_config) as conn:
zone_id = None zone_id = None
@ -389,6 +465,14 @@ def create_site(site_name):
conn.rollback() conn.rollback()
return False return False
try:
with conn.cursor() as cur:
cur.execute(sqlthree, ("None", str(datetime.datetime.now()), 1))
except (Exception, psycopg2.DatabaseError) as error:
print(error)
conn.rollback()
return False
conn.commit() conn.commit()

View File

@ -108,6 +108,7 @@ if __name__ == "__main__":
main.create_site(sys.argv[3]) main.create_site(sys.argv[3])
if func_name == "delete" and argument == "site": if func_name == "delete" and argument == "site":
print(func_name, argument)
main.delete_site(sys.argv[3]) main.delete_site(sys.argv[3])
shutil.rmtree(f"sites/{sys.argv[3]}") shutil.rmtree(f"sites/{sys.argv[3]}")
cfg.delete_site(sys.argv[3]) cfg.delete_site(sys.argv[3])

38
scratch.py Normal file
View File

@ -0,0 +1,38 @@
sql = "SELECT items FROM main_locations WHERE id=1;"
from config import config
import psycopg2, requests
import main, datetime
"""database_config = config()
with psycopg2.connect(**database_config) as conn:
result = main.setLocationData(conn, "main", "default@all", 1, 4.0, 0.0)
print(result)"""
url = "http://192.168.1.45:5810/resolveReceiptItem"
"""payload_receipt = {
"receipt_id": 123456,
"receipt_status": "Unresolved",
"date_submitted": str(datetime.datetime.now()),
"submitted_by": 1,
"vendor_id": 0,
"files": {},
"items": [
("FOOD", 0, "%1234%", "test_item", 1.0, {"cost": 1.99, "EXPIRES": False}, "Unresolved"),
("FOOD", 0, "%1235%", "test_item", 1.0, {"cost": 1.99, "EXPIRES": False}, "Unresolved"),
("FOOD", 0, "%1236%", "test_item", 1.0, {"cost": 1.99, "EXPIRES": False}, "Unresolved"),
],
"site_name": "main"
}"""
response = requests.post(url)
receipt_id = response.json()["receipt_id"]
print(receipt_id)

View File

@ -1,7 +1,7 @@
[site] [site]
site_name=test2 site_name=Backpack
site_owner=joe site_owner=Jadowyne
email=jdoe@gmail.com email=
[defaults] [defaults]
default_zone=default default_zone=default

View File

@ -1,4 +1,4 @@
CREATE TABLE IF NOT EXISTS test2_brands ( CREATE TABLE IF NOT EXISTS Backpack_brands (
id SERIAL PRIMARY KEY, id SERIAL PRIMARY KEY,
name VARCHAR(255) name VARCHAR(255)
); );

View File

@ -1,4 +1,4 @@
CREATE TABLE IF NOT EXISTS test2_food_info ( CREATE TABLE IF NOT EXISTS Backpack_food_info (
id SERIAL PRIMARY KEY, id SERIAL PRIMARY KEY,
food_groups TEXT [], food_groups TEXT [],
ingrediants TEXT [], ingrediants TEXT [],

View File

@ -1,4 +1,4 @@
CREATE TABLE IF NOT EXISTS test2_groups( CREATE TABLE IF NOT EXISTS Backpack_groups(
id SERIAL PRIMARY KEY, id SERIAL PRIMARY KEY,
name VARCHAR(255) NOT NULL, name VARCHAR(255) NOT NULL,
description TEXT, description TEXT,

View File

@ -1,4 +1,4 @@
CREATE TABLE IF NOT EXISTS test2_items( CREATE TABLE IF NOT EXISTS Backpack_items(
id SERIAL PRIMARY KEY, id SERIAL PRIMARY KEY,
barcode VARCHAR(255) NOT NULL, barcode VARCHAR(255) NOT NULL,
item_name VARCHAR(255) NOT NULL, item_name VARCHAR(255) NOT NULL,
@ -15,14 +15,18 @@ CREATE TABLE IF NOT EXISTS test2_items(
UNIQUE(barcode, item_info_id), UNIQUE(barcode, item_info_id),
CONSTRAINT fk_item_info CONSTRAINT fk_item_info
FOREIGN KEY(item_info_id) FOREIGN KEY(item_info_id)
REFERENCES test2_item_info(id), REFERENCES Backpack_item_info(id)
ON DELETE CASCADE,
CONSTRAINT fk_food_info CONSTRAINT fk_food_info
FOREIGN KEY(food_info_id) FOREIGN KEY(food_info_id)
REFERENCES test2_food_info(id), REFERENCES Backpack_food_info(id)
ON DELETE CASCADE,
CONSTRAINT fk_brand CONSTRAINT fk_brand
FOREIGN KEY(brand) FOREIGN KEY(brand)
REFERENCES test2_brands(id), REFERENCES Backpack_brands(id)
ON DELETE CASCADE,
CONSTRAINT fk_logistics_info CONSTRAINT fk_logistics_info
FOREIGN KEY(logistics_info_id) FOREIGN KEY(logistics_info_id)
REFERENCES test2_logistics_info(id) REFERENCES Backpack_logistics_info(id)
ON DELETE CASCADE
); );

View File

@ -1,4 +1,4 @@
CREATE TABLE IF NOt EXISTS test2_item_info ( CREATE TABLE IF NOt EXISTS Backpack_item_info (
id SERIAL PRIMARY KEY, id SERIAL PRIMARY KEY,
barcode VARCHAR(255) NOT NULL, barcode VARCHAR(255) NOT NULL,
linked_items INTEGER [], linked_items INTEGER [],

View File

@ -0,0 +1,23 @@
DO $$
BEGIN
IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'cost_layer') THEN
CREATE TYPE cost_layer AS (qty FLOAT8, cost FLOAT8);
END IF;
END $$;
CREATE TABLE IF NOT EXISTS Backpack_item_locations(
id SERIAL PRIMARY KEY,
part_id INTEGER NOT NULL,
location_id INTEGER NOT NULL,
quantity_on_hand FLOAT8 NOT NULL,
cost_layers cost_layer[],
UNIQUE(part_id, location_id),
CONSTRAINT fk_part_id
FOREIGN KEY(part_id)
REFERENCES Backpack_items(id)
ON DELETE CASCADE,
CONSTRAINT fk_location_id
FOREIGN KEY(location_id)
REFERENCES Backpack_locations(id)
ON DELETE CASCADE
);

View File

@ -1,4 +1,4 @@
CREATE TABLE IF NOT EXISTS test2_itemlinks ( CREATE TABLE IF NOT EXISTS Backpack_itemlinks (
id SERIAL PRIMARY KEY, id SERIAL PRIMARY KEY,
barcode VARCHAR(255) NOt NULL, barcode VARCHAR(255) NOt NULL,
link INTEGER NOT NULL, link INTEGER NOT NULL,

View File

@ -1,4 +1,4 @@
CREATE TABLE IF NOT EXISTS test2_locations( CREATE TABLE IF NOT EXISTS Backpack_locations(
id SERIAL PRIMARY KEY, id SERIAL PRIMARY KEY,
uuid VARCHAR(255) NOT NULL, uuid VARCHAR(255) NOT NULL,
name VARCHAR(32) NOT NULL, name VARCHAR(32) NOT NULL,
@ -7,5 +7,5 @@ CREATE TABLE IF NOT EXISTS test2_locations(
UNIQUE(uuid), UNIQUE(uuid),
CONSTRAINT fk_zone CONSTRAINT fk_zone
FOREIGN KEY(zone_id) FOREIGN KEY(zone_id)
REFERENCES test2_zones(id) REFERENCES Backpack_zones(id)
); );

View File

@ -1,4 +1,4 @@
CREATE TABLE IF NOT EXISTS test2_logistics_info( CREATE TABLE IF NOT EXISTS Backpack_logistics_info(
id SERIAL PRIMARY KEY, id SERIAL PRIMARY KEY,
barcode VARCHAR(255) NOT NULL, barcode VARCHAR(255) NOT NULL,
primary_location VARCHAR(64), primary_location VARCHAR(64),

View File

@ -0,0 +1,13 @@
CREATE TABLE IF NOT EXISTS Backpack_receipt_items (
id SERIAL PRIMARY KEY,
type VARCHAR(255) NOT NULL,
receipt_id INTEGER NOT NULL,
barcode VARCHAR(255) NOT NULL,
name VARCHAR(255) NOT NULL,
qty FLOAT8 NOT NULL,
data JSONB,
status VARCHAR (64),
CONSTRAINT fk_receipt
FOREIGN KEY(receipt_id)
REFERENCES Backpack_receipts(id)
);

View File

@ -0,0 +1,13 @@
CREATE TABLE IF NOT EXISTS Backpack_receipts (
id SERIAL PRIMARY KEY,
receipt_id VARCHAR (32) NOT NULL,
receipt_status VARCHAR (64) NOT NULL,
date_submitted TIMESTAMP NOT NULL,
submitted_by INTEGER NOT NULL,
vendor_id INTEGER,
files JSONB,
UNIQUE(receipt_id),
CONSTRAINT fk_vendor
FOREIGN KEY(vendor_id)
REFERENCES Backpack_vendors(id)
);

View File

@ -1,4 +1,4 @@
CREATE TABLE IF NOT EXISTS test2_recipes ( CREATE TABLE IF NOT EXISTS Backpack_recipes (
id SERIAL PRIMARY KEY, id SERIAL PRIMARY KEY,
name VARCHAR, name VARCHAR,
author INTEGER, author INTEGER,

View File

@ -1,4 +1,4 @@
CREATE TABLE IF NOT EXISTS test2_shopping_lists ( CREATE TABLE IF NOT EXISTS Backpack_shopping_lists (
id SERIAL PRIMARY KEY, id SERIAL PRIMARY KEY,
name VARCHAR(255) NOT NULL, name VARCHAR(255) NOT NULL,
description TEXT, description TEXT,

View File

@ -1,4 +1,4 @@
CREATE TABLE IF NOT EXISTS test2_Transactions ( CREATE TABLE IF NOT EXISTS Backpack_Transactions (
id SERIAL PRIMARY KEY, id SERIAL PRIMARY KEY,
timestamp TIMESTAMP, timestamp TIMESTAMP,
logistics_info_id INTEGER NOT NULL, logistics_info_id INTEGER NOT NULL,
@ -11,5 +11,5 @@ CREATE TABLE IF NOT EXISTS test2_Transactions (
data JSONB, data JSONB,
CONSTRAINT fk_logistics_info CONSTRAINT fk_logistics_info
FOREIGN KEY(logistics_info_id) FOREIGN KEY(logistics_info_id)
REFERENCES test2_logistics_info(id) REFERENCES Backpack_logistics_info(id)
); );

View File

@ -0,0 +1,8 @@
CREATE TABLE IF NOT EXISTS Backpack_vendors (
id SERIAL PRIMARY KEY,
vendor_name VARCHAR(255) NOT NULL,
vendor_address VARCHAR(255),
creation_date TIMESTAMP NOT NULL,
created_by INTEGER NOT NULL,
phone_number VARCHAR(32)
);

View File

@ -1,4 +1,4 @@
CREATE TABLE IF NOT EXISTS test2_zones( CREATE TABLE IF NOT EXISTS Backpack_zones(
id SERIAL PRIMARY KEY, id SERIAL PRIMARY KEY,
name VARCHAR(32) NOT NULL, name VARCHAR(32) NOT NULL,
UNIQUE(name) UNIQUE(name)

View File

@ -0,0 +1 @@
DROP TABLE Backpack_brands CASCADE;

View File

@ -0,0 +1 @@
DROP TABLE Backpack_food_info CASCADE;

View File

@ -0,0 +1 @@
DROP TABLE Backpack_groups CASCADE;

View File

@ -0,0 +1 @@
DROP TABLE Backpack_item_info CASCADE;

View File

@ -0,0 +1 @@
DROP TABLE Backpack_item_locations CASCADE;

View File

@ -0,0 +1 @@
DROP TABLE Backpack_items CASCADE;

View File

@ -0,0 +1 @@
DROP TABLE Backpack_itemlinks CASCADE;

View File

@ -0,0 +1 @@
DROP TABLE Backpack_locations CASCADE;

View File

@ -0,0 +1 @@
DROP TABLE Backpack_logistics_info CASCADE;

View File

@ -0,0 +1 @@
DROP TABLE Backpack_receipt_items CASCADE;

View File

@ -0,0 +1 @@
DROP TABLE Backpack_receipts CASCADE;

View File

@ -0,0 +1 @@
DROP TABLE Backpack_recipes CASCADE;

View File

@ -0,0 +1 @@
DROP TABLE Backpack_shopping_lists CASCADE;

View File

@ -0,0 +1 @@
DROP TABLE Backpack_transactions CASCADE;

View File

@ -0,0 +1 @@
DROP TABLE Backpack_vendors CASCADE;

View File

@ -0,0 +1 @@
DROP TABLE Backpack_zones CASCADE;

View File

@ -0,0 +1,3 @@
INSERT INTO Backpack_transactions
(timestamp, logistics_info_id, barcode, name, transaction_type, quantity, description, user_id, data)
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s);

View File

@ -0,0 +1,3 @@
UPDATE Backpack_logistics_info
SET quantity_on_hand = %s, location_data = %s
WHERE id = %s;

View File

@ -0,0 +1,45 @@
SELECT * FROM Backpack_items
LEFT JOIN Backpack_logistics_info ON Backpack_items.logistics_info_id = Backpack_logistics_info.id
LEFT JOIN Backpack_item_info ON Backpack_items.item_info_id = Backpack_item_info.id
LEFT JOIN Backpack_food_info ON Backpack_items.food_info_id = Backpack_food_info.id
WHERE Backpack_items.id=%s;
/*
00 - item_id
01 - barcode
02 - item_name
03 - brand (id)
04 - description
05 - tags
06 - links
07 - item_info_id
08 - logistics_info_id
09 - food_info_id
10 - row_type
11 - item_type
12 - search_string
13 - logistics_info_id
14 - barcode
15 - primary_location
16 - auto_issue_location
17 - dynamic_locations
18 - location_data
19 - quantity_on_hand
20 - item_info_id
21 - barcode
22 - linked_items
23 - shopping_lists
24 - recipes
25 - groups
26 - packaging
27 - uom
28 - cost
29 - safety_stock
30 - lead_time_days
31 - ai_pick
32 - food_info_id
33 - food_groups
34 - ingrediants
35 - nutrients
36 - expires
*/

View File

@ -0,0 +1,45 @@
SELECT * FROM Backpack_items
LEFT JOIN Backpack_logistics_info ON Backpack_items.logistics_info_id = Backpack_logistics_info.id
LEFT JOIN Backpack_item_info ON Backpack_items.item_info_id = Backpack_item_info.id
LEFT JOIN Backpack_food_info ON Backpack_items.food_info_id = Backpack_food_info.id
WHERE Backpack_items.barcode=%s;
/*
00 - item_id
01 - barcode
02 - item_name
03 - brand (id)
04 - description
05 - tags
06 - links
07 - item_info_id
08 - logistics_info_id
09 - food_info_id
10 - row_type
11 - item_type
12 - search_string
13 - logistics_info_id
14 - barcode
15 - primary_location
16 - auto_issue_location
17 - dynamic_locations
18 - location_data
19 - quantity_on_hand
20 - item_info_id
21 - barcode
22 - linked_items
23 - shopping_lists
24 - recipes
25 - groups
26 - packaging
27 - uom
28 - cost
29 - safety_stock
30 - lead_time_days
31 - ai_pick
32 - food_info_id
33 - food_groups
34 - ingrediants
35 - nutrients
36 - expires
*/

View File

@ -0,0 +1,3 @@
UPDATE Backpack_locations
SET items = %s
WHERE id = %s;

View File

@ -15,14 +15,18 @@ CREATE TABLE IF NOT EXISTS %sitename%_items(
UNIQUE(barcode, item_info_id), UNIQUE(barcode, item_info_id),
CONSTRAINT fk_item_info CONSTRAINT fk_item_info
FOREIGN KEY(item_info_id) FOREIGN KEY(item_info_id)
REFERENCES %sitename%_item_info(id), REFERENCES %sitename%_item_info(id)
ON DELETE CASCADE,
CONSTRAINT fk_food_info CONSTRAINT fk_food_info
FOREIGN KEY(food_info_id) FOREIGN KEY(food_info_id)
REFERENCES %sitename%_food_info(id), REFERENCES %sitename%_food_info(id)
ON DELETE CASCADE,
CONSTRAINT fk_brand CONSTRAINT fk_brand
FOREIGN KEY(brand) FOREIGN KEY(brand)
REFERENCES %sitename%_brands(id), REFERENCES %sitename%_brands(id)
ON DELETE CASCADE,
CONSTRAINT fk_logistics_info CONSTRAINT fk_logistics_info
FOREIGN KEY(logistics_info_id) FOREIGN KEY(logistics_info_id)
REFERENCES %sitename%_logistics_info(id) REFERENCES %sitename%_logistics_info(id)
ON DELETE CASCADE
); );

View File

@ -0,0 +1,23 @@
DO $$
BEGIN
IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'cost_layer') THEN
CREATE TYPE cost_layer AS (qty FLOAT8, cost FLOAT8);
END IF;
END $$;
CREATE TABLE IF NOT EXISTS %sitename%_item_locations(
id SERIAL PRIMARY KEY,
part_id INTEGER NOT NULL,
location_id INTEGER NOT NULL,
quantity_on_hand FLOAT8 NOT NULL,
cost_layers cost_layer[],
UNIQUE(part_id, location_id),
CONSTRAINT fk_part_id
FOREIGN KEY(part_id)
REFERENCES %sitename%_items(id)
ON DELETE CASCADE,
CONSTRAINT fk_location_id
FOREIGN KEY(location_id)
REFERENCES %sitename%_locations(id)
ON DELETE CASCADE
);

View File

@ -1,9 +1,13 @@
CREATE TABLE IF NOT EXISTS %sitename%_receipt_items ( CREATE TABLE IF NOT EXISTS %sitename%_receipt_items (
id SERIAL PRIMARY KEY, id SERIAL PRIMARY KEY,
type VARCHAR(255) NOT NULL, type VARCHAR(255) NOT NULL,
receipt_id INTEGER NOT NULL,
barcode VARCHAR(255) NOT NULL, barcode VARCHAR(255) NOT NULL,
name VARCHAR(255) NOT NULL, name VARCHAR(255) NOT NULL,
qty FLOAT8 NOT NULL, qty FLOAT8 NOT NULL,
data JSONB, data JSONB,
status VARCHAR (64) status VARCHAR (64),
CONSTRAINT fk_receipt
FOREIGN KEY(receipt_id)
REFERENCES %sitename%_receipts(id)
); );

View File

@ -1,10 +1,13 @@
CREATE TABLE IF NOT EXISTS %sitename%_receipts ( CREATE TABLE IF NOT EXISTS %sitename%_receipts (
id SERIAL PRIMARY KEY, id SERIAL PRIMARY KEY,
receipt_id INTEGER NOT NULL, receipt_id VARCHAR (32) NOT NULL,
receipt_status VARCHAR (64) NOT NULL, receipt_status VARCHAR (64) NOT NULL,
date_submitted TIMESTAMP NOT NULL, date_submitted TIMESTAMP NOT NULL,
submitted_by INTEGER NOT NULL, submitted_by INTEGER NOT NULL,
vendor_id INTEGER, vendor_id INTEGER,
files JSONB, files JSONB,
UNIQUE(receipt_id) UNIQUE(receipt_id),
CONSTRAINT fk_vendor
FOREIGN KEY(vendor_id)
REFERENCES %sitename%_vendors(id)
); );

View File

@ -1,8 +1,8 @@
CREATE TABLE IF NOT EXISTS %sitename%_vendors ( CREATE TABLE IF NOT EXISTS %sitename%_vendors (
id SERIAL PRIMARY KEY, id SERIAL PRIMARY KEY,
name VARCHAR(255) NOT NULL, vendor_name VARCHAR(255) NOT NULL,
address VARCHAR(255), vendor_address VARCHAR(255),
creation_date TIMESTAMP NOT NULL, creation_date TIMESTAMP NOT NULL,
created_by TIMESTAMP NOT NULL, created_by INTEGER NOT NULL,
phone_number VARCHAR(32) phone_number VARCHAR(32)
); );

View File

@ -0,0 +1 @@
DROP TABLE %sitename%_item_locations CASCADE;

View File

@ -0,0 +1,45 @@
SELECT * FROM %sitename%_items
LEFT JOIN %sitename%_logistics_info ON %sitename%_items.logistics_info_id = %sitename%_logistics_info.id
LEFT JOIN %sitename%_item_info ON %sitename%_items.item_info_id = %sitename%_item_info.id
LEFT JOIN %sitename%_food_info ON %sitename%_items.food_info_id = %sitename%_food_info.id
WHERE %sitename%_items.barcode=%s;
/*
00 - item_id
01 - barcode
02 - item_name
03 - brand (id)
04 - description
05 - tags
06 - links
07 - item_info_id
08 - logistics_info_id
09 - food_info_id
10 - row_type
11 - item_type
12 - search_string
13 - logistics_info_id
14 - barcode
15 - primary_location
16 - auto_issue_location
17 - dynamic_locations
18 - location_data
19 - quantity_on_hand
20 - item_info_id
21 - barcode
22 - linked_items
23 - shopping_lists
24 - recipes
25 - groups
26 - packaging
27 - uom
28 - cost
29 - safety_stock
30 - lead_time_days
31 - ai_pick
32 - food_info_id
33 - food_groups
34 - ingrediants
35 - nutrients
36 - expires
*/

View File

@ -15,14 +15,18 @@ CREATE TABLE IF NOT EXISTS main_items(
UNIQUE(barcode, item_info_id), UNIQUE(barcode, item_info_id),
CONSTRAINT fk_item_info CONSTRAINT fk_item_info
FOREIGN KEY(item_info_id) FOREIGN KEY(item_info_id)
REFERENCES main_item_info(id), REFERENCES main_item_info(id)
ON DELETE CASCADE,
CONSTRAINT fk_food_info CONSTRAINT fk_food_info
FOREIGN KEY(food_info_id) FOREIGN KEY(food_info_id)
REFERENCES main_food_info(id), REFERENCES main_food_info(id)
ON DELETE CASCADE,
CONSTRAINT fk_brand CONSTRAINT fk_brand
FOREIGN KEY(brand) FOREIGN KEY(brand)
REFERENCES main_brands(id), REFERENCES main_brands(id)
ON DELETE CASCADE,
CONSTRAINT fk_logistics_info CONSTRAINT fk_logistics_info
FOREIGN KEY(logistics_info_id) FOREIGN KEY(logistics_info_id)
REFERENCES main_logistics_info(id) REFERENCES main_logistics_info(id)
ON DELETE CASCADE
); );

View File

@ -0,0 +1,23 @@
DO $$
BEGIN
IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'cost_layer') THEN
CREATE TYPE cost_layer AS (qty FLOAT8, cost FLOAT8);
END IF;
END $$;
CREATE TABLE IF NOT EXISTS main_item_locations(
id SERIAL PRIMARY KEY,
part_id INTEGER NOT NULL,
location_id INTEGER NOT NULL,
quantity_on_hand FLOAT8 NOT NULL,
cost_layers cost_layer[],
UNIQUE(part_id, location_id),
CONSTRAINT fk_part_id
FOREIGN KEY(part_id)
REFERENCES main_items(id)
ON DELETE CASCADE,
CONSTRAINT fk_location_id
FOREIGN KEY(location_id)
REFERENCES main_locations(id)
ON DELETE CASCADE
);

View File

@ -1,9 +1,13 @@
CREATE TABLE IF NOT EXISTS main_receipt_items ( CREATE TABLE IF NOT EXISTS main_receipt_items (
id SERIAL PRIMARY KEY, id SERIAL PRIMARY KEY,
type VARCHAR(255) NOT NULL, type VARCHAR(255) NOT NULL,
receipt_id INTEGER NOT NULL,
barcode VARCHAR(255) NOT NULL, barcode VARCHAR(255) NOT NULL,
name VARCHAR(255) NOT NULL, name VARCHAR(255) NOT NULL,
qty FLOAT8 NOT NULL, qty FLOAT8 NOT NULL,
data JSONB, data JSONB,
status VARCHAR (64) status VARCHAR (64),
CONSTRAINT fk_receipt
FOREIGN KEY(receipt_id)
REFERENCES main_receipts(id)
); );

View File

@ -1,10 +1,13 @@
CREATE TABLE IF NOT EXISTS main_receipts ( CREATE TABLE IF NOT EXISTS main_receipts (
id SERIAL PRIMARY KEY, id SERIAL PRIMARY KEY,
receipt_id INTEGER NOT NULL, receipt_id VARCHAR (32) NOT NULL,
receipt_status VARCHAR (64) NOT NULL, receipt_status VARCHAR (64) NOT NULL,
date_submitted TIMESTAMP NOT NULL, date_submitted TIMESTAMP NOT NULL,
submitted_by INTEGER NOT NULL, submitted_by INTEGER NOT NULL,
vendor_id INTEGER, vendor_id INTEGER,
files JSONB, files JSONB,
UNIQUE(receipt_id) UNIQUE(receipt_id),
CONSTRAINT fk_vendor
FOREIGN KEY(vendor_id)
REFERENCES main_vendors(id)
); );

View File

@ -1,8 +1,8 @@
CREATE TABLE IF NOT EXISTS main_vendors ( CREATE TABLE IF NOT EXISTS main_vendors (
id SERIAL PRIMARY KEY, id SERIAL PRIMARY KEY,
name VARCHAR(255) NOT NULL, vendor_name VARCHAR(255) NOT NULL,
address VARCHAR(255), vendor_address VARCHAR(255),
creation_date TIMESTAMP NOT NULL, creation_date TIMESTAMP NOT NULL,
created_by TIMESTAMP NOT NULL, created_by INTEGER NOT NULL,
phone_number VARCHAR(32) phone_number VARCHAR(32)
); );

View File

@ -0,0 +1 @@
DROP TABLE main_item_locations CASCADE;

View File

@ -1,8 +1,8 @@
SELECT * FROM test2_items SELECT * FROM main_items
LEFT JOIN test2_logistics_info ON test2_items.logistics_info_id = test2_logistics_info.id LEFT JOIN main_logistics_info ON main_items.logistics_info_id = main_logistics_info.id
LEFT JOIN test2_item_info ON test2_items.item_info_id = test2_item_info.id LEFT JOIN main_item_info ON main_items.item_info_id = main_item_info.id
LEFT JOIN test2_food_info ON test2_items.food_info_id = test2_food_info.id LEFT JOIN main_food_info ON main_items.food_info_id = main_food_info.id
WHERE test2_items.id=%s; WHERE main_items.barcode=%s;
/* /*
00 - item_id 00 - item_id

View File

@ -15,14 +15,18 @@ CREATE TABLE IF NOT EXISTS test_items(
UNIQUE(barcode, item_info_id), UNIQUE(barcode, item_info_id),
CONSTRAINT fk_item_info CONSTRAINT fk_item_info
FOREIGN KEY(item_info_id) FOREIGN KEY(item_info_id)
REFERENCES test_item_info(id), REFERENCES test_item_info(id)
ON DELETE CASCADE,
CONSTRAINT fk_food_info CONSTRAINT fk_food_info
FOREIGN KEY(food_info_id) FOREIGN KEY(food_info_id)
REFERENCES test_food_info(id), REFERENCES test_food_info(id)
ON DELETE CASCADE,
CONSTRAINT fk_brand CONSTRAINT fk_brand
FOREIGN KEY(brand) FOREIGN KEY(brand)
REFERENCES test_brands(id), REFERENCES test_brands(id)
ON DELETE CASCADE,
CONSTRAINT fk_logistics_info CONSTRAINT fk_logistics_info
FOREIGN KEY(logistics_info_id) FOREIGN KEY(logistics_info_id)
REFERENCES test_logistics_info(id) REFERENCES test_logistics_info(id)
ON DELETE CASCADE
); );

View File

@ -0,0 +1,23 @@
DO $$
BEGIN
IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'cost_layer') THEN
CREATE TYPE cost_layer AS (qty FLOAT8, cost FLOAT8);
END IF;
END $$;
CREATE TABLE IF NOT EXISTS test_item_locations(
id SERIAL PRIMARY KEY,
part_id INTEGER NOT NULL,
location_id INTEGER NOT NULL,
quantity_on_hand FLOAT8 NOT NULL,
cost_layers cost_layer[],
UNIQUE(part_id, location_id),
CONSTRAINT fk_part_id
FOREIGN KEY(part_id)
REFERENCES test_items(id)
ON DELETE CASCADE,
CONSTRAINT fk_location_id
FOREIGN KEY(location_id)
REFERENCES test_locations(id)
ON DELETE CASCADE
);

View File

@ -1,9 +1,13 @@
CREATE TABLE IF NOT EXISTS test_receipt_items ( CREATE TABLE IF NOT EXISTS test_receipt_items (
id SERIAL PRIMARY KEY, id SERIAL PRIMARY KEY,
type VARCHAR(255) NOT NULL, type VARCHAR(255) NOT NULL,
receipt_id INTEGER NOT NULL,
barcode VARCHAR(255) NOT NULL, barcode VARCHAR(255) NOT NULL,
name VARCHAR(255) NOT NULL, name VARCHAR(255) NOT NULL,
qty FLOAT8 NOT NULL, qty FLOAT8 NOT NULL,
data JSONB, data JSONB,
status VARCHAR (64) status VARCHAR (64),
CONSTRAINT fk_receipt
FOREIGN KEY(receipt_id)
REFERENCES test_receipts(id)
); );

View File

@ -1,10 +1,13 @@
CREATE TABLE IF NOT EXISTS test_receipts ( CREATE TABLE IF NOT EXISTS test_receipts (
id SERIAL PRIMARY KEY, id SERIAL PRIMARY KEY,
receipt_id INTEGER NOT NULL, receipt_id VARCHAR (32) NOT NULL,
receipt_status VARCHAR (64) NOT NULL, receipt_status VARCHAR (64) NOT NULL,
date_submitted TIMESTAMP NOT NULL, date_submitted TIMESTAMP NOT NULL,
submitted_by INTEGER NOT NULL, submitted_by INTEGER NOT NULL,
vendor_id INTEGER, vendor_id INTEGER,
files JSONB, files JSONB,
UNIQUE(receipt_id) UNIQUE(receipt_id),
CONSTRAINT fk_vendor
FOREIGN KEY(vendor_id)
REFERENCES test_vendors(id)
); );

View File

@ -1,8 +1,8 @@
CREATE TABLE IF NOT EXISTS test_vendors ( CREATE TABLE IF NOT EXISTS test_vendors (
id SERIAL PRIMARY KEY, id SERIAL PRIMARY KEY,
name VARCHAR(255) NOT NULL, vendor_name VARCHAR(255) NOT NULL,
address VARCHAR(255), vendor_address VARCHAR(255),
creation_date TIMESTAMP NOT NULL, creation_date TIMESTAMP NOT NULL,
created_by TIMESTAMP NOT NULL, created_by INTEGER NOT NULL,
phone_number VARCHAR(32) phone_number VARCHAR(32)
); );

View File

@ -0,0 +1 @@
DROP TABLE test_item_locations CASCADE;

View File

@ -0,0 +1,3 @@
INSERT INTO test_transactions
(timestamp, logistics_info_id, barcode, name, transaction_type, quantity, description, user_id, data)
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s);

View File

@ -0,0 +1,3 @@
UPDATE test_logistics_info
SET quantity_on_hand = %s, location_data = %s
WHERE id = %s;

View File

@ -0,0 +1,45 @@
SELECT * FROM test_items
LEFT JOIN test_logistics_info ON test_items.logistics_info_id = test_logistics_info.id
LEFT JOIN test_item_info ON test_items.item_info_id = test_item_info.id
LEFT JOIN test_food_info ON test_items.food_info_id = test_food_info.id
WHERE test_items.barcode=%s;
/*
00 - item_id
01 - barcode
02 - item_name
03 - brand (id)
04 - description
05 - tags
06 - links
07 - item_info_id
08 - logistics_info_id
09 - food_info_id
10 - row_type
11 - item_type
12 - search_string
13 - logistics_info_id
14 - barcode
15 - primary_location
16 - auto_issue_location
17 - dynamic_locations
18 - location_data
19 - quantity_on_hand
20 - item_info_id
21 - barcode
22 - linked_items
23 - shopping_lists
24 - recipes
25 - groups
26 - packaging
27 - uom
28 - cost
29 - safety_stock
30 - lead_time_days
31 - ai_pick
32 - food_info_id
33 - food_groups
34 - ingrediants
35 - nutrients
36 - expires
*/

View File

@ -0,0 +1,3 @@
UPDATE test_locations
SET items = %s
WHERE id = %s;

View File

@ -1,9 +0,0 @@
CREATE TABLE IF NOT EXISTS test2_receipt_items (
id SERIAL PRIMARY KEY,
type VARCHAR(255) NOT NULL,
barcode VARCHAR(255) NOT NULL,
name VARCHAR(255) NOT NULL,
qty FLOAT8 NOT NULL,
data JSONB,
status VARCHAR (64)
);

View File

@ -1,10 +0,0 @@
CREATE TABLE IF NOT EXISTS test2_receipts (
id SERIAL PRIMARY KEY,
receipt_id INTEGER NOT NULL,
receipt_status VARCHAR (64) NOT NULL,
date_submitted TIMESTAMP NOT NULL,
submitted_by INTEGER NOT NULL,
vendor_id INTEGER,
files JSONB,
UNIQUE(receipt_id)
);

View File

@ -1,8 +0,0 @@
CREATE TABLE IF NOT EXISTS test2_vendors (
id SERIAL PRIMARY KEY,
name VARCHAR(255) NOT NULL,
address VARCHAR(255),
creation_date TIMESTAMP NOT NULL,
created_by TIMESTAMP NOT NULL,
phone_number VARCHAR(32)
);

View File

@ -1 +0,0 @@
DROP TABLE test2_brands CASCADE;

View File

@ -1 +0,0 @@
DROP TABLE test2_food_info CASCADE;

View File

@ -1 +0,0 @@
DROP TABLE test2_groups CASCADE;

View File

@ -1 +0,0 @@
DROP TABLE test2_item_info CASCADE;

View File

@ -1 +0,0 @@
DROP TABLE test2_items CASCADE;

View File

@ -1 +0,0 @@
DROP TABLE test2_itemlinks CASCADE;

View File

@ -1 +0,0 @@
DROP TABLE test2_locations CASCADE;

View File

@ -1 +0,0 @@
DROP TABLE test2_logistics_info CASCADE;

View File

@ -1 +0,0 @@
DROP TABLE test2_receipt_items CASCADE;

View File

@ -1 +0,0 @@
DROP TABLE test2_receipts CASCADE;

View File

@ -1 +0,0 @@
DROP TABLE test2_recipes CASCADE;

View File

@ -1 +0,0 @@
DROP TABLE test2_shopping_lists CASCADE;

View File

@ -1 +0,0 @@
DROP TABLE test2_transactions CASCADE;

View File

@ -1 +0,0 @@
DROP TABLE test2_vendors CASCADE;

View File

@ -1 +0,0 @@
DROP TABLE test2_zones CASCADE;

View File

@ -4,6 +4,7 @@ async function fetchItem() {
const response = await fetch(url); const response = await fetch(url);
data = await response.json(); data = await response.json();
item = data.item; item = data.item;
linked_items = data.linked_items;
}; };
async function fetchZones() { async function fetchZones() {
@ -91,7 +92,6 @@ function updatePrimaryLocation(){
document.getElementById('primary_location').style = "" document.getElementById('primary_location').style = ""
logistics_info['primary_location'] = `${primary_zone}@${primary_location}` logistics_info['primary_location'] = `${primary_zone}@${primary_location}`
}; };
console.log(logistics_info)
}; };
function updateIssueLocation(){ function updateIssueLocation(){
@ -108,54 +108,45 @@ function updateIssueLocation(){
function updateEntryType(){ function updateEntryType(){
updated['row_type'] = document.getElementById('entry_type').value; updated['row_type'] = document.getElementById('entry_type').value;
console.log(updated)
}; };
function updateItemType(){ function updateItemType(){
updated['item_type'] = document.getElementById('item_type').value; updated['item_type'] = document.getElementById('item_type').value;
console.log(updated)
}; };
function updatePackaging(){ function updatePackaging(){
let packaging = document.getElementById('packaging').value; let packaging = document.getElementById('packaging').value;
item_info['packaging'] = packaging; item_info['packaging'] = packaging;
console.log(item_info)
}; };
function updateUOM(){ function updateUOM(){
let uom = document.getElementById('uom').value; let uom = document.getElementById('uom').value;
item_info['uom'] = uom; item_info['uom'] = uom;
console.log(item_info)
}; };
function updateCost(){ function updateCost(){
let cost = document.getElementById('cost').value; let cost = document.getElementById('cost').value;
item_info['cost'] = parseFloat(cost); item_info['cost'] = parseFloat(cost);
console.log(item_info)
}; };
function updateSafetyStock(){ function updateSafetyStock(){
let safety_stock = document.getElementById('safety_stock').value; let safety_stock = document.getElementById('safety_stock').value;
item_info['safety_stock'] = parseFloat(safety_stock); item_info['safety_stock'] = parseFloat(safety_stock);
console.log(item_info)
}; };
function updateLeadTimeDays(){ function updateLeadTimeDays(){
let lead_time_days = document.getElementById('lead_time_days').value; let lead_time_days = document.getElementById('lead_time_days').value;
item_info['lead_time_days'] = parseFloat(lead_time_days); item_info['lead_time_days'] = parseFloat(lead_time_days);
console.log(item_info)
}; };
function updateAiPickable(){ function updateAiPickable(){
let ai_pick = document.getElementById('ai_pickable'); let ai_pick = document.getElementById('ai_pickable');
item_info['ai_pick'] = ai_pick.checked; item_info['ai_pick'] = ai_pick.checked;
console.log(item_info)
}; };
function updateExpires(){ function updateExpires(){
let expires = document.getElementById('expires'); let expires = document.getElementById('expires');
food_info['expires'] = expires.checked; food_info['expires'] = expires.checked;
console.log(food_info)
}; };
function updateNutrients(){ function updateNutrients(){
@ -177,10 +168,7 @@ function updateNutrients(){
fibers: document.getElementById('fibers').value, fibers: document.getElementById('fibers').value,
fibers_unit: document.getElementById('fibers_unit').value fibers_unit: document.getElementById('fibers_unit').value
}; };
console.log(nutrients)
nutrients_changed = true; nutrients_changed = true;
} }
async function saveItem() { async function saveItem() {
@ -202,8 +190,6 @@ async function saveItem() {
updated['links'] = links; updated['links'] = links;
}; };
console.log(`going into fetch ${logistics_info}`)
await fetch(`/updateItem`, { await fetch(`/updateItem`, {
method: 'POST', method: 'POST',
headers: { headers: {

276
static/receiptHandler.js Normal file
View File

@ -0,0 +1,276 @@
let receipt;
let receipt_items;
document.addEventListener('DOMContentLoaded', async function() {
await updateReceipt();
var elems = document.querySelectorAll('.modal');
var instances = M.Modal.init(elems, {
// specify options here
});
var elems = document.getElementById('vendor_address');
var instances = M.CharacterCounter.init(elems);
var elems = document.querySelectorAll('.tooltipped');
var instances = M.Tooltip.init(elems, {
// specify options here
});
});
async function updateReceipt(){
await fetchReceipt();
await propagateInfo();
await propagateItems();
};
async function fetchReceiptItem(index){
const url = new URL('/getReceiptItem', window.location.origin);
url.searchParams.append('index', index);
const response = await fetch(url);
data = await response.json();
console.log(data)
receipt_item = data.receipt_item;
return receipt_item
}
async function fetchReceipt(){
const url = new URL('/getReceipt', window.location.origin);
url.searchParams.append('id', receipt_id);
const response = await fetch(url);
data = await response.json();
receipt = data.receipt;
receipt_items = data.receipt_items
};
async function propagateInfo(){
document.getElementById('receipt_id').innerHTML = receipt[1]
document.getElementById('database_id').innerHTML = `Database ID: ${receipt[0]}`
document.getElementById('status').innerHTML = receipt[2]
document.getElementById('created').innerHTML = receipt[3]
document.getElementById('vendor_name').value = receipt[8]
document.getElementById('vendor_number').value = receipt[12]
document.getElementById('vendor_address').value = receipt[9]
M.Forms.textareaAutoResize(document.getElementById('vendor_address'));
};
async function propagateItems(){
const table = document.getElementById('item_table')
while (table.rows.length > 1) {
table.deleteRow(1);
};
let reference_state = 1
receipt_items.sort((a, b) => a[0] - b[0])
for (let i = 0; i < receipt_items.length; i++){
var row = table.insertRow();
if (receipt_items[i][7] == "Resolved" || receipt_items[i][7] == "Voided"){
row.classList.add("disabled-row")
}
var row_type = row.insertCell();
var row_barcode = row.insertCell();
var row_name = row.insertCell();
var row_qty = row.insertCell();
var row_cost = row.insertCell();
var row_status = row.insertCell();
row_type.innerHTML = receipt_items[i][1]
row_barcode.innerHTML = receipt_items[i][3]
row_name.innerHTML = receipt_items[i][4]
row_qty.innerHTML = receipt_items[i][5]
row_cost.innerHTML = receipt_items[i][6][28]
row_status.innerHTML = receipt_items[i][7]
if ((reference_state % 2) == 0){
row.classList.add('green')
row.classList.add('lighten-5')
}
row.classList.add("custom_row")
row.addEventListener('click', function(){
modify_item(receipt_items[i][0])
})
reference_state++
};
}
async function modify_item(index){
console.log(index)
item = await fetchReceiptItem(index)
console.log(item)
const modal = document.getElementById("modify_item")
var instance = M.Modal.getInstance(modal);
document.getElementById('item_barcode').value = item[3]
document.getElementById('item_database_id').value = item[0]
document.getElementById('item_type').value = item[1]
document.getElementById('item_name').value = item[4]
document.getElementById('item_qty').value = item[5]
document.getElementById('item_cost').value = item[6][28]
instance.open()
}
async function saveItem(){
let index = document.getElementById('item_database_id').value
console.log(index)
const url = new URL('/saveReceiptItem', window.location.origin);
await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
index: index,
cost: parseFloat(document.getElementById('item_cost').value),
qty: parseFloat(document.getElementById('item_qty').value),
barcode: document.getElementById('item_barcode').value
})
})
await updateReceipt();
const modal = document.getElementById("modify_item")
var instance = M.Modal.getInstance(modal);
instance.close()
}
async function deleteItem(){
let index = document.getElementById('item_database_id').value
const url = new URL('/deleteReceiptItem', window.location.origin);
await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
index: index
})
})
await updateReceipt();
const modal = document.getElementById("modify_item")
var instance = M.Modal.getInstance(modal);
instance.close()
}
async function voidItem(){
let index = document.getElementById('item_database_id').value
const url = new URL('/voidReceiptItem', window.location.origin);
await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
index: index
})
})
await updateReceipt();
const modal = document.getElementById("modify_item")
var instance = M.Modal.getInstance(modal);
instance.close()
}
async function resolveItem(){
let index = document.getElementById('item_database_id').value
console.log(index)
await saveItem()
const url = new URL('/resolveReceiptItem', window.location.origin);
await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
index: index
})
})
await updateReceipt();
const modal = document.getElementById("modify_item")
var instance = M.Modal.getInstance(modal);
instance.close()
}
async function fetchVendors(){
const url = new URL('/getVendors', window.location.origin);
const response = await fetch(url);
data = await response.json();
var vendors = data.vendors;
return vendors
}
async function selectVendor(index){
const url = new URL('/saveReceipt', window.location.origin);
await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
vendor_index: index,
receipt_index: receipt[0]
})
})
await updateReceipt();
const modal = document.getElementById("vendors")
var instance = M.Modal.getInstance(modal);
instance.close()
}
async function populateVendors() {
const modal = document.getElementById("vendors")
var instance = M.Modal.getInstance(modal);
vendors = await fetchVendors()
console.log(vendors)
instance.open()
var table = document.getElementById("vendors_table")
while (table.rows.length > 0) {
table.deleteRow(0);
}
const header = table.createTHead();
const row = header.insertRow(0);
var header_database_id = row.insertCell();
header_database_id.classList.add('center')
var header_name = row.insertCell();
header_name.classList.add('center')
var header_address = row.insertCell();
header_address.classList.add('center')
var header_number = row.insertCell();
header_number.classList.add('center')
header_database_id.innerHTML = `ID`;
header_name.innerHTML = `Name`;
header_address.innerHTML = `Address`;
header_number.innerHTML = `Phone Number`;
let colorstate = 1;
for (let i = 0; i < vendors.length; i++){
var vendor_row = table.insertRow();
var row_id = vendor_row.insertCell();
row_id.classList.add('center');
var row_name = vendor_row.insertCell();
row_name.classList.add('center');
var row_address = vendor_row.insertCell();
row_address.classList.add('center');
var row_number = vendor_row.insertCell();
row_number.classList.add('center');
row_id.innerHTML = vendors[i][0];
row_name.innerHTML = vendors[i][1];
row_address.innerHTML = vendors[i][2];
row_number.innerHTML = vendors[i][5];
if ((colorstate % 2) == 0){
vendor_row.classList.add('green')
vendor_row.classList.add('lighten-5')
}
vendor_row.classList.add("custom_row")
vendor_row.addEventListener('click', function(){
selectVendor(vendors[i][0])
})
colorstate++
}
}

View File

@ -3,6 +3,9 @@ let end_page;
let limit = 50 let limit = 50
let search_text = "" let search_text = ""
let zones; let zones;
let barcode = ""
let item_name = ""
let logistics_info_id = 0
async function setupZones() { async function setupZones() {
let primary_zone = document.getElementById('zone') let primary_zone = document.getElementById('zone')
@ -22,7 +25,6 @@ async function fetchZones() {
zones = data.zones; zones = data.zones;
}; };
async function fetchLocations(zone) { async function fetchLocations(zone) {
const url = new URL('/getLocations', window.location.origin); const url = new URL('/getLocations', window.location.origin);
url.searchParams.append('zone', zone); url.searchParams.append('zone', zone);
@ -79,6 +81,8 @@ async function fetchItems(){
document.getElementById('forward').classList.remove("disabled") document.getElementById('forward').classList.remove("disabled")
document.getElementById('forward').classList.add("waves-effect") document.getElementById('forward').classList.add("waves-effect")
}; };
// This is to populate the item table!
var table = document.getElementById("item_table") var table = document.getElementById("item_table")
while (table.rows.length > 0) { while (table.rows.length > 0) {
table.deleteRow(0); table.deleteRow(0);
@ -130,17 +134,87 @@ async function fetchItems(){
}) })
} }
async function populateLocations(locations) {
console.log(locations)
var table = document.getElementById("location_table")
while (table.rows.length > 0) {
table.deleteRow(0);
}
const header = table.createTHead();
const row = header.insertRow(0);
var header_database_id = row.insertCell();
header_database_id.classList.add('center')
var header_barcode = row.insertCell();
header_barcode.classList.add('center')
var header_name = row.insertCell();
header_name.classList.add('center')
header_database_id.innerHTML = `Zone`;
header_barcode.innerHTML = `Location`;
header_name.innerHTML = `QOH`;
let colorstate = 1;
for (let key in locations){
console.log(key);
var location_row = table.insertRow();
var row_zone = location_row.insertCell();
row_zone.classList.add('center');
var row_location = location_row.insertCell();
row_location.classList.add('center');
var row_qoh = location_row.insertCell();
row_qoh.classList.add('center');
let r_location = key.split("@");
row_zone.innerHTML = r_location[0];
row_location.innerHTML = r_location[1];
row_qoh.innerHTML = locations[key];
if ((colorstate % 2) == 0){
location_row.classList.add('grey')
location_row.classList.add('lighten-5')
}
location_row.classList.add("custom_row")
location_row.addEventListener('click', function(){
clickRowLocation(r_location)
})
colorstate++
}
}
async function clickRow(database_id){ async function clickRow(database_id){
let item = await fetchItem(database_id); let item = await fetchItem(database_id);
await populateFields(item) await populateFields(item)
await populateLocations(item[18])
let modal = document.getElementById("item_modal")
var instance = M.Modal.getInstance(modal)
instance.close()
};
async function clickRowLocation(location){
console.log(location)
let modal = document.getElementById("locations")
var instance = M.Modal.getInstance(modal)
await setLocation(location[0], location[1])
instance.close()
}; };
async function populateFields(item){ async function populateFields(item){
barcode = item[1]
item_name = item[2]
logistics_info_id = item[8]
document.getElementById("database_id").value = item[0]; document.getElementById("database_id").value = item[0];
document.getElementById("database_id").style = "";
document.getElementById("barcode").value = item[1]; document.getElementById("barcode").value = item[1];
document.getElementById("barcode").style = "";
document.getElementById("name").value = item[2]; document.getElementById("name").value = item[2];
document.getElementById("QOH").value = item[19]; document.getElementById("QOH").value = item[19];
document.getElementById("UOM").value = item[27];
document.getElementById("transaction_cost").value = item[28];
let location = item[16].split('@') let location = item[16].split('@')
await setLocation(location[0], location[1]) await setLocation(location[0], location[1])
@ -148,8 +222,10 @@ async function populateFields(item){
async function setLocation(zone, location){ async function setLocation(zone, location){
document.getElementById('zone').value = zone document.getElementById('zone').value = zone
document.getElementById('zone').style = ""
await loadLocations() await loadLocations()
document.getElementById('location').value = location document.getElementById('location').value = location
document.getElementById('location').style = ""
}; };
async function fetchItem(database_id){ async function fetchItem(database_id){
@ -160,6 +236,87 @@ async function fetchItem(database_id){
return data.item; return data.item;
} }
function validateSubmit(){
var checked = true;
let database_id = document.getElementById('database_id')
let barcode = document.getElementById("barcode")
let zone = document.getElementById('zone')
let loc = document.getElementById('location')
let trans_type = document.getElementById("trans_type")
let qty = document.getElementById('transaction_quantity')
if (database_id.value == ""){
database_id.style = "border-color: red;"
checked = false;
} else {
database_id.style = ""
}
if (barcode.value == ""){
barcode.style = "border-color: red;"
checked = false;
} else {
barcode.style = ""
}
if (trans_type.value == ""){
trans_type.style = "border-color: red;"
checked = false;
} else {
trans_type.style = ""
}
if (parseFloat(qty.value) == 0.0 || Number.isNaN(parseFloat(qty.value))){
qty.style = "border-color: red;"
checked = false;
}
if (zone.value == ""){
zone.style = "border-color: red;"
checked = false;
}
if (loc.value == ""){
loc.style = "border-color: red;"
checked = false;
} else {
loc.style = ""
}
return checked;
}
function addTransaction() {
let zone = document.getElementById('zone').value
let loc = document.getElementById('location').value
let location = `${zone}@${loc}`
let barcode = document.getElementById("barcode").value
let trans_type = document.getElementById("trans_type").value
let trans_cost = parseFloat(document.getElementById("transaction_cost").value)
let qty = parseFloat(document.getElementById('transaction_quantity').value)
var result = validateSubmit();
console.log(result)
if (result === true){
fetch(`/transact`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
logistics_info_id: logistics_info_id,
barcode: barcode,
name: item_name,
location: location,
qty: qty,
trans_type: trans_type,
trans_cost: trans_cost
}),
});
M.toast({text: 'Transaction Complete!'})
document.getElementById('transaction_quantity').value = "";
} else {
M.toast({text: 'Please ensure your receipt is filled out.'})
}
}
document.getElementById('forward').addEventListener('click', async function(){ document.getElementById('forward').addEventListener('click', async function(){
current_page++ current_page++
await fetchItems() await fetchItems()

View File

@ -35,7 +35,6 @@
pointer-events: none; pointer-events: none;
opacity: 0.5; /* or your desired degree of transparency */ opacity: 0.5; /* or your desired degree of transparency */
} }
</style> </style>
<ul id='dropdown1' class='dropdown-content'> <ul id='dropdown1' class='dropdown-content'>
{% for site in sites %} {% for site in sites %}
@ -58,6 +57,7 @@
<li class="active"><a href="/items">Site Items</a></li> <li class="active"><a href="/items">Site Items</a></li>
<li><a href="/groups">Site Groups</a></li> <li><a href="/groups">Site Groups</a></li>
<li><a href="/shopping-lists">Site Shopping Lists</a></li> <li><a href="/shopping-lists">Site Shopping Lists</a></li>
<li><a href="/receipts">Site Receipts</a></li>
</ul> </ul>
<body> <body>
<div class="container section" style="padding-bottom: 72px;"> <div class="container section" style="padding-bottom: 72px;">
@ -92,6 +92,12 @@
<span>Non Zero Items</span> <span>Non Zero Items</span>
</label> </label>
</div> </div>
<div class="col s6 m4 l2">
<label>
<input name="group3" type="radio" onclick="changeView(2)"/>
<span>Hidden Items</span>
</label>
</div>
<div class="col s12 divider"></div> <div class="col s12 divider"></div>
</div> </div>
<!-- This is for sorting values --> <!-- This is for sorting values -->
@ -176,7 +182,6 @@
<li id="current_page" style="padding-top: 7px; padding-left: 5px; padding-right: 5px; font-size: 18px;">page_number</li> <li id="current_page" style="padding-top: 7px; padding-left: 5px; padding-right: 5px; font-size: 18px;">page_number</li>
<li id="forward" class="waves-effect hand-pointer"><a class="green lighten-3" style="display: flex; align-items: center; justify-content: center; width: 40px; height: 40px; border-radius: 10px;"><i class="material-icons">chevron_right</i></a></li> <li id="forward" class="waves-effect hand-pointer"><a class="green lighten-3" style="display: flex; align-items: center; justify-content: center; width: 40px; height: 40px; border-radius: 10px;"><i class="material-icons">chevron_right</i></a></li>
<li id="last" class="waves-effect hand-pointer"><a class="green lighten-3" style="display: flex; align-items: center; justify-content: center; width: 40px; height: 40px; border-radius: 10px;"><i class="material-icons">last_page</i></a></li> <li id="last" class="waves-effect hand-pointer"><a class="green lighten-3" style="display: flex; align-items: center; justify-content: center; width: 40px; height: 40px; border-radius: 10px;"><i class="material-icons">last_page</i></a></li>
</ul> </ul>
</div> </div>
</div> </div>
@ -322,8 +327,6 @@
url.searchParams.append('sort_order', sort_order); url.searchParams.append('sort_order', sort_order);
url.searchParams.append('view', view); url.searchParams.append('view', view);
await fetch(url) await fetch(url)
.then(response => response.json()) .then(response => response.json())
.then(data => { .then(data => {

View File

@ -30,6 +30,10 @@
pointer-events: none; pointer-events: none;
opacity: 0.5; /* or your desired degree of transparency */ opacity: 0.5; /* or your desired degree of transparency */
} }
.custom_row:hover{
background-color: rgb(230, 230, 230) !important;
cursor: pointer;
}
</style> </style>
<ul id='dropdown1' class='dropdown-content'> <ul id='dropdown1' class='dropdown-content'>
{% for site in sites %} {% for site in sites %}
@ -148,7 +152,7 @@
<li class="tab col s3"><a class="active" href="#item_info">Item Info</a></li> <li class="tab col s3"><a class="active" href="#item_info">Item Info</a></li>
<li class="tab col s3"><a href="#food_info">Food Info</a></li> <li class="tab col s3"><a href="#food_info">Food Info</a></li>
<li class="tab col s3"><a href="#logistics_info">Logistics Info</a></li> <li class="tab col s3"><a href="#logistics_info">Logistics Info</a></li>
<li class="tab col s3 disabled"><a href="#linked_items">Linked Items</a></li> <li id="linked_items_tab" class="tab col s3 disabled"><a href="#linked_items">Linked Items</a></li>
</ul> </ul>
</div> </div>
<div id="item_info" class="col s12 grey lighten-5 p-3"> <div id="item_info" class="col s12 grey lighten-5 p-3">
@ -441,7 +445,37 @@
</div> </div>
</div> </div>
</div> </div>
<div id="linked_items" class="col s12 disabled">Linked Items</div> <div id="linked_items" class="col s12">
<div class="row" style="gap: 10px; padding-top: 10px;">
<div class="col s12 green lighten-4" style="border-radius: 10px;">
<h5 class="center">Linked Items</h5>
</div>
<div class="col s12">
<div class="card-panel green z-depth-0 lighten-5">
<span class="black-text">If an item is a linked item then it possesses other barcodes under its umbrella. The system will
use this to ensure that as things are coming in an dout of the system at the right barcode. A good example is if you want
to track cans of pop and not cases. You would link the case to the can barcode so when the case is received it will been
adjusted in at the can barcode.
</span>
</div>
</div>
<div class="col s12">
<button class="btn btn-flat green lighten-4" onclick="openLinkedItems()">Add Item</button>
</div>
<div class="col s12">
<table class="" id="linked_items_table">
<thead>
<tr>
<th>Linked Item</th>
<th></th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
</div>
</div>
</div> </div>
</div> </div>
</div> </div>
@ -476,7 +510,41 @@
<button onclick="addLink()" class="waves-effect green lighten-4 btn-flat">Add</button> <button onclick="addLink()" class="waves-effect green lighten-4 btn-flat">Add</button>
</div> </div>
</div> </div>
<!-- Item Selection Modal -->
<div id="item_modal" class="modal">
<div class="modal-content">
<div class="row" style="gap: 5px;">
<div class="col s12">
<h4>Add Items...</h4>
</div>
<div class="col s12">
<div class="card-panel grey lighten-4 z-depth-0">
<span class="black-text"> Here is where you can search, add, and remove items from this group by checking or unchecking the items below. You can select
multiple at a time or simply one. Utilize the search bar to filter down quickly to items you need or simply scroll to your hearts content.
<br><br>
<b>WARNING:</b> clicking the checkbox will not save the changes right off the bat! You <b>MUST</b> click the Update Items button!
<br><br>
</span>
</div>
</div>
<div class="col s9 m6 offset-m3 input-field outlined align-center">
<i class="material-icons prefix">search</i>
<input style="border-radius: 20px; border: 1px solid #ccc;" id="search" name="search" type="search" placeholder="Search Items" value="">
</div>
<div class="col s12 center">
<a id="item_back" class="btn icon-left purple lighten-4 black-text z-depth-0"><i class="material-icons">chevron_left</i></a>
<a id="current_page_item" class="" style="color: black;">0/0</a>
<a id="item_forward" class="btn icon-right purple lighten-4 black-text z-depth-0"><i class="material-icons">chevron_right</i></a>
</div>
<div class="divider col s12"></div>
<div id="table-container" class="col s12">
<table id="item_table">
</table>
</div>
</div>
</div>
</div>
<div class="fixed-action-btn show-on-med-and-up hide-on-small-only"> <div class="fixed-action-btn show-on-med-and-up hide-on-small-only">
<button class="btn-floating btn-large" onclick="saveItem()"> <button class="btn-floating btn-large" onclick="saveItem()">
<i class="large material-icons">save</i> <i class="large material-icons">save</i>
@ -488,6 +556,7 @@
const item_id = {{id|tojson}} const item_id = {{id|tojson}}
let item; let item;
let linked_items;
var reference_state = 1 var reference_state = 1
let zones; let zones;
let primary_locations; let primary_locations;
@ -557,8 +626,7 @@
await propagateInfo() await propagateInfo()
await propagateLinks() await propagateLinks()
await populateLocations() await populateLocations()
console.log(updated) await propagateLinkedItems()
}); });
async function propagateInfo(){ async function propagateInfo(){
@ -595,6 +663,9 @@
nutrients_changed = false; nutrients_changed = false;
links_changed = false; links_changed = false;
if (item[10] == "linked"){
document.getElementById('linked_items_tab').classList.remove('disabled')
}
M.toast({html: "Item has been loaded successfully!", classes: "rounded green lighten-4 black-text flow-text"}); M.toast({html: "Item has been loaded successfully!", classes: "rounded green lighten-4 black-text flow-text"});
}; };
@ -716,6 +787,26 @@
document.getElementById('fibers_unit').value = nutrients['fibers_unit'] document.getElementById('fibers_unit').value = nutrients['fibers_unit']
}; };
async function propagateLinkedItems(){
var table = document.getElementById("linked_items_table")
let colorstate = 1
for (let i = 0; i < linked_items.length; i++){
var row = table.insertRow();
var row_item = row.insertCell();
var row_buttons = row.insertCell();
row_item.innerHTML = `${linked_items[i][1]} - ${linked_items[i][2]}`
row_buttons.innerHTML = `<a class="btn btn-flat green lighten-4 right" href="/itemlink/${linked_items[i][0]}" style="margin-left: 5px;">Edit</a><button class="btn btn-flat red lighten-4 right" onclick="deleteLink(${linked_items[i][0]})">Delete</button>`
if ((colorstate % 2) == 0){
row.classList.add('green')
row.classList.add('lighten-5')
}
colorstate++
};
}
function populateReferences(references, reference_type){ function populateReferences(references, reference_type){
var table = document.getElementById("reference_table") var table = document.getElementById("reference_table")
for (let i = 0; i < references.length; i++){ for (let i = 0; i < references.length; i++){
@ -734,6 +825,124 @@
}; };
}; };
var current_page_item = 1
var endpage_item = 10
var limit_item = 50
var search_text_item = ""
async function fetchItems(){
if (current_page_item === 1){
document.getElementById('item_back').classList.add("disabled")
document.getElementById('item_back').classList.remove("waves-effect")
} else {
document.getElementById('item_back').classList.remove("disabled")
document.getElementById('item_back').classList.add("waves-effect")
};
const url = new URL('/getItems', window.location.origin);
url.searchParams.append('page', current_page_item);
url.searchParams.append('limit', limit_item);
await fetch(url)
.then(response => response.json())
.then(data => {
console.log(data)
endpage_item = parseInt(data.end)
if (current_page_item === endpage_item){
document.getElementById('item_forward').classList.add("disabled")
document.getElementById('item_forward').classList.remove("waves-effect")
} else {
document.getElementById('item_forward').classList.remove("disabled")
document.getElementById('item_forward').classList.add("waves-effect")
};
// This is to populate the item table!
var table = document.getElementById("item_table")
while (table.rows.length > 0) {
table.deleteRow(0);
}
const header = table.createTHead();
const row = header.insertRow(0);
var header_database_id = row.insertCell();
header_database_id.classList.add('center')
var header_barcode = row.insertCell();
header_barcode.classList.add('center')
var header_name = row.insertCell();
header_name.classList.add('center')
header_name.classList.add('hide-on-med-and-down')
header_database_id.innerHTML = `<b>Database ID</b>`;
header_barcode.innerHTML = `<b>Barcode</b>`;
header_name.innerHTML = `<b>Product Name</b>`;
let colorstate = 1;
data.items.forEach(item => {
var row = table.insertRow();
var row_id = row.insertCell();
row_id.classList.add('center')
var row_barcode = row.insertCell();
row_barcode.classList.add('center')
var row_name = row.insertCell();
row_name.classList.add('hide-on-med-and-down')
row_name.classList.add('center')
row_id.innerHTML = item[0];
row_barcode.innerHTML = item[1];
row_name.innerHTML = item[2];
if ((colorstate % 2) == 0){
row.classList.add('grey')
row.classList.add('lighten-5')
}
row.classList.add("custom_row")
row.addEventListener('click', function(){
clickRowItem(item[0])
})
colorstate++
});
})
document.getElementById("current_page_item").innerHTML = `${String(current_page_item)} / ${String(endpage_item)}`
}
async function openLinkedItems(){
var elem = document.getElementById('item_modal')
var instance = M.Modal.init(elem)
await fetchItems()
instance.open()
};
async function clickRowItem(database_id){
let linkitem = await fetchLinkedItem(database_id);
console.log(item)
const url = new URL('/linkItem', window.location.origin);
await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
master_index: item[0],
sub_index: linkitem[0]
})
})
var elem = document.getElementById('item_modal')
var instance = M.Modal.init(elem)
instance.close()
};
async function fetchLinkedItem(database_id){
const url = new URL('/getItem', window.location.origin);
url.searchParams.append('id', database_id);
const response = await fetch(url);
data = await response.json();
return data.item;
}
async function populateLocations(){ async function populateLocations(){
var table = document.getElementById("locations_table") var table = document.getElementById("locations_table")
console.log(item[18]) console.log(item[18])
@ -768,5 +977,19 @@
}; };
}; };
document.getElementById('item_forward').addEventListener('click', async function(){
current_page++
await fetchItems()
})
document.getElementById('item_back').addEventListener('click', async function(){
current_page--
await fetchItems()
})
async function deleteLink(index){
console.log(index)
}
</script> </script>
</html> </html>

View File

@ -0,0 +1,70 @@
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0" charset="utf-8" />
<title>My Pantry</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@materializecss/materialize@2.0.3-alpha/dist/css/materialize.min.css" />
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet" />
<script src="https://cdn.jsdelivr.net/npm/@materializecss/materialize@2.0.3-alpha/dist/js/materialize.min.js"></script>
</head>
<body class="grey lighten-5">
<div class="container col s12">
<div class="section">
<div id="editLinkedItem" class="row" style="gap: 1em;">
<div class="col s12">
<a href='{{ proto['referrer'] }}' class="left btn green lighten-4 black-text btn-flat"><i class="material-icons">arrow_back</i></a>
</div>
<div class="s12 m6 input-field">
<input id="barcode" type="text" name="barcode" value="" placeholder=" " maxlength="20" disabled />
<label for="barcode">Barcode</label>
<span class="supporting-text">Item Barcode</span>
</div>
<div class="s12 m6 input-field">
<input id="LinkID" type="text" name="LinkID" placeholder=" " value="" maxlength="20" disabled />
<label for="LinkID">LinkID</label>
<span class="supporting-text">Pantry Item this is linked to.</span>
</div>
<div class="s12 m6 input-field">
<input id="conversion" name="conversion" type="text" placeholder=" " value="" maxlength="20" />
<label for="conversion">Conversion Factor</label>
<span class="supporting-text">Conversion Factor for scanning adjustments.</span>
</div>
<div class="input-field col s12">
<textarea id="itemdata" class="materialize-textarea" name="itemdata" placeholder=" "></textarea>
<label for="itemdata">Saved Data</label>
</div>
<div class="divider s12"></div>
<div class="col s12">
<button class="btn icon-right waves-effect waves-light right" type="submit" name="action">Update</button>
</div>
</div>
</div>
</div>
</body>
<script>
const id = {{id|tojson}}
let linked_item;
document.addEventListener('DOMContentLoaded', async function() {
await fetchLinkedItem()
await populateForm()
});
async function fetchLinkedItem(){
const url = new URL('/getLinkedItem', window.location.origin);
url.searchParams.append('id', id);
const response = await fetch(url);
data = await response.json();
linked_item = data.linked_item;
}
async function populateForm(){
document.getElementById('barcode').value = linked_item[1];
document.getElementById('LinkID').value = linked_item[2];
document.getElementById('conversion').value = linked_item[4];
document.getElementById('itemdata').value = JSON.stringify(linked_item[3]);
M.Forms.textareaAutoResize(document.querySelector('#itemdata'));
}
</script>
</html>

View File

@ -0,0 +1,184 @@
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0" charset="utf-8" />
<title>My Pantry - Receipts</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@materializecss/materialize@2.0.3-alpha/dist/css/materialize.min.css" />
<!-- 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 href="https://fonts.googleapis.com/css2?family=Material+Symbols+Sharp" rel="stylesheet" />
<script src="https://cdn.jsdelivr.net/npm/@materializecss/materialize@2.0.3-alpha/dist/js/materialize.min.js"></script>
</head>
<style>
.hand-pointer {
cursor: pointer;
}
[type="radio"]:checked + span:after {
border: 2px solid rgb(0 128 0 / 30%); /* Outline color */
background-color: rgb(0 128 0 / 30%); /* Fill color */
}
header, main, footer, body {
padding-left: 300px;
}
@media only screen and (max-width : 992px) {
header, main, footer, body {
padding-left: 0;
}
}
.dropdown-disabled {
pointer-events: none;
opacity: 0.5; /* or your desired degree of transparency */
}
</style>
<ul id='dropdown1' class='dropdown-content'>
{% for site in sites %}
<li><button class="btn transparent black-text z-depth-0" onclick="changeSite('{{ site }}')">{{site}}</button></li>
{% endfor %}
</ul>
<ul id="slide-out" class="sidenav sidenav-fixed green lighten-4" style="width: 250px;">
<li>
<div class="user-view">
<!-- <div class="background">
<img src="images/office.jpg">
</div> -->
<!-- <a href="#user"><img class="circle" src="images/yuna.jpg"></a> -->
<a href="#name"><span class="black-text name">John Doe</span></a>
<a href="#email"><span class="black-text email">jdoe@example.com</span></a>
</div>
</li>
<li><a class="dropdown-trigger" data-target="dropdown1">Current Site > {{current_site}}<i class="material-icons right">arrow_drop_down</i></a></li>
<li><div class="divider grey darken-1" style="margin-left: 5px; margin-right: 10px;"></div></li>
<li class="active"><a href="/items">Site Items</a></li>
<li><a href="/groups">Site Groups</a></li>
<li><a href="/shopping-lists">Site Shopping Lists</a></li>
<li><a href="/receipts">Site Receipts</a></li>
</ul>
<body>
<div class="container section">
<div class="row">
<div class="col s12 collection" id="collection">
</div>
<div class="col s12 center" id="pagination_list">
<ul class="pagination">
<li id="first" class="waves-effect hand-pointer"><a class="green lighten-3" style="display: flex; align-items: center; justify-content: center; width: 40px; height: 40px; border-radius: 10px;"><i class="material-icons">first_page</i></a></li>
<li id="back" class="waves-effect hand-pointer" ><a class="green lighten-3" style="display: flex; align-items: center; justify-content: center; width: 40px; height: 40px; border-radius: 10px;"><i class="material-icons">chevron_left</i></a></li>
<li id="current_page" style="padding-top: 7px; padding-left: 5px; padding-right: 5px; font-size: 18px;">page_number</li>
<li id="forward" class="waves-effect hand-pointer"><a class="green lighten-3" style="display: flex; align-items: center; justify-content: center; width: 40px; height: 40px; border-radius: 10px;"><i class="material-icons">chevron_right</i></a></li>
<li id="last" class="waves-effect hand-pointer"><a class="green lighten-3" style="display: flex; align-items: center; justify-content: center; width: 40px; height: 40px; border-radius: 10px;"><i class="material-icons">last_page</i></a></li>
</ul>
</div>
</div>
</div>
</body>
<script>
let current_page = 1
let end_page = 10
let limit = 50
let site = {{ current_site|tojson }}
document.addEventListener('DOMContentLoaded', async function() {
await updateList()
var elems = document.querySelectorAll('.dropdown-trigger');
var instances = M.Dropdown.init(elems, {
alignment: 'right',
});
});
async function changeSite(new_site){
console.log(`current_site: ${site}`)
console.log(`new site: ${new_site}`)
site = new_site
console.log(`current_site: ${site}`)
const url = new URL('/changeSite', window.location.origin);
url.searchParams.append('site', site)
await fetch(url)
location.reload();
}
async function updateList(){
if (current_page === 1){
document.getElementById('back').classList.add("disabled")
document.getElementById('back').classList.remove("waves-effect")
document.getElementById('first').classList.add("disabled")
document.getElementById('first').classList.remove("waves-effect")
} else {
document.getElementById('back').classList.remove("disabled")
document.getElementById('back').classList.add("waves-effect")
document.getElementById('first').classList.remove("disabled")
document.getElementById('first').classList.add("waves-effect")
};
const url = new URL('/getReceipts', window.location.origin);
url.searchParams.append('page', current_page);
url.searchParams.append('limit', limit);
await fetch(url)
.then(response => response.json())
.then(data => {
end_page = parseInt(data.end)
if (current_page === end_page){
document.getElementById('forward').classList.add("disabled")
document.getElementById('forward').classList.remove("waves-effect")
document.getElementById('last').classList.add("disabled")
document.getElementById('last').classList.remove("waves-effect")
} else {
document.getElementById('forward').classList.remove("disabled")
document.getElementById('forward').classList.add("waves-effect")
document.getElementById('last').classList.remove("disabled")
document.getElementById('last').classList.add("waves-effect")
};
const collection = document.getElementById("collection")
const dummy = document.createElement('div')
data.receipts.forEach(receipt => {
var item = document.createElement("a")
if (receipt[2] == "Unresolved"){
item.innerHTML = `<span class="badge red white-text" style="border-radius: 10px;">${receipt[2]}</span>${receipt[1]}`
} else if (receipt[2] == "Resolved"){
item.innerHTML = `<span class="badge green white-text" style="border-radius: 10px;">${receipt[2]}</span>${receipt[1]}`
}
item.classList.add("collection-item")
item.href = `/receipt/${receipt[0]}`
dummy.appendChild(item)
});
collection.innerHTML = dummy.innerHTML
document.getElementById("current_page").innerHTML = `${String(current_page)} / ${String(end_page)}`
});
}
document.getElementById('forward').addEventListener('click', async () =>{
if (!(document.getElementById("forward").classList.contains("disabled"))){
current_page++
await update_list();
};
});
document.getElementById('back').addEventListener('click', async () =>{
if (!(document.getElementById("back").classList.contains("disabled"))){
current_page--
await update_list();
}
});
document.getElementById('last').addEventListener('click', async () =>{
if(!(document.getElementById("last").classList.contains("disabled"))){
current_page = end_page
await update_list();
};
});
document.getElementById('first').addEventListener('click', async () =>{
if (!(document.getElementById("first").classList.contains("disabled"))){
current_page = 1
await update_list();
};
});
</script>
</html>

View File

@ -0,0 +1,217 @@
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0" charset="utf-8" />
<title>My Pantry - Items</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@materializecss/materialize@2.0.3-alpha/dist/css/materialize.min.css" />
<!-- 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 href="https://fonts.googleapis.com/css2?family=Material+Symbols+Sharp" rel="stylesheet" />
<script src="https://cdn.jsdelivr.net/npm/@materializecss/materialize@2.0.3-alpha/dist/js/materialize.min.js"></script>
</head>
<style>
.hand-pointer {
cursor: pointer;
}
[type="radio"]:checked + span:after {
border: 2px solid rgb(0 128 0 / 30%); /* Outline color */
background-color: rgb(0 128 0 / 30%); /* Fill color */
}
header, main, footer, body {
padding-left: 300px;
}
@media only screen and (max-width : 992px) {
header, main, footer, body {
padding-left: 0;
}
}
.dropdown-disabled {
pointer-events: none;
opacity: 0.5; /* or your desired degree of transparency */
}
.disabled-row {
opacity: 0.5;
pointer-events: none;
}
.custom_row:hover{
background-color: rgb(230, 230, 230) !important;
cursor: pointer;
}
</style>
<ul id='dropdown1' class='dropdown-content'>
{% for site in sites %}
<li><button class="btn transparent black-text z-depth-0" onclick="changeSite('{{ site }}')">{{site}}</button></li>
{% endfor %}
</ul>
<ul id="slide-out" class="sidenav sidenav-fixed green lighten-4" style="width: 250px;">
<li>
<div class="user-view">
<!-- <div class="background">
<img src="images/office.jpg">
</div> -->
<!-- <a href="#user"><img class="circle" src="images/yuna.jpg"></a> -->
<a href="#name"><span class="black-text name">John Doe</span></a>
<a href="#email"><span class="black-text email">jdoe@example.com</span></a>
</div>
</li>
<li><a class="dropdown-trigger" data-target="dropdown1">Current Site > {{current_site}}<i class="material-icons right">arrow_drop_down</i></a></li>
<li><div class="divider grey darken-1" style="margin-left: 5px; margin-right: 10px;"></div></li>
<li class="active"><a href="/items">Site Items</a></li>
<li><a href="/groups">Site Groups</a></li>
<li><a href="/shopping-lists">Site Shopping Lists</a></li>
<li><a href="/receipts">Site Receipts</a></li>
</ul>
<body>
<div class="container section">
<div class="row">
<div class="col s12">
<h3 id="receipt_id"></h3>
</div>
<div class="col s6">
<h5 id="database_id"></h5>
</div>
<div class="col s6 right">
<h5 class="right" id="status"></h5>
</div>
<div class="col s12">
<p id="created"></p>
</div>
<!-- Vendor info starts here -->
<div class="col s12 green lighten-4" style="border-radius: 10px;">
<h5 class="center">Vendor</h5>
</div>
<div class="col s12 divider"></div>
<div class="row col s12" style="gap: 15px; margin-top: 10px;">
<div class="col s12 m8 input-field outlined">
<input id="vendor_name" type="text" placeholder=" " disabled>
<label for="vendor_name">Name</label>
</div>
<div class="col s12 m4 l3">
<button class="btn btn-flat green lighten-4" onclick="populateVendors()" style="display: flex; align-items: center;"><i class="material-symbols-outlined" style="padding-right: 10px;">event_list</i>Vendors</button>
</div>
<div class="col s12 input-field outlined">
<input id="vendor_number" type="text" placeholder=" " disabled>
<label for="vendor_number">Phone Number</label>
</div>
<div class="col s12 input-field">
<input id="vendor_address" class="materialize-textarea" placeholder=" " disabled>
<label for="vendor_address">Address</label>
</div>
</div>
<!-- Receipt Items start here -->
<div class="col s12 green lighten-4" style="border-radius: 10px; margin-top: 10px;">
<h5 class="center">Receipt Items</h5>
</div>
<div class="col s12">
<div class="card-panel green z-depth-0 lighten-5">
<span class="black-text">Below are the line items on this receipt. By clicking on the row you can modify, save, delete, void, resolve the lines.
</span>
</div>
</div>
<div class="col s12 divider"></div>
<div class="col s12">
<table class="" id="item_table">
<thead>
<tr>
<th>type</th>
<th>barcode</th>
<th>name</th>
<th>qty</th>
<th>cost</th>
<th>Status</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
</div>
<!-- Modal code for modifying an item -->
<div id="modify_item" class="modal">
<div class="modal-content">
<div class="row" style="gap: 15px;">
<div class="col s12 green lighten-4">
<h5 class="center" style="border-radius: 10px;">Modify Item</h5>
</div>
<div class="row col s12" style="gap: 15px;">
<div class="s12 m6 input-field outlined">
<input id="item_barcode" type="text" placeholder=" ">
<label for="item_barcode">Line Barcode</label>
<span class="supporting-text">Barcode must be in the system surrounded by %%s</span>
</div>
<div class="s12 m6 input-field outlined">
<input id="item_database_id" type="text" placeholder=" " disabled>
<label for="item_database_id">Database ID</label>
<!--<span class="supporting-text">Supporting Text</span>-->
</div>
<div class="s12 m6 input-field outlined">
<input id="item_type" type="text" placeholder=" " disabled>
<label for="item_type">Line Type</label>
<!--<span class="supporting-text">Supporting Text</span>-->
</div>
<div class="s12 m6 input-field outlined">
<input id="item_name" type="text" placeholder=" " disabled>
<label for="item_name">Line Name</label>
<!--<span class="supporting-text">Supporting Text</span>-->
</div>
<div class="s12 m6 input-field outlined">
<input id="item_qty" type="text" placeholder=" ">
<label for="item_qty">Line Quantity</label>
<!--<span class="supporting-text">Supporting Text</span>-->
</div>
<div class="s12 m6 input-field outlined">
<input id="item_cost" type="text" placeholder=" ">
<label for="item_cost">Line Cost</label>
<!--<span class="supporting-text">Supporting Text</span>-->
</div>
<div class="col s12 divider"></div>
<div class="row col s12" style="gap: 5px;">
<button onclick="saveItem()" class="waves-effect btn-flat right blue white-text tooltipped" data-position="bottom" data-tooltip="Save Changes to Line Item">
<i class="material-icons">save</i></button>
<button onclick="voidItem()" class="waves-effect btn-flat right black white-text tooltipped" data-position="bottom" data-tooltip="Void Line Item">
<i class="material-icons">cancel</i></button>
<button onclick="deleteItem()" class="waves-effect btn-flat right red white-text tooltipped" data-position="bottom" data-tooltip="Delete Line Item">
<i class="material-icons">delete</i></button>
<button onclick="resolveItem()" class="waves-effect btn-flat right green white-text tooltipped" data-position="bottom" data-tooltip="Resolve Line Item">
<i class="material-icons">check</i></button>
</div>
</div>
</div>
</div>
</div>
<!-- Modal code for selecting a vendor -->
<div id="vendors" class="modal">
<div class="modal-content">
<div class="row" style="gap: 5px;">
<div class="col s12">
<h4>Vendors</h4>
</div>
<div class="col s12">
<div class="card-panel grey lighten-4 z-depth-0">
<span class="black-text">Listed below is all the Vendors avaiable to select for this receipt. </span>
</div>
</div>
<div class="divider col s12"></div>
<div id="vendors-table-container" class="col s12">
<table id="vendors_table">
</table>
</div>
</div>
</div>
</div>
</div>
</body>
<script src="{{ url_for('static', filename='receiptHandler.js') }}"></script>
<script>
const receipt_id = {{id|tojson}}
</script>
</html>

View File

@ -13,8 +13,8 @@
<!-- Material Symbols - Sharp Set --> <!-- Material Symbols - Sharp Set -->
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Sharp" rel="stylesheet" /> <link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Sharp" rel="stylesheet" />
<script src="https://cdn.jsdelivr.net/npm/@materializecss/materialize@2.0.3-alpha/dist/js/materialize.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/@materializecss/materialize@2.0.3-alpha/dist/js/materialize.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/js/materialize.min.js"></script> <!-- <script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/js/materialize.min.js"></script>
-->
</head> </head>
<style> <style>
.custom_row:hover{ .custom_row:hover{
@ -49,36 +49,54 @@
<input id="name" type="text" placeholder="" disabled> <input id="name" type="text" placeholder="" disabled>
<label for="name">Item Name</label> <label for="name">Item Name</label>
</div> </div>
<div class="col s7 m4 input-field outlined m-2"> <div class="col s7 m4 l2 input-field outlined m-2">
<input id="QOH" type="text" placeholder="" disabled> <input id="QOH" type="text" placeholder="" disabled>
<label for="QOH">Quantity on Hand</label> <label for="QOH">Quantity on Hand</label>
</div> </div>
<div class="col s2" style="margin-top: 15px;"> <div class="col s5 m2 l2 input-field outlined m-2">
<a class="btn btn-flat green lighten-4 modal-trigger" href="#locations" style="display: flex; align-items: center; width: 180px;"><i class="material-symbols-outlined" style="padding-right: 10px;">event_list</i>Item Locations</a> <input id="UOM" type="text" placeholder="" disabled>
<label for="UOM">UOM</label>
</div> </div>
<div class="col s12 divider"></div> <div class="col s12 m6 l4 right">
<div class="col s12 m6 m-2">
<label for="trans_type">Transaction Type</label> <label for="trans_type">Transaction Type</label>
<select id="trans_type" class="browser-default"> <select id="trans_type" class="browser-default">
<option value="" disabled selected>Choose your option</option> <option value="" disabled selected>Choose your option</option>
<option value="In">Adjust In</option> <option value="Adjust In">Adjust In</option>
<option value="Out">Adjust Out</option> <option value="Adjust Out">Adjust Out</option>
</select> </select>
</div> </div>
<div class="col s6 m3 m-2"> <div class="col s12 divider hide-on-small-only"></div>
<label onchange="loadLocations()" for="zone">Zone</label> <div class="col s12 m3 m-2">
<select id="zone" class="browser-default"> <label for="zone">Zone</label>
<select onchange="loadLocations()" id="zone" class="browser-default">
<option value="" disabled selected>Choose your option</option> <option value="" disabled selected>Choose your option</option>
</select> </select>
</div> </div>
<div class="col s6 m3 m-2"> <div class="col s12 m3 m-2">
<label for="location">Location</label> <label for="location">Location</label>
<select id="location" class="browser-default"> <select id="location" class="browser-default">
<option value="" disabled selected>Choose your option</option> <option value="" disabled selected>Choose your option</option>
</select> </select>
</div> </div>
<div class="col s12 m4 l3" style="padding-top: 25px;">
<a class="btn btn-flat green lighten-4 modal-trigger" href="#locations" style="display: flex; align-items: center;"><i class="material-symbols-outlined" style="padding-right: 10px;">event_list</i>Item Locations</a>
</div>
<div class="col s12 divider hide-on-small-only"></div>
<div class="col s12 m4 l2 input-field outlined m-2">
<input id="transaction_quantity" type="text" placeholder=" " maxlength="20">
<label for="transaction_quantity">Quantity</label>
</div>
<div class="col s12 m4 l2 input-field outlined m-2">
<input id="transaction_cost" type="text" placeholder=" " maxlength="20">
<label for="transaction_cost">SKU cost</label>
</div>
<div class="col s12 divider"></div>
<div class="col s12" style="padding-top: 10px;">
<button class="btn btn-flat right green lighten-4 black-text" onclick="addTransaction()">Submit</button>
</div> </div>
</div> </div>
</div>
<!-- Item Selection Modal -->
<div id="item_modal" class="modal"> <div id="item_modal" class="modal">
<div class="modal-content"> <div class="modal-content">
<div class="row" style="gap: 5px;"> <div class="row" style="gap: 5px;">
@ -113,16 +131,53 @@
</div> </div>
</div> </div>
</div> </div>
<!-- Transaction Modal -->
<div id="locations" class="modal">
<div class="modal-content">
<div class="row" style="gap: 5px;">
<div class="col s12">
<h4>Item Locations</h4>
</div>
<div class="col s12">
<div class="card-panel grey lighten-4 z-depth-0">
<span class="black-text">Listed below is all the locations this item has ever existed in and the current QOH for each location. </span>
</div>
</div>
<div class="divider col s12"></div>
<div id="location-table-container" class="col s12">
<table id="location_table">
</table>
</div>
</div>
</div>
</div>
</body> </body>
<script src="{{ url_for('static', filename='transactionHandler.js') }}"></script> <script src="{{ url_for('static', filename='transactionHandler.js') }}"></script>
<script> <script>
document.addEventListener('DOMContentLoaded', async function() { document.addEventListener('DOMContentLoaded', async function() {
await fetchZones() await fetchZones()
await setupZones() await setupZones()
await fetchItems()
var elems = document.querySelectorAll('.modal'); var elems = document.querySelectorAll('.modal');
var instances = M.Modal.init(elems, { var instances = M.Modal.init(elems, {
// specify options here // specify options here
}); });
document.getElementById("database_id").addEventListener('keydown', async function(event){
if (event.key === "Enter") {
await clickRow(Number(event.target.value))
}
}); });
document.getElementById("trans_type").addEventListener('change', async function(){
this.style = "";
});
document.getElementById("transaction_quantity").addEventListener('change', async function(){
this.style = "";
});
});
</script> </script>

Some files were not shown because too many files have changed in this diff Show More