diff --git a/__pycache__/api.cpython-312.pyc b/__pycache__/api.cpython-312.pyc index f6dbc35..cc6fb28 100644 Binary files a/__pycache__/api.cpython-312.pyc and b/__pycache__/api.cpython-312.pyc differ diff --git a/__pycache__/config.cpython-312.pyc b/__pycache__/config.cpython-312.pyc index 10a5f90..034c139 100644 Binary files a/__pycache__/config.cpython-312.pyc and b/__pycache__/config.cpython-312.pyc differ diff --git a/__pycache__/external_devices.cpython-312.pyc b/__pycache__/external_devices.cpython-312.pyc new file mode 100644 index 0000000..2788adf Binary files /dev/null and b/__pycache__/external_devices.cpython-312.pyc differ diff --git a/__pycache__/main.cpython-312.pyc b/__pycache__/main.cpython-312.pyc index 37b8f60..071609e 100644 Binary files a/__pycache__/main.cpython-312.pyc and b/__pycache__/main.cpython-312.pyc differ diff --git a/api.py b/api.py index 2b55a8d..3ae5527 100644 --- a/api.py +++ b/api.py @@ -1,6 +1,7 @@ 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 main import unfoldCostLayers database_api= Blueprint('database_api', __name__) @@ -126,6 +127,32 @@ def paginate_groups(): 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") def pagninate_items(): page = int(request.args.get('page', 1)) @@ -184,6 +211,22 @@ def pagninate_transactions(): 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") def get_transaction(): id = int(request.args.get('id', 1)) @@ -200,7 +243,6 @@ def get_transaction(): except (Exception, psycopg2.DatabaseError) as error: print(error) - print(transaction) return jsonify(transaction=transaction) @database_api.route("/getLocations") @@ -222,7 +264,6 @@ def get_locations(): except (Exception, psycopg2.DatabaseError) as error: print(error) - print(locations) return jsonify(locations=locations) @database_api.route("/getZones") @@ -241,13 +282,243 @@ def get_zones(): print(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") def get_item(): id = int(request.args.get('id', 1)) database_config = config() site_name = session['selected_site'] - sites = sites_config() - item = [] 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];" cur.execute(SQL_shopping_lists, (item[0], )) 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: print(error) - return jsonify(item=item) + return jsonify(item=item, linked_items=linked_items) @database_api.route("/addItem") def addItem(): @@ -303,6 +588,8 @@ def addItem(): 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 *;" + sqlthree = f"INSERT INTO {site_name}_item_locations(part_id, location_id, quantity_on_hand, cost_layers) VALUES (%s, %s, %s, %s);" + row = None try: with conn.cursor() as cur: @@ -310,6 +597,9 @@ def addItem(): rows = cur.fetchone() if 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: print(error) conn.rollback() @@ -341,20 +631,82 @@ def addItem(): payload=payload, location=location, logistics_info_id=logistics_info_id, - barcode=barcode, - qty=0.0) + item_id=row[0], + qty=0.0, + cost=0.0) except (Exception, psycopg2.DatabaseError) as error: print(error) conn.rollback() return jsonify({'state': str(error)}) - - - - 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']) def updateItem(): def transformValues(values): @@ -443,8 +795,8 @@ def updateItem(): save_data[f"{k}_old"] = v; 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};") - barcode, name, primary_location = cur.fetchone() + 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};") + item_id, barcode, name, primary_location = cur.fetchone() payload = [ datetime.datetime.now(), logistics_info_id, @@ -463,8 +815,9 @@ def updateItem(): payload=payload, location=primary_location, logistics_info_id=logistics_info_id, - barcode=barcode, - qty=0.0 + item_id=item_id, + qty=0.0, + cost=0.0 ) except (Exception, psycopg2.DatabaseError) as error: @@ -475,6 +828,73 @@ def updateItem(): 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") def addGroup(): name = str(request.args.get('name', "")) @@ -641,7 +1061,7 @@ def paginate_lists(): list_length = len(custom_items) 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], )) list_length += cur.fetchone()[0] else: diff --git a/database.ini b/database.ini index e7ad19d..4967f8a 100644 --- a/database.ini +++ b/database.ini @@ -6,5 +6,5 @@ password = test port = 5432 [manage] -sites = test,test2,main +sites = ,test,main,Backpack diff --git a/external_devices.py b/external_devices.py new file mode 100644 index 0000000..635b484 --- /dev/null +++ b/external_devices.py @@ -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//") +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//") +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//") +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/") +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 + + 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!"}) diff --git a/main.py b/main.py index 2a3de0d..b7012c5 100644 --- a/main.py +++ b/main.py @@ -1,11 +1,15 @@ #!/usr/bin/python import psycopg2 from config import config -import json, datetime, copy, csv +import json, datetime, copy, csv, ast def lst2pgarr(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): 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) cur.execute(sql, (quantity_on_hand, json.dumps(location_data), logistics_info_id)) except Exception as error: - conn.rollback() return error 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 Args: @@ -198,16 +245,31 @@ def setLocationData(conn, site_name, location, barcode, qty): Returns: str: error/success """ - with open(f"sites/{site_name}/sql/unique/set_location_data.sql", "r+") as file: - sql = file.read() + #with open(f"sites/{site_name}/sql/unique/set_location_data.sql", "r+") as file: + # sql = file.read() + sql = f"UPDATE %sitename%_locations SET quantity_on_hand = %s WHERE id = %s;" try: with conn.cursor() as cur: - cur.execute(f"SELECT id, items FROM {site_name}_locations WHERE uuid=%s;", (location, )) - loc_id, items = cur.fetchone() - items[barcode] = items.get(barcode, 0) + qty - cur.execute(sql, (json.dumps(items), loc_id)) + cur.execute(f"SELECT id FROM {site_name}_locations WHERE uuid=%s;", (location, )) + loc_id = cur.fetchone() + 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)) + 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: - conn.rollback() + print(error) return error return "success" @@ -230,12 +292,11 @@ def insertTransaction(conn, site_name, payload): with conn.cursor() as cur: cur.execute(sql, payload) except Exception as error: - conn.rollback() return error 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 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: insertTransaction(conn, site_name, payload) - setLocationData(conn, site_name, location, barcode, qty) - setLogisticsDataTransaction(conn, site_name, location, logistics_info_id, qty) + if qty != 0.0: + setLocationData(conn, site_name, location, item_id, qty, cost) + #setLogisticsDataTransaction(conn, site_name, location, logistics_info_id, qty) except (Exception, psycopg2.DatabaseError) as error: print(error) 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"]) if not food_info_id: 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 *;" + sqlthree = f"INSERT INTO {site_name}_item_locations(part_id, location_id, quantity_on_hand, cost_layers) VALUES (%s, %s, %s, %s);" row = None try: 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() if rows: row = rows[:] + print(row) + cur.execute(sqlthree, (row[0], location_id, 0.0, lst2pgarr([]))) except (Exception, psycopg2.DatabaseError) as error: print(error) conn.rollback() @@ -303,7 +377,7 @@ def add_food_item(site_name: str, barcode: str, name: str, payload: dict): conn.commit() 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): 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/recipes.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): @@ -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/locations.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/receipt_items.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/item_locations.sql') 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);" - + sqlthree = f"INSERT INTO {site_name}_vendors(vendor_name, creation_date, created_by) VALUES (%s, %s, %s);" database_config = config() with psycopg2.connect(**database_config) as conn: zone_id = None @@ -388,6 +464,14 @@ def create_site(site_name): print(error) conn.rollback() 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() diff --git a/manage.py b/manage.py index c955e49..05ef3e8 100644 --- a/manage.py +++ b/manage.py @@ -108,6 +108,7 @@ if __name__ == "__main__": main.create_site(sys.argv[3]) if func_name == "delete" and argument == "site": + print(func_name, argument) main.delete_site(sys.argv[3]) shutil.rmtree(f"sites/{sys.argv[3]}") cfg.delete_site(sys.argv[3]) diff --git a/scratch.py b/scratch.py new file mode 100644 index 0000000..5b04d32 --- /dev/null +++ b/scratch.py @@ -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) diff --git a/sites/test2/site.ini b/sites/Backpack/site.ini similarity index 66% rename from sites/test2/site.ini rename to sites/Backpack/site.ini index 719bed4..a63d838 100644 --- a/sites/test2/site.ini +++ b/sites/Backpack/site.ini @@ -1,7 +1,7 @@ [site] -site_name=test2 -site_owner=joe -email=jdoe@gmail.com +site_name=Backpack +site_owner=Jadowyne +email= [defaults] default_zone=default diff --git a/sites/test2/sql/create/brands.sql b/sites/Backpack/sql/create/brands.sql similarity index 53% rename from sites/test2/sql/create/brands.sql rename to sites/Backpack/sql/create/brands.sql index bb70d3c..eb278a2 100644 --- a/sites/test2/sql/create/brands.sql +++ b/sites/Backpack/sql/create/brands.sql @@ -1,4 +1,4 @@ -CREATE TABLE IF NOT EXISTS test2_brands ( +CREATE TABLE IF NOT EXISTS Backpack_brands ( id SERIAL PRIMARY KEY, name VARCHAR(255) ); \ No newline at end of file diff --git a/sites/test2/sql/create/food_info.sql b/sites/Backpack/sql/create/food_info.sql similarity index 71% rename from sites/test2/sql/create/food_info.sql rename to sites/Backpack/sql/create/food_info.sql index a37324a..55dd00b 100644 --- a/sites/test2/sql/create/food_info.sql +++ b/sites/Backpack/sql/create/food_info.sql @@ -1,4 +1,4 @@ -CREATE TABLE IF NOT EXISTS test2_food_info ( +CREATE TABLE IF NOT EXISTS Backpack_food_info ( id SERIAL PRIMARY KEY, food_groups TEXT [], ingrediants TEXT [], diff --git a/sites/test2/sql/create/groups.sql b/sites/Backpack/sql/create/groups.sql similarity index 78% rename from sites/test2/sql/create/groups.sql rename to sites/Backpack/sql/create/groups.sql index b5d51f1..e88cfe3 100644 --- a/sites/test2/sql/create/groups.sql +++ b/sites/Backpack/sql/create/groups.sql @@ -1,4 +1,4 @@ -CREATE TABLE IF NOT EXISTS test2_groups( +CREATE TABLE IF NOT EXISTS Backpack_groups( id SERIAL PRIMARY KEY, name VARCHAR(255) NOT NULL, description TEXT, diff --git a/sites/test2/sql/create/item.sql b/sites/Backpack/sql/create/item.sql similarity index 67% rename from sites/test2/sql/create/item.sql rename to sites/Backpack/sql/create/item.sql index 71c9b76..c446cf6 100644 --- a/sites/test2/sql/create/item.sql +++ b/sites/Backpack/sql/create/item.sql @@ -1,4 +1,4 @@ -CREATE TABLE IF NOT EXISTS test2_items( +CREATE TABLE IF NOT EXISTS Backpack_items( id SERIAL PRIMARY KEY, barcode 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), CONSTRAINT fk_item_info FOREIGN KEY(item_info_id) - REFERENCES test2_item_info(id), + REFERENCES Backpack_item_info(id) + ON DELETE CASCADE, CONSTRAINT fk_food_info FOREIGN KEY(food_info_id) - REFERENCES test2_food_info(id), + REFERENCES Backpack_food_info(id) + ON DELETE CASCADE, CONSTRAINT fk_brand FOREIGN KEY(brand) - REFERENCES test2_brands(id), + REFERENCES Backpack_brands(id) + ON DELETE CASCADE, CONSTRAINT fk_logistics_info FOREIGN KEY(logistics_info_id) - REFERENCES test2_logistics_info(id) + REFERENCES Backpack_logistics_info(id) + ON DELETE CASCADE ); diff --git a/sites/test2/sql/create/item_info.sql b/sites/Backpack/sql/create/item_info.sql similarity index 87% rename from sites/test2/sql/create/item_info.sql rename to sites/Backpack/sql/create/item_info.sql index 1df3026..80e7c1b 100644 --- a/sites/test2/sql/create/item_info.sql +++ b/sites/Backpack/sql/create/item_info.sql @@ -1,4 +1,4 @@ -CREATE TABLE IF NOt EXISTS test2_item_info ( +CREATE TABLE IF NOt EXISTS Backpack_item_info ( id SERIAL PRIMARY KEY, barcode VARCHAR(255) NOT NULL, linked_items INTEGER [], diff --git a/sites/Backpack/sql/create/item_locations.sql b/sites/Backpack/sql/create/item_locations.sql new file mode 100644 index 0000000..3408889 --- /dev/null +++ b/sites/Backpack/sql/create/item_locations.sql @@ -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 +); \ No newline at end of file diff --git a/sites/test2/sql/create/linked_items.sql b/sites/Backpack/sql/create/linked_items.sql similarity index 77% rename from sites/test2/sql/create/linked_items.sql rename to sites/Backpack/sql/create/linked_items.sql index 3bfa112..f3b66f3 100644 --- a/sites/test2/sql/create/linked_items.sql +++ b/sites/Backpack/sql/create/linked_items.sql @@ -1,4 +1,4 @@ -CREATE TABLE IF NOT EXISTS test2_itemlinks ( +CREATE TABLE IF NOT EXISTS Backpack_itemlinks ( id SERIAL PRIMARY KEY, barcode VARCHAR(255) NOt NULL, link INTEGER NOT NULL, diff --git a/sites/test2/sql/create/locations.sql b/sites/Backpack/sql/create/locations.sql similarity index 71% rename from sites/test2/sql/create/locations.sql rename to sites/Backpack/sql/create/locations.sql index aadb380..8910039 100644 --- a/sites/test2/sql/create/locations.sql +++ b/sites/Backpack/sql/create/locations.sql @@ -1,4 +1,4 @@ -CREATE TABLE IF NOT EXISTS test2_locations( +CREATE TABLE IF NOT EXISTS Backpack_locations( id SERIAL PRIMARY KEY, uuid VARCHAR(255) NOT NULL, name VARCHAR(32) NOT NULL, @@ -7,5 +7,5 @@ CREATE TABLE IF NOT EXISTS test2_locations( UNIQUE(uuid), CONSTRAINT fk_zone FOREIGN KEY(zone_id) - REFERENCES test2_zones(id) + REFERENCES Backpack_zones(id) ); \ No newline at end of file diff --git a/sites/test2/sql/create/logins.sql b/sites/Backpack/sql/create/logins.sql similarity index 100% rename from sites/test2/sql/create/logins.sql rename to sites/Backpack/sql/create/logins.sql diff --git a/sites/test2/sql/create/logistics_info.sql b/sites/Backpack/sql/create/logistics_info.sql similarity index 82% rename from sites/test2/sql/create/logistics_info.sql rename to sites/Backpack/sql/create/logistics_info.sql index 3917bf0..0044b2e 100644 --- a/sites/test2/sql/create/logistics_info.sql +++ b/sites/Backpack/sql/create/logistics_info.sql @@ -1,4 +1,4 @@ -CREATE TABLE IF NOT EXISTS test2_logistics_info( +CREATE TABLE IF NOT EXISTS Backpack_logistics_info( id SERIAL PRIMARY KEY, barcode VARCHAR(255) NOT NULL, primary_location VARCHAR(64), diff --git a/sites/Backpack/sql/create/receipt_items.sql b/sites/Backpack/sql/create/receipt_items.sql new file mode 100644 index 0000000..cf1ff56 --- /dev/null +++ b/sites/Backpack/sql/create/receipt_items.sql @@ -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) +); \ No newline at end of file diff --git a/sites/Backpack/sql/create/receipts.sql b/sites/Backpack/sql/create/receipts.sql new file mode 100644 index 0000000..e06a013 --- /dev/null +++ b/sites/Backpack/sql/create/receipts.sql @@ -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) +); \ No newline at end of file diff --git a/sites/test2/sql/create/recipes.sql b/sites/Backpack/sql/create/recipes.sql similarity index 84% rename from sites/test2/sql/create/recipes.sql rename to sites/Backpack/sql/create/recipes.sql index ab89b63..b74f1d8 100644 --- a/sites/test2/sql/create/recipes.sql +++ b/sites/Backpack/sql/create/recipes.sql @@ -1,4 +1,4 @@ -CREATE TABLE IF NOT EXISTS test2_recipes ( +CREATE TABLE IF NOT EXISTS Backpack_recipes ( id SERIAL PRIMARY KEY, name VARCHAR, author INTEGER, diff --git a/sites/test2/sql/create/shopping_lists.sql b/sites/Backpack/sql/create/shopping_lists.sql similarity index 84% rename from sites/test2/sql/create/shopping_lists.sql rename to sites/Backpack/sql/create/shopping_lists.sql index cb52654..b5aae90 100644 --- a/sites/test2/sql/create/shopping_lists.sql +++ b/sites/Backpack/sql/create/shopping_lists.sql @@ -1,4 +1,4 @@ -CREATE TABLE IF NOT EXISTS test2_shopping_lists ( +CREATE TABLE IF NOT EXISTS Backpack_shopping_lists ( id SERIAL PRIMARY KEY, name VARCHAR(255) NOT NULL, description TEXT, diff --git a/sites/test2/sql/create/transactions.sql b/sites/Backpack/sql/create/transactions.sql similarity index 78% rename from sites/test2/sql/create/transactions.sql rename to sites/Backpack/sql/create/transactions.sql index 77c42d0..783cffa 100644 --- a/sites/test2/sql/create/transactions.sql +++ b/sites/Backpack/sql/create/transactions.sql @@ -1,4 +1,4 @@ -CREATE TABLE IF NOT EXISTS test2_Transactions ( +CREATE TABLE IF NOT EXISTS Backpack_Transactions ( id SERIAL PRIMARY KEY, timestamp TIMESTAMP, logistics_info_id INTEGER NOT NULL, @@ -11,5 +11,5 @@ CREATE TABLE IF NOT EXISTS test2_Transactions ( data JSONB, CONSTRAINT fk_logistics_info FOREIGN KEY(logistics_info_id) - REFERENCES test2_logistics_info(id) + REFERENCES Backpack_logistics_info(id) ); \ No newline at end of file diff --git a/sites/Backpack/sql/create/vendors.sql b/sites/Backpack/sql/create/vendors.sql new file mode 100644 index 0000000..f5f7d9f --- /dev/null +++ b/sites/Backpack/sql/create/vendors.sql @@ -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) +); \ No newline at end of file diff --git a/sites/test2/sql/create/zones.sql b/sites/Backpack/sql/create/zones.sql similarity index 64% rename from sites/test2/sql/create/zones.sql rename to sites/Backpack/sql/create/zones.sql index 17a555e..838174a 100644 --- a/sites/test2/sql/create/zones.sql +++ b/sites/Backpack/sql/create/zones.sql @@ -1,4 +1,4 @@ -CREATE TABLE IF NOT EXISTS test2_zones( +CREATE TABLE IF NOT EXISTS Backpack_zones( id SERIAL PRIMARY KEY, name VARCHAR(32) NOT NULL, UNIQUE(name) diff --git a/sites/Backpack/sql/drop/brands.sql b/sites/Backpack/sql/drop/brands.sql new file mode 100644 index 0000000..0d7b9ab --- /dev/null +++ b/sites/Backpack/sql/drop/brands.sql @@ -0,0 +1 @@ +DROP TABLE Backpack_brands CASCADE; \ No newline at end of file diff --git a/sites/Backpack/sql/drop/food_info.sql b/sites/Backpack/sql/drop/food_info.sql new file mode 100644 index 0000000..63c5a9d --- /dev/null +++ b/sites/Backpack/sql/drop/food_info.sql @@ -0,0 +1 @@ +DROP TABLE Backpack_food_info CASCADE; \ No newline at end of file diff --git a/sites/Backpack/sql/drop/groups.sql b/sites/Backpack/sql/drop/groups.sql new file mode 100644 index 0000000..9d4d469 --- /dev/null +++ b/sites/Backpack/sql/drop/groups.sql @@ -0,0 +1 @@ +DROP TABLE Backpack_groups CASCADE; \ No newline at end of file diff --git a/sites/Backpack/sql/drop/item_info.sql b/sites/Backpack/sql/drop/item_info.sql new file mode 100644 index 0000000..7abc4de --- /dev/null +++ b/sites/Backpack/sql/drop/item_info.sql @@ -0,0 +1 @@ +DROP TABLE Backpack_item_info CASCADE; \ No newline at end of file diff --git a/sites/Backpack/sql/drop/item_locations.sql b/sites/Backpack/sql/drop/item_locations.sql new file mode 100644 index 0000000..aa14dc0 --- /dev/null +++ b/sites/Backpack/sql/drop/item_locations.sql @@ -0,0 +1 @@ +DROP TABLE Backpack_item_locations CASCADE; diff --git a/sites/Backpack/sql/drop/items.sql b/sites/Backpack/sql/drop/items.sql new file mode 100644 index 0000000..6302681 --- /dev/null +++ b/sites/Backpack/sql/drop/items.sql @@ -0,0 +1 @@ +DROP TABLE Backpack_items CASCADE; \ No newline at end of file diff --git a/sites/Backpack/sql/drop/linked_items.sql b/sites/Backpack/sql/drop/linked_items.sql new file mode 100644 index 0000000..c81d2f3 --- /dev/null +++ b/sites/Backpack/sql/drop/linked_items.sql @@ -0,0 +1 @@ +DROP TABLE Backpack_itemlinks CASCADE; \ No newline at end of file diff --git a/sites/Backpack/sql/drop/locations.sql b/sites/Backpack/sql/drop/locations.sql new file mode 100644 index 0000000..d43dd2d --- /dev/null +++ b/sites/Backpack/sql/drop/locations.sql @@ -0,0 +1 @@ +DROP TABLE Backpack_locations CASCADE; \ No newline at end of file diff --git a/sites/Backpack/sql/drop/logistics_info.sql b/sites/Backpack/sql/drop/logistics_info.sql new file mode 100644 index 0000000..3cfacff --- /dev/null +++ b/sites/Backpack/sql/drop/logistics_info.sql @@ -0,0 +1 @@ +DROP TABLE Backpack_logistics_info CASCADE; \ No newline at end of file diff --git a/sites/Backpack/sql/drop/receipt_items.sql b/sites/Backpack/sql/drop/receipt_items.sql new file mode 100644 index 0000000..f466883 --- /dev/null +++ b/sites/Backpack/sql/drop/receipt_items.sql @@ -0,0 +1 @@ +DROP TABLE Backpack_receipt_items CASCADE; \ No newline at end of file diff --git a/sites/Backpack/sql/drop/receipts.sql b/sites/Backpack/sql/drop/receipts.sql new file mode 100644 index 0000000..c727eb9 --- /dev/null +++ b/sites/Backpack/sql/drop/receipts.sql @@ -0,0 +1 @@ +DROP TABLE Backpack_receipts CASCADE; \ No newline at end of file diff --git a/sites/Backpack/sql/drop/recipes.sql b/sites/Backpack/sql/drop/recipes.sql new file mode 100644 index 0000000..54ac91f --- /dev/null +++ b/sites/Backpack/sql/drop/recipes.sql @@ -0,0 +1 @@ +DROP TABLE Backpack_recipes CASCADE; \ No newline at end of file diff --git a/sites/Backpack/sql/drop/shopping_lists.sql b/sites/Backpack/sql/drop/shopping_lists.sql new file mode 100644 index 0000000..9c9706c --- /dev/null +++ b/sites/Backpack/sql/drop/shopping_lists.sql @@ -0,0 +1 @@ +DROP TABLE Backpack_shopping_lists CASCADE; \ No newline at end of file diff --git a/sites/Backpack/sql/drop/transactions.sql b/sites/Backpack/sql/drop/transactions.sql new file mode 100644 index 0000000..72b74b2 --- /dev/null +++ b/sites/Backpack/sql/drop/transactions.sql @@ -0,0 +1 @@ +DROP TABLE Backpack_transactions CASCADE; \ No newline at end of file diff --git a/sites/Backpack/sql/drop/vendors.sql b/sites/Backpack/sql/drop/vendors.sql new file mode 100644 index 0000000..c496079 --- /dev/null +++ b/sites/Backpack/sql/drop/vendors.sql @@ -0,0 +1 @@ +DROP TABLE Backpack_vendors CASCADE; \ No newline at end of file diff --git a/sites/Backpack/sql/drop/zones.sql b/sites/Backpack/sql/drop/zones.sql new file mode 100644 index 0000000..5e2592f --- /dev/null +++ b/sites/Backpack/sql/drop/zones.sql @@ -0,0 +1 @@ +DROP TABLE Backpack_zones CASCADE; \ No newline at end of file diff --git a/sites/Backpack/sql/unique/Insert_transaction.sql b/sites/Backpack/sql/unique/Insert_transaction.sql new file mode 100644 index 0000000..3065b56 --- /dev/null +++ b/sites/Backpack/sql/unique/Insert_transaction.sql @@ -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); \ No newline at end of file diff --git a/sites/Backpack/sql/unique/logistics_transactions.sql b/sites/Backpack/sql/unique/logistics_transactions.sql new file mode 100644 index 0000000..85cb2af --- /dev/null +++ b/sites/Backpack/sql/unique/logistics_transactions.sql @@ -0,0 +1,3 @@ +UPDATE Backpack_logistics_info +SET quantity_on_hand = %s, location_data = %s +WHERE id = %s; diff --git a/sites/Backpack/sql/unique/select_item_all.sql b/sites/Backpack/sql/unique/select_item_all.sql new file mode 100644 index 0000000..9c44c30 --- /dev/null +++ b/sites/Backpack/sql/unique/select_item_all.sql @@ -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 +*/ \ No newline at end of file diff --git a/sites/Backpack/sql/unique/select_item_all_barcode.sql b/sites/Backpack/sql/unique/select_item_all_barcode.sql new file mode 100644 index 0000000..0ba7aaf --- /dev/null +++ b/sites/Backpack/sql/unique/select_item_all_barcode.sql @@ -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 +*/ \ No newline at end of file diff --git a/sites/Backpack/sql/unique/set_location_data.sql b/sites/Backpack/sql/unique/set_location_data.sql new file mode 100644 index 0000000..4ccffbc --- /dev/null +++ b/sites/Backpack/sql/unique/set_location_data.sql @@ -0,0 +1,3 @@ +UPDATE Backpack_locations +SET items = %s +WHERE id = %s; \ No newline at end of file diff --git a/sites/default/sql/create/item.sql b/sites/default/sql/create/item.sql index 3e08ff9..9c3ab71 100644 --- a/sites/default/sql/create/item.sql +++ b/sites/default/sql/create/item.sql @@ -15,14 +15,18 @@ CREATE TABLE IF NOT EXISTS %sitename%_items( UNIQUE(barcode, item_info_id), CONSTRAINT fk_item_info FOREIGN KEY(item_info_id) - REFERENCES %sitename%_item_info(id), + REFERENCES %sitename%_item_info(id) + ON DELETE CASCADE, CONSTRAINT fk_food_info FOREIGN KEY(food_info_id) - REFERENCES %sitename%_food_info(id), + REFERENCES %sitename%_food_info(id) + ON DELETE CASCADE, CONSTRAINT fk_brand FOREIGN KEY(brand) - REFERENCES %sitename%_brands(id), + REFERENCES %sitename%_brands(id) + ON DELETE CASCADE, CONSTRAINT fk_logistics_info FOREIGN KEY(logistics_info_id) REFERENCES %sitename%_logistics_info(id) + ON DELETE CASCADE ); diff --git a/sites/default/sql/create/item_locations.sql b/sites/default/sql/create/item_locations.sql new file mode 100644 index 0000000..49405aa --- /dev/null +++ b/sites/default/sql/create/item_locations.sql @@ -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 +); \ No newline at end of file diff --git a/sites/default/sql/create/receipt_items.sql b/sites/default/sql/create/receipt_items.sql index 6cbfb64..42a06ea 100644 --- a/sites/default/sql/create/receipt_items.sql +++ b/sites/default/sql/create/receipt_items.sql @@ -1,9 +1,13 @@ CREATE TABLE IF NOT EXISTS %sitename%_receipt_items ( id SERIAL PRIMARY KEY, - type VARCHAR(255) NOT NULL, + 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) + status VARCHAR (64), + CONSTRAINT fk_receipt + FOREIGN KEY(receipt_id) + REFERENCES %sitename%_receipts(id) ); \ No newline at end of file diff --git a/sites/default/sql/create/receipts.sql b/sites/default/sql/create/receipts.sql index eb9d062..6e29c60 100644 --- a/sites/default/sql/create/receipts.sql +++ b/sites/default/sql/create/receipts.sql @@ -1,10 +1,13 @@ CREATE TABLE IF NOT EXISTS %sitename%_receipts ( id SERIAL PRIMARY KEY, - receipt_id INTEGER NOT NULL, + 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) + UNIQUE(receipt_id), + CONSTRAINT fk_vendor + FOREIGN KEY(vendor_id) + REFERENCES %sitename%_vendors(id) ); \ No newline at end of file diff --git a/sites/default/sql/create/vendors.sql b/sites/default/sql/create/vendors.sql index d3512a7..4442b69 100644 --- a/sites/default/sql/create/vendors.sql +++ b/sites/default/sql/create/vendors.sql @@ -1,8 +1,8 @@ CREATE TABLE IF NOT EXISTS %sitename%_vendors ( id SERIAL PRIMARY KEY, - name VARCHAR(255) NOT NULL, - address VARCHAR(255), + vendor_name VARCHAR(255) NOT NULL, + vendor_address VARCHAR(255), creation_date TIMESTAMP NOT NULL, - created_by TIMESTAMP NOT NULL, + created_by INTEGER NOT NULL, phone_number VARCHAR(32) ); \ No newline at end of file diff --git a/sites/default/sql/drop/item_locations.sql b/sites/default/sql/drop/item_locations.sql new file mode 100644 index 0000000..b03f916 --- /dev/null +++ b/sites/default/sql/drop/item_locations.sql @@ -0,0 +1 @@ +DROP TABLE %sitename%_item_locations CASCADE; diff --git a/sites/default/sql/unique/select_item_all_barcode.sql b/sites/default/sql/unique/select_item_all_barcode.sql new file mode 100644 index 0000000..2212b32 --- /dev/null +++ b/sites/default/sql/unique/select_item_all_barcode.sql @@ -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 +*/ \ No newline at end of file diff --git a/sites/main/sql/create/item.sql b/sites/main/sql/create/item.sql index 8295054..5734a61 100644 --- a/sites/main/sql/create/item.sql +++ b/sites/main/sql/create/item.sql @@ -15,14 +15,18 @@ CREATE TABLE IF NOT EXISTS main_items( UNIQUE(barcode, item_info_id), CONSTRAINT fk_item_info FOREIGN KEY(item_info_id) - REFERENCES main_item_info(id), + REFERENCES main_item_info(id) + ON DELETE CASCADE, CONSTRAINT fk_food_info FOREIGN KEY(food_info_id) - REFERENCES main_food_info(id), + REFERENCES main_food_info(id) + ON DELETE CASCADE, CONSTRAINT fk_brand FOREIGN KEY(brand) - REFERENCES main_brands(id), + REFERENCES main_brands(id) + ON DELETE CASCADE, CONSTRAINT fk_logistics_info FOREIGN KEY(logistics_info_id) REFERENCES main_logistics_info(id) + ON DELETE CASCADE ); diff --git a/sites/main/sql/create/item_locations.sql b/sites/main/sql/create/item_locations.sql new file mode 100644 index 0000000..24c0c3c --- /dev/null +++ b/sites/main/sql/create/item_locations.sql @@ -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 +); \ No newline at end of file diff --git a/sites/main/sql/create/receipt_items.sql b/sites/main/sql/create/receipt_items.sql index 6ab19d5..e9353ee 100644 --- a/sites/main/sql/create/receipt_items.sql +++ b/sites/main/sql/create/receipt_items.sql @@ -1,9 +1,13 @@ CREATE TABLE IF NOT EXISTS main_receipt_items ( id SERIAL PRIMARY KEY, - type VARCHAR(255) NOT NULL, + 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) + status VARCHAR (64), + CONSTRAINT fk_receipt + FOREIGN KEY(receipt_id) + REFERENCES main_receipts(id) ); \ No newline at end of file diff --git a/sites/main/sql/create/receipts.sql b/sites/main/sql/create/receipts.sql index f0859d2..763ff10 100644 --- a/sites/main/sql/create/receipts.sql +++ b/sites/main/sql/create/receipts.sql @@ -1,10 +1,13 @@ CREATE TABLE IF NOT EXISTS main_receipts ( id SERIAL PRIMARY KEY, - receipt_id INTEGER NOT NULL, + 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) + UNIQUE(receipt_id), + CONSTRAINT fk_vendor + FOREIGN KEY(vendor_id) + REFERENCES main_vendors(id) ); \ No newline at end of file diff --git a/sites/main/sql/create/vendors.sql b/sites/main/sql/create/vendors.sql index 50d498c..911d60d 100644 --- a/sites/main/sql/create/vendors.sql +++ b/sites/main/sql/create/vendors.sql @@ -1,8 +1,8 @@ CREATE TABLE IF NOT EXISTS main_vendors ( id SERIAL PRIMARY KEY, - name VARCHAR(255) NOT NULL, - address VARCHAR(255), + vendor_name VARCHAR(255) NOT NULL, + vendor_address VARCHAR(255), creation_date TIMESTAMP NOT NULL, - created_by TIMESTAMP NOT NULL, + created_by INTEGER NOT NULL, phone_number VARCHAR(32) ); \ No newline at end of file diff --git a/sites/main/sql/drop/item_locations.sql b/sites/main/sql/drop/item_locations.sql new file mode 100644 index 0000000..d159c01 --- /dev/null +++ b/sites/main/sql/drop/item_locations.sql @@ -0,0 +1 @@ +DROP TABLE main_item_locations CASCADE; diff --git a/sites/test2/sql/unique/select_item_all.sql b/sites/main/sql/unique/select_item_all_barcode.sql similarity index 66% rename from sites/test2/sql/unique/select_item_all.sql rename to sites/main/sql/unique/select_item_all_barcode.sql index c339e15..16e8a86 100644 --- a/sites/test2/sql/unique/select_item_all.sql +++ b/sites/main/sql/unique/select_item_all_barcode.sql @@ -1,8 +1,8 @@ -SELECT * FROM test2_items - LEFT JOIN test2_logistics_info ON test2_items.logistics_info_id = test2_logistics_info.id - LEFT JOIN test2_item_info ON test2_items.item_info_id = test2_item_info.id - LEFT JOIN test2_food_info ON test2_items.food_info_id = test2_food_info.id -WHERE test2_items.id=%s; +SELECT * FROM main_items + LEFT JOIN main_logistics_info ON main_items.logistics_info_id = main_logistics_info.id + LEFT JOIN main_item_info ON main_items.item_info_id = main_item_info.id + LEFT JOIN main_food_info ON main_items.food_info_id = main_food_info.id +WHERE main_items.barcode=%s; /* 00 - item_id diff --git a/sites/test/sql/create/item.sql b/sites/test/sql/create/item.sql index c9606af..e01be05 100644 --- a/sites/test/sql/create/item.sql +++ b/sites/test/sql/create/item.sql @@ -15,14 +15,18 @@ CREATE TABLE IF NOT EXISTS test_items( UNIQUE(barcode, item_info_id), CONSTRAINT fk_item_info FOREIGN KEY(item_info_id) - REFERENCES test_item_info(id), + REFERENCES test_item_info(id) + ON DELETE CASCADE, CONSTRAINT fk_food_info FOREIGN KEY(food_info_id) - REFERENCES test_food_info(id), + REFERENCES test_food_info(id) + ON DELETE CASCADE, CONSTRAINT fk_brand FOREIGN KEY(brand) - REFERENCES test_brands(id), + REFERENCES test_brands(id) + ON DELETE CASCADE, CONSTRAINT fk_logistics_info FOREIGN KEY(logistics_info_id) REFERENCES test_logistics_info(id) + ON DELETE CASCADE ); diff --git a/sites/test/sql/create/item_locations.sql b/sites/test/sql/create/item_locations.sql new file mode 100644 index 0000000..30aec57 --- /dev/null +++ b/sites/test/sql/create/item_locations.sql @@ -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 +); \ No newline at end of file diff --git a/sites/test/sql/create/receipt_items.sql b/sites/test/sql/create/receipt_items.sql index f52a7eb..ef34fd6 100644 --- a/sites/test/sql/create/receipt_items.sql +++ b/sites/test/sql/create/receipt_items.sql @@ -1,9 +1,13 @@ CREATE TABLE IF NOT EXISTS test_receipt_items ( id SERIAL PRIMARY KEY, - type VARCHAR(255) NOT NULL, + 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) + status VARCHAR (64), + CONSTRAINT fk_receipt + FOREIGN KEY(receipt_id) + REFERENCES test_receipts(id) ); \ No newline at end of file diff --git a/sites/test/sql/create/receipts.sql b/sites/test/sql/create/receipts.sql index c89b484..fbafa32 100644 --- a/sites/test/sql/create/receipts.sql +++ b/sites/test/sql/create/receipts.sql @@ -1,10 +1,13 @@ CREATE TABLE IF NOT EXISTS test_receipts ( id SERIAL PRIMARY KEY, - receipt_id INTEGER NOT NULL, + 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) + UNIQUE(receipt_id), + CONSTRAINT fk_vendor + FOREIGN KEY(vendor_id) + REFERENCES test_vendors(id) ); \ No newline at end of file diff --git a/sites/test/sql/create/vendors.sql b/sites/test/sql/create/vendors.sql index bc113b6..eedc869 100644 --- a/sites/test/sql/create/vendors.sql +++ b/sites/test/sql/create/vendors.sql @@ -1,8 +1,8 @@ CREATE TABLE IF NOT EXISTS test_vendors ( id SERIAL PRIMARY KEY, - name VARCHAR(255) NOT NULL, - address VARCHAR(255), + vendor_name VARCHAR(255) NOT NULL, + vendor_address VARCHAR(255), creation_date TIMESTAMP NOT NULL, - created_by TIMESTAMP NOT NULL, + created_by INTEGER NOT NULL, phone_number VARCHAR(32) ); \ No newline at end of file diff --git a/sites/test/sql/drop/item_locations.sql b/sites/test/sql/drop/item_locations.sql new file mode 100644 index 0000000..005ec66 --- /dev/null +++ b/sites/test/sql/drop/item_locations.sql @@ -0,0 +1 @@ +DROP TABLE test_item_locations CASCADE; diff --git a/sites/test/sql/unique/Insert_transaction.sql b/sites/test/sql/unique/Insert_transaction.sql new file mode 100644 index 0000000..7449439 --- /dev/null +++ b/sites/test/sql/unique/Insert_transaction.sql @@ -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); \ No newline at end of file diff --git a/sites/test/sql/unique/logistics_transactions.sql b/sites/test/sql/unique/logistics_transactions.sql new file mode 100644 index 0000000..1b3d28d --- /dev/null +++ b/sites/test/sql/unique/logistics_transactions.sql @@ -0,0 +1,3 @@ +UPDATE test_logistics_info +SET quantity_on_hand = %s, location_data = %s +WHERE id = %s; diff --git a/sites/test/sql/unique/select_item_all_barcode.sql b/sites/test/sql/unique/select_item_all_barcode.sql new file mode 100644 index 0000000..58ded7c --- /dev/null +++ b/sites/test/sql/unique/select_item_all_barcode.sql @@ -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 +*/ \ No newline at end of file diff --git a/sites/test/sql/unique/set_location_data.sql b/sites/test/sql/unique/set_location_data.sql new file mode 100644 index 0000000..4a44e02 --- /dev/null +++ b/sites/test/sql/unique/set_location_data.sql @@ -0,0 +1,3 @@ +UPDATE test_locations +SET items = %s +WHERE id = %s; \ No newline at end of file diff --git a/sites/test2/sql/create/receipt_items.sql b/sites/test2/sql/create/receipt_items.sql deleted file mode 100644 index 04145f1..0000000 --- a/sites/test2/sql/create/receipt_items.sql +++ /dev/null @@ -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) -); \ No newline at end of file diff --git a/sites/test2/sql/create/receipts.sql b/sites/test2/sql/create/receipts.sql deleted file mode 100644 index 806ec2f..0000000 --- a/sites/test2/sql/create/receipts.sql +++ /dev/null @@ -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) -); \ No newline at end of file diff --git a/sites/test2/sql/create/vendors.sql b/sites/test2/sql/create/vendors.sql deleted file mode 100644 index 2d7a277..0000000 --- a/sites/test2/sql/create/vendors.sql +++ /dev/null @@ -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) -); \ No newline at end of file diff --git a/sites/test2/sql/drop/brands.sql b/sites/test2/sql/drop/brands.sql deleted file mode 100644 index 0159f3f..0000000 --- a/sites/test2/sql/drop/brands.sql +++ /dev/null @@ -1 +0,0 @@ -DROP TABLE test2_brands CASCADE; \ No newline at end of file diff --git a/sites/test2/sql/drop/food_info.sql b/sites/test2/sql/drop/food_info.sql deleted file mode 100644 index 7c1cb91..0000000 --- a/sites/test2/sql/drop/food_info.sql +++ /dev/null @@ -1 +0,0 @@ -DROP TABLE test2_food_info CASCADE; \ No newline at end of file diff --git a/sites/test2/sql/drop/groups.sql b/sites/test2/sql/drop/groups.sql deleted file mode 100644 index 0513fb2..0000000 --- a/sites/test2/sql/drop/groups.sql +++ /dev/null @@ -1 +0,0 @@ -DROP TABLE test2_groups CASCADE; \ No newline at end of file diff --git a/sites/test2/sql/drop/item_info.sql b/sites/test2/sql/drop/item_info.sql deleted file mode 100644 index c786efe..0000000 --- a/sites/test2/sql/drop/item_info.sql +++ /dev/null @@ -1 +0,0 @@ -DROP TABLE test2_item_info CASCADE; \ No newline at end of file diff --git a/sites/test2/sql/drop/items.sql b/sites/test2/sql/drop/items.sql deleted file mode 100644 index df1f8a9..0000000 --- a/sites/test2/sql/drop/items.sql +++ /dev/null @@ -1 +0,0 @@ -DROP TABLE test2_items CASCADE; \ No newline at end of file diff --git a/sites/test2/sql/drop/linked_items.sql b/sites/test2/sql/drop/linked_items.sql deleted file mode 100644 index 0795351..0000000 --- a/sites/test2/sql/drop/linked_items.sql +++ /dev/null @@ -1 +0,0 @@ -DROP TABLE test2_itemlinks CASCADE; \ No newline at end of file diff --git a/sites/test2/sql/drop/locations.sql b/sites/test2/sql/drop/locations.sql deleted file mode 100644 index 8ea7d24..0000000 --- a/sites/test2/sql/drop/locations.sql +++ /dev/null @@ -1 +0,0 @@ -DROP TABLE test2_locations CASCADE; \ No newline at end of file diff --git a/sites/test2/sql/drop/logistics_info.sql b/sites/test2/sql/drop/logistics_info.sql deleted file mode 100644 index 5d5a625..0000000 --- a/sites/test2/sql/drop/logistics_info.sql +++ /dev/null @@ -1 +0,0 @@ -DROP TABLE test2_logistics_info CASCADE; \ No newline at end of file diff --git a/sites/test2/sql/drop/receipt_items.sql b/sites/test2/sql/drop/receipt_items.sql deleted file mode 100644 index 18c80dd..0000000 --- a/sites/test2/sql/drop/receipt_items.sql +++ /dev/null @@ -1 +0,0 @@ -DROP TABLE test2_receipt_items CASCADE; \ No newline at end of file diff --git a/sites/test2/sql/drop/receipts.sql b/sites/test2/sql/drop/receipts.sql deleted file mode 100644 index 7795023..0000000 --- a/sites/test2/sql/drop/receipts.sql +++ /dev/null @@ -1 +0,0 @@ -DROP TABLE test2_receipts CASCADE; \ No newline at end of file diff --git a/sites/test2/sql/drop/recipes.sql b/sites/test2/sql/drop/recipes.sql deleted file mode 100644 index ba30922..0000000 --- a/sites/test2/sql/drop/recipes.sql +++ /dev/null @@ -1 +0,0 @@ -DROP TABLE test2_recipes CASCADE; \ No newline at end of file diff --git a/sites/test2/sql/drop/shopping_lists.sql b/sites/test2/sql/drop/shopping_lists.sql deleted file mode 100644 index e4475ab..0000000 --- a/sites/test2/sql/drop/shopping_lists.sql +++ /dev/null @@ -1 +0,0 @@ -DROP TABLE test2_shopping_lists CASCADE; \ No newline at end of file diff --git a/sites/test2/sql/drop/transactions.sql b/sites/test2/sql/drop/transactions.sql deleted file mode 100644 index 330d9af..0000000 --- a/sites/test2/sql/drop/transactions.sql +++ /dev/null @@ -1 +0,0 @@ -DROP TABLE test2_transactions CASCADE; \ No newline at end of file diff --git a/sites/test2/sql/drop/vendors.sql b/sites/test2/sql/drop/vendors.sql deleted file mode 100644 index 5fa0868..0000000 --- a/sites/test2/sql/drop/vendors.sql +++ /dev/null @@ -1 +0,0 @@ -DROP TABLE test2_vendors CASCADE; \ No newline at end of file diff --git a/sites/test2/sql/drop/zones.sql b/sites/test2/sql/drop/zones.sql deleted file mode 100644 index fc30e59..0000000 --- a/sites/test2/sql/drop/zones.sql +++ /dev/null @@ -1 +0,0 @@ -DROP TABLE test2_zones CASCADE; \ No newline at end of file diff --git a/static/itemHandler.js b/static/itemHandler.js index ab4d8a6..9379daa 100644 --- a/static/itemHandler.js +++ b/static/itemHandler.js @@ -4,6 +4,7 @@ async function fetchItem() { const response = await fetch(url); data = await response.json(); item = data.item; + linked_items = data.linked_items; }; async function fetchZones() { @@ -91,7 +92,6 @@ function updatePrimaryLocation(){ document.getElementById('primary_location').style = "" logistics_info['primary_location'] = `${primary_zone}@${primary_location}` }; - console.log(logistics_info) }; function updateIssueLocation(){ @@ -108,54 +108,45 @@ function updateIssueLocation(){ function updateEntryType(){ updated['row_type'] = document.getElementById('entry_type').value; - console.log(updated) }; function updateItemType(){ updated['item_type'] = document.getElementById('item_type').value; - console.log(updated) }; function updatePackaging(){ let packaging = document.getElementById('packaging').value; item_info['packaging'] = packaging; - console.log(item_info) }; function updateUOM(){ let uom = document.getElementById('uom').value; item_info['uom'] = uom; - console.log(item_info) }; function updateCost(){ let cost = document.getElementById('cost').value; item_info['cost'] = parseFloat(cost); - console.log(item_info) }; function updateSafetyStock(){ let safety_stock = document.getElementById('safety_stock').value; item_info['safety_stock'] = parseFloat(safety_stock); - console.log(item_info) }; function updateLeadTimeDays(){ let lead_time_days = document.getElementById('lead_time_days').value; item_info['lead_time_days'] = parseFloat(lead_time_days); - console.log(item_info) }; function updateAiPickable(){ let ai_pick = document.getElementById('ai_pickable'); item_info['ai_pick'] = ai_pick.checked; - console.log(item_info) }; function updateExpires(){ let expires = document.getElementById('expires'); food_info['expires'] = expires.checked; - console.log(food_info) }; function updateNutrients(){ @@ -177,10 +168,7 @@ function updateNutrients(){ fibers: document.getElementById('fibers').value, fibers_unit: document.getElementById('fibers_unit').value }; - console.log(nutrients) nutrients_changed = true; - - } async function saveItem() { @@ -202,8 +190,6 @@ async function saveItem() { updated['links'] = links; }; - console.log(`going into fetch ${logistics_info}`) - await fetch(`/updateItem`, { method: 'POST', headers: { diff --git a/static/receiptHandler.js b/static/receiptHandler.js new file mode 100644 index 0000000..602c77e --- /dev/null +++ b/static/receiptHandler.js @@ -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++ + } +} \ No newline at end of file diff --git a/static/transactionHandler.js b/static/transactionHandler.js index 640bdee..7f74129 100644 --- a/static/transactionHandler.js +++ b/static/transactionHandler.js @@ -3,6 +3,9 @@ let end_page; let limit = 50 let search_text = "" let zones; +let barcode = "" +let item_name = "" +let logistics_info_id = 0 async function setupZones() { let primary_zone = document.getElementById('zone') @@ -22,7 +25,6 @@ async function fetchZones() { zones = data.zones; }; - async function fetchLocations(zone) { const url = new URL('/getLocations', window.location.origin); url.searchParams.append('zone', zone); @@ -79,6 +81,8 @@ async function fetchItems(){ document.getElementById('forward').classList.remove("disabled") document.getElementById('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); @@ -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){ let item = await fetchItem(database_id); 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){ + barcode = item[1] + item_name = item[2] + logistics_info_id = item[8] document.getElementById("database_id").value = item[0]; + document.getElementById("database_id").style = ""; document.getElementById("barcode").value = item[1]; + document.getElementById("barcode").style = ""; document.getElementById("name").value = item[2]; document.getElementById("QOH").value = item[19]; + document.getElementById("UOM").value = item[27]; + document.getElementById("transaction_cost").value = item[28]; let location = item[16].split('@') await setLocation(location[0], location[1]) @@ -148,8 +222,10 @@ async function populateFields(item){ async function setLocation(zone, location){ document.getElementById('zone').value = zone + document.getElementById('zone').style = "" await loadLocations() document.getElementById('location').value = location + document.getElementById('location').style = "" }; async function fetchItem(database_id){ @@ -160,6 +236,87 @@ async function fetchItem(database_id){ 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(){ current_page++ await fetchItems() diff --git a/templates/items/index.html b/templates/items/index.html index 36d1c85..e01ae49 100644 --- a/templates/items/index.html +++ b/templates/items/index.html @@ -35,7 +35,6 @@ pointer-events: none; opacity: 0.5; /* or your desired degree of transparency */ } -
@@ -92,6 +92,12 @@ Non Zero Items
+
+ +
@@ -176,7 +182,6 @@
  • page_number
  • chevron_right
  • last_page
  • - @@ -322,8 +327,6 @@ url.searchParams.append('sort_order', sort_order); url.searchParams.append('view', view); - - await fetch(url) .then(response => response.json()) .then(data => { diff --git a/templates/items/item.html b/templates/items/item.html index 4015aaa..a80309b 100644 --- a/templates/items/item.html +++ b/templates/items/item.html @@ -30,6 +30,10 @@ pointer-events: none; opacity: 0.5; /* or your desired degree of transparency */ } + .custom_row:hover{ + background-color: rgb(230, 230, 230) !important; + cursor: pointer; + }
    @@ -441,7 +445,37 @@
    -
    Linked Items
    +
    +
    +
    +
    Linked Items
    +
    +
    +
    + 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. + +
    +
    +
    + +
    +
    + + + + + + + + + +
    Linked Item
    +
    +
    +
    @@ -476,7 +510,41 @@ - + +
    ` + + if ((colorstate % 2) == 0){ + row.classList.add('green') + row.classList.add('lighten-5') + } + colorstate++ + }; + } + function populateReferences(references, reference_type){ var table = document.getElementById("reference_table") 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 = `Database ID`; + header_barcode.innerHTML = `Barcode`; + header_name.innerHTML = `Product Name`; + + 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(){ var table = document.getElementById("locations_table") console.log(item[18]) @@ -767,6 +976,20 @@ colorstate++ }; }; + + 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) + } \ No newline at end of file diff --git a/templates/items/itemlink.html b/templates/items/itemlink.html new file mode 100644 index 0000000..145058c --- /dev/null +++ b/templates/items/itemlink.html @@ -0,0 +1,70 @@ + + + + + My Pantry + + + + + + +
    +
    +
    + +
    + + + Item Barcode +
    +
    + + + Pantry Item this is linked to. +
    +
    + + + Conversion Factor for scanning adjustments. +
    +
    + + +
    +
    +
    + +
    +
    +
    +
    + + + diff --git a/templates/receipts/index.html b/templates/receipts/index.html new file mode 100644 index 0000000..786a420 --- /dev/null +++ b/templates/receipts/index.html @@ -0,0 +1,184 @@ + + + + + My Pantry - Receipts + + + + + + + + + + + + + + + +
    +
    +
    +
    +
    + +
    +
    +
    + + + \ No newline at end of file diff --git a/templates/receipts/receipt.html b/templates/receipts/receipt.html new file mode 100644 index 0000000..0719c7a --- /dev/null +++ b/templates/receipts/receipt.html @@ -0,0 +1,217 @@ + + + + + My Pantry - Items + + + + + + + + + + + + + + + +
    +
    +
    +

    +
    +
    +
    +
    +
    +
    +
    +
    +

    +
    + + +
    +
    Vendor
    +
    +
    +
    +
    + + +
    +
    + +
    +
    + + +
    +
    + + +
    +
    + +
    +
    Receipt Items
    +
    +
    +
    + Below are the line items on this receipt. By clicking on the row you can modify, save, delete, void, resolve the lines. + +
    +
    +
    +
    + + + + + + + + + + + + + +
    typebarcodenameqtycostStatus
    +
    +
    + + + + +
    + + + + \ No newline at end of file diff --git a/templates/transaction.html b/templates/transaction.html index 05bd45d..40318b4 100644 --- a/templates/transaction.html +++ b/templates/transaction.html @@ -13,8 +13,8 @@ - - +