From f1cc51f378151d887d6eeac79276c573b00d8934 Mon Sep 17 00:00:00 2001 From: Jadowyne Ulve Date: Sun, 8 Dec 2024 19:27:55 -0600 Subject: [PATCH] Mass Update --- __pycache__/api.cpython-312.pyc | Bin 47310 -> 70704 bytes __pycache__/config.cpython-312.pyc | Bin 2687 -> 2672 bytes __pycache__/external_devices.cpython-312.pyc | Bin 0 -> 13135 bytes __pycache__/main.cpython-312.pyc | Bin 25441 -> 30814 bytes api.py | 456 +++++++++++++++++- database.ini | 2 +- external_devices.py | 232 +++++++++ main.py | 118 ++++- manage.py | 1 + scratch.py | 38 ++ sites/{test2 => Backpack}/site.ini | 6 +- .../{test2 => Backpack}/sql/create/brands.sql | 2 +- .../sql/create/food_info.sql | 2 +- .../{test2 => Backpack}/sql/create/groups.sql | 2 +- sites/{test2 => Backpack}/sql/create/item.sql | 14 +- .../sql/create/item_info.sql | 2 +- sites/Backpack/sql/create/item_locations.sql | 23 + .../sql/create/linked_items.sql | 2 +- .../sql/create/locations.sql | 4 +- .../{test2 => Backpack}/sql/create/logins.sql | 0 .../sql/create/logistics_info.sql | 2 +- sites/Backpack/sql/create/receipt_items.sql | 13 + sites/Backpack/sql/create/receipts.sql | 13 + .../sql/create/recipes.sql | 2 +- .../sql/create/shopping_lists.sql | 2 +- .../sql/create/transactions.sql | 4 +- sites/Backpack/sql/create/vendors.sql | 8 + .../{test2 => Backpack}/sql/create/zones.sql | 2 +- sites/Backpack/sql/drop/brands.sql | 1 + sites/Backpack/sql/drop/food_info.sql | 1 + sites/Backpack/sql/drop/groups.sql | 1 + sites/Backpack/sql/drop/item_info.sql | 1 + sites/Backpack/sql/drop/item_locations.sql | 1 + sites/Backpack/sql/drop/items.sql | 1 + sites/Backpack/sql/drop/linked_items.sql | 1 + sites/Backpack/sql/drop/locations.sql | 1 + sites/Backpack/sql/drop/logistics_info.sql | 1 + sites/Backpack/sql/drop/receipt_items.sql | 1 + sites/Backpack/sql/drop/receipts.sql | 1 + sites/Backpack/sql/drop/recipes.sql | 1 + sites/Backpack/sql/drop/shopping_lists.sql | 1 + sites/Backpack/sql/drop/transactions.sql | 1 + sites/Backpack/sql/drop/vendors.sql | 1 + sites/Backpack/sql/drop/zones.sql | 1 + .../sql/unique/Insert_transaction.sql | 3 + .../sql/unique/logistics_transactions.sql | 3 + sites/Backpack/sql/unique/select_item_all.sql | 45 ++ .../sql/unique/select_item_all_barcode.sql | 45 ++ .../Backpack/sql/unique/set_location_data.sql | 3 + sites/default/sql/create/item.sql | 10 +- sites/default/sql/create/item_locations.sql | 23 + sites/default/sql/create/receipt_items.sql | 8 +- sites/default/sql/create/receipts.sql | 7 +- sites/default/sql/create/vendors.sql | 6 +- sites/default/sql/drop/item_locations.sql | 1 + .../sql/unique/select_item_all_barcode.sql | 45 ++ sites/main/sql/create/item.sql | 10 +- sites/main/sql/create/item_locations.sql | 23 + sites/main/sql/create/receipt_items.sql | 8 +- sites/main/sql/create/receipts.sql | 7 +- sites/main/sql/create/vendors.sql | 6 +- sites/main/sql/drop/item_locations.sql | 1 + .../sql/unique/select_item_all_barcode.sql} | 10 +- sites/test/sql/create/item.sql | 10 +- sites/test/sql/create/item_locations.sql | 23 + sites/test/sql/create/receipt_items.sql | 8 +- sites/test/sql/create/receipts.sql | 7 +- sites/test/sql/create/vendors.sql | 6 +- sites/test/sql/drop/item_locations.sql | 1 + sites/test/sql/unique/Insert_transaction.sql | 3 + .../sql/unique/logistics_transactions.sql | 3 + .../sql/unique/select_item_all_barcode.sql | 45 ++ sites/test/sql/unique/set_location_data.sql | 3 + sites/test2/sql/create/receipt_items.sql | 9 - sites/test2/sql/create/receipts.sql | 10 - sites/test2/sql/create/vendors.sql | 8 - sites/test2/sql/drop/brands.sql | 1 - sites/test2/sql/drop/food_info.sql | 1 - sites/test2/sql/drop/groups.sql | 1 - sites/test2/sql/drop/item_info.sql | 1 - sites/test2/sql/drop/items.sql | 1 - sites/test2/sql/drop/linked_items.sql | 1 - sites/test2/sql/drop/locations.sql | 1 - sites/test2/sql/drop/logistics_info.sql | 1 - sites/test2/sql/drop/receipt_items.sql | 1 - sites/test2/sql/drop/receipts.sql | 1 - sites/test2/sql/drop/recipes.sql | 1 - sites/test2/sql/drop/shopping_lists.sql | 1 - sites/test2/sql/drop/transactions.sql | 1 - sites/test2/sql/drop/vendors.sql | 1 - sites/test2/sql/drop/zones.sql | 1 - static/itemHandler.js | 16 +- static/receiptHandler.js | 276 +++++++++++ static/transactionHandler.js | 159 +++++- templates/items/index.html | 11 +- templates/items/item.html | 233 ++++++++- templates/items/itemlink.html | 70 +++ templates/receipts/index.html | 184 +++++++ templates/receipts/receipt.html | 217 +++++++++ templates/transaction.html | 81 +++- test.txt | 203 ++++++++ webserver.py | 23 +- 102 files changed, 2680 insertions(+), 174 deletions(-) create mode 100644 __pycache__/external_devices.cpython-312.pyc create mode 100644 external_devices.py create mode 100644 scratch.py rename sites/{test2 => Backpack}/site.ini (66%) rename sites/{test2 => Backpack}/sql/create/brands.sql (53%) rename sites/{test2 => Backpack}/sql/create/food_info.sql (71%) rename sites/{test2 => Backpack}/sql/create/groups.sql (78%) rename sites/{test2 => Backpack}/sql/create/item.sql (67%) rename sites/{test2 => Backpack}/sql/create/item_info.sql (87%) create mode 100644 sites/Backpack/sql/create/item_locations.sql rename sites/{test2 => Backpack}/sql/create/linked_items.sql (77%) rename sites/{test2 => Backpack}/sql/create/locations.sql (71%) rename sites/{test2 => Backpack}/sql/create/logins.sql (100%) rename sites/{test2 => Backpack}/sql/create/logistics_info.sql (82%) create mode 100644 sites/Backpack/sql/create/receipt_items.sql create mode 100644 sites/Backpack/sql/create/receipts.sql rename sites/{test2 => Backpack}/sql/create/recipes.sql (84%) rename sites/{test2 => Backpack}/sql/create/shopping_lists.sql (84%) rename sites/{test2 => Backpack}/sql/create/transactions.sql (78%) create mode 100644 sites/Backpack/sql/create/vendors.sql rename sites/{test2 => Backpack}/sql/create/zones.sql (64%) create mode 100644 sites/Backpack/sql/drop/brands.sql create mode 100644 sites/Backpack/sql/drop/food_info.sql create mode 100644 sites/Backpack/sql/drop/groups.sql create mode 100644 sites/Backpack/sql/drop/item_info.sql create mode 100644 sites/Backpack/sql/drop/item_locations.sql create mode 100644 sites/Backpack/sql/drop/items.sql create mode 100644 sites/Backpack/sql/drop/linked_items.sql create mode 100644 sites/Backpack/sql/drop/locations.sql create mode 100644 sites/Backpack/sql/drop/logistics_info.sql create mode 100644 sites/Backpack/sql/drop/receipt_items.sql create mode 100644 sites/Backpack/sql/drop/receipts.sql create mode 100644 sites/Backpack/sql/drop/recipes.sql create mode 100644 sites/Backpack/sql/drop/shopping_lists.sql create mode 100644 sites/Backpack/sql/drop/transactions.sql create mode 100644 sites/Backpack/sql/drop/vendors.sql create mode 100644 sites/Backpack/sql/drop/zones.sql create mode 100644 sites/Backpack/sql/unique/Insert_transaction.sql create mode 100644 sites/Backpack/sql/unique/logistics_transactions.sql create mode 100644 sites/Backpack/sql/unique/select_item_all.sql create mode 100644 sites/Backpack/sql/unique/select_item_all_barcode.sql create mode 100644 sites/Backpack/sql/unique/set_location_data.sql create mode 100644 sites/default/sql/create/item_locations.sql create mode 100644 sites/default/sql/drop/item_locations.sql create mode 100644 sites/default/sql/unique/select_item_all_barcode.sql create mode 100644 sites/main/sql/create/item_locations.sql create mode 100644 sites/main/sql/drop/item_locations.sql rename sites/{test2/sql/unique/select_item_all.sql => main/sql/unique/select_item_all_barcode.sql} (66%) create mode 100644 sites/test/sql/create/item_locations.sql create mode 100644 sites/test/sql/drop/item_locations.sql create mode 100644 sites/test/sql/unique/Insert_transaction.sql create mode 100644 sites/test/sql/unique/logistics_transactions.sql create mode 100644 sites/test/sql/unique/select_item_all_barcode.sql create mode 100644 sites/test/sql/unique/set_location_data.sql delete mode 100644 sites/test2/sql/create/receipt_items.sql delete mode 100644 sites/test2/sql/create/receipts.sql delete mode 100644 sites/test2/sql/create/vendors.sql delete mode 100644 sites/test2/sql/drop/brands.sql delete mode 100644 sites/test2/sql/drop/food_info.sql delete mode 100644 sites/test2/sql/drop/groups.sql delete mode 100644 sites/test2/sql/drop/item_info.sql delete mode 100644 sites/test2/sql/drop/items.sql delete mode 100644 sites/test2/sql/drop/linked_items.sql delete mode 100644 sites/test2/sql/drop/locations.sql delete mode 100644 sites/test2/sql/drop/logistics_info.sql delete mode 100644 sites/test2/sql/drop/receipt_items.sql delete mode 100644 sites/test2/sql/drop/receipts.sql delete mode 100644 sites/test2/sql/drop/recipes.sql delete mode 100644 sites/test2/sql/drop/shopping_lists.sql delete mode 100644 sites/test2/sql/drop/transactions.sql delete mode 100644 sites/test2/sql/drop/vendors.sql delete mode 100644 sites/test2/sql/drop/zones.sql create mode 100644 static/receiptHandler.js create mode 100644 templates/items/itemlink.html create mode 100644 templates/receipts/index.html create mode 100644 templates/receipts/receipt.html create mode 100644 test.txt diff --git a/__pycache__/api.cpython-312.pyc b/__pycache__/api.cpython-312.pyc index f6dbc35fc4fc49503aed76bf39f763c2d9877f31..cc6fb280ae8af963eec9636ad282013bbef0be98 100644 GIT binary patch delta 25432 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 delta 9090 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 diff --git a/__pycache__/config.cpython-312.pyc b/__pycache__/config.cpython-312.pyc index 10a5f907c2bac01c83e134a4a44519e9b39227a5..034c139b3fc1beaa8847def7972b0d99e7fa49bf 100644 GIT binary patch delta 59 zcmew_@04@a+dH?_b delta 74 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 P3TlaQ)<3~N(?R|R8Jo5( delta 5444 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 s2XgOkhjQ;9Z~l 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 @@ - - +