diff --git a/__pycache__/api.cpython-312.pyc b/__pycache__/api.cpython-312.pyc index 4d8b66f..f6dbc35 100644 Binary files a/__pycache__/api.cpython-312.pyc and b/__pycache__/api.cpython-312.pyc differ diff --git a/__pycache__/main.cpython-312.pyc b/__pycache__/main.cpython-312.pyc index 51ca609..37b8f60 100644 Binary files a/__pycache__/main.cpython-312.pyc and b/__pycache__/main.cpython-312.pyc differ diff --git a/api.py b/api.py index 3b20712..2b55a8d 100644 --- a/api.py +++ b/api.py @@ -184,6 +184,25 @@ def pagninate_transactions(): return jsonify({'transactions': transactions, "end": math.ceil(count/limit)}) +@database_api.route("/getTransaction") +def get_transaction(): + id = int(request.args.get('id', 1)) + database_config = config() + site_name = session['selected_site'] + + transaction = [] + with psycopg2.connect(**database_config) as conn: + try: + with conn.cursor() as cur: + sql = f"SELECT * FROM {site_name}_transactions WHERE id=%s;" + cur.execute(sql, (id, )) + transaction = list(cur.fetchone()) + except (Exception, psycopg2.DatabaseError) as error: + print(error) + + print(transaction) + return jsonify(transaction=transaction) + @database_api.route("/getLocations") def get_locations(): zone_name = request.args.get('zone', 1) @@ -298,9 +317,39 @@ def addItem(): conn.commit() + with psycopg2.connect(**database_config) as conn: + try: + with conn.cursor() as cur: + cur.execute(f"SELECT primary_location FROM {site_name}_logistics_info WHERE id={logistics_info_id};") + location = cur.fetchone()[0] + payload = [ + datetime.datetime.now(), + logistics_info_id, + barcode, + name, + "SYSTEM", + 0.0, + "Item Added to System!", + 1, + json.dumps({'location': location}) + ] + + main.addTransaction( + conn=conn, + site_name=site_name, + payload=payload, + location=location, + logistics_info_id=logistics_info_id, + barcode=barcode, + qty=0.0) + + except (Exception, psycopg2.DatabaseError) as error: + print(error) + conn.rollback() + return jsonify({'state': str(error)}) + - main.add_transaction(site_name, barcode, qty=0, user_id=1, description="Added Item to System!") @@ -328,19 +377,30 @@ def updateItem(): y = f"{', '.join(['%s' for _ in keys])}" sql = f"UPDATE {table} SET {x} = {y} WHERE id={item_id};" - return sql + sqltwo = f"SELECT {', '.join(keys)} FROM {table} WHERE id={item_id};" + return sql, sqltwo if request.method == "POST": site_name = session['selected_site'] - item_id = request.get_json()['id'] - logistics_info_id = request.get_json()['logistics_info_id'] + data = request.get_json() + logistics_info_id = request.get_json()['logistics_info_id'] food_info_id = request.get_json()['food_info_id'] item_info_id = request.get_json()['item_info_id'] updated = request.get_json()['updated'] item_info = request.get_json()['item_info'] food_info = request.get_json()['food_info'] - logistics_info = request.get_json()['logistics_info'] + logistics_info = data['logistics_info'] + + save_data = {} + for k, v in updated.items(): + save_data[f"{k}_new"] = v; + for k, v in item_info.items(): + save_data[f"{k}_new"] = v; + for k, v in food_info.items(): + save_data[f"{k}_new"] = v; + for k, v in logistics_info.items(): + save_data[f"{k}_new"] = v; database_config = config() @@ -349,25 +409,64 @@ def updateItem(): with conn.cursor() as cur: if updated != {}: values = transformValues(updated.values()) - sql = manufactureSQL(updated.keys(), item_id, f"{site_name}_items") + sql, sqltwo = manufactureSQL(updated.keys(), item_id, f"{site_name}_items") + cur.execute(sqltwo) + old_data = dict(zip(updated.keys(), cur.fetchone())) + for k, v in old_data.items(): + save_data[f"{k}_old"] = v; cur.execute(sql, values) + if item_info != {}: values = transformValues(item_info.values()) - sql = manufactureSQL(item_info.keys(), item_info_id, f"{site_name}_item_info") + sql, sqltwo = manufactureSQL(item_info.keys(), item_info_id, f"{site_name}_item_info") + cur.execute(sqltwo) + old_data = dict(zip(item_info.keys(), cur.fetchone())) + for k, v in old_data.items(): + save_data[f"{k}_old"] = v; cur.execute(sql, values) + if food_info != {}: values = transformValues(food_info.values()) - sql = manufactureSQL(food_info.keys(), food_info_id, f"{site_name}_food_info") + sql, sqltwo = manufactureSQL(food_info.keys(), food_info_id, f"{site_name}_food_info") + cur.execute(sqltwo) + old_data = dict(zip(food_info.keys(), cur.fetchone())) + for k, v in old_data.items(): + save_data[f"{k}_old"] = v; cur.execute(sql, values) + if logistics_info != {}: values = transformValues(logistics_info.values()) - sql = manufactureSQL(logistics_info.keys(), logistics_info_id, f"{site_name}_logistics_info") + sql, sqltwo = manufactureSQL(logistics_info.keys(), logistics_info_id, f"{site_name}_logistics_info") + cur.execute(sqltwo) + old_data = dict(zip(logistics_info.keys(), cur.fetchone())) + for k, v in old_data.items(): + save_data[f"{k}_old"] = v; cur.execute(sql, values) - cur.execute(f"SELECT barcode FROM {site_name}_items WHERE id={item_id};") - barcode = cur.fetchone()[0] - print(barcode) - main.add_transaction(site_name, barcode, 0, 1, "SYSTEM", "Item data was update!", data=request.get_json()) + 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() + payload = [ + datetime.datetime.now(), + logistics_info_id, + barcode, + name, + "SYSTEM", + 0.0, + "Updated Item!", + 1, + json.dumps(save_data) + ] + + main.addTransaction( + conn=conn, + site_name=site_name, + payload=payload, + location=primary_location, + logistics_info_id=logistics_info_id, + barcode=barcode, + qty=0.0 + ) + except (Exception, psycopg2.DatabaseError) as error: print(error) conn.rollback() diff --git a/main.py b/main.py index 82c9876..2a3de0d 100644 --- a/main.py +++ b/main.py @@ -159,84 +159,108 @@ def add_zone(site_name, name): conn.rollback() return error -def add_transaction(site_name, barcode, qty, user_id, transaction_type = "info", description = "", data = {}, location=None): - database_config = config() - with psycopg2.connect(**database_config) as conn: +def setLogisticsDataTransaction(conn, site_name, location, logistics_info_id, qty): + """Sets the logistic_info for site at info_id + + Args: + conn (Object): psycopg2.connector + site_name (str): name of site where data is manipulated + location (str): location to be add or append to data + logistics_info_id (int): logistics data to be manipulated + qty (float): qty to be added or appended + + Returns: + str: success/error + """ + with open(f"sites/{site_name}/sql/unique/logistics_transactions.sql", "r+") as file: + sql = file.read() + try: with conn.cursor() as cur: - cur.execute(f"SELECT item_name, logistics_info_id FROM {site_name}_items WHERE barcode=%s;", (barcode, )) - item = cur.fetchone() + cur.execute(f"SELECT quantity_on_hand, location_data FROM {site_name}_logistics_info WHERE id=%s;", (logistics_info_id, )) + quantity_on_hand, location_data = cur.fetchone() + location_data[location] = location_data.get(location, 0) + qty + 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): + """Sets location data to include barcode: qty as k:v pair + + Args: + site_name (string): Name of the site to manipulate location data on + location (string): location in said site to manipulate locationd data on + barcode (string): Barcode to add or append to + qty (float): quantity to add or append to + + Returns: + str: error/success + """ + with open(f"sites/{site_name}/sql/unique/set_location_data.sql", "r+") as file: + sql = file.read() + try: with conn.cursor() as cur: - cur.execute(f"SELECT location_data, quantity_on_hand, primary_location, barcode FROM {site_name}_logistics_info WHERE id=%s;", (item[1],)) - logistics_info = cur.fetchone() + 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)) + except Exception as error: + conn.rollback() + return error + return "success" - new_trans = copy.deepcopy(transaction_payload) - new_trans["timestamp"] = datetime.datetime.now() - new_trans["logistics_info_id"] = item[1] - new_trans["barcode"] = barcode - new_trans["user_id"] = user_id - new_trans["name"] = item[0] - new_trans["transaction_type"] = transaction_type - new_trans["description"] = description - new_trans["quantity"] = qty - new_trans["data"] = data +def insertTransaction(conn, site_name, payload): + """Insert a transaction into the site name using a payload + [timestamp, logistics_info_id, barcode, name, transaction_type, + quantity, description, user_id, data] - sql = f"INSERT INTO {site_name}_transactions(timestamp, logistics_info_id, barcode, name, transaction_type, quantity, description, user_id, data) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s);" - database_config = config() - with psycopg2.connect(**database_config) as conn: - try: - with conn.cursor() as cur: - cur.execute(sql, (new_trans["timestamp"], new_trans["logistics_info_id"], new_trans["barcode"], new_trans["name"], new_trans["transaction_type"], - new_trans["quantity"], new_trans["description"], new_trans["user_id"], json.dumps(new_trans["data"]))) - except (Exception, psycopg2.DatabaseError) as error: - print(error) - conn.rollback() - return error - - if not location: - mover = logistics_info[2] - else: - mover = location + Args: + site_name (str): _description_ + payload (list): list of values to insert into database - location_items = None - location_id = None - try: - with conn.cursor() as cur: - cur.execute(f"SELECT id, items FROM {site_name}_locations WHERE uuid=%s;", (mover, )) - location = cur.fetchone() - if location: - location_id = location[0] - location_items = location[1] - except (Exception, psycopg2.DatabaseError) as error: - print(error) - conn.rollback() - return error + Returns: + _type_: _description_ + """ + with open(f"sites/{site_name}/sql/unique/insert_transaction.sql", "r+") as file: + sql = file.read() + try: + with conn.cursor() as cur: + cur.execute(sql, payload) + except Exception as error: + conn.rollback() + return error - if logistics_info[3] in location_items.keys(): - location_items[logistics_info[3]] = location_items[logistics_info[3]] + qty - else: - location_items[logistics_info[3]] = qty + return "success" - if mover in logistics_info[0].keys(): - logistics_info[0][mover] = logistics_info[0][mover] + qty - else: - logistics_info[0][mover] = qty +def addTransaction(*, conn, site_name, payload, location, logistics_info_id, barcode, qty): + """a complete function for adding a transaction to the system + + payload = [timestamp, logistics_info_id, barcode, name, transaction_type, + quantity, description, user_id, data] + + Args: + conn (object): psycopg2.connector + site_name (str): The site to which will have a transaction added + payload (list): transaction payload + location (str): location in the site that will be manipulated + logistics_info_id (int): logistic_info id to be mainpulated + barcode (str): barcode in the site to be manipulated + qty (float): qty to be added or appened to the transaction - qty = logistics_info[1] + qty - - set_location_data = f"UPDATE {site_name}_locations SET items = %s WHERE id = %s;" - set_quantity_on_hand = f"UPDATE {site_name}_logistics_info SET quantity_on_hand = %s, location_data = %s WHERE id = %s;" - try: - with conn.cursor() as cur: - cur.execute(set_quantity_on_hand, (qty, json.dumps(logistics_info[0]), new_trans["logistics_info_id"])) - cur.execute(set_location_data, (json.dumps(location_items), location_id)) - except (Exception, psycopg2.DatabaseError) as error: - print(error) - conn.rollback() - return error - - conn.commit() + Returns: + str: success/error + """ + try: + insertTransaction(conn, site_name, payload) + setLocationData(conn, site_name, location, barcode, qty) + setLogisticsDataTransaction(conn, site_name, location, logistics_info_id, qty) + except (Exception, psycopg2.DatabaseError) as error: + print(error) + conn.rollback() + return error def add_food_item(site_name: str, barcode: str, name: str, payload: dict): @@ -276,11 +300,10 @@ def add_food_item(site_name: str, barcode: str, name: str, payload: dict): conn.rollback() return False - conn.commit() - - add_transaction(site_name, barcode, qty=0, user_id=1, description="Added Item to System!") + 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) def drop_table(sql_file: str): database_config = config() @@ -369,8 +392,6 @@ def create_site(site_name): conn.commit() - - transaction_payload = { "timestamp": None, "logistics_info_id": 0, @@ -383,7 +404,6 @@ transaction_payload = { "data": {} } - payload_food_item = { "item_info": { "linked_items": [], @@ -447,20 +467,6 @@ def parse_csv(path_to_csv): qty = float(line[30]) add_food_item(site_name="main", barcode=line[1], name=line[2], payload=payload) - - - if __name__ == "__main__": - #print(add_readitem(site_name="main", barcode="1235", name="testone")) - """database_config = config() - sql = "SELECT items FROM test_locations WHERE id=1;" - with psycopg2.connect(**database_config) as conn: - with conn.cursor() as cur: - cur.execute(sql) - - items = cur.fetchone()[0] - for k, v in items.items(): - print(f"{k}: {v}") - """ parse_csv(r"C:\\Users\\jadow\\Documents\\code\\postgresql python\\postgresql-python\\2024-10-02-Pantry.csv") diff --git a/sites/default/sql/unique/Insert_transaction.sql b/sites/default/sql/unique/Insert_transaction.sql new file mode 100644 index 0000000..7f1d0e4 --- /dev/null +++ b/sites/default/sql/unique/Insert_transaction.sql @@ -0,0 +1,3 @@ +INSERT INTO %sitename%_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/default/sql/unique/logistics_transactions.sql b/sites/default/sql/unique/logistics_transactions.sql new file mode 100644 index 0000000..7c5d704 --- /dev/null +++ b/sites/default/sql/unique/logistics_transactions.sql @@ -0,0 +1,3 @@ +UPDATE %sitename%_logistics_info +SET quantity_on_hand = %s, location_data = %s +WHERE id = %s; diff --git a/sites/default/sql/unique/set_location_data.sql b/sites/default/sql/unique/set_location_data.sql new file mode 100644 index 0000000..96bb425 --- /dev/null +++ b/sites/default/sql/unique/set_location_data.sql @@ -0,0 +1,3 @@ +UPDATE %sitename%_locations +SET items = %s +WHERE id = %s; \ No newline at end of file diff --git a/sites/main/sql/unique/Insert_transaction.sql b/sites/main/sql/unique/Insert_transaction.sql new file mode 100644 index 0000000..b6c94fe --- /dev/null +++ b/sites/main/sql/unique/Insert_transaction.sql @@ -0,0 +1,3 @@ +INSERT INTO main_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/main/sql/unique/logistics_transactions.sql b/sites/main/sql/unique/logistics_transactions.sql new file mode 100644 index 0000000..a4d3876 --- /dev/null +++ b/sites/main/sql/unique/logistics_transactions.sql @@ -0,0 +1,3 @@ +UPDATE main_logistics_info +SET quantity_on_hand = %s, location_data = %s +WHERE id = %s; diff --git a/sites/main/sql/unique/set_location_data.sql b/sites/main/sql/unique/set_location_data.sql new file mode 100644 index 0000000..e06f2b0 --- /dev/null +++ b/sites/main/sql/unique/set_location_data.sql @@ -0,0 +1,3 @@ +UPDATE main_locations +SET items = %s +WHERE id = %s; \ No newline at end of file diff --git a/static/itemHandler.js b/static/itemHandler.js index 4393244..ab4d8a6 100644 --- a/static/itemHandler.js +++ b/static/itemHandler.js @@ -91,6 +91,7 @@ function updatePrimaryLocation(){ document.getElementById('primary_location').style = "" logistics_info['primary_location'] = `${primary_zone}@${primary_location}` }; + console.log(logistics_info) }; function updateIssueLocation(){ @@ -201,6 +202,8 @@ async function saveItem() { updated['links'] = links; }; + console.log(`going into fetch ${logistics_info}`) + await fetch(`/updateItem`, { method: 'POST', headers: { @@ -213,8 +216,8 @@ async function saveItem() { item_info_id: item[7], updated: updated, item_info: item_info, - food_info: food_info, logistics_info: logistics_info, + food_info: food_info, }), }); M.toast({html: "Item has been saved successfully!", classes: "rounded green lighten-4 black-text"}); diff --git a/static/transactionHandler.js b/static/transactionHandler.js new file mode 100644 index 0000000..640bdee --- /dev/null +++ b/static/transactionHandler.js @@ -0,0 +1,171 @@ +let current_page = 1 +let end_page; +let limit = 50 +let search_text = "" +let zones; + +async function setupZones() { + let primary_zone = document.getElementById('zone') + + for (let i = 0; i < zones.length; i++){ + let option = document.createElement('option') + option.value = zones[i] + option.innerHTML = zones[i] + primary_zone.appendChild(option) + }; +}; + +async function fetchZones() { + const url = new URL('/getZones', window.location.origin); + const response = await fetch(url); + data = await response.json(); + zones = data.zones; +}; + + +async function fetchLocations(zone) { + const url = new URL('/getLocations', window.location.origin); + url.searchParams.append('zone', zone); + const response = await fetch(url); + data = await response.json(); + return data.locations; +}; + +async function loadLocations() { + let zone = document.getElementById('zone').value + let locations = await fetchLocations(zone) + await setupLocations(locations, 'location') +}; + +async function setupLocations(locations, el) { + let loc_el = document.getElementById(el) + console.log(locations) + loc_el.innerHTML = "" + + let option = document.createElement('option') + option.value = "undefined" + option.innerHTML = "Select Location..." + loc_el.appendChild(option) + + for (let i = 0; i < locations.length; i++){ + let option = document.createElement('option') + option.value = locations[i] + option.innerHTML = locations[i] + loc_el.appendChild(option) + }; +}; + +async function fetchItems(){ + if (current_page === 1){ + document.getElementById('back').classList.add("disabled") + document.getElementById('back').classList.remove("waves-effect") + } else { + document.getElementById('back').classList.remove("disabled") + document.getElementById('back').classList.add("waves-effect") + }; + + const url = new URL('/getItems', window.location.origin); + url.searchParams.append('page', current_page); + url.searchParams.append('limit', limit); + await fetch(url) + .then(response => response.json()) + .then(data => { + console.log(data) + end_page = parseInt(data.end) + if (current_page === end_page){ + document.getElementById('forward').classList.add("disabled") + document.getElementById('forward').classList.remove("waves-effect") + } else { + document.getElementById('forward').classList.remove("disabled") + document.getElementById('forward').classList.add("waves-effect") + }; + 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(transaction => { + console.log(transaction) + 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 = transaction[0]; + row_barcode.innerHTML = transaction[1]; + row_name.innerHTML = transaction[2]; + + + if ((colorstate % 2) == 0){ + row.classList.add('grey') + row.classList.add('lighten-5') + } + row.classList.add("custom_row") + row.addEventListener('click', function(){ + clickRow(transaction[0]) + }) + colorstate++ + }); + }) +} + +async function clickRow(database_id){ + let item = await fetchItem(database_id); + await populateFields(item) + +}; + +async function populateFields(item){ + document.getElementById("database_id").value = item[0]; + document.getElementById("barcode").value = item[1]; + document.getElementById("name").value = item[2]; + document.getElementById("QOH").value = item[19]; + + let location = item[16].split('@') + await setLocation(location[0], location[1]) +} + +async function setLocation(zone, location){ + document.getElementById('zone').value = zone + await loadLocations() + document.getElementById('location').value = location +}; + +async function fetchItem(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; +} + +document.getElementById('forward').addEventListener('click', async function(){ + current_page++ + await fetchItems() +}) + +document.getElementById('back').addEventListener('click', async function(){ + current_page-- + await fetchItems() +}) \ No newline at end of file diff --git a/templates/itemLookup.html b/templates/itemLookup.html new file mode 100644 index 0000000..4ef45ec --- /dev/null +++ b/templates/itemLookup.html @@ -0,0 +1,145 @@ + +